xref: /freebsd/tests/sys/netgraph/vlan_rotate.c (revision 6b08e68be111d50931b0d30145f8b7e3402decaf)
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 
45 struct vlan
46 {
47 	uint16_t	proto;
48 	uint16_t	tag;
49 }		__packed;
50 
51 struct frame
52 {
53 	u_char		dst[ETHER_ADDR_LEN];
54 	u_char		src[ETHER_ADDR_LEN];
55 	struct vlan	vlan[10];
56 }		__packed;
57 
58 static 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 
73 static void	_basic(int);
74 static void	get_vlan(void *data, size_t len, void *ctx);
75 
76 static void
get_vlan(void * data,size_t len,void * ctx)77 get_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 
87 static void
_basic(int direction)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 
135 ATF_TC(basic);
ATF_TC_HEAD(basic,conf)136 ATF_TC_HEAD(basic, conf)
137 {
138 	atf_tc_set_md_var(conf, "require.user", "root");
139 }
140 
ATF_TC_BODY(basic,dummy)141 ATF_TC_BODY(basic, dummy)
142 {
143 	_basic(1);
144 }
145 
146 ATF_TC(reverse);
ATF_TC_HEAD(reverse,conf)147 ATF_TC_HEAD(reverse, conf)
148 {
149 	atf_tc_set_md_var(conf, "require.user", "root");
150 }
151 
ATF_TC_BODY(reverse,dummy)152 ATF_TC_BODY(reverse, dummy)
153 {
154 	_basic(-1);
155 }
156 
157 static void	_ethertype(int);
158 static void	get_ethertype(void *data, size_t len, void *ctx);
159 
160 static void
get_ethertype(void * data,size_t len,void * ctx)161 get_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 
171 static void
_ethertype(int direction)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 
235 ATF_TC(ethertype);
ATF_TC_HEAD(ethertype,conf)236 ATF_TC_HEAD(ethertype, conf)
237 {
238 	atf_tc_set_md_var(conf, "require.user", "root");
239 }
240 
ATF_TC_BODY(ethertype,dummy)241 ATF_TC_BODY(ethertype, dummy)
242 {
243 	_ethertype(1);
244 }
245 
246 ATF_TC(typeether);
ATF_TC_HEAD(typeether,conf)247 ATF_TC_HEAD(typeether, conf)
248 {
249 	atf_tc_set_md_var(conf, "require.user", "root");
250 }
251 
ATF_TC_BODY(typeether,dummy)252 ATF_TC_BODY(typeether, dummy)
253 {
254 	_ethertype(-1);
255 }
256 
257 ATF_TC(minmax);
ATF_TC_HEAD(minmax,conf)258 ATF_TC_HEAD(minmax, conf)
259 {
260 	atf_tc_set_md_var(conf, "require.user", "root");
261 }
262 
ATF_TC_BODY(minmax,dummy)263 ATF_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 
ATF_TP_ADD_TCS(vlan_rotate)323 ATF_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