2 * Copyright (c) 2000 Whistle Communications, Inc.
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.
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
34 * Author: Archie Cobbs <archie@freebsd.org>
40 * ng_bridge(4) netgraph node type
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.
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.
53 * This node is optimzed for stable networks, where machines jump
54 * from one port to the other only rarely.
57 #include <sys/param.h>
58 #include <sys/systm.h>
59 #include <sys/kernel.h>
61 #include <sys/malloc.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>
72 #include <net/if_var.h>
73 #include <net/ethernet.h>
76 #include <netinet/in.h>
77 #include <netgraph/ng_message.h>
78 #include <netgraph/netgraph.h>
79 #include <netgraph/ng_parse.h>
80 #include <netgraph/ng_bridge.h>
82 #ifdef NG_SEPARATE_MALLOC
83 static MALLOC_DEFINE(M_NETGRAPH_BRIDGE, "netgraph_bridge",
84 "netgraph bridge node");
86 #define M_NETGRAPH_BRIDGE M_NETGRAPH
89 /* Counter based stats */
90 struct ng_bridge_link_kernel_stats {
91 counter_u64_t recvOctets; /* total octets rec'd on link */
92 counter_u64_t recvPackets; /* total pkts rec'd on link */
93 counter_u64_t recvMulticasts; /* multicast pkts rec'd on link */
94 counter_u64_t recvBroadcasts; /* broadcast pkts rec'd on link */
95 counter_u64_t recvUnknown; /* pkts rec'd with unknown dest addr */
96 counter_u64_t recvRunts; /* pkts rec'd less than 14 bytes */
97 counter_u64_t recvInvalid; /* pkts rec'd with bogus source addr */
98 counter_u64_t xmitOctets; /* total octets xmit'd on link */
99 counter_u64_t xmitPackets; /* total pkts xmit'd on link */
100 counter_u64_t xmitMulticasts; /* multicast pkts xmit'd on link */
101 counter_u64_t xmitBroadcasts; /* broadcast pkts xmit'd on link */
102 counter_u64_t loopDrops; /* pkts dropped due to loopback */
103 u_int64_t loopDetects; /* number of loop detections */
104 counter_u64_t memoryFailures; /* times couldn't get mem or mbuf */
107 /* Per-link private data */
108 struct ng_bridge_link {
109 hook_p hook; /* netgraph hook */
110 u_int16_t loopCount; /* loop ignore timer */
111 unsigned int learnMac : 1, /* autolearn macs */
112 sendUnknown : 1;/* send unknown macs out */
113 struct ng_bridge_link_kernel_stats stats; /* link stats */
115 typedef struct ng_bridge_link const *link_cp; /* read only access */
117 /* Per-node private data */
118 struct ng_bridge_private {
119 struct ng_bridge_bucket *tab; /* hash table bucket array */
120 struct ng_bridge_config conf; /* node configuration */
121 node_p node; /* netgraph node */
122 u_int numHosts; /* num entries in table */
123 u_int numBuckets; /* num buckets in table */
124 u_int hashMask; /* numBuckets - 1 */
125 int numLinks; /* num connected links */
126 unsigned int persistent : 1, /* can exist w/o hooks */
127 sendUnknown : 1;/* links receive unknowns by default */
128 struct callout timer; /* one second periodic timer */
130 typedef struct ng_bridge_private *priv_p;
131 typedef struct ng_bridge_private const *priv_cp; /* read only access */
133 /* Information about a host, stored in a hash table entry */
134 struct ng_bridge_host {
135 u_char addr[6]; /* ethernet address */
136 link_p link; /* link where addr can be found */
137 u_int16_t age; /* seconds ago entry was created */
138 u_int16_t staleness; /* seconds ago host last heard from */
139 SLIST_ENTRY(ng_bridge_host) next; /* next entry in bucket */
142 /* Hash table bucket declaration */
143 SLIST_HEAD(ng_bridge_bucket, ng_bridge_host);
145 /* Netgraph node methods */
146 static ng_constructor_t ng_bridge_constructor;
147 static ng_rcvmsg_t ng_bridge_rcvmsg;
148 static ng_shutdown_t ng_bridge_shutdown;
149 static ng_newhook_t ng_bridge_newhook;
150 static ng_rcvdata_t ng_bridge_rcvdata;
151 static ng_disconnect_t ng_bridge_disconnect;
153 /* Other internal functions */
154 static void ng_bridge_free_link(link_p link);
155 static struct ng_bridge_host *ng_bridge_get(priv_cp priv, const u_char *addr);
156 static int ng_bridge_put(priv_p priv, const u_char *addr, link_p link);
157 static void ng_bridge_rehash(priv_p priv);
158 static void ng_bridge_remove_hosts(priv_p priv, link_p link);
159 static void ng_bridge_timeout(node_p node, hook_p hook, void *arg1, int arg2);
160 static const char *ng_bridge_nodename(node_cp node);
162 /* Ethernet broadcast */
163 static const u_char ng_bridge_bcast_addr[ETHER_ADDR_LEN] =
164 { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
166 /* Compare Ethernet addresses using 32 and 16 bit words instead of bytewise */
167 #define ETHER_EQUAL(a,b) (((const u_int32_t *)(a))[0] \
168 == ((const u_int32_t *)(b))[0] \
169 && ((const u_int16_t *)(a))[2] \
170 == ((const u_int16_t *)(b))[2])
172 /* Minimum and maximum number of hash buckets. Must be a power of two. */
173 #define MIN_BUCKETS (1 << 5) /* 32 */
174 #define MAX_BUCKETS (1 << 14) /* 16384 */
176 /* Configuration default values */
177 #define DEFAULT_LOOP_TIMEOUT 60
178 #define DEFAULT_MAX_STALENESS (15 * 60) /* same as ARP timeout */
179 #define DEFAULT_MIN_STABLE_AGE 1
181 /******************************************************************
183 ******************************************************************/
186 * How to determine the length of the table returned by NGM_BRIDGE_GET_TABLE
189 ng_bridge_getTableLength(const struct ng_parse_type *type,
190 const u_char *start, const u_char *buf)
192 const struct ng_bridge_host_ary *const hary
193 = (const struct ng_bridge_host_ary *)(buf - sizeof(u_int32_t));
195 return hary->numHosts;
198 /* Parse type for struct ng_bridge_host_ary */
199 static const struct ng_parse_struct_field ng_bridge_host_type_fields[]
200 = NG_BRIDGE_HOST_TYPE_INFO(&ng_parse_enaddr_type);
201 static const struct ng_parse_type ng_bridge_host_type = {
202 &ng_parse_struct_type,
203 &ng_bridge_host_type_fields
205 static const struct ng_parse_array_info ng_bridge_hary_type_info = {
206 &ng_bridge_host_type,
207 ng_bridge_getTableLength
209 static const struct ng_parse_type ng_bridge_hary_type = {
210 &ng_parse_array_type,
211 &ng_bridge_hary_type_info
213 static const struct ng_parse_struct_field ng_bridge_host_ary_type_fields[]
214 = NG_BRIDGE_HOST_ARY_TYPE_INFO(&ng_bridge_hary_type);
215 static const struct ng_parse_type ng_bridge_host_ary_type = {
216 &ng_parse_struct_type,
217 &ng_bridge_host_ary_type_fields
220 /* Parse type for struct ng_bridge_config */
221 static const struct ng_parse_struct_field ng_bridge_config_type_fields[]
222 = NG_BRIDGE_CONFIG_TYPE_INFO;
223 static const struct ng_parse_type ng_bridge_config_type = {
224 &ng_parse_struct_type,
225 &ng_bridge_config_type_fields
228 /* Parse type for struct ng_bridge_link_stat */
229 static const struct ng_parse_struct_field ng_bridge_stats_type_fields[]
230 = NG_BRIDGE_STATS_TYPE_INFO;
231 static const struct ng_parse_type ng_bridge_stats_type = {
232 &ng_parse_struct_type,
233 &ng_bridge_stats_type_fields
235 /* Parse type for struct ng_bridge_move_host */
236 static const struct ng_parse_struct_field ng_bridge_move_host_type_fields[]
237 = NG_BRIDGE_MOVE_HOST_TYPE_INFO(&ng_parse_enaddr_type);
238 static const struct ng_parse_type ng_bridge_move_host_type = {
239 &ng_parse_struct_type,
240 &ng_bridge_move_host_type_fields
243 /* List of commands and how to convert arguments to/from ASCII */
244 static const struct ng_cmdlist ng_bridge_cmdlist[] = {
247 NGM_BRIDGE_SET_CONFIG,
249 &ng_bridge_config_type,
254 NGM_BRIDGE_GET_CONFIG,
257 &ng_bridge_config_type
268 NGM_BRIDGE_GET_STATS,
270 &ng_parse_uint32_type,
271 &ng_bridge_stats_type
275 NGM_BRIDGE_CLR_STATS,
277 &ng_parse_uint32_type,
282 NGM_BRIDGE_GETCLR_STATS,
284 &ng_parse_uint32_type,
285 &ng_bridge_stats_type
289 NGM_BRIDGE_GET_TABLE,
292 &ng_bridge_host_ary_type
296 NGM_BRIDGE_SET_PERSISTENT,
303 NGM_BRIDGE_MOVE_HOST,
305 &ng_bridge_move_host_type,
311 /* Node type descriptor */
312 static struct ng_type ng_bridge_typestruct = {
313 .version = NG_ABI_VERSION,
314 .name = NG_BRIDGE_NODE_TYPE,
315 .constructor = ng_bridge_constructor,
316 .rcvmsg = ng_bridge_rcvmsg,
317 .shutdown = ng_bridge_shutdown,
318 .newhook = ng_bridge_newhook,
319 .rcvdata = ng_bridge_rcvdata,
320 .disconnect = ng_bridge_disconnect,
321 .cmdlist = ng_bridge_cmdlist,
323 NETGRAPH_INIT(bridge, &ng_bridge_typestruct);
325 /******************************************************************
326 NETGRAPH NODE METHODS
327 ******************************************************************/
333 ng_bridge_constructor(node_p node)
337 /* Allocate and initialize private info */
338 priv = malloc(sizeof(*priv), M_NETGRAPH_BRIDGE, M_WAITOK | M_ZERO);
339 ng_callout_init(&priv->timer);
341 /* Allocate and initialize hash table, etc. */
342 priv->tab = malloc(MIN_BUCKETS * sizeof(*priv->tab),
343 M_NETGRAPH_BRIDGE, M_WAITOK | M_ZERO);
344 priv->numBuckets = MIN_BUCKETS;
345 priv->hashMask = MIN_BUCKETS - 1;
346 priv->conf.debugLevel = 1;
347 priv->conf.loopTimeout = DEFAULT_LOOP_TIMEOUT;
348 priv->conf.maxStaleness = DEFAULT_MAX_STALENESS;
349 priv->conf.minStableAge = DEFAULT_MIN_STABLE_AGE;
350 priv->sendUnknown = 1; /* classic bridge */
352 NG_NODE_SET_PRIVATE(node, priv);
355 /* Start timer; timer is always running while node is alive */
356 ng_callout(&priv->timer, node, NULL, hz, ng_bridge_timeout, NULL, 0);
363 * Method for attaching a new hook
366 ng_bridge_newhook(node_p node, hook_p hook, const char *name)
368 const priv_p priv = NG_NODE_PRIVATE(node);
369 char linkName[NG_HOOKSIZ];
372 const char *prefix = NG_BRIDGE_HOOK_LINK_PREFIX;
375 /* Check for a link hook */
376 if (strlen(name) <= strlen(prefix))
377 return (EINVAL); /* Unknown hook name */
379 isUplink = (name[0] == 'u');
381 prefix = NG_BRIDGE_HOOK_UPLINK_PREFIX;
383 /* primitive parsing */
384 linkNum = strtoul(name + strlen(prefix), NULL, 10);
385 /* validation by comparing against the reconstucted name */
386 snprintf(linkName, sizeof(linkName), "%s%u", prefix, linkNum);
387 if (strcmp(linkName, name) != 0)
390 if (linkNum == 0 && isUplink)
393 if(NG_PEER_NODE(hook) == node)
396 link = malloc(sizeof(*link), M_NETGRAPH_BRIDGE, M_NOWAIT | M_ZERO);
400 #define NG_BRIDGE_COUNTER_ALLOC(f) do { \
401 link->stats.f = counter_u64_alloc(M_NOWAIT); \
402 if (link->stats.f == NULL) \
405 NG_BRIDGE_COUNTER_ALLOC(recvOctets);
406 NG_BRIDGE_COUNTER_ALLOC(recvPackets);
407 NG_BRIDGE_COUNTER_ALLOC(recvMulticasts);
408 NG_BRIDGE_COUNTER_ALLOC(recvBroadcasts);
409 NG_BRIDGE_COUNTER_ALLOC(recvUnknown);
410 NG_BRIDGE_COUNTER_ALLOC(recvRunts);
411 NG_BRIDGE_COUNTER_ALLOC(recvInvalid);
412 NG_BRIDGE_COUNTER_ALLOC(xmitOctets);
413 NG_BRIDGE_COUNTER_ALLOC(xmitPackets);
414 NG_BRIDGE_COUNTER_ALLOC(xmitMulticasts);
415 NG_BRIDGE_COUNTER_ALLOC(xmitBroadcasts);
416 NG_BRIDGE_COUNTER_ALLOC(loopDrops);
417 NG_BRIDGE_COUNTER_ALLOC(memoryFailures);
418 #undef NG_BRIDGE_COUNTER_ALLOC
423 link->sendUnknown = 1;
424 if (priv->numLinks == 0) /* if the first link is an uplink */
425 priv->sendUnknown = 0; /* switch to restrictive mode */
428 link->sendUnknown = priv->sendUnknown;
431 NG_HOOK_SET_PRIVATE(hook, link);
436 ng_bridge_free_link(link);
441 * Receive a control message
444 ng_bridge_clear_link_stats(struct ng_bridge_link_kernel_stats *p)
446 counter_u64_zero(p->recvOctets);
447 counter_u64_zero(p->recvPackets);
448 counter_u64_zero(p->recvMulticasts);
449 counter_u64_zero(p->recvBroadcasts);
450 counter_u64_zero(p->recvUnknown);
451 counter_u64_zero(p->recvRunts);
452 counter_u64_zero(p->recvInvalid);
453 counter_u64_zero(p->xmitOctets);
454 counter_u64_zero(p->xmitPackets);
455 counter_u64_zero(p->xmitMulticasts);
456 counter_u64_zero(p->xmitBroadcasts);
457 counter_u64_zero(p->loopDrops);
459 counter_u64_zero(p->memoryFailures);
463 ng_bridge_free_link(link_p link)
465 counter_u64_free(link->stats.recvOctets);
466 counter_u64_free(link->stats.recvPackets);
467 counter_u64_free(link->stats.recvMulticasts);
468 counter_u64_free(link->stats.recvBroadcasts);
469 counter_u64_free(link->stats.recvUnknown);
470 counter_u64_free(link->stats.recvRunts);
471 counter_u64_free(link->stats.recvInvalid);
472 counter_u64_free(link->stats.xmitOctets);
473 counter_u64_free(link->stats.xmitPackets);
474 counter_u64_free(link->stats.xmitMulticasts);
475 counter_u64_free(link->stats.xmitBroadcasts);
476 counter_u64_free(link->stats.loopDrops);
477 counter_u64_free(link->stats.memoryFailures);
478 free(link, M_NETGRAPH_BRIDGE);
482 ng_bridge_reset_link(hook_p hook, void *arg __unused)
484 link_p priv = NG_HOOK_PRIVATE(hook);
487 ng_bridge_clear_link_stats(&priv->stats);
492 ng_bridge_rcvmsg(node_p node, item_p item, hook_p lasthook)
494 const priv_p priv = NG_NODE_PRIVATE(node);
495 struct ng_mesg *resp = NULL;
499 NGI_GET_MSG(item, msg);
500 switch (msg->header.typecookie) {
501 case NGM_BRIDGE_COOKIE:
502 switch (msg->header.cmd) {
503 case NGM_BRIDGE_GET_CONFIG:
505 struct ng_bridge_config *conf;
507 NG_MKRESPONSE(resp, msg,
508 sizeof(struct ng_bridge_config), M_NOWAIT);
513 conf = (struct ng_bridge_config *)resp->data;
514 *conf = priv->conf; /* no sanity checking needed */
517 case NGM_BRIDGE_SET_CONFIG:
519 struct ng_bridge_config *conf;
521 if (msg->header.arglen
522 != sizeof(struct ng_bridge_config)) {
526 conf = (struct ng_bridge_config *)msg->data;
530 case NGM_BRIDGE_RESET:
532 /* Flush all entries in the hash table */
533 ng_bridge_remove_hosts(priv, NULL);
535 /* Reset all loop detection counters and stats */
536 NG_NODE_FOREACH_HOOK(node, ng_bridge_reset_link, NULL);
539 case NGM_BRIDGE_GET_STATS:
540 case NGM_BRIDGE_CLR_STATS:
541 case NGM_BRIDGE_GETCLR_STATS:
545 char linkName[NG_HOOKSIZ];
548 /* Get link number */
549 if (msg->header.arglen != sizeof(u_int32_t)) {
553 linkNum = *((int32_t *)msg->data);
555 snprintf(linkName, sizeof(linkName),
556 "%s%u", NG_BRIDGE_HOOK_UPLINK_PREFIX, -linkNum);
558 snprintf(linkName, sizeof(linkName),
559 "%s%u", NG_BRIDGE_HOOK_LINK_PREFIX, linkNum);
561 if ((hook = ng_findhook(node, linkName)) == NULL) {
565 link = NG_HOOK_PRIVATE(hook);
567 /* Get/clear stats */
568 if (msg->header.cmd != NGM_BRIDGE_CLR_STATS) {
569 struct ng_bridge_link_stats *rs;
571 NG_MKRESPONSE(resp, msg,
572 sizeof(link->stats), M_NOWAIT);
577 rs = (struct ng_bridge_link_stats *)resp->data;
578 #define FETCH(x) rs->x = counter_u64_fetch(link->stats.x)
581 FETCH(recvMulticasts);
582 FETCH(recvBroadcasts);
588 FETCH(xmitMulticasts);
589 FETCH(xmitBroadcasts);
591 rs->loopDetects = link->stats.loopDetects;
592 FETCH(memoryFailures);
595 if (msg->header.cmd != NGM_BRIDGE_GET_STATS)
596 ng_bridge_clear_link_stats(&link->stats);
599 case NGM_BRIDGE_GET_TABLE:
601 struct ng_bridge_host_ary *ary;
602 struct ng_bridge_host *host;
605 NG_MKRESPONSE(resp, msg, sizeof(*ary)
606 + (priv->numHosts * sizeof(*ary->hosts)), M_NOWAIT);
611 ary = (struct ng_bridge_host_ary *)resp->data;
612 ary->numHosts = priv->numHosts;
613 for (bucket = 0; bucket < priv->numBuckets; bucket++) {
614 SLIST_FOREACH(host, &priv->tab[bucket], next) {
615 memcpy(ary->hosts[i].addr,
617 sizeof(ary->hosts[i].addr));
618 ary->hosts[i].age = host->age;
619 ary->hosts[i].staleness = host->staleness;
620 strncpy(ary->hosts[i].hook,
621 NG_HOOK_NAME(host->link->hook),
622 sizeof(ary->hosts[i].hook));
628 case NGM_BRIDGE_SET_PERSISTENT:
630 priv->persistent = 1;
633 case NGM_BRIDGE_MOVE_HOST:
635 struct ng_bridge_move_host *mh;
638 if (msg->header.arglen < sizeof(*mh)) {
642 mh = (struct ng_bridge_move_host *)msg->data;
643 hook = (mh->hook[0] == 0)
645 : ng_findhook(node, mh->hook);
650 error = ng_bridge_put(priv, mh->addr, NG_HOOK_PRIVATE(hook));
664 NG_RESPOND_MSG(error, node, item, resp);
670 * Receive data on a hook
672 struct ng_bridge_send_ctx {
673 link_p foundFirst, incoming;
679 * Update stats and send out
682 ng_bridge_send_data(link_cp dst, int manycast, struct mbuf *m, item_p item) {
684 size_t len = m->m_pkthdr.len;
687 NG_FWD_NEW_DATA(error, item, dst->hook, m);
689 NG_SEND_DATA_ONLY(error, dst->hook, m);
693 counter_u64_add(dst->stats.memoryFailures, 1);
694 /* The packet is still ours */
702 counter_u64_add(dst->stats.xmitPackets, 1);
703 counter_u64_add(dst->stats.xmitOctets, len);
705 default: /* unknown unicast */
707 case 1: /* multicast */
708 counter_u64_add(dst->stats.xmitMulticasts, 1);
710 case 2: /* broadcast */
711 counter_u64_add(dst->stats.xmitBroadcasts, 1);
718 * Loop body for sending to multiple destinations
719 * return 0 to stop looping
722 ng_bridge_send_ctx(hook_p dst, void *arg)
724 struct ng_bridge_send_ctx *ctx = arg;
725 link_p destLink = NG_HOOK_PRIVATE(dst);
726 struct mbuf *m2 = NULL;
729 /* Skip incoming link */
730 if (destLink == ctx->incoming) {
734 /* Skip sending unknowns to undesired links */
735 if (!ctx->manycast && !destLink->sendUnknown)
738 if (ctx->foundFirst == NULL) {
740 * This is the first usable link we have found.
741 * Reserve it for the originals.
742 * If we never find another we save a copy.
744 ctx->foundFirst = destLink;
749 * It's usable link but not the reserved (first) one.
750 * Copy mbuf info for sending.
752 m2 = m_dup(ctx->m, M_NOWAIT);
754 counter_u64_add(ctx->incoming->stats.memoryFailures, 1);
755 ctx->error = ENOBUFS;
756 return (0); /* abort loop, do not try again and again */
760 error = ng_bridge_send_data(destLink, ctx->manycast, m2, NULL);
767 ng_bridge_rcvdata(hook_p hook, item_p item)
769 const node_p node = NG_HOOK_NODE(hook);
770 const priv_p priv = NG_NODE_PRIVATE(node);
771 struct ng_bridge_host *host;
772 struct ether_header *eh;
773 struct ng_bridge_send_ctx ctx = { 0 };
775 NGI_GET_M(item, ctx.m);
777 ctx.incoming = NG_HOOK_PRIVATE(hook);
778 /* Sanity check packet and pull up header */
779 if (ctx.m->m_pkthdr.len < ETHER_HDR_LEN) {
780 counter_u64_add(ctx.incoming->stats.recvRunts, 1);
785 if (ctx.m->m_len < ETHER_HDR_LEN && !(ctx.m = m_pullup(ctx.m, ETHER_HDR_LEN))) {
786 counter_u64_add(ctx.incoming->stats.memoryFailures, 1);
790 eh = mtod(ctx.m, struct ether_header *);
791 if ((eh->ether_shost[0] & 1) != 0) {
792 counter_u64_add(ctx.incoming->stats.recvInvalid, 1);
798 /* Is link disabled due to a loopback condition? */
799 if (ctx.incoming->loopCount != 0) {
800 counter_u64_add(ctx.incoming->stats.loopDrops, 1);
807 counter_u64_add(ctx.incoming->stats.recvPackets, 1);
808 counter_u64_add(ctx.incoming->stats.recvOctets, ctx.m->m_pkthdr.len);
809 if ((ctx.manycast = (eh->ether_dhost[0] & 1)) != 0) {
810 if (ETHER_EQUAL(eh->ether_dhost, ng_bridge_bcast_addr)) {
811 counter_u64_add(ctx.incoming->stats.recvBroadcasts, 1);
814 counter_u64_add(ctx.incoming->stats.recvMulticasts, 1);
817 /* Look up packet's source Ethernet address in hashtable */
818 if ((host = ng_bridge_get(priv, eh->ether_shost)) != NULL)
819 /* Update time since last heard from this host.
820 * This is safe without locking, because it's
821 * the only operation during shared access.
823 if (__predict_false(host->staleness > 0))
826 if ((host == NULL && ctx.incoming->learnMac) ||
827 (host != NULL && host->link != ctx.incoming)) {
829 struct ng_bridge_move_host *mh;
832 NG_MKMESSAGE(msg, NGM_BRIDGE_COOKIE, NGM_BRIDGE_MOVE_HOST,
833 sizeof(*mh), M_NOWAIT);
835 counter_u64_add(ctx.incoming->stats.memoryFailures, 1);
840 mh = (struct ng_bridge_move_host *)msg->data;
841 strncpy(mh->hook, NG_HOOK_NAME(ctx.incoming->hook),
843 memcpy(mh->addr, eh->ether_shost, sizeof(mh->addr));
844 NG_SEND_MSG_ID(error, node, msg, NG_NODE_ID(node),
847 counter_u64_add(ctx.incoming->stats.memoryFailures, 1);
850 if (host != NULL && host->link != ctx.incoming) {
851 if (host->age < priv->conf.minStableAge) {
852 /* Drop packet on instable links */
853 counter_u64_add(ctx.incoming->stats.loopDrops, 1);
861 * If unicast and destination host known, deliver to host's link,
862 * unless it is the same link as the packet came in on.
865 /* Determine packet destination link */
866 if ((host = ng_bridge_get(priv, eh->ether_dhost)) != NULL) {
867 link_p destLink = host->link;
869 /* If destination same as incoming link, do nothing */
870 if (destLink == ctx.incoming) {
876 /* Deliver packet out the destination link */
877 return (ng_bridge_send_data(destLink, ctx.manycast, ctx.m, item));
880 /* Destination host is not known */
881 counter_u64_add(ctx.incoming->stats.recvUnknown, 1);
884 /* Distribute unknown, multicast, broadcast pkts to all other links */
885 NG_NODE_FOREACH_HOOK(node, ng_bridge_send_ctx, &ctx);
887 /* Finally send out on the first link found */
888 if (ctx.foundFirst != NULL) {
889 int error = ng_bridge_send_data(ctx.foundFirst, ctx.manycast, ctx.m, item);
892 } else { /* nothing to send at all */
904 ng_bridge_shutdown(node_p node)
906 const priv_p priv = NG_NODE_PRIVATE(node);
909 * Shut down everything including the timer. Even if the
910 * callout has already been dequeued and is about to be
911 * run, ng_bridge_timeout() won't be fired as the node
912 * is already marked NGF_INVALID, so we're safe to free
915 KASSERT(priv->numLinks == 0 && priv->numHosts == 0,
916 ("%s: numLinks=%d numHosts=%d",
917 __func__, priv->numLinks, priv->numHosts));
918 ng_uncallout(&priv->timer, node);
919 NG_NODE_SET_PRIVATE(node, NULL);
921 free(priv->tab, M_NETGRAPH_BRIDGE);
922 free(priv, M_NETGRAPH_BRIDGE);
927 * Hook disconnection.
930 ng_bridge_disconnect(hook_p hook)
932 const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
933 link_p link = NG_HOOK_PRIVATE(hook);
935 /* Remove all hosts associated with this link */
936 ng_bridge_remove_hosts(priv, link);
938 /* Free associated link information */
939 ng_bridge_free_link(link);
942 /* If no more hooks, go away */
943 if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0)
944 && (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))
945 && !priv->persistent) {
946 ng_rmnode_self(NG_HOOK_NODE(hook));
951 /******************************************************************
953 ******************************************************************/
958 #define HASH(addr,mask) ( (((const u_int16_t *)(addr))[0] \
959 ^ ((const u_int16_t *)(addr))[1] \
960 ^ ((const u_int16_t *)(addr))[2]) & (mask) )
963 * Find a host entry in the table.
965 static struct ng_bridge_host *
966 ng_bridge_get(priv_cp priv, const u_char *addr)
968 const int bucket = HASH(addr, priv->hashMask);
969 struct ng_bridge_host *host;
971 SLIST_FOREACH(host, &priv->tab[bucket], next) {
972 if (ETHER_EQUAL(host->addr, addr))
979 * Add a host entry to the table. If it already exists, move it
980 * to the new link. Returns 0 on success.
983 ng_bridge_put(priv_p priv, const u_char *addr, link_p link)
985 const int bucket = HASH(addr, priv->hashMask);
986 struct ng_bridge_host *host;
988 if ((host = ng_bridge_get(priv, addr)) != NULL) {
989 /* Host already on the correct link? */
990 if (host->link == link)
993 /* Move old host over to new link */
994 if (host->age >= priv->conf.minStableAge) {
1000 * If the host was recently moved to the old link and
1001 * it's now jumping to a new link, declare a loopback
1004 if (priv->conf.debugLevel >= 2)
1005 log(LOG_WARNING, "ng_bridge: %s:"
1006 " loopback detected on %s\n",
1007 ng_bridge_nodename(priv->node),
1008 NG_HOOK_NAME(link->hook));
1010 /* Mark link as linka non grata */
1011 link->loopCount = priv->conf.loopTimeout;
1012 link->stats.loopDetects++;
1014 /* Forget all hosts on this link */
1015 ng_bridge_remove_hosts(priv, link);
1019 /* Allocate and initialize new hashtable entry */
1020 host = malloc(sizeof(*host), M_NETGRAPH_BRIDGE, M_NOWAIT);
1023 bcopy(addr, host->addr, ETHER_ADDR_LEN);
1025 host->staleness = 0;
1028 /* Add new element to hash bucket */
1029 SLIST_INSERT_HEAD(&priv->tab[bucket], host, next);
1032 /* Resize table if necessary */
1033 ng_bridge_rehash(priv);
1038 * Resize the hash table. We try to maintain the number of buckets
1039 * such that the load factor is in the range 0.25 to 1.0.
1041 * If we can't get the new memory then we silently fail. This is OK
1042 * because things will still work and we'll try again soon anyway.
1045 ng_bridge_rehash(priv_p priv)
1047 struct ng_bridge_bucket *newTab;
1048 int oldBucket, newBucket;
1052 /* Is table too full or too empty? */
1053 if (priv->numHosts > priv->numBuckets
1054 && (priv->numBuckets << 1) <= MAX_BUCKETS)
1055 newNumBuckets = priv->numBuckets << 1;
1056 else if (priv->numHosts < (priv->numBuckets >> 2)
1057 && (priv->numBuckets >> 2) >= MIN_BUCKETS)
1058 newNumBuckets = priv->numBuckets >> 2;
1061 newMask = newNumBuckets - 1;
1063 /* Allocate and initialize new table */
1064 newTab = malloc(newNumBuckets * sizeof(*newTab),
1065 M_NETGRAPH_BRIDGE, M_NOWAIT | M_ZERO);
1069 /* Move all entries from old table to new table */
1070 for (oldBucket = 0; oldBucket < priv->numBuckets; oldBucket++) {
1071 struct ng_bridge_bucket *const oldList = &priv->tab[oldBucket];
1073 while (!SLIST_EMPTY(oldList)) {
1074 struct ng_bridge_host *const host
1075 = SLIST_FIRST(oldList);
1077 SLIST_REMOVE_HEAD(oldList, next);
1078 newBucket = HASH(host->addr, newMask);
1079 SLIST_INSERT_HEAD(&newTab[newBucket], host, next);
1083 /* Replace old table with new one */
1084 if (priv->conf.debugLevel >= 3) {
1085 log(LOG_INFO, "ng_bridge: %s: table size %d -> %d\n",
1086 ng_bridge_nodename(priv->node),
1087 priv->numBuckets, newNumBuckets);
1089 free(priv->tab, M_NETGRAPH_BRIDGE);
1090 priv->numBuckets = newNumBuckets;
1091 priv->hashMask = newMask;
1096 /******************************************************************
1098 ******************************************************************/
1101 * Remove all hosts associated with a specific link from the hashtable.
1102 * If linkNum == -1, then remove all hosts in the table.
1105 ng_bridge_remove_hosts(priv_p priv, link_p link)
1109 for (bucket = 0; bucket < priv->numBuckets; bucket++) {
1110 struct ng_bridge_host **hptr = &SLIST_FIRST(&priv->tab[bucket]);
1112 while (*hptr != NULL) {
1113 struct ng_bridge_host *const host = *hptr;
1115 if (link == NULL || host->link == link) {
1116 *hptr = SLIST_NEXT(host, next);
1117 free(host, M_NETGRAPH_BRIDGE);
1120 hptr = &SLIST_NEXT(host, next);
1126 * Handle our once-per-second timeout event. We do two things:
1127 * we decrement link->loopCount for those links being muted due to
1128 * a detected loopback condition, and we remove any hosts from
1129 * the hashtable whom we haven't heard from in a long while.
1132 ng_bridge_unmute(hook_p hook, void *arg)
1134 link_p link = NG_HOOK_PRIVATE(hook);
1135 node_p node = NG_HOOK_NODE(hook);
1136 priv_p priv = NG_NODE_PRIVATE(node);
1139 if (link->loopCount != 0) {
1141 if (link->loopCount == 0 && priv->conf.debugLevel >= 2) {
1142 log(LOG_INFO, "ng_bridge: %s:"
1143 " restoring looped back %s\n",
1144 ng_bridge_nodename(node), NG_HOOK_NAME(hook));
1152 ng_bridge_timeout(node_p node, hook_p hook, void *arg1, int arg2)
1154 const priv_p priv = NG_NODE_PRIVATE(node);
1158 /* Update host time counters and remove stale entries */
1159 for (bucket = 0; bucket < priv->numBuckets; bucket++) {
1160 struct ng_bridge_host **hptr = &SLIST_FIRST(&priv->tab[bucket]);
1162 while (*hptr != NULL) {
1163 struct ng_bridge_host *const host = *hptr;
1165 /* Remove hosts we haven't heard from in a while */
1166 if (++host->staleness >= priv->conf.maxStaleness) {
1167 *hptr = SLIST_NEXT(host, next);
1168 free(host, M_NETGRAPH_BRIDGE);
1171 if (host->age < 0xffff)
1173 hptr = &SLIST_NEXT(host, next);
1178 KASSERT(priv->numHosts == counter,
1179 ("%s: hosts: %d != %d", __func__, priv->numHosts, counter));
1181 /* Decrease table size if necessary */
1182 ng_bridge_rehash(priv);
1184 /* Decrease loop counter on muted looped back links */
1186 NG_NODE_FOREACH_HOOK(node, ng_bridge_unmute, &counter);
1187 KASSERT(priv->numLinks == counter,
1188 ("%s: links: %d != %d", __func__, priv->numLinks, counter));
1190 /* Register a new timeout, keeping the existing node reference */
1191 ng_callout(&priv->timer, node, NULL, hz, ng_bridge_timeout, NULL, 0);
1195 * Return node's "name", even if it doesn't have one.
1198 ng_bridge_nodename(node_cp node)
1200 static char name[NG_NODESIZ];
1202 if (NG_NODE_HAS_NAME(node))
1203 snprintf(name, sizeof(name), "%s", NG_NODE_NAME(node));
1205 snprintf(name, sizeof(name), "[%x]", ng_node2ID(node));