xref: /freebsd/sys/netgraph/ng_vlan_rotate.c (revision 95ee2897e98f5d444f26ed2334cc7c439f9c16c6)
1cfd6422aSLutz Donnerhacke /*-
2*ecfb43cbSWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3cfd6422aSLutz Donnerhacke  *
4cfd6422aSLutz Donnerhacke  * Copyright (c) 2019-2021 IKS Service GmbH
5cfd6422aSLutz Donnerhacke  *
6cfd6422aSLutz Donnerhacke  * Redistribution and use in source and binary forms, with or without
7cfd6422aSLutz Donnerhacke  * modification, are permitted provided that the following conditions
8cfd6422aSLutz Donnerhacke  * are met:
9cfd6422aSLutz Donnerhacke  * 1. Redistributions of source code must retain the above copyright
10cfd6422aSLutz Donnerhacke  *    notice, this list of conditions and the following disclaimer.
11cfd6422aSLutz Donnerhacke  * 2. Redistributions in binary form must reproduce the above copyright
12cfd6422aSLutz Donnerhacke  *    notice, this list of conditions and the following disclaimer in the
13cfd6422aSLutz Donnerhacke  *    documentation and/or other materials provided with the distribution.
14cfd6422aSLutz Donnerhacke  *
15cfd6422aSLutz Donnerhacke  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16cfd6422aSLutz Donnerhacke  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17cfd6422aSLutz Donnerhacke  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18cfd6422aSLutz Donnerhacke  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19cfd6422aSLutz Donnerhacke  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20cfd6422aSLutz Donnerhacke  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21cfd6422aSLutz Donnerhacke  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22cfd6422aSLutz Donnerhacke  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23cfd6422aSLutz Donnerhacke  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24cfd6422aSLutz Donnerhacke  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25cfd6422aSLutz Donnerhacke  * SUCH DAMAGE.
26cfd6422aSLutz Donnerhacke  *
27cfd6422aSLutz Donnerhacke  * Author: Lutz Donnerhacke <lutz@donnerhacke.de>
28cfd6422aSLutz Donnerhacke  */
29cfd6422aSLutz Donnerhacke 
30cfd6422aSLutz Donnerhacke #include <sys/param.h>
31cfd6422aSLutz Donnerhacke #include <sys/systm.h>
32cfd6422aSLutz Donnerhacke #include <sys/kernel.h>
33cfd6422aSLutz Donnerhacke #include <sys/mbuf.h>
34cfd6422aSLutz Donnerhacke #include <sys/malloc.h>
35cfd6422aSLutz Donnerhacke #include <sys/ctype.h>
36cfd6422aSLutz Donnerhacke #include <sys/errno.h>
37cfd6422aSLutz Donnerhacke #include <sys/syslog.h>
38cfd6422aSLutz Donnerhacke #include <sys/types.h>
39cfd6422aSLutz Donnerhacke #include <sys/counter.h>
40cfd6422aSLutz Donnerhacke 
41cfd6422aSLutz Donnerhacke #include <net/ethernet.h>
42cfd6422aSLutz Donnerhacke 
43cfd6422aSLutz Donnerhacke #include <netgraph/ng_message.h>
44cfd6422aSLutz Donnerhacke #include <netgraph/ng_parse.h>
45cfd6422aSLutz Donnerhacke #include <netgraph/ng_vlan_rotate.h>
46cfd6422aSLutz Donnerhacke #include <netgraph/netgraph.h>
47cfd6422aSLutz Donnerhacke 
48cfd6422aSLutz Donnerhacke /*
49cfd6422aSLutz Donnerhacke  * This section contains the netgraph method declarations for the
50cfd6422aSLutz Donnerhacke  * sample node. These methods define the netgraph 'type'.
51cfd6422aSLutz Donnerhacke  */
52cfd6422aSLutz Donnerhacke 
53cfd6422aSLutz Donnerhacke static ng_constructor_t ng_vlanrotate_constructor;
54cfd6422aSLutz Donnerhacke static ng_rcvmsg_t ng_vlanrotate_rcvmsg;
55cfd6422aSLutz Donnerhacke static ng_shutdown_t ng_vlanrotate_shutdown;
56cfd6422aSLutz Donnerhacke static ng_newhook_t ng_vlanrotate_newhook;
57cfd6422aSLutz Donnerhacke static ng_rcvdata_t ng_vlanrotate_rcvdata;
58cfd6422aSLutz Donnerhacke static ng_disconnect_t ng_vlanrotate_disconnect;
59cfd6422aSLutz Donnerhacke 
60cfd6422aSLutz Donnerhacke /* Parse type for struct ng_vlanrotate_conf */
61cfd6422aSLutz Donnerhacke static const struct ng_parse_struct_field ng_vlanrotate_conf_fields[] = {
62cfd6422aSLutz Donnerhacke 	{"rot", &ng_parse_int8_type},
63cfd6422aSLutz Donnerhacke 	{"min", &ng_parse_uint8_type},
64cfd6422aSLutz Donnerhacke 	{"max", &ng_parse_uint8_type},
65cfd6422aSLutz Donnerhacke 	{NULL}
66cfd6422aSLutz Donnerhacke };
67cfd6422aSLutz Donnerhacke static const struct ng_parse_type ng_vlanrotate_conf_type = {
68cfd6422aSLutz Donnerhacke 	&ng_parse_struct_type,
69cfd6422aSLutz Donnerhacke 	&ng_vlanrotate_conf_fields
70cfd6422aSLutz Donnerhacke };
71cfd6422aSLutz Donnerhacke 
72cfd6422aSLutz Donnerhacke /* Parse type for struct ng_vlanrotate_stat */
73cfd6422aSLutz Donnerhacke static struct ng_parse_fixedarray_info ng_vlanrotate_stat_hist_info = {
74cfd6422aSLutz Donnerhacke 	&ng_parse_uint64_type,
75cfd6422aSLutz Donnerhacke 	NG_VLANROTATE_MAX_VLANS
76cfd6422aSLutz Donnerhacke };
77cfd6422aSLutz Donnerhacke static struct ng_parse_type ng_vlanrotate_stat_hist = {
78cfd6422aSLutz Donnerhacke 	&ng_parse_fixedarray_type,
79cfd6422aSLutz Donnerhacke 	&ng_vlanrotate_stat_hist_info
80cfd6422aSLutz Donnerhacke };
81cfd6422aSLutz Donnerhacke static const struct ng_parse_struct_field ng_vlanrotate_stat_fields[] = {
82cfd6422aSLutz Donnerhacke 	{"drops", &ng_parse_uint64_type},
83cfd6422aSLutz Donnerhacke 	{"excessive", &ng_parse_uint64_type},
84cfd6422aSLutz Donnerhacke 	{"incomplete", &ng_parse_uint64_type},
85cfd6422aSLutz Donnerhacke 	{"histogram", &ng_vlanrotate_stat_hist},
86cfd6422aSLutz Donnerhacke 	{NULL}
87cfd6422aSLutz Donnerhacke };
88cfd6422aSLutz Donnerhacke static struct ng_parse_type ng_vlanrotate_stat_type = {
89cfd6422aSLutz Donnerhacke 	&ng_parse_struct_type,
90cfd6422aSLutz Donnerhacke 	&ng_vlanrotate_stat_fields
91cfd6422aSLutz Donnerhacke };
92cfd6422aSLutz Donnerhacke 
93cfd6422aSLutz Donnerhacke 
94cfd6422aSLutz Donnerhacke /* List of commands and how to convert arguments to/from ASCII */
95cfd6422aSLutz Donnerhacke static const struct ng_cmdlist ng_vlanrotate_cmdlist[] = {
96cfd6422aSLutz Donnerhacke 	{
97cfd6422aSLutz Donnerhacke 		NGM_VLANROTATE_COOKIE,
98cfd6422aSLutz Donnerhacke 		NGM_VLANROTATE_GET_CONF,
99cfd6422aSLutz Donnerhacke 		"getconf",
100cfd6422aSLutz Donnerhacke 		NULL,
101cfd6422aSLutz Donnerhacke 		&ng_vlanrotate_conf_type,
102cfd6422aSLutz Donnerhacke 	},
103cfd6422aSLutz Donnerhacke 	{
104cfd6422aSLutz Donnerhacke 		NGM_VLANROTATE_COOKIE,
105cfd6422aSLutz Donnerhacke 		NGM_VLANROTATE_SET_CONF,
106cfd6422aSLutz Donnerhacke 		"setconf",
107cfd6422aSLutz Donnerhacke 		&ng_vlanrotate_conf_type,
108cfd6422aSLutz Donnerhacke 		NULL
109cfd6422aSLutz Donnerhacke 	},
110cfd6422aSLutz Donnerhacke 	{
111cfd6422aSLutz Donnerhacke 		NGM_VLANROTATE_COOKIE,
112cfd6422aSLutz Donnerhacke 		NGM_VLANROTATE_GET_STAT,
113cfd6422aSLutz Donnerhacke 		"getstat",
114cfd6422aSLutz Donnerhacke 		NULL,
115cfd6422aSLutz Donnerhacke 		&ng_vlanrotate_stat_type
116cfd6422aSLutz Donnerhacke 	},
117cfd6422aSLutz Donnerhacke 	{
118cfd6422aSLutz Donnerhacke 		NGM_VLANROTATE_COOKIE,
119cfd6422aSLutz Donnerhacke 		NGM_VLANROTATE_CLR_STAT,
120cfd6422aSLutz Donnerhacke 		"clrstat",
121cfd6422aSLutz Donnerhacke 		NULL,
122cfd6422aSLutz Donnerhacke 		&ng_vlanrotate_stat_type
123cfd6422aSLutz Donnerhacke 	},
124cfd6422aSLutz Donnerhacke 	{
125cfd6422aSLutz Donnerhacke 		NGM_VLANROTATE_COOKIE,
126cfd6422aSLutz Donnerhacke 		NGM_VLANROTATE_GETCLR_STAT,
127cfd6422aSLutz Donnerhacke 		"getclrstat",
128cfd6422aSLutz Donnerhacke 		NULL,
129cfd6422aSLutz Donnerhacke 		&ng_vlanrotate_stat_type
130cfd6422aSLutz Donnerhacke 	},
131cfd6422aSLutz Donnerhacke 	{0}
132cfd6422aSLutz Donnerhacke };
133cfd6422aSLutz Donnerhacke 
134cfd6422aSLutz Donnerhacke /* Netgraph node type descriptor */
135cfd6422aSLutz Donnerhacke static struct ng_type typestruct = {
136cfd6422aSLutz Donnerhacke 	.version = NG_ABI_VERSION,
137cfd6422aSLutz Donnerhacke 	.name = NG_VLANROTATE_NODE_TYPE,
138cfd6422aSLutz Donnerhacke 	.constructor = ng_vlanrotate_constructor,
139cfd6422aSLutz Donnerhacke 	.rcvmsg = ng_vlanrotate_rcvmsg,
140cfd6422aSLutz Donnerhacke 	.shutdown = ng_vlanrotate_shutdown,
141cfd6422aSLutz Donnerhacke 	.newhook = ng_vlanrotate_newhook,
142cfd6422aSLutz Donnerhacke 	.rcvdata = ng_vlanrotate_rcvdata,
143cfd6422aSLutz Donnerhacke 	.disconnect = ng_vlanrotate_disconnect,
144cfd6422aSLutz Donnerhacke 	.cmdlist = ng_vlanrotate_cmdlist,
145cfd6422aSLutz Donnerhacke };
146cfd6422aSLutz Donnerhacke NETGRAPH_INIT(vlanrotate, &typestruct);
147cfd6422aSLutz Donnerhacke 
148cfd6422aSLutz Donnerhacke struct ng_vlanrotate_kernel_stats {
149cfd6422aSLutz Donnerhacke 	counter_u64_t	drops, excessive, incomplete;
150cfd6422aSLutz Donnerhacke 	counter_u64_t	histogram[NG_VLANROTATE_MAX_VLANS];
151cfd6422aSLutz Donnerhacke };
152cfd6422aSLutz Donnerhacke 
153cfd6422aSLutz Donnerhacke /* Information we store for each node */
154cfd6422aSLutz Donnerhacke struct vlanrotate {
155cfd6422aSLutz Donnerhacke 	hook_p		original_hook;
156cfd6422aSLutz Donnerhacke 	hook_p		ordered_hook;
157cfd6422aSLutz Donnerhacke 	hook_p		excessive_hook;
158cfd6422aSLutz Donnerhacke 	hook_p		incomplete_hook;
159cfd6422aSLutz Donnerhacke 	struct ng_vlanrotate_conf conf;
160cfd6422aSLutz Donnerhacke 	struct ng_vlanrotate_kernel_stats stats;
161cfd6422aSLutz Donnerhacke };
162cfd6422aSLutz Donnerhacke typedef struct vlanrotate *vlanrotate_p;
163cfd6422aSLutz Donnerhacke 
164cfd6422aSLutz Donnerhacke /*
165cfd6422aSLutz Donnerhacke  * Set up the private data structure.
166cfd6422aSLutz Donnerhacke  */
167cfd6422aSLutz Donnerhacke static int
ng_vlanrotate_constructor(node_p node)168cfd6422aSLutz Donnerhacke ng_vlanrotate_constructor(node_p node)
169cfd6422aSLutz Donnerhacke {
170cfd6422aSLutz Donnerhacke 	int i;
171cfd6422aSLutz Donnerhacke 
172cfd6422aSLutz Donnerhacke 	vlanrotate_p vrp = malloc(sizeof(*vrp), M_NETGRAPH, M_WAITOK | M_ZERO);
173cfd6422aSLutz Donnerhacke 
174cfd6422aSLutz Donnerhacke 	vrp->conf.max = NG_VLANROTATE_MAX_VLANS;
175cfd6422aSLutz Donnerhacke 
176cfd6422aSLutz Donnerhacke 	vrp->stats.drops = counter_u64_alloc(M_WAITOK);
177cfd6422aSLutz Donnerhacke 	vrp->stats.excessive = counter_u64_alloc(M_WAITOK);
178cfd6422aSLutz Donnerhacke 	vrp->stats.incomplete = counter_u64_alloc(M_WAITOK);
179cfd6422aSLutz Donnerhacke 	for (i = 0; i < NG_VLANROTATE_MAX_VLANS; i++)
180cfd6422aSLutz Donnerhacke 		vrp->stats.histogram[i] = counter_u64_alloc(M_WAITOK);
181cfd6422aSLutz Donnerhacke 
182cfd6422aSLutz Donnerhacke 	NG_NODE_SET_PRIVATE(node, vrp);
183cfd6422aSLutz Donnerhacke 	return (0);
184cfd6422aSLutz Donnerhacke }
185cfd6422aSLutz Donnerhacke 
186cfd6422aSLutz Donnerhacke /*
187cfd6422aSLutz Donnerhacke  * Give our ok for a hook to be added.
188cfd6422aSLutz Donnerhacke  */
189cfd6422aSLutz Donnerhacke static int
ng_vlanrotate_newhook(node_p node,hook_p hook,const char * name)190cfd6422aSLutz Donnerhacke ng_vlanrotate_newhook(node_p node, hook_p hook, const char *name)
191cfd6422aSLutz Donnerhacke {
192cfd6422aSLutz Donnerhacke 	const vlanrotate_p vrp = NG_NODE_PRIVATE(node);
193cfd6422aSLutz Donnerhacke 	hook_p *dst = NULL;
194cfd6422aSLutz Donnerhacke 
195cfd6422aSLutz Donnerhacke 	if (strcmp(name, NG_VLANROTATE_HOOK_ORDERED) == 0) {
196cfd6422aSLutz Donnerhacke 		dst = &vrp->ordered_hook;
197cfd6422aSLutz Donnerhacke 	} else if (strcmp(name, NG_VLANROTATE_HOOK_ORIGINAL) == 0) {
198cfd6422aSLutz Donnerhacke 		dst = &vrp->original_hook;
199cfd6422aSLutz Donnerhacke 	} else if (strcmp(name, NG_VLANROTATE_HOOK_EXCESSIVE) == 0) {
200cfd6422aSLutz Donnerhacke 		dst = &vrp->excessive_hook;
201cfd6422aSLutz Donnerhacke 	} else if (strcmp(name, NG_VLANROTATE_HOOK_INCOMPLETE) == 0) {
202cfd6422aSLutz Donnerhacke 		dst = &vrp->incomplete_hook;
203cfd6422aSLutz Donnerhacke 	} else
204cfd6422aSLutz Donnerhacke 		return (EINVAL);	/* not a hook we know about */
205cfd6422aSLutz Donnerhacke 
206cfd6422aSLutz Donnerhacke 	if (*dst != NULL)
207cfd6422aSLutz Donnerhacke 		return (EADDRINUSE);	/* don't override */
208cfd6422aSLutz Donnerhacke 
209cfd6422aSLutz Donnerhacke 	*dst = hook;
210cfd6422aSLutz Donnerhacke 	return (0);
211cfd6422aSLutz Donnerhacke }
212cfd6422aSLutz Donnerhacke 
213cfd6422aSLutz Donnerhacke /*
214cfd6422aSLutz Donnerhacke  * Get a netgraph control message.
215cfd6422aSLutz Donnerhacke  * A response is not required.
216cfd6422aSLutz Donnerhacke  */
217cfd6422aSLutz Donnerhacke static int
ng_vlanrotate_rcvmsg(node_p node,item_p item,hook_p lasthook)218cfd6422aSLutz Donnerhacke ng_vlanrotate_rcvmsg(node_p node, item_p item, hook_p lasthook)
219cfd6422aSLutz Donnerhacke {
220cfd6422aSLutz Donnerhacke 	const vlanrotate_p vrp = NG_NODE_PRIVATE(node);
221cfd6422aSLutz Donnerhacke 	struct ng_mesg *resp = NULL;
222cfd6422aSLutz Donnerhacke 	struct ng_mesg *msg;
223cfd6422aSLutz Donnerhacke 	struct ng_vlanrotate_conf *pcf;
224cfd6422aSLutz Donnerhacke 	int error = 0;
225cfd6422aSLutz Donnerhacke 
226cfd6422aSLutz Donnerhacke 	NGI_GET_MSG(item, msg);
227cfd6422aSLutz Donnerhacke 	/* Deal with message according to cookie and command */
228cfd6422aSLutz Donnerhacke 	switch (msg->header.typecookie) {
229cfd6422aSLutz Donnerhacke 	case NGM_VLANROTATE_COOKIE:
230cfd6422aSLutz Donnerhacke 		switch (msg->header.cmd) {
231cfd6422aSLutz Donnerhacke 		case NGM_VLANROTATE_GET_CONF:
232cfd6422aSLutz Donnerhacke 			NG_MKRESPONSE(resp, msg, sizeof(vrp->conf), M_NOWAIT);
233cfd6422aSLutz Donnerhacke 			if (!resp) {
234cfd6422aSLutz Donnerhacke 				error = ENOMEM;
235cfd6422aSLutz Donnerhacke 				break;
236cfd6422aSLutz Donnerhacke 			}
237cfd6422aSLutz Donnerhacke 			*((struct ng_vlanrotate_conf *)resp->data) = vrp->conf;
238cfd6422aSLutz Donnerhacke 			break;
239cfd6422aSLutz Donnerhacke 		case NGM_VLANROTATE_SET_CONF:
240cfd6422aSLutz Donnerhacke 			if (msg->header.arglen != sizeof(*pcf)) {
241cfd6422aSLutz Donnerhacke 				error = EINVAL;
242cfd6422aSLutz Donnerhacke 				break;
243cfd6422aSLutz Donnerhacke 			}
244cfd6422aSLutz Donnerhacke 
245cfd6422aSLutz Donnerhacke 			pcf = (struct ng_vlanrotate_conf *)msg->data;
246cfd6422aSLutz Donnerhacke 
247cfd6422aSLutz Donnerhacke 			if (pcf->max == 0)	/* keep current value */
248cfd6422aSLutz Donnerhacke 				pcf->max = vrp->conf.max;
249cfd6422aSLutz Donnerhacke 
250cfd6422aSLutz Donnerhacke 			if ((pcf->max > NG_VLANROTATE_MAX_VLANS) ||
251cfd6422aSLutz Donnerhacke 			    (pcf->min > pcf->max) ||
252cfd6422aSLutz Donnerhacke 			    (abs(pcf->rot) >= pcf->max)) {
253cfd6422aSLutz Donnerhacke 				error = EINVAL;
254cfd6422aSLutz Donnerhacke 				break;
255cfd6422aSLutz Donnerhacke 			}
256cfd6422aSLutz Donnerhacke 
257cfd6422aSLutz Donnerhacke 			vrp->conf = *pcf;
258cfd6422aSLutz Donnerhacke 			break;
259cfd6422aSLutz Donnerhacke 		case NGM_VLANROTATE_GET_STAT:
260cfd6422aSLutz Donnerhacke 		case NGM_VLANROTATE_GETCLR_STAT:
261cfd6422aSLutz Donnerhacke 		{
262cfd6422aSLutz Donnerhacke 			struct ng_vlanrotate_stat *p;
263cfd6422aSLutz Donnerhacke 			int i;
264cfd6422aSLutz Donnerhacke 
265cfd6422aSLutz Donnerhacke 			NG_MKRESPONSE(resp, msg, sizeof(*p), M_NOWAIT);
266cfd6422aSLutz Donnerhacke 			if (!resp) {
267cfd6422aSLutz Donnerhacke 				error = ENOMEM;
268cfd6422aSLutz Donnerhacke 				break;
269cfd6422aSLutz Donnerhacke 			}
270cfd6422aSLutz Donnerhacke 			p = (struct ng_vlanrotate_stat *)resp->data;
271cfd6422aSLutz Donnerhacke 			p->drops = counter_u64_fetch(vrp->stats.drops);
272cfd6422aSLutz Donnerhacke 			p->excessive = counter_u64_fetch(vrp->stats.excessive);
273cfd6422aSLutz Donnerhacke 			p->incomplete = counter_u64_fetch(vrp->stats.incomplete);
274cfd6422aSLutz Donnerhacke 			for (i = 0; i < NG_VLANROTATE_MAX_VLANS; i++)
275cfd6422aSLutz Donnerhacke 				p->histogram[i] = counter_u64_fetch(vrp->stats.histogram[i]);
276cfd6422aSLutz Donnerhacke 			if (msg->header.cmd != NGM_VLANROTATE_GETCLR_STAT)
277cfd6422aSLutz Donnerhacke 				break;
278cfd6422aSLutz Donnerhacke 		}
279cfd6422aSLutz Donnerhacke 		case NGM_VLANROTATE_CLR_STAT:
280cfd6422aSLutz Donnerhacke 		{
281cfd6422aSLutz Donnerhacke 			int i;
282cfd6422aSLutz Donnerhacke 
283cfd6422aSLutz Donnerhacke 			counter_u64_zero(vrp->stats.drops);
284cfd6422aSLutz Donnerhacke 			counter_u64_zero(vrp->stats.excessive);
285cfd6422aSLutz Donnerhacke 			counter_u64_zero(vrp->stats.incomplete);
286cfd6422aSLutz Donnerhacke 			for (i = 0; i < NG_VLANROTATE_MAX_VLANS; i++)
287cfd6422aSLutz Donnerhacke 				counter_u64_zero(vrp->stats.histogram[i]);
288cfd6422aSLutz Donnerhacke 			break;
289cfd6422aSLutz Donnerhacke 		}
290cfd6422aSLutz Donnerhacke 		default:
291cfd6422aSLutz Donnerhacke 			error = EINVAL;	/* unknown command */
292cfd6422aSLutz Donnerhacke 			break;
293cfd6422aSLutz Donnerhacke 		}
294cfd6422aSLutz Donnerhacke 		break;
295cfd6422aSLutz Donnerhacke 	default:
296cfd6422aSLutz Donnerhacke 		error = EINVAL;	/* unknown cookie type */
297cfd6422aSLutz Donnerhacke 		break;
298cfd6422aSLutz Donnerhacke 	}
299cfd6422aSLutz Donnerhacke 
300cfd6422aSLutz Donnerhacke 	/* Take care of synchronous response, if any */
301cfd6422aSLutz Donnerhacke 	NG_RESPOND_MSG(error, node, item, resp);
302cfd6422aSLutz Donnerhacke 	/* Free the message and return */
303cfd6422aSLutz Donnerhacke 	NG_FREE_MSG(msg);
304cfd6422aSLutz Donnerhacke 	return (error);
305cfd6422aSLutz Donnerhacke }
306cfd6422aSLutz Donnerhacke 
307cfd6422aSLutz Donnerhacke /*
308cfd6422aSLutz Donnerhacke  * Receive data, and do rotate the vlans as desired.
309cfd6422aSLutz Donnerhacke  *
310cfd6422aSLutz Donnerhacke  * Rotating is quite complicated if the rotation offset and the number
311cfd6422aSLutz Donnerhacke  * of vlans are not relativly prime. In this case multiple slices need
312cfd6422aSLutz Donnerhacke  * to be rotated separately.
313cfd6422aSLutz Donnerhacke  *
314cfd6422aSLutz Donnerhacke  * Rotation can be additive or subtractive. Some examples:
315cfd6422aSLutz Donnerhacke  *  01234   5 vlans given
316cfd6422aSLutz Donnerhacke  *  -----
317cfd6422aSLutz Donnerhacke  *  34012  +2 rotate
318cfd6422aSLutz Donnerhacke  *  12340  +4 rotate
319cfd6422aSLutz Donnerhacke  *  12340  -1 rotate
320cfd6422aSLutz Donnerhacke  *
321cfd6422aSLutz Donnerhacke  * First some helper functions ...
322cfd6422aSLutz Donnerhacke  */
323cfd6422aSLutz Donnerhacke 
324cfd6422aSLutz Donnerhacke struct ether_vlan_stack_entry {
325cfd6422aSLutz Donnerhacke 	uint16_t	proto;
326cfd6422aSLutz Donnerhacke 	uint16_t	tag;
327cfd6422aSLutz Donnerhacke }		__packed;
328cfd6422aSLutz Donnerhacke 
329cfd6422aSLutz Donnerhacke struct ether_vlan_stack_header {
330cfd6422aSLutz Donnerhacke 	uint8_t		dst[ETHER_ADDR_LEN];
331cfd6422aSLutz Donnerhacke 	uint8_t		src[ETHER_ADDR_LEN];
332cfd6422aSLutz Donnerhacke 	struct ether_vlan_stack_entry vlan_stack[1];
333cfd6422aSLutz Donnerhacke }		__packed;
334cfd6422aSLutz Donnerhacke 
335cfd6422aSLutz Donnerhacke static int
ng_vlanrotate_gcd(int a,int b)336cfd6422aSLutz Donnerhacke ng_vlanrotate_gcd(int a, int b)
337cfd6422aSLutz Donnerhacke {
338cfd6422aSLutz Donnerhacke 	if (b == 0)
339cfd6422aSLutz Donnerhacke 		return a;
340cfd6422aSLutz Donnerhacke 	else
341cfd6422aSLutz Donnerhacke 		return ng_vlanrotate_gcd(b, a % b);
342cfd6422aSLutz Donnerhacke }
343cfd6422aSLutz Donnerhacke 
344cfd6422aSLutz Donnerhacke static void
ng_vlanrotate_rotate(struct ether_vlan_stack_entry arr[],int d,int n)345cfd6422aSLutz Donnerhacke ng_vlanrotate_rotate(struct ether_vlan_stack_entry arr[], int d, int n)
346cfd6422aSLutz Donnerhacke {
347cfd6422aSLutz Donnerhacke 	int		i, j, k;
348cfd6422aSLutz Donnerhacke 	struct ether_vlan_stack_entry temp;
349cfd6422aSLutz Donnerhacke 
350cfd6422aSLutz Donnerhacke 	/* for each commensurable slice */
351cfd6422aSLutz Donnerhacke 	for (i = ng_vlanrotate_gcd(d, n); i-- > 0;) {
352cfd6422aSLutz Donnerhacke 		/* rotate left aka downwards */
353cfd6422aSLutz Donnerhacke 		temp = arr[i];
354cfd6422aSLutz Donnerhacke 		j = i;
355cfd6422aSLutz Donnerhacke 
356cfd6422aSLutz Donnerhacke 		while (1) {
357cfd6422aSLutz Donnerhacke 			k = j + d;
358cfd6422aSLutz Donnerhacke 			if (k >= n)
359cfd6422aSLutz Donnerhacke 				k = k - n;
360cfd6422aSLutz Donnerhacke 			if (k == i)
361cfd6422aSLutz Donnerhacke 				break;
362cfd6422aSLutz Donnerhacke 			arr[j] = arr[k];
363cfd6422aSLutz Donnerhacke 			j = k;
364cfd6422aSLutz Donnerhacke 		}
365cfd6422aSLutz Donnerhacke 
366cfd6422aSLutz Donnerhacke 		arr[j] = temp;
367cfd6422aSLutz Donnerhacke 	}
368cfd6422aSLutz Donnerhacke }
369cfd6422aSLutz Donnerhacke 
370cfd6422aSLutz Donnerhacke static int
ng_vlanrotate_rcvdata(hook_p hook,item_p item)371cfd6422aSLutz Donnerhacke ng_vlanrotate_rcvdata(hook_p hook, item_p item)
372cfd6422aSLutz Donnerhacke {
373cfd6422aSLutz Donnerhacke 	const vlanrotate_p vrp = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
374cfd6422aSLutz Donnerhacke 	struct ether_vlan_stack_header *evsh;
375cfd6422aSLutz Donnerhacke 	struct mbuf *m = NULL;
376cfd6422aSLutz Donnerhacke 	hook_p	dst_hook;
377cfd6422aSLutz Donnerhacke 	int8_t	rotate;
378cfd6422aSLutz Donnerhacke 	int8_t	vlans = 0;
379cfd6422aSLutz Donnerhacke 	int	error = ENOSYS;
380cfd6422aSLutz Donnerhacke 
381cfd6422aSLutz Donnerhacke 	NGI_GET_M(item, m);
382cfd6422aSLutz Donnerhacke 
383cfd6422aSLutz Donnerhacke 	if (hook == vrp->ordered_hook) {
384cfd6422aSLutz Donnerhacke 		rotate = +vrp->conf.rot;
385cfd6422aSLutz Donnerhacke 		dst_hook = vrp->original_hook;
386cfd6422aSLutz Donnerhacke 	} else if (hook == vrp->original_hook) {
387cfd6422aSLutz Donnerhacke 		rotate = -vrp->conf.rot;
388cfd6422aSLutz Donnerhacke 		dst_hook = vrp->ordered_hook;
389cfd6422aSLutz Donnerhacke 	} else {
390cfd6422aSLutz Donnerhacke 		dst_hook = vrp->original_hook;
391cfd6422aSLutz Donnerhacke 		goto send;	/* everything else goes out unmodified */
392cfd6422aSLutz Donnerhacke 	}
393cfd6422aSLutz Donnerhacke 
394cfd6422aSLutz Donnerhacke 	if (dst_hook == NULL) {
395cfd6422aSLutz Donnerhacke 		error = ENETDOWN;
396cfd6422aSLutz Donnerhacke 		goto fail;
397cfd6422aSLutz Donnerhacke 	}
398cfd6422aSLutz Donnerhacke 
399cfd6422aSLutz Donnerhacke 	/* count the vlans */
400cfd6422aSLutz Donnerhacke 	for (vlans = 0; vlans <= NG_VLANROTATE_MAX_VLANS; vlans++) {
401cfd6422aSLutz Donnerhacke 		size_t expected_len = sizeof(struct ether_vlan_stack_header)
402cfd6422aSLutz Donnerhacke 		    + vlans * sizeof(struct ether_vlan_stack_entry);
403cfd6422aSLutz Donnerhacke 
404cfd6422aSLutz Donnerhacke 		if (m->m_len < expected_len) {
405cfd6422aSLutz Donnerhacke 			m = m_pullup(m, expected_len);
406cfd6422aSLutz Donnerhacke 			if (m == NULL) {
407cfd6422aSLutz Donnerhacke 				error = EINVAL;
408cfd6422aSLutz Donnerhacke 				goto fail;
409cfd6422aSLutz Donnerhacke 			}
410cfd6422aSLutz Donnerhacke 		}
411cfd6422aSLutz Donnerhacke 
412cfd6422aSLutz Donnerhacke 		evsh = mtod(m, struct ether_vlan_stack_header *);
413cfd6422aSLutz Donnerhacke 		switch (ntohs(evsh->vlan_stack[vlans].proto)) {
414cfd6422aSLutz Donnerhacke 		case ETHERTYPE_VLAN:
415cfd6422aSLutz Donnerhacke 		case ETHERTYPE_QINQ:
416cfd6422aSLutz Donnerhacke 		case ETHERTYPE_8021Q9100:
417cfd6422aSLutz Donnerhacke 		case ETHERTYPE_8021Q9200:
418cfd6422aSLutz Donnerhacke 		case ETHERTYPE_8021Q9300:
419cfd6422aSLutz Donnerhacke 			break;
420cfd6422aSLutz Donnerhacke 		default:
421cfd6422aSLutz Donnerhacke 			goto out;
422cfd6422aSLutz Donnerhacke 		}
423cfd6422aSLutz Donnerhacke 	}
424cfd6422aSLutz Donnerhacke out:
425cfd6422aSLutz Donnerhacke 	if ((vlans > vrp->conf.max) || (vlans >= NG_VLANROTATE_MAX_VLANS)) {
426cfd6422aSLutz Donnerhacke 		counter_u64_add(vrp->stats.excessive, 1);
427cfd6422aSLutz Donnerhacke 		dst_hook = vrp->excessive_hook;
428cfd6422aSLutz Donnerhacke 		goto send;
429cfd6422aSLutz Donnerhacke 	}
430cfd6422aSLutz Donnerhacke 
431cfd6422aSLutz Donnerhacke 	if ((vlans < vrp->conf.min) || (vlans <= abs(rotate))) {
432cfd6422aSLutz Donnerhacke 		counter_u64_add(vrp->stats.incomplete, 1);
433cfd6422aSLutz Donnerhacke 		dst_hook = vrp->incomplete_hook;
434cfd6422aSLutz Donnerhacke 		goto send;
435cfd6422aSLutz Donnerhacke 	}
436cfd6422aSLutz Donnerhacke 	counter_u64_add(vrp->stats.histogram[vlans], 1);
437cfd6422aSLutz Donnerhacke 
438cfd6422aSLutz Donnerhacke 	/* rotating upwards always (using modular arithmetics) */
439cfd6422aSLutz Donnerhacke 	if (rotate == 0) {
440cfd6422aSLutz Donnerhacke 		/* nothing to do */
441cfd6422aSLutz Donnerhacke 	} else if (rotate > 0) {
442cfd6422aSLutz Donnerhacke 		ng_vlanrotate_rotate(evsh->vlan_stack, rotate, vlans);
443cfd6422aSLutz Donnerhacke 	} else {
444cfd6422aSLutz Donnerhacke 		ng_vlanrotate_rotate(evsh->vlan_stack, vlans + rotate, vlans);
445cfd6422aSLutz Donnerhacke 	}
446cfd6422aSLutz Donnerhacke 
447cfd6422aSLutz Donnerhacke send:
448cfd6422aSLutz Donnerhacke 	if (dst_hook == NULL)
449cfd6422aSLutz Donnerhacke 		goto fail;
450cfd6422aSLutz Donnerhacke 	NG_FWD_NEW_DATA(error, item, dst_hook, m);
451cfd6422aSLutz Donnerhacke 	return 0;
452cfd6422aSLutz Donnerhacke 
453cfd6422aSLutz Donnerhacke fail:
454cfd6422aSLutz Donnerhacke 	counter_u64_add(vrp->stats.drops, 1);
455cfd6422aSLutz Donnerhacke 	if (m != NULL)
456cfd6422aSLutz Donnerhacke 		m_freem(m);
457cfd6422aSLutz Donnerhacke 	NG_FREE_ITEM(item);
458cfd6422aSLutz Donnerhacke 	return (error);
459cfd6422aSLutz Donnerhacke }
460cfd6422aSLutz Donnerhacke 
461cfd6422aSLutz Donnerhacke /*
462cfd6422aSLutz Donnerhacke  * Do local shutdown processing..
463cfd6422aSLutz Donnerhacke  * All our links and the name have already been removed.
464cfd6422aSLutz Donnerhacke  */
465cfd6422aSLutz Donnerhacke static int
ng_vlanrotate_shutdown(node_p node)466cfd6422aSLutz Donnerhacke ng_vlanrotate_shutdown(node_p node)
467cfd6422aSLutz Donnerhacke {
468cfd6422aSLutz Donnerhacke 	const		vlanrotate_p vrp = NG_NODE_PRIVATE(node);
469cfd6422aSLutz Donnerhacke 	int i;
470cfd6422aSLutz Donnerhacke 
471cfd6422aSLutz Donnerhacke 	NG_NODE_SET_PRIVATE(node, NULL);
472cfd6422aSLutz Donnerhacke 
473cfd6422aSLutz Donnerhacke 	counter_u64_free(vrp->stats.drops);
474cfd6422aSLutz Donnerhacke 	counter_u64_free(vrp->stats.excessive);
475cfd6422aSLutz Donnerhacke 	counter_u64_free(vrp->stats.incomplete);
476cfd6422aSLutz Donnerhacke 	for (i = 0; i < NG_VLANROTATE_MAX_VLANS; i++)
477cfd6422aSLutz Donnerhacke 		counter_u64_free(vrp->stats.histogram[i]);
478cfd6422aSLutz Donnerhacke 
479cfd6422aSLutz Donnerhacke 	free(vrp, M_NETGRAPH);
480cfd6422aSLutz Donnerhacke 
481cfd6422aSLutz Donnerhacke 	NG_NODE_UNREF(node);
482cfd6422aSLutz Donnerhacke 	return (0);
483cfd6422aSLutz Donnerhacke }
484cfd6422aSLutz Donnerhacke 
485cfd6422aSLutz Donnerhacke /*
486cfd6422aSLutz Donnerhacke  * Hook disconnection
487cfd6422aSLutz Donnerhacke  * For this type, removal of the last link destroys the node
488cfd6422aSLutz Donnerhacke  */
489cfd6422aSLutz Donnerhacke static int
ng_vlanrotate_disconnect(hook_p hook)490cfd6422aSLutz Donnerhacke ng_vlanrotate_disconnect(hook_p hook)
491cfd6422aSLutz Donnerhacke {
492cfd6422aSLutz Donnerhacke 	const		vlanrotate_p vrp = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
493cfd6422aSLutz Donnerhacke 
494cfd6422aSLutz Donnerhacke 	if (vrp->original_hook == hook)
495cfd6422aSLutz Donnerhacke 		vrp->original_hook = NULL;
496cfd6422aSLutz Donnerhacke 	if (vrp->ordered_hook == hook)
497cfd6422aSLutz Donnerhacke 		vrp->ordered_hook = NULL;
498cfd6422aSLutz Donnerhacke 	if (vrp->excessive_hook == hook)
499cfd6422aSLutz Donnerhacke 		vrp->excessive_hook = NULL;
500cfd6422aSLutz Donnerhacke 	if (vrp->incomplete_hook == hook)
501cfd6422aSLutz Donnerhacke 		vrp->incomplete_hook = NULL;
502cfd6422aSLutz Donnerhacke 
503cfd6422aSLutz Donnerhacke 	/* during shutdown the node is invalid, don't shutdown twice */
504cfd6422aSLutz Donnerhacke 	if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) &&
505cfd6422aSLutz Donnerhacke 	    (NG_NODE_IS_VALID(NG_HOOK_NODE(hook))))
506cfd6422aSLutz Donnerhacke 		ng_rmnode_self(NG_HOOK_NODE(hook));
507cfd6422aSLutz Donnerhacke 	return (0);
508cfd6422aSLutz Donnerhacke }
509