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