ng_one2many.c revision 70159
1117397Skan
2117397Skan/*
3117397Skan * ng_one2many.c
4117397Skan *
5117397Skan * Copyright (c) 2000 Whistle Communications, Inc.
6117397Skan * All rights reserved.
7117397Skan *
8117397Skan * Subject to the following obligations and disclaimer of warranty, use and
9117397Skan * redistribution of this software, in source or object code forms, with or
10117397Skan * without modifications are expressly permitted by Whistle Communications;
11117397Skan * provided, however, that:
12117397Skan * 1. Any and all reproductions of the source or object code must include the
13117397Skan *    copyright notice above and the following disclaimer of warranties; and
14117397Skan * 2. No rights are granted, in any manner or form, to use Whistle
15117397Skan *    Communications, Inc. trademarks, including the mark "WHISTLE
16117397Skan *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
17117397Skan *    such appears in the above copyright notice or in the software.
18117397Skan *
19117397Skan * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
20117397Skan * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
21117397Skan * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
22117397Skan * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
23117397Skan * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
24117397Skan * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
25117397Skan * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
26117397Skan * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
27117397Skan * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
28117397Skan * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
29117397Skan * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
30117397Skan * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
31117397Skan * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
32117397Skan * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33117397Skan * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34117397Skan * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
35117397Skan * OF SUCH DAMAGE.
36117397Skan *
37117397Skan * Author: Archie Cobbs <archie@freebsd.org>
38117397Skan *
39117397Skan * $FreeBSD: head/sys/netgraph/ng_one2many.c 70159 2000-12-18 20:03:32Z julian $
40117397Skan */
41117397Skan
42117397Skan/*
43117397Skan * ng_one2many(4) netgraph node type
44117397Skan *
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
521