]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/netgraph/ng_bridge.c
nfscl: Use vfs.nfs.maxalloclen to limit Deallocate RPC RTT
[FreeBSD/FreeBSD.git] / sys / netgraph / ng_bridge.c
1 /*-
2  * Copyright (c) 2000 Whistle Communications, Inc.
3  * All rights reserved.
4  * 
5  * Subject to the following obligations and disclaimer of warranty, use and
6  * redistribution of this software, in source or object code forms, with or
7  * without modifications are expressly permitted by Whistle Communications;
8  * provided, however, that:
9  * 1. Any and all reproductions of the source or object code must include the
10  *    copyright notice above and the following disclaimer of warranties; and
11  * 2. No rights are granted, in any manner or form, to use Whistle
12  *    Communications, Inc. trademarks, including the mark "WHISTLE
13  *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
14  *    such appears in the above copyright notice or in the software.
15  * 
16  * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
17  * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
18  * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
19  * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
21  * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
22  * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
23  * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
24  * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
25  * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
26  * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
27  * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
28  * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31  * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
32  * OF SUCH DAMAGE.
33  *
34  * Author: Archie Cobbs <archie@freebsd.org>
35  *
36  * $FreeBSD$
37  */
38
39 /*
40  * ng_bridge(4) netgraph node type
41  *
42  * The node performs standard intelligent Ethernet bridging over
43  * each of its connected hooks, or links.  A simple loop detection
44  * algorithm is included which disables a link for priv->conf.loopTimeout
45  * seconds when a host is seen to have jumped from one link to
46  * another within priv->conf.minStableAge seconds.
47  *
48  * We keep a hashtable that maps Ethernet addresses to host info,
49  * which is contained in struct ng_bridge_host's. These structures
50  * tell us on which link the host may be found. A host's entry will
51  * expire after priv->conf.maxStaleness seconds.
52  *
53  * This node is optimzed for stable networks, where machines jump
54  * from one port to the other only rarely.
55  */
56
57 #include <sys/param.h>
58 #include <sys/systm.h>
59 #include <sys/kernel.h>
60 #include <sys/lock.h>
61 #include <sys/malloc.h>
62 #include <sys/mbuf.h>
63 #include <sys/errno.h>
64 #include <sys/rwlock.h>
65 #include <sys/syslog.h>
66 #include <sys/socket.h>
67 #include <sys/ctype.h>
68 #include <sys/types.h>
69 #include <sys/counter.h>
70
71 #include <net/if.h>
72 #include <net/if_var.h>
73 #include <net/ethernet.h>
74 #include <net/vnet.h>
75
76 #include <netinet/in.h>
77 #if 0   /* not used yet */
78 #include <netinet/ip_fw.h>
79 #endif
80 #include <netgraph/ng_message.h>
81 #include <netgraph/netgraph.h>
82 #include <netgraph/ng_parse.h>
83 #include <netgraph/ng_bridge.h>
84
85 #ifdef NG_SEPARATE_MALLOC
86 static MALLOC_DEFINE(M_NETGRAPH_BRIDGE, "netgraph_bridge",
87     "netgraph bridge node");
88 #else
89 #define M_NETGRAPH_BRIDGE M_NETGRAPH
90 #endif
91
92 /* Counter based stats */
93 struct ng_bridge_link_kernel_stats {
94         counter_u64_t   recvOctets;     /* total octets rec'd on link */
95         counter_u64_t   recvPackets;    /* total pkts rec'd on link */
96         counter_u64_t   recvMulticasts; /* multicast pkts rec'd on link */
97         counter_u64_t   recvBroadcasts; /* broadcast pkts rec'd on link */
98         counter_u64_t   recvUnknown;    /* pkts rec'd with unknown dest addr */
99         counter_u64_t   recvRunts;      /* pkts rec'd less than 14 bytes */
100         counter_u64_t   recvInvalid;    /* pkts rec'd with bogus source addr */
101         counter_u64_t   xmitOctets;     /* total octets xmit'd on link */
102         counter_u64_t   xmitPackets;    /* total pkts xmit'd on link */
103         counter_u64_t   xmitMulticasts; /* multicast pkts xmit'd on link */
104         counter_u64_t   xmitBroadcasts; /* broadcast pkts xmit'd on link */
105         counter_u64_t   loopDrops;      /* pkts dropped due to loopback */
106         u_int64_t       loopDetects;    /* number of loop detections */
107         counter_u64_t   memoryFailures; /* times couldn't get mem or mbuf */
108 };
109
110 /* Per-link private data */
111 struct ng_bridge_link {
112         hook_p                          hook;           /* netgraph hook */
113         u_int16_t                       loopCount;      /* loop ignore timer */
114         unsigned int                    learnMac : 1,   /* autolearn macs */
115                                         sendUnknown : 1;/* send unknown macs out */
116         struct ng_bridge_link_kernel_stats stats;       /* link stats */
117 };
118 typedef struct ng_bridge_link const *link_cp;   /* read only access */
119
120 /* Per-node private data */
121 struct ng_bridge_private {
122         struct ng_bridge_bucket *tab;           /* hash table bucket array */
123         struct ng_bridge_config conf;           /* node configuration */
124         node_p                  node;           /* netgraph node */
125         u_int                   numHosts;       /* num entries in table */
126         u_int                   numBuckets;     /* num buckets in table */
127         u_int                   hashMask;       /* numBuckets - 1 */
128         int                     numLinks;       /* num connected links */
129         unsigned int            persistent : 1, /* can exist w/o hooks */
130                                 sendUnknown : 1;/* links receive unknowns by default */
131         struct callout          timer;          /* one second periodic timer */
132 };
133 typedef struct ng_bridge_private *priv_p;
134 typedef struct ng_bridge_private const *priv_cp;        /* read only access */
135
136 /* Information about a host, stored in a hash table entry */
137 struct ng_bridge_host {
138         u_char          addr[6];        /* ethernet address */
139         link_p          link;           /* link where addr can be found */
140         u_int16_t       age;            /* seconds ago entry was created */
141         u_int16_t       staleness;      /* seconds ago host last heard from */
142         SLIST_ENTRY(ng_bridge_host)     next;   /* next entry in bucket */
143 };
144
145 /* Hash table bucket declaration */
146 SLIST_HEAD(ng_bridge_bucket, ng_bridge_host);
147
148 /* Netgraph node methods */
149 static ng_constructor_t ng_bridge_constructor;
150 static ng_rcvmsg_t      ng_bridge_rcvmsg;
151 static ng_shutdown_t    ng_bridge_shutdown;
152 static ng_newhook_t     ng_bridge_newhook;
153 static ng_rcvdata_t     ng_bridge_rcvdata;
154 static ng_disconnect_t  ng_bridge_disconnect;
155
156 /* Other internal functions */
157 static void     ng_bridge_free_link(link_p link);
158 static struct   ng_bridge_host *ng_bridge_get(priv_cp priv, const u_char *addr);
159 static int      ng_bridge_put(priv_p priv, const u_char *addr, link_p link);
160 static void     ng_bridge_rehash(priv_p priv);
161 static void     ng_bridge_remove_hosts(priv_p priv, link_p link);
162 static void     ng_bridge_timeout(node_p node, hook_p hook, void *arg1, int arg2);
163 static const    char *ng_bridge_nodename(node_cp node);
164
165 /* Ethernet broadcast */
166 static const u_char ng_bridge_bcast_addr[ETHER_ADDR_LEN] =
167     { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
168
169 /* Compare Ethernet addresses using 32 and 16 bit words instead of bytewise */
170 #define ETHER_EQUAL(a,b)        (((const u_int32_t *)(a))[0] \
171                                         == ((const u_int32_t *)(b))[0] \
172                                     && ((const u_int16_t *)(a))[2] \
173                                         == ((const u_int16_t *)(b))[2])
174
175 /* Minimum and maximum number of hash buckets. Must be a power of two. */
176 #define MIN_BUCKETS             (1 << 5)        /* 32 */
177 #define MAX_BUCKETS             (1 << 14)       /* 16384 */
178
179 /* Configuration default values */
180 #define DEFAULT_LOOP_TIMEOUT    60
181 #define DEFAULT_MAX_STALENESS   (15 * 60)       /* same as ARP timeout */
182 #define DEFAULT_MIN_STABLE_AGE  1
183
184 /******************************************************************
185                     NETGRAPH PARSE TYPES
186 ******************************************************************/
187
188 /*
189  * How to determine the length of the table returned by NGM_BRIDGE_GET_TABLE
190  */
191 static int
192 ng_bridge_getTableLength(const struct ng_parse_type *type,
193         const u_char *start, const u_char *buf)
194 {
195         const struct ng_bridge_host_ary *const hary
196             = (const struct ng_bridge_host_ary *)(buf - sizeof(u_int32_t));
197
198         return hary->numHosts;
199 }
200
201 /* Parse type for struct ng_bridge_host_ary */
202 static const struct ng_parse_struct_field ng_bridge_host_type_fields[]
203         = NG_BRIDGE_HOST_TYPE_INFO(&ng_parse_enaddr_type);
204 static const struct ng_parse_type ng_bridge_host_type = {
205         &ng_parse_struct_type,
206         &ng_bridge_host_type_fields
207 };
208 static const struct ng_parse_array_info ng_bridge_hary_type_info = {
209         &ng_bridge_host_type,
210         ng_bridge_getTableLength
211 };
212 static const struct ng_parse_type ng_bridge_hary_type = {
213         &ng_parse_array_type,
214         &ng_bridge_hary_type_info
215 };
216 static const struct ng_parse_struct_field ng_bridge_host_ary_type_fields[]
217         = NG_BRIDGE_HOST_ARY_TYPE_INFO(&ng_bridge_hary_type);
218 static const struct ng_parse_type ng_bridge_host_ary_type = {
219         &ng_parse_struct_type,
220         &ng_bridge_host_ary_type_fields
221 };
222
223 /* Parse type for struct ng_bridge_config */
224 static const struct ng_parse_struct_field ng_bridge_config_type_fields[]
225         = NG_BRIDGE_CONFIG_TYPE_INFO;
226 static const struct ng_parse_type ng_bridge_config_type = {
227         &ng_parse_struct_type,
228         &ng_bridge_config_type_fields
229 };
230
231 /* Parse type for struct ng_bridge_link_stat */
232 static const struct ng_parse_struct_field ng_bridge_stats_type_fields[]
233         = NG_BRIDGE_STATS_TYPE_INFO;
234 static const struct ng_parse_type ng_bridge_stats_type = {
235         &ng_parse_struct_type,
236         &ng_bridge_stats_type_fields
237 };
238 /* Parse type for struct ng_bridge_move_host */
239 static const struct ng_parse_struct_field ng_bridge_move_host_type_fields[]
240         = NG_BRIDGE_MOVE_HOST_TYPE_INFO(&ng_parse_enaddr_type);
241 static const struct ng_parse_type ng_bridge_move_host_type = {
242         &ng_parse_struct_type,
243         &ng_bridge_move_host_type_fields
244 };
245
246 /* List of commands and how to convert arguments to/from ASCII */
247 static const struct ng_cmdlist ng_bridge_cmdlist[] = {
248         {
249           NGM_BRIDGE_COOKIE,
250           NGM_BRIDGE_SET_CONFIG,
251           "setconfig",
252           &ng_bridge_config_type,
253           NULL
254         },
255         {
256           NGM_BRIDGE_COOKIE,
257           NGM_BRIDGE_GET_CONFIG,
258           "getconfig",
259           NULL,
260           &ng_bridge_config_type
261         },
262         {
263           NGM_BRIDGE_COOKIE,
264           NGM_BRIDGE_RESET,
265           "reset",
266           NULL,
267           NULL
268         },
269         {
270           NGM_BRIDGE_COOKIE,
271           NGM_BRIDGE_GET_STATS,
272           "getstats",
273           &ng_parse_uint32_type,
274           &ng_bridge_stats_type
275         },
276         {
277           NGM_BRIDGE_COOKIE,
278           NGM_BRIDGE_CLR_STATS,
279           "clrstats",
280           &ng_parse_uint32_type,
281           NULL
282         },
283         {
284           NGM_BRIDGE_COOKIE,
285           NGM_BRIDGE_GETCLR_STATS,
286           "getclrstats",
287           &ng_parse_uint32_type,
288           &ng_bridge_stats_type
289         },
290         {
291           NGM_BRIDGE_COOKIE,
292           NGM_BRIDGE_GET_TABLE,
293           "gettable",
294           NULL,
295           &ng_bridge_host_ary_type
296         },
297         {
298           NGM_BRIDGE_COOKIE,
299           NGM_BRIDGE_SET_PERSISTENT,
300           "setpersistent",
301           NULL,
302           NULL
303         },
304         {
305           NGM_BRIDGE_COOKIE,
306           NGM_BRIDGE_MOVE_HOST,
307           "movehost",
308           &ng_bridge_move_host_type,
309           NULL
310         },
311         { 0 }
312 };
313
314 /* Node type descriptor */
315 static struct ng_type ng_bridge_typestruct = {
316         .version =      NG_ABI_VERSION,
317         .name =         NG_BRIDGE_NODE_TYPE,
318         .constructor =  ng_bridge_constructor,
319         .rcvmsg =       ng_bridge_rcvmsg,
320         .shutdown =     ng_bridge_shutdown,
321         .newhook =      ng_bridge_newhook,
322         .rcvdata =      ng_bridge_rcvdata,
323         .disconnect =   ng_bridge_disconnect,
324         .cmdlist =      ng_bridge_cmdlist,
325 };
326 NETGRAPH_INIT(bridge, &ng_bridge_typestruct);
327
328 /******************************************************************
329                     NETGRAPH NODE METHODS
330 ******************************************************************/
331
332 /*
333  * Node constructor
334  */
335 static int
336 ng_bridge_constructor(node_p node)
337 {
338         priv_p priv;
339
340         /* Allocate and initialize private info */
341         priv = malloc(sizeof(*priv), M_NETGRAPH_BRIDGE, M_WAITOK | M_ZERO);
342         ng_callout_init(&priv->timer);
343
344         /* Allocate and initialize hash table, etc. */
345         priv->tab = malloc(MIN_BUCKETS * sizeof(*priv->tab),
346             M_NETGRAPH_BRIDGE, M_WAITOK | M_ZERO);
347         priv->numBuckets = MIN_BUCKETS;
348         priv->hashMask = MIN_BUCKETS - 1;
349         priv->conf.debugLevel = 1;
350         priv->conf.loopTimeout = DEFAULT_LOOP_TIMEOUT;
351         priv->conf.maxStaleness = DEFAULT_MAX_STALENESS;
352         priv->conf.minStableAge = DEFAULT_MIN_STABLE_AGE;
353         priv->sendUnknown = 1;         /* classic bridge */
354
355         NG_NODE_SET_PRIVATE(node, priv);
356         priv->node = node;
357
358         /* Start timer; timer is always running while node is alive */
359         ng_callout(&priv->timer, node, NULL, hz, ng_bridge_timeout, NULL, 0);
360
361         /* Done */
362         return (0);
363 }
364
365 /*
366  * Method for attaching a new hook
367  */
368 static  int
369 ng_bridge_newhook(node_p node, hook_p hook, const char *name)
370 {
371         const priv_p priv = NG_NODE_PRIVATE(node);
372         char linkName[NG_HOOKSIZ];
373         u_int32_t linkNum;
374         link_p link;
375         const char *prefix = NG_BRIDGE_HOOK_LINK_PREFIX;
376         bool isUplink;
377
378         /* Check for a link hook */
379         if (strlen(name) <= strlen(prefix))
380                 return (EINVAL);       /* Unknown hook name */
381
382         isUplink = (name[0] == 'u');
383         if (isUplink)
384                 prefix = NG_BRIDGE_HOOK_UPLINK_PREFIX;
385
386         /* primitive parsing */
387         linkNum = strtoul(name + strlen(prefix), NULL, 10);
388         /* validation by comparing against the reconstucted name  */
389         snprintf(linkName, sizeof(linkName), "%s%u", prefix, linkNum);
390         if (strcmp(linkName, name) != 0)
391                 return (EINVAL);
392
393         if (linkNum == 0 && isUplink)
394                 return (EINVAL);
395
396         if(NG_PEER_NODE(hook) == node)
397                 return (ELOOP);
398
399         link = malloc(sizeof(*link), M_NETGRAPH_BRIDGE, M_NOWAIT | M_ZERO);
400         if (link == NULL)
401                 return (ENOMEM);
402
403 #define NG_BRIDGE_COUNTER_ALLOC(f) do {                 \
404         link->stats.f = counter_u64_alloc(M_NOWAIT);    \
405         if (link->stats.f == NULL)                      \
406                 goto nomem;                             \
407 } while (0)
408         NG_BRIDGE_COUNTER_ALLOC(recvOctets);
409         NG_BRIDGE_COUNTER_ALLOC(recvPackets);
410         NG_BRIDGE_COUNTER_ALLOC(recvMulticasts);
411         NG_BRIDGE_COUNTER_ALLOC(recvBroadcasts);
412         NG_BRIDGE_COUNTER_ALLOC(recvUnknown);
413         NG_BRIDGE_COUNTER_ALLOC(recvRunts);
414         NG_BRIDGE_COUNTER_ALLOC(recvInvalid);
415         NG_BRIDGE_COUNTER_ALLOC(xmitOctets);
416         NG_BRIDGE_COUNTER_ALLOC(xmitPackets);
417         NG_BRIDGE_COUNTER_ALLOC(xmitMulticasts);
418         NG_BRIDGE_COUNTER_ALLOC(xmitBroadcasts);
419         NG_BRIDGE_COUNTER_ALLOC(loopDrops);
420         NG_BRIDGE_COUNTER_ALLOC(memoryFailures);
421 #undef NG_BRIDGE_COUNTER_ALLOC
422
423         link->hook = hook;
424         if (isUplink) {
425                 link->learnMac = 0;
426                 link->sendUnknown = 1;
427                 if (priv->numLinks == 0)        /* if the first link is an uplink */
428                         priv->sendUnknown = 0;  /* switch to restrictive mode */
429         } else {
430                 link->learnMac = 1;
431                 link->sendUnknown = priv->sendUnknown;
432         }
433
434         NG_HOOK_SET_PRIVATE(hook, link);
435         priv->numLinks++;
436         return (0);
437
438 nomem:
439         ng_bridge_free_link(link);
440         return (ENOMEM);
441 }
442
443 /*
444  * Receive a control message
445  */
446 static void
447 ng_bridge_clear_link_stats(struct ng_bridge_link_kernel_stats *p)
448 {
449         counter_u64_zero(p->recvOctets);
450         counter_u64_zero(p->recvPackets);
451         counter_u64_zero(p->recvMulticasts);
452         counter_u64_zero(p->recvBroadcasts);
453         counter_u64_zero(p->recvUnknown);
454         counter_u64_zero(p->recvRunts);
455         counter_u64_zero(p->recvInvalid);
456         counter_u64_zero(p->xmitOctets);
457         counter_u64_zero(p->xmitPackets);
458         counter_u64_zero(p->xmitMulticasts);
459         counter_u64_zero(p->xmitBroadcasts);
460         counter_u64_zero(p->loopDrops);
461         p->loopDetects = 0;
462         counter_u64_zero(p->memoryFailures);
463 }
464
465 static void
466 ng_bridge_free_link(link_p link)
467 {
468         counter_u64_free(link->stats.recvOctets);
469         counter_u64_free(link->stats.recvPackets);
470         counter_u64_free(link->stats.recvMulticasts);
471         counter_u64_free(link->stats.recvBroadcasts);
472         counter_u64_free(link->stats.recvUnknown);
473         counter_u64_free(link->stats.recvRunts);
474         counter_u64_free(link->stats.recvInvalid);
475         counter_u64_free(link->stats.xmitOctets);
476         counter_u64_free(link->stats.xmitPackets);
477         counter_u64_free(link->stats.xmitMulticasts);
478         counter_u64_free(link->stats.xmitBroadcasts);
479         counter_u64_free(link->stats.loopDrops);
480         counter_u64_free(link->stats.memoryFailures);
481         free(link, M_NETGRAPH_BRIDGE);
482 }
483
484 static int
485 ng_bridge_reset_link(hook_p hook, void *arg __unused)
486 {
487         link_p priv = NG_HOOK_PRIVATE(hook);
488
489         priv->loopCount = 0;
490         ng_bridge_clear_link_stats(&priv->stats);
491         return (1);
492 }
493
494 static int
495 ng_bridge_rcvmsg(node_p node, item_p item, hook_p lasthook)
496 {
497         const priv_p priv = NG_NODE_PRIVATE(node);
498         struct ng_mesg *resp = NULL;
499         int error = 0;
500         struct ng_mesg *msg;
501
502         NGI_GET_MSG(item, msg);
503         switch (msg->header.typecookie) {
504         case NGM_BRIDGE_COOKIE:
505                 switch (msg->header.cmd) {
506                 case NGM_BRIDGE_GET_CONFIG:
507                     {
508                         struct ng_bridge_config *conf;
509
510                         NG_MKRESPONSE(resp, msg,
511                             sizeof(struct ng_bridge_config), M_NOWAIT);
512                         if (resp == NULL) {
513                                 error = ENOMEM;
514                                 break;
515                         }
516                         conf = (struct ng_bridge_config *)resp->data;
517                         *conf = priv->conf;     /* no sanity checking needed */
518                         break;
519                     }
520                 case NGM_BRIDGE_SET_CONFIG:
521                     {
522                         struct ng_bridge_config *conf;
523
524                         if (msg->header.arglen
525                             != sizeof(struct ng_bridge_config)) {
526                                 error = EINVAL;
527                                 break;
528                         }
529                         conf = (struct ng_bridge_config *)msg->data;
530                         priv->conf = *conf;
531                         break;
532                     }
533                 case NGM_BRIDGE_RESET:
534                     {
535                         hook_p rethook;
536
537                         /* Flush all entries in the hash table */
538                         ng_bridge_remove_hosts(priv, NULL);
539
540                         /* Reset all loop detection counters and stats */
541                         NG_NODE_FOREACH_HOOK(node, ng_bridge_reset_link, NULL,
542                             rethook);
543                         break;
544                     }
545                 case NGM_BRIDGE_GET_STATS:
546                 case NGM_BRIDGE_CLR_STATS:
547                 case NGM_BRIDGE_GETCLR_STATS:
548                     {
549                         hook_p hook;
550                         link_p link;
551                         char linkName[NG_HOOKSIZ];
552                         int linkNum;
553                             
554                         /* Get link number */
555                         if (msg->header.arglen != sizeof(u_int32_t)) {
556                                 error = EINVAL;
557                                 break;
558                         }
559                         linkNum = *((int32_t *)msg->data);
560                         if (linkNum < 0)
561                                 snprintf(linkName, sizeof(linkName),
562                                     "%s%u", NG_BRIDGE_HOOK_UPLINK_PREFIX, -linkNum);
563                         else
564                                 snprintf(linkName, sizeof(linkName),
565                                     "%s%u", NG_BRIDGE_HOOK_LINK_PREFIX, linkNum);
566                             
567                         if ((hook = ng_findhook(node, linkName)) == NULL) {
568                                 error = ENOTCONN;
569                                 break;
570                         }
571                         link = NG_HOOK_PRIVATE(hook);
572
573                         /* Get/clear stats */
574                         if (msg->header.cmd != NGM_BRIDGE_CLR_STATS) {
575                                 struct ng_bridge_link_stats *rs;
576
577                                 NG_MKRESPONSE(resp, msg,
578                                     sizeof(link->stats), M_NOWAIT);
579                                 if (resp == NULL) {
580                                         error = ENOMEM;
581                                         break;
582                                 }
583                                 rs = (struct ng_bridge_link_stats *)resp->data;
584 #define FETCH(x)        rs->x = counter_u64_fetch(link->stats.x)
585                                 FETCH(recvOctets);
586                                 FETCH(recvPackets);
587                                 FETCH(recvMulticasts);
588                                 FETCH(recvBroadcasts);
589                                 FETCH(recvUnknown);
590                                 FETCH(recvRunts);
591                                 FETCH(recvInvalid);
592                                 FETCH(xmitOctets);
593                                 FETCH(xmitPackets);
594                                 FETCH(xmitMulticasts);
595                                 FETCH(xmitBroadcasts);
596                                 FETCH(loopDrops);
597                                 rs->loopDetects = link->stats.loopDetects;
598                                 FETCH(memoryFailures);
599 #undef FETCH
600                         }
601                         if (msg->header.cmd != NGM_BRIDGE_GET_STATS)
602                                 ng_bridge_clear_link_stats(&link->stats);
603                         break;
604                     }
605                 case NGM_BRIDGE_GET_TABLE:
606                     {
607                         struct ng_bridge_host_ary *ary;
608                         struct ng_bridge_host *host;
609                         int i = 0, bucket;
610
611                         NG_MKRESPONSE(resp, msg, sizeof(*ary)
612                             + (priv->numHosts * sizeof(*ary->hosts)), M_NOWAIT);
613                         if (resp == NULL) {
614                                 error = ENOMEM;
615                                 break;
616                         }
617                         ary = (struct ng_bridge_host_ary *)resp->data;
618                         ary->numHosts = priv->numHosts;
619                         for (bucket = 0; bucket < priv->numBuckets; bucket++) {
620                                 SLIST_FOREACH(host, &priv->tab[bucket], next) {
621                                         memcpy(ary->hosts[i].addr,
622                                                host->addr,
623                                                sizeof(ary->hosts[i].addr));
624                                         ary->hosts[i].age       = host->age;
625                                         ary->hosts[i].staleness = host->staleness;
626                                         strncpy(ary->hosts[i].hook,
627                                                 NG_HOOK_NAME(host->link->hook),
628                                                 sizeof(ary->hosts[i].hook));
629                                         i++;
630                                 }
631                         }
632                         break;
633                     }
634                 case NGM_BRIDGE_SET_PERSISTENT:
635                     {
636                         priv->persistent = 1;
637                         break;
638                     }
639                 case NGM_BRIDGE_MOVE_HOST:
640                 {
641                         struct ng_bridge_move_host *mh;
642                         hook_p hook;
643
644                         if (msg->header.arglen < sizeof(*mh)) {
645                                 error = EINVAL;
646                                 break;
647                         }
648                         mh = (struct ng_bridge_move_host *)msg->data;
649                         hook = (mh->hook[0] == 0)
650                             ? lasthook
651                             : ng_findhook(node, mh->hook);
652                         if (hook == NULL) {
653                                 error = ENOENT;
654                                 break;
655                         }
656                         error = ng_bridge_put(priv, mh->addr, NG_HOOK_PRIVATE(hook));
657                         break;
658                 }
659                 default:
660                         error = EINVAL;
661                         break;
662                 }
663                 break;
664         default:
665                 error = EINVAL;
666                 break;
667         }
668
669         /* Done */
670         NG_RESPOND_MSG(error, node, item, resp);
671         NG_FREE_MSG(msg);
672         return (error);
673 }
674
675 /*
676  * Receive data on a hook
677  */
678 struct ng_bridge_send_ctx {
679         link_p foundFirst, incoming;
680         struct mbuf * m;
681         int manycast, error;
682 };
683
684 /*
685  * Update stats and send out
686  */
687 static inline int
688 ng_bridge_send_data(link_cp dst, int manycast, struct mbuf *m, item_p item) {
689         int error = 0;
690         size_t len = m->m_pkthdr.len;
691
692         if(item != NULL)
693                 NG_FWD_NEW_DATA(error, item, dst->hook, m);
694         else
695                 NG_SEND_DATA_ONLY(error, dst->hook, m);
696
697         if (error) {
698                 /* The packet is still ours */
699                 if (item != NULL)
700                         NG_FREE_ITEM(item);
701                 if (m != NULL)
702                         NG_FREE_M(m);
703                 return (error);
704         }
705
706         counter_u64_add(dst->stats.xmitPackets, 1);
707         counter_u64_add(dst->stats.xmitOctets, len);
708         switch (manycast) {
709         default:                       /* unknown unicast */
710                 break;
711         case 1:                        /* multicast */
712                 counter_u64_add(dst->stats.xmitMulticasts, 1);
713                 break;
714         case 2:                        /* broadcast */
715                 counter_u64_add(dst->stats.xmitBroadcasts, 1);
716                 break;
717         }
718         return (0);
719 }
720
721 /*
722  * Loop body for sending to multiple destinations
723  * return 0 to stop looping
724  */
725 static int
726 ng_bridge_send_ctx(hook_p dst, void *arg)
727 {
728         struct ng_bridge_send_ctx *ctx = arg;
729         link_p destLink = NG_HOOK_PRIVATE(dst);
730         struct mbuf *m2 = NULL;
731         int error = 0;
732
733         /* Skip incoming link */
734         if (destLink == ctx->incoming) {
735                 return (1);
736         }
737
738         /* Skip sending unknowns to undesired links  */
739         if (!ctx->manycast && !destLink->sendUnknown)
740                 return (1);
741
742         if (ctx->foundFirst == NULL) {
743                 /*
744                  * This is the first usable link we have found.
745                  * Reserve it for the originals.
746                  * If we never find another we save a copy.
747                  */
748                 ctx->foundFirst = destLink;
749                 return (1);
750         }
751
752         /*
753          * It's usable link but not the reserved (first) one.
754          * Copy mbuf info for sending.
755          */
756         m2 = m_dup(ctx->m, M_NOWAIT);
757         if (m2 == NULL) {
758                 counter_u64_add(ctx->incoming->stats.memoryFailures, 1);
759                 ctx->error = ENOBUFS;
760                 return (0);            /* abort loop, do not try again and again */
761         }
762
763         /* Send packet */
764         error = ng_bridge_send_data(destLink, ctx->manycast, m2, NULL);
765         if (error)
766           ctx->error = error;
767         return (1);
768 }
769
770 static int
771 ng_bridge_rcvdata(hook_p hook, item_p item)
772 {
773         const node_p node = NG_HOOK_NODE(hook);
774         const priv_p priv = NG_NODE_PRIVATE(node);
775         struct ng_bridge_host *host;
776         struct ether_header *eh;
777         struct ng_bridge_send_ctx ctx = { 0 };
778         hook_p ret;
779
780         NGI_GET_M(item, ctx.m);
781
782         ctx.incoming = NG_HOOK_PRIVATE(hook);
783         /* Sanity check packet and pull up header */
784         if (ctx.m->m_pkthdr.len < ETHER_HDR_LEN) {
785                 counter_u64_add(ctx.incoming->stats.recvRunts, 1);
786                 NG_FREE_ITEM(item);
787                 NG_FREE_M(ctx.m);
788                 return (EINVAL);
789         }
790         if (ctx.m->m_len < ETHER_HDR_LEN && !(ctx.m = m_pullup(ctx.m, ETHER_HDR_LEN))) {
791                 counter_u64_add(ctx.incoming->stats.memoryFailures, 1);
792                 NG_FREE_ITEM(item);
793                 return (ENOBUFS);
794         }
795         eh = mtod(ctx.m, struct ether_header *);
796         if ((eh->ether_shost[0] & 1) != 0) {
797                 counter_u64_add(ctx.incoming->stats.recvInvalid, 1);
798                 NG_FREE_ITEM(item);
799                 NG_FREE_M(ctx.m);
800                 return (EINVAL);
801         }
802
803         /* Is link disabled due to a loopback condition? */
804         if (ctx.incoming->loopCount != 0) {
805                 counter_u64_add(ctx.incoming->stats.loopDrops, 1);
806                 NG_FREE_ITEM(item);
807                 NG_FREE_M(ctx.m);
808                 return (ELOOP);
809         }
810
811         /* Update stats */
812         counter_u64_add(ctx.incoming->stats.recvPackets, 1);
813         counter_u64_add(ctx.incoming->stats.recvOctets, ctx.m->m_pkthdr.len);
814         if ((ctx.manycast = (eh->ether_dhost[0] & 1)) != 0) {
815                 if (ETHER_EQUAL(eh->ether_dhost, ng_bridge_bcast_addr)) {
816                         counter_u64_add(ctx.incoming->stats.recvBroadcasts, 1);
817                         ctx.manycast = 2;
818                 } else
819                         counter_u64_add(ctx.incoming->stats.recvMulticasts, 1);
820         }
821
822         /* Look up packet's source Ethernet address in hashtable */
823         if ((host = ng_bridge_get(priv, eh->ether_shost)) != NULL)
824                 /* Update time since last heard from this host.
825                  * This is safe without locking, because it's
826                  * the only operation during shared access.
827                  */
828                 if (__predict_false(host->staleness > 0))
829                         host->staleness = 0;
830
831         if ((host == NULL && ctx.incoming->learnMac) ||
832             (host != NULL && host->link != ctx.incoming)) {
833                 struct ng_mesg *msg;
834                 struct ng_bridge_move_host *mh;
835                 int error = 0;
836
837                 NG_MKMESSAGE(msg, NGM_BRIDGE_COOKIE, NGM_BRIDGE_MOVE_HOST,
838                     sizeof(*mh), M_NOWAIT);
839                 if (msg == NULL) {
840                         counter_u64_add(ctx.incoming->stats.memoryFailures, 1);
841                         NG_FREE_ITEM(item);
842                         NG_FREE_M(ctx.m);
843                         return (ENOMEM);
844                 }
845                 mh = (struct ng_bridge_move_host *)msg->data;
846                 strncpy(mh->hook, NG_HOOK_NAME(ctx.incoming->hook),
847                     sizeof(mh->hook));
848                 memcpy(mh->addr, eh->ether_shost, sizeof(mh->addr));
849                 NG_SEND_MSG_ID(error, node, msg, NG_NODE_ID(node),
850                     NG_NODE_ID(node));
851                 if (error)
852                         counter_u64_add(ctx.incoming->stats.memoryFailures, 1);
853         }
854
855         if (host != NULL && host->link != ctx.incoming) {
856                 if (host->age < priv->conf.minStableAge) {
857                         /* Drop packet on instable links */
858                         counter_u64_add(ctx.incoming->stats.loopDrops, 1);
859                         NG_FREE_ITEM(item);
860                         NG_FREE_M(ctx.m);
861                         return (ELOOP);
862                 }
863         }
864
865         /* Run packet through ipfw processing, if enabled */
866 #if 0
867         if (priv->conf.ipfw[linkNum] && V_fw_enable && V_ip_fw_chk_ptr != NULL) {
868                 /* XXX not implemented yet */
869         }
870 #endif
871
872         /*
873          * If unicast and destination host known, deliver to host's link,
874          * unless it is the same link as the packet came in on.
875          */
876         if (!ctx.manycast) {
877                 /* Determine packet destination link */
878                 if ((host = ng_bridge_get(priv, eh->ether_dhost)) != NULL) {
879                         link_p destLink = host->link;
880
881                         /* If destination same as incoming link, do nothing */
882                         if (destLink == ctx.incoming) {
883                                 NG_FREE_ITEM(item);
884                                 NG_FREE_M(ctx.m);
885                                 return (0);
886                         }
887
888                         /* Deliver packet out the destination link */
889                         return (ng_bridge_send_data(destLink, ctx.manycast, ctx.m, item));
890                 }
891
892                 /* Destination host is not known */
893                 counter_u64_add(ctx.incoming->stats.recvUnknown, 1);
894         }
895
896         /* Distribute unknown, multicast, broadcast pkts to all other links */
897         NG_NODE_FOREACH_HOOK(node, ng_bridge_send_ctx, &ctx, ret);
898
899         /* Finally send out on the first link found */
900         if (ctx.foundFirst != NULL) {
901                 int error = ng_bridge_send_data(ctx.foundFirst, ctx.manycast, ctx.m, item);
902                 if (error)
903                         ctx.error = error;
904         } else {                       /* nothing to send at all */
905                 NG_FREE_ITEM(item);
906                 NG_FREE_M(ctx.m);
907         }
908
909         return (ctx.error);
910 }
911
912 /*
913  * Shutdown node
914  */
915 static int
916 ng_bridge_shutdown(node_p node)
917 {
918         const priv_p priv = NG_NODE_PRIVATE(node);
919
920         /*
921          * Shut down everything including the timer.  Even if the
922          * callout has already been dequeued and is about to be
923          * run, ng_bridge_timeout() won't be fired as the node
924          * is already marked NGF_INVALID, so we're safe to free
925          * the node now.
926          */
927         KASSERT(priv->numLinks == 0 && priv->numHosts == 0,
928             ("%s: numLinks=%d numHosts=%d",
929             __func__, priv->numLinks, priv->numHosts));
930         ng_uncallout(&priv->timer, node);
931         NG_NODE_SET_PRIVATE(node, NULL);
932         NG_NODE_UNREF(node);
933         free(priv->tab, M_NETGRAPH_BRIDGE);
934         free(priv, M_NETGRAPH_BRIDGE);
935         return (0);
936 }
937
938 /*
939  * Hook disconnection.
940  */
941 static int
942 ng_bridge_disconnect(hook_p hook)
943 {
944         const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
945         link_p link = NG_HOOK_PRIVATE(hook);
946
947         /* Remove all hosts associated with this link */
948         ng_bridge_remove_hosts(priv, link);
949
950         /* Free associated link information */
951         ng_bridge_free_link(link);
952         priv->numLinks--;
953
954         /* If no more hooks, go away */
955         if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0)
956             && (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))
957             && !priv->persistent) {
958                 ng_rmnode_self(NG_HOOK_NODE(hook));
959         }
960         return (0);
961 }
962
963 /******************************************************************
964                     HASH TABLE FUNCTIONS
965 ******************************************************************/
966
967 /*
968  * Hash algorithm
969  */
970 #define HASH(addr,mask)         ( (((const u_int16_t *)(addr))[0]       \
971                                  ^ ((const u_int16_t *)(addr))[1]       \
972                                  ^ ((const u_int16_t *)(addr))[2]) & (mask) )
973
974 /*
975  * Find a host entry in the table.
976  */
977 static struct ng_bridge_host *
978 ng_bridge_get(priv_cp priv, const u_char *addr)
979 {
980         const int bucket = HASH(addr, priv->hashMask);
981         struct ng_bridge_host *host;
982
983         SLIST_FOREACH(host, &priv->tab[bucket], next) {
984                 if (ETHER_EQUAL(host->addr, addr))
985                         return (host);
986         }
987         return (NULL);
988 }
989
990 /*
991  * Add a host entry to the table. If it already exists, move it
992  * to the new link. Returns 0 on success.
993  */
994 static int
995 ng_bridge_put(priv_p priv, const u_char *addr, link_p link)
996 {
997         const int bucket = HASH(addr, priv->hashMask);
998         struct ng_bridge_host *host;
999
1000         if ((host = ng_bridge_get(priv, addr)) != NULL) {
1001                 /* Host already on the correct link? */
1002                 if (host->link == link)
1003                         return 0;
1004
1005                 /* Move old host over to new link */
1006                 if (host->age >= priv->conf.minStableAge) {
1007                         host->link = link;
1008                         host->age = 0;
1009                         return (0);
1010                 }
1011                 /*
1012                  * If the host was recently moved to the old link and
1013                  * it's now jumping to a new link, declare a loopback
1014                  * condition.
1015                  */
1016                 if (priv->conf.debugLevel >= 2)
1017                     log(LOG_WARNING, "ng_bridge: %s:"
1018                         " loopback detected on %s\n",
1019                         ng_bridge_nodename(priv->node),
1020                         NG_HOOK_NAME(link->hook));
1021
1022                 /* Mark link as linka non grata */
1023                 link->loopCount = priv->conf.loopTimeout;
1024                 link->stats.loopDetects++;
1025
1026                 /* Forget all hosts on this link */
1027                 ng_bridge_remove_hosts(priv, link);
1028                 return (ELOOP);
1029         }
1030
1031         /* Allocate and initialize new hashtable entry */
1032         host = malloc(sizeof(*host), M_NETGRAPH_BRIDGE, M_NOWAIT);
1033         if (host == NULL)
1034                 return (ENOMEM);
1035         bcopy(addr, host->addr, ETHER_ADDR_LEN);
1036         host->link = link;
1037         host->staleness = 0;
1038         host->age = 0;
1039
1040         /* Add new element to hash bucket */
1041         SLIST_INSERT_HEAD(&priv->tab[bucket], host, next);
1042         priv->numHosts++;
1043
1044         /* Resize table if necessary */
1045         ng_bridge_rehash(priv);
1046         return (0);
1047 }
1048
1049 /*
1050  * Resize the hash table. We try to maintain the number of buckets
1051  * such that the load factor is in the range 0.25 to 1.0.
1052  *
1053  * If we can't get the new memory then we silently fail. This is OK
1054  * because things will still work and we'll try again soon anyway.
1055  */
1056 static void
1057 ng_bridge_rehash(priv_p priv)
1058 {
1059         struct ng_bridge_bucket *newTab;
1060         int oldBucket, newBucket;
1061         int newNumBuckets;
1062         u_int newMask;
1063
1064         /* Is table too full or too empty? */
1065         if (priv->numHosts > priv->numBuckets
1066             && (priv->numBuckets << 1) <= MAX_BUCKETS)
1067                 newNumBuckets = priv->numBuckets << 1;
1068         else if (priv->numHosts < (priv->numBuckets >> 2)
1069             && (priv->numBuckets >> 2) >= MIN_BUCKETS)
1070                 newNumBuckets = priv->numBuckets >> 2;
1071         else
1072                 return;
1073         newMask = newNumBuckets - 1;
1074
1075         /* Allocate and initialize new table */
1076         newTab = malloc(newNumBuckets * sizeof(*newTab),
1077             M_NETGRAPH_BRIDGE, M_NOWAIT | M_ZERO);
1078         if (newTab == NULL)
1079                 return;
1080
1081         /* Move all entries from old table to new table */
1082         for (oldBucket = 0; oldBucket < priv->numBuckets; oldBucket++) {
1083                 struct ng_bridge_bucket *const oldList = &priv->tab[oldBucket];
1084
1085                 while (!SLIST_EMPTY(oldList)) {
1086                         struct ng_bridge_host *const host
1087                             = SLIST_FIRST(oldList);
1088
1089                         SLIST_REMOVE_HEAD(oldList, next);
1090                         newBucket = HASH(host->addr, newMask);
1091                         SLIST_INSERT_HEAD(&newTab[newBucket], host, next);
1092                 }
1093         }
1094
1095         /* Replace old table with new one */
1096         if (priv->conf.debugLevel >= 3) {
1097                 log(LOG_INFO, "ng_bridge: %s: table size %d -> %d\n",
1098                     ng_bridge_nodename(priv->node),
1099                     priv->numBuckets, newNumBuckets);
1100         }
1101         free(priv->tab, M_NETGRAPH_BRIDGE);
1102         priv->numBuckets = newNumBuckets;
1103         priv->hashMask = newMask;
1104         priv->tab = newTab;
1105         return;
1106 }
1107
1108 /******************************************************************
1109                     MISC FUNCTIONS
1110 ******************************************************************/
1111
1112 /*
1113  * Remove all hosts associated with a specific link from the hashtable.
1114  * If linkNum == -1, then remove all hosts in the table.
1115  */
1116 static void
1117 ng_bridge_remove_hosts(priv_p priv, link_p link)
1118 {
1119         int bucket;
1120
1121         for (bucket = 0; bucket < priv->numBuckets; bucket++) {
1122                 struct ng_bridge_host **hptr = &SLIST_FIRST(&priv->tab[bucket]);
1123
1124                 while (*hptr != NULL) {
1125                         struct ng_bridge_host *const host = *hptr;
1126
1127                         if (link == NULL || host->link == link) {
1128                                 *hptr = SLIST_NEXT(host, next);
1129                                 free(host, M_NETGRAPH_BRIDGE);
1130                                 priv->numHosts--;
1131                         } else
1132                                 hptr = &SLIST_NEXT(host, next);
1133                 }
1134         }
1135 }
1136
1137 /*
1138  * Handle our once-per-second timeout event. We do two things:
1139  * we decrement link->loopCount for those links being muted due to
1140  * a detected loopback condition, and we remove any hosts from
1141  * the hashtable whom we haven't heard from in a long while.
1142  */
1143 static int
1144 ng_bridge_unmute(hook_p hook, void *arg)
1145 {
1146         link_p link = NG_HOOK_PRIVATE(hook);
1147         node_p node = NG_HOOK_NODE(hook);
1148         priv_p priv = NG_NODE_PRIVATE(node);
1149         int *counter = arg;
1150
1151         if (link->loopCount != 0) {
1152                 link->loopCount--;
1153                 if (link->loopCount == 0 && priv->conf.debugLevel >= 2) {
1154                         log(LOG_INFO, "ng_bridge: %s:"
1155                             " restoring looped back %s\n",
1156                             ng_bridge_nodename(node), NG_HOOK_NAME(hook));
1157                 }
1158         }
1159         (*counter)++;
1160         return (1);
1161 }
1162
1163 static void
1164 ng_bridge_timeout(node_p node, hook_p hook, void *arg1, int arg2)
1165 {
1166         const priv_p priv = NG_NODE_PRIVATE(node);
1167         int bucket;
1168         int counter = 0;
1169         hook_p ret;
1170
1171         /* Update host time counters and remove stale entries */
1172         for (bucket = 0; bucket < priv->numBuckets; bucket++) {
1173                 struct ng_bridge_host **hptr = &SLIST_FIRST(&priv->tab[bucket]);
1174
1175                 while (*hptr != NULL) {
1176                         struct ng_bridge_host *const host = *hptr;
1177
1178                         /* Remove hosts we haven't heard from in a while */
1179                         if (++host->staleness >= priv->conf.maxStaleness) {
1180                                 *hptr = SLIST_NEXT(host, next);
1181                                 free(host, M_NETGRAPH_BRIDGE);
1182                                 priv->numHosts--;
1183                         } else {
1184                                 if (host->age < 0xffff)
1185                                         host->age++;
1186                                 hptr = &SLIST_NEXT(host, next);
1187                                 counter++;
1188                         }
1189                 }
1190         }
1191         KASSERT(priv->numHosts == counter,
1192             ("%s: hosts: %d != %d", __func__, priv->numHosts, counter));
1193
1194         /* Decrease table size if necessary */
1195         ng_bridge_rehash(priv);
1196
1197         /* Decrease loop counter on muted looped back links */
1198         counter = 0;
1199         NG_NODE_FOREACH_HOOK(node, ng_bridge_unmute, &counter, ret);
1200         KASSERT(priv->numLinks == counter,
1201             ("%s: links: %d != %d", __func__, priv->numLinks, counter));
1202
1203         /* Register a new timeout, keeping the existing node reference */
1204         ng_callout(&priv->timer, node, NULL, hz, ng_bridge_timeout, NULL, 0);
1205 }
1206
1207 /*
1208  * Return node's "name", even if it doesn't have one.
1209  */
1210 static const char *
1211 ng_bridge_nodename(node_cp node)
1212 {
1213         static char name[NG_NODESIZ];
1214
1215         if (NG_NODE_HAS_NAME(node))
1216                 snprintf(name, sizeof(name), "%s", NG_NODE_NAME(node));
1217         else
1218                 snprintf(name, sizeof(name), "[%x]", ng_node2ID(node));
1219         return name;
1220 }