xref: /freebsd/sys/netgraph/ng_bridge.c (revision 4690891af6320e358e4a2beb3054cd37d065d8d2)
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
ng_bridge_getTableLength(const struct ng_parse_type * type,const u_char * start,const u_char * buf)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
ng_bridge_constructor(node_p node)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
ng_bridge_newhook(node_p node,hook_p hook,const char * name)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
ng_bridge_clear_link_stats(struct ng_bridge_link_kernel_stats * p)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
ng_bridge_free_link(link_p link)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
ng_bridge_reset_link(hook_p hook,void * arg __unused)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
ng_bridge_rcvmsg(node_p node,item_p item,hook_p lasthook)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
ng_bridge_send_data(link_cp dst,int manycast,struct mbuf * m,item_p item)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
ng_bridge_send_ctx(hook_p dst,void * arg)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
ng_bridge_rcvdata(hook_p hook,item_p item)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 	/*
862 	 * learnMac is 0 on uplink: neither insert a new host nor
863 	 * move an existing one from packets received there.
864 	 */
865 	if (ctx.incoming->learnMac &&
866 	    (host == NULL || host->link != ctx.incoming)) {
867 		struct ng_mesg *msg;
868 		struct ng_bridge_move_host *mh;
869 		int error = 0;
870 
871 		NG_MKMESSAGE(msg, NGM_BRIDGE_COOKIE, NGM_BRIDGE_MOVE_HOST,
872 		    sizeof(*mh), M_NOWAIT);
873 		if (msg == NULL) {
874 			counter_u64_add(ctx.incoming->stats.memoryFailures, 1);
875 			NG_FREE_ITEM(item);
876 			NG_FREE_M(ctx.m);
877 			return (ENOMEM);
878 		}
879 		mh = (struct ng_bridge_move_host *)msg->data;
880 		strncpy(mh->hook, NG_HOOK_NAME(ctx.incoming->hook),
881 		    sizeof(mh->hook));
882 		memcpy(mh->addr, eh->ether_shost, sizeof(mh->addr));
883 		NG_SEND_MSG_ID(error, node, msg, NG_NODE_ID(node),
884 		    NG_NODE_ID(node));
885 		if (error)
886 			counter_u64_add(ctx.incoming->stats.memoryFailures, 1);
887 	}
888 
889 	if (host != NULL && host->link != ctx.incoming) {
890 		if (host->age < priv->conf.minStableAge) {
891 			/* Drop packet on instable links */
892 			counter_u64_add(ctx.incoming->stats.loopDrops, 1);
893 			NG_FREE_ITEM(item);
894 			NG_FREE_M(ctx.m);
895 			return (ELOOP);
896 		}
897 	}
898 
899 	/*
900 	 * If unicast and destination host known, deliver to host's link,
901 	 * unless it is the same link as the packet came in on.
902 	 */
903 	if (!ctx.manycast) {
904 		/* Determine packet destination link */
905 		if ((host = ng_bridge_get(priv, eh->ether_dhost)) != NULL) {
906 			link_p destLink = host->link;
907 
908 			/* If destination same as incoming link, do nothing */
909 			if (destLink == ctx.incoming) {
910 				NG_FREE_ITEM(item);
911 				NG_FREE_M(ctx.m);
912 				return (0);
913 			}
914 
915 			/* Deliver packet out the destination link */
916 			return (ng_bridge_send_data(destLink, ctx.manycast, ctx.m, item));
917 		}
918 
919 		/* Destination host is not known */
920 		counter_u64_add(ctx.incoming->stats.recvUnknown, 1);
921 	}
922 
923 	/* Distribute unknown, multicast, broadcast pkts to all other links */
924 	NG_NODE_FOREACH_HOOK(node, ng_bridge_send_ctx, &ctx);
925 
926 	/* Finally send out on the first link found */
927 	if (ctx.foundFirst != NULL) {
928 		int error = ng_bridge_send_data(ctx.foundFirst, ctx.manycast, ctx.m, item);
929 		if (error)
930 			ctx.error = error;
931 	} else {		       /* nothing to send at all */
932 		NG_FREE_ITEM(item);
933 		NG_FREE_M(ctx.m);
934 	}
935 
936 	return (ctx.error);
937 }
938 
939 /*
940  * Shutdown node
941  */
942 static int
ng_bridge_shutdown(node_p node)943 ng_bridge_shutdown(node_p node)
944 {
945 	const priv_p priv = NG_NODE_PRIVATE(node);
946 
947 	/*
948 	 * Shut down everything including the timer.  Even if the
949 	 * callout has already been dequeued and is about to be
950 	 * run, ng_bridge_timeout() won't be fired as the node
951 	 * is already marked NGF_INVALID, so we're safe to free
952 	 * the node now.
953 	 */
954 	KASSERT(priv->numLinks == 0 && priv->numHosts == 0,
955 	    ("%s: numLinks=%d numHosts=%d",
956 	    __func__, priv->numLinks, priv->numHosts));
957 	ng_uncallout(&priv->timer, node);
958 	delete_unrhdr(priv->linkUnit);
959 	delete_unrhdr(priv->uplinkUnit);
960 	NG_NODE_SET_PRIVATE(node, NULL);
961 	NG_NODE_UNREF(node);
962 	free(priv->tab, M_NETGRAPH_BRIDGE);
963 	free(priv, M_NETGRAPH_BRIDGE);
964 	return (0);
965 }
966 
967 /*
968  * Hook disconnection.
969  */
970 static int
ng_bridge_disconnect(hook_p hook)971 ng_bridge_disconnect(hook_p hook)
972 {
973 	char *name = NG_HOOK_NAME(hook);
974 	const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
975 	link_p link = NG_HOOK_PRIVATE(hook);
976 	const struct ng_link_prefix *pfx = ng_get_link_prefix(name);
977 	uint32_t linkNum;
978 
979 	/* Remove all hosts associated with this link */
980 	ng_bridge_remove_hosts(priv, link);
981 
982 	/* Free associated link information */
983 	ng_bridge_free_link(link);
984 	priv->numLinks--;
985 
986 	linkNum = strtoul(name + pfx->len, NULL, 10);
987 	free_unr(pfx == &link_pfx ? priv->linkUnit: priv->uplinkUnit, linkNum);
988 
989 	/* If no more hooks, go away */
990 	if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0)
991 	    && (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))
992 	    && !priv->persistent) {
993 		ng_rmnode_self(NG_HOOK_NODE(hook));
994 	}
995 	return (0);
996 }
997 
998 /******************************************************************
999 		    HASH TABLE FUNCTIONS
1000 ******************************************************************/
1001 
1002 /*
1003  * Hash algorithm
1004  */
1005 #define HASH(addr,mask)		( (((const u_int16_t *)(addr))[0] 	\
1006 				 ^ ((const u_int16_t *)(addr))[1] 	\
1007 				 ^ ((const u_int16_t *)(addr))[2]) & (mask) )
1008 
1009 /*
1010  * Find a host entry in the table.
1011  */
1012 static struct ng_bridge_host *
ng_bridge_get(priv_cp priv,const u_char * addr)1013 ng_bridge_get(priv_cp priv, const u_char *addr)
1014 {
1015 	const int bucket = HASH(addr, priv->hashMask);
1016 	struct ng_bridge_host *host;
1017 
1018 	SLIST_FOREACH(host, &priv->tab[bucket], next) {
1019 		if (ETHER_EQUAL(host->addr, addr))
1020 			return (host);
1021 	}
1022 	return (NULL);
1023 }
1024 
1025 /*
1026  * Add a host entry to the table. If it already exists, move it
1027  * to the new link. Returns 0 on success.
1028  */
1029 static int
ng_bridge_put(priv_p priv,const u_char * addr,link_p link)1030 ng_bridge_put(priv_p priv, const u_char *addr, link_p link)
1031 {
1032 	const int bucket = HASH(addr, priv->hashMask);
1033 	struct ng_bridge_host *host;
1034 
1035 	if ((host = ng_bridge_get(priv, addr)) != NULL) {
1036 		/* Host already on the correct link? */
1037 		if (host->link == link)
1038 			return 0;
1039 
1040 		/* Move old host over to new link */
1041 		if (host->age >= priv->conf.minStableAge) {
1042 			host->link = link;
1043 			host->age = 0;
1044 			return (0);
1045 		}
1046 		/*
1047 		 * If the host was recently moved to the old link and
1048 		 * it's now jumping to a new link, declare a loopback
1049 		 * condition.
1050 		 */
1051 		if (priv->conf.debugLevel >= 2)
1052 		    log(LOG_WARNING, "ng_bridge: %s:"
1053 			" loopback detected on %s\n",
1054 			ng_bridge_nodename(priv->node),
1055 			NG_HOOK_NAME(link->hook));
1056 
1057 		/* Mark link as linka non grata */
1058 		link->loopCount = priv->conf.loopTimeout;
1059 		link->stats.loopDetects++;
1060 
1061 		/* Forget all hosts on this link */
1062 		ng_bridge_remove_hosts(priv, link);
1063 		return (ELOOP);
1064 	}
1065 
1066 	/* Allocate and initialize new hashtable entry */
1067 	host = malloc(sizeof(*host), M_NETGRAPH_BRIDGE, M_NOWAIT);
1068 	if (host == NULL)
1069 		return (ENOMEM);
1070 	bcopy(addr, host->addr, ETHER_ADDR_LEN);
1071 	host->link = link;
1072 	host->staleness = 0;
1073 	host->age = 0;
1074 
1075 	/* Add new element to hash bucket */
1076 	SLIST_INSERT_HEAD(&priv->tab[bucket], host, next);
1077 	priv->numHosts++;
1078 
1079 	/* Resize table if necessary */
1080 	ng_bridge_rehash(priv);
1081 	return (0);
1082 }
1083 
1084 /*
1085  * Resize the hash table. We try to maintain the number of buckets
1086  * such that the load factor is in the range 0.25 to 1.0.
1087  *
1088  * If we can't get the new memory then we silently fail. This is OK
1089  * because things will still work and we'll try again soon anyway.
1090  */
1091 static void
ng_bridge_rehash(priv_p priv)1092 ng_bridge_rehash(priv_p priv)
1093 {
1094 	struct ng_bridge_bucket *newTab;
1095 	int oldBucket, newBucket;
1096 	int newNumBuckets;
1097 	u_int newMask;
1098 
1099 	/* Is table too full or too empty? */
1100 	if (priv->numHosts > priv->numBuckets
1101 	    && (priv->numBuckets << 1) <= MAX_BUCKETS)
1102 		newNumBuckets = priv->numBuckets << 1;
1103 	else if (priv->numHosts < (priv->numBuckets >> 2)
1104 	    && (priv->numBuckets >> 2) >= MIN_BUCKETS)
1105 		newNumBuckets = priv->numBuckets >> 2;
1106 	else
1107 		return;
1108 	newMask = newNumBuckets - 1;
1109 
1110 	/* Allocate and initialize new table */
1111 	newTab = malloc(newNumBuckets * sizeof(*newTab),
1112 	    M_NETGRAPH_BRIDGE, M_NOWAIT | M_ZERO);
1113 	if (newTab == NULL)
1114 		return;
1115 
1116 	/* Move all entries from old table to new table */
1117 	for (oldBucket = 0; oldBucket < priv->numBuckets; oldBucket++) {
1118 		struct ng_bridge_bucket *const oldList = &priv->tab[oldBucket];
1119 
1120 		while (!SLIST_EMPTY(oldList)) {
1121 			struct ng_bridge_host *const host
1122 			    = SLIST_FIRST(oldList);
1123 
1124 			SLIST_REMOVE_HEAD(oldList, next);
1125 			newBucket = HASH(host->addr, newMask);
1126 			SLIST_INSERT_HEAD(&newTab[newBucket], host, next);
1127 		}
1128 	}
1129 
1130 	/* Replace old table with new one */
1131 	if (priv->conf.debugLevel >= 3) {
1132 		log(LOG_INFO, "ng_bridge: %s: table size %d -> %d\n",
1133 		    ng_bridge_nodename(priv->node),
1134 		    priv->numBuckets, newNumBuckets);
1135 	}
1136 	free(priv->tab, M_NETGRAPH_BRIDGE);
1137 	priv->numBuckets = newNumBuckets;
1138 	priv->hashMask = newMask;
1139 	priv->tab = newTab;
1140 	return;
1141 }
1142 
1143 /******************************************************************
1144 		    MISC FUNCTIONS
1145 ******************************************************************/
1146 
1147 static const struct ng_link_prefix *
ng_get_link_prefix(const char * name)1148 ng_get_link_prefix(const char *name)
1149 {
1150 	static const struct ng_link_prefix *pfxs[] =
1151 	    { &link_pfx, &uplink_pfx, };
1152 
1153 	for (u_int i = 0; i < nitems(pfxs); i++)
1154 		if (strncmp(pfxs[i]->prefix, name, pfxs[i]->len) == 0)
1155 			return (pfxs[i]);
1156 
1157 	return (NULL);
1158 }
1159 
1160 /*
1161  * Remove all hosts associated with a specific link from the hashtable.
1162  * If linkNum == -1, then remove all hosts in the table.
1163  */
1164 static void
ng_bridge_remove_hosts(priv_p priv,link_p link)1165 ng_bridge_remove_hosts(priv_p priv, link_p link)
1166 {
1167 	int bucket;
1168 
1169 	for (bucket = 0; bucket < priv->numBuckets; bucket++) {
1170 		struct ng_bridge_host **hptr = &SLIST_FIRST(&priv->tab[bucket]);
1171 
1172 		while (*hptr != NULL) {
1173 			struct ng_bridge_host *const host = *hptr;
1174 
1175 			if (link == NULL || host->link == link) {
1176 				*hptr = SLIST_NEXT(host, next);
1177 				free(host, M_NETGRAPH_BRIDGE);
1178 				priv->numHosts--;
1179 			} else
1180 				hptr = &SLIST_NEXT(host, next);
1181 		}
1182 	}
1183 }
1184 
1185 /*
1186  * Handle our once-per-second timeout event. We do two things:
1187  * we decrement link->loopCount for those links being muted due to
1188  * a detected loopback condition, and we remove any hosts from
1189  * the hashtable whom we haven't heard from in a long while.
1190  */
1191 static int
ng_bridge_unmute(hook_p hook,void * arg)1192 ng_bridge_unmute(hook_p hook, void *arg)
1193 {
1194 	link_p link = NG_HOOK_PRIVATE(hook);
1195 	node_p node = NG_HOOK_NODE(hook);
1196 	priv_p priv = NG_NODE_PRIVATE(node);
1197 	int *counter = arg;
1198 
1199 	if (link->loopCount != 0) {
1200 		link->loopCount--;
1201 		if (link->loopCount == 0 && priv->conf.debugLevel >= 2) {
1202 			log(LOG_INFO, "ng_bridge: %s:"
1203 			    " restoring looped back %s\n",
1204 			    ng_bridge_nodename(node), NG_HOOK_NAME(hook));
1205 		}
1206 	}
1207 	(*counter)++;
1208 	return (1);
1209 }
1210 
1211 static void
ng_bridge_timeout(node_p node,hook_p hook,void * arg1,int arg2)1212 ng_bridge_timeout(node_p node, hook_p hook, void *arg1, int arg2)
1213 {
1214 	const priv_p priv = NG_NODE_PRIVATE(node);
1215 	int bucket;
1216 	int counter = 0;
1217 
1218 	/* Update host time counters and remove stale entries */
1219 	for (bucket = 0; bucket < priv->numBuckets; bucket++) {
1220 		struct ng_bridge_host **hptr = &SLIST_FIRST(&priv->tab[bucket]);
1221 
1222 		while (*hptr != NULL) {
1223 			struct ng_bridge_host *const host = *hptr;
1224 
1225 			/* Remove hosts we haven't heard from in a while */
1226 			if (++host->staleness >= priv->conf.maxStaleness) {
1227 				*hptr = SLIST_NEXT(host, next);
1228 				free(host, M_NETGRAPH_BRIDGE);
1229 				priv->numHosts--;
1230 			} else {
1231 				if (host->age < 0xffff)
1232 					host->age++;
1233 				hptr = &SLIST_NEXT(host, next);
1234 				counter++;
1235 			}
1236 		}
1237 	}
1238 	KASSERT(priv->numHosts == counter,
1239 	    ("%s: hosts: %d != %d", __func__, priv->numHosts, counter));
1240 
1241 	/* Decrease table size if necessary */
1242 	ng_bridge_rehash(priv);
1243 
1244 	/* Decrease loop counter on muted looped back links */
1245 	counter = 0;
1246 	NG_NODE_FOREACH_HOOK(node, ng_bridge_unmute, &counter);
1247 	KASSERT(priv->numLinks == counter,
1248 	    ("%s: links: %d != %d", __func__, priv->numLinks, counter));
1249 
1250 	/* Register a new timeout, keeping the existing node reference */
1251 	ng_callout(&priv->timer, node, NULL, hz, ng_bridge_timeout, NULL, 0);
1252 }
1253 
1254 /*
1255  * Return node's "name", even if it doesn't have one.
1256  */
1257 static const char *
ng_bridge_nodename(node_cp node)1258 ng_bridge_nodename(node_cp node)
1259 {
1260 	static char name[NG_NODESIZ];
1261 
1262 	if (NG_NODE_HAS_NAME(node))
1263 		snprintf(name, sizeof(name), "%s", NG_NODE_NAME(node));
1264 	else
1265 		snprintf(name, sizeof(name), "[%x]", ng_node2ID(node));
1266 	return name;
1267 }
1268