1 /*
2  * Copyright (c) 2024 Nordic Semiconductor
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/logging/log.h>
8 LOG_MODULE_REGISTER(net_cooked, CONFIG_NET_CAPTURE_LOG_LEVEL);
9 
10 #include <zephyr/kernel.h>
11 #include <stdlib.h>
12 #include <zephyr/net/net_core.h>
13 #include <zephyr/net/net_ip.h>
14 #include <zephyr/net/net_if.h>
15 #include <zephyr/net/net_pkt.h>
16 #include <zephyr/net/ethernet.h>
17 #include <zephyr/net/virtual.h>
18 #include <zephyr/net/virtual_mgmt.h>
19 #include <zephyr/net/capture.h>
20 #include <zephyr/net/net_l2.h>
21 
22 #include "sll.h"
23 
24 #define BUF_ALLOC_TIMEOUT 100 /* ms */
25 
26 /* Use our own slabs for temporary pkts */
27 NET_PKT_SLAB_DEFINE(cooked_pkts, CONFIG_NET_CAPTURE_PKT_COUNT);
28 
29 #if defined(CONFIG_NET_BUF_FIXED_DATA_SIZE)
30 NET_BUF_POOL_FIXED_DEFINE(cooked_bufs, CONFIG_NET_CAPTURE_BUF_COUNT,
31 			  CONFIG_NET_BUF_DATA_SIZE, 4, NULL);
32 #else
33 NET_BUF_POOL_VAR_DEFINE(cooked_bufs, CONFIG_NET_CAPTURE_BUF_COUNT,
34 			CONFIG_NET_PKT_BUF_RX_DATA_POOL_SIZE, 4, NULL);
35 #endif
36 
37 #define COOKED_MTU 1024
38 #define COOKED_DEVICE "NET_COOKED"
39 
40 struct cooked_context {
41 	struct net_if *iface;
42 	struct net_if *attached_to;
43 
44 	/* -1 is used as a not configured link type */
45 	int link_types[CONFIG_NET_CAPTURE_COOKED_MODE_MAX_LINK_TYPES];
46 	int link_type_count;
47 	int mtu;
48 	bool init_done;
49 	bool status;
50 };
51 
iface_init(struct net_if * iface)52 static void iface_init(struct net_if *iface)
53 {
54 	struct cooked_context *ctx = net_if_get_device(iface)->data;
55 	struct net_if *any_iface;
56 	int ifindex;
57 	int ret;
58 
59 	ifindex = net_if_get_by_name("any");
60 	if (ifindex < 0) {
61 		NET_DBG("No such interface \"any\", cannot init interface %d",
62 			net_if_get_by_iface(iface));
63 		return;
64 	}
65 
66 	if (net_if_l2(net_if_get_by_index(ifindex)) != &NET_L2_GET_NAME(DUMMY)) {
67 		NET_DBG("The \"any\" interface %d is wrong type", ifindex);
68 		return;
69 	}
70 
71 	if (ctx->init_done) {
72 		return;
73 	}
74 
75 	ctx->iface = iface;
76 	any_iface = net_if_get_by_index(ifindex);
77 
78 	(void)net_if_set_name(iface,
79 			      CONFIG_NET_CAPTURE_COOKED_MODE_INTERFACE_NAME);
80 	(void)net_virtual_set_name(iface, "Cooked mode capture");
81 
82 	net_if_flag_set(iface, NET_IF_NO_AUTO_START);
83 	net_if_flag_set(iface, NET_IF_POINTOPOINT);
84 	net_if_flag_clear(iface, NET_IF_IPV4);
85 	net_if_flag_clear(iface, NET_IF_IPV6);
86 
87 	/* Hook into "any" interface so that we can receive the
88 	 * captured data.
89 	 */
90 	ret = net_virtual_interface_attach(ctx->iface, any_iface);
91 	if (ret < 0) {
92 		NET_DBG("Cannot hook into interface %d (%d)",
93 			net_if_get_by_iface(any_iface), ret);
94 		return;
95 	}
96 
97 	NET_DBG("Interface %d attached on top of %d",
98 		net_if_get_by_iface(ctx->iface),
99 		net_if_get_by_iface(any_iface));
100 
101 	ctx->init_done = true;
102 }
103 
dev_init(const struct device * dev)104 static int dev_init(const struct device *dev)
105 {
106 	struct cooked_context *ctx = dev->data;
107 
108 	memset(ctx->link_types, -1, sizeof(ctx->link_types));
109 
110 	return 0;
111 }
112 
interface_start(const struct device * dev)113 static int interface_start(const struct device *dev)
114 {
115 	struct cooked_context *ctx = dev->data;
116 	int ret = 0;
117 
118 	if (ctx->status) {
119 		return -EALREADY;
120 	}
121 
122 	ctx->status = true;
123 
124 	NET_DBG("Starting iface %d", net_if_get_by_iface(ctx->iface));
125 
126 	return ret;
127 }
128 
interface_stop(const struct device * dev)129 static int interface_stop(const struct device *dev)
130 {
131 	struct cooked_context *ctx = dev->data;
132 
133 	if (!ctx->status) {
134 		return -EALREADY;
135 	}
136 
137 	ctx->status = false;
138 
139 	NET_DBG("Stopping iface %d", net_if_get_by_iface(ctx->iface));
140 
141 	return 0;
142 }
143 
interface_recv(struct net_if * iface,struct net_pkt * pkt)144 static enum net_verdict interface_recv(struct net_if *iface, struct net_pkt *pkt)
145 {
146 	struct cooked_context *ctx = net_if_get_device(iface)->data;
147 	bool found = false;
148 	uint16_t ptype;
149 
150 	/* Feed the packet to capture system after verifying that we are capturing
151 	 * these types of packets.
152 	 * The packet will be freed by capture API after it has been processed.
153 	 */
154 
155 	ptype = net_pkt_ll_proto_type(pkt);
156 
157 	NET_DBG("Capture pkt %p for interface %d", pkt, net_if_get_by_iface(iface));
158 
159 	for (int i = 0; i < ctx->link_type_count; i++) {
160 		if (ctx->link_types[i] == ptype) {
161 			found = true;
162 			break;
163 		}
164 	}
165 
166 	if (found) {
167 		int ret;
168 
169 		NET_DBG("Handler found for packet type 0x%04x", ptype);
170 
171 		/* Normally capture API will clone the net_pkt and we would
172 		 * always need to unref it. But for cooked packets, we can avoid
173 		 * the cloning so need to unref only if there was an error with
174 		 * capturing.
175 		 */
176 		ret = net_capture_pkt_with_status(iface, pkt);
177 		if (ret < 0) {
178 			net_pkt_unref(pkt);
179 		}
180 
181 		return NET_OK;
182 	}
183 
184 	NET_DBG("No handler found for packet type 0x%04x", ptype);
185 
186 	return NET_DROP;
187 }
188 
interface_attach(struct net_if * iface,struct net_if * lower_iface)189 static int interface_attach(struct net_if *iface, struct net_if *lower_iface)
190 {
191 	struct cooked_context *ctx;
192 
193 	if (net_if_get_by_iface(iface) < 0) {
194 		return -ENOENT;
195 	}
196 
197 	ctx = net_if_get_device(iface)->data;
198 	ctx->attached_to = lower_iface;
199 
200 	return 0;
201 }
202 
interface_set_config(struct net_if * iface,enum virtual_interface_config_type type,const struct virtual_interface_config * config)203 static int interface_set_config(struct net_if *iface,
204 				enum virtual_interface_config_type type,
205 				const struct virtual_interface_config *config)
206 {
207 	struct cooked_context *ctx = net_if_get_device(iface)->data;
208 
209 	switch (type) {
210 	case VIRTUAL_INTERFACE_CONFIG_TYPE_LINK_TYPE:
211 		if (config->link_types.count > ARRAY_SIZE(ctx->link_types)) {
212 			return -ERANGE;
213 		}
214 
215 		for (int i = 0; i < config->link_types.count; i++) {
216 			NET_DBG("Adding link type %u", config->link_types.type[i]);
217 
218 			ctx->link_types[i] = (int)config->link_types.type[i];
219 		}
220 
221 		ctx->link_type_count = config->link_types.count;
222 
223 		/* Mark the rest of the types as invalid */
224 		for (int i = ctx->link_type_count; i < ARRAY_SIZE(ctx->link_types); i++) {
225 			ctx->link_types[i] = -1;
226 		}
227 
228 		return 0;
229 
230 	case VIRTUAL_INTERFACE_CONFIG_TYPE_MTU:
231 		NET_DBG("Interface %d MTU set to %d",
232 			net_if_get_by_iface(iface), config->mtu);
233 		net_if_set_mtu(iface, config->mtu);
234 		return 0;
235 
236 	default:
237 		break;
238 	}
239 
240 	return -ENOTSUP;
241 }
242 
interface_get_config(struct net_if * iface,enum virtual_interface_config_type type,struct virtual_interface_config * config)243 static int interface_get_config(struct net_if *iface,
244 				enum virtual_interface_config_type type,
245 				struct virtual_interface_config *config)
246 {
247 	struct cooked_context *ctx = net_if_get_device(iface)->data;
248 	int i;
249 
250 	switch (type) {
251 	case VIRTUAL_INTERFACE_CONFIG_TYPE_LINK_TYPE:
252 		for (i = 0; i < ctx->link_type_count; i++) {
253 			if (ctx->link_types[i] < 0) {
254 				break;
255 			}
256 
257 			config->link_types.type[i] = (uint16_t)ctx->link_types[i];
258 		}
259 
260 		config->link_types.count = i;
261 		NET_ASSERT(config->link_types.count == ctx->link_type_count);
262 		return 0;
263 
264 	case VIRTUAL_INTERFACE_CONFIG_TYPE_MTU:
265 		config->mtu = net_if_get_mtu(iface);
266 		return 0;
267 
268 	default:
269 		break;
270 	}
271 
272 	return -ENOTSUP;
273 }
274 
275 static const struct virtual_interface_api cooked_api = {
276 	.iface_api.init = iface_init,
277 
278 	.start = interface_start,
279 	.stop = interface_stop,
280 	.recv = interface_recv,
281 	.attach = interface_attach,
282 	.set_config = interface_set_config,
283 	.get_config = interface_get_config,
284 };
285 
286 static struct cooked_context cooked_context_data;
287 
288 NET_VIRTUAL_INTERFACE_INIT(cooked, COOKED_DEVICE, dev_init,
289 			   NULL, &cooked_context_data, NULL,
290 			   CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
291 			   &cooked_api, COOKED_MTU);
292 
net_capture_cooked_setup(struct net_capture_cooked * ctx,uint16_t hatype,uint16_t halen,uint8_t * addr)293 int net_capture_cooked_setup(struct net_capture_cooked *ctx,
294 			     uint16_t hatype,
295 			     uint16_t halen,
296 			     uint8_t *addr)
297 {
298 	if (halen == 0 || halen > NET_CAPTURE_LL_ADDRLEN) {
299 		return -EINVAL;
300 	}
301 
302 	memset(ctx, 0, sizeof(*ctx));
303 
304 	ctx->hatype = hatype;
305 	ctx->halen = halen;
306 
307 	memcpy(ctx->addr, addr, halen);
308 
309 	return 0;
310 }
311 
create_sll_header(struct net_if * iface,struct net_pkt * pkt,struct net_capture_cooked * ctx,enum net_capture_packet_type type,uint16_t ptype)312 static int create_sll_header(struct net_if *iface,
313 			     struct net_pkt *pkt,
314 			     struct net_capture_cooked *ctx,
315 			     enum net_capture_packet_type type,
316 			     uint16_t ptype)
317 {
318 	struct sll2_header hdr2;
319 	struct sll_header hdr1;
320 	size_t hdr_len;
321 	uint8_t *hdr;
322 	int ret;
323 
324 	if (IS_ENABLED(CONFIG_NET_CAPTURE_COOKED_MODE_SLLV1)) {
325 		hdr1.sll_pkttype = htons(type);
326 		hdr1.sll_hatype = htons(ctx->hatype);
327 		hdr1.sll_halen = htons(ctx->halen);
328 		memcpy(hdr1.sll_addr, ctx->addr, sizeof(ctx->addr));
329 		hdr1.sll_protocol = htons(ptype);
330 
331 		hdr = (uint8_t *)&hdr1;
332 		hdr_len = sizeof(hdr1);
333 
334 	} else {
335 		hdr2.sll2_protocol = htons(ptype);
336 		hdr2.sll2_reserved_mbz = 0;
337 		hdr2.sll2_if_index = net_if_get_by_iface(iface);
338 		hdr2.sll2_hatype = htons(ctx->hatype);
339 		hdr2.sll2_pkttype = htons(type);
340 		hdr2.sll2_halen = htons(ctx->halen);
341 		memcpy(hdr2.sll2_addr, ctx->addr, sizeof(ctx->addr));
342 
343 		hdr = (uint8_t *)&hdr2;
344 		hdr_len = sizeof(hdr2);
345 	}
346 
347 	ret = net_pkt_write(pkt, hdr, hdr_len);
348 	if (ret < 0) {
349 		NET_DBG("Cannot write sll%s header (%d)",
350 			IS_ENABLED(CONFIG_NET_CAPTURE_COOKED_MODE_SLLV1) ?
351 			"" : "2", ret);
352 	}
353 
354 	return ret;
355 }
356 
get_net_pkt(void)357 static struct k_mem_slab *get_net_pkt(void)
358 {
359 	return &cooked_pkts;
360 }
361 
get_net_buf(void)362 static struct net_buf_pool *get_net_buf(void)
363 {
364 	return &cooked_bufs;
365 }
366 
net_capture_data(struct net_capture_cooked * ctx,const uint8_t * data,size_t data_len,enum net_capture_packet_type type,uint16_t ptype)367 void net_capture_data(struct net_capture_cooked *ctx,
368 		      const uint8_t *data, size_t data_len,
369 		      enum net_capture_packet_type type,
370 		      uint16_t ptype)
371 {
372 	static struct net_context context;
373 	const struct device *dev;
374 	struct net_if *iface;
375 	struct net_pkt *pkt;
376 	int ret;
377 
378 	net_context_setup_pools(&context, get_net_pkt, get_net_buf);
379 
380 	pkt = net_pkt_alloc_from_slab(&cooked_pkts, K_MSEC(BUF_ALLOC_TIMEOUT));
381 	if (pkt == NULL) {
382 		NET_DBG("Cannot allocate %s", "net_pkt");
383 		return;
384 	}
385 
386 	net_pkt_set_context(pkt, &context);
387 
388 	ret = net_pkt_alloc_buffer_raw(pkt,
389 			sizeof(COND_CODE_1(CONFIG_NET_CAPTURE_COOKED_MODE_SLLV1,
390 					   (struct sll_header),
391 					   (struct sll2_header))) + data_len,
392 				       K_MSEC(BUF_ALLOC_TIMEOUT));
393 	if (ret < 0) {
394 		NET_DBG("Cannot allocate %s %zd bytes (%d)", "net_buf for",
395 			sizeof(struct sll_header) + data_len, ret);
396 		net_pkt_unref(pkt);
397 		return;
398 	}
399 
400 	/* Write the packet to "any" interface which will pass it to
401 	 * the virtual interface that does the actual capturing of the packet.
402 	 * The reason for this trickery is that we do not have a network
403 	 * interface in use in this API, and want to have the packet routed
404 	 * via the any interface which can then deliver it to registered
405 	 * virtual interfaces.
406 	 */
407 	dev = device_get_binding(COOKED_DEVICE);
408 	if (dev == NULL) {
409 		NET_DBG("No such device %s found, data not captured!",
410 			COOKED_DEVICE);
411 		net_pkt_unref(pkt);
412 		return;
413 	}
414 
415 	/* Next we feed the data to the any interface, which
416 	 * will pass the data to the virtual interface.
417 	 * When using capture API or net-shell capture command, one
418 	 * should capture packets from the virtual interface.
419 	 */
420 	iface = ((struct cooked_context *)dev->data)->attached_to;
421 
422 	ret = create_sll_header(iface, pkt, ctx, type, ptype);
423 	if (ret < 0) {
424 		NET_DBG("Cannot write %s %zd bytes (%d)", "header",
425 			sizeof(struct sll_header), ret);
426 		net_pkt_unref(pkt);
427 		return;
428 	}
429 
430 	ret = net_pkt_write(pkt, data, data_len);
431 	if (ret < 0) {
432 		NET_DBG("Cannot write %s %zd bytes (%d)", "payload",
433 			data_len, ret);
434 		net_pkt_unref(pkt);
435 		return;
436 	}
437 
438 	/* Mark that this packet came from cooked capture mode.
439 	 * This will prevent the capture API from cloning the packet,
440 	 * so that the net_pkt will be passed as is to capture interface.
441 	 */
442 	net_pkt_set_cooked_mode(pkt, true);
443 
444 	/* The protocol type is used by virtual cooked interface to decide
445 	 * whether we capture the packet or not.
446 	 */
447 	net_pkt_set_ll_proto_type(pkt, ptype);
448 
449 	net_pkt_lladdr_src(pkt)->addr = NULL;
450 	net_pkt_lladdr_src(pkt)->len = 0U;
451 	net_pkt_lladdr_src(pkt)->type = NET_LINK_DUMMY;
452 	net_pkt_lladdr_dst(pkt)->addr = NULL;
453 	net_pkt_lladdr_dst(pkt)->len = 0U;
454 	net_pkt_lladdr_dst(pkt)->type = NET_LINK_DUMMY;
455 
456 	ret = net_recv_data(iface, pkt);
457 	if (ret < 0) {
458 		net_pkt_unref(pkt);
459 	}
460 }
461