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_vlan_rotate.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  * Spdx-License-Identifier: BSD-2-Clause-FreeBSD
    3  *
    4  * Copyright (c) 2019-2021 IKS Service GmbH
    5  *
    6  * Redistribution and use in source and binary forms, with or without
    7  * modification, are permitted provided that the following conditions
    8  * are met:
    9  * 1. Redistributions of source code must retain the above copyright
   10  *    notice, this list of conditions and the following disclaimer.
   11  * 2. Redistributions in binary form must reproduce the above copyright
   12  *    notice, this list of conditions and the following disclaimer in the
   13  *    documentation and/or other materials provided with the distribution.
   14  *
   15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   25  * SUCH DAMAGE.
   26  *
   27  * Author: Lutz Donnerhacke <lutz@donnerhacke.de>
   28  *
   29  * $FreeBSD$
   30  */
   31 
   32 #include <sys/param.h>
   33 #include <sys/systm.h>
   34 #include <sys/kernel.h>
   35 #include <sys/mbuf.h>
   36 #include <sys/malloc.h>
   37 #include <sys/ctype.h>
   38 #include <sys/errno.h>
   39 #include <sys/syslog.h>
   40 #include <sys/types.h>
   41 #include <sys/counter.h>
   42 
   43 #include <net/ethernet.h>
   44 
   45 #include <netgraph/ng_message.h>
   46 #include <netgraph/ng_parse.h>
   47 #include <netgraph/ng_vlan_rotate.h>
   48 #include <netgraph/netgraph.h>
   49 
   50 /*
   51  * This section contains the netgraph method declarations for the
   52  * sample node. These methods define the netgraph 'type'.
   53  */
   54 
   55 static ng_constructor_t ng_vlanrotate_constructor;
   56 static ng_rcvmsg_t ng_vlanrotate_rcvmsg;
   57 static ng_shutdown_t ng_vlanrotate_shutdown;
   58 static ng_newhook_t ng_vlanrotate_newhook;
   59 static ng_rcvdata_t ng_vlanrotate_rcvdata;
   60 static ng_disconnect_t ng_vlanrotate_disconnect;
   61 
   62 /* Parse type for struct ng_vlanrotate_conf */
   63 static const struct ng_parse_struct_field ng_vlanrotate_conf_fields[] = {
   64         {"rot", &ng_parse_int8_type},
   65         {"min", &ng_parse_uint8_type},
   66         {"max", &ng_parse_uint8_type},
   67         {NULL}
   68 };
   69 static const struct ng_parse_type ng_vlanrotate_conf_type = {
   70         &ng_parse_struct_type,
   71         &ng_vlanrotate_conf_fields
   72 };
   73 
   74 /* Parse type for struct ng_vlanrotate_stat */
   75 static struct ng_parse_fixedarray_info ng_vlanrotate_stat_hist_info = {
   76         &ng_parse_uint64_type,
   77         NG_VLANROTATE_MAX_VLANS
   78 };
   79 static struct ng_parse_type ng_vlanrotate_stat_hist = {
   80         &ng_parse_fixedarray_type,
   81         &ng_vlanrotate_stat_hist_info
   82 };
   83 static const struct ng_parse_struct_field ng_vlanrotate_stat_fields[] = {
   84         {"drops", &ng_parse_uint64_type},
   85         {"excessive", &ng_parse_uint64_type},
   86         {"incomplete", &ng_parse_uint64_type},
   87         {"histogram", &ng_vlanrotate_stat_hist},
   88         {NULL}
   89 };
   90 static struct ng_parse_type ng_vlanrotate_stat_type = {
   91         &ng_parse_struct_type,
   92         &ng_vlanrotate_stat_fields
   93 };
   94 
   95 
   96 /* List of commands and how to convert arguments to/from ASCII */
   97 static const struct ng_cmdlist ng_vlanrotate_cmdlist[] = {
   98         {
   99                 NGM_VLANROTATE_COOKIE,
  100                 NGM_VLANROTATE_GET_CONF,
  101                 "getconf",
  102                 NULL,
  103                 &ng_vlanrotate_conf_type,
  104         },
  105         {
  106                 NGM_VLANROTATE_COOKIE,
  107                 NGM_VLANROTATE_SET_CONF,
  108                 "setconf",
  109                 &ng_vlanrotate_conf_type,
  110                 NULL
  111         },
  112         {
  113                 NGM_VLANROTATE_COOKIE,
  114                 NGM_VLANROTATE_GET_STAT,
  115                 "getstat",
  116                 NULL,
  117                 &ng_vlanrotate_stat_type
  118         },
  119         {
  120                 NGM_VLANROTATE_COOKIE,
  121                 NGM_VLANROTATE_CLR_STAT,
  122                 "clrstat",
  123                 NULL,
  124                 &ng_vlanrotate_stat_type
  125         },
  126         {
  127                 NGM_VLANROTATE_COOKIE,
  128                 NGM_VLANROTATE_GETCLR_STAT,
  129                 "getclrstat",
  130                 NULL,
  131                 &ng_vlanrotate_stat_type
  132         },
  133         {0}
  134 };
  135 
  136 /* Netgraph node type descriptor */
  137 static struct ng_type typestruct = {
  138         .version = NG_ABI_VERSION,
  139         .name = NG_VLANROTATE_NODE_TYPE,
  140         .constructor = ng_vlanrotate_constructor,
  141         .rcvmsg = ng_vlanrotate_rcvmsg,
  142         .shutdown = ng_vlanrotate_shutdown,
  143         .newhook = ng_vlanrotate_newhook,
  144         .rcvdata = ng_vlanrotate_rcvdata,
  145         .disconnect = ng_vlanrotate_disconnect,
  146         .cmdlist = ng_vlanrotate_cmdlist,
  147 };
  148 NETGRAPH_INIT(vlanrotate, &typestruct);
  149 
  150 struct ng_vlanrotate_kernel_stats {
  151         counter_u64_t   drops, excessive, incomplete;
  152         counter_u64_t   histogram[NG_VLANROTATE_MAX_VLANS];
  153 };
  154 
  155 /* Information we store for each node */
  156 struct vlanrotate {
  157         hook_p          original_hook;
  158         hook_p          ordered_hook;
  159         hook_p          excessive_hook;
  160         hook_p          incomplete_hook;
  161         struct ng_vlanrotate_conf conf;
  162         struct ng_vlanrotate_kernel_stats stats;
  163 };
  164 typedef struct vlanrotate *vlanrotate_p;
  165 
  166 /*
  167  * Set up the private data structure.
  168  */
  169 static int
  170 ng_vlanrotate_constructor(node_p node)
  171 {
  172         int i;
  173 
  174         vlanrotate_p vrp = malloc(sizeof(*vrp), M_NETGRAPH, M_WAITOK | M_ZERO);
  175 
  176         vrp->conf.max = NG_VLANROTATE_MAX_VLANS;
  177 
  178         vrp->stats.drops = counter_u64_alloc(M_WAITOK);
  179         vrp->stats.excessive = counter_u64_alloc(M_WAITOK);
  180         vrp->stats.incomplete = counter_u64_alloc(M_WAITOK);
  181         for (i = 0; i < NG_VLANROTATE_MAX_VLANS; i++)
  182                 vrp->stats.histogram[i] = counter_u64_alloc(M_WAITOK);
  183 
  184         NG_NODE_SET_PRIVATE(node, vrp);
  185         return (0);
  186 }
  187 
  188 /*
  189  * Give our ok for a hook to be added.
  190  */
  191 static int
  192 ng_vlanrotate_newhook(node_p node, hook_p hook, const char *name)
  193 {
  194         const vlanrotate_p vrp = NG_NODE_PRIVATE(node);
  195         hook_p *dst = NULL;
  196 
  197         if (strcmp(name, NG_VLANROTATE_HOOK_ORDERED) == 0) {
  198                 dst = &vrp->ordered_hook;
  199         } else if (strcmp(name, NG_VLANROTATE_HOOK_ORIGINAL) == 0) {
  200                 dst = &vrp->original_hook;
  201         } else if (strcmp(name, NG_VLANROTATE_HOOK_EXCESSIVE) == 0) {
  202                 dst = &vrp->excessive_hook;
  203         } else if (strcmp(name, NG_VLANROTATE_HOOK_INCOMPLETE) == 0) {
  204                 dst = &vrp->incomplete_hook;
  205         } else
  206                 return (EINVAL);        /* not a hook we know about */
  207 
  208         if (*dst != NULL)
  209                 return (EADDRINUSE);    /* don't override */
  210 
  211         *dst = hook;
  212         return (0);
  213 }
  214 
  215 /*
  216  * Get a netgraph control message.
  217  * A response is not required.
  218  */
  219 static int
  220 ng_vlanrotate_rcvmsg(node_p node, item_p item, hook_p lasthook)
  221 {
  222         const vlanrotate_p vrp = NG_NODE_PRIVATE(node);
  223         struct ng_mesg *resp = NULL;
  224         struct ng_mesg *msg;
  225         struct ng_vlanrotate_conf *pcf;
  226         int error = 0;
  227 
  228         NGI_GET_MSG(item, msg);
  229         /* Deal with message according to cookie and command */
  230         switch (msg->header.typecookie) {
  231         case NGM_VLANROTATE_COOKIE:
  232                 switch (msg->header.cmd) {
  233                 case NGM_VLANROTATE_GET_CONF:
  234                         NG_MKRESPONSE(resp, msg, sizeof(vrp->conf), M_NOWAIT);
  235                         if (!resp) {
  236                                 error = ENOMEM;
  237                                 break;
  238                         }
  239                         *((struct ng_vlanrotate_conf *)resp->data) = vrp->conf;
  240                         break;
  241                 case NGM_VLANROTATE_SET_CONF:
  242                         if (msg->header.arglen != sizeof(*pcf)) {
  243                                 error = EINVAL;
  244                                 break;
  245                         }
  246 
  247                         pcf = (struct ng_vlanrotate_conf *)msg->data;
  248 
  249                         if (pcf->max == 0)      /* keep current value */
  250                                 pcf->max = vrp->conf.max;
  251 
  252                         if ((pcf->max > NG_VLANROTATE_MAX_VLANS) ||
  253                             (pcf->min > pcf->max) ||
  254                             (abs(pcf->rot) >= pcf->max)) {
  255                                 error = EINVAL;
  256                                 break;
  257                         }
  258 
  259                         vrp->conf = *pcf;
  260                         break;
  261                 case NGM_VLANROTATE_GET_STAT:
  262                 case NGM_VLANROTATE_GETCLR_STAT:
  263                 {
  264                         struct ng_vlanrotate_stat *p;
  265                         int i;
  266 
  267                         NG_MKRESPONSE(resp, msg, sizeof(*p), M_NOWAIT);
  268                         if (!resp) {
  269                                 error = ENOMEM;
  270                                 break;
  271                         }
  272                         p = (struct ng_vlanrotate_stat *)resp->data;
  273                         p->drops = counter_u64_fetch(vrp->stats.drops);
  274                         p->excessive = counter_u64_fetch(vrp->stats.excessive);
  275                         p->incomplete = counter_u64_fetch(vrp->stats.incomplete);
  276                         for (i = 0; i < NG_VLANROTATE_MAX_VLANS; i++)
  277                                 p->histogram[i] = counter_u64_fetch(vrp->stats.histogram[i]);
  278                         if (msg->header.cmd != NGM_VLANROTATE_GETCLR_STAT)
  279                                 break;
  280                 }
  281                 case NGM_VLANROTATE_CLR_STAT:
  282                 {
  283                         int i;
  284 
  285                         counter_u64_zero(vrp->stats.drops);
  286                         counter_u64_zero(vrp->stats.excessive);
  287                         counter_u64_zero(vrp->stats.incomplete);
  288                         for (i = 0; i < NG_VLANROTATE_MAX_VLANS; i++)
  289                                 counter_u64_zero(vrp->stats.histogram[i]);
  290                         break;
  291                 }
  292                 default:
  293                         error = EINVAL; /* unknown command */
  294                         break;
  295                 }
  296                 break;
  297         default:
  298                 error = EINVAL; /* unknown cookie type */
  299                 break;
  300         }
  301 
  302         /* Take care of synchronous response, if any */
  303         NG_RESPOND_MSG(error, node, item, resp);
  304         /* Free the message and return */
  305         NG_FREE_MSG(msg);
  306         return (error);
  307 }
  308 
  309 /*
  310  * Receive data, and do rotate the vlans as desired.
  311  *
  312  * Rotating is quite complicated if the rotation offset and the number
  313  * of vlans are not relativly prime. In this case multiple slices need
  314  * to be rotated separately.
  315  *
  316  * Rotation can be additive or subtractive. Some examples:
  317  *  01234   5 vlans given
  318  *  -----
  319  *  34012  +2 rotate
  320  *  12340  +4 rotate
  321  *  12340  -1 rotate
  322  *
  323  * First some helper functions ...
  324  */
  325 
  326 struct ether_vlan_stack_entry {
  327         uint16_t        proto;
  328         uint16_t        tag;
  329 }               __packed;
  330 
  331 struct ether_vlan_stack_header {
  332         uint8_t         dst[ETHER_ADDR_LEN];
  333         uint8_t         src[ETHER_ADDR_LEN];
  334         struct ether_vlan_stack_entry vlan_stack[1];
  335 }               __packed;
  336 
  337 static int
  338 ng_vlanrotate_gcd(int a, int b)
  339 {
  340         if (b == 0)
  341                 return a;
  342         else
  343                 return ng_vlanrotate_gcd(b, a % b);
  344 }
  345 
  346 static void
  347 ng_vlanrotate_rotate(struct ether_vlan_stack_entry arr[], int d, int n)
  348 {
  349         int             i, j, k;
  350         struct ether_vlan_stack_entry temp;
  351 
  352         /* for each commensurable slice */
  353         for (i = ng_vlanrotate_gcd(d, n); i-- > 0;) {
  354                 /* rotate left aka downwards */
  355                 temp = arr[i];
  356                 j = i;
  357 
  358                 while (1) {
  359                         k = j + d;
  360                         if (k >= n)
  361                                 k = k - n;
  362                         if (k == i)
  363                                 break;
  364                         arr[j] = arr[k];
  365                         j = k;
  366                 }
  367 
  368                 arr[j] = temp;
  369         }
  370 }
  371 
  372 static int
  373 ng_vlanrotate_rcvdata(hook_p hook, item_p item)
  374 {
  375         const vlanrotate_p vrp = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
  376         struct ether_vlan_stack_header *evsh;
  377         struct mbuf *m = NULL;
  378         hook_p  dst_hook;
  379         int8_t  rotate;
  380         int8_t  vlans = 0;
  381         int     error = ENOSYS;
  382 
  383         NGI_GET_M(item, m);
  384 
  385         if (hook == vrp->ordered_hook) {
  386                 rotate = +vrp->conf.rot;
  387                 dst_hook = vrp->original_hook;
  388         } else if (hook == vrp->original_hook) {
  389                 rotate = -vrp->conf.rot;
  390                 dst_hook = vrp->ordered_hook;
  391         } else {
  392                 dst_hook = vrp->original_hook;
  393                 goto send;      /* everything else goes out unmodified */
  394         }
  395 
  396         if (dst_hook == NULL) {
  397                 error = ENETDOWN;
  398                 goto fail;
  399         }
  400 
  401         /* count the vlans */
  402         for (vlans = 0; vlans <= NG_VLANROTATE_MAX_VLANS; vlans++) {
  403                 size_t expected_len = sizeof(struct ether_vlan_stack_header)
  404                     + vlans * sizeof(struct ether_vlan_stack_entry);
  405 
  406                 if (m->m_len < expected_len) {
  407                         m = m_pullup(m, expected_len);
  408                         if (m == NULL) {
  409                                 error = EINVAL;
  410                                 goto fail;
  411                         }
  412                 }
  413 
  414                 evsh = mtod(m, struct ether_vlan_stack_header *);
  415                 switch (ntohs(evsh->vlan_stack[vlans].proto)) {
  416                 case ETHERTYPE_VLAN:
  417                 case ETHERTYPE_QINQ:
  418                 case ETHERTYPE_8021Q9100:
  419                 case ETHERTYPE_8021Q9200:
  420                 case ETHERTYPE_8021Q9300:
  421                         break;
  422                 default:
  423                         goto out;
  424                 }
  425         }
  426 out:
  427         if ((vlans > vrp->conf.max) || (vlans >= NG_VLANROTATE_MAX_VLANS)) {
  428                 counter_u64_add(vrp->stats.excessive, 1);
  429                 dst_hook = vrp->excessive_hook;
  430                 goto send;
  431         }
  432 
  433         if ((vlans < vrp->conf.min) || (vlans <= abs(rotate))) {
  434                 counter_u64_add(vrp->stats.incomplete, 1);
  435                 dst_hook = vrp->incomplete_hook;
  436                 goto send;
  437         }
  438         counter_u64_add(vrp->stats.histogram[vlans], 1);
  439 
  440         /* rotating upwards always (using modular arithmetics) */
  441         if (rotate == 0) {
  442                 /* nothing to do */
  443         } else if (rotate > 0) {
  444                 ng_vlanrotate_rotate(evsh->vlan_stack, rotate, vlans);
  445         } else {
  446                 ng_vlanrotate_rotate(evsh->vlan_stack, vlans + rotate, vlans);
  447         }
  448 
  449 send:
  450         if (dst_hook == NULL)
  451                 goto fail;
  452         NG_FWD_NEW_DATA(error, item, dst_hook, m);
  453         return 0;
  454 
  455 fail:
  456         counter_u64_add(vrp->stats.drops, 1);
  457         if (m != NULL)
  458                 m_freem(m);
  459         NG_FREE_ITEM(item);
  460         return (error);
  461 }
  462 
  463 /*
  464  * Do local shutdown processing..
  465  * All our links and the name have already been removed.
  466  */
  467 static int
  468 ng_vlanrotate_shutdown(node_p node)
  469 {
  470         const           vlanrotate_p vrp = NG_NODE_PRIVATE(node);
  471         int i;
  472 
  473         NG_NODE_SET_PRIVATE(node, NULL);
  474 
  475         counter_u64_free(vrp->stats.drops);
  476         counter_u64_free(vrp->stats.excessive);
  477         counter_u64_free(vrp->stats.incomplete);
  478         for (i = 0; i < NG_VLANROTATE_MAX_VLANS; i++)
  479                 counter_u64_free(vrp->stats.histogram[i]);
  480 
  481         free(vrp, M_NETGRAPH);
  482 
  483         NG_NODE_UNREF(node);
  484         return (0);
  485 }
  486 
  487 /*
  488  * Hook disconnection
  489  * For this type, removal of the last link destroys the node
  490  */
  491 static int
  492 ng_vlanrotate_disconnect(hook_p hook)
  493 {
  494         const           vlanrotate_p vrp = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
  495 
  496         if (vrp->original_hook == hook)
  497                 vrp->original_hook = NULL;
  498         if (vrp->ordered_hook == hook)
  499                 vrp->ordered_hook = NULL;
  500         if (vrp->excessive_hook == hook)
  501                 vrp->excessive_hook = NULL;
  502         if (vrp->incomplete_hook == hook)
  503                 vrp->incomplete_hook = NULL;
  504 
  505         /* during shutdown the node is invalid, don't shutdown twice */
  506         if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) &&
  507             (NG_NODE_IS_VALID(NG_HOOK_NODE(hook))))
  508                 ng_rmnode_self(NG_HOOK_NODE(hook));
  509         return (0);
  510 }

Cache object: fb8335346be76afa00a3972dbabea7f5


[ 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.