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 <stdlib.h>
37#include <stdio.h>
38
39#include <net/ethernet.h>
40#include <netinet/in.h>
41
42#include "util.h"
43#include <netgraph/ng_bridge.h>
44
45struct vlan
46{
47	uint16_t	proto;
48	uint16_t	tag;
49}		__packed;
50
51struct frame
52{
53	u_char		dst[ETHER_ADDR_LEN];
54	u_char		src[ETHER_ADDR_LEN];
55	struct vlan	vlan[10];
56}		__packed;
57
58static struct frame msg = {
59	.src = {2, 4, 6, 1, 3, 5},
60	.dst = {2, 4, 6, 1, 3, 7},
61	.vlan[0] = {htons(ETHERTYPE_VLAN), htons(EVL_MAKETAG(1, 0, 0))},
62	.vlan[1] = {htons(ETHERTYPE_VLAN), htons(EVL_MAKETAG(2, 0, 0))},
63	.vlan[2] = {htons(ETHERTYPE_VLAN), htons(EVL_MAKETAG(3, 0, 0))},
64	.vlan[3] = {htons(ETHERTYPE_VLAN), htons(EVL_MAKETAG(4, 0, 0))},
65	.vlan[4] = {htons(ETHERTYPE_VLAN), htons(EVL_MAKETAG(5, 0, 0))},
66	.vlan[5] = {htons(ETHERTYPE_VLAN), htons(EVL_MAKETAG(6, 0, 0))},
67	.vlan[6] = {htons(ETHERTYPE_VLAN), htons(EVL_MAKETAG(7, 0, 0))},
68	.vlan[7] = {htons(ETHERTYPE_VLAN), htons(EVL_MAKETAG(8, 0, 0))},
69	.vlan[8] = {htons(ETHERTYPE_VLAN), htons(EVL_MAKETAG(9, 0, 0))},
70	.vlan[9] = {0}
71};
72
73static void	_basic(int);
74static void	get_vlan(void *data, size_t len, void *ctx);
75
76static void
77get_vlan(void *data, size_t len, void *ctx)
78{
79	int	       *v = ctx, i;
80	struct frame   *f = data;
81
82	(void)len;
83	for (i = 0; i < 10; i++)
84		v[i] = EVL_VLANOFTAG(ntohs(f->vlan[i].tag));
85}
86
87static void
88_basic(int direction)
89{
90	int		r[10];
91	int		i, rot, len;
92
93	ng_init();
94	ng_errors(PASS);
95	ng_shutdown("vr:");
96	ng_errors(FAIL);
97
98	ng_mkpeer(".", "a", "vlan_rotate", direction > 0 ? "original" : "ordered");
99	ng_name("a", "vr");
100	ng_connect(".", "b", "vr:", direction > 0 ? "ordered" : "original");
101	ng_register_data("b", get_vlan);
102
103	for (len = 9; len > 0; len--)
104	{
105		/* reduce the number of vlans */
106		msg.vlan[len].proto = htons(ETHERTYPE_IP);
107
108		for (rot = -len + 1; rot < len; rot++)
109		{
110			char		cmd[40];
111
112			/* set rotation offset */
113			snprintf(cmd, sizeof(cmd), "setconf { min=0 max=9 rot=%d }", rot);
114			ng_send_msg("vr:", cmd);
115
116			ng_send_data("a", &msg, sizeof(msg));
117			ng_handle_events(50, &r);
118
119			/* check rotation */
120			for (i = 0; i < len; i++)
121			{
122				int		expect = (2 * len + i - direction * rot) % len + 1;
123				int		vlan = r[i];
124
125				ATF_CHECK_MSG(vlan == expect,
126				 "len=%d rot=%d i=%d -> vlan=%d, expect=%d",
127					      len, rot, i, r[i], expect);
128			}
129		}
130	}
131
132	ng_shutdown("vr:");
133}
134
135ATF_TC(basic);
136ATF_TC_HEAD(basic, conf)
137{
138	atf_tc_set_md_var(conf, "require.user", "root");
139}
140
141ATF_TC_BODY(basic, dummy)
142{
143	_basic(1);
144}
145
146ATF_TC(reverse);
147ATF_TC_HEAD(reverse, conf)
148{
149	atf_tc_set_md_var(conf, "require.user", "root");
150}
151
152ATF_TC_BODY(reverse, dummy)
153{
154	_basic(-1);
155}
156
157static void	_ethertype(int);
158static void	get_ethertype(void *data, size_t len, void *ctx);
159
160static void
161get_ethertype(void *data, size_t len, void *ctx)
162{
163	int	       *v = ctx, i;
164	struct frame   *f = data;
165
166	(void)len;
167	for (i = 0; i < 10; i++)
168		v[i] = ntohs(f->vlan[i].proto);
169}
170
171static void
172_ethertype(int direction)
173{
174	int		r[10];
175	int		i, rounds = 20;
176
177	ng_init();
178	ng_errors(PASS);
179	ng_shutdown("vr:");
180	ng_errors(FAIL);
181
182	ng_mkpeer(".", "a", "vlan_rotate", direction > 0 ? "original" : "ordered");
183	ng_name("a", "vr");
184	ng_connect(".", "b", "vr:", direction > 0 ? "ordered" : "original");
185	ng_register_data("b", get_ethertype);
186
187	while (rounds-- > 0)
188	{
189		char		cmd[40];
190		int		len = 9;
191		int		rot = rand() % (2 * len - 1) - len + 1;
192		int		vlan[10];
193
194		for (i = 0; i < len; i++)
195		{
196			switch (rand() % 3)
197			{
198			default:
199				msg.vlan[i].proto = htons(ETHERTYPE_VLAN);
200				break;
201			case 1:
202				msg.vlan[i].proto = htons(ETHERTYPE_QINQ);
203				break;
204			case 2:
205				msg.vlan[i].proto = htons(ETHERTYPE_8021Q9100);
206				break;
207			}
208		}
209		msg.vlan[i].proto = htons(ETHERTYPE_IP);
210
211		for (i = 0; i < len; i++)
212			vlan[i] = msg.vlan[i].proto;
213
214		snprintf(cmd, sizeof(cmd), "setconf { min=0 max=9 rot=%d }", rot);
215		ng_send_msg("vr:", cmd);
216
217		bzero(r, sizeof(r));
218		ng_send_data("a", &msg, sizeof(msg));
219		ng_handle_events(50, &r);
220
221		/* check rotation */
222		for (i = 0; i < len; i++)
223		{
224			int		expect = (2 * len + i - direction * rot) % len;
225
226			ATF_CHECK_MSG(r[i] == ntohs(vlan[expect]),
227			 "len=%d rot=%d i=%d -> vlan=%04x, expect(%d)=%04x",
228			    len, rot, i, ntohs(r[i]), expect, vlan[expect]);
229		}
230	}
231
232	ng_shutdown("vr:");
233}
234
235ATF_TC(ethertype);
236ATF_TC_HEAD(ethertype, conf)
237{
238	atf_tc_set_md_var(conf, "require.user", "root");
239}
240
241ATF_TC_BODY(ethertype, dummy)
242{
243	_ethertype(1);
244}
245
246ATF_TC(typeether);
247ATF_TC_HEAD(typeether, conf)
248{
249	atf_tc_set_md_var(conf, "require.user", "root");
250}
251
252ATF_TC_BODY(typeether, dummy)
253{
254	_ethertype(-1);
255}
256
257ATF_TC(minmax);
258ATF_TC_HEAD(minmax, conf)
259{
260	atf_tc_set_md_var(conf, "require.user", "root");
261}
262
263ATF_TC_BODY(minmax, dummy)
264{
265	ng_counter_t	r;
266	int		len;
267
268	ng_init();
269	ng_errors(PASS);
270	ng_shutdown("vr:");
271	ng_errors(FAIL);
272
273	ng_mkpeer(".", "a", "vlan_rotate", "original");
274	ng_name("a", "vr");
275	ng_connect(".", "b", "vr:", "ordered");
276	ng_connect(".", "c", "vr:", "excessive");
277	ng_connect(".", "d", "vr:", "incomplete");
278	ng_register_data("a", get_data0);
279	ng_register_data("b", get_data1);
280	ng_register_data("c", get_data2);
281	ng_register_data("d", get_data3);
282
283	ng_send_msg("vr:", "setconf { min=3 max=7 rot=0 }");
284	for (len = 9; len > 0; len--)
285	{
286		/* reduce the number of vlans */
287		msg.vlan[len].proto = htons(ETHERTYPE_IP);
288
289		ng_counter_clear(r);
290		ng_send_data("a", &msg, sizeof(msg));
291		ng_handle_events(50, &r);
292		if (len < 3)
293			ATF_CHECK(r[0] == 0 && r[1] == 0 && r[2] == 0 && r[3] == 1);
294		else if (len > 7)
295			ATF_CHECK(r[0] == 0 && r[1] == 0 && r[2] == 1 && r[3] == 0);
296		else
297			ATF_CHECK(r[0] == 0 && r[1] == 1 && r[2] == 0 && r[3] == 0);
298
299		ng_counter_clear(r);
300		ng_send_data("b", &msg, sizeof(msg));
301		ng_handle_events(50, &r);
302		if (len < 3)
303			ATF_CHECK(r[0] == 0 && r[1] == 0 && r[2] == 0 && r[3] == 1);
304		else if (len > 7)
305			ATF_CHECK(r[0] == 0 && r[1] == 0 && r[2] == 1 && r[3] == 0);
306		else
307			ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 0 && r[3] == 0);
308
309		ng_counter_clear(r);
310		ng_send_data("c", &msg, sizeof(msg));
311		ng_handle_events(50, &r);
312		ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 0 && r[3] == 0);
313
314		ng_counter_clear(r);
315		ng_send_data("d", &msg, sizeof(msg));
316		ng_handle_events(50, &r);
317		ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 0 && r[3] == 0);
318	}
319
320	ng_shutdown("vr:");
321}
322
323ATF_TP_ADD_TCS(vlan_rotate)
324{
325	/* Use "dd if=/dev/random bs=2 count=1 | od -x" to reproduce */
326	srand(0xb93b);
327
328	ATF_TP_ADD_TC(vlan_rotate, basic);
329	ATF_TP_ADD_TC(vlan_rotate, ethertype);
330	ATF_TP_ADD_TC(vlan_rotate, reverse);
331	ATF_TP_ADD_TC(vlan_rotate, typeether);
332	ATF_TP_ADD_TC(vlan_rotate, minmax);
333
334	return atf_no_error();
335}
336