aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLutz Donnerhacke <donner@FreeBSD.org>2021-06-06 23:56:12 +0000
committerLutz Donnerhacke <donner@FreeBSD.org>2021-06-16 12:04:06 +0000
commit7438333663b4d22f6863aeffba55d6a04149e3a2 (patch)
tree7207f04cd9ed4717ece8e6b631a800b7cdee6720
parentd68f32b4ac357ebd54dd1c640bb87ba92d7eb76d (diff)
downloadsrc-7438333663b4d22f6863aeffba55d6a04149e3a2.tar.gz
src-7438333663b4d22f6863aeffba55d6a04149e3a2.zip
tests/netgraph: Tests for ng_vlan_rotate
Test functionality of ng_vlan_rotate(4): - Rotate 1 to 9 stagged vlans in any possible direction and length - Rotate random combinations of ethertypes (8100, 88a8, 9100) - Automatic reverse rotating for backward data flow - Test too many and too few vlans Reviewed by: kp (earlier version) Differential Revision: https://reviews.freebsd.org/D30670 (cherry picked from commit 6b08e68be111d50931b0d30145f8b7e3402decaf)
-rw-r--r--tests/sys/netgraph/Makefile8
-rw-r--r--tests/sys/netgraph/vlan_rotate.c335
2 files changed, 340 insertions, 3 deletions
diff --git a/tests/sys/netgraph/Makefile b/tests/sys/netgraph/Makefile
index 9c5faedbc8e1..adc5a898dc02 100644
--- a/tests/sys/netgraph/Makefile
+++ b/tests/sys/netgraph/Makefile
@@ -5,13 +5,15 @@ PACKAGE= tests
TESTSDIR= ${TESTSBASE}/sys/netgraph
BINDIR= ${TESTSDIR}
-ATF_TESTS_C+= basic \
- bridge \
- hub \
+ATF_TESTS_C+= basic \
+ bridge \
+ hub \
+ vlan_rotate \
SRCS.basic= basic.c util.c
SRCS.bridge= bridge.c util.c
SRCS.hub= hub.c util.c
+SRCS.vlan_rotate=vlan_rotate.c util.c
LIBADD+= netgraph
diff --git a/tests/sys/netgraph/vlan_rotate.c b/tests/sys/netgraph/vlan_rotate.c
new file mode 100644
index 000000000000..8df3ab981a7c
--- /dev/null
+++ b/tests/sys/netgraph/vlan_rotate.c
@@ -0,0 +1,335 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright 2021 Lutz Donnerhacke
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <atf-c.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <net/ethernet.h>
+#include <netinet/in.h>
+
+#include "util.h"
+#include <netgraph/ng_bridge.h>
+
+struct vlan
+{
+ uint16_t proto;
+ uint16_t tag;
+} __packed;
+
+struct frame
+{
+ u_char dst[ETHER_ADDR_LEN];
+ u_char src[ETHER_ADDR_LEN];
+ struct vlan vlan[10];
+} __packed;
+
+static struct frame msg = {
+ .src = {2, 4, 6, 1, 3, 5},
+ .dst = {2, 4, 6, 1, 3, 7},
+ .vlan[0] = {htons(ETHERTYPE_VLAN), htons(EVL_MAKETAG(1, 0, 0))},
+ .vlan[1] = {htons(ETHERTYPE_VLAN), htons(EVL_MAKETAG(2, 0, 0))},
+ .vlan[2] = {htons(ETHERTYPE_VLAN), htons(EVL_MAKETAG(3, 0, 0))},
+ .vlan[3] = {htons(ETHERTYPE_VLAN), htons(EVL_MAKETAG(4, 0, 0))},
+ .vlan[4] = {htons(ETHERTYPE_VLAN), htons(EVL_MAKETAG(5, 0, 0))},
+ .vlan[5] = {htons(ETHERTYPE_VLAN), htons(EVL_MAKETAG(6, 0, 0))},
+ .vlan[6] = {htons(ETHERTYPE_VLAN), htons(EVL_MAKETAG(7, 0, 0))},
+ .vlan[7] = {htons(ETHERTYPE_VLAN), htons(EVL_MAKETAG(8, 0, 0))},
+ .vlan[8] = {htons(ETHERTYPE_VLAN), htons(EVL_MAKETAG(9, 0, 0))},
+ .vlan[9] = {0}
+};
+
+static void _basic(int);
+static void get_vlan(void *data, size_t len, void *ctx);
+
+static void
+get_vlan(void *data, size_t len, void *ctx)
+{
+ int *v = ctx, i;
+ struct frame *f = data;
+
+ (void)len;
+ for (i = 0; i < 10; i++)
+ v[i] = EVL_VLANOFTAG(ntohs(f->vlan[i].tag));
+}
+
+static void
+_basic(int direction)
+{
+ int r[10];
+ int i, rot, len;
+
+ ng_init();
+ ng_errors(PASS);
+ ng_shutdown("vr:");
+ ng_errors(FAIL);
+
+ ng_mkpeer(".", "a", "vlan_rotate", direction > 0 ? "original" : "ordered");
+ ng_name("a", "vr");
+ ng_connect(".", "b", "vr:", direction > 0 ? "ordered" : "original");
+ ng_register_data("b", get_vlan);
+
+ for (len = 9; len > 0; len--)
+ {
+ /* reduce the number of vlans */
+ msg.vlan[len].proto = htons(ETHERTYPE_IP);
+
+ for (rot = -len + 1; rot < len; rot++)
+ {
+ char cmd[40];
+
+ /* set rotation offset */
+ snprintf(cmd, sizeof(cmd), "setconf { min=0 max=9 rot=%d }", rot);
+ ng_send_msg("vr:", cmd);
+
+ ng_send_data("a", &msg, sizeof(msg));
+ ng_handle_events(50, &r);
+
+ /* check rotation */
+ for (i = 0; i < len; i++)
+ {
+ int expect = (2 * len + i - direction * rot) % len + 1;
+ int vlan = r[i];
+
+ ATF_CHECK_MSG(vlan == expect,
+ "len=%d rot=%d i=%d -> vlan=%d, expect=%d",
+ len, rot, i, r[i], expect);
+ }
+ }
+ }
+
+ ng_shutdown("vr:");
+}
+
+ATF_TC(basic);
+ATF_TC_HEAD(basic, conf)
+{
+ atf_tc_set_md_var(conf, "require.user", "root");
+}
+
+ATF_TC_BODY(basic, dummy)
+{
+ _basic(1);
+}
+
+ATF_TC(reverse);
+ATF_TC_HEAD(reverse, conf)
+{
+ atf_tc_set_md_var(conf, "require.user", "root");
+}
+
+ATF_TC_BODY(reverse, dummy)
+{
+ _basic(-1);
+}
+
+static void _ethertype(int);
+static void get_ethertype(void *data, size_t len, void *ctx);
+
+static void
+get_ethertype(void *data, size_t len, void *ctx)
+{
+ int *v = ctx, i;
+ struct frame *f = data;
+
+ (void)len;
+ for (i = 0; i < 10; i++)
+ v[i] = ntohs(f->vlan[i].proto);
+}
+
+static void
+_ethertype(int direction)
+{
+ int r[10];
+ int i, rounds = 20;
+
+ ng_init();
+ ng_errors(PASS);
+ ng_shutdown("vr:");
+ ng_errors(FAIL);
+
+ ng_mkpeer(".", "a", "vlan_rotate", direction > 0 ? "original" : "ordered");
+ ng_name("a", "vr");
+ ng_connect(".", "b", "vr:", direction > 0 ? "ordered" : "original");
+ ng_register_data("b", get_ethertype);
+
+ while (rounds-- > 0)
+ {
+ char cmd[40];
+ int len = 9;
+ int rot = rand() % (2 * len - 1) - len + 1;
+ int vlan[10];
+
+ for (i = 0; i < len; i++)
+ {
+ switch (rand() % 3)
+ {
+ default:
+ msg.vlan[i].proto = htons(ETHERTYPE_VLAN);
+ break;
+ case 1:
+ msg.vlan[i].proto = htons(ETHERTYPE_QINQ);
+ break;
+ case 2:
+ msg.vlan[i].proto = htons(ETHERTYPE_8021Q9100);
+ break;
+ }
+ }
+ msg.vlan[i].proto = htons(ETHERTYPE_IP);
+
+ for (i = 0; i < len; i++)
+ vlan[i] = msg.vlan[i].proto;
+
+ snprintf(cmd, sizeof(cmd), "setconf { min=0 max=9 rot=%d }", rot);
+ ng_send_msg("vr:", cmd);
+
+ bzero(r, sizeof(r));
+ ng_send_data("a", &msg, sizeof(msg));
+ ng_handle_events(50, &r);
+
+ /* check rotation */
+ for (i = 0; i < len; i++)
+ {
+ int expect = (2 * len + i - direction * rot) % len;
+
+ ATF_CHECK_MSG(r[i] == ntohs(vlan[expect]),
+ "len=%d rot=%d i=%d -> vlan=%04x, expect(%d)=%04x",
+ len, rot, i, ntohs(r[i]), expect, vlan[expect]);
+ }
+ }
+
+ ng_shutdown("vr:");
+}
+
+ATF_TC(ethertype);
+ATF_TC_HEAD(ethertype, conf)
+{
+ atf_tc_set_md_var(conf, "require.user", "root");
+}
+
+ATF_TC_BODY(ethertype, dummy)
+{
+ _ethertype(1);
+}
+
+ATF_TC(typeether);
+ATF_TC_HEAD(typeether, conf)
+{
+ atf_tc_set_md_var(conf, "require.user", "root");
+}
+
+ATF_TC_BODY(typeether, dummy)
+{
+ _ethertype(-1);
+}
+
+ATF_TC(minmax);
+ATF_TC_HEAD(minmax, conf)
+{
+ atf_tc_set_md_var(conf, "require.user", "root");
+}
+
+ATF_TC_BODY(minmax, dummy)
+{
+ ng_counter_t r;
+ int len;
+
+ ng_init();
+ ng_errors(PASS);
+ ng_shutdown("vr:");
+ ng_errors(FAIL);
+
+ ng_mkpeer(".", "a", "vlan_rotate", "original");
+ ng_name("a", "vr");
+ ng_connect(".", "b", "vr:", "ordered");
+ ng_connect(".", "c", "vr:", "excessive");
+ ng_connect(".", "d", "vr:", "incomplete");
+ ng_register_data("a", get_data0);
+ ng_register_data("b", get_data1);
+ ng_register_data("c", get_data2);
+ ng_register_data("d", get_data3);
+
+ ng_send_msg("vr:", "setconf { min=3 max=7 rot=0 }");
+ for (len = 9; len > 0; len--)
+ {
+ /* reduce the number of vlans */
+ msg.vlan[len].proto = htons(ETHERTYPE_IP);
+
+ ng_counter_clear(r);
+ ng_send_data("a", &msg, sizeof(msg));
+ ng_handle_events(50, &r);
+ if (len < 3)
+ ATF_CHECK(r[0] == 0 && r[1] == 0 && r[2] == 0 && r[3] == 1);
+ else if (len > 7)
+ ATF_CHECK(r[0] == 0 && r[1] == 0 && r[2] == 1 && r[3] == 0);
+ else
+ ATF_CHECK(r[0] == 0 && r[1] == 1 && r[2] == 0 && r[3] == 0);
+
+ ng_counter_clear(r);
+ ng_send_data("b", &msg, sizeof(msg));
+ ng_handle_events(50, &r);
+ if (len < 3)
+ ATF_CHECK(r[0] == 0 && r[1] == 0 && r[2] == 0 && r[3] == 1);
+ else if (len > 7)
+ ATF_CHECK(r[0] == 0 && r[1] == 0 && r[2] == 1 && r[3] == 0);
+ else
+ ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 0 && r[3] == 0);
+
+ ng_counter_clear(r);
+ ng_send_data("c", &msg, sizeof(msg));
+ ng_handle_events(50, &r);
+ ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 0 && r[3] == 0);
+
+ ng_counter_clear(r);
+ ng_send_data("d", &msg, sizeof(msg));
+ ng_handle_events(50, &r);
+ ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 0 && r[3] == 0);
+ }
+
+ ng_shutdown("vr:");
+}
+
+ATF_TP_ADD_TCS(vlan_rotate)
+{
+ /* Use "dd if=/dev/random bs=2 count=1 | od -x" to reproduce */
+ srand(0xb93b);
+
+ ATF_TP_ADD_TC(vlan_rotate, basic);
+ ATF_TP_ADD_TC(vlan_rotate, ethertype);
+ ATF_TP_ADD_TC(vlan_rotate, reverse);
+ ATF_TP_ADD_TC(vlan_rotate, typeether);
+ ATF_TP_ADD_TC(vlan_rotate, minmax);
+
+ return atf_no_error();
+}