The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/netgraph/ng_bridge.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

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

Cache object: 15d1b02be01ced74d7cedd8506428c38


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.