1 /*-
2 * Copyright (c) 1996-1999 Whistle Communications, Inc.
3 * All rights reserved.
4 *
5 * Subject to the following obligations and disclaimer of warranty, use and
6 * redistribution of this software, in source or object code forms, with or
7 * without modifications are expressly permitted by Whistle Communications;
8 * provided, however, that:
9 * 1. Any and all reproductions of the source or object code must include the
10 * copyright notice above and the following disclaimer of warranties; and
11 * 2. No rights are granted, in any manner or form, to use Whistle
12 * Communications, Inc. trademarks, including the mark "WHISTLE
13 * COMMUNICATIONS" on advertising, endorsements, or otherwise except as
14 * such appears in the above copyright notice or in the software.
15 *
16 * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
17 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
18 * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
19 * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
21 * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
22 * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
23 * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
24 * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
25 * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
26 * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
27 * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
28 * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31 * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
32 * OF SUCH DAMAGE.
33 *
34 * Authors: Julian Elischer <[email protected]>
35 * Archie Cobbs <[email protected]>
36 *
37 * $FreeBSD$
38 * $Whistle: ng_base.c,v 1.39 1999/01/28 23:54:53 julian Exp $
39 */
40
41 /*
42 * This file implements the base netgraph code.
43 */
44
45 #include <sys/param.h>
46 #include <sys/systm.h>
47 #include <sys/ctype.h>
48 #include <sys/hash.h>
49 #include <sys/kdb.h>
50 #include <sys/kernel.h>
51 #include <sys/kthread.h>
52 #include <sys/ktr.h>
53 #include <sys/limits.h>
54 #include <sys/lock.h>
55 #include <sys/malloc.h>
56 #include <sys/mbuf.h>
57 #include <sys/proc.h>
58 #include <sys/epoch.h>
59 #include <sys/queue.h>
60 #include <sys/refcount.h>
61 #include <sys/rwlock.h>
62 #include <sys/smp.h>
63 #include <sys/sysctl.h>
64 #include <sys/syslog.h>
65 #include <sys/unistd.h>
66 #include <machine/cpu.h>
67 #include <vm/uma.h>
68
69 #include <net/netisr.h>
70 #include <net/vnet.h>
71
72 #include <netgraph/ng_message.h>
73 #include <netgraph/netgraph.h>
74 #include <netgraph/ng_parse.h>
75
76 MODULE_VERSION(netgraph, NG_ABI_VERSION);
77
78 /* Mutex to protect topology events. */
79 static struct rwlock ng_topo_lock;
80 #define TOPOLOGY_RLOCK() rw_rlock(&ng_topo_lock)
81 #define TOPOLOGY_RUNLOCK() rw_runlock(&ng_topo_lock)
82 #define TOPOLOGY_WLOCK() rw_wlock(&ng_topo_lock)
83 #define TOPOLOGY_WUNLOCK() rw_wunlock(&ng_topo_lock)
84 #define TOPOLOGY_NOTOWNED() rw_assert(&ng_topo_lock, RA_UNLOCKED)
85
86 #ifdef NETGRAPH_DEBUG
87 static struct mtx ng_nodelist_mtx; /* protects global node/hook lists */
88 static struct mtx ngq_mtx; /* protects the queue item list */
89
90 static SLIST_HEAD(, ng_node) ng_allnodes;
91 static LIST_HEAD(, ng_node) ng_freenodes; /* in debug, we never free() them */
92 static SLIST_HEAD(, ng_hook) ng_allhooks;
93 static LIST_HEAD(, ng_hook) ng_freehooks; /* in debug, we never free() them */
94
95 static void ng_dumpitems(void);
96 static void ng_dumpnodes(void);
97 static void ng_dumphooks(void);
98
99 #endif /* NETGRAPH_DEBUG */
100 /*
101 * DEAD versions of the structures.
102 * In order to avoid races, it is sometimes necessary to point
103 * at SOMETHING even though theoretically, the current entity is
104 * INVALID. Use these to avoid these races.
105 */
106 struct ng_type ng_deadtype = {
107 NG_ABI_VERSION,
108 "dead",
109 NULL, /* modevent */
110 NULL, /* constructor */
111 NULL, /* rcvmsg */
112 NULL, /* shutdown */
113 NULL, /* newhook */
114 NULL, /* findhook */
115 NULL, /* connect */
116 NULL, /* rcvdata */
117 NULL, /* disconnect */
118 NULL, /* cmdlist */
119 };
120
121 struct ng_node ng_deadnode = {
122 "dead",
123 &ng_deadtype,
124 NGF_INVALID,
125 0, /* numhooks */
126 NULL, /* private */
127 0, /* ID */
128 LIST_HEAD_INITIALIZER(ng_deadnode.nd_hooks),
129 {}, /* all_nodes list entry */
130 {}, /* id hashtable list entry */
131 { 0,
132 0,
133 {}, /* should never use! (should hang) */
134 {}, /* workqueue entry */
135 STAILQ_HEAD_INITIALIZER(ng_deadnode.nd_input_queue.queue),
136 },
137 1, /* refs */
138 NULL, /* vnet */
139 #ifdef NETGRAPH_DEBUG
140 ND_MAGIC,
141 __FILE__,
142 __LINE__,
143 {NULL}
144 #endif /* NETGRAPH_DEBUG */
145 };
146
147 struct ng_hook ng_deadhook = {
148 "dead",
149 NULL, /* private */
150 HK_INVALID | HK_DEAD,
151 0, /* undefined data link type */
152 &ng_deadhook, /* Peer is self */
153 &ng_deadnode, /* attached to deadnode */
154 {}, /* hooks list */
155 NULL, /* override rcvmsg() */
156 NULL, /* override rcvdata() */
157 1, /* refs always >= 1 */
158 #ifdef NETGRAPH_DEBUG
159 HK_MAGIC,
160 __FILE__,
161 __LINE__,
162 {NULL}
163 #endif /* NETGRAPH_DEBUG */
164 };
165
166 /*
167 * END DEAD STRUCTURES
168 */
169 /* List nodes with unallocated work */
170 static STAILQ_HEAD(, ng_node) ng_worklist = STAILQ_HEAD_INITIALIZER(ng_worklist);
171 static struct mtx ng_worklist_mtx; /* MUST LOCK NODE FIRST */
172
173 /* List of installed types */
174 static LIST_HEAD(, ng_type) ng_typelist;
175 static struct rwlock ng_typelist_lock;
176 #define TYPELIST_RLOCK() rw_rlock(&ng_typelist_lock)
177 #define TYPELIST_RUNLOCK() rw_runlock(&ng_typelist_lock)
178 #define TYPELIST_WLOCK() rw_wlock(&ng_typelist_lock)
179 #define TYPELIST_WUNLOCK() rw_wunlock(&ng_typelist_lock)
180
181 /* Hash related definitions. */
182 LIST_HEAD(nodehash, ng_node);
183 VNET_DEFINE_STATIC(struct nodehash *, ng_ID_hash);
184 VNET_DEFINE_STATIC(u_long, ng_ID_hmask);
185 VNET_DEFINE_STATIC(u_long, ng_nodes);
186 VNET_DEFINE_STATIC(struct nodehash *, ng_name_hash);
187 VNET_DEFINE_STATIC(u_long, ng_name_hmask);
188 VNET_DEFINE_STATIC(u_long, ng_named_nodes);
189 #define V_ng_ID_hash VNET(ng_ID_hash)
190 #define V_ng_ID_hmask VNET(ng_ID_hmask)
191 #define V_ng_nodes VNET(ng_nodes)
192 #define V_ng_name_hash VNET(ng_name_hash)
193 #define V_ng_name_hmask VNET(ng_name_hmask)
194 #define V_ng_named_nodes VNET(ng_named_nodes)
195
196 static struct rwlock ng_idhash_lock;
197 #define IDHASH_RLOCK() rw_rlock(&ng_idhash_lock)
198 #define IDHASH_RUNLOCK() rw_runlock(&ng_idhash_lock)
199 #define IDHASH_WLOCK() rw_wlock(&ng_idhash_lock)
200 #define IDHASH_WUNLOCK() rw_wunlock(&ng_idhash_lock)
201
202 /* Method to find a node.. used twice so do it here */
203 #define NG_IDHASH_FN(ID) ((ID) % (V_ng_ID_hmask + 1))
204 #define NG_IDHASH_FIND(ID, node) \
205 do { \
206 rw_assert(&ng_idhash_lock, RA_LOCKED); \
207 LIST_FOREACH(node, &V_ng_ID_hash[NG_IDHASH_FN(ID)], \
208 nd_idnodes) { \
209 if (NG_NODE_IS_VALID(node) \
210 && (NG_NODE_ID(node) == ID)) { \
211 break; \
212 } \
213 } \
214 } while (0)
215
216 static struct rwlock ng_namehash_lock;
217 #define NAMEHASH_RLOCK() rw_rlock(&ng_namehash_lock)
218 #define NAMEHASH_RUNLOCK() rw_runlock(&ng_namehash_lock)
219 #define NAMEHASH_WLOCK() rw_wlock(&ng_namehash_lock)
220 #define NAMEHASH_WUNLOCK() rw_wunlock(&ng_namehash_lock)
221
222 /* Internal functions */
223 static int ng_add_hook(node_p node, const char *name, hook_p * hookp);
224 static int ng_generic_msg(node_p here, item_p item, hook_p lasthook);
225 static ng_ID_t ng_decodeidname(const char *name);
226 static int ngb_mod_event(module_t mod, int event, void *data);
227 #ifndef FSTACK
228 static void ng_worklist_add(node_p node);
229 static void ngthread(void *);
230 #endif
231 static int ng_apply_item(node_p node, item_p item, int rw);
232 #ifndef FSTACK
233 static void ng_flush_input_queue(node_p node);
234 #endif
235 static node_p ng_ID2noderef(ng_ID_t ID);
236 static int ng_con_nodes(item_p item, node_p node, const char *name,
237 node_p node2, const char *name2);
238 static int ng_con_part2(node_p node, item_p item, hook_p hook);
239 static int ng_con_part3(node_p node, item_p item, hook_p hook);
240 static int ng_mkpeer(node_p node, const char *name, const char *name2,
241 char *type);
242 static void ng_name_rehash(void);
243 static void ng_ID_rehash(void);
244
245 /* Imported, these used to be externally visible, some may go back. */
246 void ng_destroy_hook(hook_p hook);
247 int ng_path2noderef(node_p here, const char *path,
248 node_p *dest, hook_p *lasthook);
249 int ng_make_node(const char *type, node_p *nodepp);
250 int ng_path_parse(char *addr, char **node, char **path, char **hook);
251 void ng_rmnode(node_p node, hook_p dummy1, void *dummy2, int dummy3);
252 void ng_unname(node_p node);
253
254 /* Our own netgraph malloc type */
255 MALLOC_DEFINE(M_NETGRAPH, "netgraph", "netgraph structures and ctrl messages");
256 MALLOC_DEFINE(M_NETGRAPH_MSG, "netgraph_msg", "netgraph name storage");
257 static MALLOC_DEFINE(M_NETGRAPH_HOOK, "netgraph_hook",
258 "netgraph hook structures");
259 static MALLOC_DEFINE(M_NETGRAPH_NODE, "netgraph_node",
260 "netgraph node structures");
261 static MALLOC_DEFINE(M_NETGRAPH_ITEM, "netgraph_item",
262 "netgraph item structures");
263
264 /* Should not be visible outside this file */
265
266 #define _NG_ALLOC_HOOK(hook) \
267 hook = malloc(sizeof(*hook), M_NETGRAPH_HOOK, M_NOWAIT | M_ZERO)
268 #define _NG_ALLOC_NODE(node) \
269 node = malloc(sizeof(*node), M_NETGRAPH_NODE, M_NOWAIT | M_ZERO)
270
271 #define NG_QUEUE_LOCK_INIT(n) \
272 mtx_init(&(n)->q_mtx, "ng_node", NULL, MTX_DEF)
273 #define NG_QUEUE_LOCK(n) \
274 mtx_lock(&(n)->q_mtx)
275 #define NG_QUEUE_UNLOCK(n) \
276 mtx_unlock(&(n)->q_mtx)
277 #define NG_WORKLIST_LOCK_INIT() \
278 mtx_init(&ng_worklist_mtx, "ng_worklist", NULL, MTX_DEF)
279 #define NG_WORKLIST_LOCK() \
280 mtx_lock(&ng_worklist_mtx)
281 #define NG_WORKLIST_UNLOCK() \
282 mtx_unlock(&ng_worklist_mtx)
283 #define NG_WORKLIST_SLEEP() \
284 mtx_sleep(&ng_worklist, &ng_worklist_mtx, PI_NET, "sleep", 0)
285 #define NG_WORKLIST_WAKEUP() \
286 wakeup_one(&ng_worklist)
287
288 #ifdef NETGRAPH_DEBUG /*----------------------------------------------*/
289 /*
290 * In debug mode:
291 * In an attempt to help track reference count screwups
292 * we do not free objects back to the malloc system, but keep them
293 * in a local cache where we can examine them and keep information safely
294 * after they have been freed.
295 * We use this scheme for nodes and hooks, and to some extent for items.
296 */
297 static __inline hook_p
ng_alloc_hook(void)298 ng_alloc_hook(void)
299 {
300 hook_p hook;
301 SLIST_ENTRY(ng_hook) temp;
302 mtx_lock(&ng_nodelist_mtx);
303 hook = LIST_FIRST(&ng_freehooks);
304 if (hook) {
305 LIST_REMOVE(hook, hk_hooks);
306 bcopy(&hook->hk_all, &temp, sizeof(temp));
307 bzero(hook, sizeof(struct ng_hook));
308 bcopy(&temp, &hook->hk_all, sizeof(temp));
309 mtx_unlock(&ng_nodelist_mtx);
310 hook->hk_magic = HK_MAGIC;
311 } else {
312 mtx_unlock(&ng_nodelist_mtx);
313 _NG_ALLOC_HOOK(hook);
314 if (hook) {
315 hook->hk_magic = HK_MAGIC;
316 mtx_lock(&ng_nodelist_mtx);
317 SLIST_INSERT_HEAD(&ng_allhooks, hook, hk_all);
318 mtx_unlock(&ng_nodelist_mtx);
319 }
320 }
321 return (hook);
322 }
323
324 static __inline node_p
ng_alloc_node(void)325 ng_alloc_node(void)
326 {
327 node_p node;
328 SLIST_ENTRY(ng_node) temp;
329 mtx_lock(&ng_nodelist_mtx);
330 node = LIST_FIRST(&ng_freenodes);
331 if (node) {
332 LIST_REMOVE(node, nd_nodes);
333 bcopy(&node->nd_all, &temp, sizeof(temp));
334 bzero(node, sizeof(struct ng_node));
335 bcopy(&temp, &node->nd_all, sizeof(temp));
336 mtx_unlock(&ng_nodelist_mtx);
337 node->nd_magic = ND_MAGIC;
338 } else {
339 mtx_unlock(&ng_nodelist_mtx);
340 _NG_ALLOC_NODE(node);
341 if (node) {
342 node->nd_magic = ND_MAGIC;
343 mtx_lock(&ng_nodelist_mtx);
344 SLIST_INSERT_HEAD(&ng_allnodes, node, nd_all);
345 mtx_unlock(&ng_nodelist_mtx);
346 }
347 }
348 return (node);
349 }
350
351 #define NG_ALLOC_HOOK(hook) do { (hook) = ng_alloc_hook(); } while (0)
352 #define NG_ALLOC_NODE(node) do { (node) = ng_alloc_node(); } while (0)
353
354 #define NG_FREE_HOOK(hook) \
355 do { \
356 mtx_lock(&ng_nodelist_mtx); \
357 LIST_INSERT_HEAD(&ng_freehooks, hook, hk_hooks); \
358 hook->hk_magic = 0; \
359 mtx_unlock(&ng_nodelist_mtx); \
360 } while (0)
361
362 #define NG_FREE_NODE(node) \
363 do { \
364 mtx_lock(&ng_nodelist_mtx); \
365 LIST_INSERT_HEAD(&ng_freenodes, node, nd_nodes); \
366 node->nd_magic = 0; \
367 mtx_unlock(&ng_nodelist_mtx); \
368 } while (0)
369
370 #else /* NETGRAPH_DEBUG */ /*----------------------------------------------*/
371
372 #define NG_ALLOC_HOOK(hook) _NG_ALLOC_HOOK(hook)
373 #define NG_ALLOC_NODE(node) _NG_ALLOC_NODE(node)
374
375 #define NG_FREE_HOOK(hook) do { free((hook), M_NETGRAPH_HOOK); } while (0)
376 #define NG_FREE_NODE(node) do { free((node), M_NETGRAPH_NODE); } while (0)
377
378 #endif /* NETGRAPH_DEBUG */ /*----------------------------------------------*/
379
380 /* Set this to kdb_enter("X") to catch all errors as they occur */
381 #ifndef TRAP_ERROR
382 #define TRAP_ERROR()
383 #endif
384
385 VNET_DEFINE_STATIC(ng_ID_t, nextID) = 1;
386 #define V_nextID VNET(nextID)
387
388 #ifdef INVARIANTS
389 #define CHECK_DATA_MBUF(m) do { \
390 struct mbuf *n; \
391 int total; \
392 \
393 M_ASSERTPKTHDR(m); \
394 for (total = 0, n = (m); n != NULL; n = n->m_next) { \
395 total += n->m_len; \
396 if (n->m_nextpkt != NULL) \
397 panic("%s: m_nextpkt", __func__); \
398 } \
399 \
400 if ((m)->m_pkthdr.len != total) { \
401 panic("%s: %d != %d", \
402 __func__, (m)->m_pkthdr.len, total); \
403 } \
404 } while (0)
405 #else
406 #define CHECK_DATA_MBUF(m)
407 #endif
408
409 #define ERROUT(x) do { error = (x); goto done; } while (0)
410
411 /************************************************************************
412 Parse type definitions for generic messages
413 ************************************************************************/
414
415 /* Handy structure parse type defining macro */
416 #define DEFINE_PARSE_STRUCT_TYPE(lo, up, args) \
417 static const struct ng_parse_struct_field \
418 ng_ ## lo ## _type_fields[] = NG_GENERIC_ ## up ## _INFO args; \
419 static const struct ng_parse_type ng_generic_ ## lo ## _type = { \
420 &ng_parse_struct_type, \
421 &ng_ ## lo ## _type_fields \
422 }
423
424 DEFINE_PARSE_STRUCT_TYPE(mkpeer, MKPEER, ());
425 DEFINE_PARSE_STRUCT_TYPE(connect, CONNECT, ());
426 DEFINE_PARSE_STRUCT_TYPE(name, NAME, ());
427 DEFINE_PARSE_STRUCT_TYPE(rmhook, RMHOOK, ());
428 DEFINE_PARSE_STRUCT_TYPE(nodeinfo, NODEINFO, ());
429 DEFINE_PARSE_STRUCT_TYPE(typeinfo, TYPEINFO, ());
430 DEFINE_PARSE_STRUCT_TYPE(linkinfo, LINKINFO, (&ng_generic_nodeinfo_type));
431
432 /* Get length of an array when the length is stored as a 32 bit
433 value immediately preceding the array -- as with struct namelist
434 and struct typelist. */
435 static int
ng_generic_list_getLength(const struct ng_parse_type * type,const u_char * start,const u_char * buf)436 ng_generic_list_getLength(const struct ng_parse_type *type,
437 const u_char *start, const u_char *buf)
438 {
439 return *((const u_int32_t *)(buf - 4));
440 }
441
442 /* Get length of the array of struct linkinfo inside a struct hooklist */
443 static int
ng_generic_linkinfo_getLength(const struct ng_parse_type * type,const u_char * start,const u_char * buf)444 ng_generic_linkinfo_getLength(const struct ng_parse_type *type,
445 const u_char *start, const u_char *buf)
446 {
447 const struct hooklist *hl = (const struct hooklist *)start;
448
449 return hl->nodeinfo.hooks;
450 }
451
452 /* Array type for a variable length array of struct namelist */
453 static const struct ng_parse_array_info ng_nodeinfoarray_type_info = {
454 &ng_generic_nodeinfo_type,
455 &ng_generic_list_getLength
456 };
457 static const struct ng_parse_type ng_generic_nodeinfoarray_type = {
458 &ng_parse_array_type,
459 &ng_nodeinfoarray_type_info
460 };
461
462 /* Array type for a variable length array of struct typelist */
463 static const struct ng_parse_array_info ng_typeinfoarray_type_info = {
464 &ng_generic_typeinfo_type,
465 &ng_generic_list_getLength
466 };
467 static const struct ng_parse_type ng_generic_typeinfoarray_type = {
468 &ng_parse_array_type,
469 &ng_typeinfoarray_type_info
470 };
471
472 /* Array type for array of struct linkinfo in struct hooklist */
473 static const struct ng_parse_array_info ng_generic_linkinfo_array_type_info = {
474 &ng_generic_linkinfo_type,
475 &ng_generic_linkinfo_getLength
476 };
477 static const struct ng_parse_type ng_generic_linkinfo_array_type = {
478 &ng_parse_array_type,
479 &ng_generic_linkinfo_array_type_info
480 };
481
482 DEFINE_PARSE_STRUCT_TYPE(typelist, TYPELIST, (&ng_generic_typeinfoarray_type));
483 DEFINE_PARSE_STRUCT_TYPE(hooklist, HOOKLIST,
484 (&ng_generic_nodeinfo_type, &ng_generic_linkinfo_array_type));
485 DEFINE_PARSE_STRUCT_TYPE(listnodes, LISTNODES,
486 (&ng_generic_nodeinfoarray_type));
487
488 /* List of commands and how to convert arguments to/from ASCII */
489 static const struct ng_cmdlist ng_generic_cmds[] = {
490 {
491 NGM_GENERIC_COOKIE,
492 NGM_SHUTDOWN,
493 "shutdown",
494 NULL,
495 NULL
496 },
497 {
498 NGM_GENERIC_COOKIE,
499 NGM_MKPEER,
500 "mkpeer",
501 &ng_generic_mkpeer_type,
502 NULL
503 },
504 {
505 NGM_GENERIC_COOKIE,
506 NGM_CONNECT,
507 "connect",
508 &ng_generic_connect_type,
509 NULL
510 },
511 {
512 NGM_GENERIC_COOKIE,
513 NGM_NAME,
514 "name",
515 &ng_generic_name_type,
516 NULL
517 },
518 {
519 NGM_GENERIC_COOKIE,
520 NGM_RMHOOK,
521 "rmhook",
522 &ng_generic_rmhook_type,
523 NULL
524 },
525 {
526 NGM_GENERIC_COOKIE,
527 NGM_NODEINFO,
528 "nodeinfo",
529 NULL,
530 &ng_generic_nodeinfo_type
531 },
532 {
533 NGM_GENERIC_COOKIE,
534 NGM_LISTHOOKS,
535 "listhooks",
536 NULL,
537 &ng_generic_hooklist_type
538 },
539 {
540 NGM_GENERIC_COOKIE,
541 NGM_LISTNAMES,
542 "listnames",
543 NULL,
544 &ng_generic_listnodes_type /* same as NGM_LISTNODES */
545 },
546 {
547 NGM_GENERIC_COOKIE,
548 NGM_LISTNODES,
549 "listnodes",
550 NULL,
551 &ng_generic_listnodes_type
552 },
553 {
554 NGM_GENERIC_COOKIE,
555 NGM_LISTTYPES,
556 "listtypes",
557 NULL,
558 &ng_generic_typelist_type
559 },
560 {
561 NGM_GENERIC_COOKIE,
562 NGM_TEXT_CONFIG,
563 "textconfig",
564 NULL,
565 &ng_parse_string_type
566 },
567 {
568 NGM_GENERIC_COOKIE,
569 NGM_TEXT_STATUS,
570 "textstatus",
571 NULL,
572 &ng_parse_string_type
573 },
574 {
575 NGM_GENERIC_COOKIE,
576 NGM_ASCII2BINARY,
577 "ascii2binary",
578 &ng_parse_ng_mesg_type,
579 &ng_parse_ng_mesg_type
580 },
581 {
582 NGM_GENERIC_COOKIE,
583 NGM_BINARY2ASCII,
584 "binary2ascii",
585 &ng_parse_ng_mesg_type,
586 &ng_parse_ng_mesg_type
587 },
588 { 0 }
589 };
590
591 /************************************************************************
592 Node routines
593 ************************************************************************/
594
595 /*
596 * Instantiate a node of the requested type
597 */
598 int
ng_make_node(const char * typename,node_p * nodepp)599 ng_make_node(const char *typename, node_p *nodepp)
600 {
601 struct ng_type *type;
602 int error;
603
604 /* Check that the type makes sense */
605 if (typename == NULL) {
606 TRAP_ERROR();
607 return (EINVAL);
608 }
609
610 /* Locate the node type. If we fail we return. Do not try to load
611 * module.
612 */
613 if ((type = ng_findtype(typename)) == NULL)
614 return (ENXIO);
615
616 /*
617 * If we have a constructor, then make the node and
618 * call the constructor to do type specific initialisation.
619 */
620 if (type->constructor != NULL) {
621 if ((error = ng_make_node_common(type, nodepp)) == 0) {
622 if ((error = ((*type->constructor)(*nodepp))) != 0) {
623 NG_NODE_UNREF(*nodepp);
624 }
625 }
626 } else {
627 /*
628 * Node has no constructor. We cannot ask for one
629 * to be made. It must be brought into existence by
630 * some external agency. The external agency should
631 * call ng_make_node_common() directly to get the
632 * netgraph part initialised.
633 */
634 TRAP_ERROR();
635 error = EINVAL;
636 }
637 return (error);
638 }
639
640 /*
641 * Generic node creation. Called by node initialisation for externally
642 * instantiated nodes (e.g. hardware, sockets, etc ).
643 * The returned node has a reference count of 1.
644 */
645 int
ng_make_node_common(struct ng_type * type,node_p * nodepp)646 ng_make_node_common(struct ng_type *type, node_p *nodepp)
647 {
648 node_p node;
649
650 /* Require the node type to have been already installed */
651 if (ng_findtype(type->name) == NULL) {
652 TRAP_ERROR();
653 return (EINVAL);
654 }
655
656 /* Make a node and try attach it to the type */
657 NG_ALLOC_NODE(node);
658 if (node == NULL) {
659 TRAP_ERROR();
660 return (ENOMEM);
661 }
662 node->nd_type = type;
663 #ifdef VIMAGE
664 node->nd_vnet = curvnet;
665 #endif
666 NG_NODE_REF(node); /* note reference */
667 type->refs++;
668
669 NG_QUEUE_LOCK_INIT(&node->nd_input_queue);
670 STAILQ_INIT(&node->nd_input_queue.queue);
671 node->nd_input_queue.q_flags = 0;
672
673 /* Initialize hook list for new node */
674 LIST_INIT(&node->nd_hooks);
675
676 /* Get an ID and put us in the hash chain. */
677 IDHASH_WLOCK();
678 for (;;) { /* wrap protection, even if silly */
679 node_p node2 = NULL;
680 node->nd_ID = V_nextID++; /* 137/sec for 1 year before wrap */
681
682 /* Is there a problem with the new number? */
683 NG_IDHASH_FIND(node->nd_ID, node2); /* already taken? */
684 if ((node->nd_ID != 0) && (node2 == NULL)) {
685 break;
686 }
687 }
688 V_ng_nodes++;
689 if (V_ng_nodes * 2 > V_ng_ID_hmask)
690 ng_ID_rehash();
691 LIST_INSERT_HEAD(&V_ng_ID_hash[NG_IDHASH_FN(node->nd_ID)], node,
692 nd_idnodes);
693 IDHASH_WUNLOCK();
694
695 /* Done */
696 *nodepp = node;
697 return (0);
698 }
699
700 /*
701 * Forceably start the shutdown process on a node. Either call
702 * its shutdown method, or do the default shutdown if there is
703 * no type-specific method.
704 *
705 * We can only be called from a shutdown message, so we know we have
706 * a writer lock, and therefore exclusive access. It also means
707 * that we should not be on the work queue, but we check anyhow.
708 *
709 * Persistent node types must have a type-specific method which
710 * allocates a new node in which case, this one is irretrievably going away,
711 * or cleans up anything it needs, and just makes the node valid again,
712 * in which case we allow the node to survive.
713 *
714 * XXX We need to think of how to tell a persistent node that we
715 * REALLY need to go away because the hardware has gone or we
716 * are rebooting.... etc.
717 */
718 void
ng_rmnode(node_p node,hook_p dummy1,void * dummy2,int dummy3)719 ng_rmnode(node_p node, hook_p dummy1, void *dummy2, int dummy3)
720 {
721 hook_p hook;
722
723 /* Check if it's already shutting down */
724 if ((node->nd_flags & NGF_CLOSING) != 0)
725 return;
726
727 if (node == &ng_deadnode) {
728 printf ("shutdown called on deadnode\n");
729 return;
730 }
731
732 /* Add an extra reference so it doesn't go away during this */
733 NG_NODE_REF(node);
734
735 /*
736 * Mark it invalid so any newcomers know not to try use it
737 * Also add our own mark so we can't recurse
738 * note that NGF_INVALID does not do this as it's also set during
739 * creation
740 */
741 node->nd_flags |= NGF_INVALID|NGF_CLOSING;
742
743 /* If node has its pre-shutdown method, then call it first*/
744 if (node->nd_type && node->nd_type->close)
745 (*node->nd_type->close)(node);
746
747 /* Notify all remaining connected nodes to disconnect */
748 while ((hook = LIST_FIRST(&node->nd_hooks)) != NULL)
749 ng_destroy_hook(hook);
750
751 #ifndef FSTACK
752 /*
753 * Drain the input queue forceably.
754 * it has no hooks so what's it going to do, bleed on someone?
755 * Theoretically we came here from a queue entry that was added
756 * Just before the queue was closed, so it should be empty anyway.
757 * Also removes us from worklist if needed.
758 */
759 ng_flush_input_queue(node);
760 #endif
761
762 /* Ask the type if it has anything to do in this case */
763 if (node->nd_type && node->nd_type->shutdown) {
764 (*node->nd_type->shutdown)(node);
765 if (NG_NODE_IS_VALID(node)) {
766 /*
767 * Well, blow me down if the node code hasn't declared
768 * that it doesn't want to die.
769 * Presumably it is a persistent node.
770 * If we REALLY want it to go away,
771 * e.g. hardware going away,
772 * Our caller should set NGF_REALLY_DIE in nd_flags.
773 */
774 node->nd_flags &= ~(NGF_INVALID|NGF_CLOSING);
775 NG_NODE_UNREF(node); /* Assume they still have theirs */
776 return;
777 }
778 } else { /* do the default thing */
779 NG_NODE_UNREF(node);
780 }
781
782 ng_unname(node); /* basically a NOP these days */
783
784 /*
785 * Remove extra reference, possibly the last
786 * Possible other holders of references may include
787 * timeout callouts, but theoretically the node's supposed to
788 * have cancelled them. Possibly hardware dependencies may
789 * force a driver to 'linger' with a reference.
790 */
791 NG_NODE_UNREF(node);
792 }
793
794 /*
795 * Remove a reference to the node, possibly the last.
796 * deadnode always acts as it it were the last.
797 */
798 void
ng_unref_node(node_p node)799 ng_unref_node(node_p node)
800 {
801
802 if (node == &ng_deadnode)
803 return;
804
805 CURVNET_SET(node->nd_vnet);
806
807 if (refcount_release(&node->nd_refs)) { /* we were the last */
808
809 node->nd_type->refs--; /* XXX maybe should get types lock? */
810 NAMEHASH_WLOCK();
811 if (NG_NODE_HAS_NAME(node)) {
812 V_ng_named_nodes--;
813 LIST_REMOVE(node, nd_nodes);
814 }
815 NAMEHASH_WUNLOCK();
816
817 IDHASH_WLOCK();
818 V_ng_nodes--;
819 LIST_REMOVE(node, nd_idnodes);
820 IDHASH_WUNLOCK();
821
822 mtx_destroy(&node->nd_input_queue.q_mtx);
823 NG_FREE_NODE(node);
824 }
825 CURVNET_RESTORE();
826 }
827
828 /************************************************************************
829 Node ID handling
830 ************************************************************************/
831 static node_p
ng_ID2noderef(ng_ID_t ID)832 ng_ID2noderef(ng_ID_t ID)
833 {
834 node_p node;
835
836 IDHASH_RLOCK();
837 NG_IDHASH_FIND(ID, node);
838 if (node)
839 NG_NODE_REF(node);
840 IDHASH_RUNLOCK();
841 return(node);
842 }
843
844 ng_ID_t
ng_node2ID(node_p node)845 ng_node2ID(node_p node)
846 {
847 return (node ? NG_NODE_ID(node) : 0);
848 }
849
850 /************************************************************************
851 Node name handling
852 ************************************************************************/
853
854 /*
855 * Assign a node a name.
856 */
857 int
ng_name_node(node_p node,const char * name)858 ng_name_node(node_p node, const char *name)
859 {
860 uint32_t hash;
861 node_p node2;
862 int i;
863
864 /* Check the name is valid */
865 for (i = 0; i < NG_NODESIZ; i++) {
866 if (name[i] == '\0' || name[i] == '.' || name[i] == ':')
867 break;
868 }
869 if (i == 0 || name[i] != '\0') {
870 TRAP_ERROR();
871 return (EINVAL);
872 }
873 if (ng_decodeidname(name) != 0) { /* valid IDs not allowed here */
874 TRAP_ERROR();
875 return (EINVAL);
876 }
877
878 NAMEHASH_WLOCK();
879 if (V_ng_named_nodes * 2 > V_ng_name_hmask)
880 ng_name_rehash();
881
882 hash = hash32_str(name, HASHINIT) & V_ng_name_hmask;
883 /* Check the name isn't already being used. */
884 LIST_FOREACH(node2, &V_ng_name_hash[hash], nd_nodes)
885 if (NG_NODE_IS_VALID(node2) &&
886 (strcmp(NG_NODE_NAME(node2), name) == 0)) {
887 NAMEHASH_WUNLOCK();
888 return (EADDRINUSE);
889 }
890
891 if (NG_NODE_HAS_NAME(node))
892 LIST_REMOVE(node, nd_nodes);
893 else
894 V_ng_named_nodes++;
895 /* Copy it. */
896 strlcpy(NG_NODE_NAME(node), name, NG_NODESIZ);
897 /* Update name hash. */
898 LIST_INSERT_HEAD(&V_ng_name_hash[hash], node, nd_nodes);
899 NAMEHASH_WUNLOCK();
900
901 return (0);
902 }
903
904 /*
905 * Find a node by absolute name. The name should NOT end with ':'
906 * The name "." means "this node" and "[xxx]" means "the node
907 * with ID (ie, at address) xxx".
908 *
909 * Returns the node if found, else NULL.
910 * Eventually should add something faster than a sequential search.
911 * Note it acquires a reference on the node so you can be sure it's still
912 * there.
913 */
914 node_p
ng_name2noderef(node_p here,const char * name)915 ng_name2noderef(node_p here, const char *name)
916 {
917 node_p node;
918 ng_ID_t temp;
919 int hash;
920
921 /* "." means "this node" */
922 if (strcmp(name, ".") == 0) {
923 NG_NODE_REF(here);
924 return(here);
925 }
926
927 /* Check for name-by-ID */
928 if ((temp = ng_decodeidname(name)) != 0) {
929 return (ng_ID2noderef(temp));
930 }
931
932 /* Find node by name. */
933 hash = hash32_str(name, HASHINIT) & V_ng_name_hmask;
934 NAMEHASH_RLOCK();
935 LIST_FOREACH(node, &V_ng_name_hash[hash], nd_nodes)
936 if (NG_NODE_IS_VALID(node) &&
937 (strcmp(NG_NODE_NAME(node), name) == 0)) {
938 NG_NODE_REF(node);
939 break;
940 }
941 NAMEHASH_RUNLOCK();
942
943 return (node);
944 }
945
946 /*
947 * Decode an ID name, eg. "[f03034de]". Returns 0 if the
948 * string is not valid, otherwise returns the value.
949 */
950 static ng_ID_t
ng_decodeidname(const char * name)951 ng_decodeidname(const char *name)
952 {
953 const int len = strlen(name);
954 char *eptr;
955 u_long val;
956
957 /* Check for proper length, brackets, no leading junk */
958 if ((len < 3) || (name[0] != '[') || (name[len - 1] != ']') ||
959 (!isxdigit(name[1])))
960 return ((ng_ID_t)0);
961
962 /* Decode number */
963 val = strtoul(name + 1, &eptr, 16);
964 if ((eptr - name != len - 1) || (val == ULONG_MAX) || (val == 0))
965 return ((ng_ID_t)0);
966
967 return ((ng_ID_t)val);
968 }
969
970 /*
971 * Remove a name from a node. This should only be called
972 * when shutting down and removing the node.
973 */
974 void
ng_unname(node_p node)975 ng_unname(node_p node)
976 {
977 }
978
979 /*
980 * Allocate a bigger name hash.
981 */
982 static void
ng_name_rehash()983 ng_name_rehash()
984 {
985 struct nodehash *new;
986 uint32_t hash;
987 u_long hmask;
988 node_p node, node2;
989 int i;
990
991 new = hashinit_flags((V_ng_name_hmask + 1) * 2, M_NETGRAPH_NODE, &hmask,
992 HASH_NOWAIT);
993 if (new == NULL)
994 return;
995
996 for (i = 0; i <= V_ng_name_hmask; i++)
997 LIST_FOREACH_SAFE(node, &V_ng_name_hash[i], nd_nodes, node2) {
998 #ifdef INVARIANTS
999 LIST_REMOVE(node, nd_nodes);
1000 #endif
1001 hash = hash32_str(NG_NODE_NAME(node), HASHINIT) & hmask;
1002 LIST_INSERT_HEAD(&new[hash], node, nd_nodes);
1003 }
1004
1005 hashdestroy(V_ng_name_hash, M_NETGRAPH_NODE, V_ng_name_hmask);
1006 V_ng_name_hash = new;
1007 V_ng_name_hmask = hmask;
1008 }
1009
1010 /*
1011 * Allocate a bigger ID hash.
1012 */
1013 static void
ng_ID_rehash()1014 ng_ID_rehash()
1015 {
1016 struct nodehash *new;
1017 uint32_t hash;
1018 u_long hmask;
1019 node_p node, node2;
1020 int i;
1021
1022 new = hashinit_flags((V_ng_ID_hmask + 1) * 2, M_NETGRAPH_NODE, &hmask,
1023 HASH_NOWAIT);
1024 if (new == NULL)
1025 return;
1026
1027 for (i = 0; i <= V_ng_ID_hmask; i++)
1028 LIST_FOREACH_SAFE(node, &V_ng_ID_hash[i], nd_idnodes, node2) {
1029 #ifdef INVARIANTS
1030 LIST_REMOVE(node, nd_idnodes);
1031 #endif
1032 hash = (node->nd_ID % (hmask + 1));
1033 LIST_INSERT_HEAD(&new[hash], node, nd_idnodes);
1034 }
1035
1036 hashdestroy(V_ng_ID_hash, M_NETGRAPH_NODE, V_ng_name_hmask);
1037 V_ng_ID_hash = new;
1038 V_ng_ID_hmask = hmask;
1039 }
1040
1041 /************************************************************************
1042 Hook routines
1043 Names are not optional. Hooks are always connected, except for a
1044 brief moment within these routines. On invalidation or during creation
1045 they are connected to the 'dead' hook.
1046 ************************************************************************/
1047
1048 /*
1049 * Remove a hook reference
1050 */
1051 void
ng_unref_hook(hook_p hook)1052 ng_unref_hook(hook_p hook)
1053 {
1054
1055 if (hook == &ng_deadhook)
1056 return;
1057
1058 if (refcount_release(&hook->hk_refs)) { /* we were the last */
1059 if (_NG_HOOK_NODE(hook)) /* it'll probably be ng_deadnode */
1060 _NG_NODE_UNREF((_NG_HOOK_NODE(hook)));
1061 NG_FREE_HOOK(hook);
1062 }
1063 }
1064
1065 /*
1066 * Add an unconnected hook to a node. Only used internally.
1067 * Assumes node is locked. (XXX not yet true )
1068 */
1069 static int
ng_add_hook(node_p node,const char * name,hook_p * hookp)1070 ng_add_hook(node_p node, const char *name, hook_p *hookp)
1071 {
1072 hook_p hook;
1073 int error = 0;
1074
1075 /* Check that the given name is good */
1076 if (name == NULL) {
1077 TRAP_ERROR();
1078 return (EINVAL);
1079 }
1080 if (ng_findhook(node, name) != NULL) {
1081 TRAP_ERROR();
1082 return (EEXIST);
1083 }
1084
1085 /* Allocate the hook and link it up */
1086 NG_ALLOC_HOOK(hook);
1087 if (hook == NULL) {
1088 TRAP_ERROR();
1089 return (ENOMEM);
1090 }
1091 hook->hk_refs = 1; /* add a reference for us to return */
1092 hook->hk_flags = HK_INVALID;
1093 hook->hk_peer = &ng_deadhook; /* start off this way */
1094 hook->hk_node = node;
1095 NG_NODE_REF(node); /* each hook counts as a reference */
1096
1097 /* Set hook name */
1098 strlcpy(NG_HOOK_NAME(hook), name, NG_HOOKSIZ);
1099
1100 /*
1101 * Check if the node type code has something to say about it
1102 * If it fails, the unref of the hook will also unref the node.
1103 */
1104 if (node->nd_type->newhook != NULL) {
1105 if ((error = (*node->nd_type->newhook)(node, hook, name))) {
1106 NG_HOOK_UNREF(hook); /* this frees the hook */
1107 return (error);
1108 }
1109 }
1110 /*
1111 * The 'type' agrees so far, so go ahead and link it in.
1112 * We'll ask again later when we actually connect the hooks.
1113 */
1114 LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks);
1115 node->nd_numhooks++;
1116 NG_HOOK_REF(hook); /* one for the node */
1117
1118 if (hookp)
1119 *hookp = hook;
1120 return (0);
1121 }
1122
1123 /*
1124 * Find a hook
1125 *
1126 * Node types may supply their own optimized routines for finding
1127 * hooks. If none is supplied, we just do a linear search.
1128 * XXX Possibly we should add a reference to the hook?
1129 */
1130 hook_p
ng_findhook(node_p node,const char * name)1131 ng_findhook(node_p node, const char *name)
1132 {
1133 hook_p hook;
1134
1135 if (node->nd_type->findhook != NULL)
1136 return (*node->nd_type->findhook)(node, name);
1137 LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) {
1138 if (NG_HOOK_IS_VALID(hook) &&
1139 (strcmp(NG_HOOK_NAME(hook), name) == 0))
1140 return (hook);
1141 }
1142 return (NULL);
1143 }
1144
1145 /*
1146 * Destroy a hook
1147 *
1148 * As hooks are always attached, this really destroys two hooks.
1149 * The one given, and the one attached to it. Disconnect the hooks
1150 * from each other first. We reconnect the peer hook to the 'dead'
1151 * hook so that it can still exist after we depart. We then
1152 * send the peer its own destroy message. This ensures that we only
1153 * interact with the peer's structures when it is locked processing that
1154 * message. We hold a reference to the peer hook so we are guaranteed that
1155 * the peer hook and node are still going to exist until
1156 * we are finished there as the hook holds a ref on the node.
1157 * We run this same code again on the peer hook, but that time it is already
1158 * attached to the 'dead' hook.
1159 *
1160 * This routine is called at all stages of hook creation
1161 * on error detection and must be able to handle any such stage.
1162 */
1163 void
ng_destroy_hook(hook_p hook)1164 ng_destroy_hook(hook_p hook)
1165 {
1166 hook_p peer;
1167 node_p node;
1168
1169 if (hook == &ng_deadhook) { /* better safe than sorry */
1170 printf("ng_destroy_hook called on deadhook\n");
1171 return;
1172 }
1173
1174 /*
1175 * Protect divorce process with mutex, to avoid races on
1176 * simultaneous disconnect.
1177 */
1178 TOPOLOGY_WLOCK();
1179
1180 hook->hk_flags |= HK_INVALID;
1181
1182 peer = NG_HOOK_PEER(hook);
1183 node = NG_HOOK_NODE(hook);
1184
1185 if (peer && (peer != &ng_deadhook)) {
1186 /*
1187 * Set the peer to point to ng_deadhook
1188 * from this moment on we are effectively independent it.
1189 * send it an rmhook message of its own.
1190 */
1191 peer->hk_peer = &ng_deadhook; /* They no longer know us */
1192 hook->hk_peer = &ng_deadhook; /* Nor us, them */
1193 if (NG_HOOK_NODE(peer) == &ng_deadnode) {
1194 /*
1195 * If it's already divorced from a node,
1196 * just free it.
1197 */
1198 TOPOLOGY_WUNLOCK();
1199 } else {
1200 TOPOLOGY_WUNLOCK();
1201 ng_rmhook_self(peer); /* Send it a surprise */
1202 }
1203 NG_HOOK_UNREF(peer); /* account for peer link */
1204 NG_HOOK_UNREF(hook); /* account for peer link */
1205 } else
1206 TOPOLOGY_WUNLOCK();
1207
1208 TOPOLOGY_NOTOWNED();
1209
1210 /*
1211 * Remove the hook from the node's list to avoid possible recursion
1212 * in case the disconnection results in node shutdown.
1213 */
1214 if (node == &ng_deadnode) { /* happens if called from ng_con_nodes() */
1215 return;
1216 }
1217 LIST_REMOVE(hook, hk_hooks);
1218 node->nd_numhooks--;
1219 if (node->nd_type->disconnect) {
1220 /*
1221 * The type handler may elect to destroy the node so don't
1222 * trust its existence after this point. (except
1223 * that we still hold a reference on it. (which we
1224 * inherrited from the hook we are destroying)
1225 */
1226 (*node->nd_type->disconnect) (hook);
1227 }
1228
1229 /*
1230 * Note that because we will point to ng_deadnode, the original node
1231 * is not decremented automatically so we do that manually.
1232 */
1233 _NG_HOOK_NODE(hook) = &ng_deadnode;
1234 NG_NODE_UNREF(node); /* We no longer point to it so adjust count */
1235 NG_HOOK_UNREF(hook); /* Account for linkage (in list) to node */
1236 }
1237
1238 /*
1239 * Take two hooks on a node and merge the connection so that the given node
1240 * is effectively bypassed.
1241 */
1242 int
ng_bypass(hook_p hook1,hook_p hook2)1243 ng_bypass(hook_p hook1, hook_p hook2)
1244 {
1245 if (hook1->hk_node != hook2->hk_node) {
1246 TRAP_ERROR();
1247 return (EINVAL);
1248 }
1249 TOPOLOGY_WLOCK();
1250 if (NG_HOOK_NOT_VALID(hook1) || NG_HOOK_NOT_VALID(hook2)) {
1251 TOPOLOGY_WUNLOCK();
1252 return (EINVAL);
1253 }
1254 hook1->hk_peer->hk_peer = hook2->hk_peer;
1255 hook2->hk_peer->hk_peer = hook1->hk_peer;
1256
1257 hook1->hk_peer = &ng_deadhook;
1258 hook2->hk_peer = &ng_deadhook;
1259 TOPOLOGY_WUNLOCK();
1260
1261 NG_HOOK_UNREF(hook1);
1262 NG_HOOK_UNREF(hook2);
1263
1264 /* XXX If we ever cache methods on hooks update them as well */
1265 ng_destroy_hook(hook1);
1266 ng_destroy_hook(hook2);
1267 return (0);
1268 }
1269
1270 /*
1271 * Install a new netgraph type
1272 */
1273 int
ng_newtype(struct ng_type * tp)1274 ng_newtype(struct ng_type *tp)
1275 {
1276 const size_t namelen = strlen(tp->name);
1277
1278 /* Check version and type name fields */
1279 if ((tp->version != NG_ABI_VERSION) || (namelen == 0) ||
1280 (namelen >= NG_TYPESIZ)) {
1281 TRAP_ERROR();
1282 if (tp->version != NG_ABI_VERSION) {
1283 printf("Netgraph: Node type rejected. ABI mismatch. "
1284 "Suggest recompile\n");
1285 }
1286 return (EINVAL);
1287 }
1288
1289 /* Check for name collision */
1290 if (ng_findtype(tp->name) != NULL) {
1291 TRAP_ERROR();
1292 return (EEXIST);
1293 }
1294
1295 /* Link in new type */
1296 TYPELIST_WLOCK();
1297 LIST_INSERT_HEAD(&ng_typelist, tp, types);
1298 tp->refs = 1; /* first ref is linked list */
1299 TYPELIST_WUNLOCK();
1300 return (0);
1301 }
1302
1303 /*
1304 * unlink a netgraph type
1305 * If no examples exist
1306 */
1307 int
ng_rmtype(struct ng_type * tp)1308 ng_rmtype(struct ng_type *tp)
1309 {
1310 /* Check for name collision */
1311 if (tp->refs != 1) {
1312 TRAP_ERROR();
1313 return (EBUSY);
1314 }
1315
1316 /* Unlink type */
1317 TYPELIST_WLOCK();
1318 LIST_REMOVE(tp, types);
1319 TYPELIST_WUNLOCK();
1320 return (0);
1321 }
1322
1323 /*
1324 * Look for a type of the name given
1325 */
1326 struct ng_type *
ng_findtype(const char * typename)1327 ng_findtype(const char *typename)
1328 {
1329 struct ng_type *type;
1330
1331 TYPELIST_RLOCK();
1332 LIST_FOREACH(type, &ng_typelist, types) {
1333 if (strcmp(type->name, typename) == 0)
1334 break;
1335 }
1336 TYPELIST_RUNLOCK();
1337 return (type);
1338 }
1339
1340 /************************************************************************
1341 Composite routines
1342 ************************************************************************/
1343 /*
1344 * Connect two nodes using the specified hooks, using queued functions.
1345 */
1346 static int
ng_con_part3(node_p node,item_p item,hook_p hook)1347 ng_con_part3(node_p node, item_p item, hook_p hook)
1348 {
1349 int error = 0;
1350
1351 /*
1352 * When we run, we know that the node 'node' is locked for us.
1353 * Our caller has a reference on the hook.
1354 * Our caller has a reference on the node.
1355 * (In this case our caller is ng_apply_item() ).
1356 * The peer hook has a reference on the hook.
1357 * We are all set up except for the final call to the node, and
1358 * the clearing of the INVALID flag.
1359 */
1360 if (NG_HOOK_NODE(hook) == &ng_deadnode) {
1361 /*
1362 * The node must have been freed again since we last visited
1363 * here. ng_destry_hook() has this effect but nothing else does.
1364 * We should just release our references and
1365 * free anything we can think of.
1366 * Since we know it's been destroyed, and it's our caller
1367 * that holds the references, just return.
1368 */
1369 ERROUT(ENOENT);
1370 }
1371 if (hook->hk_node->nd_type->connect) {
1372 if ((error = (*hook->hk_node->nd_type->connect) (hook))) {
1373 ng_destroy_hook(hook); /* also zaps peer */
1374 printf("failed in ng_con_part3()\n");
1375 ERROUT(error);
1376 }
1377 }
1378 /*
1379 * XXX this is wrong for SMP. Possibly we need
1380 * to separate out 'create' and 'invalid' flags.
1381 * should only set flags on hooks we have locked under our node.
1382 */
1383 hook->hk_flags &= ~HK_INVALID;
1384 done:
1385 NG_FREE_ITEM(item);
1386 return (error);
1387 }
1388
1389 static int
ng_con_part2(node_p node,item_p item,hook_p hook)1390 ng_con_part2(node_p node, item_p item, hook_p hook)
1391 {
1392 hook_p peer;
1393 int error = 0;
1394
1395 /*
1396 * When we run, we know that the node 'node' is locked for us.
1397 * Our caller has a reference on the hook.
1398 * Our caller has a reference on the node.
1399 * (In this case our caller is ng_apply_item() ).
1400 * The peer hook has a reference on the hook.
1401 * our node pointer points to the 'dead' node.
1402 * First check the hook name is unique.
1403 * Should not happen because we checked before queueing this.
1404 */
1405 if (ng_findhook(node, NG_HOOK_NAME(hook)) != NULL) {
1406 TRAP_ERROR();
1407 ng_destroy_hook(hook); /* should destroy peer too */
1408 printf("failed in ng_con_part2()\n");
1409 ERROUT(EEXIST);
1410 }
1411 /*
1412 * Check if the node type code has something to say about it
1413 * If it fails, the unref of the hook will also unref the attached node,
1414 * however since that node is 'ng_deadnode' this will do nothing.
1415 * The peer hook will also be destroyed.
1416 */
1417 if (node->nd_type->newhook != NULL) {
1418 if ((error = (*node->nd_type->newhook)(node, hook,
1419 hook->hk_name))) {
1420 ng_destroy_hook(hook); /* should destroy peer too */
1421 printf("failed in ng_con_part2()\n");
1422 ERROUT(error);
1423 }
1424 }
1425
1426 /*
1427 * The 'type' agrees so far, so go ahead and link it in.
1428 * We'll ask again later when we actually connect the hooks.
1429 */
1430 hook->hk_node = node; /* just overwrite ng_deadnode */
1431 NG_NODE_REF(node); /* each hook counts as a reference */
1432 LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks);
1433 node->nd_numhooks++;
1434 NG_HOOK_REF(hook); /* one for the node */
1435
1436 /*
1437 * We now have a symmetrical situation, where both hooks have been
1438 * linked to their nodes, the newhook methods have been called
1439 * And the references are all correct. The hooks are still marked
1440 * as invalid, as we have not called the 'connect' methods
1441 * yet.
1442 * We can call the local one immediately as we have the
1443 * node locked, but we need to queue the remote one.
1444 */
1445 if (hook->hk_node->nd_type->connect) {
1446 if ((error = (*hook->hk_node->nd_type->connect) (hook))) {
1447 ng_destroy_hook(hook); /* also zaps peer */
1448 printf("failed in ng_con_part2(A)\n");
1449 ERROUT(error);
1450 }
1451 }
1452
1453 /*
1454 * Acquire topo mutex to avoid race with ng_destroy_hook().
1455 */
1456 TOPOLOGY_RLOCK();
1457 peer = hook->hk_peer;
1458 if (peer == &ng_deadhook) {
1459 TOPOLOGY_RUNLOCK();
1460 printf("failed in ng_con_part2(B)\n");
1461 ng_destroy_hook(hook);
1462 ERROUT(ENOENT);
1463 }
1464 TOPOLOGY_RUNLOCK();
1465
1466 if ((error = ng_send_fn2(peer->hk_node, peer, item, &ng_con_part3,
1467 NULL, 0, NG_REUSE_ITEM))) {
1468 printf("failed in ng_con_part2(C)\n");
1469 ng_destroy_hook(hook); /* also zaps peer */
1470 return (error); /* item was consumed. */
1471 }
1472 hook->hk_flags &= ~HK_INVALID; /* need both to be able to work */
1473 return (0); /* item was consumed. */
1474 done:
1475 NG_FREE_ITEM(item);
1476 return (error);
1477 }
1478
1479 /*
1480 * Connect this node with another node. We assume that this node is
1481 * currently locked, as we are only called from an NGM_CONNECT message.
1482 */
1483 static int
ng_con_nodes(item_p item,node_p node,const char * name,node_p node2,const char * name2)1484 ng_con_nodes(item_p item, node_p node, const char *name,
1485 node_p node2, const char *name2)
1486 {
1487 int error;
1488 hook_p hook;
1489 hook_p hook2;
1490
1491 if (ng_findhook(node2, name2) != NULL) {
1492 return(EEXIST);
1493 }
1494 if ((error = ng_add_hook(node, name, &hook))) /* gives us a ref */
1495 return (error);
1496 /* Allocate the other hook and link it up */
1497 NG_ALLOC_HOOK(hook2);
1498 if (hook2 == NULL) {
1499 TRAP_ERROR();
1500 ng_destroy_hook(hook); /* XXX check ref counts so far */
1501 NG_HOOK_UNREF(hook); /* including our ref */
1502 return (ENOMEM);
1503 }
1504 hook2->hk_refs = 1; /* start with a reference for us. */
1505 hook2->hk_flags = HK_INVALID;
1506 hook2->hk_peer = hook; /* Link the two together */
1507 hook->hk_peer = hook2;
1508 NG_HOOK_REF(hook); /* Add a ref for the peer to each*/
1509 NG_HOOK_REF(hook2);
1510 hook2->hk_node = &ng_deadnode;
1511 strlcpy(NG_HOOK_NAME(hook2), name2, NG_HOOKSIZ);
1512
1513 /*
1514 * Queue the function above.
1515 * Procesing continues in that function in the lock context of
1516 * the other node.
1517 */
1518 if ((error = ng_send_fn2(node2, hook2, item, &ng_con_part2, NULL, 0,
1519 NG_NOFLAGS))) {
1520 printf("failed in ng_con_nodes(): %d\n", error);
1521 ng_destroy_hook(hook); /* also zaps peer */
1522 }
1523
1524 NG_HOOK_UNREF(hook); /* Let each hook go if it wants to */
1525 NG_HOOK_UNREF(hook2);
1526 return (error);
1527 }
1528
1529 /*
1530 * Make a peer and connect.
1531 * We assume that the local node is locked.
1532 * The new node probably doesn't need a lock until
1533 * it has a hook, because it cannot really have any work until then,
1534 * but we should think about it a bit more.
1535 *
1536 * The problem may come if the other node also fires up
1537 * some hardware or a timer or some other source of activation,
1538 * also it may already get a command msg via it's ID.
1539 *
1540 * We could use the same method as ng_con_nodes() but we'd have
1541 * to add ability to remove the node when failing. (Not hard, just
1542 * make arg1 point to the node to remove).
1543 * Unless of course we just ignore failure to connect and leave
1544 * an unconnected node?
1545 */
1546 static int
ng_mkpeer(node_p node,const char * name,const char * name2,char * type)1547 ng_mkpeer(node_p node, const char *name, const char *name2, char *type)
1548 {
1549 node_p node2;
1550 hook_p hook1, hook2;
1551 int error;
1552
1553 if ((error = ng_make_node(type, &node2))) {
1554 return (error);
1555 }
1556
1557 if ((error = ng_add_hook(node, name, &hook1))) { /* gives us a ref */
1558 ng_rmnode(node2, NULL, NULL, 0);
1559 return (error);
1560 }
1561
1562 if ((error = ng_add_hook(node2, name2, &hook2))) {
1563 ng_rmnode(node2, NULL, NULL, 0);
1564 ng_destroy_hook(hook1);
1565 NG_HOOK_UNREF(hook1);
1566 return (error);
1567 }
1568
1569 /*
1570 * Actually link the two hooks together.
1571 */
1572 hook1->hk_peer = hook2;
1573 hook2->hk_peer = hook1;
1574
1575 /* Each hook is referenced by the other */
1576 NG_HOOK_REF(hook1);
1577 NG_HOOK_REF(hook2);
1578
1579 /* Give each node the opportunity to veto the pending connection */
1580 if (hook1->hk_node->nd_type->connect) {
1581 error = (*hook1->hk_node->nd_type->connect) (hook1);
1582 }
1583
1584 if ((error == 0) && hook2->hk_node->nd_type->connect) {
1585 error = (*hook2->hk_node->nd_type->connect) (hook2);
1586 }
1587
1588 /*
1589 * drop the references we were holding on the two hooks.
1590 */
1591 if (error) {
1592 ng_destroy_hook(hook2); /* also zaps hook1 */
1593 ng_rmnode(node2, NULL, NULL, 0);
1594 } else {
1595 /* As a last act, allow the hooks to be used */
1596 hook1->hk_flags &= ~HK_INVALID;
1597 hook2->hk_flags &= ~HK_INVALID;
1598 }
1599 NG_HOOK_UNREF(hook1);
1600 NG_HOOK_UNREF(hook2);
1601 return (error);
1602 }
1603
1604 /************************************************************************
1605 Utility routines to send self messages
1606 ************************************************************************/
1607
1608 /* Shut this node down as soon as everyone is clear of it */
1609 /* Should add arg "immediately" to jump the queue */
1610 int
ng_rmnode_self(node_p node)1611 ng_rmnode_self(node_p node)
1612 {
1613 int error;
1614
1615 if (node == &ng_deadnode)
1616 return (0);
1617 node->nd_flags |= NGF_INVALID;
1618 if (node->nd_flags & NGF_CLOSING)
1619 return (0);
1620
1621 error = ng_send_fn(node, NULL, &ng_rmnode, NULL, 0);
1622 return (error);
1623 }
1624
1625 static void
ng_rmhook_part2(node_p node,hook_p hook,void * arg1,int arg2)1626 ng_rmhook_part2(node_p node, hook_p hook, void *arg1, int arg2)
1627 {
1628 ng_destroy_hook(hook);
1629 return ;
1630 }
1631
1632 int
ng_rmhook_self(hook_p hook)1633 ng_rmhook_self(hook_p hook)
1634 {
1635 int error;
1636 node_p node = NG_HOOK_NODE(hook);
1637
1638 if (node == &ng_deadnode)
1639 return (0);
1640
1641 error = ng_send_fn(node, hook, &ng_rmhook_part2, NULL, 0);
1642 return (error);
1643 }
1644
1645 /***********************************************************************
1646 * Parse and verify a string of the form: <NODE:><PATH>
1647 *
1648 * Such a string can refer to a specific node or a specific hook
1649 * on a specific node, depending on how you look at it. In the
1650 * latter case, the PATH component must not end in a dot.
1651 *
1652 * Both <NODE:> and <PATH> are optional. The <PATH> is a string
1653 * of hook names separated by dots. This breaks out the original
1654 * string, setting *nodep to "NODE" (or NULL if none) and *pathp
1655 * to "PATH" (or NULL if degenerate). Also, *hookp will point to
1656 * the final hook component of <PATH>, if any, otherwise NULL.
1657 *
1658 * This returns -1 if the path is malformed. The char ** are optional.
1659 ***********************************************************************/
1660 int
ng_path_parse(char * addr,char ** nodep,char ** pathp,char ** hookp)1661 ng_path_parse(char *addr, char **nodep, char **pathp, char **hookp)
1662 {
1663 char *node, *path, *hook;
1664 int k;
1665
1666 /*
1667 * Extract absolute NODE, if any
1668 */
1669 for (path = addr; *path && *path != ':'; path++);
1670 if (*path) {
1671 node = addr; /* Here's the NODE */
1672 *path++ = '\0'; /* Here's the PATH */
1673
1674 /* Node name must not be empty */
1675 if (!*node)
1676 return -1;
1677
1678 /* A name of "." is OK; otherwise '.' not allowed */
1679 if (strcmp(node, ".") != 0) {
1680 for (k = 0; node[k]; k++)
1681 if (node[k] == '.')
1682 return -1;
1683 }
1684 } else {
1685 node = NULL; /* No absolute NODE */
1686 path = addr; /* Here's the PATH */
1687 }
1688
1689 /* Snoop for illegal characters in PATH */
1690 for (k = 0; path[k]; k++)
1691 if (path[k] == ':')
1692 return -1;
1693
1694 /* Check for no repeated dots in PATH */
1695 for (k = 0; path[k]; k++)
1696 if (path[k] == '.' && path[k + 1] == '.')
1697 return -1;
1698
1699 /* Remove extra (degenerate) dots from beginning or end of PATH */
1700 if (path[0] == '.')
1701 path++;
1702 if (*path && path[strlen(path) - 1] == '.')
1703 path[strlen(path) - 1] = 0;
1704
1705 /* If PATH has a dot, then we're not talking about a hook */
1706 if (*path) {
1707 for (hook = path, k = 0; path[k]; k++)
1708 if (path[k] == '.') {
1709 hook = NULL;
1710 break;
1711 }
1712 } else
1713 path = hook = NULL;
1714
1715 /* Done */
1716 if (nodep)
1717 *nodep = node;
1718 if (pathp)
1719 *pathp = path;
1720 if (hookp)
1721 *hookp = hook;
1722 return (0);
1723 }
1724
1725 /*
1726 * Given a path, which may be absolute or relative, and a starting node,
1727 * return the destination node.
1728 */
1729 int
ng_path2noderef(node_p here,const char * address,node_p * destp,hook_p * lasthook)1730 ng_path2noderef(node_p here, const char *address, node_p *destp,
1731 hook_p *lasthook)
1732 {
1733 char fullpath[NG_PATHSIZ];
1734 char *nodename, *path;
1735 node_p node, oldnode;
1736
1737 /* Initialize */
1738 if (destp == NULL) {
1739 TRAP_ERROR();
1740 return EINVAL;
1741 }
1742 *destp = NULL;
1743
1744 /* Make a writable copy of address for ng_path_parse() */
1745 strncpy(fullpath, address, sizeof(fullpath) - 1);
1746 fullpath[sizeof(fullpath) - 1] = '\0';
1747
1748 /* Parse out node and sequence of hooks */
1749 if (ng_path_parse(fullpath, &nodename, &path, NULL) < 0) {
1750 TRAP_ERROR();
1751 return EINVAL;
1752 }
1753
1754 /*
1755 * For an absolute address, jump to the starting node.
1756 * Note that this holds a reference on the node for us.
1757 * Don't forget to drop the reference if we don't need it.
1758 */
1759 if (nodename) {
1760 node = ng_name2noderef(here, nodename);
1761 if (node == NULL) {
1762 TRAP_ERROR();
1763 return (ENOENT);
1764 }
1765 } else {
1766 if (here == NULL) {
1767 TRAP_ERROR();
1768 return (EINVAL);
1769 }
1770 node = here;
1771 NG_NODE_REF(node);
1772 }
1773
1774 if (path == NULL) {
1775 if (lasthook != NULL)
1776 *lasthook = NULL;
1777 *destp = node;
1778 return (0);
1779 }
1780
1781 /*
1782 * Now follow the sequence of hooks
1783 *
1784 * XXXGL: The path may demolish as we go the sequence, but if
1785 * we hold the topology mutex at critical places, then, I hope,
1786 * we would always have valid pointers in hand, although the
1787 * path behind us may no longer exist.
1788 */
1789 for (;;) {
1790 hook_p hook;
1791 char *segment;
1792
1793 /*
1794 * Break out the next path segment. Replace the dot we just
1795 * found with a NUL; "path" points to the next segment (or the
1796 * NUL at the end).
1797 */
1798 for (segment = path; *path != '\0'; path++) {
1799 if (*path == '.') {
1800 *path++ = '\0';
1801 break;
1802 }
1803 }
1804
1805 /* We have a segment, so look for a hook by that name */
1806 hook = ng_findhook(node, segment);
1807
1808 TOPOLOGY_WLOCK();
1809 /* Can't get there from here... */
1810 if (hook == NULL || NG_HOOK_PEER(hook) == NULL ||
1811 NG_HOOK_NOT_VALID(hook) ||
1812 NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook))) {
1813 TRAP_ERROR();
1814 NG_NODE_UNREF(node);
1815 TOPOLOGY_WUNLOCK();
1816 return (ENOENT);
1817 }
1818
1819 /*
1820 * Hop on over to the next node
1821 * XXX
1822 * Big race conditions here as hooks and nodes go away
1823 * *** Idea.. store an ng_ID_t in each hook and use that
1824 * instead of the direct hook in this crawl?
1825 */
1826 oldnode = node;
1827 if ((node = NG_PEER_NODE(hook)))
1828 NG_NODE_REF(node); /* XXX RACE */
1829 NG_NODE_UNREF(oldnode); /* XXX another race */
1830 if (NG_NODE_NOT_VALID(node)) {
1831 NG_NODE_UNREF(node); /* XXX more races */
1832 TOPOLOGY_WUNLOCK();
1833 TRAP_ERROR();
1834 return (ENXIO);
1835 }
1836
1837 if (*path == '\0') {
1838 if (lasthook != NULL) {
1839 if (hook != NULL) {
1840 *lasthook = NG_HOOK_PEER(hook);
1841 NG_HOOK_REF(*lasthook);
1842 } else
1843 *lasthook = NULL;
1844 }
1845 TOPOLOGY_WUNLOCK();
1846 *destp = node;
1847 return (0);
1848 }
1849 TOPOLOGY_WUNLOCK();
1850 }
1851 }
1852
1853 #ifndef FSTACK
1854 /***************************************************************\
1855 * Input queue handling.
1856 * All activities are submitted to the node via the input queue
1857 * which implements a multiple-reader/single-writer gate.
1858 * Items which cannot be handled immediately are queued.
1859 *
1860 * read-write queue locking inline functions *
1861 \***************************************************************/
1862
1863 static __inline void ng_queue_rw(node_p node, item_p item, int rw);
1864 static __inline item_p ng_dequeue(node_p node, int *rw);
1865 static __inline item_p ng_acquire_read(node_p node, item_p item);
1866 static __inline item_p ng_acquire_write(node_p node, item_p item);
1867 static __inline void ng_leave_read(node_p node);
1868 static __inline void ng_leave_write(node_p node);
1869
1870 /*
1871 * Definition of the bits fields in the ng_queue flag word.
1872 * Defined here rather than in netgraph.h because no-one should fiddle
1873 * with them.
1874 *
1875 * The ordering here may be important! don't shuffle these.
1876 */
1877 /*-
1878 Safety Barrier--------+ (adjustable to suit taste) (not used yet)
1879 |
1880 V
1881 +-------+-------+-------+-------+-------+-------+-------+-------+
1882 | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
1883 | |A|c|t|i|v|e| |R|e|a|d|e|r| |C|o|u|n|t| | | | | | | | | |P|A|
1884 | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |O|W|
1885 +-------+-------+-------+-------+-------+-------+-------+-------+
1886 \___________________________ ____________________________/ | |
1887 V | |
1888 [active reader count] | |
1889 | |
1890 Operation Pending -------------------------------+ |
1891 |
1892 Active Writer ---------------------------------------+
1893
1894 Node queue has such semantics:
1895 - All flags modifications are atomic.
1896 - Reader count can be incremented only if there is no writer or pending flags.
1897 As soon as this can't be done with single operation, it is implemented with
1898 spin loop and atomic_cmpset().
1899 - Writer flag can be set only if there is no any bits set.
1900 It is implemented with atomic_cmpset().
1901 - Pending flag can be set any time, but to avoid collision on queue processing
1902 all queue fields are protected by the mutex.
1903 - Queue processing thread reads queue holding the mutex, but releases it while
1904 processing. When queue is empty pending flag is removed.
1905 */
1906
1907 #define WRITER_ACTIVE 0x00000001
1908 #define OP_PENDING 0x00000002
1909 #define READER_INCREMENT 0x00000004
1910 #define READER_MASK 0xfffffffc /* Not valid if WRITER_ACTIVE is set */
1911 #define SAFETY_BARRIER 0x00100000 /* 128K items queued should be enough */
1912
1913 /* Defines of more elaborate states on the queue */
1914 /* Mask of bits a new read cares about */
1915 #define NGQ_RMASK (WRITER_ACTIVE|OP_PENDING)
1916
1917 /* Mask of bits a new write cares about */
1918 #define NGQ_WMASK (NGQ_RMASK|READER_MASK)
1919
1920 /* Test to decide if there is something on the queue. */
1921 #define QUEUE_ACTIVE(QP) ((QP)->q_flags & OP_PENDING)
1922
1923 /* How to decide what the next queued item is. */
1924 #define HEAD_IS_READER(QP) NGI_QUEUED_READER(STAILQ_FIRST(&(QP)->queue))
1925 #define HEAD_IS_WRITER(QP) NGI_QUEUED_WRITER(STAILQ_FIRST(&(QP)->queue)) /* notused */
1926
1927 /* Read the status to decide if the next item on the queue can now run. */
1928 #define QUEUED_READER_CAN_PROCEED(QP) \
1929 (((QP)->q_flags & (NGQ_RMASK & ~OP_PENDING)) == 0)
1930 #define QUEUED_WRITER_CAN_PROCEED(QP) \
1931 (((QP)->q_flags & (NGQ_WMASK & ~OP_PENDING)) == 0)
1932
1933 /* Is there a chance of getting ANY work off the queue? */
1934 #define NEXT_QUEUED_ITEM_CAN_PROCEED(QP) \
1935 ((HEAD_IS_READER(QP)) ? QUEUED_READER_CAN_PROCEED(QP) : \
1936 QUEUED_WRITER_CAN_PROCEED(QP))
1937
1938 #define NGQRW_R 0
1939 #define NGQRW_W 1
1940
1941 #define NGQ2_WORKQ 0x00000001
1942
1943 /*
1944 * Taking into account the current state of the queue and node, possibly take
1945 * the next entry off the queue and return it. Return NULL if there was
1946 * nothing we could return, either because there really was nothing there, or
1947 * because the node was in a state where it cannot yet process the next item
1948 * on the queue.
1949 */
1950 static __inline item_p
ng_dequeue(node_p node,int * rw)1951 ng_dequeue(node_p node, int *rw)
1952 {
1953 item_p item;
1954 struct ng_queue *ngq = &node->nd_input_queue;
1955
1956 /* This MUST be called with the mutex held. */
1957 mtx_assert(&ngq->q_mtx, MA_OWNED);
1958
1959 /* If there is nothing queued, then just return. */
1960 if (!QUEUE_ACTIVE(ngq)) {
1961 CTR4(KTR_NET, "%20s: node [%x] (%p) queue empty; "
1962 "queue flags 0x%lx", __func__,
1963 node->nd_ID, node, ngq->q_flags);
1964 return (NULL);
1965 }
1966
1967 /*
1968 * From here, we can assume there is a head item.
1969 * We need to find out what it is and if it can be dequeued, given
1970 * the current state of the node.
1971 */
1972 if (HEAD_IS_READER(ngq)) {
1973 while (1) {
1974 long t = ngq->q_flags;
1975 if (t & WRITER_ACTIVE) {
1976 /* There is writer, reader can't proceed. */
1977 CTR4(KTR_NET, "%20s: node [%x] (%p) queued "
1978 "reader can't proceed; queue flags 0x%lx",
1979 __func__, node->nd_ID, node, t);
1980 return (NULL);
1981 }
1982 if (atomic_cmpset_acq_int(&ngq->q_flags, t,
1983 t + READER_INCREMENT))
1984 break;
1985 cpu_spinwait();
1986 }
1987 /* We have got reader lock for the node. */
1988 *rw = NGQRW_R;
1989 } else if (atomic_cmpset_acq_int(&ngq->q_flags, OP_PENDING,
1990 OP_PENDING + WRITER_ACTIVE)) {
1991 /* We have got writer lock for the node. */
1992 *rw = NGQRW_W;
1993 } else {
1994 /* There is somebody other, writer can't proceed. */
1995 CTR4(KTR_NET, "%20s: node [%x] (%p) queued writer can't "
1996 "proceed; queue flags 0x%lx", __func__, node->nd_ID, node,
1997 ngq->q_flags);
1998 return (NULL);
1999 }
2000
2001 /*
2002 * Now we dequeue the request (whatever it may be) and correct the
2003 * pending flags and the next and last pointers.
2004 */
2005 item = STAILQ_FIRST(&ngq->queue);
2006 STAILQ_REMOVE_HEAD(&ngq->queue, el_next);
2007 if (STAILQ_EMPTY(&ngq->queue))
2008 atomic_clear_int(&ngq->q_flags, OP_PENDING);
2009 CTR6(KTR_NET, "%20s: node [%x] (%p) returning item %p as %s; queue "
2010 "flags 0x%lx", __func__, node->nd_ID, node, item, *rw ? "WRITER" :
2011 "READER", ngq->q_flags);
2012 return (item);
2013 }
2014
2015 /*
2016 * Queue a packet to be picked up later by someone else.
2017 * If the queue could be run now, add node to the queue handler's worklist.
2018 */
2019 static __inline void
ng_queue_rw(node_p node,item_p item,int rw)2020 ng_queue_rw(node_p node, item_p item, int rw)
2021 {
2022 struct ng_queue *ngq = &node->nd_input_queue;
2023 if (rw == NGQRW_W)
2024 NGI_SET_WRITER(item);
2025 else
2026 NGI_SET_READER(item);
2027 item->depth = 1;
2028
2029 NG_QUEUE_LOCK(ngq);
2030 /* Set OP_PENDING flag and enqueue the item. */
2031 atomic_set_int(&ngq->q_flags, OP_PENDING);
2032 STAILQ_INSERT_TAIL(&ngq->queue, item, el_next);
2033
2034 CTR5(KTR_NET, "%20s: node [%x] (%p) queued item %p as %s", __func__,
2035 node->nd_ID, node, item, rw ? "WRITER" : "READER" );
2036
2037 /*
2038 * We can take the worklist lock with the node locked
2039 * BUT NOT THE REVERSE!
2040 */
2041 if (NEXT_QUEUED_ITEM_CAN_PROCEED(ngq))
2042 ng_worklist_add(node);
2043 NG_QUEUE_UNLOCK(ngq);
2044 }
2045
2046 /* Acquire reader lock on node. If node is busy, queue the packet. */
2047 static __inline item_p
ng_acquire_read(node_p node,item_p item)2048 ng_acquire_read(node_p node, item_p item)
2049 {
2050 KASSERT(node != &ng_deadnode,
2051 ("%s: working on deadnode", __func__));
2052
2053 /* Reader needs node without writer and pending items. */
2054 for (;;) {
2055 long t = node->nd_input_queue.q_flags;
2056 if (t & NGQ_RMASK)
2057 break; /* Node is not ready for reader. */
2058 if (atomic_cmpset_acq_int(&node->nd_input_queue.q_flags, t,
2059 t + READER_INCREMENT)) {
2060 /* Successfully grabbed node */
2061 CTR4(KTR_NET, "%20s: node [%x] (%p) acquired item %p",
2062 __func__, node->nd_ID, node, item);
2063 return (item);
2064 }
2065 cpu_spinwait();
2066 }
2067
2068 /* Queue the request for later. */
2069 ng_queue_rw(node, item, NGQRW_R);
2070
2071 return (NULL);
2072 }
2073
2074 /* Acquire writer lock on node. If node is busy, queue the packet. */
2075 static __inline item_p
ng_acquire_write(node_p node,item_p item)2076 ng_acquire_write(node_p node, item_p item)
2077 {
2078 KASSERT(node != &ng_deadnode,
2079 ("%s: working on deadnode", __func__));
2080
2081 /* Writer needs completely idle node. */
2082 if (atomic_cmpset_acq_int(&node->nd_input_queue.q_flags, 0,
2083 WRITER_ACTIVE)) {
2084 /* Successfully grabbed node */
2085 CTR4(KTR_NET, "%20s: node [%x] (%p) acquired item %p",
2086 __func__, node->nd_ID, node, item);
2087 return (item);
2088 }
2089
2090 /* Queue the request for later. */
2091 ng_queue_rw(node, item, NGQRW_W);
2092
2093 return (NULL);
2094 }
2095
2096 #if 0
2097 static __inline item_p
2098 ng_upgrade_write(node_p node, item_p item)
2099 {
2100 struct ng_queue *ngq = &node->nd_input_queue;
2101 KASSERT(node != &ng_deadnode,
2102 ("%s: working on deadnode", __func__));
2103
2104 NGI_SET_WRITER(item);
2105
2106 NG_QUEUE_LOCK(ngq);
2107
2108 /*
2109 * There will never be no readers as we are there ourselves.
2110 * Set the WRITER_ACTIVE flags ASAP to block out fast track readers.
2111 * The caller we are running from will call ng_leave_read()
2112 * soon, so we must account for that. We must leave again with the
2113 * READER lock. If we find other readers, then
2114 * queue the request for later. However "later" may be rignt now
2115 * if there are no readers. We don't really care if there are queued
2116 * items as we will bypass them anyhow.
2117 */
2118 atomic_add_int(&ngq->q_flags, WRITER_ACTIVE - READER_INCREMENT);
2119 if ((ngq->q_flags & (NGQ_WMASK & ~OP_PENDING)) == WRITER_ACTIVE) {
2120 NG_QUEUE_UNLOCK(ngq);
2121
2122 /* It's just us, act on the item. */
2123 /* will NOT drop writer lock when done */
2124 ng_apply_item(node, item, 0);
2125
2126 /*
2127 * Having acted on the item, atomically
2128 * downgrade back to READER and finish up.
2129 */
2130 atomic_add_int(&ngq->q_flags, READER_INCREMENT - WRITER_ACTIVE);
2131
2132 /* Our caller will call ng_leave_read() */
2133 return;
2134 }
2135 /*
2136 * It's not just us active, so queue us AT THE HEAD.
2137 * "Why?" I hear you ask.
2138 * Put us at the head of the queue as we've already been
2139 * through it once. If there is nothing else waiting,
2140 * set the correct flags.
2141 */
2142 if (STAILQ_EMPTY(&ngq->queue)) {
2143 /* We've gone from, 0 to 1 item in the queue */
2144 atomic_set_int(&ngq->q_flags, OP_PENDING);
2145
2146 CTR3(KTR_NET, "%20s: node [%x] (%p) set OP_PENDING", __func__,
2147 node->nd_ID, node);
2148 };
2149 STAILQ_INSERT_HEAD(&ngq->queue, item, el_next);
2150 CTR4(KTR_NET, "%20s: node [%x] (%p) requeued item %p as WRITER",
2151 __func__, node->nd_ID, node, item );
2152
2153 /* Reverse what we did above. That downgrades us back to reader */
2154 atomic_add_int(&ngq->q_flags, READER_INCREMENT - WRITER_ACTIVE);
2155 if (QUEUE_ACTIVE(ngq) && NEXT_QUEUED_ITEM_CAN_PROCEED(ngq))
2156 ng_worklist_add(node);
2157 NG_QUEUE_UNLOCK(ngq);
2158
2159 return;
2160 }
2161 #endif
2162
2163 /* Release reader lock. */
2164 static __inline void
ng_leave_read(node_p node)2165 ng_leave_read(node_p node)
2166 {
2167 atomic_subtract_rel_int(&node->nd_input_queue.q_flags, READER_INCREMENT);
2168 }
2169
2170 /* Release writer lock. */
2171 static __inline void
ng_leave_write(node_p node)2172 ng_leave_write(node_p node)
2173 {
2174 atomic_clear_rel_int(&node->nd_input_queue.q_flags, WRITER_ACTIVE);
2175 }
2176
2177 /* Purge node queue. Called on node shutdown. */
2178 static void
ng_flush_input_queue(node_p node)2179 ng_flush_input_queue(node_p node)
2180 {
2181 struct ng_queue *ngq = &node->nd_input_queue;
2182 item_p item;
2183
2184 NG_QUEUE_LOCK(ngq);
2185 while ((item = STAILQ_FIRST(&ngq->queue)) != NULL) {
2186 STAILQ_REMOVE_HEAD(&ngq->queue, el_next);
2187 if (STAILQ_EMPTY(&ngq->queue))
2188 atomic_clear_int(&ngq->q_flags, OP_PENDING);
2189 NG_QUEUE_UNLOCK(ngq);
2190
2191 /* If the item is supplying a callback, call it with an error */
2192 if (item->apply != NULL) {
2193 if (item->depth == 1)
2194 item->apply->error = ENOENT;
2195 if (refcount_release(&item->apply->refs)) {
2196 (*item->apply->apply)(item->apply->context,
2197 item->apply->error);
2198 }
2199 }
2200 NG_FREE_ITEM(item);
2201 NG_QUEUE_LOCK(ngq);
2202 }
2203 NG_QUEUE_UNLOCK(ngq);
2204 }
2205 #endif
2206
2207 /***********************************************************************
2208 * Externally visible method for sending or queueing messages or data.
2209 ***********************************************************************/
2210
2211 /*
2212 * The module code should have filled out the item correctly by this stage:
2213 * Common:
2214 * reference to destination node.
2215 * Reference to destination rcv hook if relevant.
2216 * apply pointer must be or NULL or reference valid struct ng_apply_info.
2217 * Data:
2218 * pointer to mbuf
2219 * Control_Message:
2220 * pointer to msg.
2221 * ID of original sender node. (return address)
2222 * Function:
2223 * Function pointer
2224 * void * argument
2225 * integer argument
2226 *
2227 * The nodes have several routines and macros to help with this task:
2228 */
2229
2230 int
ng_snd_item(item_p item,int flags)2231 ng_snd_item(item_p item, int flags)
2232 {
2233 hook_p hook;
2234 node_p node;
2235 #ifndef FSTACK
2236 int queue, rw;
2237 struct ng_queue *ngq;
2238 #endif
2239 int error = 0;
2240
2241 /* We are sending item, so it must be present! */
2242 KASSERT(item != NULL, ("ng_snd_item: item is NULL"));
2243
2244 #ifdef NETGRAPH_DEBUG
2245 _ngi_check(item, __FILE__, __LINE__);
2246 #endif
2247
2248 /* Item was sent once more, postpone apply() call. */
2249 if (item->apply)
2250 refcount_acquire(&item->apply->refs);
2251
2252 node = NGI_NODE(item);
2253 /* Node is never optional. */
2254 KASSERT(node != NULL, ("ng_snd_item: node is NULL"));
2255
2256 hook = NGI_HOOK(item);
2257 /* Valid hook and mbuf are mandatory for data. */
2258 if ((item->el_flags & NGQF_TYPE) == NGQF_DATA) {
2259 KASSERT(hook != NULL, ("ng_snd_item: hook for data is NULL"));
2260 if (NGI_M(item) == NULL)
2261 ERROUT(EINVAL);
2262 CHECK_DATA_MBUF(NGI_M(item));
2263 }
2264
2265 #ifndef FSTACK
2266 /*
2267 * If the item or the node specifies single threading, force
2268 * writer semantics. Similarly, the node may say one hook always
2269 * produces writers. These are overrides.
2270 */
2271 if (((item->el_flags & NGQF_RW) == NGQF_WRITER) ||
2272 (node->nd_flags & NGF_FORCE_WRITER) ||
2273 (hook && (hook->hk_flags & HK_FORCE_WRITER))) {
2274 rw = NGQRW_W;
2275 } else {
2276 rw = NGQRW_R;
2277 }
2278
2279 /*
2280 * If sender or receiver requests queued delivery, or call graph
2281 * loops back from outbound to inbound path, or stack usage
2282 * level is dangerous - enqueue message.
2283 */
2284 if ((flags & NG_QUEUE) || (hook && (hook->hk_flags & HK_QUEUE))) {
2285 queue = 1;
2286 } else if (hook && (hook->hk_flags & HK_TO_INBOUND) &&
2287 curthread->td_ng_outbound) {
2288 queue = 1;
2289 } else {
2290 queue = 0;
2291 #ifdef GET_STACK_USAGE
2292 /*
2293 * Most of netgraph nodes have small stack consumption and
2294 * for them 25% of free stack space is more than enough.
2295 * Nodes/hooks with higher stack usage should be marked as
2296 * HI_STACK. For them 50% of stack will be guaranteed then.
2297 * XXX: Values 25% and 50% are completely empirical.
2298 */
2299 size_t st, su, sl;
2300 GET_STACK_USAGE(st, su);
2301 sl = st - su;
2302 if ((sl * 4 < st) || ((sl * 2 < st) &&
2303 ((node->nd_flags & NGF_HI_STACK) || (hook &&
2304 (hook->hk_flags & HK_HI_STACK)))))
2305 queue = 1;
2306 #endif
2307 }
2308
2309 if (queue) {
2310 /* Put it on the queue for that node*/
2311 ng_queue_rw(node, item, rw);
2312 return ((flags & NG_PROGRESS) ? EINPROGRESS : 0);
2313 }
2314
2315 /*
2316 * We already decided how we will be queueud or treated.
2317 * Try get the appropriate operating permission.
2318 */
2319 if (rw == NGQRW_R)
2320 item = ng_acquire_read(node, item);
2321 else
2322 item = ng_acquire_write(node, item);
2323
2324 /* Item was queued while trying to get permission. */
2325 if (item == NULL)
2326 return ((flags & NG_PROGRESS) ? EINPROGRESS : 0);
2327 #endif
2328
2329 NGI_GET_NODE(item, node); /* zaps stored node */
2330
2331 item->depth++;
2332 #ifndef FSTACK
2333 error = ng_apply_item(node, item, rw); /* drops r/w lock when done */
2334 #else
2335 error = ng_apply_item(node, item, 0);
2336 #endif
2337
2338 #ifndef FSTACK
2339 /* If something is waiting on queue and ready, schedule it. */
2340 ngq = &node->nd_input_queue;
2341 if (QUEUE_ACTIVE(ngq)) {
2342 NG_QUEUE_LOCK(ngq);
2343 if (QUEUE_ACTIVE(ngq) && NEXT_QUEUED_ITEM_CAN_PROCEED(ngq))
2344 ng_worklist_add(node);
2345 NG_QUEUE_UNLOCK(ngq);
2346 }
2347 #endif
2348
2349 /*
2350 * Node may go away as soon as we remove the reference.
2351 * Whatever we do, DO NOT access the node again!
2352 */
2353 NG_NODE_UNREF(node);
2354
2355 return (error);
2356
2357 done:
2358 /* If was not sent, apply callback here. */
2359 if (item->apply != NULL) {
2360 if (item->depth == 0 && error != 0)
2361 item->apply->error = error;
2362 if (refcount_release(&item->apply->refs)) {
2363 (*item->apply->apply)(item->apply->context,
2364 item->apply->error);
2365 }
2366 }
2367
2368 NG_FREE_ITEM(item);
2369 return (error);
2370 }
2371
2372 /*
2373 * We have an item that was possibly queued somewhere.
2374 * It should contain all the information needed
2375 * to run it on the appropriate node/hook.
2376 * If there is apply pointer and we own the last reference, call apply().
2377 */
2378 static int
ng_apply_item(node_p node,item_p item,int rw)2379 ng_apply_item(node_p node, item_p item, int rw)
2380 {
2381 hook_p hook;
2382 ng_rcvdata_t *rcvdata;
2383 ng_rcvmsg_t *rcvmsg;
2384 struct ng_apply_info *apply;
2385 int error = 0, depth;
2386
2387 /* Node and item are never optional. */
2388 KASSERT(node != NULL, ("ng_apply_item: node is NULL"));
2389 KASSERT(item != NULL, ("ng_apply_item: item is NULL"));
2390
2391 NGI_GET_HOOK(item, hook); /* clears stored hook */
2392 #ifdef NETGRAPH_DEBUG
2393 _ngi_check(item, __FILE__, __LINE__);
2394 #endif
2395
2396 apply = item->apply;
2397 depth = item->depth;
2398
2399 switch (item->el_flags & NGQF_TYPE) {
2400 case NGQF_DATA:
2401 /*
2402 * Check things are still ok as when we were queued.
2403 */
2404 KASSERT(hook != NULL, ("ng_apply_item: hook for data is NULL"));
2405 if (NG_HOOK_NOT_VALID(hook) ||
2406 NG_NODE_NOT_VALID(node)) {
2407 error = EIO;
2408 NG_FREE_ITEM(item);
2409 break;
2410 }
2411 /*
2412 * If no receive method, just silently drop it.
2413 * Give preference to the hook over-ride method.
2414 */
2415 if ((!(rcvdata = hook->hk_rcvdata)) &&
2416 (!(rcvdata = NG_HOOK_NODE(hook)->nd_type->rcvdata))) {
2417 error = 0;
2418 NG_FREE_ITEM(item);
2419 break;
2420 }
2421 error = (*rcvdata)(hook, item);
2422 break;
2423 case NGQF_MESG:
2424 if (hook && NG_HOOK_NOT_VALID(hook)) {
2425 /*
2426 * The hook has been zapped then we can't use it.
2427 * Immediately drop its reference.
2428 * The message may not need it.
2429 */
2430 NG_HOOK_UNREF(hook);
2431 hook = NULL;
2432 }
2433 /*
2434 * Similarly, if the node is a zombie there is
2435 * nothing we can do with it, drop everything.
2436 */
2437 if (NG_NODE_NOT_VALID(node)) {
2438 TRAP_ERROR();
2439 error = EINVAL;
2440 NG_FREE_ITEM(item);
2441 break;
2442 }
2443 /*
2444 * Call the appropriate message handler for the object.
2445 * It is up to the message handler to free the message.
2446 * If it's a generic message, handle it generically,
2447 * otherwise call the type's message handler (if it exists).
2448 * XXX (race). Remember that a queued message may
2449 * reference a node or hook that has just been
2450 * invalidated. It will exist as the queue code
2451 * is holding a reference, but..
2452 */
2453 if ((NGI_MSG(item)->header.typecookie == NGM_GENERIC_COOKIE) &&
2454 ((NGI_MSG(item)->header.flags & NGF_RESP) == 0)) {
2455 error = ng_generic_msg(node, item, hook);
2456 break;
2457 }
2458 if (((!hook) || (!(rcvmsg = hook->hk_rcvmsg))) &&
2459 (!(rcvmsg = node->nd_type->rcvmsg))) {
2460 TRAP_ERROR();
2461 error = 0;
2462 NG_FREE_ITEM(item);
2463 break;
2464 }
2465 error = (*rcvmsg)(node, item, hook);
2466 break;
2467 case NGQF_FN:
2468 case NGQF_FN2:
2469 /*
2470 * In the case of the shutdown message we allow it to hit
2471 * even if the node is invalid.
2472 */
2473 if (NG_NODE_NOT_VALID(node) &&
2474 NGI_FN(item) != &ng_rmnode) {
2475 TRAP_ERROR();
2476 error = EINVAL;
2477 NG_FREE_ITEM(item);
2478 break;
2479 }
2480 /* Same is about some internal functions and invalid hook. */
2481 if (hook && NG_HOOK_NOT_VALID(hook) &&
2482 NGI_FN2(item) != &ng_con_part2 &&
2483 NGI_FN2(item) != &ng_con_part3 &&
2484 NGI_FN(item) != &ng_rmhook_part2) {
2485 TRAP_ERROR();
2486 error = EINVAL;
2487 NG_FREE_ITEM(item);
2488 break;
2489 }
2490
2491 if ((item->el_flags & NGQF_TYPE) == NGQF_FN) {
2492 (*NGI_FN(item))(node, hook, NGI_ARG1(item),
2493 NGI_ARG2(item));
2494 NG_FREE_ITEM(item);
2495 } else /* it is NGQF_FN2 */
2496 error = (*NGI_FN2(item))(node, item, hook);
2497 break;
2498 }
2499 /*
2500 * We held references on some of the resources
2501 * that we took from the item. Now that we have
2502 * finished doing everything, drop those references.
2503 */
2504 if (hook)
2505 NG_HOOK_UNREF(hook);
2506
2507 #ifndef FSTACK
2508 if (rw == NGQRW_R)
2509 ng_leave_read(node);
2510 else
2511 ng_leave_write(node);
2512 #endif
2513
2514 /* Apply callback. */
2515 if (apply != NULL) {
2516 if (depth == 1 && error != 0)
2517 apply->error = error;
2518 if (refcount_release(&apply->refs))
2519 (*apply->apply)(apply->context, apply->error);
2520 }
2521
2522 return (error);
2523 }
2524
2525 /***********************************************************************
2526 * Implement the 'generic' control messages
2527 ***********************************************************************/
2528 static int
ng_generic_msg(node_p here,item_p item,hook_p lasthook)2529 ng_generic_msg(node_p here, item_p item, hook_p lasthook)
2530 {
2531 int error = 0;
2532 struct ng_mesg *msg;
2533 struct ng_mesg *resp = NULL;
2534
2535 NGI_GET_MSG(item, msg);
2536 if (msg->header.typecookie != NGM_GENERIC_COOKIE) {
2537 TRAP_ERROR();
2538 error = EINVAL;
2539 goto out;
2540 }
2541 switch (msg->header.cmd) {
2542 case NGM_SHUTDOWN:
2543 ng_rmnode(here, NULL, NULL, 0);
2544 break;
2545 case NGM_MKPEER:
2546 {
2547 struct ngm_mkpeer *const mkp = (struct ngm_mkpeer *) msg->data;
2548
2549 if (msg->header.arglen != sizeof(*mkp)) {
2550 TRAP_ERROR();
2551 error = EINVAL;
2552 break;
2553 }
2554 mkp->type[sizeof(mkp->type) - 1] = '\0';
2555 mkp->ourhook[sizeof(mkp->ourhook) - 1] = '\0';
2556 mkp->peerhook[sizeof(mkp->peerhook) - 1] = '\0';
2557 error = ng_mkpeer(here, mkp->ourhook, mkp->peerhook, mkp->type);
2558 break;
2559 }
2560 case NGM_CONNECT:
2561 {
2562 struct ngm_connect *const con =
2563 (struct ngm_connect *) msg->data;
2564 node_p node2;
2565
2566 if (msg->header.arglen != sizeof(*con)) {
2567 TRAP_ERROR();
2568 error = EINVAL;
2569 break;
2570 }
2571 con->path[sizeof(con->path) - 1] = '\0';
2572 con->ourhook[sizeof(con->ourhook) - 1] = '\0';
2573 con->peerhook[sizeof(con->peerhook) - 1] = '\0';
2574 /* Don't forget we get a reference.. */
2575 error = ng_path2noderef(here, con->path, &node2, NULL);
2576 if (error)
2577 break;
2578 error = ng_con_nodes(item, here, con->ourhook,
2579 node2, con->peerhook);
2580 NG_NODE_UNREF(node2);
2581 break;
2582 }
2583 case NGM_NAME:
2584 {
2585 struct ngm_name *const nam = (struct ngm_name *) msg->data;
2586
2587 if (msg->header.arglen != sizeof(*nam)) {
2588 TRAP_ERROR();
2589 error = EINVAL;
2590 break;
2591 }
2592 nam->name[sizeof(nam->name) - 1] = '\0';
2593 error = ng_name_node(here, nam->name);
2594 break;
2595 }
2596 case NGM_RMHOOK:
2597 {
2598 struct ngm_rmhook *const rmh = (struct ngm_rmhook *) msg->data;
2599 hook_p hook;
2600
2601 if (msg->header.arglen != sizeof(*rmh)) {
2602 TRAP_ERROR();
2603 error = EINVAL;
2604 break;
2605 }
2606 rmh->ourhook[sizeof(rmh->ourhook) - 1] = '\0';
2607 if ((hook = ng_findhook(here, rmh->ourhook)) != NULL)
2608 ng_destroy_hook(hook);
2609 break;
2610 }
2611 case NGM_NODEINFO:
2612 {
2613 struct nodeinfo *ni;
2614
2615 NG_MKRESPONSE(resp, msg, sizeof(*ni), M_NOWAIT);
2616 if (resp == NULL) {
2617 error = ENOMEM;
2618 break;
2619 }
2620
2621 /* Fill in node info */
2622 ni = (struct nodeinfo *) resp->data;
2623 if (NG_NODE_HAS_NAME(here))
2624 strcpy(ni->name, NG_NODE_NAME(here));
2625 strcpy(ni->type, here->nd_type->name);
2626 ni->id = ng_node2ID(here);
2627 ni->hooks = here->nd_numhooks;
2628 break;
2629 }
2630 case NGM_LISTHOOKS:
2631 {
2632 const int nhooks = here->nd_numhooks;
2633 struct hooklist *hl;
2634 struct nodeinfo *ni;
2635 hook_p hook;
2636
2637 /* Get response struct */
2638 NG_MKRESPONSE(resp, msg, sizeof(*hl) +
2639 (nhooks * sizeof(struct linkinfo)), M_NOWAIT);
2640 if (resp == NULL) {
2641 error = ENOMEM;
2642 break;
2643 }
2644 hl = (struct hooklist *) resp->data;
2645 ni = &hl->nodeinfo;
2646
2647 /* Fill in node info */
2648 if (NG_NODE_HAS_NAME(here))
2649 strcpy(ni->name, NG_NODE_NAME(here));
2650 strcpy(ni->type, here->nd_type->name);
2651 ni->id = ng_node2ID(here);
2652
2653 /* Cycle through the linked list of hooks */
2654 ni->hooks = 0;
2655 LIST_FOREACH(hook, &here->nd_hooks, hk_hooks) {
2656 struct linkinfo *const link = &hl->link[ni->hooks];
2657
2658 if (ni->hooks >= nhooks) {
2659 log(LOG_ERR, "%s: number of %s changed\n",
2660 __func__, "hooks");
2661 break;
2662 }
2663 if (NG_HOOK_NOT_VALID(hook))
2664 continue;
2665 strcpy(link->ourhook, NG_HOOK_NAME(hook));
2666 strcpy(link->peerhook, NG_PEER_HOOK_NAME(hook));
2667 if (NG_PEER_NODE_NAME(hook)[0] != '\0')
2668 strcpy(link->nodeinfo.name,
2669 NG_PEER_NODE_NAME(hook));
2670 strcpy(link->nodeinfo.type,
2671 NG_PEER_NODE(hook)->nd_type->name);
2672 link->nodeinfo.id = ng_node2ID(NG_PEER_NODE(hook));
2673 link->nodeinfo.hooks = NG_PEER_NODE(hook)->nd_numhooks;
2674 ni->hooks++;
2675 }
2676 break;
2677 }
2678
2679 case NGM_LISTNODES:
2680 {
2681 struct namelist *nl;
2682 node_p node;
2683 int i;
2684
2685 IDHASH_RLOCK();
2686 /* Get response struct. */
2687 NG_MKRESPONSE(resp, msg, sizeof(*nl) +
2688 (V_ng_nodes * sizeof(struct nodeinfo)), M_NOWAIT);
2689 if (resp == NULL) {
2690 IDHASH_RUNLOCK();
2691 error = ENOMEM;
2692 break;
2693 }
2694 nl = (struct namelist *) resp->data;
2695
2696 /* Cycle through the lists of nodes. */
2697 nl->numnames = 0;
2698 for (i = 0; i <= V_ng_ID_hmask; i++) {
2699 LIST_FOREACH(node, &V_ng_ID_hash[i], nd_idnodes) {
2700 struct nodeinfo *const np =
2701 &nl->nodeinfo[nl->numnames];
2702
2703 if (NG_NODE_NOT_VALID(node))
2704 continue;
2705 if (NG_NODE_HAS_NAME(node))
2706 strcpy(np->name, NG_NODE_NAME(node));
2707 strcpy(np->type, node->nd_type->name);
2708 np->id = ng_node2ID(node);
2709 np->hooks = node->nd_numhooks;
2710 KASSERT(nl->numnames < V_ng_nodes,
2711 ("%s: no space", __func__));
2712 nl->numnames++;
2713 }
2714 }
2715 IDHASH_RUNLOCK();
2716 break;
2717 }
2718 case NGM_LISTNAMES:
2719 {
2720 struct namelist *nl;
2721 node_p node;
2722 int i;
2723
2724 NAMEHASH_RLOCK();
2725 /* Get response struct. */
2726 NG_MKRESPONSE(resp, msg, sizeof(*nl) +
2727 (V_ng_named_nodes * sizeof(struct nodeinfo)), M_NOWAIT);
2728 if (resp == NULL) {
2729 NAMEHASH_RUNLOCK();
2730 error = ENOMEM;
2731 break;
2732 }
2733 nl = (struct namelist *) resp->data;
2734
2735 /* Cycle through the lists of nodes. */
2736 nl->numnames = 0;
2737 for (i = 0; i <= V_ng_name_hmask; i++) {
2738 LIST_FOREACH(node, &V_ng_name_hash[i], nd_nodes) {
2739 struct nodeinfo *const np =
2740 &nl->nodeinfo[nl->numnames];
2741
2742 if (NG_NODE_NOT_VALID(node))
2743 continue;
2744 strcpy(np->name, NG_NODE_NAME(node));
2745 strcpy(np->type, node->nd_type->name);
2746 np->id = ng_node2ID(node);
2747 np->hooks = node->nd_numhooks;
2748 KASSERT(nl->numnames < V_ng_named_nodes,
2749 ("%s: no space", __func__));
2750 nl->numnames++;
2751 }
2752 }
2753 NAMEHASH_RUNLOCK();
2754 break;
2755 }
2756
2757 case NGM_LISTTYPES:
2758 {
2759 struct typelist *tl;
2760 struct ng_type *type;
2761 int num = 0;
2762
2763 TYPELIST_RLOCK();
2764 /* Count number of types */
2765 LIST_FOREACH(type, &ng_typelist, types)
2766 num++;
2767
2768 /* Get response struct */
2769 NG_MKRESPONSE(resp, msg, sizeof(*tl) +
2770 (num * sizeof(struct typeinfo)), M_NOWAIT);
2771 if (resp == NULL) {
2772 TYPELIST_RUNLOCK();
2773 error = ENOMEM;
2774 break;
2775 }
2776 tl = (struct typelist *) resp->data;
2777
2778 /* Cycle through the linked list of types */
2779 tl->numtypes = 0;
2780 LIST_FOREACH(type, &ng_typelist, types) {
2781 struct typeinfo *const tp = &tl->typeinfo[tl->numtypes];
2782
2783 strcpy(tp->type_name, type->name);
2784 tp->numnodes = type->refs - 1; /* don't count list */
2785 KASSERT(tl->numtypes < num, ("%s: no space", __func__));
2786 tl->numtypes++;
2787 }
2788 TYPELIST_RUNLOCK();
2789 break;
2790 }
2791
2792 case NGM_BINARY2ASCII:
2793 {
2794 int bufSize = 20 * 1024; /* XXX hard coded constant */
2795 const struct ng_parse_type *argstype;
2796 const struct ng_cmdlist *c;
2797 struct ng_mesg *binary, *ascii;
2798
2799 /* Data area must contain a valid netgraph message */
2800 binary = (struct ng_mesg *)msg->data;
2801 if (msg->header.arglen < sizeof(struct ng_mesg) ||
2802 (msg->header.arglen - sizeof(struct ng_mesg) <
2803 binary->header.arglen)) {
2804 TRAP_ERROR();
2805 error = EINVAL;
2806 break;
2807 }
2808
2809 /* Get a response message with lots of room */
2810 NG_MKRESPONSE(resp, msg, sizeof(*ascii) + bufSize, M_NOWAIT);
2811 if (resp == NULL) {
2812 error = ENOMEM;
2813 break;
2814 }
2815 ascii = (struct ng_mesg *)resp->data;
2816
2817 /* Copy binary message header to response message payload */
2818 bcopy(binary, ascii, sizeof(*binary));
2819
2820 /* Find command by matching typecookie and command number */
2821 for (c = here->nd_type->cmdlist; c != NULL && c->name != NULL;
2822 c++) {
2823 if (binary->header.typecookie == c->cookie &&
2824 binary->header.cmd == c->cmd)
2825 break;
2826 }
2827 if (c == NULL || c->name == NULL) {
2828 for (c = ng_generic_cmds; c->name != NULL; c++) {
2829 if (binary->header.typecookie == c->cookie &&
2830 binary->header.cmd == c->cmd)
2831 break;
2832 }
2833 if (c->name == NULL) {
2834 NG_FREE_MSG(resp);
2835 error = ENOSYS;
2836 break;
2837 }
2838 }
2839
2840 /* Convert command name to ASCII */
2841 snprintf(ascii->header.cmdstr, sizeof(ascii->header.cmdstr),
2842 "%s", c->name);
2843
2844 /* Convert command arguments to ASCII */
2845 argstype = (binary->header.flags & NGF_RESP) ?
2846 c->respType : c->mesgType;
2847 if (argstype == NULL) {
2848 *ascii->data = '\0';
2849 } else {
2850 if ((error = ng_unparse(argstype,
2851 (u_char *)binary->data,
2852 ascii->data, bufSize)) != 0) {
2853 NG_FREE_MSG(resp);
2854 break;
2855 }
2856 }
2857
2858 /* Return the result as struct ng_mesg plus ASCII string */
2859 bufSize = strlen(ascii->data) + 1;
2860 ascii->header.arglen = bufSize;
2861 resp->header.arglen = sizeof(*ascii) + bufSize;
2862 break;
2863 }
2864
2865 case NGM_ASCII2BINARY:
2866 {
2867 int bufSize = 20 * 1024; /* XXX hard coded constant */
2868 const struct ng_cmdlist *c;
2869 const struct ng_parse_type *argstype;
2870 struct ng_mesg *ascii, *binary;
2871 int off = 0;
2872
2873 /* Data area must contain at least a struct ng_mesg + '\0' */
2874 ascii = (struct ng_mesg *)msg->data;
2875 if ((msg->header.arglen < sizeof(*ascii) + 1) ||
2876 (ascii->header.arglen < 1) ||
2877 (msg->header.arglen < sizeof(*ascii) +
2878 ascii->header.arglen)) {
2879 TRAP_ERROR();
2880 error = EINVAL;
2881 break;
2882 }
2883 ascii->data[ascii->header.arglen - 1] = '\0';
2884
2885 /* Get a response message with lots of room */
2886 NG_MKRESPONSE(resp, msg, sizeof(*binary) + bufSize, M_NOWAIT);
2887 if (resp == NULL) {
2888 error = ENOMEM;
2889 break;
2890 }
2891 binary = (struct ng_mesg *)resp->data;
2892
2893 /* Copy ASCII message header to response message payload */
2894 bcopy(ascii, binary, sizeof(*ascii));
2895
2896 /* Find command by matching ASCII command string */
2897 for (c = here->nd_type->cmdlist;
2898 c != NULL && c->name != NULL; c++) {
2899 if (strcmp(ascii->header.cmdstr, c->name) == 0)
2900 break;
2901 }
2902 if (c == NULL || c->name == NULL) {
2903 for (c = ng_generic_cmds; c->name != NULL; c++) {
2904 if (strcmp(ascii->header.cmdstr, c->name) == 0)
2905 break;
2906 }
2907 if (c->name == NULL) {
2908 NG_FREE_MSG(resp);
2909 error = ENOSYS;
2910 break;
2911 }
2912 }
2913
2914 /* Convert command name to binary */
2915 binary->header.cmd = c->cmd;
2916 binary->header.typecookie = c->cookie;
2917
2918 /* Convert command arguments to binary */
2919 argstype = (binary->header.flags & NGF_RESP) ?
2920 c->respType : c->mesgType;
2921 if (argstype == NULL) {
2922 bufSize = 0;
2923 } else {
2924 if ((error = ng_parse(argstype, ascii->data, &off,
2925 (u_char *)binary->data, &bufSize)) != 0) {
2926 NG_FREE_MSG(resp);
2927 break;
2928 }
2929 }
2930
2931 /* Return the result */
2932 binary->header.arglen = bufSize;
2933 resp->header.arglen = sizeof(*binary) + bufSize;
2934 break;
2935 }
2936
2937 case NGM_TEXT_CONFIG:
2938 case NGM_TEXT_STATUS:
2939 /*
2940 * This one is tricky as it passes the command down to the
2941 * actual node, even though it is a generic type command.
2942 * This means we must assume that the item/msg is already freed
2943 * when control passes back to us.
2944 */
2945 if (here->nd_type->rcvmsg != NULL) {
2946 NGI_MSG(item) = msg; /* put it back as we found it */
2947 return((*here->nd_type->rcvmsg)(here, item, lasthook));
2948 }
2949 /* Fall through if rcvmsg not supported */
2950 default:
2951 TRAP_ERROR();
2952 error = EINVAL;
2953 }
2954 /*
2955 * Sometimes a generic message may be statically allocated
2956 * to avoid problems with allocating when in tight memory situations.
2957 * Don't free it if it is so.
2958 * I break them apart here, because erros may cause a free if the item
2959 * in which case we'd be doing it twice.
2960 * they are kept together above, to simplify freeing.
2961 */
2962 out:
2963 NG_RESPOND_MSG(error, here, item, resp);
2964 NG_FREE_MSG(msg);
2965 return (error);
2966 }
2967
2968 /************************************************************************
2969 Queue element get/free routines
2970 ************************************************************************/
2971
2972 uma_zone_t ng_qzone;
2973 uma_zone_t ng_qdzone;
2974 #ifndef FSTACK
2975 static int numthreads = 0; /* number of queue threads */
2976 #endif
2977 static int maxalloc = 4096;/* limit the damage of a leak */
2978 static int maxdata = 4096; /* limit the damage of a DoS */
2979
2980 #ifndef FSTACK
2981 SYSCTL_INT(_net_graph, OID_AUTO, threads, CTLFLAG_RDTUN, &numthreads,
2982 0, "Number of queue processing threads");
2983 #endif
2984 SYSCTL_INT(_net_graph, OID_AUTO, maxalloc, CTLFLAG_RDTUN, &maxalloc,
2985 0, "Maximum number of non-data queue items to allocate");
2986 SYSCTL_INT(_net_graph, OID_AUTO, maxdata, CTLFLAG_RDTUN, &maxdata,
2987 0, "Maximum number of data queue items to allocate");
2988
2989 #ifdef NETGRAPH_DEBUG
2990 static TAILQ_HEAD(, ng_item) ng_itemlist = TAILQ_HEAD_INITIALIZER(ng_itemlist);
2991 static int allocated; /* number of items malloc'd */
2992 #endif
2993
2994 /*
2995 * Get a queue entry.
2996 * This is usually called when a packet first enters netgraph.
2997 * By definition, this is usually from an interrupt, or from a user.
2998 * Users are not so important, but try be quick for the times that it's
2999 * an interrupt.
3000 */
3001 static __inline item_p
ng_alloc_item(int type,int flags)3002 ng_alloc_item(int type, int flags)
3003 {
3004 item_p item;
3005
3006 KASSERT(((type & ~NGQF_TYPE) == 0),
3007 ("%s: incorrect item type: %d", __func__, type));
3008
3009 item = uma_zalloc((type == NGQF_DATA) ? ng_qdzone : ng_qzone,
3010 ((flags & NG_WAITOK) ? M_WAITOK : M_NOWAIT) | M_ZERO);
3011
3012 if (item) {
3013 item->el_flags = type;
3014 #ifdef NETGRAPH_DEBUG
3015 mtx_lock(&ngq_mtx);
3016 TAILQ_INSERT_TAIL(&ng_itemlist, item, all);
3017 allocated++;
3018 mtx_unlock(&ngq_mtx);
3019 #endif
3020 }
3021
3022 return (item);
3023 }
3024
3025 /*
3026 * Release a queue entry
3027 */
3028 void
ng_free_item(item_p item)3029 ng_free_item(item_p item)
3030 {
3031 /*
3032 * The item may hold resources on its own. We need to free
3033 * these before we can free the item. What they are depends upon
3034 * what kind of item it is. it is important that nodes zero
3035 * out pointers to resources that they remove from the item
3036 * or we release them again here.
3037 */
3038 switch (item->el_flags & NGQF_TYPE) {
3039 case NGQF_DATA:
3040 /* If we have an mbuf still attached.. */
3041 NG_FREE_M(_NGI_M(item));
3042 break;
3043 case NGQF_MESG:
3044 _NGI_RETADDR(item) = 0;
3045 NG_FREE_MSG(_NGI_MSG(item));
3046 break;
3047 case NGQF_FN:
3048 case NGQF_FN2:
3049 /* nothing to free really, */
3050 _NGI_FN(item) = NULL;
3051 _NGI_ARG1(item) = NULL;
3052 _NGI_ARG2(item) = 0;
3053 break;
3054 }
3055 /* If we still have a node or hook referenced... */
3056 _NGI_CLR_NODE(item);
3057 _NGI_CLR_HOOK(item);
3058
3059 #ifdef NETGRAPH_DEBUG
3060 mtx_lock(&ngq_mtx);
3061 TAILQ_REMOVE(&ng_itemlist, item, all);
3062 allocated--;
3063 mtx_unlock(&ngq_mtx);
3064 #endif
3065 uma_zfree(((item->el_flags & NGQF_TYPE) == NGQF_DATA) ?
3066 ng_qdzone : ng_qzone, item);
3067 }
3068
3069 /*
3070 * Change type of the queue entry.
3071 * Possibly reallocates it from another UMA zone.
3072 */
3073 static __inline item_p
ng_realloc_item(item_p pitem,int type,int flags)3074 ng_realloc_item(item_p pitem, int type, int flags)
3075 {
3076 item_p item;
3077 int from, to;
3078
3079 KASSERT((pitem != NULL), ("%s: can't reallocate NULL", __func__));
3080 KASSERT(((type & ~NGQF_TYPE) == 0),
3081 ("%s: incorrect item type: %d", __func__, type));
3082
3083 from = ((pitem->el_flags & NGQF_TYPE) == NGQF_DATA);
3084 to = (type == NGQF_DATA);
3085 if (from != to) {
3086 /* If reallocation is required do it and copy item. */
3087 if ((item = ng_alloc_item(type, flags)) == NULL) {
3088 ng_free_item(pitem);
3089 return (NULL);
3090 }
3091 *item = *pitem;
3092 ng_free_item(pitem);
3093 } else
3094 item = pitem;
3095 item->el_flags = (item->el_flags & ~NGQF_TYPE) | type;
3096
3097 return (item);
3098 }
3099
3100 /************************************************************************
3101 Module routines
3102 ************************************************************************/
3103
3104 /*
3105 * Handle the loading/unloading of a netgraph node type module
3106 */
3107 int
ng_mod_event(module_t mod,int event,void * data)3108 ng_mod_event(module_t mod, int event, void *data)
3109 {
3110 struct ng_type *const type = data;
3111 int error = 0;
3112
3113 switch (event) {
3114 case MOD_LOAD:
3115
3116 /* Register new netgraph node type */
3117 if ((error = ng_newtype(type)) != 0)
3118 break;
3119
3120 /* Call type specific code */
3121 if (type->mod_event != NULL)
3122 if ((error = (*type->mod_event)(mod, event, data))) {
3123 TYPELIST_WLOCK();
3124 type->refs--; /* undo it */
3125 LIST_REMOVE(type, types);
3126 TYPELIST_WUNLOCK();
3127 }
3128 break;
3129
3130 case MOD_UNLOAD:
3131 if (type->refs > 1) { /* make sure no nodes exist! */
3132 error = EBUSY;
3133 } else {
3134 if (type->refs == 0) /* failed load, nothing to undo */
3135 break;
3136 if (type->mod_event != NULL) { /* check with type */
3137 error = (*type->mod_event)(mod, event, data);
3138 if (error != 0) /* type refuses.. */
3139 break;
3140 }
3141 TYPELIST_WLOCK();
3142 LIST_REMOVE(type, types);
3143 TYPELIST_WUNLOCK();
3144 }
3145 break;
3146
3147 default:
3148 if (type->mod_event != NULL)
3149 error = (*type->mod_event)(mod, event, data);
3150 else
3151 error = EOPNOTSUPP; /* XXX ? */
3152 break;
3153 }
3154 return (error);
3155 }
3156
3157 static void
vnet_netgraph_init(const void * unused __unused)3158 vnet_netgraph_init(const void *unused __unused)
3159 {
3160
3161 /* We start with small hashes, but they can grow. */
3162 V_ng_ID_hash = hashinit(16, M_NETGRAPH_NODE, &V_ng_ID_hmask);
3163 V_ng_name_hash = hashinit(16, M_NETGRAPH_NODE, &V_ng_name_hmask);
3164 }
3165 VNET_SYSINIT(vnet_netgraph_init, SI_SUB_NETGRAPH, SI_ORDER_FIRST,
3166 vnet_netgraph_init, NULL);
3167
3168 #ifdef VIMAGE
3169 static void
vnet_netgraph_uninit(const void * unused __unused)3170 vnet_netgraph_uninit(const void *unused __unused)
3171 {
3172 node_p node = NULL, last_killed = NULL;
3173 int i;
3174
3175 do {
3176 /* Find a node to kill */
3177 IDHASH_RLOCK();
3178 for (i = 0; i <= V_ng_ID_hmask; i++) {
3179 LIST_FOREACH(node, &V_ng_ID_hash[i], nd_idnodes) {
3180 if (node != &ng_deadnode) {
3181 NG_NODE_REF(node);
3182 break;
3183 }
3184 }
3185 if (node != NULL)
3186 break;
3187 }
3188 IDHASH_RUNLOCK();
3189
3190 /* Attempt to kill it only if it is a regular node */
3191 if (node != NULL) {
3192 if (node == last_killed) {
3193 if (node->nd_flags & NGF_REALLY_DIE)
3194 panic("ng node %s won't die",
3195 node->nd_name);
3196 /* The node persisted itself. Try again. */
3197 node->nd_flags |= NGF_REALLY_DIE;
3198 }
3199 ng_rmnode(node, NULL, NULL, 0);
3200 NG_NODE_UNREF(node);
3201 last_killed = node;
3202 }
3203 } while (node != NULL);
3204
3205 hashdestroy(V_ng_name_hash, M_NETGRAPH_NODE, V_ng_name_hmask);
3206 hashdestroy(V_ng_ID_hash, M_NETGRAPH_NODE, V_ng_ID_hmask);
3207 }
3208 VNET_SYSUNINIT(vnet_netgraph_uninit, SI_SUB_NETGRAPH, SI_ORDER_FIRST,
3209 vnet_netgraph_uninit, NULL);
3210 #endif /* VIMAGE */
3211
3212 /*
3213 * Handle loading and unloading for this code.
3214 * The only thing we need to link into is the NETISR strucure.
3215 */
3216 static int
ngb_mod_event(module_t mod,int event,void * data)3217 ngb_mod_event(module_t mod, int event, void *data)
3218 {
3219 #ifndef FSTACK
3220 struct proc *p;
3221 struct thread *td;
3222 #endif
3223 int i, error = 0;
3224
3225 switch (event) {
3226 case MOD_LOAD:
3227 /* Initialize everything. */
3228 NG_WORKLIST_LOCK_INIT();
3229 rw_init(&ng_typelist_lock, "netgraph types");
3230 rw_init(&ng_idhash_lock, "netgraph idhash");
3231 rw_init(&ng_namehash_lock, "netgraph namehash");
3232 rw_init(&ng_topo_lock, "netgraph topology mutex");
3233 #ifdef NETGRAPH_DEBUG
3234 mtx_init(&ng_nodelist_mtx, "netgraph nodelist mutex", NULL,
3235 MTX_DEF);
3236 mtx_init(&ngq_mtx, "netgraph item list mutex", NULL,
3237 MTX_DEF);
3238 #endif
3239 ng_qzone = uma_zcreate("NetGraph items", sizeof(struct ng_item),
3240 NULL, NULL, NULL, NULL, UMA_ALIGN_CACHE, 0);
3241 uma_zone_set_max(ng_qzone, maxalloc);
3242 ng_qdzone = uma_zcreate("NetGraph data items",
3243 sizeof(struct ng_item), NULL, NULL, NULL, NULL,
3244 UMA_ALIGN_CACHE, 0);
3245 uma_zone_set_max(ng_qdzone, maxdata);
3246 #ifndef FSTACK
3247 /* Autoconfigure number of threads. */
3248 if (numthreads <= 0)
3249 numthreads = mp_ncpus;
3250 /* Create threads. */
3251 p = NULL; /* start with no process */
3252 for (i = 0; i < numthreads; i++) {
3253 if (kproc_kthread_add(ngthread, NULL, &p, &td,
3254 RFHIGHPID, 0, "ng_queue", "ng_queue%d", i)) {
3255 numthreads = i;
3256 break;
3257 }
3258 }
3259 #endif
3260 break;
3261 case MOD_UNLOAD:
3262 /* You can't unload it because an interface may be using it. */
3263 error = EBUSY;
3264 break;
3265 default:
3266 error = EOPNOTSUPP;
3267 break;
3268 }
3269 return (error);
3270 }
3271
3272 static moduledata_t netgraph_mod = {
3273 "netgraph",
3274 ngb_mod_event,
3275 (NULL)
3276 };
3277 DECLARE_MODULE(netgraph, netgraph_mod, SI_SUB_NETGRAPH, SI_ORDER_FIRST);
3278 SYSCTL_NODE(_net, OID_AUTO, graph, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
3279 "netgraph Family");
3280 SYSCTL_INT(_net_graph, OID_AUTO, abi_version, CTLFLAG_RD, SYSCTL_NULL_INT_PTR, NG_ABI_VERSION,"");
3281 SYSCTL_INT(_net_graph, OID_AUTO, msg_version, CTLFLAG_RD, SYSCTL_NULL_INT_PTR, NG_VERSION, "");
3282
3283 #ifdef NETGRAPH_DEBUG
3284 void
dumphook(hook_p hook,char * file,int line)3285 dumphook (hook_p hook, char *file, int line)
3286 {
3287 printf("hook: name %s, %d refs, Last touched:\n",
3288 _NG_HOOK_NAME(hook), hook->hk_refs);
3289 printf(" Last active @ %s, line %d\n",
3290 hook->lastfile, hook->lastline);
3291 if (line) {
3292 printf(" problem discovered at file %s, line %d\n", file, line);
3293 #ifdef KDB
3294 kdb_backtrace();
3295 #endif
3296 }
3297 }
3298
3299 void
dumpnode(node_p node,char * file,int line)3300 dumpnode(node_p node, char *file, int line)
3301 {
3302 printf("node: ID [%x]: type '%s', %d hooks, flags 0x%x, %d refs, %s:\n",
3303 _NG_NODE_ID(node), node->nd_type->name,
3304 node->nd_numhooks, node->nd_flags,
3305 node->nd_refs, node->nd_name);
3306 printf(" Last active @ %s, line %d\n",
3307 node->lastfile, node->lastline);
3308 if (line) {
3309 printf(" problem discovered at file %s, line %d\n", file, line);
3310 #ifdef KDB
3311 kdb_backtrace();
3312 #endif
3313 }
3314 }
3315
3316 void
dumpitem(item_p item,char * file,int line)3317 dumpitem(item_p item, char *file, int line)
3318 {
3319 printf(" ACTIVE item, last used at %s, line %d",
3320 item->lastfile, item->lastline);
3321 switch(item->el_flags & NGQF_TYPE) {
3322 case NGQF_DATA:
3323 printf(" - [data]\n");
3324 break;
3325 case NGQF_MESG:
3326 printf(" - retaddr[%d]:\n", _NGI_RETADDR(item));
3327 break;
3328 case NGQF_FN:
3329 printf(" - fn@%p (%p, %p, %p, %d (%x))\n",
3330 _NGI_FN(item),
3331 _NGI_NODE(item),
3332 _NGI_HOOK(item),
3333 item->body.fn.fn_arg1,
3334 item->body.fn.fn_arg2,
3335 item->body.fn.fn_arg2);
3336 break;
3337 case NGQF_FN2:
3338 printf(" - fn2@%p (%p, %p, %p, %d (%x))\n",
3339 _NGI_FN2(item),
3340 _NGI_NODE(item),
3341 _NGI_HOOK(item),
3342 item->body.fn.fn_arg1,
3343 item->body.fn.fn_arg2,
3344 item->body.fn.fn_arg2);
3345 break;
3346 }
3347 if (line) {
3348 printf(" problem discovered at file %s, line %d\n", file, line);
3349 if (_NGI_NODE(item)) {
3350 printf("node %p ([%x])\n",
3351 _NGI_NODE(item), ng_node2ID(_NGI_NODE(item)));
3352 }
3353 }
3354 }
3355
3356 static void
ng_dumpitems(void)3357 ng_dumpitems(void)
3358 {
3359 item_p item;
3360 int i = 1;
3361 TAILQ_FOREACH(item, &ng_itemlist, all) {
3362 printf("[%d] ", i++);
3363 dumpitem(item, NULL, 0);
3364 }
3365 }
3366
3367 static void
ng_dumpnodes(void)3368 ng_dumpnodes(void)
3369 {
3370 node_p node;
3371 int i = 1;
3372 mtx_lock(&ng_nodelist_mtx);
3373 SLIST_FOREACH(node, &ng_allnodes, nd_all) {
3374 printf("[%d] ", i++);
3375 dumpnode(node, NULL, 0);
3376 }
3377 mtx_unlock(&ng_nodelist_mtx);
3378 }
3379
3380 static void
ng_dumphooks(void)3381 ng_dumphooks(void)
3382 {
3383 hook_p hook;
3384 int i = 1;
3385 mtx_lock(&ng_nodelist_mtx);
3386 SLIST_FOREACH(hook, &ng_allhooks, hk_all) {
3387 printf("[%d] ", i++);
3388 dumphook(hook, NULL, 0);
3389 }
3390 mtx_unlock(&ng_nodelist_mtx);
3391 }
3392
3393 static int
sysctl_debug_ng_dump_items(SYSCTL_HANDLER_ARGS)3394 sysctl_debug_ng_dump_items(SYSCTL_HANDLER_ARGS)
3395 {
3396 int error;
3397 int val;
3398 int i;
3399
3400 val = allocated;
3401 i = 1;
3402 error = sysctl_handle_int(oidp, &val, 0, req);
3403 if (error != 0 || req->newptr == NULL)
3404 return (error);
3405 if (val == 42) {
3406 ng_dumpitems();
3407 ng_dumpnodes();
3408 ng_dumphooks();
3409 }
3410 return (0);
3411 }
3412
3413 SYSCTL_PROC(_debug, OID_AUTO, ng_dump_items,
3414 CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, 0, sizeof(int),
3415 sysctl_debug_ng_dump_items, "I",
3416 "Number of allocated items");
3417 #endif /* NETGRAPH_DEBUG */
3418
3419 #ifndef FSTACK
3420 /***********************************************************************
3421 * Worklist routines
3422 **********************************************************************/
3423 /*
3424 * Pick a node off the list of nodes with work,
3425 * try get an item to process off it. Remove the node from the list.
3426 */
3427 static void
ngthread(void * arg)3428 ngthread(void *arg)
3429 {
3430 for (;;) {
3431 struct epoch_tracker et;
3432 node_p node;
3433
3434 /* Get node from the worklist. */
3435 NG_WORKLIST_LOCK();
3436 while ((node = STAILQ_FIRST(&ng_worklist)) == NULL)
3437 NG_WORKLIST_SLEEP();
3438 STAILQ_REMOVE_HEAD(&ng_worklist, nd_input_queue.q_work);
3439 NG_WORKLIST_UNLOCK();
3440 CURVNET_SET(node->nd_vnet);
3441 CTR3(KTR_NET, "%20s: node [%x] (%p) taken off worklist",
3442 __func__, node->nd_ID, node);
3443 /*
3444 * We have the node. We also take over the reference
3445 * that the list had on it.
3446 * Now process as much as you can, until it won't
3447 * let you have another item off the queue.
3448 * All this time, keep the reference
3449 * that lets us be sure that the node still exists.
3450 * Let the reference go at the last minute.
3451 */
3452 NET_EPOCH_ENTER(et);
3453 for (;;) {
3454 item_p item;
3455 int rw;
3456
3457 NG_QUEUE_LOCK(&node->nd_input_queue);
3458 item = ng_dequeue(node, &rw);
3459 if (item == NULL) {
3460 node->nd_input_queue.q_flags2 &= ~NGQ2_WORKQ;
3461 NG_QUEUE_UNLOCK(&node->nd_input_queue);
3462 break; /* go look for another node */
3463 } else {
3464 NG_QUEUE_UNLOCK(&node->nd_input_queue);
3465 NGI_GET_NODE(item, node); /* zaps stored node */
3466 ng_apply_item(node, item, rw);
3467 NG_NODE_UNREF(node);
3468 }
3469 }
3470 NET_EPOCH_EXIT(et);
3471 NG_NODE_UNREF(node);
3472 CURVNET_RESTORE();
3473 }
3474 }
3475
3476 /*
3477 * XXX
3478 * It's posible that a debugging NG_NODE_REF may need
3479 * to be outside the mutex zone
3480 */
3481 static void
ng_worklist_add(node_p node)3482 ng_worklist_add(node_p node)
3483 {
3484
3485 mtx_assert(&node->nd_input_queue.q_mtx, MA_OWNED);
3486
3487 if ((node->nd_input_queue.q_flags2 & NGQ2_WORKQ) == 0) {
3488 /*
3489 * If we are not already on the work queue,
3490 * then put us on.
3491 */
3492 node->nd_input_queue.q_flags2 |= NGQ2_WORKQ;
3493 NG_NODE_REF(node); /* XXX safe in mutex? */
3494 NG_WORKLIST_LOCK();
3495 STAILQ_INSERT_TAIL(&ng_worklist, node, nd_input_queue.q_work);
3496 NG_WORKLIST_UNLOCK();
3497 CTR3(KTR_NET, "%20s: node [%x] (%p) put on worklist", __func__,
3498 node->nd_ID, node);
3499 NG_WORKLIST_WAKEUP();
3500 } else {
3501 CTR3(KTR_NET, "%20s: node [%x] (%p) already on worklist",
3502 __func__, node->nd_ID, node);
3503 }
3504 }
3505 #endif
3506
3507 /***********************************************************************
3508 * Externally useable functions to set up a queue item ready for sending
3509 ***********************************************************************/
3510
3511 #ifdef NETGRAPH_DEBUG
3512 #define ITEM_DEBUG_CHECKS \
3513 do { \
3514 if (NGI_NODE(item) ) { \
3515 printf("item already has node"); \
3516 kdb_enter(KDB_WHY_NETGRAPH, "has node"); \
3517 NGI_CLR_NODE(item); \
3518 } \
3519 if (NGI_HOOK(item) ) { \
3520 printf("item already has hook"); \
3521 kdb_enter(KDB_WHY_NETGRAPH, "has hook"); \
3522 NGI_CLR_HOOK(item); \
3523 } \
3524 } while (0)
3525 #else
3526 #define ITEM_DEBUG_CHECKS
3527 #endif
3528
3529 /*
3530 * Put mbuf into the item.
3531 * Hook and node references will be removed when the item is dequeued.
3532 * (or equivalent)
3533 * (XXX) Unsafe because no reference held by peer on remote node.
3534 * remote node might go away in this timescale.
3535 * We know the hooks can't go away because that would require getting
3536 * a writer item on both nodes and we must have at least a reader
3537 * here to be able to do this.
3538 * Note that the hook loaded is the REMOTE hook.
3539 *
3540 * This is possibly in the critical path for new data.
3541 */
3542 item_p
ng_package_data(struct mbuf * m,int flags)3543 ng_package_data(struct mbuf *m, int flags)
3544 {
3545 item_p item;
3546
3547 if ((item = ng_alloc_item(NGQF_DATA, flags)) == NULL) {
3548 NG_FREE_M(m);
3549 return (NULL);
3550 }
3551 ITEM_DEBUG_CHECKS;
3552 item->el_flags |= NGQF_READER;
3553 NGI_M(item) = m;
3554 return (item);
3555 }
3556
3557 /*
3558 * Allocate a queue item and put items into it..
3559 * Evaluate the address as this will be needed to queue it and
3560 * to work out what some of the fields should be.
3561 * Hook and node references will be removed when the item is dequeued.
3562 * (or equivalent)
3563 */
3564 item_p
ng_package_msg(struct ng_mesg * msg,int flags)3565 ng_package_msg(struct ng_mesg *msg, int flags)
3566 {
3567 item_p item;
3568
3569 if ((item = ng_alloc_item(NGQF_MESG, flags)) == NULL) {
3570 NG_FREE_MSG(msg);
3571 return (NULL);
3572 }
3573 ITEM_DEBUG_CHECKS;
3574 /* Messages items count as writers unless explicitly exempted. */
3575 if (msg->header.cmd & NGM_READONLY)
3576 item->el_flags |= NGQF_READER;
3577 else
3578 item->el_flags |= NGQF_WRITER;
3579 /*
3580 * Set the current lasthook into the queue item
3581 */
3582 NGI_MSG(item) = msg;
3583 NGI_RETADDR(item) = 0;
3584 return (item);
3585 }
3586
3587 #define SET_RETADDR(item, here, retaddr) \
3588 do { /* Data or fn items don't have retaddrs */ \
3589 if ((item->el_flags & NGQF_TYPE) == NGQF_MESG) { \
3590 if (retaddr) { \
3591 NGI_RETADDR(item) = retaddr; \
3592 } else { \
3593 /* \
3594 * The old return address should be ok. \
3595 * If there isn't one, use the address \
3596 * here. \
3597 */ \
3598 if (NGI_RETADDR(item) == 0) { \
3599 NGI_RETADDR(item) \
3600 = ng_node2ID(here); \
3601 } \
3602 } \
3603 } \
3604 } while (0)
3605
3606 int
ng_address_hook(node_p here,item_p item,hook_p hook,ng_ID_t retaddr)3607 ng_address_hook(node_p here, item_p item, hook_p hook, ng_ID_t retaddr)
3608 {
3609 hook_p peer;
3610 node_p peernode;
3611 ITEM_DEBUG_CHECKS;
3612 /*
3613 * Quick sanity check..
3614 * Since a hook holds a reference on its node, once we know
3615 * that the peer is still connected (even if invalid,) we know
3616 * that the peer node is present, though maybe invalid.
3617 */
3618 TOPOLOGY_RLOCK();
3619 if ((hook == NULL) || NG_HOOK_NOT_VALID(hook) ||
3620 NG_HOOK_NOT_VALID(peer = NG_HOOK_PEER(hook)) ||
3621 NG_NODE_NOT_VALID(peernode = NG_PEER_NODE(hook))) {
3622 NG_FREE_ITEM(item);
3623 TRAP_ERROR();
3624 TOPOLOGY_RUNLOCK();
3625 return (ENETDOWN);
3626 }
3627
3628 /*
3629 * Transfer our interest to the other (peer) end.
3630 */
3631 NG_HOOK_REF(peer);
3632 NG_NODE_REF(peernode);
3633 NGI_SET_HOOK(item, peer);
3634 NGI_SET_NODE(item, peernode);
3635 SET_RETADDR(item, here, retaddr);
3636
3637 TOPOLOGY_RUNLOCK();
3638
3639 return (0);
3640 }
3641
3642 int
ng_address_path(node_p here,item_p item,const char * address,ng_ID_t retaddr)3643 ng_address_path(node_p here, item_p item, const char *address, ng_ID_t retaddr)
3644 {
3645 node_p dest = NULL;
3646 hook_p hook = NULL;
3647 int error;
3648
3649 ITEM_DEBUG_CHECKS;
3650 /*
3651 * Note that ng_path2noderef increments the reference count
3652 * on the node for us if it finds one. So we don't have to.
3653 */
3654 error = ng_path2noderef(here, address, &dest, &hook);
3655 if (error) {
3656 NG_FREE_ITEM(item);
3657 return (error);
3658 }
3659 NGI_SET_NODE(item, dest);
3660 if (hook)
3661 NGI_SET_HOOK(item, hook);
3662
3663 SET_RETADDR(item, here, retaddr);
3664 return (0);
3665 }
3666
3667 int
ng_address_ID(node_p here,item_p item,ng_ID_t ID,ng_ID_t retaddr)3668 ng_address_ID(node_p here, item_p item, ng_ID_t ID, ng_ID_t retaddr)
3669 {
3670 node_p dest;
3671
3672 ITEM_DEBUG_CHECKS;
3673 /*
3674 * Find the target node.
3675 */
3676 dest = ng_ID2noderef(ID); /* GETS REFERENCE! */
3677 if (dest == NULL) {
3678 NG_FREE_ITEM(item);
3679 TRAP_ERROR();
3680 return(EINVAL);
3681 }
3682 /* Fill out the contents */
3683 NGI_SET_NODE(item, dest);
3684 NGI_CLR_HOOK(item);
3685 SET_RETADDR(item, here, retaddr);
3686 return (0);
3687 }
3688
3689 /*
3690 * special case to send a message to self (e.g. destroy node)
3691 * Possibly indicate an arrival hook too.
3692 * Useful for removing that hook :-)
3693 */
3694 item_p
ng_package_msg_self(node_p here,hook_p hook,struct ng_mesg * msg)3695 ng_package_msg_self(node_p here, hook_p hook, struct ng_mesg *msg)
3696 {
3697 item_p item;
3698
3699 /*
3700 * Find the target node.
3701 * If there is a HOOK argument, then use that in preference
3702 * to the address.
3703 */
3704 if ((item = ng_alloc_item(NGQF_MESG, NG_NOFLAGS)) == NULL) {
3705 NG_FREE_MSG(msg);
3706 return (NULL);
3707 }
3708
3709 /* Fill out the contents */
3710 item->el_flags |= NGQF_WRITER;
3711 NG_NODE_REF(here);
3712 NGI_SET_NODE(item, here);
3713 if (hook) {
3714 NG_HOOK_REF(hook);
3715 NGI_SET_HOOK(item, hook);
3716 }
3717 NGI_MSG(item) = msg;
3718 NGI_RETADDR(item) = ng_node2ID(here);
3719 return (item);
3720 }
3721
3722 /*
3723 * Send ng_item_fn function call to the specified node.
3724 */
3725
3726 int
ng_send_fn(node_p node,hook_p hook,ng_item_fn * fn,void * arg1,int arg2)3727 ng_send_fn(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2)
3728 {
3729
3730 return ng_send_fn1(node, hook, fn, arg1, arg2, NG_NOFLAGS);
3731 }
3732
3733 int
ng_send_fn1(node_p node,hook_p hook,ng_item_fn * fn,void * arg1,int arg2,int flags)3734 ng_send_fn1(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2,
3735 int flags)
3736 {
3737 item_p item;
3738
3739 if ((item = ng_alloc_item(NGQF_FN, flags)) == NULL) {
3740 return (ENOMEM);
3741 }
3742 item->el_flags |= NGQF_WRITER;
3743 NG_NODE_REF(node); /* and one for the item */
3744 NGI_SET_NODE(item, node);
3745 if (hook) {
3746 NG_HOOK_REF(hook);
3747 NGI_SET_HOOK(item, hook);
3748 }
3749 NGI_FN(item) = fn;
3750 NGI_ARG1(item) = arg1;
3751 NGI_ARG2(item) = arg2;
3752 return(ng_snd_item(item, flags));
3753 }
3754
3755 /*
3756 * Send ng_item_fn2 function call to the specified node.
3757 *
3758 * If an optional pitem parameter is supplied, its apply
3759 * callback will be copied to the new item. If also NG_REUSE_ITEM
3760 * flag is set, no new item will be allocated, but pitem will
3761 * be used.
3762 */
3763 int
ng_send_fn2(node_p node,hook_p hook,item_p pitem,ng_item_fn2 * fn,void * arg1,int arg2,int flags)3764 ng_send_fn2(node_p node, hook_p hook, item_p pitem, ng_item_fn2 *fn, void *arg1,
3765 int arg2, int flags)
3766 {
3767 item_p item;
3768
3769 KASSERT((pitem != NULL || (flags & NG_REUSE_ITEM) == 0),
3770 ("%s: NG_REUSE_ITEM but no pitem", __func__));
3771
3772 /*
3773 * Allocate a new item if no supplied or
3774 * if we can't use supplied one.
3775 */
3776 if (pitem == NULL || (flags & NG_REUSE_ITEM) == 0) {
3777 if ((item = ng_alloc_item(NGQF_FN2, flags)) == NULL)
3778 return (ENOMEM);
3779 if (pitem != NULL)
3780 item->apply = pitem->apply;
3781 } else {
3782 if ((item = ng_realloc_item(pitem, NGQF_FN2, flags)) == NULL)
3783 return (ENOMEM);
3784 }
3785
3786 item->el_flags = (item->el_flags & ~NGQF_RW) | NGQF_WRITER;
3787 NG_NODE_REF(node); /* and one for the item */
3788 NGI_SET_NODE(item, node);
3789 if (hook) {
3790 NG_HOOK_REF(hook);
3791 NGI_SET_HOOK(item, hook);
3792 }
3793 NGI_FN2(item) = fn;
3794 NGI_ARG1(item) = arg1;
3795 NGI_ARG2(item) = arg2;
3796 return(ng_snd_item(item, flags));
3797 }
3798
3799 /*
3800 * Official timeout routines for Netgraph nodes.
3801 */
3802 static void
ng_callout_trampoline(void * arg)3803 ng_callout_trampoline(void *arg)
3804 {
3805 struct epoch_tracker et;
3806 item_p item = arg;
3807
3808 NET_EPOCH_ENTER(et);
3809 CURVNET_SET(NGI_NODE(item)->nd_vnet);
3810 ng_snd_item(item, 0);
3811 CURVNET_RESTORE();
3812 NET_EPOCH_EXIT(et);
3813 }
3814
3815 int
ng_callout(struct callout * c,node_p node,hook_p hook,int ticks,ng_item_fn * fn,void * arg1,int arg2)3816 ng_callout(struct callout *c, node_p node, hook_p hook, int ticks,
3817 ng_item_fn *fn, void * arg1, int arg2)
3818 {
3819 item_p item, oitem;
3820
3821 if ((item = ng_alloc_item(NGQF_FN, NG_NOFLAGS)) == NULL)
3822 return (ENOMEM);
3823
3824 item->el_flags |= NGQF_WRITER;
3825 NG_NODE_REF(node); /* and one for the item */
3826 NGI_SET_NODE(item, node);
3827 if (hook) {
3828 NG_HOOK_REF(hook);
3829 NGI_SET_HOOK(item, hook);
3830 }
3831 NGI_FN(item) = fn;
3832 NGI_ARG1(item) = arg1;
3833 NGI_ARG2(item) = arg2;
3834 oitem = c->c_arg;
3835 if (callout_reset(c, ticks, &ng_callout_trampoline, item) == 1 &&
3836 oitem != NULL)
3837 NG_FREE_ITEM(oitem);
3838 return (0);
3839 }
3840
3841 /* A special modified version of callout_stop() */
3842 int
ng_uncallout(struct callout * c,node_p node)3843 ng_uncallout(struct callout *c, node_p node)
3844 {
3845 item_p item;
3846 int rval;
3847
3848 KASSERT(c != NULL, ("ng_uncallout: NULL callout"));
3849 KASSERT(node != NULL, ("ng_uncallout: NULL node"));
3850
3851 rval = callout_stop(c);
3852 item = c->c_arg;
3853 /* Do an extra check */
3854 if ((rval > 0) && (c->c_func == &ng_callout_trampoline) &&
3855 (item != NULL) && (NGI_NODE(item) == node)) {
3856 /*
3857 * We successfully removed it from the queue before it ran
3858 * So now we need to unreference everything that was
3859 * given extra references. (NG_FREE_ITEM does this).
3860 */
3861 NG_FREE_ITEM(item);
3862 }
3863 c->c_arg = NULL;
3864
3865 /*
3866 * Callers only want to know if the callout was cancelled and
3867 * not draining or stopped.
3868 */
3869 return (rval > 0);
3870 }
3871
3872 /*
3873 * Set the address, if none given, give the node here.
3874 */
3875 void
ng_replace_retaddr(node_p here,item_p item,ng_ID_t retaddr)3876 ng_replace_retaddr(node_p here, item_p item, ng_ID_t retaddr)
3877 {
3878 if (retaddr) {
3879 NGI_RETADDR(item) = retaddr;
3880 } else {
3881 /*
3882 * The old return address should be ok.
3883 * If there isn't one, use the address here.
3884 */
3885 NGI_RETADDR(item) = ng_node2ID(here);
3886 }
3887 }
3888