]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/netgraph/ng_bridge.c
netgraph/ng_bridge: Make simple internal functions read-only
[FreeBSD/FreeBSD.git] / sys / netgraph / ng_bridge.c
1 /*-
2  * Copyright (c) 2000 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  * Author: Archie Cobbs <archie@freebsd.org>
35  *
36  * $FreeBSD$
37  */
38
39 /*
40  * ng_bridge(4) netgraph node type
41  *
42  * The node performs standard intelligent Ethernet bridging over
43  * each of its connected hooks, or links.  A simple loop detection
44  * algorithm is included which disables a link for priv->conf.loopTimeout
45  * seconds when a host is seen to have jumped from one link to
46  * another within priv->conf.minStableAge seconds.
47  *
48  * We keep a hashtable that maps Ethernet addresses to host info,
49  * which is contained in struct ng_bridge_host's. These structures
50  * tell us on which link the host may be found. A host's entry will
51  * expire after priv->conf.maxStaleness seconds.
52  *
53  * This node is optimzed for stable networks, where machines jump
54  * from one port to the other only rarely.
55  */
56
57 #include <sys/param.h>
58 #include <sys/systm.h>
59 #include <sys/kernel.h>
60 #include <sys/lock.h>
61 #include <sys/malloc.h>
62 #include <sys/mbuf.h>
63 #include <sys/errno.h>
64 #include <sys/rwlock.h>
65 #include <sys/syslog.h>
66 #include <sys/socket.h>
67 #include <sys/ctype.h>
68 #include <sys/types.h>
69 #include <sys/counter.h>
70
71 #include <net/if.h>
72 #include <net/if_var.h>
73 #include <net/ethernet.h>
74 #include <net/vnet.h>
75
76 #include <netinet/in.h>
77 #if 0   /* not used yet */
78 #include <netinet/ip_fw.h>
79 #endif
80 #include <netgraph/ng_message.h>
81 #include <netgraph/netgraph.h>
82 #include <netgraph/ng_parse.h>
83 #include <netgraph/ng_bridge.h>
84
85 #ifdef NG_SEPARATE_MALLOC
86 static MALLOC_DEFINE(M_NETGRAPH_BRIDGE, "netgraph_bridge",
87     "netgraph bridge node");
88 #else
89 #define M_NETGRAPH_BRIDGE M_NETGRAPH
90 #endif
91
92 /* Counter based stats */
93 struct ng_bridge_link_kernel_stats {
94         counter_u64_t   recvOctets;     /* total octets rec'd on link */
95         counter_u64_t   recvPackets;    /* total pkts rec'd on link */
96         counter_u64_t   recvMulticasts; /* multicast pkts rec'd on link */
97         counter_u64_t   recvBroadcasts; /* broadcast pkts rec'd on link */
98         counter_u64_t   recvUnknown;    /* pkts rec'd with unknown dest addr */
99         counter_u64_t   recvRunts;      /* pkts rec'd less than 14 bytes */
100         counter_u64_t   recvInvalid;    /* pkts rec'd with bogus source addr */
101         counter_u64_t   xmitOctets;     /* total octets xmit'd on link */
102         counter_u64_t   xmitPackets;    /* total pkts xmit'd on link */
103         counter_u64_t   xmitMulticasts; /* multicast pkts xmit'd on link */
104         counter_u64_t   xmitBroadcasts; /* broadcast pkts xmit'd on link */
105         counter_u64_t   loopDrops;      /* pkts dropped due to loopback */
106         counter_u64_t   loopDetects;    /* number of loop detections */
107         counter_u64_t   memoryFailures; /* times couldn't get mem or mbuf */
108 };
109
110 /* Per-link private data */
111 struct ng_bridge_link {
112         hook_p                          hook;           /* netgraph hook */
113         u_int16_t                       loopCount;      /* loop ignore timer */
114         unsigned int                    learnMac : 1,   /* autolearn macs */
115                                         sendUnknown : 1;/* send unknown macs out */
116         struct ng_bridge_link_kernel_stats stats;       /* link stats */
117 };
118 typedef struct ng_bridge_link const *link_cp;   /* read only access */
119
120 /* Per-node private data */
121 struct ng_bridge_private {
122         struct ng_bridge_bucket *tab;           /* hash table bucket array */
123         struct ng_bridge_config conf;           /* node configuration */
124         node_p                  node;           /* netgraph node */
125         u_int                   numHosts;       /* num entries in table */
126         u_int                   numBuckets;     /* num buckets in table */
127         u_int                   hashMask;       /* numBuckets - 1 */
128         int                     numLinks;       /* num connected links */
129         unsigned int            persistent : 1, /* can exist w/o hooks */
130                                 sendUnknown : 1;/* links receive unknowns by default */
131         struct callout          timer;          /* one second periodic timer */
132 };
133 typedef struct ng_bridge_private *priv_p;
134 typedef struct ng_bridge_private const *priv_cp;        /* read only access */
135
136 /* Information about a host, stored in a hash table entry */
137 struct ng_bridge_hent {
138         struct ng_bridge_host           host;   /* actual host info */
139         SLIST_ENTRY(ng_bridge_hent)     next;   /* next entry in bucket */
140 };
141
142 /* Hash table bucket declaration */
143 SLIST_HEAD(ng_bridge_bucket, ng_bridge_hent);
144
145 /* Netgraph node methods */
146 static ng_constructor_t ng_bridge_constructor;
147 static ng_rcvmsg_t      ng_bridge_rcvmsg;
148 static ng_shutdown_t    ng_bridge_shutdown;
149 static ng_newhook_t     ng_bridge_newhook;
150 static ng_rcvdata_t     ng_bridge_rcvdata;
151 static ng_disconnect_t  ng_bridge_disconnect;
152
153 /* Other internal functions */
154 static struct   ng_bridge_host *ng_bridge_get(priv_cp priv, const u_char *addr);
155 static int      ng_bridge_put(priv_p priv, const u_char *addr, link_p link);
156 static void     ng_bridge_rehash(priv_p priv);
157 static void     ng_bridge_remove_hosts(priv_p priv, link_p link);
158 static void     ng_bridge_timeout(node_p node, hook_p hook, void *arg1, int arg2);
159 static const    char *ng_bridge_nodename(node_cp node);
160
161 /* Ethernet broadcast */
162 static const u_char ng_bridge_bcast_addr[ETHER_ADDR_LEN] =
163     { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
164
165 /* Compare Ethernet addresses using 32 and 16 bit words instead of bytewise */
166 #define ETHER_EQUAL(a,b)        (((const u_int32_t *)(a))[0] \
167                                         == ((const u_int32_t *)(b))[0] \
168                                     && ((const u_int16_t *)(a))[2] \
169                                         == ((const u_int16_t *)(b))[2])
170
171 /* Minimum and maximum number of hash buckets. Must be a power of two. */
172 #define MIN_BUCKETS             (1 << 5)        /* 32 */
173 #define MAX_BUCKETS             (1 << 14)       /* 16384 */
174
175 /* Configuration default values */
176 #define DEFAULT_LOOP_TIMEOUT    60
177 #define DEFAULT_MAX_STALENESS   (15 * 60)       /* same as ARP timeout */
178 #define DEFAULT_MIN_STABLE_AGE  1
179
180 /******************************************************************
181                     NETGRAPH PARSE TYPES
182 ******************************************************************/
183
184 /*
185  * How to determine the length of the table returned by NGM_BRIDGE_GET_TABLE
186  */
187 static int
188 ng_bridge_getTableLength(const struct ng_parse_type *type,
189         const u_char *start, const u_char *buf)
190 {
191         const struct ng_bridge_host_ary *const hary
192             = (const struct ng_bridge_host_ary *)(buf - sizeof(u_int32_t));
193
194         return hary->numHosts;
195 }
196
197 /* Parse type for struct ng_bridge_host_ary */
198 static const struct ng_parse_struct_field ng_bridge_host_type_fields[]
199         = NG_BRIDGE_HOST_TYPE_INFO(&ng_parse_enaddr_type);
200 static const struct ng_parse_type ng_bridge_host_type = {
201         &ng_parse_struct_type,
202         &ng_bridge_host_type_fields
203 };
204 static const struct ng_parse_array_info ng_bridge_hary_type_info = {
205         &ng_bridge_host_type,
206         ng_bridge_getTableLength
207 };
208 static const struct ng_parse_type ng_bridge_hary_type = {
209         &ng_parse_array_type,
210         &ng_bridge_hary_type_info
211 };
212 static const struct ng_parse_struct_field ng_bridge_host_ary_type_fields[]
213         = NG_BRIDGE_HOST_ARY_TYPE_INFO(&ng_bridge_hary_type);
214 static const struct ng_parse_type ng_bridge_host_ary_type = {
215         &ng_parse_struct_type,
216         &ng_bridge_host_ary_type_fields
217 };
218
219 /* Parse type for struct ng_bridge_config */
220 static const struct ng_parse_struct_field ng_bridge_config_type_fields[]
221         = NG_BRIDGE_CONFIG_TYPE_INFO;
222 static const struct ng_parse_type ng_bridge_config_type = {
223         &ng_parse_struct_type,
224         &ng_bridge_config_type_fields
225 };
226
227 /* Parse type for struct ng_bridge_link_stat */
228 static const struct ng_parse_struct_field ng_bridge_stats_type_fields[]
229         = NG_BRIDGE_STATS_TYPE_INFO;
230 static const struct ng_parse_type ng_bridge_stats_type = {
231         &ng_parse_struct_type,
232         &ng_bridge_stats_type_fields
233 };
234
235 /* List of commands and how to convert arguments to/from ASCII */
236 static const struct ng_cmdlist ng_bridge_cmdlist[] = {
237         {
238           NGM_BRIDGE_COOKIE,
239           NGM_BRIDGE_SET_CONFIG,
240           "setconfig",
241           &ng_bridge_config_type,
242           NULL
243         },
244         {
245           NGM_BRIDGE_COOKIE,
246           NGM_BRIDGE_GET_CONFIG,
247           "getconfig",
248           NULL,
249           &ng_bridge_config_type
250         },
251         {
252           NGM_BRIDGE_COOKIE,
253           NGM_BRIDGE_RESET,
254           "reset",
255           NULL,
256           NULL
257         },
258         {
259           NGM_BRIDGE_COOKIE,
260           NGM_BRIDGE_GET_STATS,
261           "getstats",
262           &ng_parse_uint32_type,
263           &ng_bridge_stats_type
264         },
265         {
266           NGM_BRIDGE_COOKIE,
267           NGM_BRIDGE_CLR_STATS,
268           "clrstats",
269           &ng_parse_uint32_type,
270           NULL
271         },
272         {
273           NGM_BRIDGE_COOKIE,
274           NGM_BRIDGE_GETCLR_STATS,
275           "getclrstats",
276           &ng_parse_uint32_type,
277           &ng_bridge_stats_type
278         },
279         {
280           NGM_BRIDGE_COOKIE,
281           NGM_BRIDGE_GET_TABLE,
282           "gettable",
283           NULL,
284           &ng_bridge_host_ary_type
285         },
286         {
287           NGM_BRIDGE_COOKIE,
288           NGM_BRIDGE_SET_PERSISTENT,
289           "setpersistent",
290           NULL,
291           NULL
292         },
293         { 0 }
294 };
295
296 /* Node type descriptor */
297 static struct ng_type ng_bridge_typestruct = {
298         .version =      NG_ABI_VERSION,
299         .name =         NG_BRIDGE_NODE_TYPE,
300         .constructor =  ng_bridge_constructor,
301         .rcvmsg =       ng_bridge_rcvmsg,
302         .shutdown =     ng_bridge_shutdown,
303         .newhook =      ng_bridge_newhook,
304         .rcvdata =      ng_bridge_rcvdata,
305         .disconnect =   ng_bridge_disconnect,
306         .cmdlist =      ng_bridge_cmdlist,
307 };
308 NETGRAPH_INIT(bridge, &ng_bridge_typestruct);
309
310 /******************************************************************
311                     NETGRAPH NODE METHODS
312 ******************************************************************/
313
314 /*
315  * Node constructor
316  */
317 static int
318 ng_bridge_constructor(node_p node)
319 {
320         priv_p priv;
321
322         /* Allocate and initialize private info */
323         priv = malloc(sizeof(*priv), M_NETGRAPH_BRIDGE, M_WAITOK | M_ZERO);
324         ng_callout_init(&priv->timer);
325
326         /* Allocate and initialize hash table, etc. */
327         priv->tab = malloc(MIN_BUCKETS * sizeof(*priv->tab),
328             M_NETGRAPH_BRIDGE, M_WAITOK | M_ZERO);
329         priv->numBuckets = MIN_BUCKETS;
330         priv->hashMask = MIN_BUCKETS - 1;
331         priv->conf.debugLevel = 1;
332         priv->conf.loopTimeout = DEFAULT_LOOP_TIMEOUT;
333         priv->conf.maxStaleness = DEFAULT_MAX_STALENESS;
334         priv->conf.minStableAge = DEFAULT_MIN_STABLE_AGE;
335         priv->sendUnknown = 1;         /* classic bridge */
336
337         /*
338          * This node has all kinds of stuff that could be screwed by SMP.
339          * Until it gets it's own internal protection, we go through in 
340          * single file. This could hurt a machine bridging between two 
341          * GB ethernets so it should be fixed. 
342          * When it's fixed the process SHOULD NOT SLEEP, spinlocks please!
343          * (and atomic ops )
344          */
345         NG_NODE_FORCE_WRITER(node);
346         NG_NODE_SET_PRIVATE(node, priv);
347         priv->node = node;
348
349         /* Start timer; timer is always running while node is alive */
350         ng_callout(&priv->timer, node, NULL, hz, ng_bridge_timeout, NULL, 0);
351
352         /* Done */
353         return (0);
354 }
355
356 /*
357  * Method for attaching a new hook
358  */
359 static  int
360 ng_bridge_newhook(node_p node, hook_p hook, const char *name)
361 {
362         const priv_p priv = NG_NODE_PRIVATE(node);
363         char linkName[NG_HOOKSIZ];
364         u_int32_t linkNum;
365         link_p link;
366         const char *prefix = NG_BRIDGE_HOOK_LINK_PREFIX;
367         bool isUplink;
368
369         /* Check for a link hook */
370         if (strlen(name) <= strlen(prefix))
371                 return (EINVAL);       /* Unknown hook name */
372
373         isUplink = (name[0] == 'u');
374         if (isUplink)
375                 prefix = NG_BRIDGE_HOOK_UPLINK_PREFIX;
376
377         /* primitive parsing */
378         linkNum = strtoul(name + strlen(prefix), NULL, 10);
379         /* validation by comparing against the reconstucted name  */
380         snprintf(linkName, sizeof(linkName), "%s%u", prefix, linkNum);
381         if (strcmp(linkName, name) != 0)
382                 return (EINVAL);
383
384         if (linkNum == 0 && isUplink)
385                 return (EINVAL);
386
387         if(NG_PEER_NODE(hook) == node)
388                 return (ELOOP);
389
390         link = malloc(sizeof(*link), M_NETGRAPH_BRIDGE, M_ZERO);
391         if (link == NULL)
392                 return (ENOMEM);
393
394         link->stats.recvOctets = counter_u64_alloc(M_WAITOK);
395         link->stats.recvPackets = counter_u64_alloc(M_WAITOK);
396         link->stats.recvMulticasts = counter_u64_alloc(M_WAITOK);
397         link->stats.recvBroadcasts = counter_u64_alloc(M_WAITOK);
398         link->stats.recvUnknown = counter_u64_alloc(M_WAITOK);
399         link->stats.recvRunts = counter_u64_alloc(M_WAITOK);
400         link->stats.recvInvalid = counter_u64_alloc(M_WAITOK);
401         link->stats.xmitOctets = counter_u64_alloc(M_WAITOK);
402         link->stats.xmitPackets = counter_u64_alloc(M_WAITOK);
403         link->stats.xmitMulticasts = counter_u64_alloc(M_WAITOK);
404         link->stats.xmitBroadcasts = counter_u64_alloc(M_WAITOK);
405         link->stats.loopDrops = counter_u64_alloc(M_WAITOK);
406         link->stats.loopDetects = counter_u64_alloc(M_WAITOK);
407         link->stats.memoryFailures = counter_u64_alloc(M_WAITOK);
408
409         link->hook = hook;
410         if (isUplink) {
411                 link->learnMac = 0;
412                 link->sendUnknown = 1;
413                 if (priv->numLinks == 0)        /* if the first link is an uplink */
414                     priv->sendUnknown = 0;      /* switch to restrictive mode */
415         } else {
416                 link->learnMac = 1;
417                 link->sendUnknown = priv->sendUnknown;
418         }
419
420         NG_HOOK_SET_PRIVATE(hook, link);
421         priv->numLinks++;
422         return (0);
423 }
424
425 /*
426  * Receive a control message
427  */
428 static void ng_bridge_clear_link_stats(struct ng_bridge_link_kernel_stats * p)
429 {
430         counter_u64_zero(p->recvOctets);
431         counter_u64_zero(p->recvPackets);
432         counter_u64_zero(p->recvMulticasts);
433         counter_u64_zero(p->recvBroadcasts);
434         counter_u64_zero(p->recvUnknown);
435         counter_u64_zero(p->recvRunts);
436         counter_u64_zero(p->recvInvalid);
437         counter_u64_zero(p->xmitOctets);
438         counter_u64_zero(p->xmitPackets);
439         counter_u64_zero(p->xmitMulticasts);
440         counter_u64_zero(p->xmitBroadcasts);
441         counter_u64_zero(p->loopDrops);
442         counter_u64_zero(p->loopDetects);
443         counter_u64_zero(p->memoryFailures);
444 };
445
446 static int
447 ng_bridge_reset_link(hook_p hook, void *arg __unused)
448 {
449         link_p priv = NG_HOOK_PRIVATE(hook);
450
451         priv->loopCount = 0;
452         ng_bridge_clear_link_stats(&priv->stats);
453         return (1);
454 }
455
456 static int
457 ng_bridge_rcvmsg(node_p node, item_p item, hook_p lasthook)
458 {
459         const priv_p priv = NG_NODE_PRIVATE(node);
460         struct ng_mesg *resp = NULL;
461         int error = 0;
462         struct ng_mesg *msg;
463
464         NGI_GET_MSG(item, msg);
465         switch (msg->header.typecookie) {
466 #ifdef NGM_BRIDGE_TABLE_ABI
467         case NGM_BRIDGE_COOKIE_TBL:
468                 switch (msg->header.cmd) {
469                 case NGM_BRIDGE_GET_CONFIG:
470                     {
471                         struct ng_bridge_config_tbl *conf;
472
473                         NG_MKRESPONSE(resp, msg, sizeof(*conf),
474                             M_NOWAIT|M_ZERO);
475                         if (resp == NULL) {
476                                 error = ENOMEM;
477                                 break;
478                         }
479                         conf = (struct ng_bridge_config_tbl *)resp->data;
480                         conf->cfg = priv->conf;
481                         break;
482                     }
483                 case NGM_BRIDGE_SET_CONFIG:
484                     {
485                         struct ng_bridge_config_tbl *conf;
486
487                         if (msg->header.arglen != sizeof(*conf)) {
488                                 error = EINVAL;
489                                 break;
490                         }
491                         conf = (struct ng_bridge_config_tbl *)msg->data;
492                         priv->conf = conf->cfg;
493                         break;
494                     }
495                 case NGM_BRIDGE_GET_TABLE:
496                     {
497                         struct ng_bridge_host_tbl_ary *ary;
498                         struct ng_bridge_hent *hent;
499                         int i, bucket;
500
501                         NG_MKRESPONSE(resp, msg, sizeof(*ary) +
502                             (priv->numHosts * sizeof(*ary->hosts)), M_NOWAIT);
503                         if (resp == NULL) {
504                                 error = ENOMEM;
505                                 break;
506                         }
507                         ary = (struct ng_bridge_host_tbl_ary *)resp->data;
508                         ary->numHosts = priv->numHosts;
509                         i = 0;
510                         for (bucket = 0; bucket < priv->numBuckets; bucket++) {
511                                 SLIST_FOREACH(hent, &priv->tab[bucket], next) {
512                                         const char *name = NG_HOOK_NAME(hent->host.link->hook);
513                                         const char *prefix = name[0] == 'u' ?
514                                             NG_BRIDGE_HOOK_UPLINK_PREFIX :
515                                             NG_BRIDGE_HOOK_LINK_PREFIX;
516
517                                         memcpy(ary->hosts[i].addr,
518                                             hent->host.addr,
519                                             sizeof(ary->hosts[i].addr));
520                                         ary->hosts[i].age = hent->host.age;
521                                         ary->hosts[i].staleness =
522                                              hent->host.staleness;
523                                         ary->hosts[i].linkNum = strtol(
524                                             name + strlen(prefix), NULL, 10);
525                                         i++;
526                                 }
527                         }
528                         break;
529                     }
530                 }
531                 /* If already handled break, otherwise use new ABI. */
532                 if (resp != NULL || error != 0)
533                     break;
534 #endif /* NGM_BRIDGE_TABLE_ABI */
535         case NGM_BRIDGE_COOKIE:
536                 switch (msg->header.cmd) {
537                 case NGM_BRIDGE_GET_CONFIG:
538                     {
539                         struct ng_bridge_config *conf;
540
541                         NG_MKRESPONSE(resp, msg,
542                             sizeof(struct ng_bridge_config), M_NOWAIT);
543                         if (resp == NULL) {
544                                 error = ENOMEM;
545                                 break;
546                         }
547                         conf = (struct ng_bridge_config *)resp->data;
548                         *conf = priv->conf;     /* no sanity checking needed */
549                         break;
550                     }
551                 case NGM_BRIDGE_SET_CONFIG:
552                     {
553                         struct ng_bridge_config *conf;
554
555                         if (msg->header.arglen
556                             != sizeof(struct ng_bridge_config)) {
557                                 error = EINVAL;
558                                 break;
559                         }
560                         conf = (struct ng_bridge_config *)msg->data;
561                         priv->conf = *conf;
562                         break;
563                     }
564                 case NGM_BRIDGE_RESET:
565                     {
566                         hook_p rethook;
567
568                         /* Flush all entries in the hash table */
569                         ng_bridge_remove_hosts(priv, NULL);
570
571                         /* Reset all loop detection counters and stats */
572                         NG_NODE_FOREACH_HOOK(node, ng_bridge_reset_link, NULL,
573                             rethook);
574                         break;
575                     }
576                 case NGM_BRIDGE_GET_STATS:
577                 case NGM_BRIDGE_CLR_STATS:
578                 case NGM_BRIDGE_GETCLR_STATS:
579                     {
580                         hook_p hook;
581                         link_p link;
582                         char linkName[NG_HOOKSIZ];
583                         int linkNum;
584                             
585                         /* Get link number */
586                         if (msg->header.arglen != sizeof(u_int32_t)) {
587                                 error = EINVAL;
588                                 break;
589                         }
590                         linkNum = *((int32_t *)msg->data);
591                         if (linkNum < 0)
592                                 snprintf(linkName, sizeof(linkName),
593                                     "%s%u", NG_BRIDGE_HOOK_UPLINK_PREFIX, -linkNum);
594                         else
595                                 snprintf(linkName, sizeof(linkName),
596                                     "%s%u", NG_BRIDGE_HOOK_LINK_PREFIX, linkNum);
597                             
598                         if ((hook = ng_findhook(node, linkName)) == NULL) {
599                                 error = ENOTCONN;
600                                 break;
601                         }
602                         link = NG_HOOK_PRIVATE(hook);
603
604                         /* Get/clear stats */
605                         if (msg->header.cmd != NGM_BRIDGE_CLR_STATS) {
606                                 struct ng_bridge_link_stats *rs;
607
608                                 NG_MKRESPONSE(resp, msg,
609                                     sizeof(link->stats), M_NOWAIT);
610                                 if (resp == NULL) {
611                                         error = ENOMEM;
612                                         break;
613                                 }
614                                 rs = (struct ng_bridge_link_stats *)resp->data;
615 #define FETCH(x)        rs->x = counter_u64_fetch(link->stats.x)
616                                 FETCH(recvOctets);
617                                 FETCH(recvPackets);
618                                 FETCH(recvMulticasts);
619                                 FETCH(recvBroadcasts);
620                                 FETCH(recvUnknown);
621                                 FETCH(recvRunts);
622                                 FETCH(recvInvalid);
623                                 FETCH(xmitOctets);
624                                 FETCH(xmitPackets);
625                                 FETCH(xmitMulticasts);
626                                 FETCH(xmitBroadcasts);
627                                 FETCH(loopDrops);
628                                 FETCH(loopDetects);
629                                 FETCH(memoryFailures);
630 #undef FETCH
631                         }
632                         if (msg->header.cmd != NGM_BRIDGE_GET_STATS)
633                                 ng_bridge_clear_link_stats(&link->stats);
634                         break;
635                     }
636                 case NGM_BRIDGE_GET_TABLE:
637                     {
638                         struct ng_bridge_host_ary *ary;
639                         struct ng_bridge_hent *hent;
640                         int i = 0, bucket;
641
642                         NG_MKRESPONSE(resp, msg, sizeof(*ary)
643                             + (priv->numHosts * sizeof(*ary->hosts)), M_NOWAIT);
644                         if (resp == NULL) {
645                                 error = ENOMEM;
646                                 break;
647                         }
648                         ary = (struct ng_bridge_host_ary *)resp->data;
649                         ary->numHosts = priv->numHosts;
650                         for (bucket = 0; bucket < priv->numBuckets; bucket++) {
651                                 SLIST_FOREACH(hent, &priv->tab[bucket], next) {
652                                         memcpy(ary->hosts[i].addr,
653                                                hent->host.addr,
654                                                sizeof(ary->hosts[i].addr));
655                                         ary->hosts[i].age       = hent->host.age;
656                                         ary->hosts[i].staleness = hent->host.staleness;
657                                         strncpy(ary->hosts[i].hook,
658                                                 NG_HOOK_NAME(hent->host.link->hook),
659                                                 sizeof(ary->hosts[i].hook));
660                                         i++;
661                                 }
662                         }
663                         break;
664                     }
665                 case NGM_BRIDGE_SET_PERSISTENT:
666                     {
667                         priv->persistent = 1;
668                         break;
669                     }
670                 default:
671                         error = EINVAL;
672                         break;
673                 }
674                 break;
675         default:
676                 error = EINVAL;
677                 break;
678         }
679
680         /* Done */
681         NG_RESPOND_MSG(error, node, item, resp);
682         NG_FREE_MSG(msg);
683         return (error);
684 }
685
686 /*
687  * Receive data on a hook
688  */
689 struct ng_bridge_send_ctx {
690         link_p foundFirst, incoming;
691         struct mbuf * m;
692         int manycast, error;
693 };
694
695 static int
696 ng_bridge_send_ctx(hook_p dst, void *arg)
697 {
698         struct ng_bridge_send_ctx *ctx = arg;
699         link_p destLink = NG_HOOK_PRIVATE(dst);
700         struct mbuf *m2 = NULL;
701         int error = 0;
702
703         /* Skip incoming link */
704         if (destLink == ctx->incoming) {
705                 return (1);
706         }
707
708         /* Skip sending unknowns to undesired links  */
709         if (!ctx->manycast && !destLink->sendUnknown)
710                 return (1);
711
712         if (ctx->foundFirst == NULL) {
713                 /*
714                  * This is the first usable link we have found.
715                  * Reserve it for the originals.
716                  * If we never find another we save a copy.
717                  */
718                 ctx->foundFirst = destLink;
719                 return (1);
720         }
721
722         /*
723          * It's usable link but not the reserved (first) one.
724          * Copy mbuf info for sending.
725          */
726         m2 = m_dup(ctx->m, M_NOWAIT);   /* XXX m_copypacket() */
727         if (m2 == NULL) {
728                 counter_u64_add(ctx->incoming->stats.memoryFailures, 1);
729                 ctx->error = ENOBUFS;
730                 return (0);            /* abort loop */
731         }
732
733         /* Update stats */
734         counter_u64_add(destLink->stats.xmitPackets, 1);
735         counter_u64_add(destLink->stats.xmitOctets, m2->m_pkthdr.len);
736         switch (ctx->manycast) {
737          default:                                       /* unknown unicast */
738                 break;
739          case 1:                                        /* multicast */
740                 counter_u64_add(destLink->stats.xmitMulticasts, 1);
741                 break;
742          case 2:                                        /* broadcast */
743                 counter_u64_add(destLink->stats.xmitBroadcasts, 1);
744                 break;
745         }
746
747         /* Send packet */
748         NG_SEND_DATA_ONLY(error, destLink->hook, m2);
749         if(error)
750           ctx->error = error;
751         return (1);
752 }
753
754 static int
755 ng_bridge_rcvdata(hook_p hook, item_p item)
756 {
757         const node_p node = NG_HOOK_NODE(hook);
758         const priv_p priv = NG_NODE_PRIVATE(node);
759         struct ng_bridge_host *host;
760         struct ether_header *eh;
761         struct ng_bridge_send_ctx ctx = { 0 };
762         hook_p ret;
763
764         NGI_GET_M(item, ctx.m);
765
766         ctx.incoming = NG_HOOK_PRIVATE(hook);
767         /* Sanity check packet and pull up header */
768         if (ctx.m->m_pkthdr.len < ETHER_HDR_LEN) {
769                 counter_u64_add(ctx.incoming->stats.recvRunts, 1);
770                 NG_FREE_ITEM(item);
771                 NG_FREE_M(ctx.m);
772                 return (EINVAL);
773         }
774         if (ctx.m->m_len < ETHER_HDR_LEN && !(ctx.m = m_pullup(ctx.m, ETHER_HDR_LEN))) {
775                 counter_u64_add(ctx.incoming->stats.memoryFailures, 1);
776                 NG_FREE_ITEM(item);
777                 return (ENOBUFS);
778         }
779         eh = mtod(ctx.m, struct ether_header *);
780         if ((eh->ether_shost[0] & 1) != 0) {
781                 counter_u64_add(ctx.incoming->stats.recvInvalid, 1);
782                 NG_FREE_ITEM(item);
783                 NG_FREE_M(ctx.m);
784                 return (EINVAL);
785         }
786
787         /* Is link disabled due to a loopback condition? */
788         if (ctx.incoming->loopCount != 0) {
789                 counter_u64_add(ctx.incoming->stats.loopDrops, 1);
790                 NG_FREE_ITEM(item);
791                 NG_FREE_M(ctx.m);
792                 return (ELOOP);         /* XXX is this an appropriate error? */
793         }
794
795         /* Update stats */
796         counter_u64_add(ctx.incoming->stats.recvPackets, 1);
797         counter_u64_add(ctx.incoming->stats.recvOctets, ctx.m->m_pkthdr.len);
798         if ((ctx.manycast = (eh->ether_dhost[0] & 1)) != 0) {
799                 if (ETHER_EQUAL(eh->ether_dhost, ng_bridge_bcast_addr)) {
800                         counter_u64_add(ctx.incoming->stats.recvBroadcasts, 1);
801                         ctx.manycast = 2;
802                 } else
803                         counter_u64_add(ctx.incoming->stats.recvMulticasts, 1);
804         }
805
806         /* Look up packet's source Ethernet address in hashtable */
807         if ((host = ng_bridge_get(priv, eh->ether_shost)) != NULL) {
808                 /* Update time since last heard from this host */
809                 host->staleness = 0;
810
811                 /* Did host jump to a different link? */
812                 if (host->link != ctx.incoming) {
813                         /*
814                          * If the host's old link was recently established
815                          * on the old link and it's already jumped to a new
816                          * link, declare a loopback condition.
817                          */
818                         if (host->age < priv->conf.minStableAge) {
819                                 /* Log the problem */
820                                 if (priv->conf.debugLevel >= 2) {
821                                         struct ifnet *ifp = ctx.m->m_pkthdr.rcvif;
822                                         char suffix[32];
823
824                                         if (ifp != NULL)
825                                                 snprintf(suffix, sizeof(suffix),
826                                                     " (%s)", ifp->if_xname);
827                                         else
828                                                 *suffix = '\0';
829                                         log(LOG_WARNING, "ng_bridge: %s:"
830                                             " loopback detected on %s%s\n",
831                                             ng_bridge_nodename(node),
832                                             NG_HOOK_NAME(hook), suffix);
833                                 }
834
835                                 /* Mark link as linka non grata */
836                                 ctx.incoming->loopCount = priv->conf.loopTimeout;
837                                 counter_u64_add(ctx.incoming->stats.loopDetects, 1);
838
839                                 /* Forget all hosts on this link */
840                                 ng_bridge_remove_hosts(priv, ctx.incoming);
841
842                                 /* Drop packet */
843                                 counter_u64_add(ctx.incoming->stats.loopDrops, 1);
844                                 NG_FREE_ITEM(item);
845                                 NG_FREE_M(ctx.m);
846                                 return (ELOOP);         /* XXX appropriate? */
847                         }
848
849                         /* Move host over to new link */
850                         host->link = ctx.incoming;
851                         host->age = 0;
852                 }
853         } else if (ctx.incoming->learnMac) {
854                 if (!ng_bridge_put(priv, eh->ether_shost, ctx.incoming)) {
855                         counter_u64_add(ctx.incoming->stats.memoryFailures, 1);
856                         NG_FREE_ITEM(item);
857                         NG_FREE_M(ctx.m);
858                         return (ENOMEM);
859                 }
860         }
861
862         /* Run packet through ipfw processing, if enabled */
863 #if 0
864         if (priv->conf.ipfw[linkNum] && V_fw_enable && V_ip_fw_chk_ptr != NULL) {
865                 /* XXX not implemented yet */
866         }
867 #endif
868
869         /*
870          * If unicast and destination host known, deliver to host's link,
871          * unless it is the same link as the packet came in on.
872          */
873         if (!ctx.manycast) {
874                 /* Determine packet destination link */
875                 if ((host = ng_bridge_get(priv, eh->ether_dhost)) != NULL) {
876                         link_p destLink = host->link;
877
878                         /* If destination same as incoming link, do nothing */
879                         if (destLink == ctx.incoming) {
880                                 NG_FREE_ITEM(item);
881                                 NG_FREE_M(ctx.m);
882                                 return (0);
883                         }
884
885                         /* Deliver packet out the destination link */
886                         counter_u64_add(destLink->stats.xmitPackets, 1);
887                         counter_u64_add(destLink->stats.xmitOctets, ctx.m->m_pkthdr.len);
888                         NG_FWD_NEW_DATA(ctx.error, item, destLink->hook, ctx.m);
889                         return (ctx.error);
890                 }
891
892                 /* Destination host is not known */
893                 counter_u64_add(ctx.incoming->stats.recvUnknown, 1);
894         }
895
896         /* Distribute unknown, multicast, broadcast pkts to all other links */
897         NG_NODE_FOREACH_HOOK(node, ng_bridge_send_ctx, &ctx, ret);
898
899         /* If we never saw a good link, leave. */
900         if (ctx.foundFirst == NULL || ctx.error != 0) {
901                 NG_FREE_ITEM(item);
902                 NG_FREE_M(ctx.m);
903                 return (ctx.error);
904         }
905
906         /*
907          * If we've sent all the others, send the original
908          * on the first link we found.
909          */
910         NG_FWD_NEW_DATA(ctx.error, item, ctx.foundFirst->hook, ctx.m);
911         return (ctx.error);
912 }
913
914 /*
915  * Shutdown node
916  */
917 static int
918 ng_bridge_shutdown(node_p node)
919 {
920         const priv_p priv = NG_NODE_PRIVATE(node);
921
922         /*
923          * Shut down everything including the timer.  Even if the
924          * callout has already been dequeued and is about to be
925          * run, ng_bridge_timeout() won't be fired as the node
926          * is already marked NGF_INVALID, so we're safe to free
927          * the node now.
928          */
929         KASSERT(priv->numLinks == 0 && priv->numHosts == 0,
930             ("%s: numLinks=%d numHosts=%d",
931             __func__, priv->numLinks, priv->numHosts));
932         ng_uncallout(&priv->timer, node);
933         NG_NODE_SET_PRIVATE(node, NULL);
934         NG_NODE_UNREF(node);
935         free(priv->tab, M_NETGRAPH_BRIDGE);
936         free(priv, M_NETGRAPH_BRIDGE);
937         return (0);
938 }
939
940 /*
941  * Hook disconnection.
942  */
943 static int
944 ng_bridge_disconnect(hook_p hook)
945 {
946         const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
947         link_p link = NG_HOOK_PRIVATE(hook);
948
949         /* Remove all hosts associated with this link */
950         ng_bridge_remove_hosts(priv, link);
951
952         /* Free associated link information */
953         counter_u64_free(link->stats.recvOctets);
954         counter_u64_free(link->stats.recvPackets);
955         counter_u64_free(link->stats.recvMulticasts);
956         counter_u64_free(link->stats.recvBroadcasts);
957         counter_u64_free(link->stats.recvUnknown);
958         counter_u64_free(link->stats.recvRunts);
959         counter_u64_free(link->stats.recvInvalid);
960         counter_u64_free(link->stats.xmitOctets);
961         counter_u64_free(link->stats.xmitPackets);
962         counter_u64_free(link->stats.xmitMulticasts);
963         counter_u64_free(link->stats.xmitBroadcasts);
964         counter_u64_free(link->stats.loopDrops);
965         counter_u64_free(link->stats.loopDetects);
966         counter_u64_free(link->stats.memoryFailures);
967         free(link, M_NETGRAPH_BRIDGE);
968         priv->numLinks--;
969
970         /* If no more hooks, go away */
971         if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0)
972             && (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))
973             && !priv->persistent) {
974                 ng_rmnode_self(NG_HOOK_NODE(hook));
975         }
976         return (0);
977 }
978
979 /******************************************************************
980                     HASH TABLE FUNCTIONS
981 ******************************************************************/
982
983 /*
984  * Hash algorithm
985  */
986 #define HASH(addr,mask)         ( (((const u_int16_t *)(addr))[0]       \
987                                  ^ ((const u_int16_t *)(addr))[1]       \
988                                  ^ ((const u_int16_t *)(addr))[2]) & (mask) )
989
990 /*
991  * Find a host entry in the table.
992  */
993 static struct ng_bridge_host *
994 ng_bridge_get(priv_cp priv, const u_char *addr)
995 {
996         const int bucket = HASH(addr, priv->hashMask);
997         struct ng_bridge_hent *hent;
998
999         SLIST_FOREACH(hent, &priv->tab[bucket], next) {
1000                 if (ETHER_EQUAL(hent->host.addr, addr))
1001                         return (&hent->host);
1002         }
1003         return (NULL);
1004 }
1005
1006 /*
1007  * Add a new host entry to the table. This assumes the host doesn't
1008  * already exist in the table. Returns 1 on success, 0 if there
1009  * was a memory allocation failure.
1010  */
1011 static int
1012 ng_bridge_put(priv_p priv, const u_char *addr, link_p link)
1013 {
1014         const int bucket = HASH(addr, priv->hashMask);
1015         struct ng_bridge_hent *hent;
1016
1017 #ifdef INVARIANTS
1018         /* Assert that entry does not already exist in hashtable */
1019         SLIST_FOREACH(hent, &priv->tab[bucket], next) {
1020                 KASSERT(!ETHER_EQUAL(hent->host.addr, addr),
1021                     ("%s: entry %6D exists in table", __func__, addr, ":"));
1022         }
1023 #endif
1024
1025         /* Allocate and initialize new hashtable entry */
1026         hent = malloc(sizeof(*hent), M_NETGRAPH_BRIDGE, M_NOWAIT);
1027         if (hent == NULL)
1028                 return (0);
1029         bcopy(addr, hent->host.addr, ETHER_ADDR_LEN);
1030         hent->host.link = link;
1031         hent->host.staleness = 0;
1032         hent->host.age = 0;
1033
1034         /* Add new element to hash bucket */
1035         SLIST_INSERT_HEAD(&priv->tab[bucket], hent, next);
1036         priv->numHosts++;
1037
1038         /* Resize table if necessary */
1039         ng_bridge_rehash(priv);
1040         return (1);
1041 }
1042
1043 /*
1044  * Resize the hash table. We try to maintain the number of buckets
1045  * such that the load factor is in the range 0.25 to 1.0.
1046  *
1047  * If we can't get the new memory then we silently fail. This is OK
1048  * because things will still work and we'll try again soon anyway.
1049  */
1050 static void
1051 ng_bridge_rehash(priv_p priv)
1052 {
1053         struct ng_bridge_bucket *newTab;
1054         int oldBucket, newBucket;
1055         int newNumBuckets;
1056         u_int newMask;
1057
1058         /* Is table too full or too empty? */
1059         if (priv->numHosts > priv->numBuckets
1060             && (priv->numBuckets << 1) <= MAX_BUCKETS)
1061                 newNumBuckets = priv->numBuckets << 1;
1062         else if (priv->numHosts < (priv->numBuckets >> 2)
1063             && (priv->numBuckets >> 2) >= MIN_BUCKETS)
1064                 newNumBuckets = priv->numBuckets >> 2;
1065         else
1066                 return;
1067         newMask = newNumBuckets - 1;
1068
1069         /* Allocate and initialize new table */
1070         newTab = malloc(newNumBuckets * sizeof(*newTab),
1071             M_NETGRAPH_BRIDGE, M_NOWAIT | M_ZERO);
1072         if (newTab == NULL)
1073                 return;
1074
1075         /* Move all entries from old table to new table */
1076         for (oldBucket = 0; oldBucket < priv->numBuckets; oldBucket++) {
1077                 struct ng_bridge_bucket *const oldList = &priv->tab[oldBucket];
1078
1079                 while (!SLIST_EMPTY(oldList)) {
1080                         struct ng_bridge_hent *const hent
1081                             = SLIST_FIRST(oldList);
1082
1083                         SLIST_REMOVE_HEAD(oldList, next);
1084                         newBucket = HASH(hent->host.addr, newMask);
1085                         SLIST_INSERT_HEAD(&newTab[newBucket], hent, next);
1086                 }
1087         }
1088
1089         /* Replace old table with new one */
1090         if (priv->conf.debugLevel >= 3) {
1091                 log(LOG_INFO, "ng_bridge: %s: table size %d -> %d\n",
1092                     ng_bridge_nodename(priv->node),
1093                     priv->numBuckets, newNumBuckets);
1094         }
1095         free(priv->tab, M_NETGRAPH_BRIDGE);
1096         priv->numBuckets = newNumBuckets;
1097         priv->hashMask = newMask;
1098         priv->tab = newTab;
1099         return;
1100 }
1101
1102 /******************************************************************
1103                     MISC FUNCTIONS
1104 ******************************************************************/
1105
1106 /*
1107  * Remove all hosts associated with a specific link from the hashtable.
1108  * If linkNum == -1, then remove all hosts in the table.
1109  */
1110 static void
1111 ng_bridge_remove_hosts(priv_p priv, link_p link)
1112 {
1113         int bucket;
1114
1115         for (bucket = 0; bucket < priv->numBuckets; bucket++) {
1116                 struct ng_bridge_hent **hptr = &SLIST_FIRST(&priv->tab[bucket]);
1117
1118                 while (*hptr != NULL) {
1119                         struct ng_bridge_hent *const hent = *hptr;
1120
1121                         if (link == NULL || hent->host.link == link) {
1122                                 *hptr = SLIST_NEXT(hent, next);
1123                                 free(hent, M_NETGRAPH_BRIDGE);
1124                                 priv->numHosts--;
1125                         } else
1126                                 hptr = &SLIST_NEXT(hent, next);
1127                 }
1128         }
1129 }
1130
1131 /*
1132  * Handle our once-per-second timeout event. We do two things:
1133  * we decrement link->loopCount for those links being muted due to
1134  * a detected loopback condition, and we remove any hosts from
1135  * the hashtable whom we haven't heard from in a long while.
1136  */
1137 static int
1138 ng_bridge_unmute(hook_p hook, void *arg)
1139 {
1140         link_p link = NG_HOOK_PRIVATE(hook);
1141         node_p node = NG_HOOK_NODE(hook);
1142         priv_p priv = NG_NODE_PRIVATE(node);
1143         int *counter = arg;
1144
1145         if (link->loopCount != 0) {
1146                 link->loopCount--;
1147                 if (link->loopCount == 0 && priv->conf.debugLevel >= 2) {
1148                         log(LOG_INFO, "ng_bridge: %s:"
1149                             " restoring looped back %s\n",
1150                             ng_bridge_nodename(node), NG_HOOK_NAME(hook));
1151                 }
1152         }
1153         (*counter)++;
1154         return (1);
1155 }
1156
1157 static void
1158 ng_bridge_timeout(node_p node, hook_p hook, void *arg1, int arg2)
1159 {
1160         const priv_p priv = NG_NODE_PRIVATE(node);
1161         int bucket;
1162         int counter = 0;
1163         hook_p ret;
1164
1165         /* Update host time counters and remove stale entries */
1166         for (bucket = 0; bucket < priv->numBuckets; bucket++) {
1167                 struct ng_bridge_hent **hptr = &SLIST_FIRST(&priv->tab[bucket]);
1168
1169                 while (*hptr != NULL) {
1170                         struct ng_bridge_hent *const hent = *hptr;
1171
1172                         /* Remove hosts we haven't heard from in a while */
1173                         if (++hent->host.staleness >= priv->conf.maxStaleness) {
1174                                 *hptr = SLIST_NEXT(hent, next);
1175                                 free(hent, M_NETGRAPH_BRIDGE);
1176                                 priv->numHosts--;
1177                         } else {
1178                                 if (hent->host.age < 0xffff)
1179                                         hent->host.age++;
1180                                 hptr = &SLIST_NEXT(hent, next);
1181                                 counter++;
1182                         }
1183                 }
1184         }
1185         KASSERT(priv->numHosts == counter,
1186             ("%s: hosts: %d != %d", __func__, priv->numHosts, counter));
1187
1188         /* Decrease table size if necessary */
1189         ng_bridge_rehash(priv);
1190
1191         /* Decrease loop counter on muted looped back links */
1192         counter = 0;
1193         NG_NODE_FOREACH_HOOK(node, ng_bridge_unmute, &counter, ret);
1194         KASSERT(priv->numLinks == counter,
1195             ("%s: links: %d != %d", __func__, priv->numLinks, counter));
1196
1197         /* Register a new timeout, keeping the existing node reference */
1198         ng_callout(&priv->timer, node, NULL, hz, ng_bridge_timeout, NULL, 0);
1199 }
1200
1201 /*
1202  * Return node's "name", even if it doesn't have one.
1203  */
1204 static const char *
1205 ng_bridge_nodename(node_cp node)
1206 {
1207         static char name[NG_NODESIZ];
1208
1209         if (NG_NODE_HAS_NAME(node))
1210                 snprintf(name, sizeof(name), "%s", NG_NODE_NAME(node));
1211         else
1212                 snprintf(name, sizeof(name), "[%x]", ng_node2ID(node));
1213         return name;
1214 }