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