1370007Sdonner/*-
2370007Sdonner * SPDX-License-Identifier: BSD-3-Clause
3370007Sdonner *
4370007Sdonner * Copyright 2021 Lutz Donnerhacke
5370007Sdonner *
6370007Sdonner * Redistribution and use in source and binary forms, with or without
7370007Sdonner * modification, are permitted provided that the following conditions
8370007Sdonner * are met:
9370007Sdonner *
10370007Sdonner * 1. Redistributions of source code must retain the above copyright
11370007Sdonner *    notice, this list of conditions and the following disclaimer.
12370007Sdonner * 2. Redistributions in binary form must reproduce the above
13370007Sdonner *    copyright notice, this list of conditions and the following
14370007Sdonner *    disclaimer in the documentation and/or other materials provided
15370007Sdonner *    with the distribution.
16370007Sdonner * 3. Neither the name of the copyright holder nor the names of its
17370007Sdonner *    contributors may be used to endorse or promote products derived
18370007Sdonner *    from this software without specific prior written permission.
19370007Sdonner *
20370007Sdonner * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
21370007Sdonner * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
22370007Sdonner * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
23370007Sdonner * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24370007Sdonner * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
25370007Sdonner * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
26370007Sdonner * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
27370007Sdonner * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28370007Sdonner * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
29370007Sdonner * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
30370007Sdonner * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
31370007Sdonner * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32370007Sdonner * SUCH DAMAGE.
33370007Sdonner */
34370007Sdonner#include <atf-c.h>
35370007Sdonner#include <errno.h>
36370007Sdonner#include <stdio.h>
37370007Sdonner
38370007Sdonner#include <net/ethernet.h>
39370007Sdonner#include <netinet/in.h>
40370007Sdonner#include <netinet/ip.h>
41370007Sdonner#include <netinet/ip6.h>
42370007Sdonner
43370007Sdonner#include "util.h"
44370007Sdonner#include <netgraph/ng_bridge.h>
45370007Sdonner
46370007Sdonnerstatic void	get_tablesize(char const *source, struct ng_mesg *msg, void *ctx);
47370007Sdonnerstruct gettable
48370007Sdonner{
49370007Sdonner	u_int32_t	tok;
50370007Sdonner	int		cnt;
51370007Sdonner};
52370007Sdonner
53370007Sdonnerstruct frame4
54370007Sdonner{
55370007Sdonner	struct ether_header eh;
56370007Sdonner	struct ip	ip;
57370007Sdonner	char		data[64];
58370007Sdonner};
59370007Sdonnerstruct frame6
60370007Sdonner{
61370007Sdonner	struct ether_header eh;
62370007Sdonner	struct ip6_hdr	ip;
63370007Sdonner	char		data[64];
64370007Sdonner};
65370007Sdonner
66370007Sdonnerstatic struct frame4 msg4 = {
67370007Sdonner	.ip.ip_v = 4,
68370007Sdonner	.ip.ip_hl = 5,
69370007Sdonner	.ip.ip_ttl = 1,
70370007Sdonner	.ip.ip_p = 254,
71370007Sdonner	.ip.ip_src = {htonl(0x0a00dead)},
72370007Sdonner	.ip.ip_dst = {htonl(0x0a00beef)},
73370007Sdonner	.ip.ip_len = 32,
74370007Sdonner	.eh.ether_type = ETHERTYPE_IP,
75370007Sdonner	.eh.ether_shost = {2, 4, 6},
76370007Sdonner	.eh.ether_dhost = {2, 4, 6},
77370007Sdonner};
78370007Sdonner
79370007Sdonner
80370007SdonnerATF_TC(basic);
81370007SdonnerATF_TC_HEAD(basic, conf)
82370007Sdonner{
83370007Sdonner	atf_tc_set_md_var(conf, "require.user", "root");
84370007Sdonner}
85370007Sdonner
86370007SdonnerATF_TC_BODY(basic, dummy)
87370007Sdonner{
88370007Sdonner	ng_counter_t	r;
89370007Sdonner	struct gettable	rm;
90370007Sdonner
91370007Sdonner	ng_init();
92370007Sdonner	ng_errors(PASS);
93370007Sdonner	ng_shutdown("bridge:");
94370007Sdonner	ng_errors(FAIL);
95370007Sdonner
96370007Sdonner	ng_mkpeer(".", "a", "bridge", "link0");
97370007Sdonner	ng_name("a", "bridge");
98370007Sdonner	ng_connect(".", "b", "bridge:", "link1");
99370007Sdonner	ng_connect(".", "c", "bridge:", "link2");
100370007Sdonner
101370007Sdonner	/* do not bounce back */
102370007Sdonner	ng_register_data("a", get_data0);
103370007Sdonner	ng_counter_clear(r);
104370007Sdonner	msg4.eh.ether_shost[5] = 1;
105370007Sdonner	ng_send_data("a", &msg4, sizeof(msg4));
106370007Sdonner	ng_handle_events(50, &r);
107370007Sdonner	ATF_CHECK(r[0] == 0);
108370007Sdonner
109370007Sdonner	/* send to others */
110370007Sdonner	ng_register_data("b", get_data1);
111370007Sdonner	ng_register_data("c", get_data2);
112370007Sdonner	ng_counter_clear(r);
113370007Sdonner	msg4.eh.ether_shost[5] = 1;
114370007Sdonner	ng_send_data("a", &msg4, sizeof(msg4));
115370007Sdonner	ng_handle_events(50, &r);
116370007Sdonner	ATF_CHECK(r[0] == 0 && r[1] == 1 && r[2] == 1);
117370007Sdonner
118370007Sdonner	ng_counter_clear(r);
119370007Sdonner	msg4.eh.ether_shost[5] = 2;
120370007Sdonner	ng_send_data("b", &msg4, sizeof(msg4));
121370007Sdonner	ng_handle_events(50, &r);
122370007Sdonner	ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 1);
123370007Sdonner
124370007Sdonner	ng_counter_clear(r);
125370007Sdonner	msg4.eh.ether_shost[5] = 3;
126370007Sdonner	ng_send_data("c", &msg4, sizeof(msg4));
127370007Sdonner	ng_handle_events(50, &r);
128370007Sdonner	ATF_CHECK(r[0] == 1 && r[1] == 1 && r[2] == 0);
129370007Sdonner
130370007Sdonner	/* send to learned unicast */
131370007Sdonner	ng_counter_clear(r);
132370007Sdonner	msg4.eh.ether_shost[5] = 1;
133370007Sdonner	msg4.eh.ether_dhost[5] = 3;
134370007Sdonner	ng_send_data("a", &msg4, sizeof(msg4));
135370007Sdonner	ng_handle_events(50, &r);
136370007Sdonner	ATF_CHECK(r[0] == 0 && r[1] == 0 && r[2] == 1);
137370007Sdonner
138370007Sdonner	/* inspect mac table */
139370007Sdonner	ng_register_msg(get_tablesize);
140370007Sdonner	rm.tok = ng_send_msg("bridge:", "gettable");
141370007Sdonner	rm.cnt = 0;
142370007Sdonner	ng_handle_events(50, &rm);
143370007Sdonner	ATF_CHECK(rm.cnt == 3);
144370007Sdonner
145370007Sdonner	/* remove a link */
146370007Sdonner	ng_rmhook(".", "b");
147370007Sdonner	ng_counter_clear(r);
148370007Sdonner	msg4.eh.ether_shost[5] = 1;
149370007Sdonner	msg4.eh.ether_dhost[5] = 0;
150370007Sdonner	ng_send_data("a", &msg4, sizeof(msg4));
151370007Sdonner	ng_handle_events(50, &r);
152370007Sdonner	ATF_CHECK(r[0] == 0 && r[1] == 0 && r[2] == 1);
153370007Sdonner
154370007Sdonner	/* inspect mac table */
155370007Sdonner	ng_register_msg(get_tablesize);
156370007Sdonner	rm.tok = ng_send_msg("bridge:", "gettable");
157370007Sdonner	rm.cnt = 0;
158370007Sdonner	ng_handle_events(50, &rm);
159370007Sdonner	ATF_CHECK(rm.cnt == 2);
160370007Sdonner
161370007Sdonner	ng_shutdown("bridge:");
162370007Sdonner}
163370007Sdonner
164370007SdonnerATF_TC(persistence);
165370007SdonnerATF_TC_HEAD(persistence, conf)
166370007Sdonner{
167370007Sdonner	atf_tc_set_md_var(conf, "require.user", "root");
168370007Sdonner}
169370007Sdonner
170370007SdonnerATF_TC_BODY(persistence, dummy)
171370007Sdonner{
172370007Sdonner	ng_init();
173370007Sdonner	ng_errors(PASS);
174370007Sdonner	ng_shutdown("bridge:");
175370007Sdonner	ng_errors(FAIL);
176370007Sdonner
177370007Sdonner	ng_mkpeer(".", "a", "bridge", "link0");
178370007Sdonner	ng_name("a", "bridge");
179370007Sdonner
180370007Sdonner	ng_send_msg("bridge:", "setpersistent");
181370007Sdonner	ng_rmhook(".", "a");
182370007Sdonner
183370007Sdonner	ng_shutdown("bridge:");
184370007Sdonner}
185370007Sdonner
186370007SdonnerATF_TC(loop);
187370007SdonnerATF_TC_HEAD(loop, conf)
188370007Sdonner{
189370007Sdonner	atf_tc_set_md_var(conf, "require.user", "root");
190370007Sdonner}
191370007Sdonner
192370007SdonnerATF_TC_BODY(loop, dummy)
193370007Sdonner{
194370007Sdonner	ng_counter_t	r;
195370007Sdonner	int		i;
196370007Sdonner
197370007Sdonner	ng_init();
198370007Sdonner	ng_errors(PASS);
199370007Sdonner	ng_shutdown("bridge1:");
200370007Sdonner	ng_shutdown("bridge2:");
201370007Sdonner	ng_errors(FAIL);
202370007Sdonner
203370007Sdonner	ng_mkpeer(".", "a", "bridge", "link0");
204370007Sdonner	ng_name("a", "bridge1");
205370007Sdonner	ng_mkpeer(".", "b", "bridge", "link1");
206370007Sdonner	ng_name("b", "bridge2");
207370007Sdonner
208370007Sdonner	ng_register_data("a", get_data0);
209370007Sdonner	ng_register_data("b", get_data1);
210370007Sdonner
211370007Sdonner	/*-
212370007Sdonner	 * Open loop
213370007Sdonner	 *
214370007Sdonner	 *    /-- bridge1
215370007Sdonner	 * . <    |
216370007Sdonner	 *    \-- bridge2
217370007Sdonner	 */
218370007Sdonner	ng_connect("bridge1:", "link11", "bridge2:", "link11");
219370007Sdonner
220370007Sdonner	ng_counter_clear(r);
221370007Sdonner	msg4.eh.ether_shost[5] = 1;
222370007Sdonner	ng_send_data("a", &msg4, sizeof(msg4));
223370007Sdonner	ng_handle_events(50, &r);
224370007Sdonner	ATF_CHECK(r[0] == 0 && r[1] == 1);
225370007Sdonner
226370007Sdonner	/*-
227370007Sdonner	 * Closed loop, DANGEROUS!
228370007Sdonner	 *
229370007Sdonner	 *    /-- bridge1 -\
230370007Sdonner	 * . <     |       |
231370007Sdonner	 *    \-- bridge2 -/
232370007Sdonner	 */
233370007Sdonner	ng_connect("bridge1:", "link12", "bridge2:", "link12");
234370007Sdonner
235370007Sdonner	ng_counter_clear(r);
236370007Sdonner	msg4.eh.ether_shost[5] = 1;
237370007Sdonner	ng_errors(PASS);
238370007Sdonner	ng_send_data("a", &msg4, sizeof(msg4));
239370007Sdonner	ATF_CHECK_ERRNO(ELOOP, errno != 0);	/* loop might be detected */
240370007Sdonner	ng_errors(FAIL);
241370007Sdonner	for (i = 0; i < 10; i++)	/* don't run forever */
242370007Sdonner		if (!ng_handle_event(50, &r))
243370007Sdonner			break;
244370007Sdonner	ATF_CHECK(r[0] == 0 && r[1] == 1);
245370007Sdonner
246370007Sdonner	ng_shutdown("bridge1:");
247370007Sdonner	ng_shutdown("bridge2:");
248370007Sdonner}
249370007Sdonner
250370007SdonnerATF_TC(many_unicasts);
251370007SdonnerATF_TC_HEAD(many_unicasts, conf)
252370007Sdonner{
253370007Sdonner	atf_tc_set_md_var(conf, "require.user", "root");
254370007Sdonner}
255370007Sdonner
256370007SdonnerATF_TC_BODY(many_unicasts, dummy)
257370007Sdonner{
258370007Sdonner	ng_counter_t	r;
259370007Sdonner	int		i;
260370007Sdonner	const int	HOOKS = 1000;
261370007Sdonner	struct gettable	rm;
262370007Sdonner
263370007Sdonner	ng_init();
264370007Sdonner	ng_errors(PASS);
265370007Sdonner	ng_shutdown("bridge:");
266370007Sdonner	ng_errors(FAIL);
267370007Sdonner
268370007Sdonner	ng_mkpeer(".", "a", "bridge", "link0");
269370007Sdonner	ng_name("a", "bridge");
270370007Sdonner	ng_register_data("a", get_data0);
271370007Sdonner
272370007Sdonner	/* learn MAC */
273370007Sdonner	ng_counter_clear(r);
274370007Sdonner	msg4.eh.ether_shost[3] = 0xff;
275370007Sdonner	ng_send_data("a", &msg4, sizeof(msg4));
276370007Sdonner	ng_handle_events(50, &r);
277370007Sdonner	ATF_CHECK(r[0] == 0);
278370007Sdonner
279370007Sdonner	/* use learned MAC as destination */
280370007Sdonner	msg4.eh.ether_shost[3] = 0;
281370007Sdonner	msg4.eh.ether_dhost[3] = 0xff;
282370007Sdonner
283370007Sdonner	/* now send */
284370007Sdonner	ng_counter_clear(r);
285370007Sdonner	for (i = 1; i <= HOOKS; i++)
286370007Sdonner	{
287370007Sdonner		char		hook[20];
288370007Sdonner
289370007Sdonner		snprintf(hook, sizeof(hook), "link%d", i);
290370007Sdonner		ng_connect(".", hook, "bridge:", hook);
291370007Sdonner		ng_register_data(hook, get_data2);
292370007Sdonner
293370007Sdonner		msg4.eh.ether_shost[4] = i >> 8;
294370007Sdonner		msg4.eh.ether_shost[5] = i & 0xff;
295370007Sdonner		ng_errors(PASS);
296370007Sdonner		ng_send_data(hook, &msg4, sizeof(msg4));
297370007Sdonner		ng_errors(FAIL);
298370007Sdonner		if (errno != 0)
299370007Sdonner			break;
300370007Sdonner		ng_handle_events(50, &r);
301370007Sdonner	}
302370007Sdonner	ATF_CHECK(r[0] == HOOKS && r[2] == 0);
303370007Sdonner
304370007Sdonner	/* inspect mac table */
305370007Sdonner	ng_register_msg(get_tablesize);
306370007Sdonner	rm.cnt = 0;
307370007Sdonner	ng_errors(PASS);
308370007Sdonner	rm.tok = ng_send_msg("bridge:", "gettable");
309370007Sdonner	ng_errors(FAIL);
310370007Sdonner	if (rm.tok == (u_int32_t)-1)
311370007Sdonner	{
312370007Sdonner		ATF_CHECK_ERRNO(ENOBUFS, 1);
313370007Sdonner		atf_tc_expect_fail("response too large");
314370007Sdonner	}
315370007Sdonner	ng_handle_events(50, &rm);
316370007Sdonner	ATF_CHECK(rm.cnt == HOOKS + 1);
317370007Sdonner	atf_tc_expect_pass();
318370007Sdonner
319370007Sdonner	ng_shutdown("bridge:");
320370007Sdonner}
321370007Sdonner
322370007SdonnerATF_TC(many_broadcasts);
323370007SdonnerATF_TC_HEAD(many_broadcasts, conf)
324370007Sdonner{
325370007Sdonner	atf_tc_set_md_var(conf, "require.user", "root");
326370007Sdonner}
327370007Sdonner
328370007SdonnerATF_TC_BODY(many_broadcasts, dummy)
329370007Sdonner{
330370007Sdonner	ng_counter_t	r;
331370007Sdonner	int		i;
332370007Sdonner	const int	HOOKS = 1000;
333370007Sdonner
334370007Sdonner	ng_init();
335370007Sdonner	ng_errors(PASS);
336370007Sdonner	ng_shutdown("bridge:");
337370007Sdonner	ng_errors(FAIL);
338370007Sdonner
339370007Sdonner	ng_mkpeer(".", "a", "bridge", "link0");
340370007Sdonner	ng_name("a", "bridge");
341370007Sdonner	ng_register_data("a", get_data0);
342370007Sdonner
343370007Sdonner	/* learn MAC */
344370007Sdonner	ng_counter_clear(r);
345370007Sdonner	msg4.eh.ether_shost[3] = 0xff;
346370007Sdonner	ng_send_data("a", &msg4, sizeof(msg4));
347370007Sdonner	ng_handle_events(50, &r);
348370007Sdonner	ATF_CHECK(r[0] == 0);
349370007Sdonner
350370007Sdonner	/* use broadcast MAC */
351370007Sdonner	msg4.eh.ether_shost[3] = 0;
352370007Sdonner	memset(msg4.eh.ether_dhost, 0xff, sizeof(msg4.eh.ether_dhost));
353370007Sdonner
354370007Sdonner	/* now send */
355370007Sdonner	ng_counter_clear(r);
356370007Sdonner	for (i = 1; i <= HOOKS; i++)
357370007Sdonner	{
358370007Sdonner		char		hook[20];
359370007Sdonner
360370007Sdonner		snprintf(hook, sizeof(hook), "link%d", i);
361370007Sdonner		ng_connect(".", hook, "bridge:", hook);
362370007Sdonner		ng_register_data(hook, get_data3);
363370007Sdonner
364370007Sdonner		msg4.eh.ether_shost[4] = i >> 8;
365370007Sdonner		msg4.eh.ether_shost[5] = i & 0xff;
366370007Sdonner		ng_errors(PASS);
367370007Sdonner		ng_send_data(hook, &msg4, sizeof(msg4));
368370007Sdonner		ng_errors(FAIL);
369370007Sdonner		if (errno != 0)
370370007Sdonner			break;
371370007Sdonner		ng_handle_events(50, &r);
372370007Sdonner	}
373370007Sdonner	ATF_CHECK(r[0] > 100 && r[3] > 100);
374370007Sdonner	if (i < HOOKS)
375370007Sdonner		atf_tc_expect_fail("netgraph queue full (%d)", i);
376370007Sdonner	ATF_CHECK(r[0] == HOOKS);
377370007Sdonner	atf_tc_expect_pass();
378370007Sdonner
379370007Sdonner	ng_shutdown("bridge:");
380370007Sdonner}
381370007Sdonner
382370007SdonnerATF_TC(uplink_private);
383370007SdonnerATF_TC_HEAD(uplink_private, conf)
384370007Sdonner{
385370007Sdonner	atf_tc_set_md_var(conf, "require.user", "root");
386370007Sdonner}
387370007Sdonner
388370007SdonnerATF_TC_BODY(uplink_private, dummy)
389370007Sdonner{
390370007Sdonner	ng_counter_t	r;
391370007Sdonner	struct gettable	rm;
392370007Sdonner
393370007Sdonner	ng_init();
394370007Sdonner	ng_errors(PASS);
395370007Sdonner	ng_shutdown("bridge:");
396370007Sdonner
397370007Sdonner	ng_mkpeer(".", "u1", "bridge", "uplink1");
398370007Sdonner	if (errno > 0)
399370007Sdonner		atf_tc_skip("uplinks are not supported.");
400370007Sdonner	ng_errors(FAIL);
401370007Sdonner	ng_name("u1", "bridge");
402370007Sdonner	ng_register_data("u1", get_data1);
403370007Sdonner	ng_connect(".", "u2", "bridge:", "uplink2");
404370007Sdonner	ng_register_data("u2", get_data2);
405370007Sdonner	ng_connect(".", "l0", "bridge:", "link0");
406370007Sdonner	ng_register_data("l0", get_data0);
407370007Sdonner	ng_connect(".", "l3", "bridge:", "link3");
408370007Sdonner	ng_register_data("l3", get_data3);
409370007Sdonner
410370007Sdonner	/* unknown unicast 0 from uplink1 */
411370007Sdonner	ng_counter_clear(r);
412370007Sdonner	msg4.eh.ether_shost[5] = 1;
413370007Sdonner	ng_send_data("u1", &msg4, sizeof(msg4));
414370007Sdonner	ng_handle_events(50, &r);
415370007Sdonner	ATF_CHECK(r[0] == 0 && r[1] == 0 && r[2] == 1 && r[3] == 0);
416370007Sdonner
417370007Sdonner	/* unknown unicast 2 from link0 */
418370007Sdonner	ng_counter_clear(r);
419370007Sdonner	msg4.eh.ether_shost[5] = 0;
420370007Sdonner	msg4.eh.ether_dhost[5] = 2;
421370007Sdonner	ng_send_data("l0", &msg4, sizeof(msg4));
422370007Sdonner	ng_handle_events(50, &r);
423370007Sdonner	ATF_CHECK(r[0] == 0 && r[1] == 1 && r[2] == 1 && r[3] == 0);
424370007Sdonner
425370007Sdonner	/* known unicast 0 from uplink2 */
426370007Sdonner	ng_counter_clear(r);
427370007Sdonner	msg4.eh.ether_shost[5] = 2;
428370007Sdonner	msg4.eh.ether_dhost[5] = 0;
429370007Sdonner	ng_send_data("u2", &msg4, sizeof(msg4));
430370007Sdonner	ng_handle_events(50, &r);
431370007Sdonner	ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 0 && r[3] == 0);
432370007Sdonner
433370007Sdonner	/* known unicast 0 from link3 */
434370007Sdonner	ng_counter_clear(r);
435370007Sdonner	msg4.eh.ether_shost[5] = 3;
436370007Sdonner	msg4.eh.ether_dhost[5] = 0;
437370007Sdonner	ng_send_data("l3", &msg4, sizeof(msg4));
438370007Sdonner	ng_handle_events(50, &r);
439370007Sdonner	ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 0 && r[3] == 0);
440370007Sdonner
441370007Sdonner	/* (un)known unicast 2 from uplink1 */
442370007Sdonner	ng_counter_clear(r);
443370007Sdonner	msg4.eh.ether_shost[5] = 1;
444370007Sdonner	msg4.eh.ether_dhost[5] = 2;
445370007Sdonner	ng_send_data("u1", &msg4, sizeof(msg4));
446370007Sdonner	ng_handle_events(50, &r);
447370007Sdonner	ATF_CHECK(r[0] == 0 && r[1] == 0 && r[2] == 1 && r[3] == 0);
448370007Sdonner
449370007Sdonner	/* (un)known unicast 2 from link0 */
450370007Sdonner	ng_counter_clear(r);
451370007Sdonner	msg4.eh.ether_shost[5] = 0;
452370007Sdonner	ng_send_data("l0", &msg4, sizeof(msg4));
453370007Sdonner	ng_handle_events(50, &r);
454370007Sdonner	ATF_CHECK(r[0] == 0 && r[1] == 1 && r[2] == 1 && r[3] == 0);
455370007Sdonner
456370007Sdonner	/* unknown multicast 2 from uplink1 */
457370007Sdonner	ng_counter_clear(r);
458370007Sdonner	msg4.eh.ether_shost[5] = 1;
459370007Sdonner	msg4.eh.ether_dhost[0] = 0xff;
460370007Sdonner	ng_send_data("u1", &msg4, sizeof(msg4));
461370007Sdonner	ng_handle_events(50, &r);
462370007Sdonner	ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 1 && r[3] == 1);
463370007Sdonner
464370007Sdonner	/* unknown multicast 2 from link0 */
465370007Sdonner	ng_counter_clear(r);
466370007Sdonner	msg4.eh.ether_shost[5] = 0;
467370007Sdonner	ng_send_data("l0", &msg4, sizeof(msg4));
468370007Sdonner	ng_handle_events(50, &r);
469370007Sdonner	ATF_CHECK(r[0] == 0 && r[1] == 1 && r[2] == 1 && r[3] == 1);
470370007Sdonner
471370007Sdonner	/* broadcast from uplink1 */
472370007Sdonner	ng_counter_clear(r);
473370007Sdonner	msg4.eh.ether_shost[5] = 1;
474370007Sdonner	memset(msg4.eh.ether_dhost, 0xff, sizeof(msg4.eh.ether_dhost));
475370007Sdonner	ng_send_data("u1", &msg4, sizeof(msg4));
476370007Sdonner	ng_handle_events(50, &r);
477370007Sdonner	ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 1 && r[3] == 1);
478370007Sdonner
479370007Sdonner	/* broadcast from link0 */
480370007Sdonner	ng_counter_clear(r);
481370007Sdonner	msg4.eh.ether_shost[5] = 0;
482370007Sdonner	ng_send_data("l0", &msg4, sizeof(msg4));
483370007Sdonner	ng_handle_events(50, &r);
484370007Sdonner	ATF_CHECK(r[0] == 0 && r[1] == 1 && r[2] == 1 && r[3] == 1);
485370007Sdonner
486370007Sdonner	/* inspect mac table */
487370007Sdonner	ng_register_msg(get_tablesize);
488370007Sdonner	rm.tok = ng_send_msg("bridge:", "gettable");
489370007Sdonner	rm.cnt = 0;
490370007Sdonner	ng_handle_events(50, &rm);
491370007Sdonner	ATF_CHECK(rm.cnt == 2);
492370007Sdonner
493370007Sdonner	ng_shutdown("bridge:");
494370007Sdonner}
495370007Sdonner
496370007SdonnerATF_TC(uplink_classic);
497370007SdonnerATF_TC_HEAD(uplink_classic, conf)
498370007Sdonner{
499370007Sdonner	atf_tc_set_md_var(conf, "require.user", "root");
500370007Sdonner}
501370007Sdonner
502370007SdonnerATF_TC_BODY(uplink_classic, dummy)
503370007Sdonner{
504370007Sdonner	ng_counter_t	r;
505370007Sdonner	struct gettable	rm;
506370007Sdonner
507370007Sdonner	ng_init();
508370007Sdonner	ng_errors(PASS);
509370007Sdonner	ng_shutdown("bridge:");
510370007Sdonner
511370007Sdonner	ng_mkpeer(".", "l0", "bridge", "link0");
512370007Sdonner	if (errno > 0)
513370007Sdonner		atf_tc_skip("uplinks are not supported.");
514370007Sdonner	ng_errors(FAIL);
515370007Sdonner	ng_name("l0", "bridge");
516370007Sdonner	ng_register_data("l0", get_data0);
517370007Sdonner	ng_connect(".", "u1", "bridge:", "uplink1");
518370007Sdonner	ng_register_data("u1", get_data1);
519370007Sdonner	ng_connect(".", "u2", "bridge:", "uplink2");
520370007Sdonner	ng_register_data("u2", get_data2);
521370007Sdonner	ng_connect(".", "l3", "bridge:", "link3");
522370007Sdonner	ng_register_data("l3", get_data3);
523370007Sdonner
524370007Sdonner	/* unknown unicast 0 from uplink1 */
525370007Sdonner	ng_counter_clear(r);
526370007Sdonner	msg4.eh.ether_shost[5] = 1;
527370007Sdonner	ng_send_data("u1", &msg4, sizeof(msg4));
528370007Sdonner	ng_handle_events(50, &r);
529370007Sdonner	ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 1 && r[3] == 1);
530370007Sdonner
531370007Sdonner	/* unknown unicast 2 from link0 */
532370007Sdonner	ng_counter_clear(r);
533370007Sdonner	msg4.eh.ether_shost[5] = 0;
534370007Sdonner	msg4.eh.ether_dhost[5] = 2;
535370007Sdonner	ng_send_data("l0", &msg4, sizeof(msg4));
536370007Sdonner	ng_handle_events(50, &r);
537370007Sdonner	ATF_CHECK(r[0] == 0 && r[1] == 1 && r[2] == 1 && r[3] == 1);
538370007Sdonner
539370007Sdonner	/* known unicast 0 from uplink2 */
540370007Sdonner	ng_counter_clear(r);
541370007Sdonner	msg4.eh.ether_shost[5] = 2;
542370007Sdonner	msg4.eh.ether_dhost[5] = 0;
543370007Sdonner	ng_send_data("u2", &msg4, sizeof(msg4));
544370007Sdonner	ng_handle_events(50, &r);
545370007Sdonner	ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 0 && r[3] == 0);
546370007Sdonner
547370007Sdonner	/* known unicast 0 from link3 */
548370007Sdonner	ng_counter_clear(r);
549370007Sdonner	msg4.eh.ether_shost[5] = 3;
550370007Sdonner	msg4.eh.ether_dhost[5] = 0;
551370007Sdonner	ng_send_data("l3", &msg4, sizeof(msg4));
552370007Sdonner	ng_handle_events(50, &r);
553370007Sdonner	ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 0 && r[3] == 0);
554370007Sdonner
555370007Sdonner	/* (un)known unicast 2 from uplink1 */
556370007Sdonner	ng_counter_clear(r);
557370007Sdonner	msg4.eh.ether_shost[5] = 1;
558370007Sdonner	msg4.eh.ether_dhost[5] = 2;
559370007Sdonner	ng_send_data("u1", &msg4, sizeof(msg4));
560370007Sdonner	ng_handle_events(50, &r);
561370007Sdonner	ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 1 && r[3] == 1);
562370007Sdonner
563370007Sdonner	/* (un)known unicast 2 from link0 */
564370007Sdonner	ng_counter_clear(r);
565370007Sdonner	msg4.eh.ether_shost[5] = 0;
566370007Sdonner	ng_send_data("l0", &msg4, sizeof(msg4));
567370007Sdonner	ng_handle_events(50, &r);
568370007Sdonner	ATF_CHECK(r[0] == 0 && r[1] == 1 && r[2] == 1 && r[3] == 1);
569370007Sdonner
570370007Sdonner	/* unknown multicast 2 from uplink1 */
571370007Sdonner	ng_counter_clear(r);
572370007Sdonner	msg4.eh.ether_shost[5] = 1;
573370007Sdonner	msg4.eh.ether_dhost[0] = 0xff;
574370007Sdonner	ng_send_data("u1", &msg4, sizeof(msg4));
575370007Sdonner	ng_handle_events(50, &r);
576370007Sdonner	ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 1 && r[3] == 1);
577370007Sdonner
578370007Sdonner	/* unknown multicast 2 from link0 */
579370007Sdonner	ng_counter_clear(r);
580370007Sdonner	msg4.eh.ether_shost[5] = 0;
581370007Sdonner	ng_send_data("l0", &msg4, sizeof(msg4));
582370007Sdonner	ng_handle_events(50, &r);
583370007Sdonner	ATF_CHECK(r[0] == 0 && r[1] == 1 && r[2] == 1 && r[3] == 1);
584370007Sdonner
585370007Sdonner	/* broadcast from uplink1 */
586370007Sdonner	ng_counter_clear(r);
587370007Sdonner	msg4.eh.ether_shost[5] = 1;
588370007Sdonner	memset(msg4.eh.ether_dhost, 0xff, sizeof(msg4.eh.ether_dhost));
589370007Sdonner	ng_send_data("u1", &msg4, sizeof(msg4));
590370007Sdonner	ng_handle_events(50, &r);
591370007Sdonner	ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 1 && r[3] == 1);
592370007Sdonner
593370007Sdonner	/* broadcast from link0 */
594370007Sdonner	ng_counter_clear(r);
595370007Sdonner	msg4.eh.ether_shost[5] = 0;
596370007Sdonner	ng_send_data("l0", &msg4, sizeof(msg4));
597370007Sdonner	ng_handle_events(50, &r);
598370007Sdonner	ATF_CHECK(r[0] == 0 && r[1] == 1 && r[2] == 1 && r[3] == 1);
599370007Sdonner
600370007Sdonner	/* inspect mac table */
601370007Sdonner	ng_register_msg(get_tablesize);
602370007Sdonner	rm.tok = ng_send_msg("bridge:", "gettable");
603370007Sdonner	rm.cnt = 0;
604370007Sdonner	ng_handle_events(50, &rm);
605370007Sdonner	ATF_CHECK(rm.cnt == 2);
606370007Sdonner
607370007Sdonner	ng_shutdown("bridge:");
608370007Sdonner}
609370007Sdonner
610370007SdonnerATF_TP_ADD_TCS(bridge)
611370007Sdonner{
612370007Sdonner	ATF_TP_ADD_TC(bridge, basic);
613370007Sdonner	ATF_TP_ADD_TC(bridge, loop);
614370007Sdonner	ATF_TP_ADD_TC(bridge, persistence);
615370007Sdonner	ATF_TP_ADD_TC(bridge, many_unicasts);
616370007Sdonner	ATF_TP_ADD_TC(bridge, many_broadcasts);
617370007Sdonner	ATF_TP_ADD_TC(bridge, uplink_private);
618370007Sdonner	ATF_TP_ADD_TC(bridge, uplink_classic);
619370007Sdonner
620370007Sdonner	return atf_no_error();
621370007Sdonner}
622370007Sdonner
623370007Sdonnerstatic void
624370007Sdonnerget_tablesize(char const *source, struct ng_mesg *msg, void *ctx)
625370007Sdonner{
626370007Sdonner	struct gettable *rm = ctx;
627370007Sdonner	struct ng_bridge_host_ary *gt = (void *)msg->data;
628370007Sdonner
629370007Sdonner	fprintf(stderr, "Response from %s to query %d\n", source, msg->header.token);
630370007Sdonner	if (rm->tok == msg->header.token)
631370007Sdonner		rm->cnt = gt->numHosts;
632370007Sdonner}
633