Index: share/man/man4/Makefile =================================================================== --- share/man/man4/Makefile +++ share/man/man4/Makefile @@ -382,6 +382,7 @@ ng_uni.4 \ ng_vjc.4 \ ng_vlan.4 \ + ng_vlan_rotate.4 \ nmdm.4 \ ${_ntb.4} \ ${_ntb_hw_amd.4} \ Index: share/man/man4/ng_vlan_rotate.4 =================================================================== --- /dev/null +++ share/man/man4/ng_vlan_rotate.4 @@ -0,0 +1,257 @@ +.\"- +.\" SPDX-License-Identifier: BSD-2-Clause-FreeBSD +.\" +.\" Copyright (c) 2019 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 +.\" +.\" $FreeBSD$ +.\" +.Dd November 20, 2020 +.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 vlan_rotate +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- < 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- < Index: sys/netgraph/ng_vlan_rotate.h =================================================================== --- /dev/null +++ sys/netgraph/ng_vlan_rotate.h @@ -0,0 +1,67 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 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 + * + * $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_ */ Index: sys/netgraph/ng_vlan_rotate.c =================================================================== --- /dev/null +++ sys/netgraph/ng_vlan_rotate.c @@ -0,0 +1,470 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 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 + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +/* + * 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); + +/* 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_stat stat; +}; +typedef struct vlanrotate *vlanrotate_p; + +/* + * Set up the private data structure. + */ +static int +ng_vlanrotate_constructor(node_p node) +{ + vlanrotate_p vrp = malloc(sizeof(*vrp), M_NETGRAPH, M_WAITOK | M_ZERO); + + vrp->conf.max = NG_VLANROTATE_MAX_VLANS; + + 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; + } + + if (dst == NULL) + 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; + int error = 0; + struct ng_mesg *msg; + struct ng_vlanrotate_conf *pcf; + + 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) + pcf->max = vrp->conf.max; /* keep current value */ + + 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: + NG_MKRESPONSE(resp, msg, sizeof(vrp->stat), M_NOWAIT); + if (!resp) { + error = ENOMEM; + break; + } + *(struct ng_vlanrotate_stat *)resp->data = vrp->stat; + if (msg->header.cmd != NGM_VLANROTATE_GETCLR_STAT) + break; + case NGM_VLANROTATE_CLR_STAT: + bzero(&(vrp->stat), sizeof(vrp->stat)); + 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 seperatly. + * + * 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 mbuf *m = NULL; + hook_p dst_hook; + int8_t rotate; + int8_t vlans = 0; + int error = ENOSYS; + struct ether_vlan_stack_header *evsh; + + 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)) { + vrp->stat.excessive++; + dst_hook = vrp->excessive_hook; + goto send; + } + + if ((vlans < vrp->conf.min) || (vlans <= abs(rotate))) { + vrp->stat.incomplete++; + dst_hook = vrp->incomplete_hook; + goto send; + } + vrp->stat.histogram[vlans]++; + + /* 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: + vrp->stat.drops++; + 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); + + NG_NODE_SET_PRIVATE(node, NULL); + NG_NODE_UNREF(node); + free(vrp, M_NETGRAPH); + + 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); +}