ng_sample.c revision 70784
1
2/*
3 * ng_sample.c
4 *
5 * Copyright (c) 1996-1999 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: Julian Elischer <julian@freebsd.org>
38 *
39 * $FreeBSD: head/sys/netgraph/ng_sample.c 70784 2001-01-08 05:34:06Z julian $
40 * $Whistle: ng_sample.c,v 1.13 1999/11/01 09:24:52 julian Exp $
41 */
42
43#include <sys/param.h>
44#include <sys/systm.h>
45#include <sys/kernel.h>
46#include <sys/mbuf.h>
47#include <sys/malloc.h>
48#include <sys/ctype.h>
49#include <sys/errno.h>
50#include <sys/syslog.h>
51
52#include <netgraph/ng_message.h>
53#include <netgraph/ng_parse.h>
54#include <netgraph/ng_sample.h>
55#include <netgraph/netgraph.h>
56
57/*
58 * This section contains the netgraph method declarations for the
59 * sample node. These methods define the netgraph 'type'.
60 */
61
62static ng_constructor_t	ng_xxx_constructor;
63static ng_rcvmsg_t	ng_xxx_rcvmsg;
64static ng_shutdown_t	ng_xxx_shutdown;
65static ng_newhook_t	ng_xxx_newhook;
66static ng_connect_t	ng_xxx_connect;
67static ng_rcvdata_t	ng_xxx_rcvdata;	 /* note these are both ng_rcvdata_t */
68static ng_disconnect_t	ng_xxx_disconnect;
69
70/* Parse type for struct ngxxxstat */
71static const struct ng_parse_struct_info
72	ng_xxx_stat_type_info = NG_XXX_STATS_TYPE_INFO;
73static const struct ng_parse_type ng_xxx_stat_type = {
74	&ng_parse_struct_type,
75	&ng_xxx_stat_type_info
76};
77
78/* List of commands and how to convert arguments to/from ASCII */
79static const struct ng_cmdlist ng_xxx_cmdlist[] = {
80	{
81	  NGM_XXX_COOKIE,
82	  NGM_XXX_GET_STATUS,
83	  "getstatus",
84	  NULL,
85	  &ng_xxx_stat_type,
86	},
87	{
88	  NGM_XXX_COOKIE,
89	  NGM_XXX_SET_FLAG,
90	  "setflag",
91	  &ng_parse_int32_type,
92	  NULL
93	},
94	{ 0 }
95};
96
97/* Netgraph node type descriptor */
98static struct ng_type typestruct = {
99	NG_ABI_VERSION,
100	NG_XXX_NODE_TYPE,
101	NULL,
102	ng_xxx_constructor,
103	ng_xxx_rcvmsg,
104	ng_xxx_shutdown,
105	ng_xxx_newhook,
106	NULL,
107	ng_xxx_connect,
108	ng_xxx_rcvdata,
109	ng_xxx_disconnect,
110	ng_xxx_cmdlist
111};
112NETGRAPH_INIT(xxx, &typestruct);
113
114/* Information we store for each hook on each node */
115struct XXX_hookinfo {
116	int     dlci;		/* The DLCI it represents, -1 == downstream */
117	int     channel;	/* The channel representing this DLCI */
118	hook_p  hook;
119};
120
121/* Information we store for each node */
122struct XXX {
123	struct XXX_hookinfo channel[XXX_NUM_DLCIS];
124	struct XXX_hookinfo downstream_hook;
125	node_p		node;		/* back pointer to node */
126	hook_p  	debughook;
127	u_int   	packets_in;	/* packets in from downstream */
128	u_int   	packets_out;	/* packets out towards downstream */
129	u_int32_t	flags;
130};
131typedef struct XXX *xxx_p;
132
133/*
134 * Allocate the private data structure. The generic node has already
135 * been created. Link them together. We arrive with a reference to the node
136 * i.e. the reference count is incremented for us already.
137 *
138 * If this were a device node than this work would be done in the attach()
139 * routine and the constructor would return EINVAL as you should not be able
140 * to creatednodes that depend on hardware (unless you can add the hardware :)
141 */
142static int
143ng_xxx_constructor(node_p node)
144{
145	xxx_p privdata;
146	int i, error;
147
148	/* Initialize private descriptor */
149	MALLOC(privdata, xxx_p, sizeof(*privdata), M_NETGRAPH,
150		M_NOWAIT | M_ZERO);
151	if (privdata == NULL)
152		return (ENOMEM);
153	for (i = 0; i < XXX_NUM_DLCIS; i++) {
154		privdata->channel[i].dlci = -2;
155		privdata->channel[i].channel = i;
156	}
157
158	/* Link structs together; this counts as our one reference to *nodep */
159	NG_NODE_PRIVATE(node) = privdata;
160	privdata->node = node;
161	return (0);
162}
163
164/*
165 * Give our ok for a hook to be added...
166 * If we are not running this might kick a device into life.
167 * Possibly decode information out of the hook name.
168 * Add the hook's private info to the hook structure.
169 * (if we had some). In this example, we assume that there is a
170 * an array of structs, called 'channel' in the private info,
171 * one for each active channel. The private
172 * pointer of each hook points to the appropriate XXX_hookinfo struct
173 * so that the source of an input packet is easily identified.
174 * (a dlci is a frame relay channel)
175 */
176static int
177ng_xxx_newhook(node_p node, hook_p hook, const char *name)
178{
179	const xxx_p xxxp = NG_NODE_PRIVATE(node);
180	const char *cp;
181	int dlci = 0;
182	int chan;
183
184#if 0
185	/* Possibly start up the device if it's not already going */
186	if ((xxxp->flags & SCF_RUNNING) == 0) {
187		ng_xxx_start_hardware(xxxp);
188	}
189#endif
190
191	/* Example of how one might use hooks with embedded numbers: All
192	 * hooks start with 'dlci' and have a decimal trailing channel
193	 * number up to 4 digits Use the leadin defined int he associated .h
194	 * file. */
195	if (strncmp(name,
196	    NG_XXX_HOOK_DLCI_LEADIN, strlen(NG_XXX_HOOK_DLCI_LEADIN)) == 0) {
197		char *eptr;
198
199		cp = name + sizeof(NG_XXX_HOOK_DLCI_LEADIN);
200		if (!isdigit(*cp) || (cp[0] == '0' && cp[1] != '\0'))
201			return (EINVAL);
202		dlci = (int)strtoul(cp, &eptr, 10);
203		if (*eptr != '\0' || dlci < 0 || dlci > 1023)
204			return (EINVAL);
205
206		/* We have a dlci, now either find it, or allocate it */
207		for (chan = 0; chan < XXX_NUM_DLCIS; chan++)
208			if (xxxp->channel[chan].dlci == dlci)
209				break;
210		if (chan == XXX_NUM_DLCIS) {
211			for (chan = 0; chan < XXX_NUM_DLCIS; chan++)
212				if (xxxp->channel[chan].dlci != -2)
213					continue;
214			if (chan == XXX_NUM_DLCIS)
215				return (ENOBUFS);
216		}
217		if (xxxp->channel[chan].hook != NULL)
218			return (EADDRINUSE);
219		NG_HOOK_SET_PRIVATE(hook, xxxp->channel + chan);
220		xxxp->channel[chan].hook = hook;
221		return (0);
222	} else if (strcmp(name, NG_XXX_HOOK_DOWNSTREAM) == 0) {
223		/* Example of simple predefined hooks. */
224		/* do something specific to the downstream connection */
225		xxxp->downstream_hook.hook = hook;
226		NG_HOOK_SET_PRIVATE(hook, &xxxp->downstream_hook);
227	} else if (strcmp(name, NG_XXX_HOOK_DEBUG) == 0) {
228		/* do something specific to a debug connection */
229		xxxp->debughook = hook;
230		NG_HOOK_SET_PRIVATE(hook, NULL);
231	} else
232		return (EINVAL);	/* not a hook we know about */
233	return(0);
234}
235
236/*
237 * Get a netgraph control message.
238 * We actually recieve a queue item that has a pointer to the message.
239 * If we free the item, the message will be freed too, unless we remove
240 * it from the item using NGI_GET_MSG();
241 * The return address is also stored in the item, as an ng_ID_t,
242 * accessible as NGI_RETADDR(item);
243 * Check it is one we understand. If needed, send a response.
244 * We could save the address for an async action later, but don't here.
245 * Always free the message.
246 * The response should be in a malloc'd region that the caller can 'free'.
247 * A response is not required.
248 * Theoretically you could respond defferently to old message types if
249 * the cookie in the header didn't match what we consider to be current
250 * (so that old userland programs could continue to work).
251 */
252static int
253ng_xxx_rcvmsg(node_p node, item_p item, hook_p lasthook)
254{
255	const xxx_p xxxp = NG_NODE_PRIVATE(node);
256	struct ng_mesg *resp = NULL;
257	int error = 0;
258	struct ng_mesg *msg;
259
260	NGI_GET_MSG(item, msg);
261	/* Deal with message according to cookie and command */
262	switch (msg->header.typecookie) {
263	case NGM_XXX_COOKIE:
264		switch (msg->header.cmd) {
265		case NGM_XXX_GET_STATUS:
266		    {
267			struct ngxxxstat *stats;
268
269			NG_MKRESPONSE(resp, msg, sizeof(*stats), M_NOWAIT);
270			if (!resp) {
271				error = ENOMEM;
272				break;
273			}
274			stats = (struct ngxxxstat *) resp->data;
275			stats->packets_in = xxxp->packets_in;
276			stats->packets_out = xxxp->packets_out;
277			break;
278		    }
279		case NGM_XXX_SET_FLAG:
280			if (msg->header.arglen != sizeof(u_int32_t)) {
281				error = EINVAL;
282				break;
283			}
284			xxxp->flags = *((u_int32_t *) msg->data);
285			break;
286		default:
287			error = EINVAL;		/* unknown command */
288			break;
289		}
290		break;
291	default:
292		error = EINVAL;			/* unknown cookie type */
293		break;
294	}
295
296	/* Take care of synchronous response, if any */
297	NG_RESPOND_MSG(error, node, item, resp);
298	/* Free the message and return */
299	NG_FREE_MSG(msg);
300	return(error);
301}
302
303/*
304 * Receive data, and do something with it.
305 * Actually we receive a queue item which holds the data.
306 * If we free the item it wil also froo the data and metadata unless
307 * we have previously disassociated them using the NGI_GET_xxx() macros.
308 * Possibly send it out on another link after processing.
309 * Possibly do something different if it comes from different
310 * hooks. the caller will never free m or meta, so
311 * if we use up this data or abort we must free BOTH of these.
312 *
313 * If we want, we may decide to force this data to be queued and reprocessed
314 * at the netgraph NETISR time.
315 * We would do that by setting the HK_QUEUE flag on our hook. We would do that
316 * in the connect() method.
317 */
318static int
319ng_xxx_rcvdata(hook_p hook, item_p item )
320{
321	const xxx_p xxxp = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
322	int chan = -2;
323	int dlci = -2;
324	int error;
325	struct mbuf *m;
326	meta_p meta;
327
328
329	NGI_GET_M(item, m);
330	if (NG_HOOK_PRIVATE(hook)) {
331		dlci = ((struct XXX_hookinfo *) NG_HOOK_PRIVATE(hook))->dlci;
332		chan = ((struct XXX_hookinfo *) NG_HOOK_PRIVATE(hook))->channel;
333		if (dlci != -1) {
334			/* If received on a DLCI hook process for this
335			 * channel and pass it to the downstream module.
336			 * Normally one would add a multiplexing header at
337			 * the front here */
338			/* M_PREPEND(....)	; */
339			/* mtod(m, xxxxxx)->dlci = dlci; */
340			NG_FWD_NEW_DATA(error, item,
341				xxxp->downstream_hook.hook, m);
342			xxxp->packets_out++;
343		} else {
344			/* data came from the multiplexed link */
345			dlci = 1;	/* get dlci from header */
346			/* madjust(....) *//* chop off header */
347			for (chan = 0; chan < XXX_NUM_DLCIS; chan++)
348				if (xxxp->channel[chan].dlci == dlci)
349					break;
350			if (chan == XXX_NUM_DLCIS) {
351				NG_FREE_ITEM(item);
352				NG_FREE_M(m);
353				return (ENETUNREACH);
354			}
355			/* If we were called at splnet, use the following:
356			 * NG_SEND_DATA(error, otherhook, m, meta); if this
357			 * node is running at some SPL other than SPLNET
358			 * then you should use instead: error =
359			 * ng_queueit(otherhook, m, meta); m = NULL: meta =
360			 * NULL; this queues the data using the standard
361			 * NETISR system and schedules the data to be picked
362			 * up again once the system has moved to SPLNET and
363			 * the processing of the data can continue. after
364			 * these are run 'm' and 'meta' should be considered
365			 * as invalid and NG_SEND_DATA actually zaps them. */
366			NG_FWD_NEW_DATA(error, item,
367				xxxp->channel[chan].hook, m);
368			xxxp->packets_in++;
369		}
370	} else {
371		/* It's the debug hook, throw it away.. */
372		if (hook == xxxp->downstream_hook.hook)
373			NG_FREE_ITEM(item);
374			NG_FREE_M(m);
375	}
376	return 0;
377}
378
379#if 0
380/*
381 * If this were a device node, the data may have been received in response
382 * to some interrupt.
383 * in which case it would probably look as follows:
384 */
385devintr()
386{
387	int error;
388				 * here */
389
390	/* get packet from device and send on */
391	m = MGET(blah blah)
392
393	NG_SEND_DATA_ONLY(error, xxxp->upstream_hook.hook, m);
394				/* see note above in xxx_rcvdata() */
395				/* and ng_xxx_connect() */
396}
397
398#endif				/* 0 */
399
400/*
401 * Do local shutdown processing..
402 * All our links and the name have already been removed.
403 * If we are a persistant device, we might refuse to go away, and
404 * we'd create a new node immediatly.
405 */
406static int
407ng_xxx_shutdown(node_p node)
408{
409	const xxx_p privdata = NG_NODE_PRIVATE(node);
410	int error;
411
412	NG_NODE_SET_PRIVATE(node, NULL);
413	NG_NODE_UNREF(privdata->node);
414#ifndef PERSISTANT_NODE
415	FREE(privdata, M_NETGRAPH);
416#else
417	/*
418	 * Create a new node. This is basically what a device
419	 * driver would do in the attach routine.
420	 */
421	error = ng_make_node_common(&typestruct, &node);
422	if (node == NULL) {
423		printf ("node recreation failed:");
424		return (error);
425	}
426	if ( ng_name_node(node, "name")) {	/* whatever name is needed */
427		printf("something informative");
428		NG_NODE_UNREF(node);			/* drop it again */
429		return (0);
430	}
431	privdata->packets_in = 0;		/* reset stats */
432	privdata->packets_out = 0;
433	for (i = 0; i < XXX_NUM_DLCIS; i++) {
434		privdata->channel[i].dlci = -2;
435		privdata->channel[i].channel = i;
436	}
437
438	/* Link structs together; this counts as our one reference to node */
439	privdata->node = node;
440	NG_NODE_SET_PRIVATE(node, privdata);
441	node->nd_flags &= ~NG_INVALID;		/* reset invalid flag */
442#endif /* PERSISTANT_NODE */
443	return (0);
444}
445
446/*
447 * This is called once we've already connected a new hook to the other node.
448 * It gives us a chance to balk at the last minute.
449 */
450static int
451ng_xxx_connect(hook_p hook)
452{
453#if 0
454	/*
455	 * If we were a driver running at other than splnet then
456	 * we should set the QUEUE bit on the edge so that we
457	 * will deliver by queing.
458	 */
459	if /*it is the upstream hook */
460	NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook));
461#endif
462#if 0
463	/*
464	 * If for some reason we want incoming date to be queued
465	 * by the NETISR system and delivered later we can set the same bit on
466	 * OUR hook. (maybe to allow unwinding of the stack)
467	 */
468
469	if (NG_HOOK_PRIVATE(hook)) {
470		int dlci;
471		/*
472		 * If it's dlci 1023, requeue it so that it's handled
473		 * at a lower priority. This is how a node decides to
474		 * defer a data message.
475		 */
476		dlci = ((struct XXX_hookinfo *) NG_HOOK_PRIVATE(hook))->dlci;
477		if (dlci == 1023) {
478			NG_HOOK_FORCE_QUEUE(hook);
479		}
480#endif
481	/* otherwise be really amiable and just say "YUP that's OK by me! " */
482	return (0);
483}
484
485/*
486 * Dook disconnection
487 *
488 * For this type, removal of the last link destroys the node
489 */
490static int
491ng_xxx_disconnect(hook_p hook)
492{
493	if (NG_HOOK_PRIVATE(hook))
494		((struct XXX_hookinfo *) (NG_HOOK_PRIVATE(hook)))->hook = NULL;
495	if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0)
496	&& (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) /* already shutting down? */
497		ng_rmnode_self(NG_HOOK_NODE(hook));
498	return (0);
499}
500
501