1
2/*
3 * ng_one2many.c
4 *
5 * Copyright (c) 2000 Whistle Communications, Inc.
6 * All rights reserved.
7 *
8 * Subject to the following obligations and disclaimer of warranty, use and
9 * redistribution of this software, in source or object code forms, with or
10 * without modifications are expressly permitted by Whistle Communications;
11 * provided, however, that:
12 * 1. Any and all reproductions of the source or object code must include the
13 * copyright notice above and the following disclaimer of warranties; and
14 * 2. No rights are granted, in any manner or form, to use Whistle
15 * Communications, Inc. trademarks, including the mark "WHISTLE
16 * COMMUNICATIONS" on advertising, endorsements, or otherwise except as
17 * such appears in the above copyright notice or in the software.
18 *
19 * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
20 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
21 * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
22 * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
23 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
24 * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
25 * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
26 * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
27 * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
28 * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
29 * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
30 * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
31 * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34 * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
35 * OF SUCH DAMAGE.
36 *
37 * Author: Archie Cobbs <archie@freebsd.org>
38 *
39 * $FreeBSD: head/sys/netgraph/ng_one2many.c 70159 2000-12-18 20:03:32Z julian $
40 */
41
42/*
43 * ng_one2many(4) netgraph node type
44 *
45 * Packets received on the "one" hook are sent out each of the
46 * "many" hooks in round-robin fashion. Packets received on any
47 * "many" hook are always delivered to the "one" hook.
48 */
49
50#include <sys/param.h>
51#include <sys/systm.h>
52#include <sys/kernel.h>
53#include <sys/malloc.h>
54#include <sys/ctype.h>
55#include <sys/mbuf.h>
56#include <sys/errno.h>
57
58#include <netgraph/ng_message.h>
59#include <netgraph/netgraph.h>
60#include <netgraph/ng_parse.h>
61#include <netgraph/ng_one2many.h>
62
63/* Per-link private data */
64struct ng_one2many_link {
65 hook_p hook; /* netgraph hook */
66 struct ng_one2many_link_stats stats; /* link stats */
67};
68
69/* Per-node private data */
70struct ng_one2many_private {
71 struct ng_one2many_config conf; /* node configuration */
72 struct ng_one2many_link one; /* "one" hook */
73 struct ng_one2many_link many[NG_ONE2MANY_MAX_LINKS];
74 u_int16_t nextMany; /* next round-robin */
75 u_int16_t numActiveMany; /* # active "many" */
76 u_int16_t activeMany[NG_ONE2MANY_MAX_LINKS];
77};
78typedef struct ng_one2many_private *priv_p;
79
80/* Netgraph node methods */
81static ng_constructor_t ng_one2many_constructor;
82static ng_rcvmsg_t ng_one2many_rcvmsg;
83static ng_shutdown_t ng_one2many_rmnode;
84static ng_newhook_t ng_one2many_newhook;
85static ng_rcvdata_t ng_one2many_rcvdata;
86static ng_disconnect_t ng_one2many_disconnect;
87
88/* Other functions */
89static void ng_one2many_update_many(priv_p priv);
90
91/* Store each hook's link number in the private field */
92#define LINK_NUM(hook) (*(int16_t *)(&(hook)->private))
93
94/******************************************************************
95 NETGRAPH PARSE TYPES
96******************************************************************/
97
98/* Parse type for struct ng_one2many_config */
99static const struct ng_parse_fixedarray_info
100 ng_one2many_enableLinks_array_type_info = {
101 &ng_parse_uint8_type,
102 NG_ONE2MANY_MAX_LINKS
103};
104static const struct ng_parse_type ng_one2many_enableLinks_array_type = {
105 &ng_parse_fixedarray_type,
106 &ng_one2many_enableLinks_array_type_info,
107};
108static const struct ng_parse_struct_info ng_one2many_config_type_info
109 = NG_ONE2MANY_CONFIG_TYPE_INFO(&ng_one2many_enableLinks_array_type);
110static const struct ng_parse_type ng_one2many_config_type = {
111 &ng_parse_struct_type,
112 &ng_one2many_config_type_info,
113};
114
115/* Parse type for struct ng_one2many_link_stats */
116static const struct ng_parse_struct_info
117 ng_one2many_link_stats_type_info = NG_ONE2MANY_LINK_STATS_TYPE_INFO;
118static const struct ng_parse_type ng_one2many_link_stats_type = {
119 &ng_parse_struct_type,
120 &ng_one2many_link_stats_type_info
121};
122
123/* List of commands and how to convert arguments to/from ASCII */
124static const struct ng_cmdlist ng_one2many_cmdlist[] = {
125 {
126 NGM_ONE2MANY_COOKIE,
127 NGM_ONE2MANY_SET_CONFIG,
128 "setconfig",
129 &ng_one2many_config_type,
130 NULL
131 },
132 {
133 NGM_ONE2MANY_COOKIE,
134 NGM_ONE2MANY_GET_CONFIG,
135 "getconfig",
136 NULL,
137 &ng_one2many_config_type
138 },
139 {
140 NGM_ONE2MANY_COOKIE,
141 NGM_ONE2MANY_GET_STATS,
142 "getstats",
143 &ng_parse_int32_type,
144 &ng_one2many_link_stats_type
145 },
146 {
147 NGM_ONE2MANY_COOKIE,
148 NGM_ONE2MANY_CLR_STATS,
149 "clrstats",
150 &ng_parse_int32_type,
151 NULL,
152 },
153 {
154 NGM_ONE2MANY_COOKIE,
155 NGM_ONE2MANY_GETCLR_STATS,
156 "getclrstats",
157 &ng_parse_int32_type,
158 &ng_one2many_link_stats_type
159 },
160 { 0 }
161};
162
163/* Node type descriptor */
164static struct ng_type ng_one2many_typestruct = {
165 NG_ABI_VERSION,
166 NG_ONE2MANY_NODE_TYPE,
167 NULL,
168 ng_one2many_constructor,
169 ng_one2many_rcvmsg,
170 ng_one2many_rmnode,
171 ng_one2many_newhook,
172 NULL,
173 NULL,
174 ng_one2many_rcvdata,
175 ng_one2many_disconnect,
176 ng_one2many_cmdlist,
177};
178NETGRAPH_INIT(one2many, &ng_one2many_typestruct);
179
180/******************************************************************
181 NETGRAPH NODE METHODS
182******************************************************************/
183
184/*
185 * Node constructor
186 */
187static int
188ng_one2many_constructor(node_p *nodep)
189{
190 priv_p priv;
191 int error;
192
193 /* Allocate and initialize private info */
194 MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO);
195 if (priv == NULL)
196 return (ENOMEM);
197 priv->conf.xmitAlg = NG_ONE2MANY_XMIT_ROUNDROBIN;
198 priv->conf.failAlg = NG_ONE2MANY_FAIL_MANUAL;
199
200 /* Call superclass constructor */
201 if ((error = ng_make_node_common(&ng_one2many_typestruct, nodep))) {
202 FREE(priv, M_NETGRAPH);
203 return (error);
204 }
205 (*nodep)->private = priv;
206
207 /* Done */
208 return (0);
209}
210
211/*
212 * Method for attaching a new hook
213 */
214static int
215ng_one2many_newhook(node_p node, hook_p hook, const char *name)
216{
217 const priv_p priv = node->private;
218 struct ng_one2many_link *link;
219 int linkNum;
220 u_long i;
221
222 /* Which hook? */
223 if (strncmp(name, NG_ONE2MANY_HOOK_MANY_PREFIX,
224 strlen(NG_ONE2MANY_HOOK_MANY_PREFIX)) == 0) {
225 const char *cp;
226 char *eptr;
227
228 cp = name + strlen(NG_ONE2MANY_HOOK_MANY_PREFIX);
229 if (!isdigit(*cp) || (cp[0] == '0' && cp[1] != '\0'))
230 return (EINVAL);
231 i = strtoul(cp, &eptr, 10);
232 if (*eptr != '\0' || i < 0 || i >= NG_ONE2MANY_MAX_LINKS)
233 return (EINVAL);
234 linkNum = (int)i;
235 link = &priv->many[linkNum];
236 } else if (strcmp(name, NG_ONE2MANY_HOOK_ONE) == 0) {
237 linkNum = NG_ONE2MANY_ONE_LINKNUM;
238 link = &priv->one;
239 } else
240 return (EINVAL);
241
242 /* Is hook already connected? (should never happen) */
243 if (link->hook != NULL)
244 return (EISCONN);
245
246 /* Setup private info for this link */
247 LINK_NUM(hook) = linkNum;
248 link->hook = hook;
249 bzero(&link->stats, sizeof(link->stats));
250 if (linkNum != NG_ONE2MANY_ONE_LINKNUM) {
251 priv->conf.enabledLinks[linkNum] = 1; /* auto-enable link */
252 ng_one2many_update_many(priv);
253 }
254
255 /* Done */
256 return (0);
257}
258
259/*
260 * Receive a control message
261 */
262static int
263ng_one2many_rcvmsg(node_p node, struct ng_mesg *msg,
264 const char *retaddr, struct ng_mesg **rptr, hook_p lasthook)
265{
266 const priv_p priv = node->private;
267 struct ng_mesg *resp = NULL;
268 int error = 0;
269
270 switch (msg->header.typecookie) {
271 case NGM_ONE2MANY_COOKIE:
272 switch (msg->header.cmd) {
273 case NGM_ONE2MANY_SET_CONFIG:
274 {
275 struct ng_one2many_config *conf;
276 int i;
277
278 /* Check that new configuration is valid */
279 if (msg->header.arglen != sizeof(*conf)) {
280 error = EINVAL;
281 break;
282 }
283 conf = (struct ng_one2many_config *)msg->data;
284 switch (conf->xmitAlg) {
285 case NG_ONE2MANY_XMIT_ROUNDROBIN:
286 break;
287 default:
288 error = EINVAL;
289 break;
290 }
291 switch (conf->failAlg) {
292 case NG_ONE2MANY_FAIL_MANUAL:
293 break;
294 default:
295 error = EINVAL;
296 break;
297 }
298 if (error != 0)
299 break;
300
301 /* Normalized many link enabled bits */
302 for (i = 0; i < NG_ONE2MANY_MAX_LINKS; i++)
303 conf->enabledLinks[i] = !!conf->enabledLinks[i];
304
305 /* Copy config and reset */
306 bcopy(conf, &priv->conf, sizeof(*conf));
307 ng_one2many_update_many(priv);
308 break;
309 }
310 case NGM_ONE2MANY_GET_CONFIG:
311 {
312 struct ng_one2many_config *conf;
313
314 NG_MKRESPONSE(resp, msg, sizeof(*conf), M_NOWAIT);
315 if (resp == NULL) {
316 error = ENOMEM;
317 break;
318 }
319 conf = (struct ng_one2many_config *)resp->data;
320 bcopy(&priv->conf, conf, sizeof(priv->conf));
321 break;
322 }
323 case NGM_ONE2MANY_GET_STATS:
324 case NGM_ONE2MANY_CLR_STATS:
325 case NGM_ONE2MANY_GETCLR_STATS:
326 {
327 struct ng_one2many_link *link;
328 int linkNum;
329
330 /* Get link */
331 if (msg->header.arglen != sizeof(int32_t)) {
332 error = EINVAL;
333 break;
334 }
335 linkNum = *((int32_t *)msg->data);
336 if (linkNum == NG_ONE2MANY_ONE_LINKNUM)
337 link = &priv->one;
338 else if (linkNum == 0
339 && linkNum < NG_ONE2MANY_MAX_LINKS) {
340 link = &priv->many[linkNum];
341 } else {
342 error = EINVAL;
343 break;
344 }
345
346 /* Get/clear stats */
347 if (msg->header.cmd != NGM_ONE2MANY_CLR_STATS) {
348 NG_MKRESPONSE(resp, msg,
349 sizeof(link->stats), M_NOWAIT);
350 if (resp == NULL) {
351 error = ENOMEM;
352 break;
353 }
354 bcopy(&link->stats,
355 resp->data, sizeof(link->stats));
356 }
357 if (msg->header.cmd != NGM_ONE2MANY_GET_STATS)
358 bzero(&link->stats, sizeof(link->stats));
359 break;
360 }
361 default:
362 error = EINVAL;
363 break;
364 }
365 break;
366 default:
367 error = EINVAL;
368 break;
369 }
370
371 /* Done */
372 if (rptr)
373 *rptr = resp;
374 else if (resp != NULL)
375 FREE(resp, M_NETGRAPH);
376 FREE(msg, M_NETGRAPH);
377 return (error);
378}
379
380/*
381 * Receive data on a hook
382 */
383static int
384ng_one2many_rcvdata(hook_p hook, struct mbuf *m, meta_p meta,
385 struct mbuf **ret_m, meta_p *ret_meta, struct ng_mesg **resp)
386{
387 const node_p node = hook->node;
388 const priv_p priv = node->private;
389 struct ng_one2many_link *src;
390 struct ng_one2many_link *dst;
391 int error = 0;
392 int linkNum;
393
394 /* Get link number */
395 linkNum = LINK_NUM(hook);
396 KASSERT(linkNum == NG_ONE2MANY_ONE_LINKNUM
397 || (linkNum >= 0 && linkNum < NG_ONE2MANY_MAX_LINKS),
398 ("%s: linkNum=%d", __FUNCTION__, linkNum));
399
400 /* Figure out source link */
401 src = (linkNum == NG_ONE2MANY_ONE_LINKNUM) ?
402 &priv->one : &priv->many[linkNum];
403 KASSERT(src->hook != NULL, ("%s: no src%d", __FUNCTION__, linkNum));
404
405 /* Update receive stats */
406 src->stats.recvPackets++;
407 src->stats.recvOctets += m->m_pkthdr.len;
408
409 /* Figure out destination link */
410 if (linkNum == NG_ONE2MANY_ONE_LINKNUM) {
411 if (priv->numActiveMany == 0) {
412 NG_FREE_DATA(m, meta);
413 return (ENOTCONN);
414 }
415 dst = &priv->many[priv->activeMany[priv->nextMany]];
416 priv->nextMany = (priv->nextMany + 1) % priv->numActiveMany;
417 } else
418 dst = &priv->one;
419
420 /* Update transmit stats */
421 dst->stats.xmitPackets++;
422 dst->stats.xmitOctets += m->m_pkthdr.len;
423
424 /* Deliver packet */
425 NG_SEND_DATA(error, dst->hook, m, meta);
426 return (error);
427}
428
429/*
430 * Shutdown node
431 */
432static int
433ng_one2many_rmnode(node_p node)
434{
435 const priv_p priv = node->private;
436
437 ng_unname(node);
438 ng_cutlinks(node);
439 KASSERT(priv->numActiveMany == 0,
440 ("%s: numActiveMany=%d", __FUNCTION__, priv->numActiveMany));
441 FREE(priv, M_NETGRAPH);
442 node->private = NULL;
443 ng_unref(node);
444 return (0);
445}
446
447/*
448 * Hook disconnection.
449 */
450static int
451ng_one2many_disconnect(hook_p hook)
452{
453 const priv_p priv = hook->node->private;
454 int linkNum;
455
456 /* Get link number */
457 linkNum = LINK_NUM(hook);
458 KASSERT(linkNum == NG_ONE2MANY_ONE_LINKNUM
459 || (linkNum >= 0 && linkNum < NG_ONE2MANY_MAX_LINKS),
460 ("%s: linkNum=%d", __FUNCTION__, linkNum));
461
462 /* Nuke the link */
463 if (linkNum == NG_ONE2MANY_ONE_LINKNUM)
464 priv->one.hook = NULL;
465 else {
466 priv->many[linkNum].hook = NULL;
467 priv->conf.enabledLinks[linkNum] = 0;
468 ng_one2many_update_many(priv);
469 }
470
471 /* If no hooks left, go away */
472 if (hook->node->numhooks == 0)
473 ng_rmnode(hook->node);
474 return (0);
475}
476
477/******************************************************************
478 OTHER FUNCTIONS
479******************************************************************/
480
481/*
482 * Update internal state after the addition or removal of a "many" link
483 */
484static void
485ng_one2many_update_many(priv_p priv)
486{
487 int linkNum;
488
489 /* Update list of which "many" links are up */
490 priv->numActiveMany = 0;
491 for (linkNum = 0; linkNum < NG_ONE2MANY_MAX_LINKS; linkNum++) {
492 switch (priv->conf.failAlg) {
493 case NG_ONE2MANY_FAIL_MANUAL:
494 if (priv->many[linkNum].hook != NULL
495 && priv->conf.enabledLinks[linkNum]) {
496 priv->activeMany[priv->numActiveMany] = linkNum;
497 priv->numActiveMany++;
498 }
499 break;
500#ifdef INVARIANTS
501 default:
502 panic("%s: invalid failAlg", __FUNCTION__);
503#endif
504 }
505 }
506
507 /* Update transmit algorithm state */
508 switch (priv->conf.xmitAlg) {
509 case NG_ONE2MANY_XMIT_ROUNDROBIN:
510 if (priv->numActiveMany > 0)
511 priv->nextMany %= priv->numActiveMany;
512 break;
513#ifdef INVARIANTS
514 default:
515 panic("%s: invalid xmitAlg", __FUNCTION__);
516#endif
517 }
518}
519
520
2/*
3 * ng_one2many.c
4 *
5 * Copyright (c) 2000 Whistle Communications, Inc.
6 * All rights reserved.
7 *
8 * Subject to the following obligations and disclaimer of warranty, use and
9 * redistribution of this software, in source or object code forms, with or
10 * without modifications are expressly permitted by Whistle Communications;
11 * provided, however, that:
12 * 1. Any and all reproductions of the source or object code must include the
13 * copyright notice above and the following disclaimer of warranties; and
14 * 2. No rights are granted, in any manner or form, to use Whistle
15 * Communications, Inc. trademarks, including the mark "WHISTLE
16 * COMMUNICATIONS" on advertising, endorsements, or otherwise except as
17 * such appears in the above copyright notice or in the software.
18 *
19 * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
20 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
21 * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
22 * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
23 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
24 * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
25 * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
26 * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
27 * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
28 * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
29 * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
30 * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
31 * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34 * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
35 * OF SUCH DAMAGE.
36 *
37 * Author: Archie Cobbs <archie@freebsd.org>
38 *
39 * $FreeBSD: head/sys/netgraph/ng_one2many.c 70159 2000-12-18 20:03:32Z julian $
40 */
41
42/*
43 * ng_one2many(4) netgraph node type
44 *
45 * Packets received on the "one" hook are sent out each of the
46 * "many" hooks in round-robin fashion. Packets received on any
47 * "many" hook are always delivered to the "one" hook.
48 */
49
50#include <sys/param.h>
51#include <sys/systm.h>
52#include <sys/kernel.h>
53#include <sys/malloc.h>
54#include <sys/ctype.h>
55#include <sys/mbuf.h>
56#include <sys/errno.h>
57
58#include <netgraph/ng_message.h>
59#include <netgraph/netgraph.h>
60#include <netgraph/ng_parse.h>
61#include <netgraph/ng_one2many.h>
62
63/* Per-link private data */
64struct ng_one2many_link {
65 hook_p hook; /* netgraph hook */
66 struct ng_one2many_link_stats stats; /* link stats */
67};
68
69/* Per-node private data */
70struct ng_one2many_private {
71 struct ng_one2many_config conf; /* node configuration */
72 struct ng_one2many_link one; /* "one" hook */
73 struct ng_one2many_link many[NG_ONE2MANY_MAX_LINKS];
74 u_int16_t nextMany; /* next round-robin */
75 u_int16_t numActiveMany; /* # active "many" */
76 u_int16_t activeMany[NG_ONE2MANY_MAX_LINKS];
77};
78typedef struct ng_one2many_private *priv_p;
79
80/* Netgraph node methods */
81static ng_constructor_t ng_one2many_constructor;
82static ng_rcvmsg_t ng_one2many_rcvmsg;
83static ng_shutdown_t ng_one2many_rmnode;
84static ng_newhook_t ng_one2many_newhook;
85static ng_rcvdata_t ng_one2many_rcvdata;
86static ng_disconnect_t ng_one2many_disconnect;
87
88/* Other functions */
89static void ng_one2many_update_many(priv_p priv);
90
91/* Store each hook's link number in the private field */
92#define LINK_NUM(hook) (*(int16_t *)(&(hook)->private))
93
94/******************************************************************
95 NETGRAPH PARSE TYPES
96******************************************************************/
97
98/* Parse type for struct ng_one2many_config */
99static const struct ng_parse_fixedarray_info
100 ng_one2many_enableLinks_array_type_info = {
101 &ng_parse_uint8_type,
102 NG_ONE2MANY_MAX_LINKS
103};
104static const struct ng_parse_type ng_one2many_enableLinks_array_type = {
105 &ng_parse_fixedarray_type,
106 &ng_one2many_enableLinks_array_type_info,
107};
108static const struct ng_parse_struct_info ng_one2many_config_type_info
109 = NG_ONE2MANY_CONFIG_TYPE_INFO(&ng_one2many_enableLinks_array_type);
110static const struct ng_parse_type ng_one2many_config_type = {
111 &ng_parse_struct_type,
112 &ng_one2many_config_type_info,
113};
114
115/* Parse type for struct ng_one2many_link_stats */
116static const struct ng_parse_struct_info
117 ng_one2many_link_stats_type_info = NG_ONE2MANY_LINK_STATS_TYPE_INFO;
118static const struct ng_parse_type ng_one2many_link_stats_type = {
119 &ng_parse_struct_type,
120 &ng_one2many_link_stats_type_info
121};
122
123/* List of commands and how to convert arguments to/from ASCII */
124static const struct ng_cmdlist ng_one2many_cmdlist[] = {
125 {
126 NGM_ONE2MANY_COOKIE,
127 NGM_ONE2MANY_SET_CONFIG,
128 "setconfig",
129 &ng_one2many_config_type,
130 NULL
131 },
132 {
133 NGM_ONE2MANY_COOKIE,
134 NGM_ONE2MANY_GET_CONFIG,
135 "getconfig",
136 NULL,
137 &ng_one2many_config_type
138 },
139 {
140 NGM_ONE2MANY_COOKIE,
141 NGM_ONE2MANY_GET_STATS,
142 "getstats",
143 &ng_parse_int32_type,
144 &ng_one2many_link_stats_type
145 },
146 {
147 NGM_ONE2MANY_COOKIE,
148 NGM_ONE2MANY_CLR_STATS,
149 "clrstats",
150 &ng_parse_int32_type,
151 NULL,
152 },
153 {
154 NGM_ONE2MANY_COOKIE,
155 NGM_ONE2MANY_GETCLR_STATS,
156 "getclrstats",
157 &ng_parse_int32_type,
158 &ng_one2many_link_stats_type
159 },
160 { 0 }
161};
162
163/* Node type descriptor */
164static struct ng_type ng_one2many_typestruct = {
165 NG_ABI_VERSION,
166 NG_ONE2MANY_NODE_TYPE,
167 NULL,
168 ng_one2many_constructor,
169 ng_one2many_rcvmsg,
170 ng_one2many_rmnode,
171 ng_one2many_newhook,
172 NULL,
173 NULL,
174 ng_one2many_rcvdata,
175 ng_one2many_disconnect,
176 ng_one2many_cmdlist,
177};
178NETGRAPH_INIT(one2many, &ng_one2many_typestruct);
179
180/******************************************************************
181 NETGRAPH NODE METHODS
182******************************************************************/
183
184/*
185 * Node constructor
186 */
187static int
188ng_one2many_constructor(node_p *nodep)
189{
190 priv_p priv;
191 int error;
192
193 /* Allocate and initialize private info */
194 MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO);
195 if (priv == NULL)
196 return (ENOMEM);
197 priv->conf.xmitAlg = NG_ONE2MANY_XMIT_ROUNDROBIN;
198 priv->conf.failAlg = NG_ONE2MANY_FAIL_MANUAL;
199
200 /* Call superclass constructor */
201 if ((error = ng_make_node_common(&ng_one2many_typestruct, nodep))) {
202 FREE(priv, M_NETGRAPH);
203 return (error);
204 }
205 (*nodep)->private = priv;
206
207 /* Done */
208 return (0);
209}
210
211/*
212 * Method for attaching a new hook
213 */
214static int
215ng_one2many_newhook(node_p node, hook_p hook, const char *name)
216{
217 const priv_p priv = node->private;
218 struct ng_one2many_link *link;
219 int linkNum;
220 u_long i;
221
222 /* Which hook? */
223 if (strncmp(name, NG_ONE2MANY_HOOK_MANY_PREFIX,
224 strlen(NG_ONE2MANY_HOOK_MANY_PREFIX)) == 0) {
225 const char *cp;
226 char *eptr;
227
228 cp = name + strlen(NG_ONE2MANY_HOOK_MANY_PREFIX);
229 if (!isdigit(*cp) || (cp[0] == '0' && cp[1] != '\0'))
230 return (EINVAL);
231 i = strtoul(cp, &eptr, 10);
232 if (*eptr != '\0' || i < 0 || i >= NG_ONE2MANY_MAX_LINKS)
233 return (EINVAL);
234 linkNum = (int)i;
235 link = &priv->many[linkNum];
236 } else if (strcmp(name, NG_ONE2MANY_HOOK_ONE) == 0) {
237 linkNum = NG_ONE2MANY_ONE_LINKNUM;
238 link = &priv->one;
239 } else
240 return (EINVAL);
241
242 /* Is hook already connected? (should never happen) */
243 if (link->hook != NULL)
244 return (EISCONN);
245
246 /* Setup private info for this link */
247 LINK_NUM(hook) = linkNum;
248 link->hook = hook;
249 bzero(&link->stats, sizeof(link->stats));
250 if (linkNum != NG_ONE2MANY_ONE_LINKNUM) {
251 priv->conf.enabledLinks[linkNum] = 1; /* auto-enable link */
252 ng_one2many_update_many(priv);
253 }
254
255 /* Done */
256 return (0);
257}
258
259/*
260 * Receive a control message
261 */
262static int
263ng_one2many_rcvmsg(node_p node, struct ng_mesg *msg,
264 const char *retaddr, struct ng_mesg **rptr, hook_p lasthook)
265{
266 const priv_p priv = node->private;
267 struct ng_mesg *resp = NULL;
268 int error = 0;
269
270 switch (msg->header.typecookie) {
271 case NGM_ONE2MANY_COOKIE:
272 switch (msg->header.cmd) {
273 case NGM_ONE2MANY_SET_CONFIG:
274 {
275 struct ng_one2many_config *conf;
276 int i;
277
278 /* Check that new configuration is valid */
279 if (msg->header.arglen != sizeof(*conf)) {
280 error = EINVAL;
281 break;
282 }
283 conf = (struct ng_one2many_config *)msg->data;
284 switch (conf->xmitAlg) {
285 case NG_ONE2MANY_XMIT_ROUNDROBIN:
286 break;
287 default:
288 error = EINVAL;
289 break;
290 }
291 switch (conf->failAlg) {
292 case NG_ONE2MANY_FAIL_MANUAL:
293 break;
294 default:
295 error = EINVAL;
296 break;
297 }
298 if (error != 0)
299 break;
300
301 /* Normalized many link enabled bits */
302 for (i = 0; i < NG_ONE2MANY_MAX_LINKS; i++)
303 conf->enabledLinks[i] = !!conf->enabledLinks[i];
304
305 /* Copy config and reset */
306 bcopy(conf, &priv->conf, sizeof(*conf));
307 ng_one2many_update_many(priv);
308 break;
309 }
310 case NGM_ONE2MANY_GET_CONFIG:
311 {
312 struct ng_one2many_config *conf;
313
314 NG_MKRESPONSE(resp, msg, sizeof(*conf), M_NOWAIT);
315 if (resp == NULL) {
316 error = ENOMEM;
317 break;
318 }
319 conf = (struct ng_one2many_config *)resp->data;
320 bcopy(&priv->conf, conf, sizeof(priv->conf));
321 break;
322 }
323 case NGM_ONE2MANY_GET_STATS:
324 case NGM_ONE2MANY_CLR_STATS:
325 case NGM_ONE2MANY_GETCLR_STATS:
326 {
327 struct ng_one2many_link *link;
328 int linkNum;
329
330 /* Get link */
331 if (msg->header.arglen != sizeof(int32_t)) {
332 error = EINVAL;
333 break;
334 }
335 linkNum = *((int32_t *)msg->data);
336 if (linkNum == NG_ONE2MANY_ONE_LINKNUM)
337 link = &priv->one;
338 else if (linkNum == 0
339 && linkNum < NG_ONE2MANY_MAX_LINKS) {
340 link = &priv->many[linkNum];
341 } else {
342 error = EINVAL;
343 break;
344 }
345
346 /* Get/clear stats */
347 if (msg->header.cmd != NGM_ONE2MANY_CLR_STATS) {
348 NG_MKRESPONSE(resp, msg,
349 sizeof(link->stats), M_NOWAIT);
350 if (resp == NULL) {
351 error = ENOMEM;
352 break;
353 }
354 bcopy(&link->stats,
355 resp->data, sizeof(link->stats));
356 }
357 if (msg->header.cmd != NGM_ONE2MANY_GET_STATS)
358 bzero(&link->stats, sizeof(link->stats));
359 break;
360 }
361 default:
362 error = EINVAL;
363 break;
364 }
365 break;
366 default:
367 error = EINVAL;
368 break;
369 }
370
371 /* Done */
372 if (rptr)
373 *rptr = resp;
374 else if (resp != NULL)
375 FREE(resp, M_NETGRAPH);
376 FREE(msg, M_NETGRAPH);
377 return (error);
378}
379
380/*
381 * Receive data on a hook
382 */
383static int
384ng_one2many_rcvdata(hook_p hook, struct mbuf *m, meta_p meta,
385 struct mbuf **ret_m, meta_p *ret_meta, struct ng_mesg **resp)
386{
387 const node_p node = hook->node;
388 const priv_p priv = node->private;
389 struct ng_one2many_link *src;
390 struct ng_one2many_link *dst;
391 int error = 0;
392 int linkNum;
393
394 /* Get link number */
395 linkNum = LINK_NUM(hook);
396 KASSERT(linkNum == NG_ONE2MANY_ONE_LINKNUM
397 || (linkNum >= 0 && linkNum < NG_ONE2MANY_MAX_LINKS),
398 ("%s: linkNum=%d", __FUNCTION__, linkNum));
399
400 /* Figure out source link */
401 src = (linkNum == NG_ONE2MANY_ONE_LINKNUM) ?
402 &priv->one : &priv->many[linkNum];
403 KASSERT(src->hook != NULL, ("%s: no src%d", __FUNCTION__, linkNum));
404
405 /* Update receive stats */
406 src->stats.recvPackets++;
407 src->stats.recvOctets += m->m_pkthdr.len;
408
409 /* Figure out destination link */
410 if (linkNum == NG_ONE2MANY_ONE_LINKNUM) {
411 if (priv->numActiveMany == 0) {
412 NG_FREE_DATA(m, meta);
413 return (ENOTCONN);
414 }
415 dst = &priv->many[priv->activeMany[priv->nextMany]];
416 priv->nextMany = (priv->nextMany + 1) % priv->numActiveMany;
417 } else
418 dst = &priv->one;
419
420 /* Update transmit stats */
421 dst->stats.xmitPackets++;
422 dst->stats.xmitOctets += m->m_pkthdr.len;
423
424 /* Deliver packet */
425 NG_SEND_DATA(error, dst->hook, m, meta);
426 return (error);
427}
428
429/*
430 * Shutdown node
431 */
432static int
433ng_one2many_rmnode(node_p node)
434{
435 const priv_p priv = node->private;
436
437 ng_unname(node);
438 ng_cutlinks(node);
439 KASSERT(priv->numActiveMany == 0,
440 ("%s: numActiveMany=%d", __FUNCTION__, priv->numActiveMany));
441 FREE(priv, M_NETGRAPH);
442 node->private = NULL;
443 ng_unref(node);
444 return (0);
445}
446
447/*
448 * Hook disconnection.
449 */
450static int
451ng_one2many_disconnect(hook_p hook)
452{
453 const priv_p priv = hook->node->private;
454 int linkNum;
455
456 /* Get link number */
457 linkNum = LINK_NUM(hook);
458 KASSERT(linkNum == NG_ONE2MANY_ONE_LINKNUM
459 || (linkNum >= 0 && linkNum < NG_ONE2MANY_MAX_LINKS),
460 ("%s: linkNum=%d", __FUNCTION__, linkNum));
461
462 /* Nuke the link */
463 if (linkNum == NG_ONE2MANY_ONE_LINKNUM)
464 priv->one.hook = NULL;
465 else {
466 priv->many[linkNum].hook = NULL;
467 priv->conf.enabledLinks[linkNum] = 0;
468 ng_one2many_update_many(priv);
469 }
470
471 /* If no hooks left, go away */
472 if (hook->node->numhooks == 0)
473 ng_rmnode(hook->node);
474 return (0);
475}
476
477/******************************************************************
478 OTHER FUNCTIONS
479******************************************************************/
480
481/*
482 * Update internal state after the addition or removal of a "many" link
483 */
484static void
485ng_one2many_update_many(priv_p priv)
486{
487 int linkNum;
488
489 /* Update list of which "many" links are up */
490 priv->numActiveMany = 0;
491 for (linkNum = 0; linkNum < NG_ONE2MANY_MAX_LINKS; linkNum++) {
492 switch (priv->conf.failAlg) {
493 case NG_ONE2MANY_FAIL_MANUAL:
494 if (priv->many[linkNum].hook != NULL
495 && priv->conf.enabledLinks[linkNum]) {
496 priv->activeMany[priv->numActiveMany] = linkNum;
497 priv->numActiveMany++;
498 }
499 break;
500#ifdef INVARIANTS
501 default:
502 panic("%s: invalid failAlg", __FUNCTION__);
503#endif
504 }
505 }
506
507 /* Update transmit algorithm state */
508 switch (priv->conf.xmitAlg) {
509 case NG_ONE2MANY_XMIT_ROUNDROBIN:
510 if (priv->numActiveMany > 0)
511 priv->nextMany %= priv->numActiveMany;
512 break;
513#ifdef INVARIANTS
514 default:
515 panic("%s: invalid xmitAlg", __FUNCTION__);
516#endif
517 }
518}
519
520