xref: /freebsd/sys/netgraph/ng_sample.c (revision 4cf49a43559ed9fdad601bdcccd2c55963008675)
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@whistle.com>
38  *
39  * $FreeBSD$
40  * $Whistle: ng_sample.c,v 1.11 1999/01/28 23:54:54 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/errno.h>
49 #include <sys/syslog.h>
50 
51 #include <netgraph/ng_message.h>
52 #include <netgraph/ng_sample.h>
53 #include <netgraph/netgraph.h>
54 
55 /*
56  * This section contains the netgraph method declarations for the
57  * sample node. These methods define the netgraph 'type'.
58  */
59 
60 static int	ng_xxx_constructor(node_p *node);
61 static int	ng_xxx_rcvmsg(node_p node, struct ng_mesg *msg,
62 		  const char *retaddr, struct ng_mesg **resp);
63 static int	ng_xxx_rmnode(node_p node);
64 static int	ng_xxx_newhook(node_p node, hook_p hook, const char *name);
65 static int	ng_xxx_connect(hook_p hook);
66 static int	ng_xxx_rcvdata(hook_p hook, struct mbuf *m, meta_p meta);
67 static int	ng_xxx_rcvdataq(hook_p hook, struct mbuf *m, meta_p meta);
68 static int	ng_xxx_disconnect(hook_p hook);
69 
70 /* Netgraph node type descriptor */
71 static struct ng_type typestruct = {
72 	NG_VERSION,
73 	NG_XXX_NODE_TYPE,
74 	NULL,
75 	ng_xxx_constructor,
76 	ng_xxx_rcvmsg,
77 	ng_xxx_rmnode,
78 	ng_xxx_newhook,
79 	NULL,
80 	ng_xxx_connect,
81 	ng_xxx_rcvdata,
82 	ng_xxx_rcvdataq,
83 	ng_xxx_disconnect
84 };
85 NETGRAPH_INIT(xxx, &typestruct);
86 
87 /* Information we store for each hook on each node */
88 struct XXX_hookinfo {
89 	int     dlci;		/* The DLCI it represents, -1 == downstream */
90 	int     channel;	/* The channel representing this DLCI */
91 	hook_p  hook;
92 };
93 
94 /* Information we store for each node */
95 struct XXX {
96 	struct XXX_hookinfo channel[XXX_NUM_DLCIS];
97 	struct XXX_hookinfo downstream_hook;
98 	node_p		node;		/* back pointer to node */
99 	hook_p  	debughook;
100 	u_int   	packets_in;	/* packets in from downstream */
101 	u_int   	packets_out;	/* packets out towards downstream */
102 	u_int32_t	flags;
103 };
104 typedef struct XXX *xxx_p;
105 
106 /*
107  * Allocate the private data structure and the generic node
108  * and link them together.
109  *
110  * ng_make_node_common() returns with a generic node struct
111  * with a single reference for us.. we transfer it to the
112  * private structure.. when we free the private struct we must
113  * unref the node so it gets freed too.
114  *
115  * If this were a device node than this work would be done in the attach()
116  * routine and the constructor would return EINVAL as you should not be able
117  * to creatednodes that depend on hardware (unless you can add the hardware :)
118  */
119 static int
120 ng_xxx_constructor(node_p *nodep)
121 {
122 	xxx_p privdata;
123 	int i, error;
124 
125 	/* Initialize private descriptor */
126 	MALLOC(privdata, xxx_p, sizeof(*privdata), M_NETGRAPH, M_WAITOK);
127 	if (privdata == NULL)
128 		return (ENOMEM);
129 	bzero(privdata, sizeof(struct XXX));
130 	for (i = 0; i < XXX_NUM_DLCIS; i++) {
131 		privdata->channel[i].dlci = -2;
132 		privdata->channel[i].channel = i;
133 	}
134 
135 	/* Call the 'generic' (ie, superclass) node constructor */
136 	if ((error = ng_make_node_common(&typestruct, nodep))) {
137 		FREE(privdata, M_NETGRAPH);
138 		return (error);
139 	}
140 
141 	/* Link structs together; this counts as our one reference to *nodep */
142 	(*nodep)->private = privdata;
143 	privdata->node = *nodep;
144 	return (0);
145 }
146 
147 /*
148  * Give our ok for a hook to be added...
149  * If we are not running this might kick a device into life.
150  * Possibly decode information out of the hook name.
151  * Add the hook's private info to the hook structure.
152  * (if we had some). In this example, we assume that there is a
153  * an array of structs, called 'channel' in the private info,
154  * one for each active channel. The private
155  * pointer of each hook points to the appropriate XXX_hookinfo struct
156  * so that the source of an input packet is easily identified.
157  * (a dlci is a frame relay channel)
158  */
159 static int
160 ng_xxx_newhook(node_p node, hook_p hook, const char *name)
161 {
162 	const xxx_p xxxp = node->private;
163 	const char *cp;
164 	char c = '\0';
165 	int digits = 0;
166 	int dlci = 0;
167 	int chan;
168 
169 #if 0
170 	/* Possibly start up the device if it's not already going */
171 	if ((xxxp->flags & SCF_RUNNING) == 0) {
172 		ng_xxx_start_hardware(xxxp);
173 	}
174 #endif
175 
176 	/* Example of how one might use hooks with embedded numbers: All
177 	 * hooks start with 'dlci' and have a decimal trailing channel
178 	 * number up to 4 digits Use the leadin defined int he associated .h
179 	 * file. */
180 	if (strncmp(name, NG_XXX_HOOK_DLCI_LEADIN, 4) == 0) {
181 		cp = name + sizeof(NG_XXX_HOOK_DLCI_LEADIN);
182 		while ((digits < 5)
183 		       && ((c = *cp++) > '0') && (c < '9')) {
184 			dlci *= 10;
185 			dlci += c - '0';
186 			digits++;
187 		}
188 		if ((c != 0) || (digits == 5)
189 		    || (dlci <= 0) || (dlci > 1023))
190 			return (EINVAL);
191 		/* We have a dlci, now either find it, or allocate it */
192 		for (chan = 0; chan < XXX_NUM_DLCIS; chan++)
193 			if (xxxp->channel[chan].dlci == dlci)
194 				break;
195 		if (chan == XXX_NUM_DLCIS) {
196 			for (chan = 0; chan < XXX_NUM_DLCIS; chan++)
197 				if (xxxp->channel[chan].dlci != -2)
198 					continue;
199 			if (chan == XXX_NUM_DLCIS)
200 				return (ENOBUFS);
201 		}
202 		if (xxxp->channel[chan].hook != NULL)
203 			return (EADDRINUSE);
204 		hook->private = xxxp->channel + chan;
205 		xxxp->channel[chan].hook = hook;
206 		return (0);
207 	} else if (strcmp(name, NG_XXX_HOOK_DOWNSTREAM) == 0) {
208 		/* Example of simple predefined hooks. */
209 		/* do something specific to the downstream connection */
210 		xxxp->downstream_hook.hook = hook;
211 		hook->private = &xxxp->downstream_hook;
212 	} else if (strcmp(name, NG_XXX_HOOK_DEBUG) == 0) {
213 		/* do something specific to a debug connection */
214 		xxxp->debughook = hook;
215 		hook->private = NULL;
216 	} else
217 		return (EINVAL);	/* not a hook we know about */
218 	return(0);
219 }
220 
221 /*
222  * Get a netgraph control message.
223  * Check it is one we understand. If needed, send a response.
224  * We could save the address for an async action later, but don't here.
225  * Always free the message.
226  * The response should be in a malloc'd region that the caller can 'free'.
227  * A response is not required.
228  * Theoretically you could respond defferently to old message types if
229  * the cookie in the header didn't match what we consider to be current
230  * (so that old userland programs could continue to work).
231  */
232 static int
233 ng_xxx_rcvmsg(node_p node,
234 	   struct ng_mesg *msg, const char *retaddr, struct ng_mesg **rptr)
235 {
236 	const xxx_p xxxp = node->private;
237 	struct ng_mesg *resp = NULL;
238 	int error = 0;
239 
240 	/* Deal with message according to cookie and command */
241 	switch (msg->header.typecookie) {
242 	case NGM_XXX_COOKIE:
243 		switch (msg->header.cmd) {
244 		case NGM_XXX_GET_STATUS:
245 		    {
246 			struct ngxxxstat *stats;
247 
248 			NG_MKRESPONSE(resp, msg, sizeof(*stats), M_NOWAIT);
249 			if (!resp) {
250 				error = ENOMEM;
251 				break;
252 			}
253 			stats = (struct ngxxxstat *) resp->data;
254 			stats->packets_in = xxxp->packets_in;
255 			stats->packets_out = xxxp->packets_out;
256 			break;
257 		    }
258 		case NGM_XXX_SET_FLAG:
259 			if (msg->header.arglen != sizeof(u_int32_t)) {
260 				error = EINVAL;
261 				break;
262 			}
263 			xxxp->flags = *((u_int32_t *) msg->data);
264 			break;
265 		default:
266 			error = EINVAL;		/* unknown command */
267 			break;
268 		}
269 		break;
270 	default:
271 		error = EINVAL;			/* unknown cookie type */
272 		break;
273 	}
274 
275 	/* Take care of synchronous response, if any */
276 	if (rptr)
277 		*rptr = resp;
278 	else if (resp)
279 		FREE(resp, M_NETGRAPH);
280 
281 	/* Free the message and return */
282 	FREE(msg, M_NETGRAPH);
283 	return(error);
284 }
285 
286 /*
287  * Receive data, and do something with it.
288  * Possibly send it out on another link after processing.
289  * Possibly do something different if it comes from different
290  * hooks. the caller will never free m or meta, so
291  * if we use up this data or abort we must free BOTH of these.
292  *
293  * If we want, we may decide to force this data to be queued and reprocessed
294  * at the netgraph NETISR time. (at which time it will be entered using ng_xxx_rcvdataq().
295  */
296 static int
297 ng_xxx_rcvdata(hook_p hook, struct mbuf *m, meta_p meta)
298 {
299 	int dlci = -2;
300 	int error;
301 
302 	if (hook->private) {
303 		/*
304 		 * If it's dlci 1023, requeue it so that it's handled at a lower priority.
305 		 * This is how a node decides to defer a data message.
306 		 */
307 		dlci = ((struct XXX_hookinfo *) hook->private)->dlci;
308 		if (dlci == 1023) {
309 			ng_queue_data(hook->peer, m, meta);
310 		}
311 	}
312 	ng_xxx_rcvdataq(hook, m, meta);
313 }
314 
315 /*
316  * Always accept the data. This version of rcvdata is called from the dequeueing routine.
317  */
318 static int
319 ng_xxx_rcvdataq(hook_p hook, struct mbuf *m, meta_p meta)
320 {
321 	const xxx_p xxxp = hook->node->private;
322 	int chan = -2;
323 	int dlci = -2;
324 	int error;
325 
326 	if (hook->private) {
327 		dlci = ((struct XXX_hookinfo *) hook->private)->dlci;
328 		chan = ((struct XXX_hookinfo *) hook->private)->channel;
329 		if (dlci != -1) {
330 			/* If received on a DLCI hook process for this
331 			 * channel and pass it to the downstream module.
332 			 * Normally one would add a multiplexing header at
333 			 * the front here */
334 			/* M_PREPEND(....)	; */
335 			/* mtod(m, xxxxxx)->dlci = dlci; */
336 			error = ng_send_data(xxxp->downstream_hook.hook,
337 			    m, meta);
338 			xxxp->packets_out++;
339 		} else {
340 			/* data came from the multiplexed link */
341 			dlci = 1;	/* get dlci from header */
342 			/* madjust(....) *//* chop off header */
343 			for (chan = 0; chan < XXX_NUM_DLCIS; chan++)
344 				if (xxxp->channel[chan].dlci == dlci)
345 					break;
346 			if (chan == XXX_NUM_DLCIS) {
347 				NG_FREE_DATA(m, meta);
348 				return (ENETUNREACH);
349 			}
350 			/* If we were called at splnet, use the following:
351 			 * NG_SEND_DATA(error, otherhook, m, meta); if this
352 			 * node is running at some SPL other than SPLNET
353 			 * then you should use instead: error =
354 			 * ng_queueit(otherhook, m, meta); m = NULL: meta =
355 			 * NULL; this queues the data using the standard
356 			 * NETISR system and schedules the data to be picked
357 			 * up again once the system has moved to SPLNET and
358 			 * the processing of the data can continue. after
359 			 * these are run 'm' and 'meta' should be considered
360 			 * as invalid and NG_SEND_DATA actually zaps them. */
361 			NG_SEND_DATA(error, xxxp->channel[chan].hook, m, meta);
362 			xxxp->packets_in++;
363 		}
364 	} else {
365 		/* It's the debug hook, throw it away.. */
366 		if (hook == xxxp->downstream_hook.hook)
367 			NG_FREE_DATA(m, meta);
368 	}
369 	return 0;
370 }
371 
372 #if 0
373 /*
374  * If this were a device node, the data may have been received in response
375  * to some interrupt.
376  * in which case it would probably look as follows:
377  */
378 devintr()
379 {
380 	meta_p  meta = NULL;	/* whatever metadata we might imagine goes
381 				 * here */
382 
383 	/* get packet from device and send on */
384 	m = MGET(blah blah)
385 	    error = ng_queueit(upstream, m, meta);	/* see note above in
386 							 * xxx_rcvdata() */
387 }
388 
389 #endif				/* 0 */
390 
391 /*
392  * Do local shutdown processing..
393  * If we are a persistant device, we might refuse to go away, and
394  * we'd only remove our links and reset ourself.
395  */
396 static int
397 ng_xxx_rmnode(node_p node)
398 {
399 	const xxx_p privdata = node->private;
400 
401 	node->flags |= NG_INVALID;
402 	ng_cutlinks(node);
403 #ifndef PERSISTANT_NODE
404 	ng_unname(node);
405 	node->private = NULL;
406 	ng_unref(privdata->node);
407 	FREE(privdata, M_NETGRAPH);
408 #else
409 	privdata->packets_in = 0;		/* reset stats */
410 	privdata->packets_out = 0;
411 	node->flags &= ~NG_INVALID;		/* reset invalid flag */
412 #endif /* PERSISTANT_NODE */
413 	return (0);
414 }
415 
416 /*
417  * This is called once we've already connected a new hook to the other node.
418  * It gives us a chance to balk at the last minute.
419  */
420 static int
421 ng_xxx_connect(hook_p hook)
422 {
423 	/* be really amiable and just say "YUP that's OK by me! " */
424 	return (0);
425 }
426 
427 /*
428  * Dook disconnection
429  *
430  * For this type, removal of the last link destroys the node
431  */
432 static int
433 ng_xxx_disconnect(hook_p hook)
434 {
435 	if (hook->private)
436 		((struct XXX_hookinfo *) (hook->private))->hook == NULL;
437 	if (hook->node->numhooks == 0)
438 		ng_rmnode(hook->node);
439 	return (0);
440 }
441 
442