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