Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 25 Feb 2021 10:12:45 GMT
From:      Lutz Donnerhacke <donner@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org
Subject:   git: b2c14db4f8fd - stable/11 - netgraph/ng_vlan_rotate: IEEE 802.1ad VLAN manipulation netgraph type
Message-ID:  <202102251012.11PACjQf033252@gitrepo.freebsd.org>

next in thread | raw e-mail | index | archive | help
The branch stable/11 has been updated by donner:

URL: https://cgit.FreeBSD.org/src/commit/?id=b2c14db4f8fda2c015a9b58f6d8b0e29fea3779d

commit b2c14db4f8fda2c015a9b58f6d8b0e29fea3779d
Author:     Lutz Donnerhacke <donner@FreeBSD.org>
AuthorDate: 2021-01-26 15:50:04 +0000
Commit:     Lutz Donnerhacke <donner@FreeBSD.org>
CommitDate: 2021-02-25 10:12:17 +0000

    netgraph/ng_vlan_rotate: IEEE 802.1ad VLAN manipulation netgraph type
    
    This node is part of an A10-NSP (L2-BSA) development.
    
    Carrier networks tend to stack three or more tags for internal
    purposes and therefore hiding the service tags deep inside of the
    stack. When decomposing such an access network frame, the processing
    order is typically reversed: First distinguish by service, than by
    other means.
    
    This new netgragh node allows to bring the relevant VLAN in front (to
    the out-most position). This way other netgraph nodes (like ng_vlan)
    can operate on this specific type.
    
    Reviewed by:    manpages (gbe), brueffer (manpages), kp
    Relnotes:       yes
    Sponsored by:   IKS Service GmbH
    Differential Revision: https://reviews.freebsd.org/D22076
    
    (cherry picked from commit cfd6422a5217410fbd66f7a7a8a64d9d85e61229)
---
 share/man/man4/Makefile                   |   1 +
 share/man/man4/ng_vlan_rotate.4           | 252 +++++++++++++++
 sys/conf/files                            |   1 +
 sys/modules/netgraph/Makefile             |   3 +-
 sys/modules/netgraph/vlan_rotate/Makefile |   6 +
 sys/netgraph/ng_vlan_rotate.c             | 510 ++++++++++++++++++++++++++++++
 sys/netgraph/ng_vlan_rotate.h             |  67 ++++
 7 files changed, 839 insertions(+), 1 deletion(-)

diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile
index 44e0c3ab22f9..837e667217b3 100644
--- a/share/man/man4/Makefile
+++ b/share/man/man4/Makefile
@@ -391,6 +391,7 @@ MAN=	aac.4 \
 	ng_uni.4 \
 	ng_vjc.4 \
 	ng_vlan.4 \
+	ng_vlan_rotate.4 \
 	nmdm.4 \
 	nsp.4 \
 	${_ntb.4} \
diff --git a/share/man/man4/ng_vlan_rotate.4 b/share/man/man4/ng_vlan_rotate.4
new file mode 100644
index 000000000000..4640737c7d54
--- /dev/null
+++ b/share/man/man4/ng_vlan_rotate.4
@@ -0,0 +1,252 @@
+.\"-
+.\" SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+.\"
+.\" Copyright (c) 2019-2021 IKS Service GmbH
+.\"
+.\" 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.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+.\"
+.\" Author: Lutz Donnerhacke <lutz@donnerhacke.de>
+.\"
+.\" $FreeBSD$
+.\"
+.Dd January 26, 2021
+.Dt NG_VLAN_ROTATE 4
+.Os
+.Sh NAME
+.Nm ng_vlan_rotate
+.Nd IEEE 802.1ad VLAN manipulation netgraph node type
+.Sh SYNOPSIS
+.In sys/types.h
+.In netgraph.h
+.In netgraph/ng_vlan_rotate.h
+.Sh DESCRIPTION
+The
+.Nm
+node type manipulates the order of VLAN tags of frames tagged
+according to the IEEE 802.1ad (an extension of IEEE 802.1Q) standard
+between different hooks.
+.Pp
+Each node has four special hooks,
+.Va original ,
+.Va ordered ,
+.Va excessive ,
+and
+.Va incomplete .
+.Pp
+A frame tagged with an arbitrary number of
+.Dv ETHERTYPE_VLAN ,
+.Dv ETHERTYPE_QINQ ,
+and
+.Dv 0x9100
+tags received on the
+.Va original
+hook will be rearranged to a new order of those tags and is sent out
+the
+.Dq ordered
+hook.
+After successful processing the
+.Va histogram
+counter for the observed stack size increments.
+.Pp
+If it contains fewer VLANs in the stack than the configured
+.Va min
+limit, the frame is sent out to the
+.Va incomplete
+hook and the
+.Va incomplete
+counter increments.
+.Pp
+If there are more VLANs in the stack than the configured
+.Va max
+limit, the frame is sent out to the
+.Va excessive
+hook and the
+.Va excessive
+counter increments.
+.Pp
+If the destination hook is not connected, the frame is dropped and the
+.Va drops
+counter increments.
+.Pp
+For Ethernet frames received on the
+.Va ordered
+hook, the transformation is reversed and is passed to the
+.Va original
+hook.
+Please note that this process is identical to the one described
+above, besides the ordered/original hooks are swapped and the
+transformation is reversed.
+.Pp
+An Ethernet frame received on the
+.Va incomplete
+or
+.Va excessive
+hook is forwarded to the
+.Va original
+hook without any modification.
+.Pp
+This node supports only one operation at the moment: Rotation of the
+VLANs in the stack.
+Setting the configuration parameter
+.Va rot
+to a positive value, the stack will roll up by this amount.
+Negative values will roll down.
+A typical scenario is setting the value to 1 in order to bring the
+innermost VLAN tag to the outmost level.
+Rotation includes the VLAN id, the ether type, and the QOS parameters
+pcp and cfi.
+Typical QOS handling refers to the outmost setting, so be careful to
+keep your QOS intact.
+.Sh HOOKS
+This node type supports the following hooks:
+.Bl -tag -width incomplete
+.It Va original
+Typically this hook would be connected to a
+.Xr ng_ether 4
+node, using the
+.Va lower
+hook connected to a carrier network.
+.It Va ordered
+Typically this hook would be connected to a
+.Xr ng_vlan 4
+type node using the
+.Va downstream
+hook in order to separate services.
+.It Va excessive
+see below.
+.It Va incomplete
+Typically those hooks would be attached to a
+.Xr ng_eiface 4
+type node using the
+.Va ether
+hook for anomaly monitoring purposes.
+.El
+.Sh CONTROL MESSAGES
+This node type supports the generic control messages, plus the following:
+.Bl -tag -width foo
+.It Dv NGM_VLANROTATE_GET_CONF Pq Ic getconf
+Read the current configuration.
+.It Dv NGM_VLANROTATE_SET_CONF Pq Ic setconf
+Set the current configuration.
+.It Dv NGM_VLANROTATE_GET_STAT Pq Ic getstat
+Read the current statistics.
+.It Dv NGM_VLANROTATE_CLR_STAT Pq Ic clrstat
+Zeroize the statistics.
+.It Dv NGM_VLANROTATE_GETCLR_STAT Pq Ic getclrstat
+Read the current statistics and zeroize it in one step.
+.El
+.Sh EXAMPLES
+The first example demonstrates how to rotate double or triple tagged
+frames so that the innermost C-VLAN can be used as service
+discriminator.
+The single or double tagged frames (C-VLAN removed) are sent out to an
+interface pointing to different infrastucture.
+.Bd -literal
+#!/bin/sh
+
+BNG_IF=ixl3
+VOIP_IF=bge2
+
+ngctl -f- <<EOF
+mkpeer ${BNG_IF}: vlan_rotate lower original
+name ${BNG_IF}:lower rotate
+msg rotate: setconf { min=2 max=3 rot=1 }
+mkpeer rotate: vlan ordered downstream
+name rotate:ordered services
+connect services: ${VOIP_IF} voip lower
+msg services: addfilter { vlan=123 hook="voip" }
+EOF
+.Ed
+.Pp
+Now inject the following sample frame on the
+.Dv BNG_IF
+interface:
+.Bd -literal
+00:00:00:00:01:01 > 00:01:02:03:04:05,
+ ethertype 802.1Q-9100 (0x9100), length 110: vlan 2, p 1,
+ ethertype 802.1Q-QinQ, vlan 101, p 0,
+ ethertype 802.1Q, vlan 123, p 7,
+ ethertype IPv4, (tos 0x0, ttl 64, id 15994, offset 0, flags [none],
+  proto ICMP (1), length 84) 192.168.140.101 > 192.168.140.1:
+  ICMP echo request, id 40234, seq 0, length 64
+.Ed
+.Pp
+The frame ejected on the
+.Va ordered
+hook will look like this:
+.Bd -literal
+00:00:00:00:01:01 > 00:01:02:03:04:05,
+ ethertype 802.1Q (0x8100), length 110: vlan 123, p 7,
+ ethertype 802.1Q-9100, vlan 2, p 1,
+ ethertype 802.1Q-QinQ, vlan 101, p 0,
+ ethertype IPv4, (tos 0x0, ttl 64, id 15994, offset 0, flags [none],
+  proto ICMP (1), length 84) 192.168.140.101 > 192.168.140.1:
+  ICMP echo request, id 40234, seq 0, length 64
+.Ed
+.Pp
+Hence, the frame pushed out to the
+.Dv VOIP_IF
+will have this form:
+.Bd -literal
+00:00:00:00:01:01 > 00:01:02:03:04:05,
+ ethertype 802.1Q-9100, vlan 2, p 1,
+ ethertype 802.1Q-QinQ, vlan 101, p 0,
+ ethertype IPv4, (tos 0x0, ttl 64, id 15994, offset 0, flags [none],
+  proto ICMP (1), length 84) 192.168.140.101 > 192.168.140.1:
+  ICMP echo request, id 40234, seq 0, length 64
+.Ed
+.Pp
+The second example distinguishes between double tagged and single
+tagged frames.
+.Bd -literal
+#!/bin/sh
+
+IN_IF=bge1
+
+ngctl -f- <<EOF
+mkpeer ${IN_IF}: vlan_rotate lower original
+name ${IN_IF}:lower separate
+msg separate: setconf { min=1 max=1 rot=0 }
+mkpeer separate: eiface incomplete ether
+name separate:incomplete untagged
+mkpeer separate: eiface ordered ether
+name separate:ordered tagged
+EOF
+.Ed
+.Pp
+Setting the
+.Va rot
+parameter to zero (or omitting it) does not change
+the order of the tags within the frame.
+Frames with more VLAN tags are dropped.
+.Sh SHUTDOWN
+This node shuts down upon receipt of a
+.Dv NGM_SHUTDOWN
+control message, or when all hooks have been disconnected.
+.Sh SEE ALSO
+.Xr netgraph 4 ,
+.Xr ng_eiface 4 ,
+.Xr ng_ether 4 ,
+.Xr ng_vlan 4 ,
+.Xr ngctl 8
+.Sh AUTHORS
+.An Lutz Donnerhacke Aq Mt lutz@donnerhacke.de
diff --git a/sys/conf/files b/sys/conf/files
index 05c2fdf0351d..0356ac8fd471 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -4087,6 +4087,7 @@ netgraph/ng_tee.c		optional netgraph_tee
 netgraph/ng_tty.c		optional netgraph_tty
 netgraph/ng_vjc.c		optional netgraph_vjc
 netgraph/ng_vlan.c		optional netgraph_vlan
+netgraph/ng_vlan_rotate.c	optional netgraph_vlan_rotate
 netinet/accf_data.c		optional accept_filter_data inet
 netinet/accf_dns.c		optional accept_filter_dns inet
 netinet/accf_http.c		optional accept_filter_http inet
diff --git a/sys/modules/netgraph/Makefile b/sys/modules/netgraph/Makefile
index ac674d16d812..20a98bfe357f 100644
--- a/sys/modules/netgraph/Makefile
+++ b/sys/modules/netgraph/Makefile
@@ -52,7 +52,8 @@ SUBDIR=	async \
 	tty \
 	UI \
 	vjc \
-	vlan
+	vlan \
+	vlan_rotate
 
 .if ${MK_BLUETOOTH} != "no" || defined(ALL_MODULES)
 _bluetooth=	bluetooth
diff --git a/sys/modules/netgraph/vlan_rotate/Makefile b/sys/modules/netgraph/vlan_rotate/Makefile
new file mode 100644
index 000000000000..98ff2b13863f
--- /dev/null
+++ b/sys/modules/netgraph/vlan_rotate/Makefile
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+KMOD=	ng_vlan_rotate
+SRCS=	ng_vlan_rotate.c
+
+.include <bsd.kmod.mk>
diff --git a/sys/netgraph/ng_vlan_rotate.c b/sys/netgraph/ng_vlan_rotate.c
new file mode 100644
index 000000000000..e6de782d9bb3
--- /dev/null
+++ b/sys/netgraph/ng_vlan_rotate.c
@@ -0,0 +1,510 @@
+/*-
+ * Spdx-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2019-2021 IKS Service GmbH
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * Author: Lutz Donnerhacke <lutz@donnerhacke.de>
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/ctype.h>
+#include <sys/errno.h>
+#include <sys/syslog.h>
+#include <sys/types.h>
+#include <sys/counter.h>
+
+#include <net/ethernet.h>
+
+#include <netgraph/ng_message.h>
+#include <netgraph/ng_parse.h>
+#include <netgraph/ng_vlan_rotate.h>
+#include <netgraph/netgraph.h>
+
+/*
+ * This section contains the netgraph method declarations for the
+ * sample node. These methods define the netgraph 'type'.
+ */
+
+static ng_constructor_t ng_vlanrotate_constructor;
+static ng_rcvmsg_t ng_vlanrotate_rcvmsg;
+static ng_shutdown_t ng_vlanrotate_shutdown;
+static ng_newhook_t ng_vlanrotate_newhook;
+static ng_rcvdata_t ng_vlanrotate_rcvdata;
+static ng_disconnect_t ng_vlanrotate_disconnect;
+
+/* Parse type for struct ng_vlanrotate_conf */
+static const struct ng_parse_struct_field ng_vlanrotate_conf_fields[] = {
+	{"rot", &ng_parse_int8_type},
+	{"min", &ng_parse_uint8_type},
+	{"max", &ng_parse_uint8_type},
+	{NULL}
+};
+static const struct ng_parse_type ng_vlanrotate_conf_type = {
+	&ng_parse_struct_type,
+	&ng_vlanrotate_conf_fields
+};
+
+/* Parse type for struct ng_vlanrotate_stat */
+static struct ng_parse_fixedarray_info ng_vlanrotate_stat_hist_info = {
+	&ng_parse_uint64_type,
+	NG_VLANROTATE_MAX_VLANS
+};
+static struct ng_parse_type ng_vlanrotate_stat_hist = {
+	&ng_parse_fixedarray_type,
+	&ng_vlanrotate_stat_hist_info
+};
+static const struct ng_parse_struct_field ng_vlanrotate_stat_fields[] = {
+	{"drops", &ng_parse_uint64_type},
+	{"excessive", &ng_parse_uint64_type},
+	{"incomplete", &ng_parse_uint64_type},
+	{"histogram", &ng_vlanrotate_stat_hist},
+	{NULL}
+};
+static struct ng_parse_type ng_vlanrotate_stat_type = {
+	&ng_parse_struct_type,
+	&ng_vlanrotate_stat_fields
+};
+
+
+/* List of commands and how to convert arguments to/from ASCII */
+static const struct ng_cmdlist ng_vlanrotate_cmdlist[] = {
+	{
+		NGM_VLANROTATE_COOKIE,
+		NGM_VLANROTATE_GET_CONF,
+		"getconf",
+		NULL,
+		&ng_vlanrotate_conf_type,
+	},
+	{
+		NGM_VLANROTATE_COOKIE,
+		NGM_VLANROTATE_SET_CONF,
+		"setconf",
+		&ng_vlanrotate_conf_type,
+		NULL
+	},
+	{
+		NGM_VLANROTATE_COOKIE,
+		NGM_VLANROTATE_GET_STAT,
+		"getstat",
+		NULL,
+		&ng_vlanrotate_stat_type
+	},
+	{
+		NGM_VLANROTATE_COOKIE,
+		NGM_VLANROTATE_CLR_STAT,
+		"clrstat",
+		NULL,
+		&ng_vlanrotate_stat_type
+	},
+	{
+		NGM_VLANROTATE_COOKIE,
+		NGM_VLANROTATE_GETCLR_STAT,
+		"getclrstat",
+		NULL,
+		&ng_vlanrotate_stat_type
+	},
+	{0}
+};
+
+/* Netgraph node type descriptor */
+static struct ng_type typestruct = {
+	.version = NG_ABI_VERSION,
+	.name = NG_VLANROTATE_NODE_TYPE,
+	.constructor = ng_vlanrotate_constructor,
+	.rcvmsg = ng_vlanrotate_rcvmsg,
+	.shutdown = ng_vlanrotate_shutdown,
+	.newhook = ng_vlanrotate_newhook,
+	.rcvdata = ng_vlanrotate_rcvdata,
+	.disconnect = ng_vlanrotate_disconnect,
+	.cmdlist = ng_vlanrotate_cmdlist,
+};
+NETGRAPH_INIT(vlanrotate, &typestruct);
+
+struct ng_vlanrotate_kernel_stats {
+	counter_u64_t	drops, excessive, incomplete;
+	counter_u64_t	histogram[NG_VLANROTATE_MAX_VLANS];
+};
+
+/* Information we store for each node */
+struct vlanrotate {
+	hook_p		original_hook;
+	hook_p		ordered_hook;
+	hook_p		excessive_hook;
+	hook_p		incomplete_hook;
+	struct ng_vlanrotate_conf conf;
+	struct ng_vlanrotate_kernel_stats stats;
+};
+typedef struct vlanrotate *vlanrotate_p;
+
+/*
+ * Set up the private data structure.
+ */
+static int
+ng_vlanrotate_constructor(node_p node)
+{
+	int i;
+
+	vlanrotate_p vrp = malloc(sizeof(*vrp), M_NETGRAPH, M_WAITOK | M_ZERO);
+
+	vrp->conf.max = NG_VLANROTATE_MAX_VLANS;
+
+	vrp->stats.drops = counter_u64_alloc(M_WAITOK);
+	vrp->stats.excessive = counter_u64_alloc(M_WAITOK);
+	vrp->stats.incomplete = counter_u64_alloc(M_WAITOK);
+	for (i = 0; i < NG_VLANROTATE_MAX_VLANS; i++)
+		vrp->stats.histogram[i] = counter_u64_alloc(M_WAITOK);
+
+	NG_NODE_SET_PRIVATE(node, vrp);
+	return (0);
+}
+
+/*
+ * Give our ok for a hook to be added.
+ */
+static int
+ng_vlanrotate_newhook(node_p node, hook_p hook, const char *name)
+{
+	const vlanrotate_p vrp = NG_NODE_PRIVATE(node);
+	hook_p *dst = NULL;
+
+	if (strcmp(name, NG_VLANROTATE_HOOK_ORDERED) == 0) {
+		dst = &vrp->ordered_hook;
+	} else if (strcmp(name, NG_VLANROTATE_HOOK_ORIGINAL) == 0) {
+		dst = &vrp->original_hook;
+	} else if (strcmp(name, NG_VLANROTATE_HOOK_EXCESSIVE) == 0) {
+		dst = &vrp->excessive_hook;
+	} else if (strcmp(name, NG_VLANROTATE_HOOK_INCOMPLETE) == 0) {
+		dst = &vrp->incomplete_hook;
+	} else
+		return (EINVAL);	/* not a hook we know about */
+
+	if (*dst != NULL)
+		return (EADDRINUSE);	/* don't override */
+
+	*dst = hook;
+	return (0);
+}
+
+/*
+ * Get a netgraph control message.
+ * A response is not required.
+ */
+static int
+ng_vlanrotate_rcvmsg(node_p node, item_p item, hook_p lasthook)
+{
+	const vlanrotate_p vrp = NG_NODE_PRIVATE(node);
+	struct ng_mesg *resp = NULL;
+	struct ng_mesg *msg;
+	struct ng_vlanrotate_conf *pcf;
+	int error = 0;
+
+	NGI_GET_MSG(item, msg);
+	/* Deal with message according to cookie and command */
+	switch (msg->header.typecookie) {
+	case NGM_VLANROTATE_COOKIE:
+		switch (msg->header.cmd) {
+		case NGM_VLANROTATE_GET_CONF:
+			NG_MKRESPONSE(resp, msg, sizeof(vrp->conf), M_NOWAIT);
+			if (!resp) {
+				error = ENOMEM;
+				break;
+			}
+			*((struct ng_vlanrotate_conf *)resp->data) = vrp->conf;
+			break;
+		case NGM_VLANROTATE_SET_CONF:
+			if (msg->header.arglen != sizeof(*pcf)) {
+				error = EINVAL;
+				break;
+			}
+
+			pcf = (struct ng_vlanrotate_conf *)msg->data;
+
+			if (pcf->max == 0)	/* keep current value */
+				pcf->max = vrp->conf.max;
+
+			if ((pcf->max > NG_VLANROTATE_MAX_VLANS) ||
+			    (pcf->min > pcf->max) ||
+			    (abs(pcf->rot) >= pcf->max)) {
+				error = EINVAL;
+				break;
+			}
+
+			vrp->conf = *pcf;
+			break;
+		case NGM_VLANROTATE_GET_STAT:
+		case NGM_VLANROTATE_GETCLR_STAT:
+		{
+			struct ng_vlanrotate_stat *p;
+			int i;
+
+			NG_MKRESPONSE(resp, msg, sizeof(*p), M_NOWAIT);
+			if (!resp) {
+				error = ENOMEM;
+				break;
+			}
+			p = (struct ng_vlanrotate_stat *)resp->data;
+			p->drops = counter_u64_fetch(vrp->stats.drops);
+			p->excessive = counter_u64_fetch(vrp->stats.excessive);
+			p->incomplete = counter_u64_fetch(vrp->stats.incomplete);
+			for (i = 0; i < NG_VLANROTATE_MAX_VLANS; i++)
+				p->histogram[i] = counter_u64_fetch(vrp->stats.histogram[i]);
+			if (msg->header.cmd != NGM_VLANROTATE_GETCLR_STAT)
+				break;
+		}
+		case NGM_VLANROTATE_CLR_STAT:
+		{
+			int i;
+
+			counter_u64_zero(vrp->stats.drops);
+			counter_u64_zero(vrp->stats.excessive);
+			counter_u64_zero(vrp->stats.incomplete);
+			for (i = 0; i < NG_VLANROTATE_MAX_VLANS; i++)
+				counter_u64_zero(vrp->stats.histogram[i]);
+			break;
+		}
+		default:
+			error = EINVAL;	/* unknown command */
+			break;
+		}
+		break;
+	default:
+		error = EINVAL;	/* unknown cookie type */
+		break;
+	}
+
+	/* Take care of synchronous response, if any */
+	NG_RESPOND_MSG(error, node, item, resp);
+	/* Free the message and return */
+	NG_FREE_MSG(msg);
+	return (error);
+}
+
+/*
+ * Receive data, and do rotate the vlans as desired.
+ *
+ * Rotating is quite complicated if the rotation offset and the number
+ * of vlans are not relativly prime. In this case multiple slices need
+ * to be rotated separately.
+ *
+ * Rotation can be additive or subtractive. Some examples:
+ *  01234   5 vlans given
+ *  -----
+ *  34012  +2 rotate
+ *  12340  +4 rotate
+ *  12340  -1 rotate
+ *
+ * First some helper functions ...
+ */
+
+struct ether_vlan_stack_entry {
+	uint16_t	proto;
+	uint16_t	tag;
+}		__packed;
+
+struct ether_vlan_stack_header {
+	uint8_t		dst[ETHER_ADDR_LEN];
+	uint8_t		src[ETHER_ADDR_LEN];
+	struct ether_vlan_stack_entry vlan_stack[1];
+}		__packed;
+
+static int
+ng_vlanrotate_gcd(int a, int b)
+{
+	if (b == 0)
+		return a;
+	else
+		return ng_vlanrotate_gcd(b, a % b);
+}
+
+static void
+ng_vlanrotate_rotate(struct ether_vlan_stack_entry arr[], int d, int n)
+{
+	int		i, j, k;
+	struct ether_vlan_stack_entry temp;
+
+	/* for each commensurable slice */
+	for (i = ng_vlanrotate_gcd(d, n); i-- > 0;) {
+		/* rotate left aka downwards */
+		temp = arr[i];
+		j = i;
+
+		while (1) {
+			k = j + d;
+			if (k >= n)
+				k = k - n;
+			if (k == i)
+				break;
+			arr[j] = arr[k];
+			j = k;
+		}
+
+		arr[j] = temp;
+	}
+}
+
+static int
+ng_vlanrotate_rcvdata(hook_p hook, item_p item)
+{
+	const vlanrotate_p vrp = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+	struct ether_vlan_stack_header *evsh;
+	struct mbuf *m = NULL;
+	hook_p	dst_hook;
+	int8_t	rotate;
+	int8_t	vlans = 0;
+	int	error = ENOSYS;
+
+	NGI_GET_M(item, m);
+
+	if (hook == vrp->ordered_hook) {
+		rotate = +vrp->conf.rot;
+		dst_hook = vrp->original_hook;
+	} else if (hook == vrp->original_hook) {
+		rotate = -vrp->conf.rot;
+		dst_hook = vrp->ordered_hook;
+	} else {
+		dst_hook = vrp->original_hook;
+		goto send;	/* everything else goes out unmodified */
+	}
+
+	if (dst_hook == NULL) {
+		error = ENETDOWN;
+		goto fail;
+	}
+
+	/* count the vlans */
+	for (vlans = 0; vlans <= NG_VLANROTATE_MAX_VLANS; vlans++) {
+		size_t expected_len = sizeof(struct ether_vlan_stack_header)
+		    + vlans * sizeof(struct ether_vlan_stack_entry);
+
+		if (m->m_len < expected_len) {
+			m = m_pullup(m, expected_len);
+			if (m == NULL) {
+				error = EINVAL;
+				goto fail;
+			}
+		}
+
+		evsh = mtod(m, struct ether_vlan_stack_header *);
+		switch (ntohs(evsh->vlan_stack[vlans].proto)) {
+		case ETHERTYPE_VLAN:
+		case ETHERTYPE_QINQ:
+		case ETHERTYPE_8021Q9100:
+		case ETHERTYPE_8021Q9200:
+		case ETHERTYPE_8021Q9300:
+			break;
+		default:
+			goto out;
+		}
+	}
+out:
+	if ((vlans > vrp->conf.max) || (vlans >= NG_VLANROTATE_MAX_VLANS)) {
+		counter_u64_add(vrp->stats.excessive, 1);
+		dst_hook = vrp->excessive_hook;
+		goto send;
+	}
+
+	if ((vlans < vrp->conf.min) || (vlans <= abs(rotate))) {
+		counter_u64_add(vrp->stats.incomplete, 1);
+		dst_hook = vrp->incomplete_hook;
+		goto send;
+	}
+	counter_u64_add(vrp->stats.histogram[vlans], 1);
+
+	/* rotating upwards always (using modular arithmetics) */
+	if (rotate == 0) {
+		/* nothing to do */
+	} else if (rotate > 0) {
+		ng_vlanrotate_rotate(evsh->vlan_stack, rotate, vlans);
+	} else {
+		ng_vlanrotate_rotate(evsh->vlan_stack, vlans + rotate, vlans);
+	}
+
+send:
+	if (dst_hook == NULL)
+		goto fail;
+	NG_FWD_NEW_DATA(error, item, dst_hook, m);
+	return 0;
+
+fail:
+	counter_u64_add(vrp->stats.drops, 1);
+	if (m != NULL)
+		m_freem(m);
+	NG_FREE_ITEM(item);
+	return (error);
+}
+
+/*
+ * Do local shutdown processing..
+ * All our links and the name have already been removed.
+ */
+static int
+ng_vlanrotate_shutdown(node_p node)
+{
+	const		vlanrotate_p vrp = NG_NODE_PRIVATE(node);
+	int i;
+
+	NG_NODE_SET_PRIVATE(node, NULL);
+
+	counter_u64_free(vrp->stats.drops);
+	counter_u64_free(vrp->stats.excessive);
+	counter_u64_free(vrp->stats.incomplete);
+	for (i = 0; i < NG_VLANROTATE_MAX_VLANS; i++)
+		counter_u64_free(vrp->stats.histogram[i]);
+
+	free(vrp, M_NETGRAPH);
+
+	NG_NODE_UNREF(node);
+	return (0);
+}
+
+/*
+ * Hook disconnection
+ * For this type, removal of the last link destroys the node
+ */
+static int
+ng_vlanrotate_disconnect(hook_p hook)
+{
+	const		vlanrotate_p vrp = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+
+	if (vrp->original_hook == hook)
+		vrp->original_hook = NULL;
+	if (vrp->ordered_hook == hook)
+		vrp->ordered_hook = NULL;
+	if (vrp->excessive_hook == hook)
+		vrp->excessive_hook = NULL;
+	if (vrp->incomplete_hook == hook)
+		vrp->incomplete_hook = NULL;
+
+	/* during shutdown the node is invalid, don't shutdown twice */
+	if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) &&
+	    (NG_NODE_IS_VALID(NG_HOOK_NODE(hook))))
+		ng_rmnode_self(NG_HOOK_NODE(hook));
+	return (0);
+}
diff --git a/sys/netgraph/ng_vlan_rotate.h b/sys/netgraph/ng_vlan_rotate.h
new file mode 100644
index 000000000000..ef6dfd6b506a
--- /dev/null
+++ b/sys/netgraph/ng_vlan_rotate.h
@@ -0,0 +1,67 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2019-2021 IKS Service GmbH
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * Author: Lutz Donnerhacke <lutz@donnerhacke.de>
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _NETGRAPH_NG_VLAN_ROTATE_H_
+#define _NETGRAPH_NG_VLAN_ROTATE_H_
+
+#define NG_VLANROTATE_NODE_TYPE		"vlan_rotate"
+#define NGM_VLANROTATE_COOKIE		1568378766
+
+/* Hook names */
+#define NG_VLANROTATE_HOOK_ORDERED	"ordered"
+#define NG_VLANROTATE_HOOK_ORIGINAL	"original"
+#define NG_VLANROTATE_HOOK_EXCESSIVE	"excessive"
+#define NG_VLANROTATE_HOOK_INCOMPLETE	"incomplete"
+
+/* Limits */
+#define NG_VLANROTATE_MAX_VLANS		10
+
+/* Datastructures for netgraph commands */
+struct ng_vlanrotate_conf {
+	int8_t		rot;
+	uint8_t		min, max;
+};
+
+struct ng_vlanrotate_stat {
+	uint64_t	drops, excessive, incomplete;
+	uint64_t	histogram[NG_VLANROTATE_MAX_VLANS];
+};
+
+/* Netgraph commands understood by this node type */
+enum {
+	NGM_VLANROTATE_GET_CONF = 1,
+	NGM_VLANROTATE_SET_CONF,
+	NGM_VLANROTATE_GET_STAT,
+	NGM_VLANROTATE_CLR_STAT,
+	NGM_VLANROTATE_GETCLR_STAT
+};
+
+#endif				/* _NETGRAPH_NG_VLAN_ROTATE_H_ */



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202102251012.11PACjQf033252>