ng_bridge.c revision 111119
165310Sarchie
265310Sarchie/*
365310Sarchie * ng_bridge.c
465310Sarchie *
565310Sarchie * Copyright (c) 2000 Whistle Communications, Inc.
665310Sarchie * All rights reserved.
765310Sarchie *
865310Sarchie * Subject to the following obligations and disclaimer of warranty, use and
965310Sarchie * redistribution of this software, in source or object code forms, with or
1065310Sarchie * without modifications are expressly permitted by Whistle Communications;
1165310Sarchie * provided, however, that:
1265310Sarchie * 1. Any and all reproductions of the source or object code must include the
1365310Sarchie *    copyright notice above and the following disclaimer of warranties; and
1465310Sarchie * 2. No rights are granted, in any manner or form, to use Whistle
1565310Sarchie *    Communications, Inc. trademarks, including the mark "WHISTLE
1665310Sarchie *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
1765310Sarchie *    such appears in the above copyright notice or in the software.
1865310Sarchie *
1965310Sarchie * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
2065310Sarchie * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
2165310Sarchie * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
2265310Sarchie * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
2365310Sarchie * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
2465310Sarchie * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
2565310Sarchie * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
2665310Sarchie * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
2765310Sarchie * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
2865310Sarchie * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
2965310Sarchie * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
3065310Sarchie * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
3165310Sarchie * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
3265310Sarchie * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3365310Sarchie * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3465310Sarchie * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
3565310Sarchie * OF SUCH DAMAGE.
3665310Sarchie *
3765310Sarchie * Author: Archie Cobbs <archie@freebsd.org>
3865310Sarchie *
3965310Sarchie * $FreeBSD: head/sys/netgraph/ng_bridge.c 111119 2003-02-19 05:47:46Z imp $
4065310Sarchie */
4165310Sarchie
4265310Sarchie/*
4365310Sarchie * ng_bridge(4) netgraph node type
4465310Sarchie *
4565310Sarchie * The node performs standard intelligent Ethernet bridging over
4665310Sarchie * each of its connected hooks, or links.  A simple loop detection
4765310Sarchie * algorithm is included which disables a link for priv->conf.loopTimeout
4865310Sarchie * seconds when a host is seen to have jumped from one link to
4965310Sarchie * another within priv->conf.minStableAge seconds.
5065310Sarchie *
5165310Sarchie * We keep a hashtable that maps Ethernet addresses to host info,
5265310Sarchie * which is contained in struct ng_bridge_host's. These structures
5365310Sarchie * tell us on which link the host may be found. A host's entry will
5465310Sarchie * expire after priv->conf.maxStaleness seconds.
5565310Sarchie *
5665310Sarchie * This node is optimzed for stable networks, where machines jump
5765310Sarchie * from one port to the other only rarely.
5865310Sarchie */
5965310Sarchie
6065310Sarchie#include <sys/param.h>
6165310Sarchie#include <sys/systm.h>
6265310Sarchie#include <sys/kernel.h>
6365310Sarchie#include <sys/malloc.h>
6465310Sarchie#include <sys/mbuf.h>
6565310Sarchie#include <sys/errno.h>
6665310Sarchie#include <sys/syslog.h>
6765310Sarchie#include <sys/socket.h>
6865310Sarchie#include <sys/ctype.h>
6965310Sarchie
7065310Sarchie#include <net/if.h>
7165310Sarchie#include <net/ethernet.h>
7265310Sarchie
7365310Sarchie#include <netinet/in.h>
7465310Sarchie#include <netinet/ip_fw.h>
7565310Sarchie
7665310Sarchie#include <netgraph/ng_message.h>
7765310Sarchie#include <netgraph/netgraph.h>
7865310Sarchie#include <netgraph/ng_parse.h>
7965310Sarchie#include <netgraph/ng_bridge.h>
8065310Sarchie#include <netgraph/ng_ether.h>
8165310Sarchie
8270870Sjulian#ifdef NG_SEPARATE_MALLOC
8370870SjulianMALLOC_DEFINE(M_NETGRAPH_BRIDGE, "netgraph_bridge", "netgraph bridge node ");
8470870Sjulian#else
8570870Sjulian#define M_NETGRAPH_BRIDGE M_NETGRAPH
8670870Sjulian#endif
8770870Sjulian
8865310Sarchie/* Per-link private data */
8965310Sarchiestruct ng_bridge_link {
9065310Sarchie	hook_p				hook;		/* netgraph hook */
9165310Sarchie	u_int16_t			loopCount;	/* loop ignore timer */
9265310Sarchie	struct ng_bridge_link_stats	stats;		/* link stats */
9365310Sarchie};
9465310Sarchie
9565310Sarchie/* Per-node private data */
9665310Sarchiestruct ng_bridge_private {
9765310Sarchie	struct ng_bridge_bucket	*tab;		/* hash table bucket array */
9865310Sarchie	struct ng_bridge_link	*links[NG_BRIDGE_MAX_LINKS];
9965310Sarchie	struct ng_bridge_config	conf;		/* node configuration */
10065310Sarchie	node_p			node;		/* netgraph node */
10165310Sarchie	u_int			numHosts;	/* num entries in table */
10265310Sarchie	u_int			numBuckets;	/* num buckets in table */
10365310Sarchie	u_int			hashMask;	/* numBuckets - 1 */
10465310Sarchie	int			numLinks;	/* num connected links */
10565310Sarchie	struct callout		timer;		/* one second periodic timer */
10665310Sarchie};
10765310Sarchietypedef struct ng_bridge_private *priv_p;
10865310Sarchie
10965310Sarchie/* Information about a host, stored in a hash table entry */
11065310Sarchiestruct ng_bridge_hent {
11165310Sarchie	struct ng_bridge_host		host;	/* actual host info */
11265310Sarchie	SLIST_ENTRY(ng_bridge_hent)	next;	/* next entry in bucket */
11365310Sarchie};
11465310Sarchie
11565310Sarchie/* Hash table bucket declaration */
11665310SarchieSLIST_HEAD(ng_bridge_bucket, ng_bridge_hent);
11765310Sarchie
11865310Sarchie/* Netgraph node methods */
11965310Sarchiestatic ng_constructor_t	ng_bridge_constructor;
12065310Sarchiestatic ng_rcvmsg_t	ng_bridge_rcvmsg;
12170700Sjulianstatic ng_shutdown_t	ng_bridge_shutdown;
12265310Sarchiestatic ng_newhook_t	ng_bridge_newhook;
12365310Sarchiestatic ng_rcvdata_t	ng_bridge_rcvdata;
12465310Sarchiestatic ng_disconnect_t	ng_bridge_disconnect;
12565310Sarchie
12665310Sarchie/* Other internal functions */
12765310Sarchiestatic struct	ng_bridge_host *ng_bridge_get(priv_p priv, const u_char *addr);
12865310Sarchiestatic int	ng_bridge_put(priv_p priv, const u_char *addr, int linkNum);
12965310Sarchiestatic void	ng_bridge_rehash(priv_p priv);
13065310Sarchiestatic void	ng_bridge_remove_hosts(priv_p priv, int linkNum);
13165310Sarchiestatic void	ng_bridge_timeout(void *arg);
13265310Sarchiestatic const	char *ng_bridge_nodename(node_p node);
13365310Sarchie
13465310Sarchie/* Ethernet broadcast */
13565310Sarchiestatic const u_char ng_bridge_bcast_addr[ETHER_ADDR_LEN] =
13665310Sarchie    { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
13765310Sarchie
13865310Sarchie/* Store each hook's link number in the private field */
13965310Sarchie#define LINK_NUM(hook)		(*(u_int16_t *)(&(hook)->private))
14065310Sarchie
14165310Sarchie/* Compare Ethernet addresses using 32 and 16 bit words instead of bytewise */
14265310Sarchie#define ETHER_EQUAL(a,b)	(((const u_int32_t *)(a))[0] \
14365310Sarchie					== ((const u_int32_t *)(b))[0] \
14465310Sarchie				    && ((const u_int16_t *)(a))[2] \
14565310Sarchie					== ((const u_int16_t *)(b))[2])
14665310Sarchie
14765310Sarchie/* Minimum and maximum number of hash buckets. Must be a power of two. */
14865310Sarchie#define MIN_BUCKETS		(1 << 5)	/* 32 */
14965310Sarchie#define MAX_BUCKETS		(1 << 14)	/* 16384 */
15065310Sarchie
15165310Sarchie/* Configuration default values */
15265310Sarchie#define DEFAULT_LOOP_TIMEOUT	60
15365310Sarchie#define DEFAULT_MAX_STALENESS	(15 * 60)	/* same as ARP timeout */
15465310Sarchie#define DEFAULT_MIN_STABLE_AGE	1
15565310Sarchie
15665310Sarchie/******************************************************************
15765310Sarchie		    NETGRAPH PARSE TYPES
15865310Sarchie******************************************************************/
15965310Sarchie
16065310Sarchie/*
16165310Sarchie * How to determine the length of the table returned by NGM_BRIDGE_GET_TABLE
16265310Sarchie */
16365310Sarchiestatic int
16465310Sarchieng_bridge_getTableLength(const struct ng_parse_type *type,
16565310Sarchie	const u_char *start, const u_char *buf)
16665310Sarchie{
16765310Sarchie	const struct ng_bridge_host_ary *const hary
16865310Sarchie	    = (const struct ng_bridge_host_ary *)(buf - sizeof(u_int32_t));
16965310Sarchie
17065310Sarchie	return hary->numHosts;
17165310Sarchie}
17265310Sarchie
17365310Sarchie/* Parse type for struct ng_bridge_host_ary */
17497685Sarchiestatic const struct ng_parse_struct_field ng_bridge_host_type_fields[]
17565310Sarchie	= NG_BRIDGE_HOST_TYPE_INFO(&ng_ether_enaddr_type);
17665310Sarchiestatic const struct ng_parse_type ng_bridge_host_type = {
17765310Sarchie	&ng_parse_struct_type,
17897685Sarchie	&ng_bridge_host_type_fields
17965310Sarchie};
18065310Sarchiestatic const struct ng_parse_array_info ng_bridge_hary_type_info = {
18165310Sarchie	&ng_bridge_host_type,
18265310Sarchie	ng_bridge_getTableLength
18365310Sarchie};
18465310Sarchiestatic const struct ng_parse_type ng_bridge_hary_type = {
18565310Sarchie	&ng_parse_array_type,
18665310Sarchie	&ng_bridge_hary_type_info
18765310Sarchie};
18897685Sarchiestatic const struct ng_parse_struct_field ng_bridge_host_ary_type_fields[]
18965310Sarchie	= NG_BRIDGE_HOST_ARY_TYPE_INFO(&ng_bridge_hary_type);
19065310Sarchiestatic const struct ng_parse_type ng_bridge_host_ary_type = {
19165310Sarchie	&ng_parse_struct_type,
19297685Sarchie	&ng_bridge_host_ary_type_fields
19365310Sarchie};
19465310Sarchie
19565310Sarchie/* Parse type for struct ng_bridge_config */
19665310Sarchiestatic const struct ng_parse_fixedarray_info ng_bridge_ipfwary_type_info = {
19765310Sarchie	&ng_parse_uint8_type,
19865310Sarchie	NG_BRIDGE_MAX_LINKS
19965310Sarchie};
20065310Sarchiestatic const struct ng_parse_type ng_bridge_ipfwary_type = {
20165310Sarchie	&ng_parse_fixedarray_type,
20265310Sarchie	&ng_bridge_ipfwary_type_info
20365310Sarchie};
20497685Sarchiestatic const struct ng_parse_struct_field ng_bridge_config_type_fields[]
20565310Sarchie	= NG_BRIDGE_CONFIG_TYPE_INFO(&ng_bridge_ipfwary_type);
20665310Sarchiestatic const struct ng_parse_type ng_bridge_config_type = {
20765310Sarchie	&ng_parse_struct_type,
20897685Sarchie	&ng_bridge_config_type_fields
20965310Sarchie};
21065310Sarchie
21165310Sarchie/* Parse type for struct ng_bridge_link_stat */
21297685Sarchiestatic const struct ng_parse_struct_field ng_bridge_stats_type_fields[]
21397685Sarchie	= NG_BRIDGE_STATS_TYPE_INFO;
21465310Sarchiestatic const struct ng_parse_type ng_bridge_stats_type = {
21565310Sarchie	&ng_parse_struct_type,
21697685Sarchie	&ng_bridge_stats_type_fields
21765310Sarchie};
21865310Sarchie
21965310Sarchie/* List of commands and how to convert arguments to/from ASCII */
22065310Sarchiestatic const struct ng_cmdlist ng_bridge_cmdlist[] = {
22165310Sarchie	{
22265310Sarchie	  NGM_BRIDGE_COOKIE,
22365310Sarchie	  NGM_BRIDGE_SET_CONFIG,
22465310Sarchie	  "setconfig",
22565310Sarchie	  &ng_bridge_config_type,
22665310Sarchie	  NULL
22765310Sarchie	},
22865310Sarchie	{
22965310Sarchie	  NGM_BRIDGE_COOKIE,
23065310Sarchie	  NGM_BRIDGE_GET_CONFIG,
23165310Sarchie	  "getconfig",
23265310Sarchie	  NULL,
23365310Sarchie	  &ng_bridge_config_type
23465310Sarchie	},
23565310Sarchie	{
23665310Sarchie	  NGM_BRIDGE_COOKIE,
23765310Sarchie	  NGM_BRIDGE_RESET,
23865310Sarchie	  "reset",
23965310Sarchie	  NULL,
24065310Sarchie	  NULL
24165310Sarchie	},
24265310Sarchie	{
24365310Sarchie	  NGM_BRIDGE_COOKIE,
24465310Sarchie	  NGM_BRIDGE_GET_STATS,
24565310Sarchie	  "getstats",
24665310Sarchie	  &ng_parse_uint32_type,
24765310Sarchie	  &ng_bridge_stats_type
24865310Sarchie	},
24965310Sarchie	{
25065310Sarchie	  NGM_BRIDGE_COOKIE,
25165310Sarchie	  NGM_BRIDGE_CLR_STATS,
25265310Sarchie	  "clrstats",
25365310Sarchie	  &ng_parse_uint32_type,
25465310Sarchie	  NULL
25565310Sarchie	},
25665310Sarchie	{
25765310Sarchie	  NGM_BRIDGE_COOKIE,
25865310Sarchie	  NGM_BRIDGE_GETCLR_STATS,
25965310Sarchie	  "getclrstats",
26065310Sarchie	  &ng_parse_uint32_type,
26165310Sarchie	  &ng_bridge_stats_type
26265310Sarchie	},
26365310Sarchie	{
26465310Sarchie	  NGM_BRIDGE_COOKIE,
26565310Sarchie	  NGM_BRIDGE_GET_TABLE,
26665310Sarchie	  "gettable",
26765310Sarchie	  NULL,
26865310Sarchie	  &ng_bridge_host_ary_type
26965310Sarchie	},
27065310Sarchie	{ 0 }
27165310Sarchie};
27265310Sarchie
27365310Sarchie/* Node type descriptor */
27465310Sarchiestatic struct ng_type ng_bridge_typestruct = {
27570159Sjulian	NG_ABI_VERSION,
27665310Sarchie	NG_BRIDGE_NODE_TYPE,
27765310Sarchie	NULL,
27865310Sarchie	ng_bridge_constructor,
27965310Sarchie	ng_bridge_rcvmsg,
28070700Sjulian	ng_bridge_shutdown,
28165310Sarchie	ng_bridge_newhook,
28265310Sarchie	NULL,
28365310Sarchie	NULL,
28465310Sarchie	ng_bridge_rcvdata,
28565310Sarchie	ng_bridge_disconnect,
28665310Sarchie	ng_bridge_cmdlist,
28765310Sarchie};
28866887SarchieNETGRAPH_INIT(bridge, &ng_bridge_typestruct);
28965310Sarchie
29065310Sarchie/* Depend on ng_ether so we can use the Ethernet parse type */
29165310SarchieMODULE_DEPEND(ng_bridge, ng_ether, 1, 1, 1);
29265310Sarchie
29365310Sarchie/******************************************************************
29465310Sarchie		    NETGRAPH NODE METHODS
29565310Sarchie******************************************************************/
29665310Sarchie
29765310Sarchie/*
29865310Sarchie * Node constructor
29965310Sarchie */
30065310Sarchiestatic int
30170700Sjulianng_bridge_constructor(node_p node)
30265310Sarchie{
30365310Sarchie	priv_p priv;
30465310Sarchie
30565310Sarchie	/* Allocate and initialize private info */
30670870Sjulian	MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH_BRIDGE, M_NOWAIT | M_ZERO);
30765310Sarchie	if (priv == NULL)
30865310Sarchie		return (ENOMEM);
30969225Sjlemon	callout_init(&priv->timer, 0);
31065310Sarchie
31165310Sarchie	/* Allocate and initialize hash table, etc. */
31265310Sarchie	MALLOC(priv->tab, struct ng_bridge_bucket *,
31370870Sjulian	    MIN_BUCKETS * sizeof(*priv->tab), M_NETGRAPH_BRIDGE, M_NOWAIT | M_ZERO);
31465310Sarchie	if (priv->tab == NULL) {
31570870Sjulian		FREE(priv, M_NETGRAPH_BRIDGE);
31665310Sarchie		return (ENOMEM);
31765310Sarchie	}
31865310Sarchie	priv->numBuckets = MIN_BUCKETS;
31965310Sarchie	priv->hashMask = MIN_BUCKETS - 1;
32065310Sarchie	priv->conf.debugLevel = 1;
32165310Sarchie	priv->conf.loopTimeout = DEFAULT_LOOP_TIMEOUT;
32265310Sarchie	priv->conf.maxStaleness = DEFAULT_MAX_STALENESS;
32365310Sarchie	priv->conf.minStableAge = DEFAULT_MIN_STABLE_AGE;
32465310Sarchie
32570700Sjulian	/*
32670700Sjulian	 * This node has all kinds of stuff that could be screwed by SMP.
32770700Sjulian	 * Until it gets it's own internal protection, we go through in
32870700Sjulian	 * single file. This could hurt a machine bridging beteen two
32970700Sjulian	 * GB ethernets so it should be fixed.
33070700Sjulian	 * When it's fixed the process SHOULD NOT SLEEP, spinlocks please!
33170700Sjulian	 * (and atomic ops )
33270700Sjulian	 */
33370784Sjulian	NG_NODE_FORCE_WRITER(node);
33470784Sjulian	NG_NODE_SET_PRIVATE(node, priv);
33570700Sjulian	priv->node = node;
33665310Sarchie
33787997Sarchie	/* Start timer; timer is always running while node is alive */
33887997Sarchie	callout_reset(&priv->timer, hz, ng_bridge_timeout, priv->node);
33987997Sarchie
34087997Sarchie	/* Done */
34165310Sarchie	return (0);
34265310Sarchie}
34365310Sarchie
34465310Sarchie/*
34565310Sarchie * Method for attaching a new hook
34665310Sarchie */
34765310Sarchiestatic	int
34865310Sarchieng_bridge_newhook(node_p node, hook_p hook, const char *name)
34965310Sarchie{
35070784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
35165310Sarchie
35265310Sarchie	/* Check for a link hook */
35365310Sarchie	if (strncmp(name, NG_BRIDGE_HOOK_LINK_PREFIX,
35465310Sarchie	    strlen(NG_BRIDGE_HOOK_LINK_PREFIX)) == 0) {
35565310Sarchie		const char *cp;
35665310Sarchie		char *eptr;
35765310Sarchie		u_long linkNum;
35865310Sarchie
35965310Sarchie		cp = name + strlen(NG_BRIDGE_HOOK_LINK_PREFIX);
36065310Sarchie		if (!isdigit(*cp) || (cp[0] == '0' && cp[1] != '\0'))
36165310Sarchie			return (EINVAL);
36265310Sarchie		linkNum = strtoul(cp, &eptr, 10);
36365310Sarchie		if (*eptr != '\0' || linkNum >= NG_BRIDGE_MAX_LINKS)
36465310Sarchie			return (EINVAL);
36565310Sarchie		if (priv->links[linkNum] != NULL)
36665310Sarchie			return (EISCONN);
36765310Sarchie		MALLOC(priv->links[linkNum], struct ng_bridge_link *,
36870870Sjulian		    sizeof(*priv->links[linkNum]), M_NETGRAPH_BRIDGE, M_NOWAIT|M_ZERO);
36965310Sarchie		if (priv->links[linkNum] == NULL)
37065310Sarchie			return (ENOMEM);
37165310Sarchie		priv->links[linkNum]->hook = hook;
37270784Sjulian		NG_HOOK_SET_PRIVATE(hook, (void *)linkNum);
37365310Sarchie		priv->numLinks++;
37465310Sarchie		return (0);
37565310Sarchie	}
37665310Sarchie
37765310Sarchie	/* Unknown hook name */
37865310Sarchie	return (EINVAL);
37965310Sarchie}
38065310Sarchie
38165310Sarchie/*
38265310Sarchie * Receive a control message
38365310Sarchie */
38465310Sarchiestatic int
38570700Sjulianng_bridge_rcvmsg(node_p node, item_p item, hook_p lasthook)
38665310Sarchie{
38770784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
38865310Sarchie	struct ng_mesg *resp = NULL;
38965310Sarchie	int error = 0;
39070700Sjulian	struct ng_mesg *msg;
39165310Sarchie
39270700Sjulian	NGI_GET_MSG(item, msg);
39365310Sarchie	switch (msg->header.typecookie) {
39465310Sarchie	case NGM_BRIDGE_COOKIE:
39565310Sarchie		switch (msg->header.cmd) {
39665310Sarchie		case NGM_BRIDGE_GET_CONFIG:
39765310Sarchie		    {
39865310Sarchie			struct ng_bridge_config *conf;
39965310Sarchie
40065310Sarchie			NG_MKRESPONSE(resp, msg,
40165310Sarchie			    sizeof(struct ng_bridge_config), M_NOWAIT);
40265310Sarchie			if (resp == NULL) {
40365310Sarchie				error = ENOMEM;
40465310Sarchie				break;
40565310Sarchie			}
40665310Sarchie			conf = (struct ng_bridge_config *)resp->data;
40765310Sarchie			*conf = priv->conf;	/* no sanity checking needed */
40865310Sarchie			break;
40965310Sarchie		    }
41065310Sarchie		case NGM_BRIDGE_SET_CONFIG:
41165310Sarchie		    {
41265310Sarchie			struct ng_bridge_config *conf;
41365310Sarchie			int i;
41465310Sarchie
41565310Sarchie			if (msg->header.arglen
41665310Sarchie			    != sizeof(struct ng_bridge_config)) {
41765310Sarchie				error = EINVAL;
41865310Sarchie				break;
41965310Sarchie			}
42065310Sarchie			conf = (struct ng_bridge_config *)msg->data;
42165310Sarchie			priv->conf = *conf;
42265310Sarchie			for (i = 0; i < NG_BRIDGE_MAX_LINKS; i++)
42365310Sarchie				priv->conf.ipfw[i] = !!priv->conf.ipfw[i];
42465310Sarchie			break;
42565310Sarchie		    }
42665310Sarchie		case NGM_BRIDGE_RESET:
42765310Sarchie		    {
42865310Sarchie			int i;
42965310Sarchie
43065310Sarchie			/* Flush all entries in the hash table */
43165310Sarchie			ng_bridge_remove_hosts(priv, -1);
43265310Sarchie
43365310Sarchie			/* Reset all loop detection counters and stats */
43465310Sarchie			for (i = 0; i < NG_BRIDGE_MAX_LINKS; i++) {
43565310Sarchie				if (priv->links[i] == NULL)
43665310Sarchie					continue;
43765310Sarchie				priv->links[i]->loopCount = 0;
43865310Sarchie				bzero(&priv->links[i]->stats,
43965310Sarchie				    sizeof(priv->links[i]->stats));
44065310Sarchie			}
44165310Sarchie			break;
44265310Sarchie		    }
44365310Sarchie		case NGM_BRIDGE_GET_STATS:
44465310Sarchie		case NGM_BRIDGE_CLR_STATS:
44565310Sarchie		case NGM_BRIDGE_GETCLR_STATS:
44665310Sarchie		    {
44765310Sarchie			struct ng_bridge_link *link;
44865310Sarchie			int linkNum;
44965310Sarchie
45065310Sarchie			/* Get link number */
45165310Sarchie			if (msg->header.arglen != sizeof(u_int32_t)) {
45265310Sarchie				error = EINVAL;
45365310Sarchie				break;
45465310Sarchie			}
45565310Sarchie			linkNum = *((u_int32_t *)msg->data);
45665310Sarchie			if (linkNum < 0 || linkNum >= NG_BRIDGE_MAX_LINKS) {
45765310Sarchie				error = EINVAL;
45865310Sarchie				break;
45965310Sarchie			}
46065310Sarchie			if ((link = priv->links[linkNum]) == NULL) {
46165310Sarchie				error = ENOTCONN;
46265310Sarchie				break;
46365310Sarchie			}
46465310Sarchie
46565310Sarchie			/* Get/clear stats */
46665310Sarchie			if (msg->header.cmd != NGM_BRIDGE_CLR_STATS) {
46765310Sarchie				NG_MKRESPONSE(resp, msg,
46865310Sarchie				    sizeof(link->stats), M_NOWAIT);
46965310Sarchie				if (resp == NULL) {
47065310Sarchie					error = ENOMEM;
47165310Sarchie					break;
47265310Sarchie				}
47365310Sarchie				bcopy(&link->stats,
47465310Sarchie				    resp->data, sizeof(link->stats));
47565310Sarchie			}
47665310Sarchie			if (msg->header.cmd != NGM_BRIDGE_GET_STATS)
47765310Sarchie				bzero(&link->stats, sizeof(link->stats));
47865310Sarchie			break;
47965310Sarchie		    }
48065310Sarchie		case NGM_BRIDGE_GET_TABLE:
48165310Sarchie		    {
48265310Sarchie			struct ng_bridge_host_ary *ary;
48365310Sarchie			struct ng_bridge_hent *hent;
48465310Sarchie			int i = 0, bucket;
48565310Sarchie
48665310Sarchie			NG_MKRESPONSE(resp, msg, sizeof(*ary)
48765310Sarchie			    + (priv->numHosts * sizeof(*ary->hosts)), M_NOWAIT);
48865310Sarchie			if (resp == NULL) {
48965310Sarchie				error = ENOMEM;
49065310Sarchie				break;
49165310Sarchie			}
49265310Sarchie			ary = (struct ng_bridge_host_ary *)resp->data;
49365310Sarchie			ary->numHosts = priv->numHosts;
49465310Sarchie			for (bucket = 0; bucket < priv->numBuckets; bucket++) {
49565310Sarchie				SLIST_FOREACH(hent, &priv->tab[bucket], next)
49665310Sarchie					ary->hosts[i++] = hent->host;
49765310Sarchie			}
49865310Sarchie			break;
49965310Sarchie		    }
50065310Sarchie		default:
50165310Sarchie			error = EINVAL;
50265310Sarchie			break;
50365310Sarchie		}
50465310Sarchie		break;
50565310Sarchie	default:
50665310Sarchie		error = EINVAL;
50765310Sarchie		break;
50865310Sarchie	}
50965310Sarchie
51065310Sarchie	/* Done */
51170700Sjulian	NG_RESPOND_MSG(error, node, item, resp);
51270700Sjulian	NG_FREE_MSG(msg);
51365310Sarchie	return (error);
51465310Sarchie}
51565310Sarchie
51665310Sarchie/*
51765310Sarchie * Receive data on a hook
51865310Sarchie */
51965310Sarchiestatic int
52070700Sjulianng_bridge_rcvdata(hook_p hook, item_p item)
52165310Sarchie{
52270784Sjulian	const node_p node = NG_HOOK_NODE(hook);
52370784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
52465310Sarchie	struct ng_bridge_host *host;
52565310Sarchie	struct ng_bridge_link *link;
52665310Sarchie	struct ether_header *eh;
52765310Sarchie	int error = 0, linkNum;
52870700Sjulian	int manycast;
52970700Sjulian	struct mbuf *m;
53070700Sjulian	meta_p meta;
53170700Sjulian	struct ng_bridge_link *firstLink;
53265310Sarchie
53370700Sjulian	NGI_GET_M(item, m);
53465310Sarchie	/* Get link number */
535106665Sjhb	linkNum = (intptr_t)NG_HOOK_PRIVATE(hook);
53665310Sarchie	KASSERT(linkNum >= 0 && linkNum < NG_BRIDGE_MAX_LINKS,
53787599Sobrien	    ("%s: linkNum=%u", __func__, linkNum));
53865310Sarchie	link = priv->links[linkNum];
53987599Sobrien	KASSERT(link != NULL, ("%s: link%d null", __func__, linkNum));
54065310Sarchie
54165310Sarchie	/* Sanity check packet and pull up header */
54265310Sarchie	if (m->m_pkthdr.len < ETHER_HDR_LEN) {
54365310Sarchie		link->stats.recvRunts++;
54470700Sjulian		NG_FREE_ITEM(item);
54570700Sjulian		NG_FREE_M(m);
54665310Sarchie		return (EINVAL);
54765310Sarchie	}
54865310Sarchie	if (m->m_len < ETHER_HDR_LEN && !(m = m_pullup(m, ETHER_HDR_LEN))) {
54965310Sarchie		link->stats.memoryFailures++;
55070700Sjulian		NG_FREE_ITEM(item);
55165310Sarchie		return (ENOBUFS);
55265310Sarchie	}
55365310Sarchie	eh = mtod(m, struct ether_header *);
55465310Sarchie	if ((eh->ether_shost[0] & 1) != 0) {
55565310Sarchie		link->stats.recvInvalid++;
55670700Sjulian		NG_FREE_ITEM(item);
55770700Sjulian		NG_FREE_M(m);
55865310Sarchie		return (EINVAL);
55965310Sarchie	}
56065310Sarchie
56165310Sarchie	/* Is link disabled due to a loopback condition? */
56265310Sarchie	if (link->loopCount != 0) {
56365310Sarchie		link->stats.loopDrops++;
56470700Sjulian		NG_FREE_ITEM(item);
56570700Sjulian		NG_FREE_M(m);
56665310Sarchie		return (ELOOP);		/* XXX is this an appropriate error? */
56765310Sarchie	}
56865310Sarchie
56965310Sarchie	/* Update stats */
57065310Sarchie	link->stats.recvPackets++;
57165310Sarchie	link->stats.recvOctets += m->m_pkthdr.len;
57265310Sarchie	if ((manycast = (eh->ether_dhost[0] & 1)) != 0) {
57365310Sarchie		if (ETHER_EQUAL(eh->ether_dhost, ng_bridge_bcast_addr)) {
57465310Sarchie			link->stats.recvBroadcasts++;
57565310Sarchie			manycast = 2;
57665310Sarchie		} else
57765310Sarchie			link->stats.recvMulticasts++;
57865310Sarchie	}
57965310Sarchie
58065310Sarchie	/* Look up packet's source Ethernet address in hashtable */
58165310Sarchie	if ((host = ng_bridge_get(priv, eh->ether_shost)) != NULL) {
58265310Sarchie
58365310Sarchie		/* Update time since last heard from this host */
58465310Sarchie		host->staleness = 0;
58565310Sarchie
58665310Sarchie		/* Did host jump to a different link? */
58765310Sarchie		if (host->linkNum != linkNum) {
58865310Sarchie
58965310Sarchie			/*
59065310Sarchie			 * If the host's old link was recently established
59165310Sarchie			 * on the old link and it's already jumped to a new
59265310Sarchie			 * link, declare a loopback condition.
59365310Sarchie			 */
59465310Sarchie			if (host->age < priv->conf.minStableAge) {
59565310Sarchie
59665310Sarchie				/* Log the problem */
59765310Sarchie				if (priv->conf.debugLevel >= 2) {
59865310Sarchie					struct ifnet *ifp = m->m_pkthdr.rcvif;
59965310Sarchie					char suffix[32];
60065310Sarchie
60165310Sarchie					if (ifp != NULL)
60265310Sarchie						snprintf(suffix, sizeof(suffix),
60365310Sarchie						    " (%s%d)", ifp->if_name,
60465310Sarchie						    ifp->if_unit);
60565310Sarchie					else
60665310Sarchie						*suffix = '\0';
60765310Sarchie					log(LOG_WARNING, "ng_bridge: %s:"
60865310Sarchie					    " loopback detected on %s%s\n",
60965310Sarchie					    ng_bridge_nodename(node),
61070784Sjulian					    NG_HOOK_NAME(hook), suffix);
61165310Sarchie				}
61265310Sarchie
61365310Sarchie				/* Mark link as linka non grata */
61465310Sarchie				link->loopCount = priv->conf.loopTimeout;
61565310Sarchie				link->stats.loopDetects++;
61665310Sarchie
61765310Sarchie				/* Forget all hosts on this link */
61865310Sarchie				ng_bridge_remove_hosts(priv, linkNum);
61965310Sarchie
62065310Sarchie				/* Drop packet */
62165310Sarchie				link->stats.loopDrops++;
62270700Sjulian				NG_FREE_ITEM(item);
62370700Sjulian				NG_FREE_M(m);
62465310Sarchie				return (ELOOP);		/* XXX appropriate? */
62565310Sarchie			}
62665310Sarchie
62765310Sarchie			/* Move host over to new link */
62865310Sarchie			host->linkNum = linkNum;
62965310Sarchie			host->age = 0;
63065310Sarchie		}
63165310Sarchie	} else {
63265310Sarchie		if (!ng_bridge_put(priv, eh->ether_shost, linkNum)) {
63365310Sarchie			link->stats.memoryFailures++;
63470700Sjulian			NG_FREE_ITEM(item);
63570700Sjulian			NG_FREE_M(m);
63665310Sarchie			return (ENOMEM);
63765310Sarchie		}
63865310Sarchie	}
63965310Sarchie
64065310Sarchie	/* Run packet through ipfw processing, if enabled */
64165310Sarchie	if (priv->conf.ipfw[linkNum] && fw_enable && ip_fw_chk_ptr != NULL) {
64265310Sarchie		/* XXX not implemented yet */
64365310Sarchie	}
64465310Sarchie
64565310Sarchie	/*
64665310Sarchie	 * If unicast and destination host known, deliver to host's link,
64765310Sarchie	 * unless it is the same link as the packet came in on.
64865310Sarchie	 */
64965310Sarchie	if (!manycast) {
65065310Sarchie
65165310Sarchie		/* Determine packet destination link */
65265310Sarchie		if ((host = ng_bridge_get(priv, eh->ether_dhost)) != NULL) {
65365310Sarchie			struct ng_bridge_link *const destLink
65465310Sarchie			    = priv->links[host->linkNum];
65565310Sarchie
65665310Sarchie			/* If destination same as incoming link, do nothing */
65765310Sarchie			KASSERT(destLink != NULL,
65887599Sobrien			    ("%s: link%d null", __func__, host->linkNum));
65965310Sarchie			if (destLink == link) {
66070700Sjulian				NG_FREE_ITEM(item);
66170700Sjulian				NG_FREE_M(m);
66265310Sarchie				return (0);
66365310Sarchie			}
66465310Sarchie
66565310Sarchie			/* Deliver packet out the destination link */
66665310Sarchie			destLink->stats.xmitPackets++;
66765310Sarchie			destLink->stats.xmitOctets += m->m_pkthdr.len;
66870700Sjulian			NG_FWD_NEW_DATA(error, item, destLink->hook, m);
66965310Sarchie			return (error);
67065310Sarchie		}
67165310Sarchie
67265310Sarchie		/* Destination host is not known */
67365310Sarchie		link->stats.recvUnknown++;
67465310Sarchie	}
67565310Sarchie
67665310Sarchie	/* Distribute unknown, multicast, broadcast pkts to all other links */
67770700Sjulian	meta = NGI_META(item); /* peek.. */
67870700Sjulian	firstLink = NULL;
67970700Sjulian	for (linkNum = 0; linkNum <= priv->numLinks; linkNum++) {
68070700Sjulian		struct ng_bridge_link *destLink;
68165310Sarchie		meta_p meta2 = NULL;
68270700Sjulian		struct mbuf *m2 = NULL;
68365310Sarchie
68470700Sjulian		/*
68570700Sjulian		 * If we have checked all the links then now
68670700Sjulian		 * send the original on its reserved link
68770700Sjulian		 */
68870700Sjulian		if (linkNum == priv->numLinks) {
68970700Sjulian			/* If we never saw a good link, leave. */
69070700Sjulian			if (firstLink == NULL) {
69170700Sjulian				NG_FREE_ITEM(item);
69270700Sjulian				NG_FREE_M(m);
69370700Sjulian				return (0);
69470700Sjulian			}
69570700Sjulian			destLink = firstLink;
69670700Sjulian		} else {
69770700Sjulian			destLink = priv->links[linkNum];
69870700Sjulian			/* Skip incoming link and disconnected links */
69970700Sjulian			if (destLink == NULL || destLink == link) {
70070700Sjulian				continue;
70170700Sjulian			}
70270700Sjulian			if (firstLink == NULL) {
70370700Sjulian				/*
70470700Sjulian				 * This is the first usable link we have found.
70570700Sjulian				 * Reserve it for the originals.
70670700Sjulian				 * If we never find another we save a copy.
70770700Sjulian				 */
70870700Sjulian				firstLink = destLink;
70970700Sjulian				continue;
71070700Sjulian			}
71165310Sarchie
71270700Sjulian			/*
71370700Sjulian			 * It's usable link but not the reserved (first) one.
71470700Sjulian			 * Copy mbuf and meta info for sending.
71570700Sjulian			 */
716111119Simp			m2 = m_dup(m, M_DONTWAIT);	/* XXX m_copypacket() */
71765310Sarchie			if (m2 == NULL) {
71865310Sarchie				link->stats.memoryFailures++;
71970700Sjulian				NG_FREE_ITEM(item);
72070700Sjulian				NG_FREE_M(m);
72165310Sarchie				return (ENOBUFS);
72265310Sarchie			}
72365310Sarchie			if (meta != NULL
72465310Sarchie			    && (meta2 = ng_copy_meta(meta)) == NULL) {
72565310Sarchie				link->stats.memoryFailures++;
72665310Sarchie				m_freem(m2);
72770700Sjulian				NG_FREE_ITEM(item);
72870700Sjulian				NG_FREE_M(m);
72965310Sarchie				return (ENOMEM);
73065310Sarchie			}
73165310Sarchie		}
73265310Sarchie
73365310Sarchie		/* Update stats */
73465310Sarchie		destLink->stats.xmitPackets++;
73565310Sarchie		destLink->stats.xmitOctets += m->m_pkthdr.len;
73665310Sarchie		switch (manycast) {
73765310Sarchie		case 0:					/* unicast */
73865310Sarchie			break;
73965310Sarchie		case 1:					/* multicast */
74065310Sarchie			destLink->stats.xmitMulticasts++;
74165310Sarchie			break;
74265310Sarchie		case 2:					/* broadcast */
74365310Sarchie			destLink->stats.xmitBroadcasts++;
74465310Sarchie			break;
74565310Sarchie		}
74665310Sarchie
74765310Sarchie		/* Send packet */
74870700Sjulian		if (destLink == firstLink) {
74970700Sjulian			/*
75070700Sjulian			 * If we've sent all the others, send the original
75170700Sjulian			 * on the first link we found.
75270700Sjulian			 */
75370700Sjulian			NG_FWD_NEW_DATA(error, item, destLink->hook, m);
75470700Sjulian			break; /* always done last - not really needed. */
75570700Sjulian		} else {
75670700Sjulian			NG_SEND_DATA(error, destLink->hook, m2, meta2);
75770700Sjulian		}
75865310Sarchie	}
75965310Sarchie	return (error);
76065310Sarchie}
76165310Sarchie
76265310Sarchie/*
76365310Sarchie * Shutdown node
76465310Sarchie */
76565310Sarchiestatic int
76670700Sjulianng_bridge_shutdown(node_p node)
76765310Sarchie{
76870784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
76965310Sarchie
77087997Sarchie	/*
77187997Sarchie	 * Shut down everything except the timer. There's no way to
77287997Sarchie	 * avoid another possible timeout event (it may have already
77387997Sarchie	 * been dequeued), so we can't free the node yet.
77487997Sarchie	 */
77565310Sarchie	KASSERT(priv->numLinks == 0 && priv->numHosts == 0,
77665310Sarchie	    ("%s: numLinks=%d numHosts=%d",
77787599Sobrien	    __func__, priv->numLinks, priv->numHosts));
77870870Sjulian	FREE(priv->tab, M_NETGRAPH_BRIDGE);
77987997Sarchie
78087997Sarchie	/* NG_INVALID flag is now set so node will be freed at next timeout */
78165310Sarchie	return (0);
78265310Sarchie}
78365310Sarchie
78465310Sarchie/*
78565310Sarchie * Hook disconnection.
78665310Sarchie */
78765310Sarchiestatic int
78865310Sarchieng_bridge_disconnect(hook_p hook)
78965310Sarchie{
79070784Sjulian	const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
79165310Sarchie	int linkNum;
79265310Sarchie
79365310Sarchie	/* Get link number */
794106665Sjhb	linkNum = (intptr_t)NG_HOOK_PRIVATE(hook);
79565310Sarchie	KASSERT(linkNum >= 0 && linkNum < NG_BRIDGE_MAX_LINKS,
79687599Sobrien	    ("%s: linkNum=%u", __func__, linkNum));
79765310Sarchie
79865310Sarchie	/* Remove all hosts associated with this link */
79965310Sarchie	ng_bridge_remove_hosts(priv, linkNum);
80065310Sarchie
80165310Sarchie	/* Free associated link information */
80287599Sobrien	KASSERT(priv->links[linkNum] != NULL, ("%s: no link", __func__));
80370870Sjulian	FREE(priv->links[linkNum], M_NETGRAPH_BRIDGE);
80465310Sarchie	priv->links[linkNum] = NULL;
80565310Sarchie	priv->numLinks--;
80665310Sarchie
80765310Sarchie	/* If no more hooks, go away */
80870784Sjulian	if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0)
80970784Sjulian	&& (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) {
81070784Sjulian		ng_rmnode_self(NG_HOOK_NODE(hook));
81170784Sjulian	}
81265310Sarchie	return (0);
81365310Sarchie}
81465310Sarchie
81565310Sarchie/******************************************************************
81665310Sarchie		    HASH TABLE FUNCTIONS
81765310Sarchie******************************************************************/
81865310Sarchie
81965310Sarchie/*
82065310Sarchie * Hash algorithm
82165310Sarchie *
82265310Sarchie * Only hashing bytes 3-6 of the Ethernet address is sufficient and fast.
82365310Sarchie */
82465310Sarchie#define HASH(addr,mask)		( (((const u_int16_t *)(addr))[0] 	\
82565310Sarchie				 ^ ((const u_int16_t *)(addr))[1] 	\
82665310Sarchie				 ^ ((const u_int16_t *)(addr))[2]) & (mask) )
82765310Sarchie
82865310Sarchie/*
82965310Sarchie * Find a host entry in the table.
83065310Sarchie */
83165310Sarchiestatic struct ng_bridge_host *
83265310Sarchieng_bridge_get(priv_p priv, const u_char *addr)
83365310Sarchie{
83465310Sarchie	const int bucket = HASH(addr, priv->hashMask);
83565310Sarchie	struct ng_bridge_hent *hent;
83665310Sarchie
83765310Sarchie	SLIST_FOREACH(hent, &priv->tab[bucket], next) {
83865310Sarchie		if (ETHER_EQUAL(hent->host.addr, addr))
83965310Sarchie			return (&hent->host);
84065310Sarchie	}
84165310Sarchie	return (NULL);
84265310Sarchie}
84365310Sarchie
84465310Sarchie/*
84565310Sarchie * Add a new host entry to the table. This assumes the host doesn't
84665310Sarchie * already exist in the table. Returns 1 on success, 0 if there
84765310Sarchie * was a memory allocation failure.
84865310Sarchie */
84965310Sarchiestatic int
85065310Sarchieng_bridge_put(priv_p priv, const u_char *addr, int linkNum)
85165310Sarchie{
85265310Sarchie	const int bucket = HASH(addr, priv->hashMask);
85365310Sarchie	struct ng_bridge_hent *hent;
85465310Sarchie
85565310Sarchie#ifdef INVARIANTS
85665310Sarchie	/* Assert that entry does not already exist in hashtable */
85765310Sarchie	SLIST_FOREACH(hent, &priv->tab[bucket], next) {
85865310Sarchie		KASSERT(!ETHER_EQUAL(hent->host.addr, addr),
85987599Sobrien		    ("%s: entry %6D exists in table", __func__, addr, ":"));
86065310Sarchie	}
86165310Sarchie#endif
86265310Sarchie
86365310Sarchie	/* Allocate and initialize new hashtable entry */
86465310Sarchie	MALLOC(hent, struct ng_bridge_hent *,
86570870Sjulian	    sizeof(*hent), M_NETGRAPH_BRIDGE, M_NOWAIT);
86665310Sarchie	if (hent == NULL)
86765310Sarchie		return (0);
86865310Sarchie	bcopy(addr, hent->host.addr, ETHER_ADDR_LEN);
86965310Sarchie	hent->host.linkNum = linkNum;
87065310Sarchie	hent->host.staleness = 0;
87165310Sarchie	hent->host.age = 0;
87265310Sarchie
87365310Sarchie	/* Add new element to hash bucket */
87465310Sarchie	SLIST_INSERT_HEAD(&priv->tab[bucket], hent, next);
87565310Sarchie	priv->numHosts++;
87665310Sarchie
87765310Sarchie	/* Resize table if necessary */
87865310Sarchie	ng_bridge_rehash(priv);
87965310Sarchie	return (1);
88065310Sarchie}
88165310Sarchie
88265310Sarchie/*
88365310Sarchie * Resize the hash table. We try to maintain the number of buckets
88465310Sarchie * such that the load factor is in the range 0.25 to 1.0.
88565310Sarchie *
88665310Sarchie * If we can't get the new memory then we silently fail. This is OK
88765310Sarchie * because things will still work and we'll try again soon anyway.
88865310Sarchie */
88965310Sarchiestatic void
89065310Sarchieng_bridge_rehash(priv_p priv)
89165310Sarchie{
89265310Sarchie	struct ng_bridge_bucket *newTab;
89365310Sarchie	int oldBucket, newBucket;
89465310Sarchie	int newNumBuckets;
89565310Sarchie	u_int newMask;
89665310Sarchie
89765310Sarchie	/* Is table too full or too empty? */
89865310Sarchie	if (priv->numHosts > priv->numBuckets
89965310Sarchie	    && (priv->numBuckets << 1) <= MAX_BUCKETS)
90065310Sarchie		newNumBuckets = priv->numBuckets << 1;
90165310Sarchie	else if (priv->numHosts < (priv->numBuckets >> 2)
90265310Sarchie	    && (priv->numBuckets >> 2) >= MIN_BUCKETS)
90365310Sarchie		newNumBuckets = priv->numBuckets >> 2;
90465310Sarchie	else
90565310Sarchie		return;
90665310Sarchie	newMask = newNumBuckets - 1;
90765310Sarchie
90865310Sarchie	/* Allocate and initialize new table */
90965310Sarchie	MALLOC(newTab, struct ng_bridge_bucket *,
91070870Sjulian	    newNumBuckets * sizeof(*newTab), M_NETGRAPH_BRIDGE, M_NOWAIT | M_ZERO);
91165310Sarchie	if (newTab == NULL)
91265310Sarchie		return;
91365310Sarchie
91465310Sarchie	/* Move all entries from old table to new table */
91565310Sarchie	for (oldBucket = 0; oldBucket < priv->numBuckets; oldBucket++) {
91665310Sarchie		struct ng_bridge_bucket *const oldList = &priv->tab[oldBucket];
91765310Sarchie
91865310Sarchie		while (!SLIST_EMPTY(oldList)) {
91965310Sarchie			struct ng_bridge_hent *const hent
92065310Sarchie			    = SLIST_FIRST(oldList);
92165310Sarchie
92265310Sarchie			SLIST_REMOVE_HEAD(oldList, next);
92365310Sarchie			newBucket = HASH(hent->host.addr, newMask);
92465310Sarchie			SLIST_INSERT_HEAD(&newTab[newBucket], hent, next);
92565310Sarchie		}
92665310Sarchie	}
92765310Sarchie
92865310Sarchie	/* Replace old table with new one */
92965310Sarchie	if (priv->conf.debugLevel >= 3) {
93065310Sarchie		log(LOG_INFO, "ng_bridge: %s: table size %d -> %d\n",
93165310Sarchie		    ng_bridge_nodename(priv->node),
93265310Sarchie		    priv->numBuckets, newNumBuckets);
93365310Sarchie	}
93470870Sjulian	FREE(priv->tab, M_NETGRAPH_BRIDGE);
93565310Sarchie	priv->numBuckets = newNumBuckets;
93665310Sarchie	priv->hashMask = newMask;
93765310Sarchie	priv->tab = newTab;
93865310Sarchie	return;
93965310Sarchie}
94065310Sarchie
94165310Sarchie/******************************************************************
94265310Sarchie		    MISC FUNCTIONS
94365310Sarchie******************************************************************/
94465310Sarchie
94565310Sarchie/*
94665310Sarchie * Remove all hosts associated with a specific link from the hashtable.
94765310Sarchie * If linkNum == -1, then remove all hosts in the table.
94865310Sarchie */
94965310Sarchiestatic void
95065310Sarchieng_bridge_remove_hosts(priv_p priv, int linkNum)
95165310Sarchie{
95265310Sarchie	int bucket;
95365310Sarchie
95465310Sarchie	for (bucket = 0; bucket < priv->numBuckets; bucket++) {
95565310Sarchie		struct ng_bridge_hent **hptr = &SLIST_FIRST(&priv->tab[bucket]);
95665310Sarchie
95765310Sarchie		while (*hptr != NULL) {
95865310Sarchie			struct ng_bridge_hent *const hent = *hptr;
95965310Sarchie
96065310Sarchie			if (linkNum == -1 || hent->host.linkNum == linkNum) {
96165310Sarchie				*hptr = SLIST_NEXT(hent, next);
96270870Sjulian				FREE(hent, M_NETGRAPH_BRIDGE);
96365310Sarchie				priv->numHosts--;
96465310Sarchie			} else
96565310Sarchie				hptr = &SLIST_NEXT(hent, next);
96665310Sarchie		}
96765310Sarchie	}
96865310Sarchie}
96965310Sarchie
97065310Sarchie/*
97165310Sarchie * Handle our once-per-second timeout event. We do two things:
97265310Sarchie * we decrement link->loopCount for those links being muted due to
97365310Sarchie * a detected loopback condition, and we remove any hosts from
97465310Sarchie * the hashtable whom we haven't heard from in a long while.
97587997Sarchie *
97687998Sarchie * If the node has the NG_INVALID flag set, our job is to kill it.
97765310Sarchie */
97865310Sarchiestatic void
97965310Sarchieng_bridge_timeout(void *arg)
98065310Sarchie{
98165310Sarchie	const node_p node = arg;
98270784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
98365310Sarchie	int s, bucket;
98465310Sarchie	int counter = 0;
98565310Sarchie	int linkNum;
98665310Sarchie
98787997Sarchie	/* If node was shut down, this is the final lingering timeout */
98865310Sarchie	s = splnet();
98987997Sarchie	if (NG_NODE_NOT_VALID(node)) {
99087997Sarchie		FREE(priv, M_NETGRAPH);
99187997Sarchie		NG_NODE_SET_PRIVATE(node, NULL);
99270784Sjulian		NG_NODE_UNREF(node);
99365310Sarchie		splx(s);
99465310Sarchie		return;
99565310Sarchie	}
99665310Sarchie
99765310Sarchie	/* Register a new timeout, keeping the existing node reference */
99865310Sarchie	callout_reset(&priv->timer, hz, ng_bridge_timeout, node);
99965310Sarchie
100065310Sarchie	/* Update host time counters and remove stale entries */
100165310Sarchie	for (bucket = 0; bucket < priv->numBuckets; bucket++) {
100265310Sarchie		struct ng_bridge_hent **hptr = &SLIST_FIRST(&priv->tab[bucket]);
100365310Sarchie
100465310Sarchie		while (*hptr != NULL) {
100565310Sarchie			struct ng_bridge_hent *const hent = *hptr;
100665310Sarchie
100765310Sarchie			/* Make sure host's link really exists */
100865310Sarchie			KASSERT(priv->links[hent->host.linkNum] != NULL,
100965310Sarchie			    ("%s: host %6D on nonexistent link %d\n",
101087599Sobrien			    __func__, hent->host.addr, ":",
101165310Sarchie			    hent->host.linkNum));
101265310Sarchie
101365310Sarchie			/* Remove hosts we haven't heard from in a while */
101465310Sarchie			if (++hent->host.staleness >= priv->conf.maxStaleness) {
101565310Sarchie				*hptr = SLIST_NEXT(hent, next);
101670870Sjulian				FREE(hent, M_NETGRAPH_BRIDGE);
101765310Sarchie				priv->numHosts--;
101865310Sarchie			} else {
101965310Sarchie				if (hent->host.age < 0xffff)
102065310Sarchie					hent->host.age++;
102165310Sarchie				hptr = &SLIST_NEXT(hent, next);
102265310Sarchie				counter++;
102365310Sarchie			}
102465310Sarchie		}
102565310Sarchie	}
102665310Sarchie	KASSERT(priv->numHosts == counter,
102787599Sobrien	    ("%s: hosts: %d != %d", __func__, priv->numHosts, counter));
102865310Sarchie
102965310Sarchie	/* Decrease table size if necessary */
103065310Sarchie	ng_bridge_rehash(priv);
103165310Sarchie
103265310Sarchie	/* Decrease loop counter on muted looped back links */
103365310Sarchie	for (counter = linkNum = 0; linkNum < NG_BRIDGE_MAX_LINKS; linkNum++) {
103465310Sarchie		struct ng_bridge_link *const link = priv->links[linkNum];
103565310Sarchie
103665310Sarchie		if (link != NULL) {
103765310Sarchie			if (link->loopCount != 0) {
103865310Sarchie				link->loopCount--;
103965310Sarchie				if (link->loopCount == 0
104065310Sarchie				    && priv->conf.debugLevel >= 2) {
104165310Sarchie					log(LOG_INFO, "ng_bridge: %s:"
104265310Sarchie					    " restoring looped back link%d\n",
104365310Sarchie					    ng_bridge_nodename(node), linkNum);
104465310Sarchie				}
104565310Sarchie			}
104665310Sarchie			counter++;
104765310Sarchie		}
104865310Sarchie	}
104965310Sarchie	KASSERT(priv->numLinks == counter,
105087599Sobrien	    ("%s: links: %d != %d", __func__, priv->numLinks, counter));
105165310Sarchie
105265310Sarchie	/* Done */
105365310Sarchie	splx(s);
105465310Sarchie}
105565310Sarchie
105665310Sarchie/*
105765310Sarchie * Return node's "name", even if it doesn't have one.
105865310Sarchie */
105965310Sarchiestatic const char *
106065310Sarchieng_bridge_nodename(node_p node)
106165310Sarchie{
106265310Sarchie	static char name[NG_NODELEN+1];
106365310Sarchie
106470784Sjulian	if (NG_NODE_NAME(node) != NULL)
106570784Sjulian		snprintf(name, sizeof(name), "%s", NG_NODE_NAME(node));
106665310Sarchie	else
106765310Sarchie		snprintf(name, sizeof(name), "[%x]", ng_node2ID(node));
106865310Sarchie	return name;
106965310Sarchie}
107065310Sarchie
1071