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 ð_fake_data[0], NULL, CONFIG_ETH_INIT_PRIORITY,
143 ð_fake_api_funcs, NET_ETH_MTU);
144
145 ETH_NET_DEVICE_INIT(eth_fake1, "eth_fake1",
146 eth_fake_init, NULL,
147 ð_fake_data[1], NULL, CONFIG_ETH_INIT_PRIORITY,
148 ð_fake_api_funcs, NET_ETH_MTU);
149
150 ETH_NET_DEVICE_INIT(eth_fake2, "eth_fake2",
151 eth_fake_init, NULL,
152 ð_fake_data[2], NULL, CONFIG_ETH_INIT_PRIORITY,
153 ð_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, ð_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