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