xref: /dflybsd-src/sys/netgraph7/bridge/ng_bridge.c (revision 1e290df35345fbdb4da7834e2bc7c2c5a083b4a4)
1512a0173SNuno Antunes /*
2512a0173SNuno Antunes  * ng_bridge.c
3512a0173SNuno Antunes  */
4512a0173SNuno Antunes 
5512a0173SNuno Antunes /*-
6512a0173SNuno Antunes  * Copyright (c) 2000 Whistle Communications, Inc.
7512a0173SNuno Antunes  * All rights reserved.
8512a0173SNuno Antunes  *
9512a0173SNuno Antunes  * Subject to the following obligations and disclaimer of warranty, use and
10512a0173SNuno Antunes  * redistribution of this software, in source or object code forms, with or
11512a0173SNuno Antunes  * without modifications are expressly permitted by Whistle Communications;
12512a0173SNuno Antunes  * provided, however, that:
13512a0173SNuno Antunes  * 1. Any and all reproductions of the source or object code must include the
14512a0173SNuno Antunes  *    copyright notice above and the following disclaimer of warranties; and
15512a0173SNuno Antunes  * 2. No rights are granted, in any manner or form, to use Whistle
16512a0173SNuno Antunes  *    Communications, Inc. trademarks, including the mark "WHISTLE
17512a0173SNuno Antunes  *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
18512a0173SNuno Antunes  *    such appears in the above copyright notice or in the software.
19512a0173SNuno Antunes  *
20512a0173SNuno Antunes  * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
21512a0173SNuno Antunes  * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
22512a0173SNuno Antunes  * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
23512a0173SNuno Antunes  * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
24512a0173SNuno Antunes  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
25512a0173SNuno Antunes  * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
26512a0173SNuno Antunes  * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
27512a0173SNuno Antunes  * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
28512a0173SNuno Antunes  * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
29512a0173SNuno Antunes  * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
30512a0173SNuno Antunes  * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
31512a0173SNuno Antunes  * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
32512a0173SNuno Antunes  * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
33512a0173SNuno Antunes  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34512a0173SNuno Antunes  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
35512a0173SNuno Antunes  * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
36512a0173SNuno Antunes  * OF SUCH DAMAGE.
37512a0173SNuno Antunes  *
38512a0173SNuno Antunes  * Author: Archie Cobbs <archie@freebsd.org>
39512a0173SNuno Antunes  *
40512a0173SNuno Antunes  * $FreeBSD: src/sys/netgraph/ng_bridge.c,v 1.31 2005/02/09 15:14:44 ru Exp $
41512a0173SNuno Antunes  */
42512a0173SNuno Antunes 
43512a0173SNuno Antunes /*
44512a0173SNuno Antunes  * ng_bridge(4) netgraph node type
45512a0173SNuno Antunes  *
46512a0173SNuno Antunes  * The node performs standard intelligent Ethernet bridging over
47512a0173SNuno Antunes  * each of its connected hooks, or links.  A simple loop detection
48512a0173SNuno Antunes  * algorithm is included which disables a link for priv->conf.loopTimeout
49512a0173SNuno Antunes  * seconds when a host is seen to have jumped from one link to
50512a0173SNuno Antunes  * another within priv->conf.minStableAge seconds.
51512a0173SNuno Antunes  *
52512a0173SNuno Antunes  * We keep a hashtable that maps Ethernet addresses to host info,
53512a0173SNuno Antunes  * which is contained in struct ng_bridge_host's. These structures
54512a0173SNuno Antunes  * tell us on which link the host may be found. A host's entry will
55512a0173SNuno Antunes  * expire after priv->conf.maxStaleness seconds.
56512a0173SNuno Antunes  *
57512a0173SNuno Antunes  * This node is optimzed for stable networks, where machines jump
58512a0173SNuno Antunes  * from one port to the other only rarely.
59512a0173SNuno Antunes  */
60512a0173SNuno Antunes 
61512a0173SNuno Antunes #include <sys/param.h>
62512a0173SNuno Antunes #include <sys/systm.h>
63512a0173SNuno Antunes #include <sys/kernel.h>
64512a0173SNuno Antunes #include <sys/malloc.h>
65512a0173SNuno Antunes #include <sys/mbuf.h>
66512a0173SNuno Antunes #include <sys/errno.h>
67512a0173SNuno Antunes #include <sys/syslog.h>
68512a0173SNuno Antunes #include <sys/socket.h>
69512a0173SNuno Antunes #include <sys/ctype.h>
70512a0173SNuno Antunes 
71512a0173SNuno Antunes #include <net/if.h>
72512a0173SNuno Antunes #include <net/ethernet.h>
73512a0173SNuno Antunes 
74512a0173SNuno Antunes #include <netinet/in.h>
75512a0173SNuno Antunes #include <net/ipfw/ip_fw.h>
76512a0173SNuno Antunes 
77512a0173SNuno Antunes #include <netgraph7/ng_message.h>
78512a0173SNuno Antunes #include <netgraph7/netgraph.h>
79512a0173SNuno Antunes #include <netgraph7/ng_parse.h>
80512a0173SNuno Antunes #include "ng_bridge.h"
81512a0173SNuno Antunes 
82512a0173SNuno Antunes #ifdef NG_SEPARATE_MALLOC
83512a0173SNuno Antunes MALLOC_DEFINE(M_NETGRAPH_BRIDGE, "netgraph_bridge", "netgraph bridge node ");
84512a0173SNuno Antunes #else
85512a0173SNuno Antunes #define M_NETGRAPH_BRIDGE M_NETGRAPH
86512a0173SNuno Antunes #endif
87512a0173SNuno Antunes 
88512a0173SNuno Antunes /* Per-link private data */
89512a0173SNuno Antunes struct ng_bridge_link {
90512a0173SNuno Antunes 	hook_p				hook;		/* netgraph hook */
91512a0173SNuno Antunes 	u_int16_t			loopCount;	/* loop ignore timer */
92512a0173SNuno Antunes 	struct ng_bridge_link_stats	stats;		/* link stats */
93512a0173SNuno Antunes };
94512a0173SNuno Antunes 
95512a0173SNuno Antunes /* Per-node private data */
96512a0173SNuno Antunes struct ng_bridge_private {
97512a0173SNuno Antunes 	struct ng_bridge_bucket	*tab;		/* hash table bucket array */
98512a0173SNuno Antunes 	struct ng_bridge_link	*links[NG_BRIDGE_MAX_LINKS];
99512a0173SNuno Antunes 	struct ng_bridge_config	conf;		/* node configuration */
100512a0173SNuno Antunes 	node_p			node;		/* netgraph node */
101512a0173SNuno Antunes 	u_int			numHosts;	/* num entries in table */
102512a0173SNuno Antunes 	u_int			numBuckets;	/* num buckets in table */
103512a0173SNuno Antunes 	u_int			hashMask;	/* numBuckets - 1 */
104512a0173SNuno Antunes 	int			numLinks;	/* num connected links */
105512a0173SNuno Antunes 	struct callout		timer;		/* one second periodic timer */
106512a0173SNuno Antunes };
107512a0173SNuno Antunes typedef struct ng_bridge_private *priv_p;
108512a0173SNuno Antunes 
109512a0173SNuno Antunes /* Information about a host, stored in a hash table entry */
110512a0173SNuno Antunes struct ng_bridge_hent {
111512a0173SNuno Antunes 	struct ng_bridge_host		host;	/* actual host info */
112512a0173SNuno Antunes 	SLIST_ENTRY(ng_bridge_hent)	next;	/* next entry in bucket */
113512a0173SNuno Antunes };
114512a0173SNuno Antunes 
115512a0173SNuno Antunes /* Hash table bucket declaration */
116512a0173SNuno Antunes SLIST_HEAD(ng_bridge_bucket, ng_bridge_hent);
117512a0173SNuno Antunes 
118512a0173SNuno Antunes /* Netgraph node methods */
119512a0173SNuno Antunes static ng_constructor_t	ng_bridge_constructor;
120512a0173SNuno Antunes static ng_rcvmsg_t	ng_bridge_rcvmsg;
121512a0173SNuno Antunes static ng_shutdown_t	ng_bridge_shutdown;
122512a0173SNuno Antunes static ng_newhook_t	ng_bridge_newhook;
123512a0173SNuno Antunes static ng_rcvdata_t	ng_bridge_rcvdata;
124512a0173SNuno Antunes static ng_disconnect_t	ng_bridge_disconnect;
125512a0173SNuno Antunes 
126512a0173SNuno Antunes /* Other internal functions */
127512a0173SNuno Antunes static struct	ng_bridge_host *ng_bridge_get(priv_p priv, const u_char *addr);
128512a0173SNuno Antunes static int	ng_bridge_put(priv_p priv, const u_char *addr, int linkNum);
129512a0173SNuno Antunes static void	ng_bridge_rehash(priv_p priv);
130512a0173SNuno Antunes static void	ng_bridge_remove_hosts(priv_p priv, int linkNum);
131512a0173SNuno Antunes static void	ng_bridge_timeout(node_p node, hook_p hook, void *arg1, int arg2);
132512a0173SNuno Antunes static const	char *ng_bridge_nodename(node_p node);
133512a0173SNuno Antunes 
134512a0173SNuno Antunes /* Ethernet broadcast */
135512a0173SNuno Antunes static const u_char ng_bridge_bcast_addr[ETHER_ADDR_LEN] =
136512a0173SNuno Antunes     { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
137512a0173SNuno Antunes 
138512a0173SNuno Antunes /* Store each hook's link number in the private field */
139512a0173SNuno Antunes #define LINK_NUM(hook)		(*(u_int16_t *)(&(hook)->private))
140512a0173SNuno Antunes 
141512a0173SNuno Antunes /* Compare Ethernet addresses using 32 and 16 bit words instead of bytewise */
142512a0173SNuno Antunes #define ETHER_EQUAL(a,b)	(((const u_int32_t *)(a))[0] \
143512a0173SNuno Antunes 					== ((const u_int32_t *)(b))[0] \
144512a0173SNuno Antunes 				    && ((const u_int16_t *)(a))[2] \
145512a0173SNuno Antunes 					== ((const u_int16_t *)(b))[2])
146512a0173SNuno Antunes 
147512a0173SNuno Antunes /* Minimum and maximum number of hash buckets. Must be a power of two. */
148512a0173SNuno Antunes #define MIN_BUCKETS		(1 << 5)	/* 32 */
149512a0173SNuno Antunes #define MAX_BUCKETS		(1 << 14)	/* 16384 */
150512a0173SNuno Antunes 
151512a0173SNuno Antunes /* Configuration default values */
152512a0173SNuno Antunes #define DEFAULT_LOOP_TIMEOUT	60
153512a0173SNuno Antunes #define DEFAULT_MAX_STALENESS	(15 * 60)	/* same as ARP timeout */
154512a0173SNuno Antunes #define DEFAULT_MIN_STABLE_AGE	1
155512a0173SNuno Antunes 
156512a0173SNuno Antunes /******************************************************************
157512a0173SNuno Antunes 		    NETGRAPH PARSE TYPES
158512a0173SNuno Antunes ******************************************************************/
159512a0173SNuno Antunes 
160512a0173SNuno Antunes /*
161512a0173SNuno Antunes  * How to determine the length of the table returned by NGM_BRIDGE_GET_TABLE
162512a0173SNuno Antunes  */
163512a0173SNuno Antunes static int
164512a0173SNuno Antunes ng_bridge_getTableLength(const struct ng_parse_type *type,
165512a0173SNuno Antunes 	const u_char *start, const u_char *buf)
166512a0173SNuno Antunes {
167512a0173SNuno Antunes 	const struct ng_bridge_host_ary *const hary
168512a0173SNuno Antunes 	    = (const struct ng_bridge_host_ary *)(buf - sizeof(u_int32_t));
169512a0173SNuno Antunes 
170512a0173SNuno Antunes 	return hary->numHosts;
171512a0173SNuno Antunes }
172512a0173SNuno Antunes 
173512a0173SNuno Antunes /* Parse type for struct ng_bridge_host_ary */
174512a0173SNuno Antunes static const struct ng_parse_struct_field ng_bridge_host_type_fields[]
175512a0173SNuno Antunes 	= NG_BRIDGE_HOST_TYPE_INFO(&ng_parse_enaddr_type);
176512a0173SNuno Antunes static const struct ng_parse_type ng_bridge_host_type = {
177512a0173SNuno Antunes 	&ng_parse_struct_type,
178512a0173SNuno Antunes 	&ng_bridge_host_type_fields
179512a0173SNuno Antunes };
180512a0173SNuno Antunes static const struct ng_parse_array_info ng_bridge_hary_type_info = {
181512a0173SNuno Antunes 	&ng_bridge_host_type,
182512a0173SNuno Antunes 	ng_bridge_getTableLength
183512a0173SNuno Antunes };
184512a0173SNuno Antunes static const struct ng_parse_type ng_bridge_hary_type = {
185512a0173SNuno Antunes 	&ng_parse_array_type,
186512a0173SNuno Antunes 	&ng_bridge_hary_type_info
187512a0173SNuno Antunes };
188512a0173SNuno Antunes static const struct ng_parse_struct_field ng_bridge_host_ary_type_fields[]
189512a0173SNuno Antunes 	= NG_BRIDGE_HOST_ARY_TYPE_INFO(&ng_bridge_hary_type);
190512a0173SNuno Antunes static const struct ng_parse_type ng_bridge_host_ary_type = {
191512a0173SNuno Antunes 	&ng_parse_struct_type,
192512a0173SNuno Antunes 	&ng_bridge_host_ary_type_fields
193512a0173SNuno Antunes };
194512a0173SNuno Antunes 
195512a0173SNuno Antunes /* Parse type for struct ng_bridge_config */
196512a0173SNuno Antunes static const struct ng_parse_fixedarray_info ng_bridge_ipfwary_type_info = {
197512a0173SNuno Antunes 	&ng_parse_uint8_type,
198512a0173SNuno Antunes 	NG_BRIDGE_MAX_LINKS
199512a0173SNuno Antunes };
200512a0173SNuno Antunes static const struct ng_parse_type ng_bridge_ipfwary_type = {
201512a0173SNuno Antunes 	&ng_parse_fixedarray_type,
202512a0173SNuno Antunes 	&ng_bridge_ipfwary_type_info
203512a0173SNuno Antunes };
204512a0173SNuno Antunes static const struct ng_parse_struct_field ng_bridge_config_type_fields[]
205512a0173SNuno Antunes 	= NG_BRIDGE_CONFIG_TYPE_INFO(&ng_bridge_ipfwary_type);
206512a0173SNuno Antunes static const struct ng_parse_type ng_bridge_config_type = {
207512a0173SNuno Antunes 	&ng_parse_struct_type,
208512a0173SNuno Antunes 	&ng_bridge_config_type_fields
209512a0173SNuno Antunes };
210512a0173SNuno Antunes 
211512a0173SNuno Antunes /* Parse type for struct ng_bridge_link_stat */
212512a0173SNuno Antunes static const struct ng_parse_struct_field ng_bridge_stats_type_fields[]
213512a0173SNuno Antunes 	= NG_BRIDGE_STATS_TYPE_INFO;
214512a0173SNuno Antunes static const struct ng_parse_type ng_bridge_stats_type = {
215512a0173SNuno Antunes 	&ng_parse_struct_type,
216512a0173SNuno Antunes 	&ng_bridge_stats_type_fields
217512a0173SNuno Antunes };
218512a0173SNuno Antunes 
219512a0173SNuno Antunes /* List of commands and how to convert arguments to/from ASCII */
220512a0173SNuno Antunes static const struct ng_cmdlist ng_bridge_cmdlist[] = {
221512a0173SNuno Antunes 	{
222512a0173SNuno Antunes 	  NGM_BRIDGE_COOKIE,
223512a0173SNuno Antunes 	  NGM_BRIDGE_SET_CONFIG,
224512a0173SNuno Antunes 	  "setconfig",
225512a0173SNuno Antunes 	  &ng_bridge_config_type,
226512a0173SNuno Antunes 	  NULL
227512a0173SNuno Antunes 	},
228512a0173SNuno Antunes 	{
229512a0173SNuno Antunes 	  NGM_BRIDGE_COOKIE,
230512a0173SNuno Antunes 	  NGM_BRIDGE_GET_CONFIG,
231512a0173SNuno Antunes 	  "getconfig",
232512a0173SNuno Antunes 	  NULL,
233512a0173SNuno Antunes 	  &ng_bridge_config_type
234512a0173SNuno Antunes 	},
235512a0173SNuno Antunes 	{
236512a0173SNuno Antunes 	  NGM_BRIDGE_COOKIE,
237512a0173SNuno Antunes 	  NGM_BRIDGE_RESET,
238512a0173SNuno Antunes 	  "reset",
239512a0173SNuno Antunes 	  NULL,
240512a0173SNuno Antunes 	  NULL
241512a0173SNuno Antunes 	},
242512a0173SNuno Antunes 	{
243512a0173SNuno Antunes 	  NGM_BRIDGE_COOKIE,
244512a0173SNuno Antunes 	  NGM_BRIDGE_GET_STATS,
245512a0173SNuno Antunes 	  "getstats",
246512a0173SNuno Antunes 	  &ng_parse_uint32_type,
247512a0173SNuno Antunes 	  &ng_bridge_stats_type
248512a0173SNuno Antunes 	},
249512a0173SNuno Antunes 	{
250512a0173SNuno Antunes 	  NGM_BRIDGE_COOKIE,
251512a0173SNuno Antunes 	  NGM_BRIDGE_CLR_STATS,
252512a0173SNuno Antunes 	  "clrstats",
253512a0173SNuno Antunes 	  &ng_parse_uint32_type,
254512a0173SNuno Antunes 	  NULL
255512a0173SNuno Antunes 	},
256512a0173SNuno Antunes 	{
257512a0173SNuno Antunes 	  NGM_BRIDGE_COOKIE,
258512a0173SNuno Antunes 	  NGM_BRIDGE_GETCLR_STATS,
259512a0173SNuno Antunes 	  "getclrstats",
260512a0173SNuno Antunes 	  &ng_parse_uint32_type,
261512a0173SNuno Antunes 	  &ng_bridge_stats_type
262512a0173SNuno Antunes 	},
263512a0173SNuno Antunes 	{
264512a0173SNuno Antunes 	  NGM_BRIDGE_COOKIE,
265512a0173SNuno Antunes 	  NGM_BRIDGE_GET_TABLE,
266512a0173SNuno Antunes 	  "gettable",
267512a0173SNuno Antunes 	  NULL,
268512a0173SNuno Antunes 	  &ng_bridge_host_ary_type
269512a0173SNuno Antunes 	},
270512a0173SNuno Antunes 	{ 0 }
271512a0173SNuno Antunes };
272512a0173SNuno Antunes 
273512a0173SNuno Antunes /* Node type descriptor */
274512a0173SNuno Antunes static struct ng_type ng_bridge_typestruct = {
275512a0173SNuno Antunes 	.version =	NG_ABI_VERSION,
276512a0173SNuno Antunes 	.name =		NG_BRIDGE_NODE_TYPE,
277512a0173SNuno Antunes 	.constructor =	ng_bridge_constructor,
278512a0173SNuno Antunes 	.rcvmsg =	ng_bridge_rcvmsg,
279512a0173SNuno Antunes 	.shutdown =	ng_bridge_shutdown,
280512a0173SNuno Antunes 	.newhook =	ng_bridge_newhook,
281512a0173SNuno Antunes 	.rcvdata =	ng_bridge_rcvdata,
282512a0173SNuno Antunes 	.disconnect =	ng_bridge_disconnect,
283512a0173SNuno Antunes 	.cmdlist =	ng_bridge_cmdlist,
284512a0173SNuno Antunes };
285512a0173SNuno Antunes NETGRAPH_INIT(bridge, &ng_bridge_typestruct);
286512a0173SNuno Antunes 
287512a0173SNuno Antunes /******************************************************************
288512a0173SNuno Antunes 		    NETGRAPH NODE METHODS
289512a0173SNuno Antunes ******************************************************************/
290512a0173SNuno Antunes 
291512a0173SNuno Antunes /*
292512a0173SNuno Antunes  * Node constructor
293512a0173SNuno Antunes  */
294512a0173SNuno Antunes static int
295512a0173SNuno Antunes ng_bridge_constructor(node_p node)
296512a0173SNuno Antunes {
297512a0173SNuno Antunes 	priv_p priv;
298512a0173SNuno Antunes 
299512a0173SNuno Antunes 	/* Allocate and initialize private info */
300512a0173SNuno Antunes 	priv = kmalloc(sizeof(*priv), M_NETGRAPH_BRIDGE,
301512a0173SNuno Antunes 		       M_WAITOK | M_NULLOK | M_ZERO);
302512a0173SNuno Antunes 	if (priv == NULL)
303512a0173SNuno Antunes 		return (ENOMEM);
304512a0173SNuno Antunes 	ng_callout_init(&priv->timer);
305512a0173SNuno Antunes 
306512a0173SNuno Antunes 	/* Allocate and initialize hash table, etc. */
307512a0173SNuno Antunes 	priv->tab = kmalloc(MIN_BUCKETS * sizeof(*priv->tab),
308512a0173SNuno Antunes 			    M_NETGRAPH_BRIDGE, M_WAITOK | M_NULLOK | M_ZERO);
309512a0173SNuno Antunes 	if (priv->tab == NULL) {
310512a0173SNuno Antunes 		kfree(priv, M_NETGRAPH_BRIDGE);
311512a0173SNuno Antunes 		return (ENOMEM);
312512a0173SNuno Antunes 	}
313512a0173SNuno Antunes 	priv->numBuckets = MIN_BUCKETS;
314512a0173SNuno Antunes 	priv->hashMask = MIN_BUCKETS - 1;
315512a0173SNuno Antunes 	priv->conf.debugLevel = 1;
316512a0173SNuno Antunes 	priv->conf.loopTimeout = DEFAULT_LOOP_TIMEOUT;
317512a0173SNuno Antunes 	priv->conf.maxStaleness = DEFAULT_MAX_STALENESS;
318512a0173SNuno Antunes 	priv->conf.minStableAge = DEFAULT_MIN_STABLE_AGE;
319512a0173SNuno Antunes 
320512a0173SNuno Antunes 	/*
321512a0173SNuno Antunes 	 * This node has all kinds of stuff that could be screwed by SMP.
322512a0173SNuno Antunes 	 * Until it gets it's own internal protection, we go through in
323512a0173SNuno Antunes 	 * single file. This could hurt a machine bridging beteen two
324512a0173SNuno Antunes 	 * GB ethernets so it should be fixed.
325512a0173SNuno Antunes 	 * When it's fixed the process SHOULD NOT SLEEP, spinlocks please!
326512a0173SNuno Antunes 	 * (and atomic ops )
327512a0173SNuno Antunes 	 */
328512a0173SNuno Antunes 	NG_NODE_FORCE_WRITER(node);
329512a0173SNuno Antunes 	NG_NODE_SET_PRIVATE(node, priv);
330512a0173SNuno Antunes 	priv->node = node;
331512a0173SNuno Antunes 
332512a0173SNuno Antunes 	/* Start timer; timer is always running while node is alive */
333512a0173SNuno Antunes 	ng_callout(&priv->timer, node, NULL, hz, ng_bridge_timeout, NULL, 0);
334512a0173SNuno Antunes 
335512a0173SNuno Antunes 	/* Done */
336512a0173SNuno Antunes 	return (0);
337512a0173SNuno Antunes }
338512a0173SNuno Antunes 
339512a0173SNuno Antunes /*
340512a0173SNuno Antunes  * Method for attaching a new hook
341512a0173SNuno Antunes  */
342512a0173SNuno Antunes static	int
343512a0173SNuno Antunes ng_bridge_newhook(node_p node, hook_p hook, const char *name)
344512a0173SNuno Antunes {
345512a0173SNuno Antunes 	const priv_p priv = NG_NODE_PRIVATE(node);
346512a0173SNuno Antunes 
347512a0173SNuno Antunes 	/* Check for a link hook */
348512a0173SNuno Antunes 	if (strncmp(name, NG_BRIDGE_HOOK_LINK_PREFIX,
349512a0173SNuno Antunes 	    strlen(NG_BRIDGE_HOOK_LINK_PREFIX)) == 0) {
350512a0173SNuno Antunes 		const char *cp;
351512a0173SNuno Antunes 		char *eptr;
352512a0173SNuno Antunes 		u_long linkNum;
353512a0173SNuno Antunes 
354512a0173SNuno Antunes 		cp = name + strlen(NG_BRIDGE_HOOK_LINK_PREFIX);
355512a0173SNuno Antunes 		if (!isdigit(*cp) || (cp[0] == '0' && cp[1] != '\0'))
356512a0173SNuno Antunes 			return (EINVAL);
357512a0173SNuno Antunes 		linkNum = strtoul(cp, &eptr, 10);
358512a0173SNuno Antunes 		if (*eptr != '\0' || linkNum >= NG_BRIDGE_MAX_LINKS)
359512a0173SNuno Antunes 			return (EINVAL);
360512a0173SNuno Antunes 		if (priv->links[linkNum] != NULL)
361512a0173SNuno Antunes 			return (EISCONN);
362512a0173SNuno Antunes 		priv->links[linkNum] = kmalloc(sizeof(*priv->links[linkNum]),
363512a0173SNuno Antunes 					       M_NETGRAPH_BRIDGE,
364512a0173SNuno Antunes 					       M_WAITOK | M_NULLOK | M_ZERO);
365512a0173SNuno Antunes 		if (priv->links[linkNum] == NULL)
366512a0173SNuno Antunes 			return (ENOMEM);
367512a0173SNuno Antunes 		priv->links[linkNum]->hook = hook;
368512a0173SNuno Antunes 		NG_HOOK_SET_PRIVATE(hook, (void *)linkNum);
369512a0173SNuno Antunes 		priv->numLinks++;
370512a0173SNuno Antunes 		return (0);
371512a0173SNuno Antunes 	}
372512a0173SNuno Antunes 
373512a0173SNuno Antunes 	/* Unknown hook name */
374512a0173SNuno Antunes 	return (EINVAL);
375512a0173SNuno Antunes }
376512a0173SNuno Antunes 
377512a0173SNuno Antunes /*
378512a0173SNuno Antunes  * Receive a control message
379512a0173SNuno Antunes  */
380512a0173SNuno Antunes static int
381512a0173SNuno Antunes ng_bridge_rcvmsg(node_p node, item_p item, hook_p lasthook)
382512a0173SNuno Antunes {
383512a0173SNuno Antunes 	const priv_p priv = NG_NODE_PRIVATE(node);
384512a0173SNuno Antunes 	struct ng_mesg *resp = NULL;
385512a0173SNuno Antunes 	int error = 0;
386512a0173SNuno Antunes 	struct ng_mesg *msg;
387512a0173SNuno Antunes 
388512a0173SNuno Antunes 	NGI_GET_MSG(item, msg);
389512a0173SNuno Antunes 	switch (msg->header.typecookie) {
390512a0173SNuno Antunes 	case NGM_BRIDGE_COOKIE:
391512a0173SNuno Antunes 		switch (msg->header.cmd) {
392512a0173SNuno Antunes 		case NGM_BRIDGE_GET_CONFIG:
393512a0173SNuno Antunes 		    {
394512a0173SNuno Antunes 			struct ng_bridge_config *conf;
395512a0173SNuno Antunes 
396512a0173SNuno Antunes 			NG_MKRESPONSE(resp, msg,
397512a0173SNuno Antunes 			    sizeof(struct ng_bridge_config), M_WAITOK | M_NULLOK);
398512a0173SNuno Antunes 			if (resp == NULL) {
399512a0173SNuno Antunes 				error = ENOMEM;
400512a0173SNuno Antunes 				break;
401512a0173SNuno Antunes 			}
402512a0173SNuno Antunes 			conf = (struct ng_bridge_config *)resp->data;
403512a0173SNuno Antunes 			*conf = priv->conf;	/* no sanity checking needed */
404512a0173SNuno Antunes 			break;
405512a0173SNuno Antunes 		    }
406512a0173SNuno Antunes 		case NGM_BRIDGE_SET_CONFIG:
407512a0173SNuno Antunes 		    {
408512a0173SNuno Antunes 			struct ng_bridge_config *conf;
409512a0173SNuno Antunes 			int i;
410512a0173SNuno Antunes 
411512a0173SNuno Antunes 			if (msg->header.arglen
412512a0173SNuno Antunes 			    != sizeof(struct ng_bridge_config)) {
413512a0173SNuno Antunes 				error = EINVAL;
414512a0173SNuno Antunes 				break;
415512a0173SNuno Antunes 			}
416512a0173SNuno Antunes 			conf = (struct ng_bridge_config *)msg->data;
417512a0173SNuno Antunes 			priv->conf = *conf;
418512a0173SNuno Antunes 			for (i = 0; i < NG_BRIDGE_MAX_LINKS; i++)
419512a0173SNuno Antunes 				priv->conf.ipfw[i] = !!priv->conf.ipfw[i];
420512a0173SNuno Antunes 			break;
421512a0173SNuno Antunes 		    }
422512a0173SNuno Antunes 		case NGM_BRIDGE_RESET:
423512a0173SNuno Antunes 		    {
424512a0173SNuno Antunes 			int i;
425512a0173SNuno Antunes 
426512a0173SNuno Antunes 			/* Flush all entries in the hash table */
427512a0173SNuno Antunes 			ng_bridge_remove_hosts(priv, -1);
428512a0173SNuno Antunes 
429512a0173SNuno Antunes 			/* Reset all loop detection counters and stats */
430512a0173SNuno Antunes 			for (i = 0; i < NG_BRIDGE_MAX_LINKS; i++) {
431512a0173SNuno Antunes 				if (priv->links[i] == NULL)
432512a0173SNuno Antunes 					continue;
433512a0173SNuno Antunes 				priv->links[i]->loopCount = 0;
434512a0173SNuno Antunes 				bzero(&priv->links[i]->stats,
435512a0173SNuno Antunes 				    sizeof(priv->links[i]->stats));
436512a0173SNuno Antunes 			}
437512a0173SNuno Antunes 			break;
438512a0173SNuno Antunes 		    }
439512a0173SNuno Antunes 		case NGM_BRIDGE_GET_STATS:
440512a0173SNuno Antunes 		case NGM_BRIDGE_CLR_STATS:
441512a0173SNuno Antunes 		case NGM_BRIDGE_GETCLR_STATS:
442512a0173SNuno Antunes 		    {
443512a0173SNuno Antunes 			struct ng_bridge_link *link;
444512a0173SNuno Antunes 			int linkNum;
445512a0173SNuno Antunes 
446512a0173SNuno Antunes 			/* Get link number */
447512a0173SNuno Antunes 			if (msg->header.arglen != sizeof(u_int32_t)) {
448512a0173SNuno Antunes 				error = EINVAL;
449512a0173SNuno Antunes 				break;
450512a0173SNuno Antunes 			}
451512a0173SNuno Antunes 			linkNum = *((u_int32_t *)msg->data);
452512a0173SNuno Antunes 			if (linkNum < 0 || linkNum >= NG_BRIDGE_MAX_LINKS) {
453512a0173SNuno Antunes 				error = EINVAL;
454512a0173SNuno Antunes 				break;
455512a0173SNuno Antunes 			}
456512a0173SNuno Antunes 			if ((link = priv->links[linkNum]) == NULL) {
457512a0173SNuno Antunes 				error = ENOTCONN;
458512a0173SNuno Antunes 				break;
459512a0173SNuno Antunes 			}
460512a0173SNuno Antunes 
461512a0173SNuno Antunes 			/* Get/clear stats */
462512a0173SNuno Antunes 			if (msg->header.cmd != NGM_BRIDGE_CLR_STATS) {
463512a0173SNuno Antunes 				NG_MKRESPONSE(resp, msg,
464512a0173SNuno Antunes 				    sizeof(link->stats), M_WAITOK | M_NULLOK);
465512a0173SNuno Antunes 				if (resp == NULL) {
466512a0173SNuno Antunes 					error = ENOMEM;
467512a0173SNuno Antunes 					break;
468512a0173SNuno Antunes 				}
469512a0173SNuno Antunes 				bcopy(&link->stats,
470512a0173SNuno Antunes 				    resp->data, sizeof(link->stats));
471512a0173SNuno Antunes 			}
472512a0173SNuno Antunes 			if (msg->header.cmd != NGM_BRIDGE_GET_STATS)
473512a0173SNuno Antunes 				bzero(&link->stats, sizeof(link->stats));
474512a0173SNuno Antunes 			break;
475512a0173SNuno Antunes 		    }
476512a0173SNuno Antunes 		case NGM_BRIDGE_GET_TABLE:
477512a0173SNuno Antunes 		    {
478512a0173SNuno Antunes 			struct ng_bridge_host_ary *ary;
479512a0173SNuno Antunes 			struct ng_bridge_hent *hent;
480512a0173SNuno Antunes 			int i = 0, bucket;
481512a0173SNuno Antunes 
482512a0173SNuno Antunes 			NG_MKRESPONSE(resp, msg, sizeof(*ary)
483512a0173SNuno Antunes 			    + (priv->numHosts * sizeof(*ary->hosts)), M_WAITOK | M_NULLOK);
484512a0173SNuno Antunes 			if (resp == NULL) {
485512a0173SNuno Antunes 				error = ENOMEM;
486512a0173SNuno Antunes 				break;
487512a0173SNuno Antunes 			}
488512a0173SNuno Antunes 			ary = (struct ng_bridge_host_ary *)resp->data;
489512a0173SNuno Antunes 			ary->numHosts = priv->numHosts;
490512a0173SNuno Antunes 			for (bucket = 0; bucket < priv->numBuckets; bucket++) {
491512a0173SNuno Antunes 				SLIST_FOREACH(hent, &priv->tab[bucket], next)
492512a0173SNuno Antunes 					ary->hosts[i++] = hent->host;
493512a0173SNuno Antunes 			}
494512a0173SNuno Antunes 			break;
495512a0173SNuno Antunes 		    }
496512a0173SNuno Antunes 		default:
497512a0173SNuno Antunes 			error = EINVAL;
498512a0173SNuno Antunes 			break;
499512a0173SNuno Antunes 		}
500512a0173SNuno Antunes 		break;
501512a0173SNuno Antunes 	default:
502512a0173SNuno Antunes 		error = EINVAL;
503512a0173SNuno Antunes 		break;
504512a0173SNuno Antunes 	}
505512a0173SNuno Antunes 
506512a0173SNuno Antunes 	/* Done */
507512a0173SNuno Antunes 	NG_RESPOND_MSG(error, node, item, resp);
508512a0173SNuno Antunes 	NG_FREE_MSG(msg);
509512a0173SNuno Antunes 	return (error);
510512a0173SNuno Antunes }
511512a0173SNuno Antunes 
512512a0173SNuno Antunes /*
513512a0173SNuno Antunes  * Receive data on a hook
514512a0173SNuno Antunes  */
515512a0173SNuno Antunes static int
516512a0173SNuno Antunes ng_bridge_rcvdata(hook_p hook, item_p item)
517512a0173SNuno Antunes {
518512a0173SNuno Antunes 	const node_p node = NG_HOOK_NODE(hook);
519512a0173SNuno Antunes 	const priv_p priv = NG_NODE_PRIVATE(node);
520512a0173SNuno Antunes 	struct ng_bridge_host *host;
521512a0173SNuno Antunes 	struct ng_bridge_link *link;
522512a0173SNuno Antunes 	struct ether_header *eh;
523512a0173SNuno Antunes 	int error = 0, linkNum, linksSeen;
524512a0173SNuno Antunes 	int manycast;
525512a0173SNuno Antunes 	struct mbuf *m;
526512a0173SNuno Antunes 	struct ng_bridge_link *firstLink;
527512a0173SNuno Antunes 
528512a0173SNuno Antunes 	NGI_GET_M(item, m);
529512a0173SNuno Antunes 	/* Get link number */
530512a0173SNuno Antunes 	linkNum = (intptr_t)NG_HOOK_PRIVATE(hook);
531512a0173SNuno Antunes 	KASSERT(linkNum >= 0 && linkNum < NG_BRIDGE_MAX_LINKS,
532512a0173SNuno Antunes 	    ("%s: linkNum=%u", __func__, linkNum));
533512a0173SNuno Antunes 	link = priv->links[linkNum];
534512a0173SNuno Antunes 	KASSERT(link != NULL, ("%s: link%d null", __func__, linkNum));
535512a0173SNuno Antunes 
536512a0173SNuno Antunes 	/* Sanity check packet and pull up header */
537512a0173SNuno Antunes 	if (m->m_pkthdr.len < ETHER_HDR_LEN) {
538512a0173SNuno Antunes 		link->stats.recvRunts++;
539512a0173SNuno Antunes 		NG_FREE_ITEM(item);
540512a0173SNuno Antunes 		NG_FREE_M(m);
541512a0173SNuno Antunes 		return (EINVAL);
542512a0173SNuno Antunes 	}
543512a0173SNuno Antunes 	if (m->m_len < ETHER_HDR_LEN && !(m = m_pullup(m, ETHER_HDR_LEN))) {
544512a0173SNuno Antunes 		link->stats.memoryFailures++;
545512a0173SNuno Antunes 		NG_FREE_ITEM(item);
546512a0173SNuno Antunes 		return (ENOBUFS);
547512a0173SNuno Antunes 	}
548512a0173SNuno Antunes 	eh = mtod(m, struct ether_header *);
549512a0173SNuno Antunes 	if ((eh->ether_shost[0] & 1) != 0) {
550512a0173SNuno Antunes 		link->stats.recvInvalid++;
551512a0173SNuno Antunes 		NG_FREE_ITEM(item);
552512a0173SNuno Antunes 		NG_FREE_M(m);
553512a0173SNuno Antunes 		return (EINVAL);
554512a0173SNuno Antunes 	}
555512a0173SNuno Antunes 
556512a0173SNuno Antunes 	/* Is link disabled due to a loopback condition? */
557512a0173SNuno Antunes 	if (link->loopCount != 0) {
558512a0173SNuno Antunes 		link->stats.loopDrops++;
559512a0173SNuno Antunes 		NG_FREE_ITEM(item);
560512a0173SNuno Antunes 		NG_FREE_M(m);
561512a0173SNuno Antunes 		return (ELOOP);		/* XXX is this an appropriate error? */
562512a0173SNuno Antunes 	}
563512a0173SNuno Antunes 
564512a0173SNuno Antunes 	/* Update stats */
565512a0173SNuno Antunes 	link->stats.recvPackets++;
566512a0173SNuno Antunes 	link->stats.recvOctets += m->m_pkthdr.len;
567512a0173SNuno Antunes 	if ((manycast = (eh->ether_dhost[0] & 1)) != 0) {
568512a0173SNuno Antunes 		if (ETHER_EQUAL(eh->ether_dhost, ng_bridge_bcast_addr)) {
569512a0173SNuno Antunes 			link->stats.recvBroadcasts++;
570512a0173SNuno Antunes 			manycast = 2;
571512a0173SNuno Antunes 		} else
572512a0173SNuno Antunes 			link->stats.recvMulticasts++;
573512a0173SNuno Antunes 	}
574512a0173SNuno Antunes 
575512a0173SNuno Antunes 	/* Look up packet's source Ethernet address in hashtable */
576512a0173SNuno Antunes 	if ((host = ng_bridge_get(priv, eh->ether_shost)) != NULL) {
577512a0173SNuno Antunes 
578512a0173SNuno Antunes 		/* Update time since last heard from this host */
579512a0173SNuno Antunes 		host->staleness = 0;
580512a0173SNuno Antunes 
581512a0173SNuno Antunes 		/* Did host jump to a different link? */
582512a0173SNuno Antunes 		if (host->linkNum != linkNum) {
583512a0173SNuno Antunes 
584512a0173SNuno Antunes 			/*
585512a0173SNuno Antunes 			 * If the host's old link was recently established
586512a0173SNuno Antunes 			 * on the old link and it's already jumped to a new
587512a0173SNuno Antunes 			 * link, declare a loopback condition.
588512a0173SNuno Antunes 			 */
589512a0173SNuno Antunes 			if (host->age < priv->conf.minStableAge) {
590512a0173SNuno Antunes 
591512a0173SNuno Antunes 				/* Log the problem */
592512a0173SNuno Antunes 				if (priv->conf.debugLevel >= 2) {
593512a0173SNuno Antunes 					struct ifnet *ifp = m->m_pkthdr.rcvif;
594512a0173SNuno Antunes 					char suffix[32];
595512a0173SNuno Antunes 
596512a0173SNuno Antunes 					if (ifp != NULL)
597512a0173SNuno Antunes 						snprintf(suffix, sizeof(suffix),
598512a0173SNuno Antunes 						    " (%s)", ifp->if_xname);
599512a0173SNuno Antunes 					else
600512a0173SNuno Antunes 						*suffix = '\0';
601512a0173SNuno Antunes 					log(LOG_WARNING, "ng_bridge: %s:"
602512a0173SNuno Antunes 					    " loopback detected on %s%s\n",
603512a0173SNuno Antunes 					    ng_bridge_nodename(node),
604512a0173SNuno Antunes 					    NG_HOOK_NAME(hook), suffix);
605512a0173SNuno Antunes 				}
606512a0173SNuno Antunes 
607512a0173SNuno Antunes 				/* Mark link as linka non grata */
608512a0173SNuno Antunes 				link->loopCount = priv->conf.loopTimeout;
609512a0173SNuno Antunes 				link->stats.loopDetects++;
610512a0173SNuno Antunes 
611512a0173SNuno Antunes 				/* Forget all hosts on this link */
612512a0173SNuno Antunes 				ng_bridge_remove_hosts(priv, linkNum);
613512a0173SNuno Antunes 
614512a0173SNuno Antunes 				/* Drop packet */
615512a0173SNuno Antunes 				link->stats.loopDrops++;
616512a0173SNuno Antunes 				NG_FREE_ITEM(item);
617512a0173SNuno Antunes 				NG_FREE_M(m);
618512a0173SNuno Antunes 				return (ELOOP);		/* XXX appropriate? */
619512a0173SNuno Antunes 			}
620512a0173SNuno Antunes 
621512a0173SNuno Antunes 			/* Move host over to new link */
622512a0173SNuno Antunes 			host->linkNum = linkNum;
623512a0173SNuno Antunes 			host->age = 0;
624512a0173SNuno Antunes 		}
625512a0173SNuno Antunes 	} else {
626512a0173SNuno Antunes 		if (!ng_bridge_put(priv, eh->ether_shost, linkNum)) {
627512a0173SNuno Antunes 			link->stats.memoryFailures++;
628512a0173SNuno Antunes 			NG_FREE_ITEM(item);
629512a0173SNuno Antunes 			NG_FREE_M(m);
630512a0173SNuno Antunes 			return (ENOMEM);
631512a0173SNuno Antunes 		}
632512a0173SNuno Antunes 	}
633512a0173SNuno Antunes 
634512a0173SNuno Antunes 	/* Run packet through ipfw processing, if enabled */
635512a0173SNuno Antunes #if 0
636512a0173SNuno Antunes 	if (priv->conf.ipfw[linkNum] && fw_enable && ip_fw_chk_ptr != NULL) {
637512a0173SNuno Antunes 		/* XXX not implemented yet */
638512a0173SNuno Antunes 	}
639512a0173SNuno Antunes #endif
640512a0173SNuno Antunes 
641512a0173SNuno Antunes 	/*
642512a0173SNuno Antunes 	 * If unicast and destination host known, deliver to host's link,
643512a0173SNuno Antunes 	 * unless it is the same link as the packet came in on.
644512a0173SNuno Antunes 	 */
645512a0173SNuno Antunes 	if (!manycast) {
646512a0173SNuno Antunes 
647512a0173SNuno Antunes 		/* Determine packet destination link */
648512a0173SNuno Antunes 		if ((host = ng_bridge_get(priv, eh->ether_dhost)) != NULL) {
649512a0173SNuno Antunes 			struct ng_bridge_link *const destLink
650512a0173SNuno Antunes 			    = priv->links[host->linkNum];
651512a0173SNuno Antunes 
652512a0173SNuno Antunes 			/* If destination same as incoming link, do nothing */
653512a0173SNuno Antunes 			KASSERT(destLink != NULL,
654512a0173SNuno Antunes 			    ("%s: link%d null", __func__, host->linkNum));
655512a0173SNuno Antunes 			if (destLink == link) {
656512a0173SNuno Antunes 				NG_FREE_ITEM(item);
657512a0173SNuno Antunes 				NG_FREE_M(m);
658512a0173SNuno Antunes 				return (0);
659512a0173SNuno Antunes 			}
660512a0173SNuno Antunes 
661512a0173SNuno Antunes 			/* Deliver packet out the destination link */
662512a0173SNuno Antunes 			destLink->stats.xmitPackets++;
663512a0173SNuno Antunes 			destLink->stats.xmitOctets += m->m_pkthdr.len;
664512a0173SNuno Antunes 			NG_FWD_NEW_DATA(error, item, destLink->hook, m);
665512a0173SNuno Antunes 			return (error);
666512a0173SNuno Antunes 		}
667512a0173SNuno Antunes 
668512a0173SNuno Antunes 		/* Destination host is not known */
669512a0173SNuno Antunes 		link->stats.recvUnknown++;
670512a0173SNuno Antunes 	}
671512a0173SNuno Antunes 
672512a0173SNuno Antunes 	/* Distribute unknown, multicast, broadcast pkts to all other links */
673512a0173SNuno Antunes 	firstLink = NULL;
674512a0173SNuno Antunes 	for (linkNum = linksSeen = 0; linksSeen <= priv->numLinks; linkNum++) {
675512a0173SNuno Antunes 		struct ng_bridge_link *destLink;
676512a0173SNuno Antunes 		struct mbuf *m2 = NULL;
677512a0173SNuno Antunes 
678512a0173SNuno Antunes 		/*
679512a0173SNuno Antunes 		 * If we have checked all the links then now
680512a0173SNuno Antunes 		 * send the original on its reserved link
681512a0173SNuno Antunes 		 */
682512a0173SNuno Antunes 		if (linksSeen == priv->numLinks) {
683512a0173SNuno Antunes 			/* If we never saw a good link, leave. */
684512a0173SNuno Antunes 			if (firstLink == NULL) {
685512a0173SNuno Antunes 				NG_FREE_ITEM(item);
686512a0173SNuno Antunes 				NG_FREE_M(m);
687512a0173SNuno Antunes 				return (0);
688512a0173SNuno Antunes 			}
689512a0173SNuno Antunes 			destLink = firstLink;
690512a0173SNuno Antunes 		} else {
691512a0173SNuno Antunes 			destLink = priv->links[linkNum];
692512a0173SNuno Antunes 			if (destLink != NULL)
693512a0173SNuno Antunes 				linksSeen++;
694512a0173SNuno Antunes 			/* Skip incoming link and disconnected links */
695512a0173SNuno Antunes 			if (destLink == NULL || destLink == link) {
696512a0173SNuno Antunes 				continue;
697512a0173SNuno Antunes 			}
698512a0173SNuno Antunes 			if (firstLink == NULL) {
699512a0173SNuno Antunes 				/*
700512a0173SNuno Antunes 				 * This is the first usable link we have found.
701512a0173SNuno Antunes 				 * Reserve it for the originals.
702512a0173SNuno Antunes 				 * If we never find another we save a copy.
703512a0173SNuno Antunes 				 */
704512a0173SNuno Antunes 				firstLink = destLink;
705512a0173SNuno Antunes 				continue;
706512a0173SNuno Antunes 			}
707512a0173SNuno Antunes 
708512a0173SNuno Antunes 			/*
709512a0173SNuno Antunes 			 * It's usable link but not the reserved (first) one.
710512a0173SNuno Antunes 			 * Copy mbuf info for sending.
711512a0173SNuno Antunes 			 */
712512a0173SNuno Antunes 			m2 = m_dup(m, MB_DONTWAIT);	/* XXX m_copypacket() */
713512a0173SNuno Antunes 			if (m2 == NULL) {
714512a0173SNuno Antunes 				link->stats.memoryFailures++;
715512a0173SNuno Antunes 				NG_FREE_ITEM(item);
716512a0173SNuno Antunes 				NG_FREE_M(m);
717512a0173SNuno Antunes 				return (ENOBUFS);
718512a0173SNuno Antunes 			}
719512a0173SNuno Antunes 		}
720512a0173SNuno Antunes 
721512a0173SNuno Antunes 		/* Update stats */
722512a0173SNuno Antunes 		destLink->stats.xmitPackets++;
723512a0173SNuno Antunes 		destLink->stats.xmitOctets += m->m_pkthdr.len;
724512a0173SNuno Antunes 		switch (manycast) {
725512a0173SNuno Antunes 		case 0:					/* unicast */
726512a0173SNuno Antunes 			break;
727512a0173SNuno Antunes 		case 1:					/* multicast */
728512a0173SNuno Antunes 			destLink->stats.xmitMulticasts++;
729512a0173SNuno Antunes 			break;
730512a0173SNuno Antunes 		case 2:					/* broadcast */
731512a0173SNuno Antunes 			destLink->stats.xmitBroadcasts++;
732512a0173SNuno Antunes 			break;
733512a0173SNuno Antunes 		}
734512a0173SNuno Antunes 
735512a0173SNuno Antunes 		/* Send packet */
736512a0173SNuno Antunes 		if (destLink == firstLink) {
737512a0173SNuno Antunes 			/*
738512a0173SNuno Antunes 			 * If we've sent all the others, send the original
739512a0173SNuno Antunes 			 * on the first link we found.
740512a0173SNuno Antunes 			 */
741512a0173SNuno Antunes 			NG_FWD_NEW_DATA(error, item, destLink->hook, m);
742512a0173SNuno Antunes 			break; /* always done last - not really needed. */
743512a0173SNuno Antunes 		} else {
744512a0173SNuno Antunes 			NG_SEND_DATA_ONLY(error, destLink->hook, m2);
745512a0173SNuno Antunes 		}
746512a0173SNuno Antunes 	}
747512a0173SNuno Antunes 	return (error);
748512a0173SNuno Antunes }
749512a0173SNuno Antunes 
750512a0173SNuno Antunes /*
751512a0173SNuno Antunes  * Shutdown node
752512a0173SNuno Antunes  */
753512a0173SNuno Antunes static int
754512a0173SNuno Antunes ng_bridge_shutdown(node_p node)
755512a0173SNuno Antunes {
756512a0173SNuno Antunes 	const priv_p priv = NG_NODE_PRIVATE(node);
757512a0173SNuno Antunes 
758512a0173SNuno Antunes 	/*
759512a0173SNuno Antunes 	 * Shut down everything including the timer.  Even if the
760512a0173SNuno Antunes 	 * callout has already been dequeued and is about to be
761512a0173SNuno Antunes 	 * run, ng_bridge_timeout() won't be fired as the node
762512a0173SNuno Antunes 	 * is already marked NGF_INVALID, so we're safe to free
763512a0173SNuno Antunes 	 * the node now.
764512a0173SNuno Antunes 	 */
765512a0173SNuno Antunes 	KASSERT(priv->numLinks == 0 && priv->numHosts == 0,
766512a0173SNuno Antunes 	    ("%s: numLinks=%d numHosts=%d",
767512a0173SNuno Antunes 	    __func__, priv->numLinks, priv->numHosts));
768512a0173SNuno Antunes 	ng_uncallout(&priv->timer, node);
769512a0173SNuno Antunes 	NG_NODE_SET_PRIVATE(node, NULL);
770512a0173SNuno Antunes 	NG_NODE_UNREF(node);
771512a0173SNuno Antunes 	kfree(priv->tab, M_NETGRAPH_BRIDGE);
772512a0173SNuno Antunes 	kfree(priv, M_NETGRAPH_BRIDGE);
773512a0173SNuno Antunes 	return (0);
774512a0173SNuno Antunes }
775512a0173SNuno Antunes 
776512a0173SNuno Antunes /*
777512a0173SNuno Antunes  * Hook disconnection.
778512a0173SNuno Antunes  */
779512a0173SNuno Antunes static int
780512a0173SNuno Antunes ng_bridge_disconnect(hook_p hook)
781512a0173SNuno Antunes {
782512a0173SNuno Antunes 	const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
783512a0173SNuno Antunes 	int linkNum;
784512a0173SNuno Antunes 
785512a0173SNuno Antunes 	/* Get link number */
786512a0173SNuno Antunes 	linkNum = (intptr_t)NG_HOOK_PRIVATE(hook);
787512a0173SNuno Antunes 	KASSERT(linkNum >= 0 && linkNum < NG_BRIDGE_MAX_LINKS,
788512a0173SNuno Antunes 	    ("%s: linkNum=%u", __func__, linkNum));
789512a0173SNuno Antunes 
790512a0173SNuno Antunes 	/* Remove all hosts associated with this link */
791512a0173SNuno Antunes 	ng_bridge_remove_hosts(priv, linkNum);
792512a0173SNuno Antunes 
793512a0173SNuno Antunes 	/* Free associated link information */
794512a0173SNuno Antunes 	KASSERT(priv->links[linkNum] != NULL, ("%s: no link", __func__));
795512a0173SNuno Antunes 	kfree(priv->links[linkNum], M_NETGRAPH_BRIDGE);
796512a0173SNuno Antunes 	priv->links[linkNum] = NULL;
797512a0173SNuno Antunes 	priv->numLinks--;
798512a0173SNuno Antunes 
799512a0173SNuno Antunes 	/* If no more hooks, go away */
800512a0173SNuno Antunes 	if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0)
801512a0173SNuno Antunes 	&& (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) {
802512a0173SNuno Antunes 		ng_rmnode_self(NG_HOOK_NODE(hook));
803512a0173SNuno Antunes 	}
804512a0173SNuno Antunes 	return (0);
805512a0173SNuno Antunes }
806512a0173SNuno Antunes 
807512a0173SNuno Antunes /******************************************************************
808512a0173SNuno Antunes 		    HASH TABLE FUNCTIONS
809512a0173SNuno Antunes ******************************************************************/
810512a0173SNuno Antunes 
811512a0173SNuno Antunes /*
812512a0173SNuno Antunes  * Hash algorithm
813512a0173SNuno Antunes  */
814512a0173SNuno Antunes #define HASH(addr,mask)		( (((const u_int16_t *)(addr))[0] 	\
815512a0173SNuno Antunes 				 ^ ((const u_int16_t *)(addr))[1] 	\
816512a0173SNuno Antunes 				 ^ ((const u_int16_t *)(addr))[2]) & (mask) )
817512a0173SNuno Antunes 
818512a0173SNuno Antunes /*
819512a0173SNuno Antunes  * Find a host entry in the table.
820512a0173SNuno Antunes  */
821512a0173SNuno Antunes static struct ng_bridge_host *
822512a0173SNuno Antunes ng_bridge_get(priv_p priv, const u_char *addr)
823512a0173SNuno Antunes {
824512a0173SNuno Antunes 	const int bucket = HASH(addr, priv->hashMask);
825512a0173SNuno Antunes 	struct ng_bridge_hent *hent;
826512a0173SNuno Antunes 
827512a0173SNuno Antunes 	SLIST_FOREACH(hent, &priv->tab[bucket], next) {
828512a0173SNuno Antunes 		if (ETHER_EQUAL(hent->host.addr, addr))
829512a0173SNuno Antunes 			return (&hent->host);
830512a0173SNuno Antunes 	}
831512a0173SNuno Antunes 	return (NULL);
832512a0173SNuno Antunes }
833512a0173SNuno Antunes 
834512a0173SNuno Antunes /*
835512a0173SNuno Antunes  * Add a new host entry to the table. This assumes the host doesn't
836512a0173SNuno Antunes  * already exist in the table. Returns 1 on success, 0 if there
837512a0173SNuno Antunes  * was a memory allocation failure.
838512a0173SNuno Antunes  */
839512a0173SNuno Antunes static int
840512a0173SNuno Antunes ng_bridge_put(priv_p priv, const u_char *addr, int linkNum)
841512a0173SNuno Antunes {
842512a0173SNuno Antunes 	const int bucket = HASH(addr, priv->hashMask);
843512a0173SNuno Antunes 	struct ng_bridge_hent *hent;
844512a0173SNuno Antunes 
845512a0173SNuno Antunes #ifdef INVARIANTS
846512a0173SNuno Antunes 	/* Assert that entry does not already exist in hashtable */
847512a0173SNuno Antunes 	SLIST_FOREACH(hent, &priv->tab[bucket], next) {
848512a0173SNuno Antunes 		KASSERT(!ETHER_EQUAL(hent->host.addr, addr),
849*1e290df3SAntonio Huete Jimenez 		    ("%s: entry %s exists in table", __func__, ether_sprintf(addr)));
850512a0173SNuno Antunes 	}
851512a0173SNuno Antunes #endif
852512a0173SNuno Antunes 
853512a0173SNuno Antunes 	/* Allocate and initialize new hashtable entry */
854512a0173SNuno Antunes 	hent = kmalloc(sizeof(*hent), M_NETGRAPH_BRIDGE, M_WAITOK | M_NULLOK);
855512a0173SNuno Antunes 	if (hent == NULL)
856512a0173SNuno Antunes 		return (0);
857512a0173SNuno Antunes 	bcopy(addr, hent->host.addr, ETHER_ADDR_LEN);
858512a0173SNuno Antunes 	hent->host.linkNum = linkNum;
859512a0173SNuno Antunes 	hent->host.staleness = 0;
860512a0173SNuno Antunes 	hent->host.age = 0;
861512a0173SNuno Antunes 
862512a0173SNuno Antunes 	/* Add new element to hash bucket */
863512a0173SNuno Antunes 	SLIST_INSERT_HEAD(&priv->tab[bucket], hent, next);
864512a0173SNuno Antunes 	priv->numHosts++;
865512a0173SNuno Antunes 
866512a0173SNuno Antunes 	/* Resize table if necessary */
867512a0173SNuno Antunes 	ng_bridge_rehash(priv);
868512a0173SNuno Antunes 	return (1);
869512a0173SNuno Antunes }
870512a0173SNuno Antunes 
871512a0173SNuno Antunes /*
872512a0173SNuno Antunes  * Resize the hash table. We try to maintain the number of buckets
873512a0173SNuno Antunes  * such that the load factor is in the range 0.25 to 1.0.
874512a0173SNuno Antunes  *
875512a0173SNuno Antunes  * If we can't get the new memory then we silently fail. This is OK
876512a0173SNuno Antunes  * because things will still work and we'll try again soon anyway.
877512a0173SNuno Antunes  */
878512a0173SNuno Antunes static void
879512a0173SNuno Antunes ng_bridge_rehash(priv_p priv)
880512a0173SNuno Antunes {
881512a0173SNuno Antunes 	struct ng_bridge_bucket *newTab;
882512a0173SNuno Antunes 	int oldBucket, newBucket;
883512a0173SNuno Antunes 	int newNumBuckets;
884512a0173SNuno Antunes 	u_int newMask;
885512a0173SNuno Antunes 
886512a0173SNuno Antunes 	/* Is table too full or too empty? */
887512a0173SNuno Antunes 	if (priv->numHosts > priv->numBuckets
888512a0173SNuno Antunes 	    && (priv->numBuckets << 1) <= MAX_BUCKETS)
889512a0173SNuno Antunes 		newNumBuckets = priv->numBuckets << 1;
890512a0173SNuno Antunes 	else if (priv->numHosts < (priv->numBuckets >> 2)
891512a0173SNuno Antunes 	    && (priv->numBuckets >> 2) >= MIN_BUCKETS)
892512a0173SNuno Antunes 		newNumBuckets = priv->numBuckets >> 2;
893512a0173SNuno Antunes 	else
894512a0173SNuno Antunes 		return;
895512a0173SNuno Antunes 	newMask = newNumBuckets - 1;
896512a0173SNuno Antunes 
897512a0173SNuno Antunes 	/* Allocate and initialize new table */
898512a0173SNuno Antunes 	newTab = kmalloc(newNumBuckets * sizeof(*newTab), M_NETGRAPH_BRIDGE,
899512a0173SNuno Antunes 			 M_NOWAIT | M_ZERO);
900512a0173SNuno Antunes 	if (newTab == NULL)
901512a0173SNuno Antunes 		return;
902512a0173SNuno Antunes 
903512a0173SNuno Antunes 	/* Move all entries from old table to new table */
904512a0173SNuno Antunes 	for (oldBucket = 0; oldBucket < priv->numBuckets; oldBucket++) {
905512a0173SNuno Antunes 		struct ng_bridge_bucket *const oldList = &priv->tab[oldBucket];
906512a0173SNuno Antunes 
907512a0173SNuno Antunes 		while (!SLIST_EMPTY(oldList)) {
908512a0173SNuno Antunes 			struct ng_bridge_hent *const hent
909512a0173SNuno Antunes 			    = SLIST_FIRST(oldList);
910512a0173SNuno Antunes 
911512a0173SNuno Antunes 			SLIST_REMOVE_HEAD(oldList, next);
912512a0173SNuno Antunes 			newBucket = HASH(hent->host.addr, newMask);
913512a0173SNuno Antunes 			SLIST_INSERT_HEAD(&newTab[newBucket], hent, next);
914512a0173SNuno Antunes 		}
915512a0173SNuno Antunes 	}
916512a0173SNuno Antunes 
917512a0173SNuno Antunes 	/* Replace old table with new one */
918512a0173SNuno Antunes 	if (priv->conf.debugLevel >= 3) {
919512a0173SNuno Antunes 		log(LOG_INFO, "ng_bridge: %s: table size %d -> %d\n",
920512a0173SNuno Antunes 		    ng_bridge_nodename(priv->node),
921512a0173SNuno Antunes 		    priv->numBuckets, newNumBuckets);
922512a0173SNuno Antunes 	}
923512a0173SNuno Antunes 	kfree(priv->tab, M_NETGRAPH_BRIDGE);
924512a0173SNuno Antunes 	priv->numBuckets = newNumBuckets;
925512a0173SNuno Antunes 	priv->hashMask = newMask;
926512a0173SNuno Antunes 	priv->tab = newTab;
927512a0173SNuno Antunes 	return;
928512a0173SNuno Antunes }
929512a0173SNuno Antunes 
930512a0173SNuno Antunes /******************************************************************
931512a0173SNuno Antunes 		    MISC FUNCTIONS
932512a0173SNuno Antunes ******************************************************************/
933512a0173SNuno Antunes 
934512a0173SNuno Antunes /*
935512a0173SNuno Antunes  * Remove all hosts associated with a specific link from the hashtable.
936512a0173SNuno Antunes  * If linkNum == -1, then remove all hosts in the table.
937512a0173SNuno Antunes  */
938512a0173SNuno Antunes static void
939512a0173SNuno Antunes ng_bridge_remove_hosts(priv_p priv, int linkNum)
940512a0173SNuno Antunes {
941512a0173SNuno Antunes 	int bucket;
942512a0173SNuno Antunes 
943512a0173SNuno Antunes 	for (bucket = 0; bucket < priv->numBuckets; bucket++) {
944512a0173SNuno Antunes 		struct ng_bridge_hent **hptr = &SLIST_FIRST(&priv->tab[bucket]);
945512a0173SNuno Antunes 
946512a0173SNuno Antunes 		while (*hptr != NULL) {
947512a0173SNuno Antunes 			struct ng_bridge_hent *const hent = *hptr;
948512a0173SNuno Antunes 
949512a0173SNuno Antunes 			if (linkNum == -1 || hent->host.linkNum == linkNum) {
950512a0173SNuno Antunes 				*hptr = SLIST_NEXT(hent, next);
951512a0173SNuno Antunes 				kfree(hent, M_NETGRAPH_BRIDGE);
952512a0173SNuno Antunes 				priv->numHosts--;
953512a0173SNuno Antunes 			} else
954512a0173SNuno Antunes 				hptr = &SLIST_NEXT(hent, next);
955512a0173SNuno Antunes 		}
956512a0173SNuno Antunes 	}
957512a0173SNuno Antunes }
958512a0173SNuno Antunes 
959512a0173SNuno Antunes /*
960512a0173SNuno Antunes  * Handle our once-per-second timeout event. We do two things:
961512a0173SNuno Antunes  * we decrement link->loopCount for those links being muted due to
962512a0173SNuno Antunes  * a detected loopback condition, and we remove any hosts from
963512a0173SNuno Antunes  * the hashtable whom we haven't heard from in a long while.
964512a0173SNuno Antunes  */
965512a0173SNuno Antunes static void
966512a0173SNuno Antunes ng_bridge_timeout(node_p node, hook_p hook, void *arg1, int arg2)
967512a0173SNuno Antunes {
968512a0173SNuno Antunes 	const priv_p priv = NG_NODE_PRIVATE(node);
969512a0173SNuno Antunes 	int bucket;
970512a0173SNuno Antunes 	int counter = 0;
971512a0173SNuno Antunes 	int linkNum;
972512a0173SNuno Antunes 
973512a0173SNuno Antunes 	/* Update host time counters and remove stale entries */
974512a0173SNuno Antunes 	for (bucket = 0; bucket < priv->numBuckets; bucket++) {
975512a0173SNuno Antunes 		struct ng_bridge_hent **hptr = &SLIST_FIRST(&priv->tab[bucket]);
976512a0173SNuno Antunes 
977512a0173SNuno Antunes 		while (*hptr != NULL) {
978512a0173SNuno Antunes 			struct ng_bridge_hent *const hent = *hptr;
979512a0173SNuno Antunes 
980512a0173SNuno Antunes 			/* Make sure host's link really exists */
981512a0173SNuno Antunes 			KASSERT(priv->links[hent->host.linkNum] != NULL,
982*1e290df3SAntonio Huete Jimenez 			    ("%s: host %s on nonexistent link %d",
983*1e290df3SAntonio Huete Jimenez 			    __func__, ether_sprintf(hent->host.addr),
984512a0173SNuno Antunes 			    hent->host.linkNum));
985512a0173SNuno Antunes 
986512a0173SNuno Antunes 			/* Remove hosts we haven't heard from in a while */
987512a0173SNuno Antunes 			if (++hent->host.staleness >= priv->conf.maxStaleness) {
988512a0173SNuno Antunes 				*hptr = SLIST_NEXT(hent, next);
989512a0173SNuno Antunes 				kfree(hent, M_NETGRAPH_BRIDGE);
990512a0173SNuno Antunes 				priv->numHosts--;
991512a0173SNuno Antunes 			} else {
992512a0173SNuno Antunes 				if (hent->host.age < 0xffff)
993512a0173SNuno Antunes 					hent->host.age++;
994512a0173SNuno Antunes 				hptr = &SLIST_NEXT(hent, next);
995512a0173SNuno Antunes 				counter++;
996512a0173SNuno Antunes 			}
997512a0173SNuno Antunes 		}
998512a0173SNuno Antunes 	}
999512a0173SNuno Antunes 	KASSERT(priv->numHosts == counter,
1000512a0173SNuno Antunes 	    ("%s: hosts: %d != %d", __func__, priv->numHosts, counter));
1001512a0173SNuno Antunes 
1002512a0173SNuno Antunes 	/* Decrease table size if necessary */
1003512a0173SNuno Antunes 	ng_bridge_rehash(priv);
1004512a0173SNuno Antunes 
1005512a0173SNuno Antunes 	/* Decrease loop counter on muted looped back links */
1006512a0173SNuno Antunes 	for (counter = linkNum = 0; linkNum < NG_BRIDGE_MAX_LINKS; linkNum++) {
1007512a0173SNuno Antunes 		struct ng_bridge_link *const link = priv->links[linkNum];
1008512a0173SNuno Antunes 
1009512a0173SNuno Antunes 		if (link != NULL) {
1010512a0173SNuno Antunes 			if (link->loopCount != 0) {
1011512a0173SNuno Antunes 				link->loopCount--;
1012512a0173SNuno Antunes 				if (link->loopCount == 0
1013512a0173SNuno Antunes 				    && priv->conf.debugLevel >= 2) {
1014512a0173SNuno Antunes 					log(LOG_INFO, "ng_bridge: %s:"
1015512a0173SNuno Antunes 					    " restoring looped back link%d\n",
1016512a0173SNuno Antunes 					    ng_bridge_nodename(node), linkNum);
1017512a0173SNuno Antunes 				}
1018512a0173SNuno Antunes 			}
1019512a0173SNuno Antunes 			counter++;
1020512a0173SNuno Antunes 		}
1021512a0173SNuno Antunes 	}
1022512a0173SNuno Antunes 	KASSERT(priv->numLinks == counter,
1023512a0173SNuno Antunes 	    ("%s: links: %d != %d", __func__, priv->numLinks, counter));
1024512a0173SNuno Antunes 
1025512a0173SNuno Antunes 	/* Register a new timeout, keeping the existing node reference */
1026512a0173SNuno Antunes 	ng_callout(&priv->timer, node, NULL, hz, ng_bridge_timeout, NULL, 0);
1027512a0173SNuno Antunes }
1028512a0173SNuno Antunes 
1029512a0173SNuno Antunes /*
1030512a0173SNuno Antunes  * Return node's "name", even if it doesn't have one.
1031512a0173SNuno Antunes  */
1032512a0173SNuno Antunes static const char *
1033512a0173SNuno Antunes ng_bridge_nodename(node_p node)
1034512a0173SNuno Antunes {
1035512a0173SNuno Antunes 	static char name[NG_NODESIZ];
1036512a0173SNuno Antunes 
1037512a0173SNuno Antunes 	if (NG_NODE_NAME(node) != NULL)
1038512a0173SNuno Antunes 		snprintf(name, sizeof(name), "%s", NG_NODE_NAME(node));
1039512a0173SNuno Antunes 	else
1040512a0173SNuno Antunes 		snprintf(name, sizeof(name), "[%x]", ng_node2ID(node));
1041512a0173SNuno Antunes 	return name;
1042512a0173SNuno Antunes }
1043512a0173SNuno Antunes 
1044