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