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 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 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); 136 ATF_TC_HEAD(basic, conf) 137 { 138 atf_tc_set_md_var(conf, "require.user", "root"); 139 } 140 141 ATF_TC_BODY(basic, dummy) 142 { 143 _basic(1); 144 } 145 146 ATF_TC(reverse); 147 ATF_TC_HEAD(reverse, conf) 148 { 149 atf_tc_set_md_var(conf, "require.user", "root"); 150 } 151 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 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 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); 236 ATF_TC_HEAD(ethertype, conf) 237 { 238 atf_tc_set_md_var(conf, "require.user", "root"); 239 } 240 241 ATF_TC_BODY(ethertype, dummy) 242 { 243 _ethertype(1); 244 } 245 246 ATF_TC(typeether); 247 ATF_TC_HEAD(typeether, conf) 248 { 249 atf_tc_set_md_var(conf, "require.user", "root"); 250 } 251 252 ATF_TC_BODY(typeether, dummy) 253 { 254 _ethertype(-1); 255 } 256 257 ATF_TC(minmax); 258 ATF_TC_HEAD(minmax, conf) 259 { 260 atf_tc_set_md_var(conf, "require.user", "root"); 261 } 262 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 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