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