Commit | Line | Data |
---|---|---|
984263bc MD |
1 | |
2 | /* | |
3 | * ng_base.c | |
4 | * | |
5 | * Copyright (c) 1996-1999 Whistle Communications, Inc. | |
6 | * All rights reserved. | |
7 | * | |
8 | * Subject to the following obligations and disclaimer of warranty, use and | |
9 | * redistribution of this software, in source or object code forms, with or | |
10 | * without modifications are expressly permitted by Whistle Communications; | |
11 | * provided, however, that: | |
12 | * 1. Any and all reproductions of the source or object code must include the | |
13 | * copyright notice above and the following disclaimer of warranties; and | |
14 | * 2. No rights are granted, in any manner or form, to use Whistle | |
15 | * Communications, Inc. trademarks, including the mark "WHISTLE | |
16 | * COMMUNICATIONS" on advertising, endorsements, or otherwise except as | |
17 | * such appears in the above copyright notice or in the software. | |
18 | * | |
19 | * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND | |
20 | * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO | |
21 | * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, | |
22 | * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF | |
23 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. | |
24 | * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY | |
25 | * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS | |
26 | * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. | |
27 | * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES | |
28 | * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING | |
29 | * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, | |
30 | * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR | |
31 | * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY | |
32 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
33 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
34 | * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY | |
35 | * OF SUCH DAMAGE. | |
36 | * | |
37 | * Authors: Julian Elischer <julian@freebsd.org> | |
38 | * Archie Cobbs <archie@freebsd.org> | |
39 | * | |
40 | * $FreeBSD: src/sys/netgraph/ng_base.c,v 1.11.2.17 2002/07/02 23:44:02 archie Exp $ | |
41 | * $Whistle: ng_base.c,v 1.39 1999/01/28 23:54:53 julian Exp $ | |
42 | */ | |
43 | ||
44 | /* | |
45 | * This file implements the base netgraph code. | |
46 | */ | |
47 | ||
48 | #include <sys/param.h> | |
49 | #include <sys/systm.h> | |
50 | #include <sys/errno.h> | |
51 | #include <sys/kernel.h> | |
52 | #include <sys/malloc.h> | |
53 | #include <sys/syslog.h> | |
54 | #include <sys/linker.h> | |
55 | #include <sys/queue.h> | |
56 | #include <sys/mbuf.h> | |
57 | #include <sys/ctype.h> | |
58 | #include <sys/sysctl.h> | |
191cd68c | 59 | #include <sys/vnode.h> |
984263bc MD |
60 | #include <machine/limits.h> |
61 | ||
38c37c8c JH |
62 | #include <sys/thread2.h> |
63 | #include <sys/msgport2.h> | |
ba27fc0d | 64 | #include <sys/mplock2.h> |
38c37c8c | 65 | |
984263bc MD |
66 | #include <net/netisr.h> |
67 | ||
68 | #include <netgraph/ng_message.h> | |
69 | #include <netgraph/netgraph.h> | |
d2438d69 | 70 | #include <netgraph/ng_parse.h> |
984263bc | 71 | |
09c280ec SS |
72 | MODULE_VERSION(netgraph, NG_ABI_VERSION); |
73 | ||
002c1265 MD |
74 | union netmsg; |
75 | ||
984263bc MD |
76 | /* List of all nodes */ |
77 | static LIST_HEAD(, ng_node) nodelist; | |
78 | ||
79 | /* List of installed types */ | |
80 | static LIST_HEAD(, ng_type) typelist; | |
81 | ||
82 | /* Hash releted definitions */ | |
83 | #define ID_HASH_SIZE 32 /* most systems wont need even this many */ | |
84 | static LIST_HEAD(, ng_node) ID_hash[ID_HASH_SIZE]; | |
85 | /* Don't nead to initialise them because it's a LIST */ | |
86 | ||
87 | /* Internal functions */ | |
88 | static int ng_add_hook(node_p node, const char *name, hook_p * hookp); | |
89 | static int ng_connect(hook_p hook1, hook_p hook2); | |
90 | static void ng_disconnect_hook(hook_p hook); | |
91 | static int ng_generic_msg(node_p here, struct ng_mesg *msg, | |
92 | const char *retaddr, struct ng_mesg ** resp); | |
93 | static ng_ID_t ng_decodeidname(const char *name); | |
94 | static int ngb_mod_event(module_t mod, int event, void *data); | |
002c1265 | 95 | static void ngintr(union netmsg *); |
d8fef73e | 96 | static int ng_load_module(const char *); |
984263bc MD |
97 | |
98 | /* Our own netgraph malloc type */ | |
99 | MALLOC_DEFINE(M_NETGRAPH, "netgraph", "netgraph structures and ctrl messages"); | |
100 | ||
101 | /* Set this to Debugger("X") to catch all errors as they occur */ | |
102 | #ifndef TRAP_ERROR | |
103 | #define TRAP_ERROR | |
104 | #endif | |
105 | ||
106 | static ng_ID_t nextID = 1; | |
107 | ||
108 | #ifdef INVARIANTS | |
109 | #define CHECK_DATA_MBUF(m) do { \ | |
110 | struct mbuf *n; \ | |
111 | int total; \ | |
112 | \ | |
113 | if (((m)->m_flags & M_PKTHDR) == 0) \ | |
5e2195bf | 114 | panic("%s: !PKTHDR", __func__); \ |
984263bc MD |
115 | for (total = 0, n = (m); n != NULL; n = n->m_next) \ |
116 | total += n->m_len; \ | |
117 | if ((m)->m_pkthdr.len != total) { \ | |
118 | panic("%s: %d != %d", \ | |
5e2195bf | 119 | __func__, (m)->m_pkthdr.len, total); \ |
984263bc MD |
120 | } \ |
121 | } while (0) | |
122 | #else | |
123 | #define CHECK_DATA_MBUF(m) | |
124 | #endif | |
125 | ||
126 | ||
127 | /************************************************************************ | |
128 | Parse type definitions for generic messages | |
129 | ************************************************************************/ | |
130 | ||
131 | /* Handy structure parse type defining macro */ | |
132 | #define DEFINE_PARSE_STRUCT_TYPE(lo, up, args) \ | |
133 | static const struct ng_parse_struct_field \ | |
134 | ng_ ## lo ## _type_fields[] = NG_GENERIC_ ## up ## _INFO args; \ | |
135 | static const struct ng_parse_type ng_generic_ ## lo ## _type = { \ | |
136 | &ng_parse_struct_type, \ | |
137 | &ng_ ## lo ## _type_fields \ | |
138 | } | |
139 | ||
140 | DEFINE_PARSE_STRUCT_TYPE(mkpeer, MKPEER, ()); | |
141 | DEFINE_PARSE_STRUCT_TYPE(connect, CONNECT, ()); | |
142 | DEFINE_PARSE_STRUCT_TYPE(name, NAME, ()); | |
143 | DEFINE_PARSE_STRUCT_TYPE(rmhook, RMHOOK, ()); | |
144 | DEFINE_PARSE_STRUCT_TYPE(nodeinfo, NODEINFO, ()); | |
145 | DEFINE_PARSE_STRUCT_TYPE(typeinfo, TYPEINFO, ()); | |
146 | DEFINE_PARSE_STRUCT_TYPE(linkinfo, LINKINFO, (&ng_generic_nodeinfo_type)); | |
147 | ||
148 | /* Get length of an array when the length is stored as a 32 bit | |
149 | value immediately preceeding the array -- as with struct namelist | |
150 | and struct typelist. */ | |
151 | static int | |
152 | ng_generic_list_getLength(const struct ng_parse_type *type, | |
153 | const u_char *start, const u_char *buf) | |
154 | { | |
155 | return *((const u_int32_t *)(buf - 4)); | |
156 | } | |
157 | ||
158 | /* Get length of the array of struct linkinfo inside a struct hooklist */ | |
159 | static int | |
160 | ng_generic_linkinfo_getLength(const struct ng_parse_type *type, | |
161 | const u_char *start, const u_char *buf) | |
162 | { | |
163 | const struct hooklist *hl = (const struct hooklist *)start; | |
164 | ||
165 | return hl->nodeinfo.hooks; | |
166 | } | |
167 | ||
168 | /* Array type for a variable length array of struct namelist */ | |
169 | static const struct ng_parse_array_info ng_nodeinfoarray_type_info = { | |
170 | &ng_generic_nodeinfo_type, | |
171 | &ng_generic_list_getLength | |
172 | }; | |
173 | static const struct ng_parse_type ng_generic_nodeinfoarray_type = { | |
174 | &ng_parse_array_type, | |
175 | &ng_nodeinfoarray_type_info | |
176 | }; | |
177 | ||
e535ebfa | 178 | #if 0 /* unused */ |
984263bc MD |
179 | /* Array type for a variable length array of struct typelist */ |
180 | static const struct ng_parse_array_info ng_typeinfoarray_type_info = { | |
181 | &ng_generic_typeinfo_type, | |
182 | &ng_generic_list_getLength | |
183 | }; | |
184 | static const struct ng_parse_type ng_generic_typeinfoarray_type = { | |
185 | &ng_parse_array_type, | |
186 | &ng_typeinfoarray_type_info | |
187 | }; | |
e535ebfa | 188 | #endif |
984263bc MD |
189 | |
190 | /* Array type for array of struct linkinfo in struct hooklist */ | |
191 | static const struct ng_parse_array_info ng_generic_linkinfo_array_type_info = { | |
192 | &ng_generic_linkinfo_type, | |
193 | &ng_generic_linkinfo_getLength | |
194 | }; | |
195 | static const struct ng_parse_type ng_generic_linkinfo_array_type = { | |
196 | &ng_parse_array_type, | |
197 | &ng_generic_linkinfo_array_type_info | |
198 | }; | |
199 | ||
e535ebfa | 200 | #if 0 /* unused */ |
984263bc | 201 | DEFINE_PARSE_STRUCT_TYPE(typelist, TYPELIST, (&ng_generic_nodeinfoarray_type)); |
e535ebfa | 202 | #endif |
984263bc MD |
203 | DEFINE_PARSE_STRUCT_TYPE(hooklist, HOOKLIST, |
204 | (&ng_generic_nodeinfo_type, &ng_generic_linkinfo_array_type)); | |
205 | DEFINE_PARSE_STRUCT_TYPE(listnodes, LISTNODES, | |
206 | (&ng_generic_nodeinfoarray_type)); | |
207 | ||
208 | /* List of commands and how to convert arguments to/from ASCII */ | |
209 | static const struct ng_cmdlist ng_generic_cmds[] = { | |
210 | { | |
211 | NGM_GENERIC_COOKIE, | |
212 | NGM_SHUTDOWN, | |
213 | "shutdown", | |
214 | NULL, | |
215 | NULL | |
216 | }, | |
217 | { | |
218 | NGM_GENERIC_COOKIE, | |
219 | NGM_MKPEER, | |
220 | "mkpeer", | |
221 | &ng_generic_mkpeer_type, | |
222 | NULL | |
223 | }, | |
224 | { | |
225 | NGM_GENERIC_COOKIE, | |
226 | NGM_CONNECT, | |
227 | "connect", | |
228 | &ng_generic_connect_type, | |
229 | NULL | |
230 | }, | |
231 | { | |
232 | NGM_GENERIC_COOKIE, | |
233 | NGM_NAME, | |
234 | "name", | |
235 | &ng_generic_name_type, | |
236 | NULL | |
237 | }, | |
238 | { | |
239 | NGM_GENERIC_COOKIE, | |
240 | NGM_RMHOOK, | |
241 | "rmhook", | |
242 | &ng_generic_rmhook_type, | |
243 | NULL | |
244 | }, | |
245 | { | |
246 | NGM_GENERIC_COOKIE, | |
247 | NGM_NODEINFO, | |
248 | "nodeinfo", | |
249 | NULL, | |
250 | &ng_generic_nodeinfo_type | |
251 | }, | |
252 | { | |
253 | NGM_GENERIC_COOKIE, | |
254 | NGM_LISTHOOKS, | |
255 | "listhooks", | |
256 | NULL, | |
257 | &ng_generic_hooklist_type | |
258 | }, | |
259 | { | |
260 | NGM_GENERIC_COOKIE, | |
261 | NGM_LISTNAMES, | |
262 | "listnames", | |
263 | NULL, | |
264 | &ng_generic_listnodes_type /* same as NGM_LISTNODES */ | |
265 | }, | |
266 | { | |
267 | NGM_GENERIC_COOKIE, | |
268 | NGM_LISTNODES, | |
269 | "listnodes", | |
270 | NULL, | |
271 | &ng_generic_listnodes_type | |
272 | }, | |
273 | { | |
274 | NGM_GENERIC_COOKIE, | |
275 | NGM_LISTTYPES, | |
276 | "listtypes", | |
277 | NULL, | |
278 | &ng_generic_typeinfo_type | |
279 | }, | |
280 | { | |
281 | NGM_GENERIC_COOKIE, | |
282 | NGM_TEXT_CONFIG, | |
283 | "textconfig", | |
284 | NULL, | |
285 | &ng_parse_string_type | |
286 | }, | |
287 | { | |
288 | NGM_GENERIC_COOKIE, | |
289 | NGM_TEXT_STATUS, | |
290 | "textstatus", | |
291 | NULL, | |
292 | &ng_parse_string_type | |
293 | }, | |
294 | { | |
295 | NGM_GENERIC_COOKIE, | |
296 | NGM_ASCII2BINARY, | |
297 | "ascii2binary", | |
298 | &ng_parse_ng_mesg_type, | |
299 | &ng_parse_ng_mesg_type | |
300 | }, | |
301 | { | |
302 | NGM_GENERIC_COOKIE, | |
303 | NGM_BINARY2ASCII, | |
304 | "binary2ascii", | |
305 | &ng_parse_ng_mesg_type, | |
306 | &ng_parse_ng_mesg_type | |
307 | }, | |
308 | { 0 } | |
309 | }; | |
310 | ||
311 | /************************************************************************ | |
312 | Node routines | |
313 | ************************************************************************/ | |
314 | ||
191cd68c YT |
315 | static int |
316 | linker_api_available(void) | |
317 | { | |
318 | /* linker_* API won't work without a process context */ | |
319 | if (curproc == NULL) | |
320 | return 0; | |
321 | /* | |
70aac194 | 322 | * nlookup_init() relies on namei_oc to be initialized, |
191cd68c YT |
323 | * but it's not when the netgraph module is loaded during boot. |
324 | */ | |
70aac194 | 325 | if (namei_oc == NULL) |
191cd68c YT |
326 | return 0; |
327 | return 1; | |
328 | } | |
329 | ||
d8fef73e YT |
330 | static int |
331 | ng_load_module(const char *name) | |
332 | { | |
f15ba1a8 | 333 | char *path, filename[NG_TYPESIZ + 3]; |
d8fef73e YT |
334 | linker_file_t lf; |
335 | int error; | |
336 | ||
191cd68c | 337 | if (!linker_api_available()) |
d8fef73e YT |
338 | return (ENXIO); |
339 | ||
340 | /* Not found, try to load it as a loadable module */ | |
f8c7a42d | 341 | ksnprintf(filename, sizeof(filename), "ng_%s.ko", name); |
d8fef73e YT |
342 | if ((path = linker_search_path(filename)) == NULL) |
343 | return (ENXIO); | |
1c0e3286 | 344 | error = linker_load_file(path, &lf); |
884717e1 | 345 | kfree(path, M_LINKER); |
d8fef73e YT |
346 | if (error == 0) |
347 | lf->userrefs++; /* pretend kldload'ed */ | |
348 | return (error); | |
349 | } | |
350 | ||
984263bc MD |
351 | /* |
352 | * Instantiate a node of the requested type | |
353 | */ | |
354 | int | |
355 | ng_make_node(const char *typename, node_p *nodepp) | |
356 | { | |
357 | struct ng_type *type; | |
358 | ||
359 | /* Check that the type makes sense */ | |
360 | if (typename == NULL) { | |
361 | TRAP_ERROR; | |
362 | return (EINVAL); | |
363 | } | |
364 | ||
365 | /* Locate the node type */ | |
d8fef73e YT |
366 | if ((type = ng_findtype(typename)) == NULL) |
367 | return (ENXIO); | |
984263bc MD |
368 | |
369 | /* Call the constructor */ | |
370 | if (type->constructor != NULL) | |
371 | return ((*type->constructor)(nodepp)); | |
372 | else | |
373 | return (ng_make_node_common(type, nodepp)); | |
374 | } | |
375 | ||
376 | /* | |
377 | * Generic node creation. Called by node constructors. | |
378 | * The returned node has a reference count of 1. | |
379 | */ | |
380 | int | |
381 | ng_make_node_common(struct ng_type *type, node_p *nodepp) | |
382 | { | |
383 | node_p node; | |
384 | ||
385 | /* Require the node type to have been already installed */ | |
386 | if (ng_findtype(type->name) == NULL) { | |
387 | TRAP_ERROR; | |
388 | return (EINVAL); | |
389 | } | |
390 | ||
391 | /* Make a node and try attach it to the type */ | |
884717e1 | 392 | node = kmalloc(sizeof(*node), M_NETGRAPH, M_NOWAIT | M_ZERO); |
984263bc MD |
393 | if (node == NULL) { |
394 | TRAP_ERROR; | |
395 | return (ENOMEM); | |
396 | } | |
984263bc MD |
397 | node->type = type; |
398 | node->refs++; /* note reference */ | |
399 | type->refs++; | |
400 | ||
401 | /* Link us into the node linked list */ | |
402 | LIST_INSERT_HEAD(&nodelist, node, nodes); | |
403 | ||
404 | /* Initialize hook list for new node */ | |
405 | LIST_INIT(&node->hooks); | |
406 | ||
407 | /* get an ID and put us in the hash chain */ | |
408 | node->ID = nextID++; /* 137 per second for 1 year before wrap */ | |
409 | LIST_INSERT_HEAD(&ID_hash[node->ID % ID_HASH_SIZE], node, idnodes); | |
410 | ||
411 | /* Done */ | |
412 | *nodepp = node; | |
413 | return (0); | |
414 | } | |
415 | ||
416 | /* | |
417 | * Forceably start the shutdown process on a node. Either call | |
418 | * it's shutdown method, or do the default shutdown if there is | |
419 | * no type-specific method. | |
420 | * | |
421 | * Persistent nodes must have a type-specific method which | |
422 | * resets the NG_INVALID flag. | |
423 | */ | |
424 | void | |
425 | ng_rmnode(node_p node) | |
426 | { | |
427 | /* Check if it's already shutting down */ | |
428 | if ((node->flags & NG_INVALID) != 0) | |
429 | return; | |
430 | ||
431 | /* Add an extra reference so it doesn't go away during this */ | |
432 | node->refs++; | |
433 | ||
434 | /* Mark it invalid so any newcomers know not to try use it */ | |
435 | node->flags |= NG_INVALID; | |
436 | ||
437 | /* Ask the type if it has anything to do in this case */ | |
438 | if (node->type && node->type->shutdown) | |
439 | (*node->type->shutdown)(node); | |
440 | else { /* do the default thing */ | |
441 | ng_unname(node); | |
442 | ng_cutlinks(node); | |
443 | ng_unref(node); | |
444 | } | |
445 | ||
446 | /* Remove extra reference, possibly the last */ | |
447 | ng_unref(node); | |
448 | } | |
449 | ||
450 | /* | |
451 | * Called by the destructor to remove any STANDARD external references | |
452 | */ | |
453 | void | |
454 | ng_cutlinks(node_p node) | |
455 | { | |
456 | hook_p hook; | |
457 | ||
458 | /* Make sure that this is set to stop infinite loops */ | |
459 | node->flags |= NG_INVALID; | |
460 | ||
461 | /* If we have sleepers, wake them up; they'll see NG_INVALID */ | |
462 | if (node->sleepers) | |
463 | wakeup(node); | |
464 | ||
465 | /* Notify all remaining connected nodes to disconnect */ | |
466 | while ((hook = LIST_FIRST(&node->hooks)) != NULL) | |
467 | ng_destroy_hook(hook); | |
468 | } | |
469 | ||
470 | /* | |
471 | * Remove a reference to the node, possibly the last | |
472 | */ | |
473 | void | |
474 | ng_unref(node_p node) | |
475 | { | |
41e90fed | 476 | crit_enter(); |
984263bc MD |
477 | if (--node->refs <= 0) { |
478 | node->type->refs--; | |
479 | LIST_REMOVE(node, nodes); | |
480 | LIST_REMOVE(node, idnodes); | |
884717e1 | 481 | kfree(node, M_NETGRAPH); |
984263bc | 482 | } |
41e90fed | 483 | crit_exit(); |
984263bc MD |
484 | } |
485 | ||
486 | /* | |
487 | * Wait for a node to come ready. Returns a node with a reference count; | |
488 | * don't forget to drop it when we are done with it using ng_release_node(). | |
489 | */ | |
490 | int | |
491 | ng_wait_node(node_p node, char *msg) | |
492 | { | |
41e90fed | 493 | int error = 0; |
984263bc MD |
494 | |
495 | if (msg == NULL) | |
496 | msg = "netgraph"; | |
41e90fed | 497 | crit_enter(); |
984263bc MD |
498 | node->sleepers++; |
499 | node->refs++; /* the sleeping process counts as a reference */ | |
500 | while ((node->flags & (NG_BUSY | NG_INVALID)) == NG_BUSY) | |
377d4740 | 501 | error = tsleep(node, PCATCH, msg, 0); |
984263bc MD |
502 | node->sleepers--; |
503 | if (node->flags & NG_INVALID) { | |
504 | TRAP_ERROR; | |
505 | error = ENXIO; | |
506 | } else { | |
507 | KASSERT(node->refs > 1, | |
5e2195bf | 508 | ("%s: refs=%d", __func__, node->refs)); |
984263bc MD |
509 | node->flags |= NG_BUSY; |
510 | } | |
41e90fed | 511 | crit_exit(); |
984263bc MD |
512 | |
513 | /* Release the reference we had on it */ | |
514 | if (error != 0) | |
515 | ng_unref(node); | |
516 | return error; | |
517 | } | |
518 | ||
519 | /* | |
520 | * Release a node acquired via ng_wait_node() | |
521 | */ | |
522 | void | |
523 | ng_release_node(node_p node) | |
524 | { | |
525 | /* Declare that we don't want it */ | |
526 | node->flags &= ~NG_BUSY; | |
527 | ||
528 | /* If we have sleepers, then wake them up */ | |
529 | if (node->sleepers) | |
530 | wakeup(node); | |
531 | ||
532 | /* We also have a reference.. drop it too */ | |
533 | ng_unref(node); | |
534 | } | |
535 | ||
536 | /************************************************************************ | |
537 | Node ID handling | |
538 | ************************************************************************/ | |
539 | static node_p | |
540 | ng_ID2node(ng_ID_t ID) | |
541 | { | |
542 | node_p np; | |
543 | LIST_FOREACH(np, &ID_hash[ID % ID_HASH_SIZE], idnodes) { | |
544 | if ((np->flags & NG_INVALID) == 0 && np->ID == ID) | |
545 | break; | |
546 | } | |
547 | return(np); | |
548 | } | |
549 | ||
550 | ng_ID_t | |
551 | ng_node2ID(node_p node) | |
552 | { | |
553 | return (node->ID); | |
554 | } | |
555 | ||
556 | /************************************************************************ | |
557 | Node name handling | |
558 | ************************************************************************/ | |
559 | ||
560 | /* | |
561 | * Assign a node a name. Once assigned, the name cannot be changed. | |
562 | */ | |
563 | int | |
564 | ng_name_node(node_p node, const char *name) | |
565 | { | |
566 | int i; | |
567 | ||
568 | /* Check the name is valid */ | |
f15ba1a8 | 569 | for (i = 0; i < NG_NODESIZ; i++) { |
984263bc MD |
570 | if (name[i] == '\0' || name[i] == '.' || name[i] == ':') |
571 | break; | |
572 | } | |
573 | if (i == 0 || name[i] != '\0') { | |
574 | TRAP_ERROR; | |
575 | return (EINVAL); | |
576 | } | |
577 | if (ng_decodeidname(name) != 0) { /* valid IDs not allowed here */ | |
578 | TRAP_ERROR; | |
579 | return (EINVAL); | |
580 | } | |
581 | ||
582 | /* Check the node isn't already named */ | |
583 | if (node->name != NULL) { | |
584 | TRAP_ERROR; | |
585 | return (EISCONN); | |
586 | } | |
587 | ||
588 | /* Check the name isn't already being used */ | |
589 | if (ng_findname(node, name) != NULL) { | |
590 | TRAP_ERROR; | |
591 | return (EADDRINUSE); | |
592 | } | |
593 | ||
594 | /* Allocate space and copy it */ | |
884717e1 | 595 | node->name = kmalloc(strlen(name) + 1, M_NETGRAPH, M_NOWAIT); |
984263bc MD |
596 | if (node->name == NULL) { |
597 | TRAP_ERROR; | |
598 | return (ENOMEM); | |
599 | } | |
600 | strcpy(node->name, name); | |
601 | ||
602 | /* The name counts as a reference */ | |
603 | node->refs++; | |
604 | return (0); | |
605 | } | |
606 | ||
607 | /* | |
608 | * Find a node by absolute name. The name should NOT end with ':' | |
609 | * The name "." means "this node" and "[xxx]" means "the node | |
610 | * with ID (ie, at address) xxx". | |
611 | * | |
612 | * Returns the node if found, else NULL. | |
613 | */ | |
614 | node_p | |
615 | ng_findname(node_p this, const char *name) | |
616 | { | |
617 | node_p node; | |
618 | ng_ID_t temp; | |
619 | ||
620 | /* "." means "this node" */ | |
621 | if (strcmp(name, ".") == 0) | |
622 | return(this); | |
623 | ||
624 | /* Check for name-by-ID */ | |
625 | if ((temp = ng_decodeidname(name)) != 0) { | |
626 | return (ng_ID2node(temp)); | |
627 | } | |
628 | ||
629 | /* Find node by name */ | |
630 | LIST_FOREACH(node, &nodelist, nodes) { | |
631 | if ((node->name != NULL) | |
632 | && (strcmp(node->name, name) == 0) | |
633 | && ((node->flags & NG_INVALID) == 0)) | |
634 | break; | |
635 | } | |
636 | return (node); | |
637 | } | |
638 | ||
639 | /* | |
640 | * Decode a ID name, eg. "[f03034de]". Returns 0 if the | |
641 | * string is not valid, otherwise returns the value. | |
642 | */ | |
643 | static ng_ID_t | |
644 | ng_decodeidname(const char *name) | |
645 | { | |
646 | const int len = strlen(name); | |
647 | char *eptr; | |
648 | u_long val; | |
649 | ||
650 | /* Check for proper length, brackets, no leading junk */ | |
651 | if (len < 3 || name[0] != '[' || name[len - 1] != ']' | |
652 | || !isxdigit(name[1])) | |
653 | return (0); | |
654 | ||
655 | /* Decode number */ | |
656 | val = strtoul(name + 1, &eptr, 16); | |
657 | if (eptr - name != len - 1 || val == ULONG_MAX || val == 0) | |
658 | return ((ng_ID_t)0); | |
659 | return (ng_ID_t)val; | |
660 | } | |
661 | ||
662 | /* | |
663 | * Remove a name from a node. This should only be called | |
664 | * when shutting down and removing the node. | |
665 | */ | |
666 | void | |
667 | ng_unname(node_p node) | |
668 | { | |
669 | if (node->name) { | |
884717e1 | 670 | kfree(node->name, M_NETGRAPH); |
984263bc MD |
671 | node->name = NULL; |
672 | ng_unref(node); | |
673 | } | |
674 | } | |
675 | ||
676 | /************************************************************************ | |
677 | Hook routines | |
678 | ||
679 | Names are not optional. Hooks are always connected, except for a | |
680 | brief moment within these routines. | |
681 | ||
682 | ************************************************************************/ | |
683 | ||
684 | /* | |
685 | * Remove a hook reference | |
686 | */ | |
687 | void | |
688 | ng_unref_hook(hook_p hook) | |
689 | { | |
41e90fed | 690 | crit_enter(); |
984263bc | 691 | if (--hook->refs == 0) |
884717e1 | 692 | kfree(hook, M_NETGRAPH); |
41e90fed | 693 | crit_exit(); |
984263bc MD |
694 | } |
695 | ||
696 | /* | |
697 | * Add an unconnected hook to a node. Only used internally. | |
698 | */ | |
699 | static int | |
700 | ng_add_hook(node_p node, const char *name, hook_p *hookp) | |
701 | { | |
702 | hook_p hook; | |
703 | int error = 0; | |
704 | ||
705 | /* Check that the given name is good */ | |
706 | if (name == NULL) { | |
707 | TRAP_ERROR; | |
708 | return (EINVAL); | |
709 | } | |
710 | if (ng_findhook(node, name) != NULL) { | |
711 | TRAP_ERROR; | |
712 | return (EEXIST); | |
713 | } | |
714 | ||
715 | /* Allocate the hook and link it up */ | |
884717e1 | 716 | hook = kmalloc(sizeof(*hook), M_NETGRAPH, M_NOWAIT | M_ZERO); |
984263bc MD |
717 | if (hook == NULL) { |
718 | TRAP_ERROR; | |
719 | return (ENOMEM); | |
720 | } | |
984263bc MD |
721 | hook->refs = 1; |
722 | hook->flags = HK_INVALID; | |
723 | hook->node = node; | |
724 | node->refs++; /* each hook counts as a reference */ | |
725 | ||
726 | /* Check if the node type code has something to say about it */ | |
727 | if (node->type->newhook != NULL) | |
728 | if ((error = (*node->type->newhook)(node, hook, name)) != 0) | |
729 | goto fail; | |
730 | ||
731 | /* | |
732 | * The 'type' agrees so far, so go ahead and link it in. | |
733 | * We'll ask again later when we actually connect the hooks. | |
734 | */ | |
735 | LIST_INSERT_HEAD(&node->hooks, hook, hooks); | |
736 | node->numhooks++; | |
737 | ||
738 | /* Set hook name */ | |
884717e1 | 739 | hook->name = kmalloc(strlen(name) + 1, M_NETGRAPH, M_NOWAIT); |
984263bc MD |
740 | if (hook->name == NULL) { |
741 | error = ENOMEM; | |
742 | LIST_REMOVE(hook, hooks); | |
743 | node->numhooks--; | |
744 | fail: | |
745 | hook->node = NULL; | |
746 | ng_unref(node); | |
747 | ng_unref_hook(hook); /* this frees the hook */ | |
748 | return (error); | |
749 | } | |
750 | strcpy(hook->name, name); | |
751 | if (hookp) | |
752 | *hookp = hook; | |
753 | return (error); | |
754 | } | |
755 | ||
756 | /* | |
757 | * Connect a pair of hooks. Only used internally. | |
758 | */ | |
759 | static int | |
760 | ng_connect(hook_p hook1, hook_p hook2) | |
761 | { | |
762 | int error; | |
763 | ||
764 | hook1->peer = hook2; | |
765 | hook2->peer = hook1; | |
766 | ||
767 | /* Give each node the opportunity to veto the impending connection */ | |
768 | if (hook1->node->type->connect) { | |
769 | if ((error = (*hook1->node->type->connect) (hook1))) { | |
770 | ng_destroy_hook(hook1); /* also zaps hook2 */ | |
771 | return (error); | |
772 | } | |
773 | } | |
774 | if (hook2->node->type->connect) { | |
775 | if ((error = (*hook2->node->type->connect) (hook2))) { | |
776 | ng_destroy_hook(hook2); /* also zaps hook1 */ | |
777 | return (error); | |
778 | } | |
779 | } | |
780 | hook1->flags &= ~HK_INVALID; | |
781 | hook2->flags &= ~HK_INVALID; | |
782 | return (0); | |
783 | } | |
784 | ||
785 | /* | |
786 | * Find a hook | |
787 | * | |
788 | * Node types may supply their own optimized routines for finding | |
789 | * hooks. If none is supplied, we just do a linear search. | |
790 | */ | |
791 | hook_p | |
792 | ng_findhook(node_p node, const char *name) | |
793 | { | |
794 | hook_p hook; | |
795 | ||
796 | if (node->type->findhook != NULL) | |
797 | return (*node->type->findhook)(node, name); | |
798 | LIST_FOREACH(hook, &node->hooks, hooks) { | |
799 | if (hook->name != NULL | |
800 | && strcmp(hook->name, name) == 0 | |
801 | && (hook->flags & HK_INVALID) == 0) | |
802 | return (hook); | |
803 | } | |
804 | return (NULL); | |
805 | } | |
806 | ||
807 | /* | |
808 | * Destroy a hook | |
809 | * | |
810 | * As hooks are always attached, this really destroys two hooks. | |
811 | * The one given, and the one attached to it. Disconnect the hooks | |
812 | * from each other first. | |
813 | */ | |
814 | void | |
815 | ng_destroy_hook(hook_p hook) | |
816 | { | |
817 | hook_p peer = hook->peer; | |
818 | ||
819 | hook->flags |= HK_INVALID; /* as soon as possible */ | |
820 | if (peer) { | |
821 | peer->flags |= HK_INVALID; /* as soon as possible */ | |
822 | hook->peer = NULL; | |
823 | peer->peer = NULL; | |
824 | ng_disconnect_hook(peer); | |
825 | } | |
826 | ng_disconnect_hook(hook); | |
827 | } | |
828 | ||
829 | /* | |
830 | * Notify the node of the hook's demise. This may result in more actions | |
831 | * (e.g. shutdown) but we don't do that ourselves and don't know what | |
832 | * happens there. If there is no appropriate handler, then just remove it | |
833 | * (and decrement the reference count of it's node which in turn might | |
834 | * make something happen). | |
835 | */ | |
836 | static void | |
837 | ng_disconnect_hook(hook_p hook) | |
838 | { | |
839 | node_p node = hook->node; | |
840 | ||
841 | /* | |
842 | * Remove the hook from the node's list to avoid possible recursion | |
843 | * in case the disconnection results in node shutdown. | |
844 | */ | |
845 | LIST_REMOVE(hook, hooks); | |
846 | node->numhooks--; | |
847 | if (node->type->disconnect) { | |
848 | /* | |
849 | * The type handler may elect to destroy the peer so don't | |
850 | * trust its existance after this point. | |
851 | */ | |
852 | (*node->type->disconnect) (hook); | |
853 | } | |
854 | ng_unref(node); /* might be the last reference */ | |
855 | if (hook->name) | |
884717e1 | 856 | kfree(hook->name, M_NETGRAPH); |
984263bc MD |
857 | hook->node = NULL; /* may still be referenced elsewhere */ |
858 | ng_unref_hook(hook); | |
859 | } | |
860 | ||
861 | /* | |
862 | * Take two hooks on a node and merge the connection so that the given node | |
863 | * is effectively bypassed. | |
864 | */ | |
865 | int | |
866 | ng_bypass(hook_p hook1, hook_p hook2) | |
867 | { | |
868 | if (hook1->node != hook2->node) | |
869 | return (EINVAL); | |
870 | hook1->peer->peer = hook2->peer; | |
871 | hook2->peer->peer = hook1->peer; | |
872 | ||
873 | /* XXX If we ever cache methods on hooks update them as well */ | |
874 | hook1->peer = NULL; | |
875 | hook2->peer = NULL; | |
876 | ng_destroy_hook(hook1); | |
877 | ng_destroy_hook(hook2); | |
878 | return (0); | |
879 | } | |
880 | ||
881 | /* | |
882 | * Install a new netgraph type | |
883 | */ | |
884 | int | |
885 | ng_newtype(struct ng_type *tp) | |
886 | { | |
887 | const size_t namelen = strlen(tp->name); | |
888 | ||
889 | /* Check version and type name fields */ | |
f15ba1a8 | 890 | if (tp->version != NG_VERSION || namelen == 0 || namelen >= NG_TYPESIZ) { |
984263bc MD |
891 | TRAP_ERROR; |
892 | return (EINVAL); | |
893 | } | |
894 | ||
895 | /* Check for name collision */ | |
896 | if (ng_findtype(tp->name) != NULL) { | |
897 | TRAP_ERROR; | |
898 | return (EEXIST); | |
899 | } | |
900 | ||
901 | /* Link in new type */ | |
902 | LIST_INSERT_HEAD(&typelist, tp, types); | |
903 | tp->refs = 1; /* first ref is linked list */ | |
904 | return (0); | |
905 | } | |
906 | ||
907 | /* | |
908 | * Look for a type of the name given | |
909 | */ | |
910 | struct ng_type * | |
911 | ng_findtype(const char *typename) | |
912 | { | |
913 | struct ng_type *type; | |
914 | ||
915 | LIST_FOREACH(type, &typelist, types) { | |
916 | if (strcmp(type->name, typename) == 0) | |
917 | break; | |
918 | } | |
919 | return (type); | |
920 | } | |
921 | ||
922 | ||
923 | /************************************************************************ | |
924 | Composite routines | |
925 | ************************************************************************/ | |
926 | ||
927 | /* | |
928 | * Make a peer and connect. The order is arranged to minimise | |
929 | * the work needed to back out in case of error. | |
930 | */ | |
931 | int | |
932 | ng_mkpeer(node_p node, const char *name, const char *name2, char *type) | |
933 | { | |
934 | node_p node2; | |
935 | hook_p hook; | |
936 | hook_p hook2; | |
937 | int error; | |
938 | ||
939 | if ((error = ng_add_hook(node, name, &hook))) | |
940 | return (error); | |
d8fef73e YT |
941 | |
942 | /* make sure we have the module needed */ | |
943 | if (ng_findtype(type) == NULL) { | |
944 | /* Not found, try to load it as a loadable module */ | |
945 | error = ng_load_module(type); | |
946 | if (error != 0) { | |
4b1cf444 | 947 | kprintf("required netgraph module ng_%s not loaded\n", |
d8fef73e YT |
948 | type); |
949 | return (error); | |
950 | } | |
951 | } | |
984263bc MD |
952 | if ((error = ng_make_node(type, &node2))) { |
953 | ng_destroy_hook(hook); | |
954 | return (error); | |
955 | } | |
956 | if ((error = ng_add_hook(node2, name2, &hook2))) { | |
957 | ng_rmnode(node2); | |
958 | ng_destroy_hook(hook); | |
959 | return (error); | |
960 | } | |
961 | ||
962 | /* | |
963 | * Actually link the two hooks together.. on failure they are | |
964 | * destroyed so we don't have to do that here. | |
965 | */ | |
966 | if ((error = ng_connect(hook, hook2))) | |
967 | ng_rmnode(node2); | |
968 | return (error); | |
969 | } | |
970 | ||
971 | /* | |
972 | * Connect two nodes using the specified hooks | |
973 | */ | |
974 | int | |
975 | ng_con_nodes(node_p node, const char *name, node_p node2, const char *name2) | |
976 | { | |
977 | int error; | |
978 | hook_p hook; | |
979 | hook_p hook2; | |
980 | ||
981 | if ((error = ng_add_hook(node, name, &hook))) | |
982 | return (error); | |
983 | if ((error = ng_add_hook(node2, name2, &hook2))) { | |
984 | ng_destroy_hook(hook); | |
985 | return (error); | |
986 | } | |
987 | return (ng_connect(hook, hook2)); | |
988 | } | |
989 | ||
990 | /* | |
991 | * Parse and verify a string of the form: <NODE:><PATH> | |
992 | * | |
993 | * Such a string can refer to a specific node or a specific hook | |
994 | * on a specific node, depending on how you look at it. In the | |
995 | * latter case, the PATH component must not end in a dot. | |
996 | * | |
997 | * Both <NODE:> and <PATH> are optional. The <PATH> is a string | |
998 | * of hook names separated by dots. This breaks out the original | |
999 | * string, setting *nodep to "NODE" (or NULL if none) and *pathp | |
1000 | * to "PATH" (or NULL if degenerate). Also, *hookp will point to | |
1001 | * the final hook component of <PATH>, if any, otherwise NULL. | |
1002 | * | |
1003 | * This returns -1 if the path is malformed. The char ** are optional. | |
1004 | */ | |
1005 | ||
1006 | int | |
1007 | ng_path_parse(char *addr, char **nodep, char **pathp, char **hookp) | |
1008 | { | |
1009 | char *node, *path, *hook; | |
1010 | int k; | |
1011 | ||
1012 | /* | |
1013 | * Extract absolute NODE, if any | |
1014 | */ | |
1015 | for (path = addr; *path && *path != ':'; path++); | |
1016 | if (*path) { | |
1017 | node = addr; /* Here's the NODE */ | |
1018 | *path++ = '\0'; /* Here's the PATH */ | |
1019 | ||
1020 | /* Node name must not be empty */ | |
1021 | if (!*node) | |
1022 | return -1; | |
1023 | ||
1024 | /* A name of "." is OK; otherwise '.' not allowed */ | |
1025 | if (strcmp(node, ".") != 0) { | |
1026 | for (k = 0; node[k]; k++) | |
1027 | if (node[k] == '.') | |
1028 | return -1; | |
1029 | } | |
1030 | } else { | |
1031 | node = NULL; /* No absolute NODE */ | |
1032 | path = addr; /* Here's the PATH */ | |
1033 | } | |
1034 | ||
1035 | /* Snoop for illegal characters in PATH */ | |
1036 | for (k = 0; path[k]; k++) | |
1037 | if (path[k] == ':') | |
1038 | return -1; | |
1039 | ||
1040 | /* Check for no repeated dots in PATH */ | |
1041 | for (k = 0; path[k]; k++) | |
1042 | if (path[k] == '.' && path[k + 1] == '.') | |
1043 | return -1; | |
1044 | ||
1045 | /* Remove extra (degenerate) dots from beginning or end of PATH */ | |
1046 | if (path[0] == '.') | |
1047 | path++; | |
1048 | if (*path && path[strlen(path) - 1] == '.') | |
1049 | path[strlen(path) - 1] = 0; | |
1050 | ||
1051 | /* If PATH has a dot, then we're not talking about a hook */ | |
1052 | if (*path) { | |
1053 | for (hook = path, k = 0; path[k]; k++) | |
1054 | if (path[k] == '.') { | |
1055 | hook = NULL; | |
1056 | break; | |
1057 | } | |
1058 | } else | |
1059 | path = hook = NULL; | |
1060 | ||
1061 | /* Done */ | |
1062 | if (nodep) | |
1063 | *nodep = node; | |
1064 | if (pathp) | |
1065 | *pathp = path; | |
1066 | if (hookp) | |
1067 | *hookp = hook; | |
1068 | return (0); | |
1069 | } | |
1070 | ||
1071 | /* | |
1072 | * Given a path, which may be absolute or relative, and a starting node, | |
1073 | * return the destination node. Compute the "return address" if desired. | |
1074 | */ | |
1075 | int | |
1076 | ng_path2node(node_p here, const char *address, node_p *destp, char **rtnp) | |
1077 | { | |
1078 | const node_p start = here; | |
f15ba1a8 | 1079 | char fullpath[NG_PATHSIZ]; |
984263bc MD |
1080 | char *nodename, *path, pbuf[2]; |
1081 | node_p node; | |
1082 | char *cp; | |
1083 | ||
1084 | /* Initialize */ | |
1085 | if (rtnp) | |
1086 | *rtnp = NULL; | |
1087 | if (destp == NULL) | |
1088 | return EINVAL; | |
1089 | *destp = NULL; | |
1090 | ||
1091 | /* Make a writable copy of address for ng_path_parse() */ | |
1092 | strncpy(fullpath, address, sizeof(fullpath) - 1); | |
1093 | fullpath[sizeof(fullpath) - 1] = '\0'; | |
1094 | ||
1095 | /* Parse out node and sequence of hooks */ | |
1096 | if (ng_path_parse(fullpath, &nodename, &path, NULL) < 0) { | |
1097 | TRAP_ERROR; | |
1098 | return EINVAL; | |
1099 | } | |
1100 | if (path == NULL) { | |
1101 | pbuf[0] = '.'; /* Needs to be writable */ | |
1102 | pbuf[1] = '\0'; | |
1103 | path = pbuf; | |
1104 | } | |
1105 | ||
1106 | /* For an absolute address, jump to the starting node */ | |
1107 | if (nodename) { | |
1108 | node = ng_findname(here, nodename); | |
1109 | if (node == NULL) { | |
1110 | TRAP_ERROR; | |
1111 | return (ENOENT); | |
1112 | } | |
1113 | } else | |
1114 | node = here; | |
1115 | ||
1116 | /* Now follow the sequence of hooks */ | |
1117 | for (cp = path; node != NULL && *cp != '\0'; ) { | |
1118 | hook_p hook; | |
1119 | char *segment; | |
1120 | ||
1121 | /* | |
1122 | * Break out the next path segment. Replace the dot we just | |
1123 | * found with a NUL; "cp" points to the next segment (or the | |
1124 | * NUL at the end). | |
1125 | */ | |
1126 | for (segment = cp; *cp != '\0'; cp++) { | |
1127 | if (*cp == '.') { | |
1128 | *cp++ = '\0'; | |
1129 | break; | |
1130 | } | |
1131 | } | |
1132 | ||
1133 | /* Empty segment */ | |
1134 | if (*segment == '\0') | |
1135 | continue; | |
1136 | ||
1137 | /* We have a segment, so look for a hook by that name */ | |
1138 | hook = ng_findhook(node, segment); | |
1139 | ||
1140 | /* Can't get there from here... */ | |
1141 | if (hook == NULL | |
1142 | || hook->peer == NULL | |
1143 | || (hook->flags & HK_INVALID) != 0) { | |
1144 | TRAP_ERROR; | |
1145 | return (ENOENT); | |
1146 | } | |
1147 | ||
1148 | /* Hop on over to the next node */ | |
1149 | node = hook->peer->node; | |
1150 | } | |
1151 | ||
1152 | /* If node somehow missing, fail here (probably this is not needed) */ | |
1153 | if (node == NULL) { | |
1154 | TRAP_ERROR; | |
1155 | return (ENXIO); | |
1156 | } | |
1157 | ||
1158 | /* Now compute return address, i.e., the path to the sender */ | |
1159 | if (rtnp != NULL) { | |
884717e1 | 1160 | *rtnp = kmalloc(NG_NODESIZ + 1, M_NETGRAPH, M_NOWAIT); |
984263bc MD |
1161 | if (*rtnp == NULL) { |
1162 | TRAP_ERROR; | |
1163 | return (ENOMEM); | |
1164 | } | |
1165 | if (start->name != NULL) | |
f8c7a42d | 1166 | ksprintf(*rtnp, "%s:", start->name); |
984263bc | 1167 | else |
f8c7a42d | 1168 | ksprintf(*rtnp, "[%x]:", ng_node2ID(start)); |
984263bc MD |
1169 | } |
1170 | ||
1171 | /* Done */ | |
1172 | *destp = node; | |
1173 | return (0); | |
1174 | } | |
1175 | ||
1176 | /* | |
1177 | * Call the appropriate message handler for the object. | |
1178 | * It is up to the message handler to free the message. | |
1179 | * If it's a generic message, handle it generically, otherwise | |
1180 | * call the type's message handler (if it exists) | |
1181 | * XXX (race). Remember that a queued message may reference a node | |
1182 | * or hook that has just been invalidated. It will exist | |
1183 | * as the queue code is holding a reference, but.. | |
1184 | */ | |
1185 | ||
1186 | #define CALL_MSG_HANDLER(error, node, msg, retaddr, resp) \ | |
1187 | do { \ | |
1188 | if((msg)->header.typecookie == NGM_GENERIC_COOKIE) { \ | |
1189 | (error) = ng_generic_msg((node), (msg), \ | |
1190 | (retaddr), (resp)); \ | |
1191 | } else { \ | |
1192 | if ((node)->type->rcvmsg != NULL) { \ | |
1193 | (error) = (*(node)->type->rcvmsg)((node), \ | |
1194 | (msg), (retaddr), (resp)); \ | |
1195 | } else { \ | |
1196 | TRAP_ERROR; \ | |
884717e1 | 1197 | kfree((msg), M_NETGRAPH); \ |
984263bc MD |
1198 | (error) = EINVAL; \ |
1199 | } \ | |
1200 | } \ | |
1201 | } while (0) | |
1202 | ||
1203 | ||
1204 | /* | |
1205 | * Send a control message to a node | |
1206 | */ | |
1207 | int | |
1208 | ng_send_msg(node_p here, struct ng_mesg *msg, const char *address, | |
1209 | struct ng_mesg **rptr) | |
1210 | { | |
1211 | node_p dest = NULL; | |
1212 | char *retaddr = NULL; | |
1213 | int error; | |
1214 | ||
1215 | /* Find the target node */ | |
1216 | error = ng_path2node(here, address, &dest, &retaddr); | |
1217 | if (error) { | |
884717e1 | 1218 | kfree(msg, M_NETGRAPH); |
984263bc MD |
1219 | return error; |
1220 | } | |
1221 | ||
1222 | /* Make sure the resp field is null before we start */ | |
1223 | if (rptr != NULL) | |
1224 | *rptr = NULL; | |
1225 | ||
1226 | CALL_MSG_HANDLER(error, dest, msg, retaddr, rptr); | |
1227 | ||
1228 | /* Make sure that if there is a response, it has the RESP bit set */ | |
1229 | if ((error == 0) && rptr && *rptr) | |
1230 | (*rptr)->header.flags |= NGF_RESP; | |
1231 | ||
1232 | /* | |
1233 | * If we had a return address it is up to us to free it. They should | |
1234 | * have taken a copy if they needed to make a delayed response. | |
1235 | */ | |
1236 | if (retaddr) | |
884717e1 | 1237 | kfree(retaddr, M_NETGRAPH); |
984263bc MD |
1238 | return (error); |
1239 | } | |
1240 | ||
1241 | /* | |
1242 | * Implement the 'generic' control messages | |
1243 | */ | |
1244 | static int | |
1245 | ng_generic_msg(node_p here, struct ng_mesg *msg, const char *retaddr, | |
1246 | struct ng_mesg **resp) | |
1247 | { | |
1248 | int error = 0; | |
1249 | ||
1250 | if (msg->header.typecookie != NGM_GENERIC_COOKIE) { | |
1251 | TRAP_ERROR; | |
884717e1 | 1252 | kfree(msg, M_NETGRAPH); |
984263bc MD |
1253 | return (EINVAL); |
1254 | } | |
1255 | switch (msg->header.cmd) { | |
1256 | case NGM_SHUTDOWN: | |
1257 | ng_rmnode(here); | |
1258 | break; | |
1259 | case NGM_MKPEER: | |
1260 | { | |
1261 | struct ngm_mkpeer *const mkp = (struct ngm_mkpeer *) msg->data; | |
1262 | ||
1263 | if (msg->header.arglen != sizeof(*mkp)) { | |
1264 | TRAP_ERROR; | |
1265 | return (EINVAL); | |
1266 | } | |
1267 | mkp->type[sizeof(mkp->type) - 1] = '\0'; | |
1268 | mkp->ourhook[sizeof(mkp->ourhook) - 1] = '\0'; | |
1269 | mkp->peerhook[sizeof(mkp->peerhook) - 1] = '\0'; | |
1270 | error = ng_mkpeer(here, mkp->ourhook, mkp->peerhook, mkp->type); | |
1271 | break; | |
1272 | } | |
1273 | case NGM_CONNECT: | |
1274 | { | |
1275 | struct ngm_connect *const con = | |
1276 | (struct ngm_connect *) msg->data; | |
1277 | node_p node2; | |
1278 | ||
1279 | if (msg->header.arglen != sizeof(*con)) { | |
1280 | TRAP_ERROR; | |
1281 | return (EINVAL); | |
1282 | } | |
1283 | con->path[sizeof(con->path) - 1] = '\0'; | |
1284 | con->ourhook[sizeof(con->ourhook) - 1] = '\0'; | |
1285 | con->peerhook[sizeof(con->peerhook) - 1] = '\0'; | |
1286 | error = ng_path2node(here, con->path, &node2, NULL); | |
1287 | if (error) | |
1288 | break; | |
1289 | error = ng_con_nodes(here, con->ourhook, node2, con->peerhook); | |
1290 | break; | |
1291 | } | |
1292 | case NGM_NAME: | |
1293 | { | |
1294 | struct ngm_name *const nam = (struct ngm_name *) msg->data; | |
1295 | ||
1296 | if (msg->header.arglen != sizeof(*nam)) { | |
1297 | TRAP_ERROR; | |
1298 | return (EINVAL); | |
1299 | } | |
1300 | nam->name[sizeof(nam->name) - 1] = '\0'; | |
1301 | error = ng_name_node(here, nam->name); | |
1302 | break; | |
1303 | } | |
1304 | case NGM_RMHOOK: | |
1305 | { | |
1306 | struct ngm_rmhook *const rmh = (struct ngm_rmhook *) msg->data; | |
1307 | hook_p hook; | |
1308 | ||
1309 | if (msg->header.arglen != sizeof(*rmh)) { | |
1310 | TRAP_ERROR; | |
1311 | return (EINVAL); | |
1312 | } | |
1313 | rmh->ourhook[sizeof(rmh->ourhook) - 1] = '\0'; | |
1314 | if ((hook = ng_findhook(here, rmh->ourhook)) != NULL) | |
1315 | ng_destroy_hook(hook); | |
1316 | break; | |
1317 | } | |
1318 | case NGM_NODEINFO: | |
1319 | { | |
1320 | struct nodeinfo *ni; | |
1321 | struct ng_mesg *rp; | |
1322 | ||
1323 | /* Get response struct */ | |
1324 | if (resp == NULL) { | |
1325 | error = EINVAL; | |
1326 | break; | |
1327 | } | |
1328 | NG_MKRESPONSE(rp, msg, sizeof(*ni), M_NOWAIT); | |
1329 | if (rp == NULL) { | |
1330 | error = ENOMEM; | |
1331 | break; | |
1332 | } | |
1333 | ||
1334 | /* Fill in node info */ | |
1335 | ni = (struct nodeinfo *) rp->data; | |
1336 | if (here->name != NULL) | |
f15ba1a8 MD |
1337 | strlcpy(ni->name, here->name, NG_NODESIZ); |
1338 | strlcpy(ni->type, here->type->name, NG_TYPESIZ); | |
984263bc MD |
1339 | ni->id = ng_node2ID(here); |
1340 | ni->hooks = here->numhooks; | |
1341 | *resp = rp; | |
1342 | break; | |
1343 | } | |
1344 | case NGM_LISTHOOKS: | |
1345 | { | |
1346 | const int nhooks = here->numhooks; | |
1347 | struct hooklist *hl; | |
1348 | struct nodeinfo *ni; | |
1349 | struct ng_mesg *rp; | |
1350 | hook_p hook; | |
1351 | ||
1352 | /* Get response struct */ | |
1353 | if (resp == NULL) { | |
1354 | error = EINVAL; | |
1355 | break; | |
1356 | } | |
1357 | NG_MKRESPONSE(rp, msg, sizeof(*hl) | |
1358 | + (nhooks * sizeof(struct linkinfo)), M_NOWAIT); | |
1359 | if (rp == NULL) { | |
1360 | error = ENOMEM; | |
1361 | break; | |
1362 | } | |
1363 | hl = (struct hooklist *) rp->data; | |
1364 | ni = &hl->nodeinfo; | |
1365 | ||
1366 | /* Fill in node info */ | |
1367 | if (here->name) | |
f15ba1a8 MD |
1368 | strlcpy(ni->name, here->name, NG_NODESIZ); |
1369 | strlcpy(ni->type, here->type->name, NG_TYPESIZ); | |
984263bc MD |
1370 | ni->id = ng_node2ID(here); |
1371 | ||
1372 | /* Cycle through the linked list of hooks */ | |
1373 | ni->hooks = 0; | |
1374 | LIST_FOREACH(hook, &here->hooks, hooks) { | |
1375 | struct linkinfo *const link = &hl->link[ni->hooks]; | |
1376 | ||
1377 | if (ni->hooks >= nhooks) { | |
1378 | log(LOG_ERR, "%s: number of %s changed\n", | |
5e2195bf | 1379 | __func__, "hooks"); |
984263bc MD |
1380 | break; |
1381 | } | |
1382 | if ((hook->flags & HK_INVALID) != 0) | |
1383 | continue; | |
f15ba1a8 MD |
1384 | strlcpy(link->ourhook, hook->name, NG_HOOKSIZ); |
1385 | strlcpy(link->peerhook, hook->peer->name, NG_HOOKSIZ); | |
984263bc | 1386 | if (hook->peer->node->name != NULL) |
f15ba1a8 MD |
1387 | strlcpy(link->nodeinfo.name, |
1388 | hook->peer->node->name, NG_NODESIZ); | |
1389 | strlcpy(link->nodeinfo.type, | |
1390 | hook->peer->node->type->name, NG_TYPESIZ); | |
984263bc MD |
1391 | link->nodeinfo.id = ng_node2ID(hook->peer->node); |
1392 | link->nodeinfo.hooks = hook->peer->node->numhooks; | |
1393 | ni->hooks++; | |
1394 | } | |
1395 | *resp = rp; | |
1396 | break; | |
1397 | } | |
1398 | ||
1399 | case NGM_LISTNAMES: | |
1400 | case NGM_LISTNODES: | |
1401 | { | |
1402 | const int unnamed = (msg->header.cmd == NGM_LISTNODES); | |
1403 | struct namelist *nl; | |
1404 | struct ng_mesg *rp; | |
1405 | node_p node; | |
1406 | int num = 0; | |
1407 | ||
1408 | if (resp == NULL) { | |
1409 | error = EINVAL; | |
1410 | break; | |
1411 | } | |
1412 | ||
1413 | /* Count number of nodes */ | |
1414 | LIST_FOREACH(node, &nodelist, nodes) { | |
1415 | if ((node->flags & NG_INVALID) == 0 | |
1416 | && (unnamed || node->name != NULL)) | |
1417 | num++; | |
1418 | } | |
1419 | ||
1420 | /* Get response struct */ | |
1421 | if (resp == NULL) { | |
1422 | error = EINVAL; | |
1423 | break; | |
1424 | } | |
1425 | NG_MKRESPONSE(rp, msg, sizeof(*nl) | |
1426 | + (num * sizeof(struct nodeinfo)), M_NOWAIT); | |
1427 | if (rp == NULL) { | |
1428 | error = ENOMEM; | |
1429 | break; | |
1430 | } | |
1431 | nl = (struct namelist *) rp->data; | |
1432 | ||
1433 | /* Cycle through the linked list of nodes */ | |
1434 | nl->numnames = 0; | |
1435 | LIST_FOREACH(node, &nodelist, nodes) { | |
1436 | struct nodeinfo *const np = &nl->nodeinfo[nl->numnames]; | |
1437 | ||
1438 | if (nl->numnames >= num) { | |
1439 | log(LOG_ERR, "%s: number of %s changed\n", | |
5e2195bf | 1440 | __func__, "nodes"); |
984263bc MD |
1441 | break; |
1442 | } | |
1443 | if ((node->flags & NG_INVALID) != 0) | |
1444 | continue; | |
1445 | if (!unnamed && node->name == NULL) | |
1446 | continue; | |
1447 | if (node->name != NULL) | |
f15ba1a8 MD |
1448 | strlcpy(np->name, node->name, NG_NODESIZ); |
1449 | strlcpy(np->type, node->type->name, NG_TYPESIZ); | |
984263bc MD |
1450 | np->id = ng_node2ID(node); |
1451 | np->hooks = node->numhooks; | |
1452 | nl->numnames++; | |
1453 | } | |
1454 | *resp = rp; | |
1455 | break; | |
1456 | } | |
1457 | ||
1458 | case NGM_LISTTYPES: | |
1459 | { | |
1460 | struct typelist *tl; | |
1461 | struct ng_mesg *rp; | |
1462 | struct ng_type *type; | |
1463 | int num = 0; | |
1464 | ||
1465 | if (resp == NULL) { | |
1466 | error = EINVAL; | |
1467 | break; | |
1468 | } | |
1469 | ||
1470 | /* Count number of types */ | |
1471 | LIST_FOREACH(type, &typelist, types) | |
1472 | num++; | |
1473 | ||
1474 | /* Get response struct */ | |
1475 | if (resp == NULL) { | |
1476 | error = EINVAL; | |
1477 | break; | |
1478 | } | |
1479 | NG_MKRESPONSE(rp, msg, sizeof(*tl) | |
1480 | + (num * sizeof(struct typeinfo)), M_NOWAIT); | |
1481 | if (rp == NULL) { | |
1482 | error = ENOMEM; | |
1483 | break; | |
1484 | } | |
1485 | tl = (struct typelist *) rp->data; | |
1486 | ||
1487 | /* Cycle through the linked list of types */ | |
1488 | tl->numtypes = 0; | |
1489 | LIST_FOREACH(type, &typelist, types) { | |
1490 | struct typeinfo *const tp = &tl->typeinfo[tl->numtypes]; | |
1491 | ||
1492 | if (tl->numtypes >= num) { | |
1493 | log(LOG_ERR, "%s: number of %s changed\n", | |
5e2195bf | 1494 | __func__, "types"); |
984263bc MD |
1495 | break; |
1496 | } | |
f15ba1a8 | 1497 | strlcpy(tp->type_name, type->name, NG_TYPESIZ); |
984263bc MD |
1498 | tp->numnodes = type->refs - 1; /* don't count list */ |
1499 | tl->numtypes++; | |
1500 | } | |
1501 | *resp = rp; | |
1502 | break; | |
1503 | } | |
1504 | ||
1505 | case NGM_BINARY2ASCII: | |
1506 | { | |
1507 | int bufSize = 20 * 1024; /* XXX hard coded constant */ | |
1508 | const struct ng_parse_type *argstype; | |
1509 | const struct ng_cmdlist *c; | |
1510 | struct ng_mesg *rp, *binary, *ascii; | |
1511 | ||
1512 | /* Data area must contain a valid netgraph message */ | |
1513 | binary = (struct ng_mesg *)msg->data; | |
1514 | if (msg->header.arglen < sizeof(struct ng_mesg) | |
1515 | || msg->header.arglen - sizeof(struct ng_mesg) | |
1516 | < binary->header.arglen) { | |
1517 | error = EINVAL; | |
1518 | break; | |
1519 | } | |
1520 | ||
1521 | /* Get a response message with lots of room */ | |
1522 | NG_MKRESPONSE(rp, msg, sizeof(*ascii) + bufSize, M_NOWAIT); | |
1523 | if (rp == NULL) { | |
1524 | error = ENOMEM; | |
1525 | break; | |
1526 | } | |
1527 | ascii = (struct ng_mesg *)rp->data; | |
1528 | ||
1529 | /* Copy binary message header to response message payload */ | |
1530 | bcopy(binary, ascii, sizeof(*binary)); | |
1531 | ||
1532 | /* Find command by matching typecookie and command number */ | |
1533 | for (c = here->type->cmdlist; | |
1534 | c != NULL && c->name != NULL; c++) { | |
1535 | if (binary->header.typecookie == c->cookie | |
1536 | && binary->header.cmd == c->cmd) | |
1537 | break; | |
1538 | } | |
1539 | if (c == NULL || c->name == NULL) { | |
1540 | for (c = ng_generic_cmds; c->name != NULL; c++) { | |
1541 | if (binary->header.typecookie == c->cookie | |
1542 | && binary->header.cmd == c->cmd) | |
1543 | break; | |
1544 | } | |
1545 | if (c->name == NULL) { | |
884717e1 | 1546 | kfree(rp, M_NETGRAPH); |
984263bc MD |
1547 | error = ENOSYS; |
1548 | break; | |
1549 | } | |
1550 | } | |
1551 | ||
1552 | /* Convert command name to ASCII */ | |
f8c7a42d | 1553 | ksnprintf(ascii->header.cmdstr, sizeof(ascii->header.cmdstr), |
984263bc MD |
1554 | "%s", c->name); |
1555 | ||
1556 | /* Convert command arguments to ASCII */ | |
1557 | argstype = (binary->header.flags & NGF_RESP) ? | |
1558 | c->respType : c->mesgType; | |
1559 | if (argstype == NULL) | |
1560 | *ascii->data = '\0'; | |
1561 | else { | |
1562 | if ((error = ng_unparse(argstype, | |
1563 | (u_char *)binary->data, | |
1564 | ascii->data, bufSize)) != 0) { | |
884717e1 | 1565 | kfree(rp, M_NETGRAPH); |
984263bc MD |
1566 | break; |
1567 | } | |
1568 | } | |
1569 | ||
1570 | /* Return the result as struct ng_mesg plus ASCII string */ | |
1571 | bufSize = strlen(ascii->data) + 1; | |
1572 | ascii->header.arglen = bufSize; | |
1573 | rp->header.arglen = sizeof(*ascii) + bufSize; | |
1574 | *resp = rp; | |
1575 | break; | |
1576 | } | |
1577 | ||
1578 | case NGM_ASCII2BINARY: | |
1579 | { | |
1580 | int bufSize = 2000; /* XXX hard coded constant */ | |
1581 | const struct ng_cmdlist *c; | |
1582 | const struct ng_parse_type *argstype; | |
1583 | struct ng_mesg *rp, *ascii, *binary; | |
1584 | int off = 0; | |
1585 | ||
1586 | /* Data area must contain at least a struct ng_mesg + '\0' */ | |
1587 | ascii = (struct ng_mesg *)msg->data; | |
1588 | if (msg->header.arglen < sizeof(*ascii) + 1 | |
1589 | || ascii->header.arglen < 1 | |
1590 | || msg->header.arglen | |
1591 | < sizeof(*ascii) + ascii->header.arglen) { | |
1592 | error = EINVAL; | |
1593 | break; | |
1594 | } | |
1595 | ascii->data[ascii->header.arglen - 1] = '\0'; | |
1596 | ||
1597 | /* Get a response message with lots of room */ | |
1598 | NG_MKRESPONSE(rp, msg, sizeof(*binary) + bufSize, M_NOWAIT); | |
1599 | if (rp == NULL) { | |
1600 | error = ENOMEM; | |
1601 | break; | |
1602 | } | |
1603 | binary = (struct ng_mesg *)rp->data; | |
1604 | ||
1605 | /* Copy ASCII message header to response message payload */ | |
1606 | bcopy(ascii, binary, sizeof(*ascii)); | |
1607 | ||
1608 | /* Find command by matching ASCII command string */ | |
1609 | for (c = here->type->cmdlist; | |
1610 | c != NULL && c->name != NULL; c++) { | |
1611 | if (strcmp(ascii->header.cmdstr, c->name) == 0) | |
1612 | break; | |
1613 | } | |
1614 | if (c == NULL || c->name == NULL) { | |
1615 | for (c = ng_generic_cmds; c->name != NULL; c++) { | |
1616 | if (strcmp(ascii->header.cmdstr, c->name) == 0) | |
1617 | break; | |
1618 | } | |
1619 | if (c->name == NULL) { | |
884717e1 | 1620 | kfree(rp, M_NETGRAPH); |
984263bc MD |
1621 | error = ENOSYS; |
1622 | break; | |
1623 | } | |
1624 | } | |
1625 | ||
1626 | /* Convert command name to binary */ | |
1627 | binary->header.cmd = c->cmd; | |
1628 | binary->header.typecookie = c->cookie; | |
1629 | ||
1630 | /* Convert command arguments to binary */ | |
1631 | argstype = (binary->header.flags & NGF_RESP) ? | |
1632 | c->respType : c->mesgType; | |
1633 | if (argstype == NULL) | |
1634 | bufSize = 0; | |
1635 | else { | |
1636 | if ((error = ng_parse(argstype, ascii->data, | |
1637 | &off, (u_char *)binary->data, &bufSize)) != 0) { | |
884717e1 | 1638 | kfree(rp, M_NETGRAPH); |
984263bc MD |
1639 | break; |
1640 | } | |
1641 | } | |
1642 | ||
1643 | /* Return the result */ | |
1644 | binary->header.arglen = bufSize; | |
1645 | rp->header.arglen = sizeof(*binary) + bufSize; | |
1646 | *resp = rp; | |
1647 | break; | |
1648 | } | |
1649 | ||
1650 | case NGM_TEXT_CONFIG: | |
1651 | case NGM_TEXT_STATUS: | |
1652 | /* | |
1653 | * This one is tricky as it passes the command down to the | |
1654 | * actual node, even though it is a generic type command. | |
1655 | * This means we must assume that the msg is already freed | |
1656 | * when control passes back to us. | |
1657 | */ | |
1658 | if (resp == NULL) { | |
1659 | error = EINVAL; | |
1660 | break; | |
1661 | } | |
1662 | if (here->type->rcvmsg != NULL) | |
1663 | return((*here->type->rcvmsg)(here, msg, retaddr, resp)); | |
1664 | /* Fall through if rcvmsg not supported */ | |
1665 | default: | |
1666 | TRAP_ERROR; | |
1667 | error = EINVAL; | |
1668 | } | |
884717e1 | 1669 | kfree(msg, M_NETGRAPH); |
984263bc MD |
1670 | return (error); |
1671 | } | |
1672 | ||
1673 | /* | |
1674 | * Send a data packet to a node. If the recipient has no | |
1675 | * 'receive data' method, then silently discard the packet. | |
1676 | */ | |
1677 | int | |
1678 | ng_send_data(hook_p hook, struct mbuf *m, meta_p meta) | |
1679 | { | |
1680 | int (*rcvdata)(hook_p, struct mbuf *, meta_p); | |
1681 | int error; | |
1682 | ||
1683 | CHECK_DATA_MBUF(m); | |
1684 | if (hook && (hook->flags & HK_INVALID) == 0) { | |
1685 | rcvdata = hook->peer->node->type->rcvdata; | |
1686 | if (rcvdata != NULL) | |
1687 | error = (*rcvdata)(hook->peer, m, meta); | |
1688 | else { | |
1689 | error = 0; | |
1690 | NG_FREE_DATA(m, meta); | |
1691 | } | |
1692 | } else { | |
1693 | TRAP_ERROR; | |
1694 | error = ENOTCONN; | |
1695 | NG_FREE_DATA(m, meta); | |
1696 | } | |
1697 | return (error); | |
1698 | } | |
1699 | ||
1700 | /* | |
1701 | * Send a queued data packet to a node. If the recipient has no | |
1702 | * 'receive queued data' method, then try the 'receive data' method above. | |
1703 | */ | |
1704 | int | |
1705 | ng_send_dataq(hook_p hook, struct mbuf *m, meta_p meta) | |
1706 | { | |
1707 | int (*rcvdataq)(hook_p, struct mbuf *, meta_p); | |
1708 | int error; | |
1709 | ||
1710 | CHECK_DATA_MBUF(m); | |
1711 | if (hook && (hook->flags & HK_INVALID) == 0) { | |
1712 | rcvdataq = hook->peer->node->type->rcvdataq; | |
1713 | if (rcvdataq != NULL) | |
1714 | error = (*rcvdataq)(hook->peer, m, meta); | |
1715 | else { | |
1716 | error = ng_send_data(hook, m, meta); | |
1717 | } | |
1718 | } else { | |
1719 | TRAP_ERROR; | |
1720 | error = ENOTCONN; | |
1721 | NG_FREE_DATA(m, meta); | |
1722 | } | |
1723 | return (error); | |
1724 | } | |
1725 | ||
1726 | /* | |
1727 | * Copy a 'meta'. | |
1728 | * | |
1729 | * Returns new meta, or NULL if original meta is NULL or ENOMEM. | |
1730 | */ | |
1731 | meta_p | |
1732 | ng_copy_meta(meta_p meta) | |
1733 | { | |
1734 | meta_p meta2; | |
1735 | ||
1736 | if (meta == NULL) | |
1737 | return (NULL); | |
884717e1 | 1738 | meta2 = kmalloc(meta->used_len, M_NETGRAPH, M_NOWAIT); |
984263bc MD |
1739 | if (meta2 == NULL) |
1740 | return (NULL); | |
1741 | meta2->allocated_len = meta->used_len; | |
1742 | bcopy(meta, meta2, meta->used_len); | |
1743 | return (meta2); | |
1744 | } | |
1745 | ||
1746 | /************************************************************************ | |
1747 | Module routines | |
1748 | ************************************************************************/ | |
1749 | ||
1750 | /* | |
1751 | * Handle the loading/unloading of a netgraph node type module | |
1752 | */ | |
1753 | int | |
1754 | ng_mod_event(module_t mod, int event, void *data) | |
1755 | { | |
1756 | struct ng_type *const type = data; | |
41e90fed | 1757 | int error = 0; |
984263bc MD |
1758 | |
1759 | switch (event) { | |
1760 | case MOD_LOAD: | |
1761 | ||
1762 | /* Register new netgraph node type */ | |
41e90fed | 1763 | crit_enter(); |
984263bc | 1764 | if ((error = ng_newtype(type)) != 0) { |
41e90fed | 1765 | crit_exit(); |
984263bc MD |
1766 | break; |
1767 | } | |
1768 | ||
1769 | /* Call type specific code */ | |
1770 | if (type->mod_event != NULL) | |
1771 | if ((error = (*type->mod_event)(mod, event, data))) { | |
1772 | type->refs--; /* undo it */ | |
1773 | LIST_REMOVE(type, types); | |
1774 | } | |
41e90fed | 1775 | crit_exit(); |
984263bc MD |
1776 | break; |
1777 | ||
1778 | case MOD_UNLOAD: | |
41e90fed | 1779 | crit_enter(); |
984263bc MD |
1780 | if (type->refs > 1) { /* make sure no nodes exist! */ |
1781 | error = EBUSY; | |
1782 | } else { | |
1783 | if (type->refs == 0) { | |
1784 | /* failed load, nothing to undo */ | |
41e90fed | 1785 | crit_exit(); |
984263bc MD |
1786 | break; |
1787 | } | |
1788 | if (type->mod_event != NULL) { /* check with type */ | |
1789 | error = (*type->mod_event)(mod, event, data); | |
1790 | if (error != 0) { /* type refuses.. */ | |
41e90fed | 1791 | crit_exit(); |
984263bc MD |
1792 | break; |
1793 | } | |
1794 | } | |
1795 | LIST_REMOVE(type, types); | |
1796 | } | |
41e90fed | 1797 | crit_exit(); |
984263bc MD |
1798 | break; |
1799 | ||
1800 | default: | |
1801 | if (type->mod_event != NULL) | |
1802 | error = (*type->mod_event)(mod, event, data); | |
1803 | else | |
1804 | error = 0; /* XXX ? */ | |
1805 | break; | |
1806 | } | |
1807 | return (error); | |
1808 | } | |
1809 | ||
1810 | /* | |
1811 | * Handle loading and unloading for this code. | |
1812 | * The only thing we need to link into is the NETISR strucure. | |
1813 | */ | |
1814 | static int | |
1815 | ngb_mod_event(module_t mod, int event, void *data) | |
1816 | { | |
41e90fed | 1817 | int error = 0; |
984263bc MD |
1818 | |
1819 | switch (event) { | |
1820 | case MOD_LOAD: | |
1821 | /* Register line discipline */ | |
41e90fed | 1822 | crit_enter(); |
c3c96e44 | 1823 | netisr_register(NETISR_NETGRAPH, ngintr, NULL); |
41e90fed | 1824 | crit_exit(); |
984263bc MD |
1825 | break; |
1826 | case MOD_UNLOAD: | |
1827 | /* You cant unload it because an interface may be using it. */ | |
1828 | error = EBUSY; | |
1829 | break; | |
1830 | default: | |
1831 | error = EOPNOTSUPP; | |
1832 | break; | |
1833 | } | |
1834 | return (error); | |
1835 | } | |
1836 | ||
1837 | static moduledata_t netgraph_mod = { | |
1838 | "netgraph", | |
1839 | ngb_mod_event, | |
1840 | (NULL) | |
1841 | }; | |
1842 | DECLARE_MODULE(netgraph, netgraph_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); | |
1843 | SYSCTL_NODE(_net, OID_AUTO, graph, CTLFLAG_RW, 0, "netgraph Family"); | |
1844 | SYSCTL_INT(_net_graph, OID_AUTO, abi_version, CTLFLAG_RD, 0, NG_ABI_VERSION,""); | |
1845 | SYSCTL_INT(_net_graph, OID_AUTO, msg_version, CTLFLAG_RD, 0, NG_VERSION, ""); | |
1846 | ||
1847 | /************************************************************************ | |
1848 | Queueing routines | |
1849 | ************************************************************************/ | |
1850 | ||
1851 | /* The structure for queueing across ISR switches */ | |
1852 | struct ng_queue_entry { | |
1853 | u_long flags; | |
1854 | struct ng_queue_entry *next; | |
1855 | union { | |
1856 | struct { | |
1857 | hook_p da_hook; /* target hook */ | |
1858 | struct mbuf *da_m; | |
1859 | meta_p da_meta; | |
1860 | } data; | |
1861 | struct { | |
1862 | struct ng_mesg *msg_msg; | |
1863 | node_p msg_node; | |
1864 | void *msg_retaddr; | |
1865 | } msg; | |
1866 | } body; | |
1867 | }; | |
1868 | #define NGQF_DATA 0x01 /* the queue element is data */ | |
1869 | #define NGQF_MESG 0x02 /* the queue element is a message */ | |
1870 | ||
1871 | static struct ng_queue_entry *ngqbase; /* items to be unqueued */ | |
1872 | static struct ng_queue_entry *ngqlast; /* last item queued */ | |
1873 | static const int ngqroom = 256; /* max items to queue */ | |
1874 | static int ngqsize; /* number of items in queue */ | |
1875 | ||
1876 | static struct ng_queue_entry *ngqfree; /* free ones */ | |
1877 | static const int ngqfreemax = 256;/* cache at most this many */ | |
1878 | static int ngqfreesize; /* number of cached entries */ | |
1879 | ||
1880 | /* | |
1881 | * Get a queue entry | |
1882 | */ | |
1883 | static struct ng_queue_entry * | |
1884 | ng_getqblk(void) | |
1885 | { | |
5e748529 | 1886 | struct ng_queue_entry *q; |
984263bc MD |
1887 | |
1888 | /* Could be guarding against tty ints or whatever */ | |
41e90fed | 1889 | crit_enter(); |
984263bc MD |
1890 | |
1891 | /* Try get a cached queue block, or else allocate a new one */ | |
1892 | if ((q = ngqfree) == NULL) { | |
41e90fed | 1893 | crit_exit(); |
984263bc | 1894 | if (ngqsize < ngqroom) { /* don't worry about races */ |
884717e1 | 1895 | q = kmalloc(sizeof(*q), M_NETGRAPH, M_NOWAIT); |
984263bc MD |
1896 | } |
1897 | } else { | |
1898 | ngqfree = q->next; | |
1899 | ngqfreesize--; | |
41e90fed | 1900 | crit_exit(); |
984263bc MD |
1901 | } |
1902 | return (q); | |
1903 | } | |
1904 | ||
1905 | /* | |
1906 | * Release a queue entry | |
1907 | */ | |
1908 | #define RETURN_QBLK(q) \ | |
1909 | do { \ | |
984263bc | 1910 | if (ngqfreesize < ngqfreemax) { /* don't worry about races */ \ |
41e90fed | 1911 | crit_enter(); \ |
984263bc MD |
1912 | (q)->next = ngqfree; \ |
1913 | ngqfree = (q); \ | |
1914 | ngqfreesize++; \ | |
41e90fed | 1915 | crit_exit(); \ |
984263bc | 1916 | } else { \ |
884717e1 | 1917 | kfree((q), M_NETGRAPH); \ |
984263bc MD |
1918 | } \ |
1919 | } while (0) | |
1920 | ||
1921 | /* | |
1922 | * Running at a raised (but we don't know which) processor priority level, | |
1923 | * put the data onto a queue to be picked up by another PPL (probably splnet) | |
1924 | */ | |
1925 | int | |
1926 | ng_queue_data(hook_p hook, struct mbuf *m, meta_p meta) | |
1927 | { | |
1928 | struct ng_queue_entry *q; | |
984263bc MD |
1929 | |
1930 | if (hook == NULL) { | |
1931 | NG_FREE_DATA(m, meta); | |
1932 | return (0); | |
1933 | } | |
1934 | if ((q = ng_getqblk()) == NULL) { | |
1935 | NG_FREE_DATA(m, meta); | |
1936 | return (ENOBUFS); | |
1937 | } | |
1938 | ||
1939 | /* Fill out the contents */ | |
1940 | q->flags = NGQF_DATA; | |
1941 | q->next = NULL; | |
1942 | q->body.data.da_hook = hook; | |
1943 | q->body.data.da_m = m; | |
1944 | q->body.data.da_meta = meta; | |
41e90fed | 1945 | crit_enter(); /* protect refs and queue */ |
984263bc MD |
1946 | hook->refs++; /* don't let it go away while on the queue */ |
1947 | ||
1948 | /* Put it on the queue */ | |
1949 | if (ngqbase) { | |
1950 | ngqlast->next = q; | |
1951 | } else { | |
1952 | ngqbase = q; | |
1953 | } | |
1954 | ngqlast = q; | |
1955 | ngqsize++; | |
41e90fed | 1956 | crit_exit(); |
984263bc MD |
1957 | |
1958 | /* Schedule software interrupt to handle it later */ | |
1959 | schednetisr(NETISR_NETGRAPH); | |
1960 | return (0); | |
1961 | } | |
1962 | ||
1963 | /* | |
1964 | * Running at a raised (but we don't know which) processor priority level, | |
1965 | * put the msg onto a queue to be picked up by another PPL (probably splnet) | |
1966 | */ | |
1967 | int | |
1968 | ng_queue_msg(node_p here, struct ng_mesg *msg, const char *address) | |
1969 | { | |
5e748529 | 1970 | struct ng_queue_entry *q; |
984263bc MD |
1971 | node_p dest = NULL; |
1972 | char *retaddr = NULL; | |
1973 | int error; | |
1974 | ||
1975 | /* Find the target node. */ | |
1976 | error = ng_path2node(here, address, &dest, &retaddr); | |
1977 | if (error) { | |
884717e1 | 1978 | kfree(msg, M_NETGRAPH); |
984263bc MD |
1979 | return (error); |
1980 | } | |
1981 | if ((q = ng_getqblk()) == NULL) { | |
884717e1 | 1982 | kfree(msg, M_NETGRAPH); |
984263bc | 1983 | if (retaddr) |
884717e1 | 1984 | kfree(retaddr, M_NETGRAPH); |
984263bc MD |
1985 | return (ENOBUFS); |
1986 | } | |
1987 | ||
1988 | /* Fill out the contents */ | |
1989 | q->flags = NGQF_MESG; | |
1990 | q->next = NULL; | |
1991 | q->body.msg.msg_node = dest; | |
1992 | q->body.msg.msg_msg = msg; | |
1993 | q->body.msg.msg_retaddr = retaddr; | |
41e90fed | 1994 | crit_enter(); /* protect refs and queue */ |
984263bc MD |
1995 | dest->refs++; /* don't let it go away while on the queue */ |
1996 | ||
1997 | /* Put it on the queue */ | |
1998 | if (ngqbase) { | |
1999 | ngqlast->next = q; | |
2000 | } else { | |
2001 | ngqbase = q; | |
2002 | } | |
2003 | ngqlast = q; | |
2004 | ngqsize++; | |
41e90fed | 2005 | crit_exit(); |
984263bc MD |
2006 | |
2007 | /* Schedule software interrupt to handle it later */ | |
2008 | schednetisr(NETISR_NETGRAPH); | |
2009 | return (0); | |
2010 | } | |
2011 | ||
2012 | /* | |
2013 | * Pick an item off the queue, process it, and dispose of the queue entry. | |
984263bc | 2014 | */ |
4599cf19 | 2015 | static void |
002c1265 | 2016 | ngintr(netmsg_t pmsg) |
984263bc MD |
2017 | { |
2018 | hook_p hook; | |
2682b36e | 2019 | struct mbuf *m; |
984263bc | 2020 | struct ng_queue_entry *ngq; |
984263bc MD |
2021 | meta_p meta; |
2022 | void *retaddr; | |
2023 | struct ng_mesg *msg; | |
2024 | node_p node; | |
2025 | int error = 0; | |
984263bc | 2026 | |
2682b36e MD |
2027 | /* |
2028 | * Packets are never sent to this netisr so the message must always | |
2029 | * be replied. Interlock processing and notification by replying | |
2030 | * the message first. | |
2031 | */ | |
002c1265 | 2032 | lwkt_replymsg(&pmsg->lmsg, 0); |
2682b36e | 2033 | |
c3c96e44 MD |
2034 | get_mplock(); |
2035 | ||
984263bc | 2036 | while (1) { |
41e90fed | 2037 | crit_enter(); |
984263bc MD |
2038 | if ((ngq = ngqbase)) { |
2039 | ngqbase = ngq->next; | |
2040 | ngqsize--; | |
2041 | } | |
41e90fed | 2042 | crit_exit(); |
984263bc | 2043 | if (ngq == NULL) |
a80cf23b | 2044 | goto out; |
984263bc MD |
2045 | switch (ngq->flags) { |
2046 | case NGQF_DATA: | |
2047 | hook = ngq->body.data.da_hook; | |
2048 | m = ngq->body.data.da_m; | |
2049 | meta = ngq->body.data.da_meta; | |
2050 | RETURN_QBLK(ngq); | |
2051 | NG_SEND_DATAQ(error, hook, m, meta); | |
2052 | ng_unref_hook(hook); | |
2053 | break; | |
2054 | case NGQF_MESG: | |
2055 | node = ngq->body.msg.msg_node; | |
2056 | msg = ngq->body.msg.msg_msg; | |
2057 | retaddr = ngq->body.msg.msg_retaddr; | |
2058 | RETURN_QBLK(ngq); | |
2059 | if (node->flags & NG_INVALID) { | |
884717e1 | 2060 | kfree(msg, M_NETGRAPH); |
984263bc MD |
2061 | } else { |
2062 | CALL_MSG_HANDLER(error, node, msg, | |
2063 | retaddr, NULL); | |
2064 | } | |
2065 | ng_unref(node); | |
2066 | if (retaddr) | |
884717e1 | 2067 | kfree(retaddr, M_NETGRAPH); |
984263bc MD |
2068 | break; |
2069 | default: | |
2070 | RETURN_QBLK(ngq); | |
2071 | } | |
2072 | } | |
a80cf23b | 2073 | out: |
c3c96e44 | 2074 | rel_mplock(); |
984263bc MD |
2075 | } |
2076 | ||
2077 |