xref: /freebsd/sys/netgraph/ng_bridge.c (revision 7fdf597e96a02165cfe22ff357b857d5fa15ed8a)
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
81 static 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 */
88 struct 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 */
106 struct 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 };
113 typedef struct ng_bridge_link const *link_cp;	/* read only access */
114 
115 /* Per-node private data */
116 struct 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 };
130 typedef struct ng_bridge_private *priv_p;
131 typedef struct ng_bridge_private const *priv_cp;	/* read only access */
132 
133 /* Information about a host, stored in a hash table entry */
134 struct 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 */
143 SLIST_HEAD(ng_bridge_bucket, ng_bridge_host);
144 
145 /* [up]link prefix matching */
146 struct ng_link_prefix {
147 	const char * const	prefix;
148 	size_t			len;
149 };
150 
151 static const struct ng_link_prefix link_pfx = {
152        .prefix = NG_BRIDGE_HOOK_LINK_PREFIX,
153        .len = sizeof(NG_BRIDGE_HOOK_LINK_PREFIX) - 1,
154 };
155 static 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 */
161 static ng_constructor_t	ng_bridge_constructor;
162 static ng_rcvmsg_t	ng_bridge_rcvmsg;
163 static ng_shutdown_t	ng_bridge_shutdown;
164 static ng_newhook_t	ng_bridge_newhook;
165 static ng_rcvdata_t	ng_bridge_rcvdata;
166 static ng_disconnect_t	ng_bridge_disconnect;
167 
168 /* Other internal functions */
169 static const struct	ng_link_prefix *ng_get_link_prefix(const char *name);
170 static void	ng_bridge_free_link(link_p link);
171 static struct	ng_bridge_host *ng_bridge_get(priv_cp priv, const u_char *addr);
172 static int	ng_bridge_put(priv_p priv, const u_char *addr, link_p link);
173 static void	ng_bridge_rehash(priv_p priv);
174 static void	ng_bridge_remove_hosts(priv_p priv, link_p link);
175 static void	ng_bridge_timeout(node_p node, hook_p hook, void *arg1, int arg2);
176 static const	char *ng_bridge_nodename(node_cp node);
177 
178 /* Ethernet broadcast */
179 static 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  */
204 static int
205 ng_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 */
215 static const struct ng_parse_struct_field ng_bridge_host_type_fields[]
216 	= NG_BRIDGE_HOST_TYPE_INFO(&ng_parse_enaddr_type);
217 static const struct ng_parse_type ng_bridge_host_type = {
218 	&ng_parse_struct_type,
219 	&ng_bridge_host_type_fields
220 };
221 static const struct ng_parse_array_info ng_bridge_hary_type_info = {
222 	&ng_bridge_host_type,
223 	ng_bridge_getTableLength
224 };
225 static const struct ng_parse_type ng_bridge_hary_type = {
226 	&ng_parse_array_type,
227 	&ng_bridge_hary_type_info
228 };
229 static const struct ng_parse_struct_field ng_bridge_host_ary_type_fields[]
230 	= NG_BRIDGE_HOST_ARY_TYPE_INFO(&ng_bridge_hary_type);
231 static 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 */
237 static const struct ng_parse_struct_field ng_bridge_config_type_fields[]
238 	= NG_BRIDGE_CONFIG_TYPE_INFO;
239 static 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 */
245 static const struct ng_parse_struct_field ng_bridge_stats_type_fields[]
246 	= NG_BRIDGE_STATS_TYPE_INFO;
247 static 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 */
252 static const struct ng_parse_struct_field ng_bridge_move_host_type_fields[]
253 	= NG_BRIDGE_MOVE_HOST_TYPE_INFO(&ng_parse_enaddr_type);
254 static 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 */
260 static 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 */
328 static 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 };
339 NETGRAPH_INIT(bridge, &ng_bridge_typestruct);
340 
341 /******************************************************************
342 		    NETGRAPH NODE METHODS
343 ******************************************************************/
344 
345 /*
346  * Node constructor
347  */
348 static int
349 ng_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  */
385 static	int
386 ng_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 
469 nomem:
470 	free_unr(unit, linkNum);
471 	ng_bridge_free_link(link);
472 	return (ENOMEM);
473 }
474 
475 /*
476  * Receive a control message
477  */
478 static void
479 ng_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 
497 static void
498 ng_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 
516 static int
517 ng_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 
526 static int
527 ng_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  */
707 struct 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  */
716 static inline int
717 ng_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  */
756 static int
757 ng_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 
801 static int
802 ng_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  */
938 static int
939 ng_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  */
966 static int
967 ng_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  */
1008 static struct ng_bridge_host *
1009 ng_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  */
1025 static int
1026 ng_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  */
1087 static void
1088 ng_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 
1143 static const struct ng_link_prefix *
1144 ng_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  */
1160 static void
1161 ng_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  */
1187 static int
1188 ng_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 
1207 static void
1208 ng_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  */
1253 static const char *
1254 ng_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