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