xref: /freebsd/tests/sys/netgraph/bridge.c (revision 5ca8e32633c4ffbbcd6762e5888b6a4ba0708c6c)
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_tablesize(char const *source, struct ng_mesg *msg, void *ctx);
47 struct gettable
48 {
49 	u_int32_t	tok;
50 	int		cnt;
51 };
52 
53 struct frame4
54 {
55 	struct ether_header eh;
56 	struct ip	ip;
57 	char		data[64];
58 };
59 struct frame6
60 {
61 	struct ether_header eh;
62 	struct ip6_hdr	ip;
63 	char		data[64];
64 };
65 
66 static struct frame4 msg4 = {
67 	.ip.ip_v = 4,
68 	.ip.ip_hl = 5,
69 	.ip.ip_ttl = 1,
70 	.ip.ip_p = 254,
71 	.ip.ip_src = {htonl(0x0a00dead)},
72 	.ip.ip_dst = {htonl(0x0a00beef)},
73 	.ip.ip_len = 32,
74 	.eh.ether_type = ETHERTYPE_IP,
75 	.eh.ether_shost = {2, 4, 6},
76 	.eh.ether_dhost = {2, 4, 6},
77 };
78 
79 
80 ATF_TC(basic);
81 ATF_TC_HEAD(basic, conf)
82 {
83 	atf_tc_set_md_var(conf, "require.user", "root");
84 }
85 
86 ATF_TC_BODY(basic, dummy)
87 {
88 	ng_counter_t	r;
89 	struct gettable	rm;
90 
91 	ng_init();
92 	ng_errors(PASS);
93 	ng_shutdown("bridge:");
94 	ng_errors(FAIL);
95 
96 	ng_mkpeer(".", "a", "bridge", "link0");
97 	ng_name("a", "bridge");
98 	ng_connect(".", "b", "bridge:", "link1");
99 	ng_connect(".", "c", "bridge:", "link2");
100 
101 	/* do not bounce back */
102 	ng_register_data("a", get_data0);
103 	ng_counter_clear(r);
104 	msg4.eh.ether_shost[5] = 1;
105 	ng_send_data("a", &msg4, sizeof(msg4));
106 	ng_handle_events(50, &r);
107 	ATF_CHECK(r[0] == 0);
108 
109 	/* send to others */
110 	ng_register_data("b", get_data1);
111 	ng_register_data("c", get_data2);
112 	ng_counter_clear(r);
113 	msg4.eh.ether_shost[5] = 1;
114 	ng_send_data("a", &msg4, sizeof(msg4));
115 	ng_handle_events(50, &r);
116 	ATF_CHECK(r[0] == 0 && r[1] == 1 && r[2] == 1);
117 
118 	ng_counter_clear(r);
119 	msg4.eh.ether_shost[5] = 2;
120 	ng_send_data("b", &msg4, sizeof(msg4));
121 	ng_handle_events(50, &r);
122 	ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 1);
123 
124 	ng_counter_clear(r);
125 	msg4.eh.ether_shost[5] = 3;
126 	ng_send_data("c", &msg4, sizeof(msg4));
127 	ng_handle_events(50, &r);
128 	ATF_CHECK(r[0] == 1 && r[1] == 1 && r[2] == 0);
129 
130 	/* send to learned unicast */
131 	ng_counter_clear(r);
132 	msg4.eh.ether_shost[5] = 1;
133 	msg4.eh.ether_dhost[5] = 3;
134 	ng_send_data("a", &msg4, sizeof(msg4));
135 	ng_handle_events(50, &r);
136 	ATF_CHECK(r[0] == 0 && r[1] == 0 && r[2] == 1);
137 
138 	/* inspect mac table */
139 	ng_register_msg(get_tablesize);
140 	rm.tok = ng_send_msg("bridge:", "gettable");
141 	rm.cnt = 0;
142 	ng_handle_events(50, &rm);
143 	ATF_CHECK(rm.cnt == 3);
144 
145 	/* remove a link */
146 	ng_rmhook(".", "b");
147 	ng_counter_clear(r);
148 	msg4.eh.ether_shost[5] = 1;
149 	msg4.eh.ether_dhost[5] = 0;
150 	ng_send_data("a", &msg4, sizeof(msg4));
151 	ng_handle_events(50, &r);
152 	ATF_CHECK(r[0] == 0 && r[1] == 0 && r[2] == 1);
153 
154 	/* inspect mac table */
155 	ng_register_msg(get_tablesize);
156 	rm.tok = ng_send_msg("bridge:", "gettable");
157 	rm.cnt = 0;
158 	ng_handle_events(50, &rm);
159 	ATF_CHECK(rm.cnt == 2);
160 
161 	ng_shutdown("bridge:");
162 }
163 
164 ATF_TC(persistence);
165 ATF_TC_HEAD(persistence, conf)
166 {
167 	atf_tc_set_md_var(conf, "require.user", "root");
168 }
169 
170 ATF_TC_BODY(persistence, dummy)
171 {
172 	ng_init();
173 	ng_errors(PASS);
174 	ng_shutdown("bridge:");
175 	ng_errors(FAIL);
176 
177 	ng_mkpeer(".", "a", "bridge", "link0");
178 	ng_name("a", "bridge");
179 
180 	ng_send_msg("bridge:", "setpersistent");
181 	ng_rmhook(".", "a");
182 
183 	ng_shutdown("bridge:");
184 }
185 
186 ATF_TC(loop);
187 ATF_TC_HEAD(loop, conf)
188 {
189 	atf_tc_set_md_var(conf, "require.user", "root");
190 }
191 
192 ATF_TC_BODY(loop, dummy)
193 {
194 	ng_counter_t	r;
195 	int		i;
196 
197 	ng_init();
198 	ng_errors(PASS);
199 	ng_shutdown("bridge1:");
200 	ng_shutdown("bridge2:");
201 	ng_errors(FAIL);
202 
203 	ng_mkpeer(".", "a", "bridge", "link0");
204 	ng_name("a", "bridge1");
205 	ng_mkpeer(".", "b", "bridge", "link1");
206 	ng_name("b", "bridge2");
207 
208 	ng_register_data("a", get_data0);
209 	ng_register_data("b", get_data1);
210 
211 	/*-
212 	 * Open loop
213 	 *
214 	 *    /-- bridge1
215 	 * . <    |
216 	 *    \-- bridge2
217 	 */
218 	ng_connect("bridge1:", "link11", "bridge2:", "link11");
219 
220 	ng_counter_clear(r);
221 	msg4.eh.ether_shost[5] = 1;
222 	ng_send_data("a", &msg4, sizeof(msg4));
223 	ng_handle_events(50, &r);
224 	ATF_CHECK(r[0] == 0 && r[1] == 1);
225 
226 	/*-
227 	 * Closed loop, DANGEROUS!
228 	 *
229 	 *    /-- bridge1 -\
230 	 * . <     |       |
231 	 *    \-- bridge2 -/
232 	 */
233 	ng_connect("bridge1:", "link12", "bridge2:", "link12");
234 
235 	ng_counter_clear(r);
236 	msg4.eh.ether_shost[5] = 1;
237 	ng_errors(PASS);
238 	ng_send_data("a", &msg4, sizeof(msg4));
239 	ATF_CHECK_ERRNO(ELOOP, errno != 0);	/* loop might be detected */
240 	ng_errors(FAIL);
241 	for (i = 0; i < 10; i++)	/* don't run forever */
242 		if (!ng_handle_event(50, &r))
243 			break;
244 	ATF_CHECK(r[0] == 0 && r[1] == 1);
245 
246 	ng_shutdown("bridge1:");
247 	ng_shutdown("bridge2:");
248 }
249 
250 ATF_TC(many_unicasts);
251 ATF_TC_HEAD(many_unicasts, conf)
252 {
253 	atf_tc_set_md_var(conf, "require.user", "root");
254 }
255 
256 ATF_TC_BODY(many_unicasts, dummy)
257 {
258 	ng_counter_t	r;
259 	int		i;
260 	const int	HOOKS = 1000;
261 	struct gettable	rm;
262 
263 	ng_init();
264 	ng_errors(PASS);
265 	ng_shutdown("bridge:");
266 	ng_errors(FAIL);
267 
268 	ng_mkpeer(".", "a", "bridge", "link0");
269 	ng_name("a", "bridge");
270 	ng_register_data("a", get_data0);
271 
272 	/* learn MAC */
273 	ng_counter_clear(r);
274 	msg4.eh.ether_shost[3] = 0xff;
275 	ng_send_data("a", &msg4, sizeof(msg4));
276 	ng_handle_events(50, &r);
277 	ATF_CHECK(r[0] == 0);
278 
279 	/* use learned MAC as destination */
280 	msg4.eh.ether_shost[3] = 0;
281 	msg4.eh.ether_dhost[3] = 0xff;
282 
283 	/* now send */
284 	ng_counter_clear(r);
285 	for (i = 1; i <= HOOKS; i++)
286 	{
287 		char		hook[20];
288 
289 		snprintf(hook, sizeof(hook), "link%d", i);
290 		ng_connect(".", hook, "bridge:", hook);
291 		ng_register_data(hook, get_data2);
292 
293 		msg4.eh.ether_shost[4] = i >> 8;
294 		msg4.eh.ether_shost[5] = i & 0xff;
295 		ng_errors(PASS);
296 		ng_send_data(hook, &msg4, sizeof(msg4));
297 		ng_errors(FAIL);
298 		if (errno != 0)
299 			break;
300 		ng_handle_events(50, &r);
301 	}
302 	ATF_CHECK(r[0] == HOOKS && r[2] == 0);
303 
304 	/* inspect mac table */
305 	ng_register_msg(get_tablesize);
306 	rm.cnt = 0;
307 	ng_errors(PASS);
308 	rm.tok = ng_send_msg("bridge:", "gettable");
309 	ng_errors(FAIL);
310 	if (rm.tok == (u_int32_t)-1)
311 	{
312 		ATF_CHECK_ERRNO(ENOBUFS, 1);
313 		atf_tc_expect_fail("response too large");
314 	}
315 	ng_handle_events(50, &rm);
316 	ATF_CHECK(rm.cnt == HOOKS + 1);
317 	atf_tc_expect_pass();
318 
319 	ng_shutdown("bridge:");
320 }
321 
322 ATF_TC(many_broadcasts);
323 ATF_TC_HEAD(many_broadcasts, conf)
324 {
325 	atf_tc_set_md_var(conf, "require.user", "root");
326 }
327 
328 ATF_TC_BODY(many_broadcasts, dummy)
329 {
330 	ng_counter_t	r;
331 	int		i;
332 	const int	HOOKS = 1000;
333 
334 	ng_init();
335 	ng_errors(PASS);
336 	ng_shutdown("bridge:");
337 	ng_errors(FAIL);
338 
339 	ng_mkpeer(".", "a", "bridge", "link0");
340 	ng_name("a", "bridge");
341 	ng_register_data("a", get_data0);
342 
343 	/* learn MAC */
344 	ng_counter_clear(r);
345 	msg4.eh.ether_shost[3] = 0xff;
346 	ng_send_data("a", &msg4, sizeof(msg4));
347 	ng_handle_events(50, &r);
348 	ATF_CHECK(r[0] == 0);
349 
350 	/* use broadcast MAC */
351 	msg4.eh.ether_shost[3] = 0;
352 	memset(msg4.eh.ether_dhost, 0xff, sizeof(msg4.eh.ether_dhost));
353 
354 	/* now send */
355 	ng_counter_clear(r);
356 	for (i = 1; i <= HOOKS; i++)
357 	{
358 		char		hook[20];
359 
360 		snprintf(hook, sizeof(hook), "link%d", i);
361 		ng_connect(".", hook, "bridge:", hook);
362 		ng_register_data(hook, get_data3);
363 
364 		msg4.eh.ether_shost[4] = i >> 8;
365 		msg4.eh.ether_shost[5] = i & 0xff;
366 		ng_errors(PASS);
367 		ng_send_data(hook, &msg4, sizeof(msg4));
368 		ng_errors(FAIL);
369 		if (errno != 0)
370 			break;
371 		ng_handle_events(50, &r);
372 	}
373 	ATF_CHECK(r[0] > 100 && r[3] > 100);
374 	if (i < HOOKS)
375 		atf_tc_expect_fail("netgraph queue full (%d)", i);
376 	ATF_CHECK(r[0] == HOOKS);
377 	atf_tc_expect_pass();
378 
379 	ng_shutdown("bridge:");
380 }
381 
382 ATF_TC(uplink_private);
383 ATF_TC_HEAD(uplink_private, conf)
384 {
385 	atf_tc_set_md_var(conf, "require.user", "root");
386 }
387 
388 ATF_TC_BODY(uplink_private, dummy)
389 {
390 	ng_counter_t	r;
391 	struct gettable	rm;
392 
393 	ng_init();
394 	ng_errors(PASS);
395 	ng_shutdown("bridge:");
396 
397 	ng_mkpeer(".", "u1", "bridge", "uplink1");
398 	if (errno > 0)
399 		atf_tc_skip("uplinks are not supported.");
400 	ng_errors(FAIL);
401 	ng_name("u1", "bridge");
402 	ng_register_data("u1", get_data1);
403 	ng_connect(".", "u2", "bridge:", "uplink2");
404 	ng_register_data("u2", get_data2);
405 	ng_connect(".", "l0", "bridge:", "link0");
406 	ng_register_data("l0", get_data0);
407 	ng_connect(".", "l3", "bridge:", "link3");
408 	ng_register_data("l3", get_data3);
409 
410 	/* unknown unicast 0 from uplink1 */
411 	ng_counter_clear(r);
412 	msg4.eh.ether_shost[5] = 1;
413 	ng_send_data("u1", &msg4, sizeof(msg4));
414 	ng_handle_events(50, &r);
415 	ATF_CHECK(r[0] == 0 && r[1] == 0 && r[2] == 1 && r[3] == 0);
416 
417 	/* unknown unicast 2 from link0 */
418 	ng_counter_clear(r);
419 	msg4.eh.ether_shost[5] = 0;
420 	msg4.eh.ether_dhost[5] = 2;
421 	ng_send_data("l0", &msg4, sizeof(msg4));
422 	ng_handle_events(50, &r);
423 	ATF_CHECK(r[0] == 0 && r[1] == 1 && r[2] == 1 && r[3] == 0);
424 
425 	/* known unicast 0 from uplink2 */
426 	ng_counter_clear(r);
427 	msg4.eh.ether_shost[5] = 2;
428 	msg4.eh.ether_dhost[5] = 0;
429 	ng_send_data("u2", &msg4, sizeof(msg4));
430 	ng_handle_events(50, &r);
431 	ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 0 && r[3] == 0);
432 
433 	/* known unicast 0 from link3 */
434 	ng_counter_clear(r);
435 	msg4.eh.ether_shost[5] = 3;
436 	msg4.eh.ether_dhost[5] = 0;
437 	ng_send_data("l3", &msg4, sizeof(msg4));
438 	ng_handle_events(50, &r);
439 	ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 0 && r[3] == 0);
440 
441 	/* (un)known unicast 2 from uplink1 */
442 	ng_counter_clear(r);
443 	msg4.eh.ether_shost[5] = 1;
444 	msg4.eh.ether_dhost[5] = 2;
445 	ng_send_data("u1", &msg4, sizeof(msg4));
446 	ng_handle_events(50, &r);
447 	ATF_CHECK(r[0] == 0 && r[1] == 0 && r[2] == 1 && r[3] == 0);
448 
449 	/* (un)known unicast 2 from link0 */
450 	ng_counter_clear(r);
451 	msg4.eh.ether_shost[5] = 0;
452 	ng_send_data("l0", &msg4, sizeof(msg4));
453 	ng_handle_events(50, &r);
454 	ATF_CHECK(r[0] == 0 && r[1] == 1 && r[2] == 1 && r[3] == 0);
455 
456 	/* unknown multicast 2 from uplink1 */
457 	ng_counter_clear(r);
458 	msg4.eh.ether_shost[5] = 1;
459 	msg4.eh.ether_dhost[0] = 0xff;
460 	ng_send_data("u1", &msg4, sizeof(msg4));
461 	ng_handle_events(50, &r);
462 	ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 1 && r[3] == 1);
463 
464 	/* unknown multicast 2 from link0 */
465 	ng_counter_clear(r);
466 	msg4.eh.ether_shost[5] = 0;
467 	ng_send_data("l0", &msg4, sizeof(msg4));
468 	ng_handle_events(50, &r);
469 	ATF_CHECK(r[0] == 0 && r[1] == 1 && r[2] == 1 && r[3] == 1);
470 
471 	/* broadcast from uplink1 */
472 	ng_counter_clear(r);
473 	msg4.eh.ether_shost[5] = 1;
474 	memset(msg4.eh.ether_dhost, 0xff, sizeof(msg4.eh.ether_dhost));
475 	ng_send_data("u1", &msg4, sizeof(msg4));
476 	ng_handle_events(50, &r);
477 	ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 1 && r[3] == 1);
478 
479 	/* broadcast from link0 */
480 	ng_counter_clear(r);
481 	msg4.eh.ether_shost[5] = 0;
482 	ng_send_data("l0", &msg4, sizeof(msg4));
483 	ng_handle_events(50, &r);
484 	ATF_CHECK(r[0] == 0 && r[1] == 1 && r[2] == 1 && r[3] == 1);
485 
486 	/* inspect mac table */
487 	ng_register_msg(get_tablesize);
488 	rm.tok = ng_send_msg("bridge:", "gettable");
489 	rm.cnt = 0;
490 	ng_handle_events(50, &rm);
491 	ATF_CHECK(rm.cnt == 2);
492 
493 	ng_shutdown("bridge:");
494 }
495 
496 ATF_TC(uplink_classic);
497 ATF_TC_HEAD(uplink_classic, conf)
498 {
499 	atf_tc_set_md_var(conf, "require.user", "root");
500 }
501 
502 ATF_TC_BODY(uplink_classic, dummy)
503 {
504 	ng_counter_t	r;
505 	struct gettable	rm;
506 
507 	ng_init();
508 	ng_errors(PASS);
509 	ng_shutdown("bridge:");
510 
511 	ng_mkpeer(".", "l0", "bridge", "link0");
512 	if (errno > 0)
513 		atf_tc_skip("uplinks are not supported.");
514 	ng_errors(FAIL);
515 	ng_name("l0", "bridge");
516 	ng_register_data("l0", get_data0);
517 	ng_connect(".", "u1", "bridge:", "uplink1");
518 	ng_register_data("u1", get_data1);
519 	ng_connect(".", "u2", "bridge:", "uplink2");
520 	ng_register_data("u2", get_data2);
521 	ng_connect(".", "l3", "bridge:", "link3");
522 	ng_register_data("l3", get_data3);
523 
524 	/* unknown unicast 0 from uplink1 */
525 	ng_counter_clear(r);
526 	msg4.eh.ether_shost[5] = 1;
527 	ng_send_data("u1", &msg4, sizeof(msg4));
528 	ng_handle_events(50, &r);
529 	ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 1 && r[3] == 1);
530 
531 	/* unknown unicast 2 from link0 */
532 	ng_counter_clear(r);
533 	msg4.eh.ether_shost[5] = 0;
534 	msg4.eh.ether_dhost[5] = 2;
535 	ng_send_data("l0", &msg4, sizeof(msg4));
536 	ng_handle_events(50, &r);
537 	ATF_CHECK(r[0] == 0 && r[1] == 1 && r[2] == 1 && r[3] == 1);
538 
539 	/* known unicast 0 from uplink2 */
540 	ng_counter_clear(r);
541 	msg4.eh.ether_shost[5] = 2;
542 	msg4.eh.ether_dhost[5] = 0;
543 	ng_send_data("u2", &msg4, sizeof(msg4));
544 	ng_handle_events(50, &r);
545 	ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 0 && r[3] == 0);
546 
547 	/* known unicast 0 from link3 */
548 	ng_counter_clear(r);
549 	msg4.eh.ether_shost[5] = 3;
550 	msg4.eh.ether_dhost[5] = 0;
551 	ng_send_data("l3", &msg4, sizeof(msg4));
552 	ng_handle_events(50, &r);
553 	ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 0 && r[3] == 0);
554 
555 	/* (un)known unicast 2 from uplink1 */
556 	ng_counter_clear(r);
557 	msg4.eh.ether_shost[5] = 1;
558 	msg4.eh.ether_dhost[5] = 2;
559 	ng_send_data("u1", &msg4, sizeof(msg4));
560 	ng_handle_events(50, &r);
561 	ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 1 && r[3] == 1);
562 
563 	/* (un)known unicast 2 from link0 */
564 	ng_counter_clear(r);
565 	msg4.eh.ether_shost[5] = 0;
566 	ng_send_data("l0", &msg4, sizeof(msg4));
567 	ng_handle_events(50, &r);
568 	ATF_CHECK(r[0] == 0 && r[1] == 1 && r[2] == 1 && r[3] == 1);
569 
570 	/* unknown multicast 2 from uplink1 */
571 	ng_counter_clear(r);
572 	msg4.eh.ether_shost[5] = 1;
573 	msg4.eh.ether_dhost[0] = 0xff;
574 	ng_send_data("u1", &msg4, sizeof(msg4));
575 	ng_handle_events(50, &r);
576 	ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 1 && r[3] == 1);
577 
578 	/* unknown multicast 2 from link0 */
579 	ng_counter_clear(r);
580 	msg4.eh.ether_shost[5] = 0;
581 	ng_send_data("l0", &msg4, sizeof(msg4));
582 	ng_handle_events(50, &r);
583 	ATF_CHECK(r[0] == 0 && r[1] == 1 && r[2] == 1 && r[3] == 1);
584 
585 	/* broadcast from uplink1 */
586 	ng_counter_clear(r);
587 	msg4.eh.ether_shost[5] = 1;
588 	memset(msg4.eh.ether_dhost, 0xff, sizeof(msg4.eh.ether_dhost));
589 	ng_send_data("u1", &msg4, sizeof(msg4));
590 	ng_handle_events(50, &r);
591 	ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 1 && r[3] == 1);
592 
593 	/* broadcast from link0 */
594 	ng_counter_clear(r);
595 	msg4.eh.ether_shost[5] = 0;
596 	ng_send_data("l0", &msg4, sizeof(msg4));
597 	ng_handle_events(50, &r);
598 	ATF_CHECK(r[0] == 0 && r[1] == 1 && r[2] == 1 && r[3] == 1);
599 
600 	/* inspect mac table */
601 	ng_register_msg(get_tablesize);
602 	rm.tok = ng_send_msg("bridge:", "gettable");
603 	rm.cnt = 0;
604 	ng_handle_events(50, &rm);
605 	ATF_CHECK(rm.cnt == 2);
606 
607 	ng_shutdown("bridge:");
608 }
609 
610 ATF_TP_ADD_TCS(bridge)
611 {
612 	ATF_TP_ADD_TC(bridge, basic);
613 	ATF_TP_ADD_TC(bridge, loop);
614 	ATF_TP_ADD_TC(bridge, persistence);
615 	ATF_TP_ADD_TC(bridge, many_unicasts);
616 	ATF_TP_ADD_TC(bridge, many_broadcasts);
617 	ATF_TP_ADD_TC(bridge, uplink_private);
618 	ATF_TP_ADD_TC(bridge, uplink_classic);
619 
620 	return atf_no_error();
621 }
622 
623 static void
624 get_tablesize(char const *source, struct ng_mesg *msg, void *ctx)
625 {
626 	struct gettable *rm = ctx;
627 	struct ng_bridge_host_ary *gt = (void *)msg->data;
628 
629 	fprintf(stderr, "Response from %s to query %d\n", source, msg->header.token);
630 	if (rm->tok == msg->header.token)
631 		rm->cnt = gt->numHosts;
632 }
633