ng_bridge.c revision 111119
1112918Sjeff
2112918Sjeff/*
3112918Sjeff * ng_bridge.c
4112918Sjeff *
5112918Sjeff * Copyright (c) 2000 Whistle Communications, Inc.
6112918Sjeff * All rights reserved.
7112918Sjeff *
8112918Sjeff * Subject to the following obligations and disclaimer of warranty, use and
9112918Sjeff * redistribution of this software, in source or object code forms, with or
10112918Sjeff * without modifications are expressly permitted by Whistle Communications;
11112918Sjeff * provided, however, that:
12112918Sjeff * 1. Any and all reproductions of the source or object code must include the
13112918Sjeff *    copyright notice above and the following disclaimer of warranties; and
14112918Sjeff * 2. No rights are granted, in any manner or form, to use Whistle
15112918Sjeff *    Communications, Inc. trademarks, including the mark "WHISTLE
16112918Sjeff *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
17112918Sjeff *    such appears in the above copyright notice or in the software.
18112918Sjeff *
19112918Sjeff * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
20112918Sjeff * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
21112918Sjeff * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
22112918Sjeff * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
23112918Sjeff * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
24112918Sjeff * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
25112918Sjeff * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
26112918Sjeff * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
27112918Sjeff * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
28112918Sjeff * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
29112918Sjeff * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
30112918Sjeff * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
31112918Sjeff * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
32112918Sjeff * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33112918Sjeff * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34112918Sjeff * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
35112918Sjeff * OF SUCH DAMAGE.
36112918Sjeff *
37112918Sjeff * Author: Archie Cobbs <archie@freebsd.org>
38112918Sjeff *
39112918Sjeff * $FreeBSD: head/sys/netgraph/ng_bridge.c 111119 2003-02-19 05:47:46Z imp $
40112918Sjeff */
41112918Sjeff
42112918Sjeff/*
43112918Sjeff * ng_bridge(4) netgraph node type
44112918Sjeff *
45112918Sjeff * The node performs standard intelligent Ethernet bridging over
46115035Smtm * each of its connected hooks, or links.  A simple loop detection
47115035Smtm * algorithm is included which disables a link for priv->conf.loopTimeout
48112918Sjeff * seconds when a host is seen to have jumped from one link to
49112918Sjeff * another within priv->conf.minStableAge seconds.
50112918Sjeff *
51112918Sjeff * We keep a hashtable that maps Ethernet addresses to host info,
52112918Sjeff * which is contained in struct ng_bridge_host's. These structures
53112918Sjeff * tell us on which link the host may be found. A host's entry will
54112918Sjeff * expire after priv->conf.maxStaleness seconds.
55112918Sjeff *
56112918Sjeff * This node is optimzed for stable networks, where machines jump
57112918Sjeff * from one port to the other only rarely.
58112918Sjeff */
59112918Sjeff
60112918Sjeff#include <sys/param.h>
61112918Sjeff#include <sys/systm.h>
62112918Sjeff#include <sys/kernel.h>
63112918Sjeff#include <sys/malloc.h>
64112918Sjeff#include <sys/mbuf.h>
65112918Sjeff#include <sys/errno.h>
66112918Sjeff#include <sys/syslog.h>
67112918Sjeff#include <sys/socket.h>
68112918Sjeff#include <sys/ctype.h>
69112918Sjeff
70112918Sjeff#include <net/if.h>
71112918Sjeff#include <net/ethernet.h>
72112918Sjeff
73112918Sjeff#include <netinet/in.h>
74112918Sjeff#include <netinet/ip_fw.h>
75112918Sjeff
76112918Sjeff#include <netgraph/ng_message.h>
77112918Sjeff#include <netgraph/netgraph.h>
78112918Sjeff#include <netgraph/ng_parse.h>
79112918Sjeff#include <netgraph/ng_bridge.h>
80112918Sjeff#include <netgraph/ng_ether.h>
81112918Sjeff
82112918Sjeff#ifdef NG_SEPARATE_MALLOC
83112918SjeffMALLOC_DEFINE(M_NETGRAPH_BRIDGE, "netgraph_bridge", "netgraph bridge node ");
84112918Sjeff#else
85112918Sjeff#define M_NETGRAPH_BRIDGE M_NETGRAPH
86112918Sjeff#endif
87112918Sjeff
88112918Sjeff/* Per-link private data */
89112918Sjeffstruct ng_bridge_link {
90112918Sjeff	hook_p				hook;		/* netgraph hook */
91112918Sjeff	u_int16_t			loopCount;	/* loop ignore timer */
92112918Sjeff	struct ng_bridge_link_stats	stats;		/* link stats */
93112918Sjeff};
94112918Sjeff
95112918Sjeff/* Per-node private data */
96112918Sjeffstruct ng_bridge_private {
97112918Sjeff	struct ng_bridge_bucket	*tab;		/* hash table bucket array */
98112918Sjeff	struct ng_bridge_link	*links[NG_BRIDGE_MAX_LINKS];
99112918Sjeff	struct ng_bridge_config	conf;		/* node configuration */
100112918Sjeff	node_p			node;		/* netgraph node */
101112918Sjeff	u_int			numHosts;	/* num entries in table */
102112918Sjeff	u_int			numBuckets;	/* num buckets in table */
103112918Sjeff	u_int			hashMask;	/* numBuckets - 1 */
104112918Sjeff	int			numLinks;	/* num connected links */
105112918Sjeff	struct callout		timer;		/* one second periodic timer */
106112918Sjeff};
107112918Sjefftypedef struct ng_bridge_private *priv_p;
108112918Sjeff
109112918Sjeff/* Information about a host, stored in a hash table entry */
110112918Sjeffstruct ng_bridge_hent {
111112918Sjeff	struct ng_bridge_host		host;	/* actual host info */
112112918Sjeff	SLIST_ENTRY(ng_bridge_hent)	next;	/* next entry in bucket */
113112918Sjeff};
114112918Sjeff
115112918Sjeff/* Hash table bucket declaration */
116112918SjeffSLIST_HEAD(ng_bridge_bucket, ng_bridge_hent);
117112918Sjeff
118112918Sjeff/* Netgraph node methods */
119112918Sjeffstatic ng_constructor_t	ng_bridge_constructor;
120112918Sjeffstatic ng_rcvmsg_t	ng_bridge_rcvmsg;
121112918Sjeffstatic ng_shutdown_t	ng_bridge_shutdown;
122112918Sjeffstatic ng_newhook_t	ng_bridge_newhook;
123112918Sjeffstatic ng_rcvdata_t	ng_bridge_rcvdata;
124112918Sjeffstatic ng_disconnect_t	ng_bridge_disconnect;
125112918Sjeff
126112918Sjeff/* Other internal functions */
127112918Sjeffstatic struct	ng_bridge_host *ng_bridge_get(priv_p priv, const u_char *addr);
128112918Sjeffstatic int	ng_bridge_put(priv_p priv, const u_char *addr, int linkNum);
129112918Sjeffstatic void	ng_bridge_rehash(priv_p priv);
130112918Sjeffstatic void	ng_bridge_remove_hosts(priv_p priv, int linkNum);
131112918Sjeffstatic void	ng_bridge_timeout(void *arg);
132112918Sjeffstatic const	char *ng_bridge_nodename(node_p node);
133112918Sjeff
134112918Sjeff/* Ethernet broadcast */
135112918Sjeffstatic const u_char ng_bridge_bcast_addr[ETHER_ADDR_LEN] =
136112918Sjeff    { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
137112918Sjeff
138112918Sjeff/* Store each hook's link number in the private field */
139112918Sjeff#define LINK_NUM(hook)		(*(u_int16_t *)(&(hook)->private))
140112918Sjeff
141112918Sjeff/* Compare Ethernet addresses using 32 and 16 bit words instead of bytewise */
142112918Sjeff#define ETHER_EQUAL(a,b)	(((const u_int32_t *)(a))[0] \
143112918Sjeff					== ((const u_int32_t *)(b))[0] \
144112918Sjeff				    && ((const u_int16_t *)(a))[2] \
145112918Sjeff					== ((const u_int16_t *)(b))[2])
146112918Sjeff
147112918Sjeff/* Minimum and maximum number of hash buckets. Must be a power of two. */
148112918Sjeff#define MIN_BUCKETS		(1 << 5)	/* 32 */
149112918Sjeff#define MAX_BUCKETS		(1 << 14)	/* 16384 */
150112918Sjeff
151112918Sjeff/* Configuration default values */
152112918Sjeff#define DEFAULT_LOOP_TIMEOUT	60
153112918Sjeff#define DEFAULT_MAX_STALENESS	(15 * 60)	/* same as ARP timeout */
154112918Sjeff#define DEFAULT_MIN_STABLE_AGE	1
155112918Sjeff
156112918Sjeff/******************************************************************
157112918Sjeff		    NETGRAPH PARSE TYPES
158112918Sjeff******************************************************************/
159112918Sjeff
160112918Sjeff/*
161112918Sjeff * How to determine the length of the table returned by NGM_BRIDGE_GET_TABLE
162112918Sjeff */
163112918Sjeffstatic int
164112918Sjeffng_bridge_getTableLength(const struct ng_parse_type *type,
165112918Sjeff	const u_char *start, const u_char *buf)
166112918Sjeff{
167115035Smtm	const struct ng_bridge_host_ary *const hary
168112918Sjeff	    = (const struct ng_bridge_host_ary *)(buf - sizeof(u_int32_t));
169112918Sjeff
170112918Sjeff	return hary->numHosts;
171112918Sjeff}
172112918Sjeff
173112918Sjeff/* Parse type for struct ng_bridge_host_ary */
174112918Sjeffstatic const struct ng_parse_struct_field ng_bridge_host_type_fields[]
175112918Sjeff	= NG_BRIDGE_HOST_TYPE_INFO(&ng_ether_enaddr_type);
176112918Sjeffstatic const struct ng_parse_type ng_bridge_host_type = {
177112918Sjeff	&ng_parse_struct_type,
178112918Sjeff	&ng_bridge_host_type_fields
179112918Sjeff};
180115035Smtmstatic const struct ng_parse_array_info ng_bridge_hary_type_info = {
181115035Smtm	&ng_bridge_host_type,
182115035Smtm	ng_bridge_getTableLength
183115035Smtm};
184115035Smtmstatic const struct ng_parse_type ng_bridge_hary_type = {
185115035Smtm	&ng_parse_array_type,
186115035Smtm	&ng_bridge_hary_type_info
187115035Smtm};
188115035Smtmstatic const struct ng_parse_struct_field ng_bridge_host_ary_type_fields[]
189115035Smtm	= NG_BRIDGE_HOST_ARY_TYPE_INFO(&ng_bridge_hary_type);
190112918Sjeffstatic const struct ng_parse_type ng_bridge_host_ary_type = {
191112918Sjeff	&ng_parse_struct_type,
192112918Sjeff	&ng_bridge_host_ary_type_fields
193112918Sjeff};
194112918Sjeff
195112918Sjeff/* Parse type for struct ng_bridge_config */
196112918Sjeffstatic const struct ng_parse_fixedarray_info ng_bridge_ipfwary_type_info = {
197112918Sjeff	&ng_parse_uint8_type,
198112918Sjeff	NG_BRIDGE_MAX_LINKS
199112918Sjeff};
200112918Sjeffstatic const struct ng_parse_type ng_bridge_ipfwary_type = {
201112918Sjeff	&ng_parse_fixedarray_type,
202112918Sjeff	&ng_bridge_ipfwary_type_info
203112918Sjeff};
204112918Sjeffstatic const struct ng_parse_struct_field ng_bridge_config_type_fields[]
205112918Sjeff	= NG_BRIDGE_CONFIG_TYPE_INFO(&ng_bridge_ipfwary_type);
206112918Sjeffstatic const struct ng_parse_type ng_bridge_config_type = {
207112918Sjeff	&ng_parse_struct_type,
208112918Sjeff	&ng_bridge_config_type_fields
209112918Sjeff};
210112918Sjeff
211112918Sjeff/* Parse type for struct ng_bridge_link_stat */
212112918Sjeffstatic const struct ng_parse_struct_field ng_bridge_stats_type_fields[]
213112918Sjeff	= NG_BRIDGE_STATS_TYPE_INFO;
214112918Sjeffstatic const struct ng_parse_type ng_bridge_stats_type = {
215112918Sjeff	&ng_parse_struct_type,
216112918Sjeff	&ng_bridge_stats_type_fields
217112918Sjeff};
218112918Sjeff
219112918Sjeff/* List of commands and how to convert arguments to/from ASCII */
220112918Sjeffstatic const struct ng_cmdlist ng_bridge_cmdlist[] = {
221112918Sjeff	{
222112918Sjeff	  NGM_BRIDGE_COOKIE,
223112918Sjeff	  NGM_BRIDGE_SET_CONFIG,
224112918Sjeff	  "setconfig",
225112918Sjeff	  &ng_bridge_config_type,
226112918Sjeff	  NULL
227112918Sjeff	},
228112918Sjeff	{
229112918Sjeff	  NGM_BRIDGE_COOKIE,
230112918Sjeff	  NGM_BRIDGE_GET_CONFIG,
231112918Sjeff	  "getconfig",
232112918Sjeff	  NULL,
233112918Sjeff	  &ng_bridge_config_type
234112918Sjeff	},
235112918Sjeff	{
236112918Sjeff	  NGM_BRIDGE_COOKIE,
237112918Sjeff	  NGM_BRIDGE_RESET,
238112918Sjeff	  "reset",
239112918Sjeff	  NULL,
240112918Sjeff	  NULL
241112918Sjeff	},
242112918Sjeff	{
243112918Sjeff	  NGM_BRIDGE_COOKIE,
244112918Sjeff	  NGM_BRIDGE_GET_STATS,
245112918Sjeff	  "getstats",
246112918Sjeff	  &ng_parse_uint32_type,
247112918Sjeff	  &ng_bridge_stats_type
248112918Sjeff	},
249112918Sjeff	{
250112918Sjeff	  NGM_BRIDGE_COOKIE,
251112918Sjeff	  NGM_BRIDGE_CLR_STATS,
252112918Sjeff	  "clrstats",
253112918Sjeff	  &ng_parse_uint32_type,
254112918Sjeff	  NULL
255112918Sjeff	},
256112918Sjeff	{
257112918Sjeff	  NGM_BRIDGE_COOKIE,
258112918Sjeff	  NGM_BRIDGE_GETCLR_STATS,
259112918Sjeff	  "getclrstats",
260112918Sjeff	  &ng_parse_uint32_type,
261112918Sjeff	  &ng_bridge_stats_type
262112918Sjeff	},
263112918Sjeff	{
264112918Sjeff	  NGM_BRIDGE_COOKIE,
265112918Sjeff	  NGM_BRIDGE_GET_TABLE,
266112918Sjeff	  "gettable",
267112918Sjeff	  NULL,
268112918Sjeff	  &ng_bridge_host_ary_type
269112918Sjeff	},
270112918Sjeff	{ 0 }
271112918Sjeff};
272112918Sjeff
273112918Sjeff/* Node type descriptor */
274112960Sjeffstatic struct ng_type ng_bridge_typestruct = {
275115035Smtm	NG_ABI_VERSION,
276112918Sjeff	NG_BRIDGE_NODE_TYPE,
277112918Sjeff	NULL,
278112918Sjeff	ng_bridge_constructor,
279112918Sjeff	ng_bridge_rcvmsg,
280112918Sjeff	ng_bridge_shutdown,
281112960Sjeff	ng_bridge_newhook,
282112918Sjeff	NULL,
283112918Sjeff	NULL,
284112918Sjeff	ng_bridge_rcvdata,
285112918Sjeff	ng_bridge_disconnect,
286112918Sjeff	ng_bridge_cmdlist,
287112918Sjeff};
288112918SjeffNETGRAPH_INIT(bridge, &ng_bridge_typestruct);
289112918Sjeff
290112918Sjeff/* Depend on ng_ether so we can use the Ethernet parse type */
291112918SjeffMODULE_DEPEND(ng_bridge, ng_ether, 1, 1, 1);
292112918Sjeff
293112918Sjeff/******************************************************************
294112918Sjeff		    NETGRAPH NODE METHODS
295112918Sjeff******************************************************************/
296112918Sjeff
297112918Sjeff/*
298112918Sjeff * Node constructor
299112918Sjeff */
300112918Sjeffstatic int
301112918Sjeffng_bridge_constructor(node_p node)
302112918Sjeff{
303112918Sjeff	priv_p priv;
304112918Sjeff
305112918Sjeff	/* Allocate and initialize private info */
306112918Sjeff	MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH_BRIDGE, M_NOWAIT | M_ZERO);
307112918Sjeff	if (priv == NULL)
308112918Sjeff		return (ENOMEM);
309112918Sjeff	callout_init(&priv->timer, 0);
310112918Sjeff
311112918Sjeff	/* Allocate and initialize hash table, etc. */
312112918Sjeff	MALLOC(priv->tab, struct ng_bridge_bucket *,
313112918Sjeff	    MIN_BUCKETS * sizeof(*priv->tab), M_NETGRAPH_BRIDGE, M_NOWAIT | M_ZERO);
314112918Sjeff	if (priv->tab == NULL) {
315112918Sjeff		FREE(priv, M_NETGRAPH_BRIDGE);
316112918Sjeff		return (ENOMEM);
317112918Sjeff	}
318112918Sjeff	priv->numBuckets = MIN_BUCKETS;
319112918Sjeff	priv->hashMask = MIN_BUCKETS - 1;
320112918Sjeff	priv->conf.debugLevel = 1;
321112918Sjeff	priv->conf.loopTimeout = DEFAULT_LOOP_TIMEOUT;
322112918Sjeff	priv->conf.maxStaleness = DEFAULT_MAX_STALENESS;
323112918Sjeff	priv->conf.minStableAge = DEFAULT_MIN_STABLE_AGE;
324112918Sjeff
325112918Sjeff	/*
326112918Sjeff	 * This node has all kinds of stuff that could be screwed by SMP.
327112918Sjeff	 * Until it gets it's own internal protection, we go through in
328112918Sjeff	 * single file. This could hurt a machine bridging beteen two
329112918Sjeff	 * GB ethernets so it should be fixed.
330112918Sjeff	 * When it's fixed the process SHOULD NOT SLEEP, spinlocks please!
331112918Sjeff	 * (and atomic ops )
332112918Sjeff	 */
333112918Sjeff	NG_NODE_FORCE_WRITER(node);
334112918Sjeff	NG_NODE_SET_PRIVATE(node, priv);
335112918Sjeff	priv->node = node;
336112918Sjeff
337112918Sjeff	/* Start timer; timer is always running while node is alive */
338112918Sjeff	callout_reset(&priv->timer, hz, ng_bridge_timeout, priv->node);
339112918Sjeff
340112918Sjeff	/* Done */
341112918Sjeff	return (0);
342112918Sjeff}
343112918Sjeff
344112918Sjeff/*
345112918Sjeff * Method for attaching a new hook
346112918Sjeff */
347112918Sjeffstatic	int
348112918Sjeffng_bridge_newhook(node_p node, hook_p hook, const char *name)
349112918Sjeff{
350112918Sjeff	const priv_p priv = NG_NODE_PRIVATE(node);
351112918Sjeff
352112918Sjeff	/* Check for a link hook */
353112918Sjeff	if (strncmp(name, NG_BRIDGE_HOOK_LINK_PREFIX,
354112918Sjeff	    strlen(NG_BRIDGE_HOOK_LINK_PREFIX)) == 0) {
355112918Sjeff		const char *cp;
356112918Sjeff		char *eptr;
357112918Sjeff		u_long linkNum;
358112918Sjeff
359112918Sjeff		cp = name + strlen(NG_BRIDGE_HOOK_LINK_PREFIX);
360112918Sjeff		if (!isdigit(*cp) || (cp[0] == '0' && cp[1] != '\0'))
361112918Sjeff			return (EINVAL);
362112918Sjeff		linkNum = strtoul(cp, &eptr, 10);
363112918Sjeff		if (*eptr != '\0' || linkNum >= NG_BRIDGE_MAX_LINKS)
364112918Sjeff			return (EINVAL);
365112918Sjeff		if (priv->links[linkNum] != NULL)
366112918Sjeff			return (EISCONN);
367112918Sjeff		MALLOC(priv->links[linkNum], struct ng_bridge_link *,
368112918Sjeff		    sizeof(*priv->links[linkNum]), M_NETGRAPH_BRIDGE, M_NOWAIT|M_ZERO);
369112918Sjeff		if (priv->links[linkNum] == NULL)
370112918Sjeff			return (ENOMEM);
371112918Sjeff		priv->links[linkNum]->hook = hook;
372112918Sjeff		NG_HOOK_SET_PRIVATE(hook, (void *)linkNum);
373112918Sjeff		priv->numLinks++;
374112918Sjeff		return (0);
375112918Sjeff	}
376112918Sjeff
377112918Sjeff	/* Unknown hook name */
378112918Sjeff	return (EINVAL);
379112918Sjeff}
380112918Sjeff
381112918Sjeff/*
382112918Sjeff * Receive a control message
383112918Sjeff */
384112918Sjeffstatic int
385112918Sjeffng_bridge_rcvmsg(node_p node, item_p item, hook_p lasthook)
386112918Sjeff{
387112918Sjeff	const priv_p priv = NG_NODE_PRIVATE(node);
388112918Sjeff	struct ng_mesg *resp = NULL;
389112918Sjeff	int error = 0;
390112918Sjeff	struct ng_mesg *msg;
391112918Sjeff
392112918Sjeff	NGI_GET_MSG(item, msg);
393112918Sjeff	switch (msg->header.typecookie) {
394112918Sjeff	case NGM_BRIDGE_COOKIE:
395112918Sjeff		switch (msg->header.cmd) {
396112918Sjeff		case NGM_BRIDGE_GET_CONFIG:
397112918Sjeff		    {
398112918Sjeff			struct ng_bridge_config *conf;
399112918Sjeff
400112918Sjeff			NG_MKRESPONSE(resp, msg,
401112918Sjeff			    sizeof(struct ng_bridge_config), M_NOWAIT);
402112918Sjeff			if (resp == NULL) {
403112918Sjeff				error = ENOMEM;
404112918Sjeff				break;
405112918Sjeff			}
406112918Sjeff			conf = (struct ng_bridge_config *)resp->data;
407112918Sjeff			*conf = priv->conf;	/* no sanity checking needed */
408112918Sjeff			break;
409112918Sjeff		    }
410112918Sjeff		case NGM_BRIDGE_SET_CONFIG:
411112918Sjeff		    {
412112918Sjeff			struct ng_bridge_config *conf;
413112918Sjeff			int i;
414112918Sjeff
415112918Sjeff			if (msg->header.arglen
416112918Sjeff			    != sizeof(struct ng_bridge_config)) {
417112918Sjeff				error = EINVAL;
418112918Sjeff				break;
419112918Sjeff			}
420112918Sjeff			conf = (struct ng_bridge_config *)msg->data;
421112918Sjeff			priv->conf = *conf;
422112918Sjeff			for (i = 0; i < NG_BRIDGE_MAX_LINKS; i++)
423112918Sjeff				priv->conf.ipfw[i] = !!priv->conf.ipfw[i];
424112918Sjeff			break;
425112918Sjeff		    }
426112918Sjeff		case NGM_BRIDGE_RESET:
427112918Sjeff		    {
428112918Sjeff			int i;
429112918Sjeff
430112918Sjeff			/* Flush all entries in the hash table */
431112918Sjeff			ng_bridge_remove_hosts(priv, -1);
432112918Sjeff
433112918Sjeff			/* Reset all loop detection counters and stats */
434112918Sjeff			for (i = 0; i < NG_BRIDGE_MAX_LINKS; i++) {
435112918Sjeff				if (priv->links[i] == NULL)
436112918Sjeff					continue;
437112918Sjeff				priv->links[i]->loopCount = 0;
438112918Sjeff				bzero(&priv->links[i]->stats,
439112918Sjeff				    sizeof(priv->links[i]->stats));
440112918Sjeff			}
441112918Sjeff			break;
442112918Sjeff		    }
443112918Sjeff		case NGM_BRIDGE_GET_STATS:
444112918Sjeff		case NGM_BRIDGE_CLR_STATS:
445112918Sjeff		case NGM_BRIDGE_GETCLR_STATS:
446112918Sjeff		    {
447112918Sjeff			struct ng_bridge_link *link;
448112918Sjeff			int linkNum;
449112918Sjeff
450112918Sjeff			/* Get link number */
451112918Sjeff			if (msg->header.arglen != sizeof(u_int32_t)) {
452112918Sjeff				error = EINVAL;
453112918Sjeff				break;
454112918Sjeff			}
455112918Sjeff			linkNum = *((u_int32_t *)msg->data);
456112918Sjeff			if (linkNum < 0 || linkNum >= NG_BRIDGE_MAX_LINKS) {
457112918Sjeff				error = EINVAL;
458112918Sjeff				break;
459112918Sjeff			}
460112918Sjeff			if ((link = priv->links[linkNum]) == NULL) {
461112918Sjeff				error = ENOTCONN;
462112918Sjeff				break;
463112918Sjeff			}
464112918Sjeff
465112918Sjeff			/* Get/clear stats */
466112918Sjeff			if (msg->header.cmd != NGM_BRIDGE_CLR_STATS) {
467112918Sjeff				NG_MKRESPONSE(resp, msg,
468112918Sjeff				    sizeof(link->stats), M_NOWAIT);
469112918Sjeff				if (resp == NULL) {
470112918Sjeff					error = ENOMEM;
471112918Sjeff					break;
472112918Sjeff				}
473112918Sjeff				bcopy(&link->stats,
474112918Sjeff				    resp->data, sizeof(link->stats));
475112918Sjeff			}
476112918Sjeff			if (msg->header.cmd != NGM_BRIDGE_GET_STATS)
477112918Sjeff				bzero(&link->stats, sizeof(link->stats));
478112918Sjeff			break;
479112918Sjeff		    }
480112918Sjeff		case NGM_BRIDGE_GET_TABLE:
481112918Sjeff		    {
482112918Sjeff			struct ng_bridge_host_ary *ary;
483112918Sjeff			struct ng_bridge_hent *hent;
484112918Sjeff			int i = 0, bucket;
485112918Sjeff
486112918Sjeff			NG_MKRESPONSE(resp, msg, sizeof(*ary)
487112918Sjeff			    + (priv->numHosts * sizeof(*ary->hosts)), M_NOWAIT);
488112918Sjeff			if (resp == NULL) {
489112918Sjeff				error = ENOMEM;
490112918Sjeff				break;
491112918Sjeff			}
492112918Sjeff			ary = (struct ng_bridge_host_ary *)resp->data;
493112918Sjeff			ary->numHosts = priv->numHosts;
494112918Sjeff			for (bucket = 0; bucket < priv->numBuckets; bucket++) {
495112918Sjeff				SLIST_FOREACH(hent, &priv->tab[bucket], next)
496112918Sjeff					ary->hosts[i++] = hent->host;
497112918Sjeff			}
498112918Sjeff			break;
499112918Sjeff		    }
500112918Sjeff		default:
501112918Sjeff			error = EINVAL;
502112918Sjeff			break;
503112918Sjeff		}
504112918Sjeff		break;
505112918Sjeff	default:
506112918Sjeff		error = EINVAL;
507112918Sjeff		break;
508112918Sjeff	}
509112918Sjeff
510112918Sjeff	/* Done */
511112918Sjeff	NG_RESPOND_MSG(error, node, item, resp);
512112918Sjeff	NG_FREE_MSG(msg);
513112918Sjeff	return (error);
514112918Sjeff}
515112918Sjeff
516112918Sjeff/*
517112918Sjeff * Receive data on a hook
518112918Sjeff */
519112918Sjeffstatic int
520112918Sjeffng_bridge_rcvdata(hook_p hook, item_p item)
521112918Sjeff{
522112918Sjeff	const node_p node = NG_HOOK_NODE(hook);
523112918Sjeff	const priv_p priv = NG_NODE_PRIVATE(node);
524112918Sjeff	struct ng_bridge_host *host;
525112918Sjeff	struct ng_bridge_link *link;
526112918Sjeff	struct ether_header *eh;
527112918Sjeff	int error = 0, linkNum;
528112918Sjeff	int manycast;
529112918Sjeff	struct mbuf *m;
530112918Sjeff	meta_p meta;
531112918Sjeff	struct ng_bridge_link *firstLink;
532112918Sjeff
533112918Sjeff	NGI_GET_M(item, m);
534112918Sjeff	/* Get link number */
535112918Sjeff	linkNum = (intptr_t)NG_HOOK_PRIVATE(hook);
536112918Sjeff	KASSERT(linkNum >= 0 && linkNum < NG_BRIDGE_MAX_LINKS,
537112918Sjeff	    ("%s: linkNum=%u", __func__, linkNum));
538112918Sjeff	link = priv->links[linkNum];
539112918Sjeff	KASSERT(link != NULL, ("%s: link%d null", __func__, linkNum));
540
541	/* Sanity check packet and pull up header */
542	if (m->m_pkthdr.len < ETHER_HDR_LEN) {
543		link->stats.recvRunts++;
544		NG_FREE_ITEM(item);
545		NG_FREE_M(m);
546		return (EINVAL);
547	}
548	if (m->m_len < ETHER_HDR_LEN && !(m = m_pullup(m, ETHER_HDR_LEN))) {
549		link->stats.memoryFailures++;
550		NG_FREE_ITEM(item);
551		return (ENOBUFS);
552	}
553	eh = mtod(m, struct ether_header *);
554	if ((eh->ether_shost[0] & 1) != 0) {
555		link->stats.recvInvalid++;
556		NG_FREE_ITEM(item);
557		NG_FREE_M(m);
558		return (EINVAL);
559	}
560
561	/* Is link disabled due to a loopback condition? */
562	if (link->loopCount != 0) {
563		link->stats.loopDrops++;
564		NG_FREE_ITEM(item);
565		NG_FREE_M(m);
566		return (ELOOP);		/* XXX is this an appropriate error? */
567	}
568
569	/* Update stats */
570	link->stats.recvPackets++;
571	link->stats.recvOctets += m->m_pkthdr.len;
572	if ((manycast = (eh->ether_dhost[0] & 1)) != 0) {
573		if (ETHER_EQUAL(eh->ether_dhost, ng_bridge_bcast_addr)) {
574			link->stats.recvBroadcasts++;
575			manycast = 2;
576		} else
577			link->stats.recvMulticasts++;
578	}
579
580	/* Look up packet's source Ethernet address in hashtable */
581	if ((host = ng_bridge_get(priv, eh->ether_shost)) != NULL) {
582
583		/* Update time since last heard from this host */
584		host->staleness = 0;
585
586		/* Did host jump to a different link? */
587		if (host->linkNum != linkNum) {
588
589			/*
590			 * If the host's old link was recently established
591			 * on the old link and it's already jumped to a new
592			 * link, declare a loopback condition.
593			 */
594			if (host->age < priv->conf.minStableAge) {
595
596				/* Log the problem */
597				if (priv->conf.debugLevel >= 2) {
598					struct ifnet *ifp = m->m_pkthdr.rcvif;
599					char suffix[32];
600
601					if (ifp != NULL)
602						snprintf(suffix, sizeof(suffix),
603						    " (%s%d)", ifp->if_name,
604						    ifp->if_unit);
605					else
606						*suffix = '\0';
607					log(LOG_WARNING, "ng_bridge: %s:"
608					    " loopback detected on %s%s\n",
609					    ng_bridge_nodename(node),
610					    NG_HOOK_NAME(hook), suffix);
611				}
612
613				/* Mark link as linka non grata */
614				link->loopCount = priv->conf.loopTimeout;
615				link->stats.loopDetects++;
616
617				/* Forget all hosts on this link */
618				ng_bridge_remove_hosts(priv, linkNum);
619
620				/* Drop packet */
621				link->stats.loopDrops++;
622				NG_FREE_ITEM(item);
623				NG_FREE_M(m);
624				return (ELOOP);		/* XXX appropriate? */
625			}
626
627			/* Move host over to new link */
628			host->linkNum = linkNum;
629			host->age = 0;
630		}
631	} else {
632		if (!ng_bridge_put(priv, eh->ether_shost, linkNum)) {
633			link->stats.memoryFailures++;
634			NG_FREE_ITEM(item);
635			NG_FREE_M(m);
636			return (ENOMEM);
637		}
638	}
639
640	/* Run packet through ipfw processing, if enabled */
641	if (priv->conf.ipfw[linkNum] && fw_enable && ip_fw_chk_ptr != NULL) {
642		/* XXX not implemented yet */
643	}
644
645	/*
646	 * If unicast and destination host known, deliver to host's link,
647	 * unless it is the same link as the packet came in on.
648	 */
649	if (!manycast) {
650
651		/* Determine packet destination link */
652		if ((host = ng_bridge_get(priv, eh->ether_dhost)) != NULL) {
653			struct ng_bridge_link *const destLink
654			    = priv->links[host->linkNum];
655
656			/* If destination same as incoming link, do nothing */
657			KASSERT(destLink != NULL,
658			    ("%s: link%d null", __func__, host->linkNum));
659			if (destLink == link) {
660				NG_FREE_ITEM(item);
661				NG_FREE_M(m);
662				return (0);
663			}
664
665			/* Deliver packet out the destination link */
666			destLink->stats.xmitPackets++;
667			destLink->stats.xmitOctets += m->m_pkthdr.len;
668			NG_FWD_NEW_DATA(error, item, destLink->hook, m);
669			return (error);
670		}
671
672		/* Destination host is not known */
673		link->stats.recvUnknown++;
674	}
675
676	/* Distribute unknown, multicast, broadcast pkts to all other links */
677	meta = NGI_META(item); /* peek.. */
678	firstLink = NULL;
679	for (linkNum = 0; linkNum <= priv->numLinks; linkNum++) {
680		struct ng_bridge_link *destLink;
681		meta_p meta2 = NULL;
682		struct mbuf *m2 = NULL;
683
684		/*
685		 * If we have checked all the links then now
686		 * send the original on its reserved link
687		 */
688		if (linkNum == priv->numLinks) {
689			/* If we never saw a good link, leave. */
690			if (firstLink == NULL) {
691				NG_FREE_ITEM(item);
692				NG_FREE_M(m);
693				return (0);
694			}
695			destLink = firstLink;
696		} else {
697			destLink = priv->links[linkNum];
698			/* Skip incoming link and disconnected links */
699			if (destLink == NULL || destLink == link) {
700				continue;
701			}
702			if (firstLink == NULL) {
703				/*
704				 * This is the first usable link we have found.
705				 * Reserve it for the originals.
706				 * If we never find another we save a copy.
707				 */
708				firstLink = destLink;
709				continue;
710			}
711
712			/*
713			 * It's usable link but not the reserved (first) one.
714			 * Copy mbuf and meta info for sending.
715			 */
716			m2 = m_dup(m, M_DONTWAIT);	/* XXX m_copypacket() */
717			if (m2 == NULL) {
718				link->stats.memoryFailures++;
719				NG_FREE_ITEM(item);
720				NG_FREE_M(m);
721				return (ENOBUFS);
722			}
723			if (meta != NULL
724			    && (meta2 = ng_copy_meta(meta)) == NULL) {
725				link->stats.memoryFailures++;
726				m_freem(m2);
727				NG_FREE_ITEM(item);
728				NG_FREE_M(m);
729				return (ENOMEM);
730			}
731		}
732
733		/* Update stats */
734		destLink->stats.xmitPackets++;
735		destLink->stats.xmitOctets += m->m_pkthdr.len;
736		switch (manycast) {
737		case 0:					/* unicast */
738			break;
739		case 1:					/* multicast */
740			destLink->stats.xmitMulticasts++;
741			break;
742		case 2:					/* broadcast */
743			destLink->stats.xmitBroadcasts++;
744			break;
745		}
746
747		/* Send packet */
748		if (destLink == firstLink) {
749			/*
750			 * If we've sent all the others, send the original
751			 * on the first link we found.
752			 */
753			NG_FWD_NEW_DATA(error, item, destLink->hook, m);
754			break; /* always done last - not really needed. */
755		} else {
756			NG_SEND_DATA(error, destLink->hook, m2, meta2);
757		}
758	}
759	return (error);
760}
761
762/*
763 * Shutdown node
764 */
765static int
766ng_bridge_shutdown(node_p node)
767{
768	const priv_p priv = NG_NODE_PRIVATE(node);
769
770	/*
771	 * Shut down everything except the timer. There's no way to
772	 * avoid another possible timeout event (it may have already
773	 * been dequeued), so we can't free the node yet.
774	 */
775	KASSERT(priv->numLinks == 0 && priv->numHosts == 0,
776	    ("%s: numLinks=%d numHosts=%d",
777	    __func__, priv->numLinks, priv->numHosts));
778	FREE(priv->tab, M_NETGRAPH_BRIDGE);
779
780	/* NG_INVALID flag is now set so node will be freed at next timeout */
781	return (0);
782}
783
784/*
785 * Hook disconnection.
786 */
787static int
788ng_bridge_disconnect(hook_p hook)
789{
790	const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
791	int linkNum;
792
793	/* Get link number */
794	linkNum = (intptr_t)NG_HOOK_PRIVATE(hook);
795	KASSERT(linkNum >= 0 && linkNum < NG_BRIDGE_MAX_LINKS,
796	    ("%s: linkNum=%u", __func__, linkNum));
797
798	/* Remove all hosts associated with this link */
799	ng_bridge_remove_hosts(priv, linkNum);
800
801	/* Free associated link information */
802	KASSERT(priv->links[linkNum] != NULL, ("%s: no link", __func__));
803	FREE(priv->links[linkNum], M_NETGRAPH_BRIDGE);
804	priv->links[linkNum] = NULL;
805	priv->numLinks--;
806
807	/* If no more hooks, go away */
808	if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0)
809	&& (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) {
810		ng_rmnode_self(NG_HOOK_NODE(hook));
811	}
812	return (0);
813}
814
815/******************************************************************
816		    HASH TABLE FUNCTIONS
817******************************************************************/
818
819/*
820 * Hash algorithm
821 *
822 * Only hashing bytes 3-6 of the Ethernet address is sufficient and fast.
823 */
824#define HASH(addr,mask)		( (((const u_int16_t *)(addr))[0] 	\
825				 ^ ((const u_int16_t *)(addr))[1] 	\
826				 ^ ((const u_int16_t *)(addr))[2]) & (mask) )
827
828/*
829 * Find a host entry in the table.
830 */
831static struct ng_bridge_host *
832ng_bridge_get(priv_p priv, const u_char *addr)
833{
834	const int bucket = HASH(addr, priv->hashMask);
835	struct ng_bridge_hent *hent;
836
837	SLIST_FOREACH(hent, &priv->tab[bucket], next) {
838		if (ETHER_EQUAL(hent->host.addr, addr))
839			return (&hent->host);
840	}
841	return (NULL);
842}
843
844/*
845 * Add a new host entry to the table. This assumes the host doesn't
846 * already exist in the table. Returns 1 on success, 0 if there
847 * was a memory allocation failure.
848 */
849static int
850ng_bridge_put(priv_p priv, const u_char *addr, int linkNum)
851{
852	const int bucket = HASH(addr, priv->hashMask);
853	struct ng_bridge_hent *hent;
854
855#ifdef INVARIANTS
856	/* Assert that entry does not already exist in hashtable */
857	SLIST_FOREACH(hent, &priv->tab[bucket], next) {
858		KASSERT(!ETHER_EQUAL(hent->host.addr, addr),
859		    ("%s: entry %6D exists in table", __func__, addr, ":"));
860	}
861#endif
862
863	/* Allocate and initialize new hashtable entry */
864	MALLOC(hent, struct ng_bridge_hent *,
865	    sizeof(*hent), M_NETGRAPH_BRIDGE, M_NOWAIT);
866	if (hent == NULL)
867		return (0);
868	bcopy(addr, hent->host.addr, ETHER_ADDR_LEN);
869	hent->host.linkNum = linkNum;
870	hent->host.staleness = 0;
871	hent->host.age = 0;
872
873	/* Add new element to hash bucket */
874	SLIST_INSERT_HEAD(&priv->tab[bucket], hent, next);
875	priv->numHosts++;
876
877	/* Resize table if necessary */
878	ng_bridge_rehash(priv);
879	return (1);
880}
881
882/*
883 * Resize the hash table. We try to maintain the number of buckets
884 * such that the load factor is in the range 0.25 to 1.0.
885 *
886 * If we can't get the new memory then we silently fail. This is OK
887 * because things will still work and we'll try again soon anyway.
888 */
889static void
890ng_bridge_rehash(priv_p priv)
891{
892	struct ng_bridge_bucket *newTab;
893	int oldBucket, newBucket;
894	int newNumBuckets;
895	u_int newMask;
896
897	/* Is table too full or too empty? */
898	if (priv->numHosts > priv->numBuckets
899	    && (priv->numBuckets << 1) <= MAX_BUCKETS)
900		newNumBuckets = priv->numBuckets << 1;
901	else if (priv->numHosts < (priv->numBuckets >> 2)
902	    && (priv->numBuckets >> 2) >= MIN_BUCKETS)
903		newNumBuckets = priv->numBuckets >> 2;
904	else
905		return;
906	newMask = newNumBuckets - 1;
907
908	/* Allocate and initialize new table */
909	MALLOC(newTab, struct ng_bridge_bucket *,
910	    newNumBuckets * sizeof(*newTab), M_NETGRAPH_BRIDGE, M_NOWAIT | M_ZERO);
911	if (newTab == NULL)
912		return;
913
914	/* Move all entries from old table to new table */
915	for (oldBucket = 0; oldBucket < priv->numBuckets; oldBucket++) {
916		struct ng_bridge_bucket *const oldList = &priv->tab[oldBucket];
917
918		while (!SLIST_EMPTY(oldList)) {
919			struct ng_bridge_hent *const hent
920			    = SLIST_FIRST(oldList);
921
922			SLIST_REMOVE_HEAD(oldList, next);
923			newBucket = HASH(hent->host.addr, newMask);
924			SLIST_INSERT_HEAD(&newTab[newBucket], hent, next);
925		}
926	}
927
928	/* Replace old table with new one */
929	if (priv->conf.debugLevel >= 3) {
930		log(LOG_INFO, "ng_bridge: %s: table size %d -> %d\n",
931		    ng_bridge_nodename(priv->node),
932		    priv->numBuckets, newNumBuckets);
933	}
934	FREE(priv->tab, M_NETGRAPH_BRIDGE);
935	priv->numBuckets = newNumBuckets;
936	priv->hashMask = newMask;
937	priv->tab = newTab;
938	return;
939}
940
941/******************************************************************
942		    MISC FUNCTIONS
943******************************************************************/
944
945/*
946 * Remove all hosts associated with a specific link from the hashtable.
947 * If linkNum == -1, then remove all hosts in the table.
948 */
949static void
950ng_bridge_remove_hosts(priv_p priv, int linkNum)
951{
952	int bucket;
953
954	for (bucket = 0; bucket < priv->numBuckets; bucket++) {
955		struct ng_bridge_hent **hptr = &SLIST_FIRST(&priv->tab[bucket]);
956
957		while (*hptr != NULL) {
958			struct ng_bridge_hent *const hent = *hptr;
959
960			if (linkNum == -1 || hent->host.linkNum == linkNum) {
961				*hptr = SLIST_NEXT(hent, next);
962				FREE(hent, M_NETGRAPH_BRIDGE);
963				priv->numHosts--;
964			} else
965				hptr = &SLIST_NEXT(hent, next);
966		}
967	}
968}
969
970/*
971 * Handle our once-per-second timeout event. We do two things:
972 * we decrement link->loopCount for those links being muted due to
973 * a detected loopback condition, and we remove any hosts from
974 * the hashtable whom we haven't heard from in a long while.
975 *
976 * If the node has the NG_INVALID flag set, our job is to kill it.
977 */
978static void
979ng_bridge_timeout(void *arg)
980{
981	const node_p node = arg;
982	const priv_p priv = NG_NODE_PRIVATE(node);
983	int s, bucket;
984	int counter = 0;
985	int linkNum;
986
987	/* If node was shut down, this is the final lingering timeout */
988	s = splnet();
989	if (NG_NODE_NOT_VALID(node)) {
990		FREE(priv, M_NETGRAPH);
991		NG_NODE_SET_PRIVATE(node, NULL);
992		NG_NODE_UNREF(node);
993		splx(s);
994		return;
995	}
996
997	/* Register a new timeout, keeping the existing node reference */
998	callout_reset(&priv->timer, hz, ng_bridge_timeout, node);
999
1000	/* Update host time counters and remove stale entries */
1001	for (bucket = 0; bucket < priv->numBuckets; bucket++) {
1002		struct ng_bridge_hent **hptr = &SLIST_FIRST(&priv->tab[bucket]);
1003
1004		while (*hptr != NULL) {
1005			struct ng_bridge_hent *const hent = *hptr;
1006
1007			/* Make sure host's link really exists */
1008			KASSERT(priv->links[hent->host.linkNum] != NULL,
1009			    ("%s: host %6D on nonexistent link %d\n",
1010			    __func__, hent->host.addr, ":",
1011			    hent->host.linkNum));
1012
1013			/* Remove hosts we haven't heard from in a while */
1014			if (++hent->host.staleness >= priv->conf.maxStaleness) {
1015				*hptr = SLIST_NEXT(hent, next);
1016				FREE(hent, M_NETGRAPH_BRIDGE);
1017				priv->numHosts--;
1018			} else {
1019				if (hent->host.age < 0xffff)
1020					hent->host.age++;
1021				hptr = &SLIST_NEXT(hent, next);
1022				counter++;
1023			}
1024		}
1025	}
1026	KASSERT(priv->numHosts == counter,
1027	    ("%s: hosts: %d != %d", __func__, priv->numHosts, counter));
1028
1029	/* Decrease table size if necessary */
1030	ng_bridge_rehash(priv);
1031
1032	/* Decrease loop counter on muted looped back links */
1033	for (counter = linkNum = 0; linkNum < NG_BRIDGE_MAX_LINKS; linkNum++) {
1034		struct ng_bridge_link *const link = priv->links[linkNum];
1035
1036		if (link != NULL) {
1037			if (link->loopCount != 0) {
1038				link->loopCount--;
1039				if (link->loopCount == 0
1040				    && priv->conf.debugLevel >= 2) {
1041					log(LOG_INFO, "ng_bridge: %s:"
1042					    " restoring looped back link%d\n",
1043					    ng_bridge_nodename(node), linkNum);
1044				}
1045			}
1046			counter++;
1047		}
1048	}
1049	KASSERT(priv->numLinks == counter,
1050	    ("%s: links: %d != %d", __func__, priv->numLinks, counter));
1051
1052	/* Done */
1053	splx(s);
1054}
1055
1056/*
1057 * Return node's "name", even if it doesn't have one.
1058 */
1059static const char *
1060ng_bridge_nodename(node_p node)
1061{
1062	static char name[NG_NODELEN+1];
1063
1064	if (NG_NODE_NAME(node) != NULL)
1065		snprintf(name, sizeof(name), "%s", NG_NODE_NAME(node));
1066	else
1067		snprintf(name, sizeof(name), "[%x]", ng_node2ID(node));
1068	return name;
1069}
1070
1071