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