1 /*
2  * Copyright (c) 2021 BayLibre SAS
3  * Copyright (c) 2024 Nordic Semiconductor
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #define NET_LOG_LEVEL CONFIG_NET_ETHERNET_BRIDGE_LOG_LEVEL
9 
10 #include <zephyr/logging/log.h>
11 LOG_MODULE_REGISTER(net_test, NET_LOG_LEVEL);
12 
13 #include <zephyr/types.h>
14 #include <stdbool.h>
15 #include <stddef.h>
16 #include <string.h>
17 #include <errno.h>
18 #include <zephyr/sys/printk.h>
19 
20 #include <zephyr/ztest.h>
21 
22 #include <zephyr/net/net_if.h>
23 #include <zephyr/net/ethernet.h>
24 #include <zephyr/net/ethernet_bridge.h>
25 #include <zephyr/net/virtual.h>
26 #include <zephyr/net/promiscuous.h>
27 
28 #if NET_LOG_LEVEL >= LOG_LEVEL_DBG
29 #define DBG(fmt, ...) printk(fmt, ##__VA_ARGS__)
30 #else
31 #define DBG(fmt, ...)
32 #endif
33 
34 static struct net_if *bridge;
35 
36 struct eth_fake_context {
37 	struct net_if *iface;
38 	struct net_pkt *sent_pkt;
39 	uint8_t mac_address[6];
40 	bool promisc_mode;
41 };
42 
eth_fake_iface_init(struct net_if * iface)43 static void eth_fake_iface_init(struct net_if *iface)
44 {
45 	const struct device *dev = net_if_get_device(iface);
46 	struct eth_fake_context *ctx = dev->data;
47 
48 	ctx->iface = iface;
49 
50 	ctx->mac_address[0] = 0xc2;
51 	ctx->mac_address[1] = 0xaa;
52 	ctx->mac_address[2] = 0xbb;
53 	ctx->mac_address[3] = 0xcc;
54 	ctx->mac_address[4] = 0xdd;
55 	ctx->mac_address[5] = 0xee;
56 
57 	net_if_set_link_addr(iface, ctx->mac_address,
58 			     sizeof(ctx->mac_address),
59 			     NET_LINK_ETHERNET);
60 
61 	ethernet_init(iface);
62 }
63 
eth_fake_send(const struct device * dev,struct net_pkt * pkt)64 static int eth_fake_send(const struct device *dev,
65 			 struct net_pkt *pkt)
66 {
67 	struct eth_fake_context *ctx = dev->data;
68 	struct net_eth_hdr *eth_hdr = NET_ETH_HDR(pkt);
69 
70 	/*
71 	 * Ignore packets we don't care about for this test, like
72 	 * the IP autoconfig related ones, etc.
73 	 */
74 	if (eth_hdr->type != htons(NET_ETH_PTYPE_ALL)) {
75 		DBG("Fake send ignoring pkt %p\n", pkt);
76 		return 0;
77 	}
78 
79 	if (ctx->sent_pkt != NULL) {
80 		DBG("Fake send found pkt %p while sending %p\n",
81 		    ctx->sent_pkt, pkt);
82 		return -EBUSY;
83 	}
84 	ctx->sent_pkt = net_pkt_shallow_clone(pkt, K_NO_WAIT);
85 	if (ctx->sent_pkt == NULL) {
86 		DBG("Fake send out of mem while sending pkt %p\n", pkt);
87 		return -ENOMEM;
88 	}
89 
90 	DBG("Fake send pkt %p kept locally as %p\n", pkt, ctx->sent_pkt);
91 	return 0;
92 }
93 
eth_fake_get_capabilities(const struct device * dev)94 static enum ethernet_hw_caps eth_fake_get_capabilities(const struct device *dev)
95 {
96 	return ETHERNET_PROMISC_MODE;
97 }
98 
eth_fake_set_config(const struct device * dev,enum ethernet_config_type type,const struct ethernet_config * config)99 static int eth_fake_set_config(const struct device *dev,
100 			       enum ethernet_config_type type,
101 			       const struct ethernet_config *config)
102 {
103 	struct eth_fake_context *ctx = dev->data;
104 
105 	switch (type) {
106 	case ETHERNET_CONFIG_TYPE_PROMISC_MODE:
107 		if (config->promisc_mode == ctx->promisc_mode) {
108 			return -EALREADY;
109 		}
110 
111 		ctx->promisc_mode = config->promisc_mode;
112 
113 		break;
114 
115 	default:
116 		return -EINVAL;
117 	}
118 
119 	return 0;
120 }
121 
122 static const struct ethernet_api eth_fake_api_funcs = {
123 	.iface_api.init = eth_fake_iface_init,
124 	.get_capabilities = eth_fake_get_capabilities,
125 	.set_config = eth_fake_set_config,
126 	.send = eth_fake_send,
127 };
128 
eth_fake_init(const struct device * dev)129 static int eth_fake_init(const struct device *dev)
130 {
131 	struct eth_fake_context *ctx = dev->data;
132 
133 	ctx->promisc_mode = false;
134 
135 	return 0;
136 }
137 
138 static struct eth_fake_context eth_fake_data[3];
139 
140 ETH_NET_DEVICE_INIT(eth_fake0, "eth_fake0",
141 		    eth_fake_init, NULL,
142 		    &eth_fake_data[0], NULL, CONFIG_ETH_INIT_PRIORITY,
143 		    &eth_fake_api_funcs, NET_ETH_MTU);
144 
145 ETH_NET_DEVICE_INIT(eth_fake1, "eth_fake1",
146 		    eth_fake_init, NULL,
147 		    &eth_fake_data[1], NULL, CONFIG_ETH_INIT_PRIORITY,
148 		    &eth_fake_api_funcs, NET_ETH_MTU);
149 
150 ETH_NET_DEVICE_INIT(eth_fake2, "eth_fake2",
151 		    eth_fake_init, NULL,
152 		    &eth_fake_data[2], NULL, CONFIG_ETH_INIT_PRIORITY,
153 		    &eth_fake_api_funcs, NET_ETH_MTU);
154 
155 static struct net_if *fake_iface[3];
156 
iface_cb(struct net_if * iface,void * user_data)157 static void iface_cb(struct net_if *iface, void *user_data)
158 {
159 	static int if_count;
160 
161 	if (if_count >= ARRAY_SIZE(fake_iface)) {
162 		return;
163 	}
164 
165 	DBG("Interface %p [%d]\n", iface, net_if_get_by_iface(iface));
166 
167 	if (net_if_l2(iface) == &NET_L2_GET_NAME(ETHERNET)) {
168 		const struct ethernet_api *api =
169 			net_if_get_device(iface)->api;
170 
171 		/*
172 		 * We want to only use struct net_if devices defined in
173 		 * this test as board on which it is run can have its
174 		 * own set of interfaces.
175 		 */
176 		if (api->get_capabilities ==
177 		    eth_fake_api_funcs.get_capabilities) {
178 			fake_iface[if_count++] = iface;
179 		}
180 	}
181 
182 	if (net_if_l2(iface) == &NET_L2_GET_NAME(VIRTUAL)) {
183 		enum virtual_interface_caps caps;
184 
185 		caps = net_virtual_get_iface_capabilities(iface);
186 		if (caps & VIRTUAL_INTERFACE_BRIDGE) {
187 			bridge = iface;
188 		}
189 	}
190 }
191 
192 static int orig_rx_num_blocks;
193 static int orig_tx_num_blocks;
194 
get_free_packet_count(void)195 static void get_free_packet_count(void)
196 {
197 	struct k_mem_slab *rx, *tx;
198 
199 	net_pkt_get_info(&rx, &tx, NULL, NULL);
200 	orig_rx_num_blocks = rx->info.num_blocks;
201 	orig_tx_num_blocks = tx->info.num_blocks;
202 }
203 
check_free_packet_count(void)204 static void check_free_packet_count(void)
205 {
206 	struct k_mem_slab *rx, *tx;
207 
208 	net_pkt_get_info(&rx, &tx, NULL, NULL);
209 	zassert_equal(rx->info.num_blocks, orig_rx_num_blocks, "");
210 	zassert_equal(tx->info.num_blocks, orig_tx_num_blocks, "");
211 }
212 
test_iface_setup(void)213 static void test_iface_setup(void)
214 {
215 	net_if_foreach(iface_cb, NULL);
216 
217 	zassert_not_null(fake_iface[0], "");
218 	zassert_not_null(fake_iface[1], "");
219 	zassert_not_null(fake_iface[2], "");
220 
221 	DBG("Interfaces: [%d] iface0 %p, [%d] iface1 %p, [%d] iface2 %p\n",
222 	    net_if_get_by_iface(fake_iface[0]), fake_iface[0],
223 	    net_if_get_by_iface(fake_iface[1]), fake_iface[1],
224 	    net_if_get_by_iface(fake_iface[2]), fake_iface[2]);
225 
226 	net_if_up(fake_iface[0]);
227 	net_if_up(fake_iface[1]);
228 	net_if_up(fake_iface[2]);
229 
230 	/* Remember the initial number of free packets in the pool. */
231 	get_free_packet_count();
232 }
233 
234 /*
235  * Simulate a packet reception from the outside world
236  */
_recv_data(struct net_if * iface)237 static void _recv_data(struct net_if *iface)
238 {
239 	struct net_pkt *pkt;
240 	struct net_eth_hdr eth_hdr;
241 	static uint8_t data[] = { 't', 'e', 's', 't', '\0' };
242 	int ret;
243 
244 	pkt = net_pkt_rx_alloc_with_buffer(iface, sizeof(eth_hdr) + sizeof(data),
245 					   AF_UNSPEC, 0, K_FOREVER);
246 	zassert_not_null(pkt, "");
247 
248 	/*
249 	 * The source and destination MAC addresses are completely arbitrary
250 	 * except for the U/L and I/G bits. However, the index of the faked
251 	 * incoming interface is mixed in as well to create some variation,
252 	 * and to help with validation on the transmit side.
253 	 */
254 
255 	eth_hdr.dst.addr[0] = 0xb2;
256 	eth_hdr.dst.addr[1] = 0x11;
257 	eth_hdr.dst.addr[2] = 0x22;
258 	eth_hdr.dst.addr[3] = 0x33;
259 	eth_hdr.dst.addr[4] = net_if_get_by_iface(iface);
260 	eth_hdr.dst.addr[5] = 0x55;
261 
262 	eth_hdr.src.addr[0] = 0xa2;
263 	eth_hdr.src.addr[1] = 0x11;
264 	eth_hdr.src.addr[2] = 0x22;
265 	eth_hdr.src.addr[3] = net_if_get_by_iface(iface);
266 	eth_hdr.src.addr[4] = 0x77;
267 	eth_hdr.src.addr[5] = 0x88;
268 
269 	eth_hdr.type = htons(NET_ETH_PTYPE_ALL);
270 
271 	ret = net_pkt_write(pkt, &eth_hdr, sizeof(eth_hdr));
272 	zassert_equal(ret, 0, "");
273 
274 	ret = net_pkt_write(pkt, data, sizeof(data));
275 	zassert_equal(ret, 0, "");
276 
277 	DBG("[%d] Fake recv pkt %p\n", net_if_get_by_iface(iface), pkt);
278 	ret = net_recv_data(iface, pkt);
279 	zassert_equal(ret, 0, "");
280 }
281 
test_recv_before_bridging(void)282 static void test_recv_before_bridging(void)
283 {
284 	/* fake some packet reception */
285 	_recv_data(fake_iface[0]);
286 	_recv_data(fake_iface[1]);
287 	_recv_data(fake_iface[2]);
288 
289 	/* give time to the processing threads to run */
290 	k_sleep(K_MSEC(100));
291 
292 	/* nothing should have been transmitted at this point */
293 	zassert_is_null(eth_fake_data[0].sent_pkt, "");
294 	zassert_is_null(eth_fake_data[1].sent_pkt, "");
295 	zassert_is_null(eth_fake_data[2].sent_pkt, "");
296 
297 	/* and everything already dropped. */
298 	check_free_packet_count();
299 }
300 
test_setup_bridge(void)301 static void test_setup_bridge(void)
302 {
303 	int ret;
304 
305 	/* add our interfaces to the bridge */
306 	ret = eth_bridge_iface_add(bridge, fake_iface[0]);
307 	zassert_equal(ret, 0, "");
308 	ret = eth_bridge_iface_add(bridge, fake_iface[1]);
309 	zassert_equal(ret, 0, "");
310 
311 	/* Try to add the bridge twice, there should be no error */
312 	ret = eth_bridge_iface_add(bridge, fake_iface[1]);
313 	zassert_equal(ret, 0, "");
314 
315 	ret = eth_bridge_iface_add(bridge, fake_iface[2]);
316 	zassert_equal(ret, 0, "");
317 
318 	ret = net_if_up(bridge);
319 	zassert_equal(ret, 0, "");
320 }
321 
test_recv_with_bridge(void)322 static void test_recv_with_bridge(void)
323 {
324 	int i, j;
325 
326 	for (i = 0; i < 3; i++) {
327 		int src_if_idx = net_if_get_by_iface(fake_iface[i]);
328 
329 		/* fake reception of packets */
330 		_recv_data(fake_iface[i]);
331 
332 		/* give time to the processing threads to run */
333 		k_sleep(K_MSEC(100));
334 
335 		/*
336 		 * fake_iface[0] and fake_iface[2] should have sent the packet
337 		 * but only if it didn't come from them.
338 		 * We skip fake_iface[1] handled above.
339 		 */
340 		for (j = 0; j < 3; j += 2) {
341 			struct net_pkt *pkt = eth_fake_data[j].sent_pkt;
342 			struct net_eth_hdr *hdr;
343 
344 			if (eth_fake_data[j].iface == fake_iface[i]) {
345 				zassert_is_null(pkt, "");
346 				continue;
347 			}
348 
349 			eth_fake_data[j].sent_pkt = NULL;
350 			zassert_not_null(pkt, "");
351 
352 			/* make sure nothing messed up our ethernet header */
353 			hdr = NET_ETH_HDR(pkt);
354 
355 			zassert_equal(hdr->dst.addr[0], 0xb2, "");
356 			zassert_equal(hdr->src.addr[0], 0xa2, "");
357 			zassert_equal(hdr->dst.addr[4], src_if_idx, "");
358 			zassert_equal(hdr->src.addr[3], src_if_idx, "");
359 
360 			net_pkt_unref(pkt);
361 		}
362 	}
363 
364 	check_free_packet_count();
365 }
366 
test_recv_after_bridging(void)367 static void test_recv_after_bridging(void)
368 {
369 	int ret;
370 
371 	ret = net_if_down(bridge);
372 	zassert_equal(ret, 0, "");
373 
374 	/* remove our interfaces from the bridge */
375 	ret = eth_bridge_iface_remove(bridge, fake_iface[0]);
376 	zassert_equal(ret, 0, "");
377 	ret = eth_bridge_iface_remove(bridge, fake_iface[1]);
378 	zassert_equal(ret, 0, "");
379 	ret = eth_bridge_iface_remove(bridge, fake_iface[2]);
380 	zassert_equal(ret, 0, "");
381 
382 	/* If there are not enough interfaces in the bridge, it is not created */
383 	ret = net_if_up(bridge);
384 	zassert_equal(ret, -ENOENT, "");
385 
386 	eth_fake_data[0].sent_pkt = eth_fake_data[1].sent_pkt =
387 		eth_fake_data[2].sent_pkt = NULL;
388 
389 	/* things should have returned to the pre-bridging state */
390 	test_recv_before_bridging();
391 }
392 
393 /* Make sure bridge interface support promiscuous API */
ZTEST(net_eth_bridge,test_verify_promisc_mode)394 ZTEST(net_eth_bridge, test_verify_promisc_mode)
395 {
396 	int ret;
397 
398 	ret = net_promisc_mode_on(bridge);
399 	zassert_equal(ret, 0, "");
400 }
401 
ZTEST(net_eth_bridge,test_net_eth_bridge)402 ZTEST(net_eth_bridge, test_net_eth_bridge)
403 {
404 	DBG("Before bridging\n");
405 	test_iface_setup();
406 	test_recv_before_bridging();
407 	DBG("With bridging\n");
408 	test_setup_bridge();
409 	test_recv_with_bridge();
410 	DBG("After bridging\n");
411 	test_recv_after_bridging();
412 }
413 
414 ZTEST_SUITE(net_eth_bridge, NULL, NULL, NULL, NULL, NULL);
415