xref: /dpdk/lib/ipsec/ipsec_telemetry.c (revision 8f1d23ece06adff5eae9f1b4365bdbbd3abee2b2)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2021 Intel Corporation
3  */
4 
5 #include <stdlib.h>
6 #include <rte_ipsec.h>
7 #include <rte_telemetry.h>
8 #include <rte_malloc.h>
9 #include "sa.h"
10 
11 
12 struct ipsec_telemetry_entry {
13 	LIST_ENTRY(ipsec_telemetry_entry) next;
14 	const struct rte_ipsec_sa *sa;
15 };
16 static LIST_HEAD(ipsec_telemetry_head, ipsec_telemetry_entry)
17 		ipsec_telemetry_list = LIST_HEAD_INITIALIZER();
18 
19 static int
20 handle_telemetry_cmd_ipsec_sa_list(const char *cmd __rte_unused,
21 		const char *params __rte_unused,
22 		struct rte_tel_data *data)
23 {
24 	struct ipsec_telemetry_entry *entry;
25 	rte_tel_data_start_array(data, RTE_TEL_U64_VAL);
26 
27 	LIST_FOREACH(entry, &ipsec_telemetry_list, next) {
28 		const struct rte_ipsec_sa *sa = entry->sa;
29 		rte_tel_data_add_array_u64(data, rte_be_to_cpu_32(sa->spi));
30 	}
31 
32 	return 0;
33 }
34 
35 /**
36  * Handle IPsec SA statistics telemetry request
37  *
38  * Return dict of SA's with dict of key/value counters
39  *
40  * {
41  *     "SA_SPI_XX": {"count": 0, "bytes": 0, "errors": 0},
42  *     "SA_SPI_YY": {"count": 0, "bytes": 0, "errors": 0}
43  * }
44  *
45  */
46 static int
47 handle_telemetry_cmd_ipsec_sa_stats(const char *cmd __rte_unused,
48 		const char *params,
49 		struct rte_tel_data *data)
50 {
51 	struct ipsec_telemetry_entry *entry;
52 	const struct rte_ipsec_sa *sa;
53 	uint32_t sa_spi = 0;
54 
55 	if (params) {
56 		sa_spi = rte_cpu_to_be_32((uint32_t)strtoul(params, NULL, 0));
57 		if (sa_spi == 0)
58 			return -EINVAL;
59 	}
60 
61 	rte_tel_data_start_dict(data);
62 
63 	LIST_FOREACH(entry, &ipsec_telemetry_list, next) {
64 		char sa_name[64];
65 		sa = entry->sa;
66 		static const char *name_pkt_cnt = "count";
67 		static const char *name_byte_cnt = "bytes";
68 		static const char *name_error_cnt = "errors";
69 		struct rte_tel_data *sa_data;
70 
71 		/* If user provided SPI only get telemetry for that SA */
72 		if (sa_spi && (sa_spi != sa->spi))
73 			continue;
74 
75 		/* allocate telemetry data struct for SA telemetry */
76 		sa_data = rte_tel_data_alloc();
77 		if (!sa_data)
78 			return -ENOMEM;
79 
80 		rte_tel_data_start_dict(sa_data);
81 
82 		/* add telemetry key/values pairs */
83 		rte_tel_data_add_dict_u64(sa_data, name_pkt_cnt,
84 					sa->statistics.count);
85 
86 		rte_tel_data_add_dict_u64(sa_data, name_byte_cnt,
87 					sa->statistics.bytes -
88 					(sa->statistics.count * sa->hdr_len));
89 
90 		rte_tel_data_add_dict_u64(sa_data, name_error_cnt,
91 					sa->statistics.errors.count);
92 
93 		/* generate telemetry label */
94 		snprintf(sa_name, sizeof(sa_name), "SA_SPI_%i",
95 				rte_be_to_cpu_32(sa->spi));
96 
97 		/* add SA telemetry to dictionary container */
98 		rte_tel_data_add_dict_container(data, sa_name, sa_data, 0);
99 	}
100 
101 	return 0;
102 }
103 
104 static int
105 handle_telemetry_cmd_ipsec_sa_details(const char *cmd __rte_unused,
106 		const char *params,
107 		struct rte_tel_data *data)
108 {
109 	struct ipsec_telemetry_entry *entry;
110 	const struct rte_ipsec_sa *sa;
111 	uint32_t sa_spi = 0;
112 
113 	if (params)
114 		sa_spi = rte_cpu_to_be_32((uint32_t)strtoul(params, NULL, 0));
115 	/* valid SPI needed */
116 	if (sa_spi == 0)
117 		return -EINVAL;
118 
119 
120 	rte_tel_data_start_dict(data);
121 
122 	LIST_FOREACH(entry, &ipsec_telemetry_list, next) {
123 		uint64_t mode;
124 		sa = entry->sa;
125 		if (sa_spi != sa->spi)
126 			continue;
127 
128 		/* add SA configuration key/values pairs */
129 		rte_tel_data_add_dict_string(data, "Type",
130 			(sa->type & RTE_IPSEC_SATP_PROTO_MASK) ==
131 			RTE_IPSEC_SATP_PROTO_AH ? "AH" : "ESP");
132 
133 		rte_tel_data_add_dict_string(data, "Direction",
134 			(sa->type & RTE_IPSEC_SATP_DIR_MASK) ==
135 			RTE_IPSEC_SATP_DIR_IB ?	"Inbound" : "Outbound");
136 
137 		mode = sa->type & RTE_IPSEC_SATP_MODE_MASK;
138 
139 		if (mode == RTE_IPSEC_SATP_MODE_TRANS) {
140 			rte_tel_data_add_dict_string(data, "Mode", "Transport");
141 		} else {
142 			rte_tel_data_add_dict_string(data, "Mode", "Tunnel");
143 
144 			if ((sa->type & RTE_IPSEC_SATP_NATT_MASK) ==
145 				RTE_IPSEC_SATP_NATT_ENABLE) {
146 				if (sa->type & RTE_IPSEC_SATP_MODE_TUNLV4) {
147 					rte_tel_data_add_dict_string(data,
148 						"Tunnel-Type",
149 						"IPv4-UDP");
150 				} else if (sa->type &
151 						RTE_IPSEC_SATP_MODE_TUNLV6) {
152 					rte_tel_data_add_dict_string(data,
153 						"Tunnel-Type",
154 						"IPv6-UDP");
155 				}
156 			} else {
157 				if (sa->type & RTE_IPSEC_SATP_MODE_TUNLV4) {
158 					rte_tel_data_add_dict_string(data,
159 						"Tunnel-Type",
160 						"IPv4");
161 				} else if (sa->type &
162 						RTE_IPSEC_SATP_MODE_TUNLV6) {
163 					rte_tel_data_add_dict_string(data,
164 						"Tunnel-Type",
165 						"IPv6");
166 				}
167 			}
168 		}
169 
170 		rte_tel_data_add_dict_string(data,
171 				"extended-sequence-number",
172 				(sa->type & RTE_IPSEC_SATP_ESN_MASK) ==
173 				 RTE_IPSEC_SATP_ESN_ENABLE ?
174 				"enabled" : "disabled");
175 
176 		if ((sa->type & RTE_IPSEC_SATP_DIR_MASK) ==
177 			RTE_IPSEC_SATP_DIR_IB)
178 
179 			if (sa->sqn.inb.rsn[sa->sqn.inb.rdidx])
180 				rte_tel_data_add_dict_u64(data,
181 				"sequence-number",
182 				sa->sqn.inb.rsn[sa->sqn.inb.rdidx]->sqn);
183 			else
184 				rte_tel_data_add_dict_u64(data,
185 					"sequence-number", 0);
186 		else
187 			rte_tel_data_add_dict_u64(data, "sequence-number",
188 					sa->sqn.outb);
189 
190 		rte_tel_data_add_dict_string(data,
191 				"explicit-congestion-notification",
192 				(sa->type & RTE_IPSEC_SATP_ECN_MASK) ==
193 				RTE_IPSEC_SATP_ECN_ENABLE ?
194 				"enabled" : "disabled");
195 
196 		rte_tel_data_add_dict_string(data,
197 				"copy-DSCP",
198 				(sa->type & RTE_IPSEC_SATP_DSCP_MASK) ==
199 				RTE_IPSEC_SATP_DSCP_ENABLE ?
200 				"enabled" : "disabled");
201 	}
202 
203 	return 0;
204 }
205 
206 
207 int
208 rte_ipsec_telemetry_sa_add(const struct rte_ipsec_sa *sa)
209 {
210 	struct ipsec_telemetry_entry *entry = rte_zmalloc(NULL,
211 			sizeof(struct ipsec_telemetry_entry), 0);
212 	if (entry == NULL)
213 		return -ENOMEM;
214 	entry->sa = sa;
215 	LIST_INSERT_HEAD(&ipsec_telemetry_list, entry, next);
216 	return 0;
217 }
218 
219 void
220 rte_ipsec_telemetry_sa_del(const struct rte_ipsec_sa *sa)
221 {
222 	struct ipsec_telemetry_entry *entry;
223 	LIST_FOREACH(entry, &ipsec_telemetry_list, next) {
224 		if (sa == entry->sa) {
225 			LIST_REMOVE(entry, next);
226 			rte_free(entry);
227 			return;
228 		}
229 	}
230 }
231 
232 
233 RTE_INIT(rte_ipsec_telemetry_init)
234 {
235 	rte_telemetry_register_cmd("/ipsec/sa/list",
236 		handle_telemetry_cmd_ipsec_sa_list,
237 		"Return list of IPsec SAs with telemetry enabled.");
238 	rte_telemetry_register_cmd("/ipsec/sa/stats",
239 		handle_telemetry_cmd_ipsec_sa_stats,
240 		"Returns IPsec SA statistics. Parameters: int sa_spi");
241 	rte_telemetry_register_cmd("/ipsec/sa/details",
242 		handle_telemetry_cmd_ipsec_sa_details,
243 		"Returns IPsec SA configuration. Parameters: int sa_spi");
244 }
245