xref: /freebsd/tests/sys/netgraph/bridge.c (revision 1603881667360c015f6685131f2f25474fa67a72)
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright 2021 Lutz Donnerhacke
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above
13  *    copyright notice, this list of conditions and the following
14  *    disclaimer in the documentation and/or other materials provided
15  *    with the distribution.
16  * 3. Neither the name of the copyright holder nor the names of its
17  *    contributors may be used to endorse or promote products derived
18  *    from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
21  * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
22  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
23  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
25  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
26  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
27  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
29  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
30  * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
31  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 #include <atf-c.h>
35 #include <errno.h>
36 #include <stdio.h>
37 
38 #include <net/ethernet.h>
39 #include <netinet/in.h>
40 #include <netinet/ip.h>
41 #include <netinet/ip6.h>
42 
43 #include "util.h"
44 #include <netgraph/ng_bridge.h>
45 
46 static void	get_data0(void *data, size_t len, void *ctx);
47 static void	get_data1(void *data, size_t len, void *ctx);
48 static void	get_data2(void *data, size_t len, void *ctx);
49 static void	get_data3(void *data, size_t len, void *ctx);
50 
51 static void	get_tablesize(char const *source, struct ng_mesg *msg, void *ctx);
52 struct gettable
53 {
54 	u_int32_t	tok;
55 	int		cnt;
56 };
57 
58 struct frame4
59 {
60 	struct ether_header eh;
61 	struct ip	ip;
62 	char		data[64];
63 };
64 struct frame6
65 {
66 	struct ether_header eh;
67 	struct ip6_hdr	ip;
68 	char		data[64];
69 };
70 
71 static struct frame4 msg4 = {
72 	.ip.ip_v = 4,
73 	.ip.ip_hl = 5,
74 	.ip.ip_ttl = 1,
75 	.ip.ip_p = 254,
76 	.ip.ip_src = {htonl(0x0a00dead)},
77 	.ip.ip_dst = {htonl(0x0a00beef)},
78 	.ip.ip_len = 32,
79 	.eh.ether_type = ETHERTYPE_IP,
80 	.eh.ether_shost = {2, 4, 6},
81 	.eh.ether_dhost = {2, 4, 6},
82 };
83 
84 
85 ATF_TC(basic);
86 ATF_TC_HEAD(basic, conf)
87 {
88 	atf_tc_set_md_var(conf, "require.user", "root");
89 }
90 
91 ATF_TC_BODY(basic, dummy)
92 {
93 	int		r[4];
94 	struct gettable	rm;
95 
96 	ng_init();
97 	ng_errors(PASS);
98 	ng_shutdown("bridge:");
99 	ng_errors(FAIL);
100 
101 	ng_mkpeer(".", "a", "bridge", "link0");
102 	ng_name("a", "bridge");
103 	ng_connect(".", "b", "bridge:", "link1");
104 	ng_connect(".", "c", "bridge:", "link2");
105 
106 	/* do not bounce back */
107 	ng_register_data("a", get_data0);
108 	bzero(r, sizeof(r));
109 	msg4.eh.ether_shost[5] = 1;
110 	ng_send_data("a", &msg4, sizeof(msg4));
111 	ng_handle_events(50, &r);
112 	ATF_CHECK(r[0] == 0);
113 
114 	/* send to others */
115 	ng_register_data("b", get_data1);
116 	ng_register_data("c", get_data2);
117 	bzero(r, sizeof(r));
118 	msg4.eh.ether_shost[5] = 1;
119 	ng_send_data("a", &msg4, sizeof(msg4));
120 	ng_handle_events(50, &r);
121 	ATF_CHECK(r[0] == 0 && r[1] == 1 && r[2] == 1);
122 
123 	bzero(r, sizeof(r));
124 	msg4.eh.ether_shost[5] = 2;
125 	ng_send_data("b", &msg4, sizeof(msg4));
126 	ng_handle_events(50, &r);
127 	ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 1);
128 
129 	bzero(r, sizeof(r));
130 	msg4.eh.ether_shost[5] = 3;
131 	ng_send_data("c", &msg4, sizeof(msg4));
132 	ng_handle_events(50, &r);
133 	ATF_CHECK(r[0] == 1 && r[1] == 1 && r[2] == 0);
134 
135 	/* send to learned unicast */
136 	bzero(r, sizeof(r));
137 	msg4.eh.ether_shost[5] = 1;
138 	msg4.eh.ether_dhost[5] = 3;
139 	ng_send_data("a", &msg4, sizeof(msg4));
140 	ng_handle_events(50, &r);
141 	ATF_CHECK(r[0] == 0 && r[1] == 0 && r[2] == 1);
142 
143 	/* inspect mac table */
144 	ng_register_msg(get_tablesize);
145 	rm.tok = ng_send_msg("bridge:", "gettable");
146 	rm.cnt = 0;
147 	ng_handle_events(50, &rm);
148 	ATF_CHECK(rm.cnt == 3);
149 
150 	/* remove a link */
151 	ng_rmhook(".", "b");
152 	bzero(r, sizeof(r));
153 	msg4.eh.ether_shost[5] = 1;
154 	msg4.eh.ether_dhost[5] = 0;
155 	ng_send_data("a", &msg4, sizeof(msg4));
156 	ng_handle_events(50, &r);
157 	ATF_CHECK(r[0] == 0 && r[1] == 0 && r[2] == 1);
158 
159 	/* inspect mac table */
160 	ng_register_msg(get_tablesize);
161 	rm.tok = ng_send_msg("bridge:", "gettable");
162 	rm.cnt = 0;
163 	ng_handle_events(50, &rm);
164 	ATF_CHECK(rm.cnt == 2);
165 
166 	ng_shutdown("bridge:");
167 }
168 
169 ATF_TC(persistence);
170 ATF_TC_HEAD(persistence, conf)
171 {
172 	atf_tc_set_md_var(conf, "require.user", "root");
173 }
174 
175 ATF_TC_BODY(persistence, dummy)
176 {
177 	ng_init();
178 	ng_errors(PASS);
179 	ng_shutdown("bridge:");
180 	ng_errors(FAIL);
181 
182 	ng_mkpeer(".", "a", "bridge", "link0");
183 	ng_name("a", "bridge");
184 
185 	ng_send_msg("bridge:", "setpersistent");
186 	ng_rmhook(".", "a");
187 
188 	ng_shutdown("bridge:");
189 }
190 
191 ATF_TC(loop);
192 ATF_TC_HEAD(loop, conf)
193 {
194 	atf_tc_set_md_var(conf, "require.user", "root");
195 }
196 
197 ATF_TC_BODY(loop, dummy)
198 {
199 	int		r[4], i;
200 
201 	ng_init();
202 	ng_errors(PASS);
203 	ng_shutdown("bridge1:");
204 	ng_shutdown("bridge2:");
205 	ng_errors(FAIL);
206 
207 	ng_mkpeer(".", "a", "bridge", "link0");
208 	ng_name("a", "bridge1");
209 	ng_mkpeer(".", "b", "bridge", "link1");
210 	ng_name("b", "bridge2");
211 
212 	ng_register_data("a", get_data0);
213 	ng_register_data("b", get_data1);
214 
215 	/*-
216 	 * Open loop
217 	 *
218 	 *    /-- bridge1
219 	 * . <    |
220 	 *    \-- bridge2
221 	 */
222 	ng_connect("bridge1:", "link11", "bridge2:", "link11");
223 
224 	bzero(r, sizeof(r));
225 	msg4.eh.ether_shost[5] = 1;
226 	ng_send_data("a", &msg4, sizeof(msg4));
227 	ng_handle_events(50, &r);
228 	ATF_CHECK(r[0] == 0 && r[1] == 1);
229 
230 	/*-
231 	 * Closed loop, DANGEROUS!
232 	 *
233 	 *    /-- bridge1 -\
234 	 * . <     |       |
235 	 *    \-- bridge2 -/
236 	 */
237 	ng_connect("bridge1:", "link12", "bridge2:", "link12");
238 
239 	bzero(r, sizeof(r));
240 	msg4.eh.ether_shost[5] = 1;
241 	ng_errors(PASS);
242 	ng_send_data("a", &msg4, sizeof(msg4));
243 	ATF_CHECK_ERRNO(ELOOP, errno != 0);	/* loop might be detected */
244 	ng_errors(FAIL);
245 	for (i = 0; i < 10; i++)	/* don't run forever */
246 		if (!ng_handle_event(50, &r))
247 			break;
248 	ATF_CHECK(r[0] == 0 && r[1] == 1);
249 
250 	ng_shutdown("bridge1:");
251 	ng_shutdown("bridge2:");
252 }
253 
254 ATF_TC(many_unicasts);
255 ATF_TC_HEAD(many_unicasts, conf)
256 {
257 	atf_tc_set_md_var(conf, "require.user", "root");
258 }
259 
260 ATF_TC_BODY(many_unicasts, dummy)
261 {
262 	int		r[4], i;
263 	const int	HOOKS = 1000;
264 	struct gettable	rm;
265 
266 	ng_init();
267 	ng_errors(PASS);
268 	ng_shutdown("bridge:");
269 	ng_errors(FAIL);
270 
271 	ng_mkpeer(".", "a", "bridge", "link0");
272 	ng_name("a", "bridge");
273 	ng_register_data("a", get_data0);
274 
275 	/* learn MAC */
276 	bzero(r, sizeof(r));
277 	msg4.eh.ether_shost[3] = 0xff;
278 	ng_send_data("a", &msg4, sizeof(msg4));
279 	ng_handle_events(50, &r);
280 	ATF_CHECK(r[0] == 0);
281 
282 	/* use learned MAC as destination */
283 	msg4.eh.ether_shost[3] = 0;
284 	msg4.eh.ether_dhost[3] = 0xff;
285 
286 	/* now send */
287 	bzero(r, sizeof(r));
288 	for (i = 1; i <= HOOKS; i++)
289 	{
290 		char		hook[20];
291 
292 		snprintf(hook, sizeof(hook), "link%d", i);
293 		ng_connect(".", hook, "bridge:", hook);
294 		ng_register_data(hook, get_data2);
295 
296 		msg4.eh.ether_shost[4] = i >> 8;
297 		msg4.eh.ether_shost[5] = i & 0xff;
298 		ng_errors(PASS);
299 		ng_send_data(hook, &msg4, sizeof(msg4));
300 		ng_errors(FAIL);
301 		if (errno != 0)
302 			break;
303 		ng_handle_events(50, &r);
304 	}
305 	ATF_CHECK(r[0] == HOOKS && r[2] == 0);
306 
307 	/* inspect mac table */
308 	ng_register_msg(get_tablesize);
309 	rm.cnt = 0;
310 	ng_errors(PASS);
311 	rm.tok = ng_send_msg("bridge:", "gettable");
312 	ng_errors(FAIL);
313 	if (rm.tok == (u_int32_t)-1)
314 	{
315 		ATF_CHECK_ERRNO(ENOBUFS, 1);
316 		atf_tc_expect_fail("response too large");
317 	}
318 	ng_handle_events(50, &rm);
319 	ATF_CHECK(rm.cnt == HOOKS + 1);
320 	atf_tc_expect_pass();
321 
322 	ng_shutdown("bridge:");
323 }
324 
325 ATF_TC(many_broadcasts);
326 ATF_TC_HEAD(many_broadcasts, conf)
327 {
328 	atf_tc_set_md_var(conf, "require.user", "root");
329 }
330 
331 ATF_TC_BODY(many_broadcasts, dummy)
332 {
333 	int		r[4], i;
334 	const int	HOOKS = 1000;
335 
336 	ng_init();
337 	ng_errors(PASS);
338 	ng_shutdown("bridge:");
339 	ng_errors(FAIL);
340 
341 	ng_mkpeer(".", "a", "bridge", "link0");
342 	ng_name("a", "bridge");
343 	ng_register_data("a", get_data0);
344 
345 	/* learn MAC */
346 	bzero(r, sizeof(r));
347 	msg4.eh.ether_shost[3] = 0xff;
348 	ng_send_data("a", &msg4, sizeof(msg4));
349 	ng_handle_events(50, &r);
350 	ATF_CHECK(r[0] == 0);
351 
352 	/* use broadcast MAC */
353 	msg4.eh.ether_shost[3] = 0;
354 	memset(msg4.eh.ether_dhost, 0xff, sizeof(msg4.eh.ether_dhost));
355 
356 	/* now send */
357 	bzero(r, sizeof(r));
358 	for (i = 1; i <= HOOKS; i++)
359 	{
360 		char		hook[20];
361 
362 		snprintf(hook, sizeof(hook), "link%d", i);
363 		ng_connect(".", hook, "bridge:", hook);
364 		ng_register_data(hook, get_data3);
365 
366 		msg4.eh.ether_shost[4] = i >> 8;
367 		msg4.eh.ether_shost[5] = i & 0xff;
368 		ng_errors(PASS);
369 		ng_send_data(hook, &msg4, sizeof(msg4));
370 		ng_errors(FAIL);
371 		if (errno != 0)
372 			break;
373 		ng_handle_events(50, &r);
374 	}
375 	ATF_CHECK(r[0] > 100 && r[3] > 100);
376 	if (i < HOOKS)
377 		atf_tc_expect_fail("netgraph queue full (%d)", i);
378 	ATF_CHECK(r[0] == HOOKS);
379 	atf_tc_expect_pass();
380 
381 	ng_shutdown("bridge:");
382 }
383 
384 ATF_TC(uplink_private);
385 ATF_TC_HEAD(uplink_private, conf)
386 {
387 	atf_tc_set_md_var(conf, "require.user", "root");
388 }
389 
390 ATF_TC_BODY(uplink_private, dummy)
391 {
392 	int		r[4];
393 	struct gettable	rm;
394 
395 	ng_init();
396 	ng_errors(PASS);
397 	ng_shutdown("bridge:");
398 
399 	ng_mkpeer(".", "u1", "bridge", "uplink1");
400 	if (errno > 0)
401 		atf_tc_skip("uplinks are not supported.");
402 	ng_errors(FAIL);
403 	ng_name("u1", "bridge");
404 	ng_register_data("u1", get_data1);
405 	ng_connect(".", "u2", "bridge:", "uplink2");
406 	ng_register_data("u2", get_data2);
407 	ng_connect(".", "l0", "bridge:", "link0");
408 	ng_register_data("l0", get_data0);
409 	ng_connect(".", "l3", "bridge:", "link3");
410 	ng_register_data("l3", get_data3);
411 
412 	/* unknown unicast 0 from uplink1 */
413 	bzero(r, sizeof(r));
414 	msg4.eh.ether_shost[5] = 1;
415 	ng_send_data("u1", &msg4, sizeof(msg4));
416 	ng_handle_events(50, &r);
417 	ATF_CHECK(r[0] == 0 && r[1] == 0 && r[2] == 1 && r[3] == 0);
418 
419 	/* unknown unicast 2 from link0 */
420 	bzero(r, sizeof(r));
421 	msg4.eh.ether_shost[5] = 0;
422 	msg4.eh.ether_dhost[5] = 2;
423 	ng_send_data("l0", &msg4, sizeof(msg4));
424 	ng_handle_events(50, &r);
425 	ATF_CHECK(r[0] == 0 && r[1] == 1 && r[2] == 1 && r[3] == 0);
426 
427 	/* known unicast 0 from uplink2 */
428 	bzero(r, sizeof(r));
429 	msg4.eh.ether_shost[5] = 2;
430 	msg4.eh.ether_dhost[5] = 0;
431 	ng_send_data("u2", &msg4, sizeof(msg4));
432 	ng_handle_events(50, &r);
433 	ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 0 && r[3] == 0);
434 
435 	/* known unicast 0 from link3 */
436 	bzero(r, sizeof(r));
437 	msg4.eh.ether_shost[5] = 3;
438 	msg4.eh.ether_dhost[5] = 0;
439 	ng_send_data("l3", &msg4, sizeof(msg4));
440 	ng_handle_events(50, &r);
441 	ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 0 && r[3] == 0);
442 
443 	/* (un)known unicast 2 from uplink1 */
444 	bzero(r, sizeof(r));
445 	msg4.eh.ether_shost[5] = 1;
446 	msg4.eh.ether_dhost[5] = 2;
447 	ng_send_data("u1", &msg4, sizeof(msg4));
448 	ng_handle_events(50, &r);
449 	ATF_CHECK(r[0] == 0 && r[1] == 0 && r[2] == 1 && r[3] == 0);
450 
451 	/* (un)known unicast 2 from link0 */
452 	bzero(r, sizeof(r));
453 	msg4.eh.ether_shost[5] = 0;
454 	ng_send_data("l0", &msg4, sizeof(msg4));
455 	ng_handle_events(50, &r);
456 	ATF_CHECK(r[0] == 0 && r[1] == 1 && r[2] == 1 && r[3] == 0);
457 
458 	/* unknown multicast 2 from uplink1 */
459 	bzero(r, sizeof(r));
460 	msg4.eh.ether_shost[5] = 1;
461 	msg4.eh.ether_dhost[0] = 0xff;
462 	ng_send_data("u1", &msg4, sizeof(msg4));
463 	ng_handle_events(50, &r);
464 	ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 1 && r[3] == 1);
465 
466 	/* unknown multicast 2 from link0 */
467 	bzero(r, sizeof(r));
468 	msg4.eh.ether_shost[5] = 0;
469 	ng_send_data("l0", &msg4, sizeof(msg4));
470 	ng_handle_events(50, &r);
471 	ATF_CHECK(r[0] == 0 && r[1] == 1 && r[2] == 1 && r[3] == 1);
472 
473 	/* broadcast from uplink1 */
474 	bzero(r, sizeof(r));
475 	msg4.eh.ether_shost[5] = 1;
476 	memset(msg4.eh.ether_dhost, 0xff, sizeof(msg4.eh.ether_dhost));
477 	ng_send_data("u1", &msg4, sizeof(msg4));
478 	ng_handle_events(50, &r);
479 	ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 1 && r[3] == 1);
480 
481 	/* broadcast from link0 */
482 	bzero(r, sizeof(r));
483 	msg4.eh.ether_shost[5] = 0;
484 	ng_send_data("l0", &msg4, sizeof(msg4));
485 	ng_handle_events(50, &r);
486 	ATF_CHECK(r[0] == 0 && r[1] == 1 && r[2] == 1 && r[3] == 1);
487 
488 	/* inspect mac table */
489 	ng_register_msg(get_tablesize);
490 	rm.tok = ng_send_msg("bridge:", "gettable");
491 	rm.cnt = 0;
492 	ng_handle_events(50, &rm);
493 	ATF_CHECK(rm.cnt == 2);
494 
495 	ng_shutdown("bridge:");
496 }
497 
498 ATF_TC(uplink_classic);
499 ATF_TC_HEAD(uplink_classic, conf)
500 {
501 	atf_tc_set_md_var(conf, "require.user", "root");
502 }
503 
504 ATF_TC_BODY(uplink_classic, dummy)
505 {
506 	int		r[4];
507 	struct gettable	rm;
508 
509 	ng_init();
510 	ng_errors(PASS);
511 	ng_shutdown("bridge:");
512 
513 	ng_mkpeer(".", "l0", "bridge", "link0");
514 	if (errno > 0)
515 		atf_tc_skip("uplinks are not supported.");
516 	ng_errors(FAIL);
517 	ng_name("l0", "bridge");
518 	ng_register_data("l0", get_data0);
519 	ng_connect(".", "u1", "bridge:", "uplink1");
520 	ng_register_data("u1", get_data1);
521 	ng_connect(".", "u2", "bridge:", "uplink2");
522 	ng_register_data("u2", get_data2);
523 	ng_connect(".", "l3", "bridge:", "link3");
524 	ng_register_data("l3", get_data3);
525 
526 	/* unknown unicast 0 from uplink1 */
527 	bzero(r, sizeof(r));
528 	msg4.eh.ether_shost[5] = 1;
529 	ng_send_data("u1", &msg4, sizeof(msg4));
530 	ng_handle_events(50, &r);
531 	ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 1 && r[3] == 1);
532 
533 	/* unknown unicast 2 from link0 */
534 	bzero(r, sizeof(r));
535 	msg4.eh.ether_shost[5] = 0;
536 	msg4.eh.ether_dhost[5] = 2;
537 	ng_send_data("l0", &msg4, sizeof(msg4));
538 	ng_handle_events(50, &r);
539 	ATF_CHECK(r[0] == 0 && r[1] == 1 && r[2] == 1 && r[3] == 1);
540 
541 	/* known unicast 0 from uplink2 */
542 	bzero(r, sizeof(r));
543 	msg4.eh.ether_shost[5] = 2;
544 	msg4.eh.ether_dhost[5] = 0;
545 	ng_send_data("u2", &msg4, sizeof(msg4));
546 	ng_handle_events(50, &r);
547 	ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 0 && r[3] == 0);
548 
549 	/* known unicast 0 from link3 */
550 	bzero(r, sizeof(r));
551 	msg4.eh.ether_shost[5] = 3;
552 	msg4.eh.ether_dhost[5] = 0;
553 	ng_send_data("l3", &msg4, sizeof(msg4));
554 	ng_handle_events(50, &r);
555 	ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 0 && r[3] == 0);
556 
557 	/* (un)known unicast 2 from uplink1 */
558 	bzero(r, sizeof(r));
559 	msg4.eh.ether_shost[5] = 1;
560 	msg4.eh.ether_dhost[5] = 2;
561 	ng_send_data("u1", &msg4, sizeof(msg4));
562 	ng_handle_events(50, &r);
563 	ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 1 && r[3] == 1);
564 
565 	/* (un)known unicast 2 from link0 */
566 	bzero(r, sizeof(r));
567 	msg4.eh.ether_shost[5] = 0;
568 	ng_send_data("l0", &msg4, sizeof(msg4));
569 	ng_handle_events(50, &r);
570 	ATF_CHECK(r[0] == 0 && r[1] == 1 && r[2] == 1 && r[3] == 1);
571 
572 	/* unknown multicast 2 from uplink1 */
573 	bzero(r, sizeof(r));
574 	msg4.eh.ether_shost[5] = 1;
575 	msg4.eh.ether_dhost[0] = 0xff;
576 	ng_send_data("u1", &msg4, sizeof(msg4));
577 	ng_handle_events(50, &r);
578 	ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 1 && r[3] == 1);
579 
580 	/* unknown multicast 2 from link0 */
581 	bzero(r, sizeof(r));
582 	msg4.eh.ether_shost[5] = 0;
583 	ng_send_data("l0", &msg4, sizeof(msg4));
584 	ng_handle_events(50, &r);
585 	ATF_CHECK(r[0] == 0 && r[1] == 1 && r[2] == 1 && r[3] == 1);
586 
587 	/* broadcast from uplink1 */
588 	bzero(r, sizeof(r));
589 	msg4.eh.ether_shost[5] = 1;
590 	memset(msg4.eh.ether_dhost, 0xff, sizeof(msg4.eh.ether_dhost));
591 	ng_send_data("u1", &msg4, sizeof(msg4));
592 	ng_handle_events(50, &r);
593 	ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 1 && r[3] == 1);
594 
595 	/* broadcast from link0 */
596 	bzero(r, sizeof(r));
597 	msg4.eh.ether_shost[5] = 0;
598 	ng_send_data("l0", &msg4, sizeof(msg4));
599 	ng_handle_events(50, &r);
600 	ATF_CHECK(r[0] == 0 && r[1] == 1 && r[2] == 1 && r[3] == 1);
601 
602 	/* inspect mac table */
603 	ng_register_msg(get_tablesize);
604 	rm.tok = ng_send_msg("bridge:", "gettable");
605 	rm.cnt = 0;
606 	ng_handle_events(50, &rm);
607 	ATF_CHECK(rm.cnt == 2);
608 
609 	ng_shutdown("bridge:");
610 }
611 
612 ATF_TP_ADD_TCS(bridge)
613 {
614 	ATF_TP_ADD_TC(bridge, basic);
615 	ATF_TP_ADD_TC(bridge, loop);
616 	ATF_TP_ADD_TC(bridge, persistence);
617 	ATF_TP_ADD_TC(bridge, many_unicasts);
618 	ATF_TP_ADD_TC(bridge, many_broadcasts);
619 	ATF_TP_ADD_TC(bridge, uplink_private);
620 	ATF_TP_ADD_TC(bridge, uplink_classic);
621 
622 	return atf_no_error();
623 }
624 
625 static inline void
626 _get_data(void *data, size_t len, void *ctx, int i)
627 {
628 	int	       *cnt = ctx;
629 
630 	(void)data;
631 	fprintf(stderr, "[%d] Got %zu bytes of data.\n", i, len);
632 	cnt[i]++;
633 }
634 
635 #define GD(x) static void			\
636 get_data##x(void *data, size_t len, void *ctx) {\
637 	_get_data(data, len, ctx, x);		\
638 }
639 
640 GD(0)
641 GD(1)
642 GD(2)
643 GD(3)
644 
645 static void
646 get_tablesize(char const *source, struct ng_mesg *msg, void *ctx)
647 {
648 	struct gettable *rm = ctx;
649 	struct ng_bridge_host_ary *gt = (void *)msg->data;
650 
651 	fprintf(stderr, "Response from %s to query %d\n", source, msg->header.token);
652 	if (rm->tok == msg->header.token)
653 		rm->cnt = gt->numHosts;
654 }
655