1370009Sdonner/*-
2370009Sdonner * SPDX-License-Identifier: BSD-3-Clause
3370009Sdonner *
4370009Sdonner * Copyright 2021 Lutz Donnerhacke
5370009Sdonner *
6370009Sdonner * Redistribution and use in source and binary forms, with or without
7370009Sdonner * modification, are permitted provided that the following conditions
8370009Sdonner * are met:
9370009Sdonner *
10370009Sdonner * 1. Redistributions of source code must retain the above copyright
11370009Sdonner *    notice, this list of conditions and the following disclaimer.
12370009Sdonner * 2. Redistributions in binary form must reproduce the above
13370009Sdonner *    copyright notice, this list of conditions and the following
14370009Sdonner *    disclaimer in the documentation and/or other materials provided
15370009Sdonner *    with the distribution.
16370009Sdonner * 3. Neither the name of the copyright holder nor the names of its
17370009Sdonner *    contributors may be used to endorse or promote products derived
18370009Sdonner *    from this software without specific prior written permission.
19370009Sdonner *
20370009Sdonner * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
21370009Sdonner * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
22370009Sdonner * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
23370009Sdonner * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24370009Sdonner * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
25370009Sdonner * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
26370009Sdonner * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
27370009Sdonner * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28370009Sdonner * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
29370009Sdonner * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
30370009Sdonner * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
31370009Sdonner * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32370009Sdonner * SUCH DAMAGE.
33370009Sdonner */
34370009Sdonner#include <atf-c.h>
35370009Sdonner#include <errno.h>
36370009Sdonner#include <stdlib.h>
37370009Sdonner#include <stdio.h>
38370009Sdonner
39370009Sdonner#include <net/ethernet.h>
40370009Sdonner#include <netinet/in.h>
41370009Sdonner
42370009Sdonner#include "util.h"
43370009Sdonner#include <netgraph/ng_bridge.h>
44370009Sdonner
45370009Sdonnerstruct vlan
46370009Sdonner{
47370009Sdonner	uint16_t	proto;
48370009Sdonner	uint16_t	tag;
49370009Sdonner}		__packed;
50370009Sdonner
51370009Sdonnerstruct frame
52370009Sdonner{
53370009Sdonner	u_char		dst[ETHER_ADDR_LEN];
54370009Sdonner	u_char		src[ETHER_ADDR_LEN];
55370009Sdonner	struct vlan	vlan[10];
56370009Sdonner}		__packed;
57370009Sdonner
58370009Sdonnerstatic struct frame msg = {
59370009Sdonner	.src = {2, 4, 6, 1, 3, 5},
60370009Sdonner	.dst = {2, 4, 6, 1, 3, 7},
61370009Sdonner	.vlan[0] = {htons(ETHERTYPE_VLAN), htons(EVL_MAKETAG(1, 0, 0))},
62370009Sdonner	.vlan[1] = {htons(ETHERTYPE_VLAN), htons(EVL_MAKETAG(2, 0, 0))},
63370009Sdonner	.vlan[2] = {htons(ETHERTYPE_VLAN), htons(EVL_MAKETAG(3, 0, 0))},
64370009Sdonner	.vlan[3] = {htons(ETHERTYPE_VLAN), htons(EVL_MAKETAG(4, 0, 0))},
65370009Sdonner	.vlan[4] = {htons(ETHERTYPE_VLAN), htons(EVL_MAKETAG(5, 0, 0))},
66370009Sdonner	.vlan[5] = {htons(ETHERTYPE_VLAN), htons(EVL_MAKETAG(6, 0, 0))},
67370009Sdonner	.vlan[6] = {htons(ETHERTYPE_VLAN), htons(EVL_MAKETAG(7, 0, 0))},
68370009Sdonner	.vlan[7] = {htons(ETHERTYPE_VLAN), htons(EVL_MAKETAG(8, 0, 0))},
69370009Sdonner	.vlan[8] = {htons(ETHERTYPE_VLAN), htons(EVL_MAKETAG(9, 0, 0))},
70370009Sdonner	.vlan[9] = {0}
71370009Sdonner};
72370009Sdonner
73370009Sdonnerstatic void	_basic(int);
74370009Sdonnerstatic void	get_vlan(void *data, size_t len, void *ctx);
75370009Sdonner
76370009Sdonnerstatic void
77370009Sdonnerget_vlan(void *data, size_t len, void *ctx)
78370009Sdonner{
79370009Sdonner	int	       *v = ctx, i;
80370009Sdonner	struct frame   *f = data;
81370009Sdonner
82370009Sdonner	(void)len;
83370009Sdonner	for (i = 0; i < 10; i++)
84370009Sdonner		v[i] = EVL_VLANOFTAG(ntohs(f->vlan[i].tag));
85370009Sdonner}
86370009Sdonner
87370009Sdonnerstatic void
88370009Sdonner_basic(int direction)
89370009Sdonner{
90370009Sdonner	int		r[10];
91370009Sdonner	int		i, rot, len;
92370009Sdonner
93370009Sdonner	ng_init();
94370009Sdonner	ng_errors(PASS);
95370009Sdonner	ng_shutdown("vr:");
96370009Sdonner	ng_errors(FAIL);
97370009Sdonner
98370009Sdonner	ng_mkpeer(".", "a", "vlan_rotate", direction > 0 ? "original" : "ordered");
99370009Sdonner	ng_name("a", "vr");
100370009Sdonner	ng_connect(".", "b", "vr:", direction > 0 ? "ordered" : "original");
101370009Sdonner	ng_register_data("b", get_vlan);
102370009Sdonner
103370009Sdonner	for (len = 9; len > 0; len--)
104370009Sdonner	{
105370009Sdonner		/* reduce the number of vlans */
106370009Sdonner		msg.vlan[len].proto = htons(ETHERTYPE_IP);
107370009Sdonner
108370009Sdonner		for (rot = -len + 1; rot < len; rot++)
109370009Sdonner		{
110370009Sdonner			char		cmd[40];
111370009Sdonner
112370009Sdonner			/* set rotation offset */
113370009Sdonner			snprintf(cmd, sizeof(cmd), "setconf { min=0 max=9 rot=%d }", rot);
114370009Sdonner			ng_send_msg("vr:", cmd);
115370009Sdonner
116370009Sdonner			ng_send_data("a", &msg, sizeof(msg));
117370009Sdonner			ng_handle_events(50, &r);
118370009Sdonner
119370009Sdonner			/* check rotation */
120370009Sdonner			for (i = 0; i < len; i++)
121370009Sdonner			{
122370009Sdonner				int		expect = (2 * len + i - direction * rot) % len + 1;
123370009Sdonner				int		vlan = r[i];
124370009Sdonner
125370009Sdonner				ATF_CHECK_MSG(vlan == expect,
126370009Sdonner				 "len=%d rot=%d i=%d -> vlan=%d, expect=%d",
127370009Sdonner					      len, rot, i, r[i], expect);
128370009Sdonner			}
129370009Sdonner		}
130370009Sdonner	}
131370009Sdonner
132370009Sdonner	ng_shutdown("vr:");
133370009Sdonner}
134370009Sdonner
135370009SdonnerATF_TC(basic);
136370009SdonnerATF_TC_HEAD(basic, conf)
137370009Sdonner{
138370009Sdonner	atf_tc_set_md_var(conf, "require.user", "root");
139370009Sdonner}
140370009Sdonner
141370009SdonnerATF_TC_BODY(basic, dummy)
142370009Sdonner{
143370009Sdonner	_basic(1);
144370009Sdonner}
145370009Sdonner
146370009SdonnerATF_TC(reverse);
147370009SdonnerATF_TC_HEAD(reverse, conf)
148370009Sdonner{
149370009Sdonner	atf_tc_set_md_var(conf, "require.user", "root");
150370009Sdonner}
151370009Sdonner
152370009SdonnerATF_TC_BODY(reverse, dummy)
153370009Sdonner{
154370009Sdonner	_basic(-1);
155370009Sdonner}
156370009Sdonner
157370009Sdonnerstatic void	_ethertype(int);
158370009Sdonnerstatic void	get_ethertype(void *data, size_t len, void *ctx);
159370009Sdonner
160370009Sdonnerstatic void
161370009Sdonnerget_ethertype(void *data, size_t len, void *ctx)
162370009Sdonner{
163370009Sdonner	int	       *v = ctx, i;
164370009Sdonner	struct frame   *f = data;
165370009Sdonner
166370009Sdonner	(void)len;
167370009Sdonner	for (i = 0; i < 10; i++)
168370009Sdonner		v[i] = ntohs(f->vlan[i].proto);
169370009Sdonner}
170370009Sdonner
171370009Sdonnerstatic void
172370009Sdonner_ethertype(int direction)
173370009Sdonner{
174370009Sdonner	int		r[10];
175370009Sdonner	int		i, rounds = 20;
176370009Sdonner
177370009Sdonner	ng_init();
178370009Sdonner	ng_errors(PASS);
179370009Sdonner	ng_shutdown("vr:");
180370009Sdonner	ng_errors(FAIL);
181370009Sdonner
182370009Sdonner	ng_mkpeer(".", "a", "vlan_rotate", direction > 0 ? "original" : "ordered");
183370009Sdonner	ng_name("a", "vr");
184370009Sdonner	ng_connect(".", "b", "vr:", direction > 0 ? "ordered" : "original");
185370009Sdonner	ng_register_data("b", get_ethertype);
186370009Sdonner
187370009Sdonner	while (rounds-- > 0)
188370009Sdonner	{
189370009Sdonner		char		cmd[40];
190370009Sdonner		int		len = 9;
191370009Sdonner		int		rot = rand() % (2 * len - 1) - len + 1;
192370009Sdonner		int		vlan[10];
193370009Sdonner
194370009Sdonner		for (i = 0; i < len; i++)
195370009Sdonner		{
196370009Sdonner			switch (rand() % 3)
197370009Sdonner			{
198370009Sdonner			default:
199370009Sdonner				msg.vlan[i].proto = htons(ETHERTYPE_VLAN);
200370009Sdonner				break;
201370009Sdonner			case 1:
202370009Sdonner				msg.vlan[i].proto = htons(ETHERTYPE_QINQ);
203370009Sdonner				break;
204370009Sdonner			case 2:
205370009Sdonner				msg.vlan[i].proto = htons(ETHERTYPE_8021Q9100);
206370009Sdonner				break;
207370009Sdonner			}
208370009Sdonner		}
209370009Sdonner		msg.vlan[i].proto = htons(ETHERTYPE_IP);
210370009Sdonner
211370009Sdonner		for (i = 0; i < len; i++)
212370009Sdonner			vlan[i] = msg.vlan[i].proto;
213370009Sdonner
214370009Sdonner		snprintf(cmd, sizeof(cmd), "setconf { min=0 max=9 rot=%d }", rot);
215370009Sdonner		ng_send_msg("vr:", cmd);
216370009Sdonner
217370009Sdonner		bzero(r, sizeof(r));
218370009Sdonner		ng_send_data("a", &msg, sizeof(msg));
219370009Sdonner		ng_handle_events(50, &r);
220370009Sdonner
221370009Sdonner		/* check rotation */
222370009Sdonner		for (i = 0; i < len; i++)
223370009Sdonner		{
224370009Sdonner			int		expect = (2 * len + i - direction * rot) % len;
225370009Sdonner
226370009Sdonner			ATF_CHECK_MSG(r[i] == ntohs(vlan[expect]),
227370009Sdonner			 "len=%d rot=%d i=%d -> vlan=%04x, expect(%d)=%04x",
228370009Sdonner			    len, rot, i, ntohs(r[i]), expect, vlan[expect]);
229370009Sdonner		}
230370009Sdonner	}
231370009Sdonner
232370009Sdonner	ng_shutdown("vr:");
233370009Sdonner}
234370009Sdonner
235370009SdonnerATF_TC(ethertype);
236370009SdonnerATF_TC_HEAD(ethertype, conf)
237370009Sdonner{
238370009Sdonner	atf_tc_set_md_var(conf, "require.user", "root");
239370009Sdonner}
240370009Sdonner
241370009SdonnerATF_TC_BODY(ethertype, dummy)
242370009Sdonner{
243370009Sdonner	_ethertype(1);
244370009Sdonner}
245370009Sdonner
246370009SdonnerATF_TC(typeether);
247370009SdonnerATF_TC_HEAD(typeether, conf)
248370009Sdonner{
249370009Sdonner	atf_tc_set_md_var(conf, "require.user", "root");
250370009Sdonner}
251370009Sdonner
252370009SdonnerATF_TC_BODY(typeether, dummy)
253370009Sdonner{
254370009Sdonner	_ethertype(-1);
255370009Sdonner}
256370009Sdonner
257370009SdonnerATF_TC(minmax);
258370009SdonnerATF_TC_HEAD(minmax, conf)
259370009Sdonner{
260370009Sdonner	atf_tc_set_md_var(conf, "require.user", "root");
261370009Sdonner}
262370009Sdonner
263370009SdonnerATF_TC_BODY(minmax, dummy)
264370009Sdonner{
265370009Sdonner	ng_counter_t	r;
266370009Sdonner	int		len;
267370009Sdonner
268370009Sdonner	ng_init();
269370009Sdonner	ng_errors(PASS);
270370009Sdonner	ng_shutdown("vr:");
271370009Sdonner	ng_errors(FAIL);
272370009Sdonner
273370009Sdonner	ng_mkpeer(".", "a", "vlan_rotate", "original");
274370009Sdonner	ng_name("a", "vr");
275370009Sdonner	ng_connect(".", "b", "vr:", "ordered");
276370009Sdonner	ng_connect(".", "c", "vr:", "excessive");
277370009Sdonner	ng_connect(".", "d", "vr:", "incomplete");
278370009Sdonner	ng_register_data("a", get_data0);
279370009Sdonner	ng_register_data("b", get_data1);
280370009Sdonner	ng_register_data("c", get_data2);
281370009Sdonner	ng_register_data("d", get_data3);
282370009Sdonner
283370009Sdonner	ng_send_msg("vr:", "setconf { min=3 max=7 rot=0 }");
284370009Sdonner	for (len = 9; len > 0; len--)
285370009Sdonner	{
286370009Sdonner		/* reduce the number of vlans */
287370009Sdonner		msg.vlan[len].proto = htons(ETHERTYPE_IP);
288370009Sdonner
289370009Sdonner		ng_counter_clear(r);
290370009Sdonner		ng_send_data("a", &msg, sizeof(msg));
291370009Sdonner		ng_handle_events(50, &r);
292370009Sdonner		if (len < 3)
293370009Sdonner			ATF_CHECK(r[0] == 0 && r[1] == 0 && r[2] == 0 && r[3] == 1);
294370009Sdonner		else if (len > 7)
295370009Sdonner			ATF_CHECK(r[0] == 0 && r[1] == 0 && r[2] == 1 && r[3] == 0);
296370009Sdonner		else
297370009Sdonner			ATF_CHECK(r[0] == 0 && r[1] == 1 && r[2] == 0 && r[3] == 0);
298370009Sdonner
299370009Sdonner		ng_counter_clear(r);
300370009Sdonner		ng_send_data("b", &msg, sizeof(msg));
301370009Sdonner		ng_handle_events(50, &r);
302370009Sdonner		if (len < 3)
303370009Sdonner			ATF_CHECK(r[0] == 0 && r[1] == 0 && r[2] == 0 && r[3] == 1);
304370009Sdonner		else if (len > 7)
305370009Sdonner			ATF_CHECK(r[0] == 0 && r[1] == 0 && r[2] == 1 && r[3] == 0);
306370009Sdonner		else
307370009Sdonner			ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 0 && r[3] == 0);
308370009Sdonner
309370009Sdonner		ng_counter_clear(r);
310370009Sdonner		ng_send_data("c", &msg, sizeof(msg));
311370009Sdonner		ng_handle_events(50, &r);
312370009Sdonner		ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 0 && r[3] == 0);
313370009Sdonner
314370009Sdonner		ng_counter_clear(r);
315370009Sdonner		ng_send_data("d", &msg, sizeof(msg));
316370009Sdonner		ng_handle_events(50, &r);
317370009Sdonner		ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 0 && r[3] == 0);
318370009Sdonner	}
319370009Sdonner
320370009Sdonner	ng_shutdown("vr:");
321370009Sdonner}
322370009Sdonner
323370009SdonnerATF_TP_ADD_TCS(vlan_rotate)
324370009Sdonner{
325370009Sdonner	/* Use "dd if=/dev/random bs=2 count=1 | od -x" to reproduce */
326370009Sdonner	srand(0xb93b);
327370009Sdonner
328370009Sdonner	ATF_TP_ADD_TC(vlan_rotate, basic);
329370009Sdonner	ATF_TP_ADD_TC(vlan_rotate, ethertype);
330370009Sdonner	ATF_TP_ADD_TC(vlan_rotate, reverse);
331370009Sdonner	ATF_TP_ADD_TC(vlan_rotate, typeether);
332370009Sdonner	ATF_TP_ADD_TC(vlan_rotate, minmax);
333370009Sdonner
334370009Sdonner	return atf_no_error();
335370009Sdonner}
336