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/net/if_vlan.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 /*      $NetBSD: if_vlan.c,v 1.52 2006/11/16 01:33:40 christos Exp $    */
    2 
    3 /*-
    4  * Copyright (c) 2000, 2001 The NetBSD Foundation, Inc.
    5  * All rights reserved.
    6  *
    7  * This code is derived from software contributed to The NetBSD Foundation
    8  * by Andrew Doran, and by Jason R. Thorpe of Zembu Labs, Inc.
    9  *
   10  * Redistribution and use in source and binary forms, with or without
   11  * modification, are permitted provided that the following conditions
   12  * are met:
   13  * 1. Redistributions of source code must retain the above copyright
   14  *    notice, this list of conditions and the following disclaimer.
   15  * 2. Redistributions in binary form must reproduce the above copyright
   16  *    notice, this list of conditions and the following disclaimer in the
   17  *    documentation and/or other materials provided with the distribution.
   18  * 3. All advertising materials mentioning features or use of this software
   19  *    must display the following acknowledgement:
   20  *      This product includes software developed by the NetBSD
   21  *      Foundation, Inc. and its contributors.
   22  * 4. Neither the name of The NetBSD Foundation nor the names of its
   23  *    contributors may be used to endorse or promote products derived
   24  *    from this software without specific prior written permission.
   25  *
   26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
   27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
   28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
   29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
   30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   36  * POSSIBILITY OF SUCH DAMAGE.
   37  */
   38 
   39 /*
   40  * Copyright 1998 Massachusetts Institute of Technology
   41  *
   42  * Permission to use, copy, modify, and distribute this software and
   43  * its documentation for any purpose and without fee is hereby
   44  * granted, provided that both the above copyright notice and this
   45  * permission notice appear in all copies, that both the above
   46  * copyright notice and this permission notice appear in all
   47  * supporting documentation, and that the name of M.I.T. not be used
   48  * in advertising or publicity pertaining to distribution of the
   49  * software without specific, written prior permission.  M.I.T. makes
   50  * no representations about the suitability of this software for any
   51  * purpose.  It is provided "as is" without express or implied
   52  * warranty.
   53  *
   54  * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''.  M.I.T. DISCLAIMS
   55  * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
   56  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
   57  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
   58  * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   59  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   60  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   61  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   62  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   63  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   64  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   65  * SUCH DAMAGE.
   66  *
   67  * from FreeBSD: if_vlan.c,v 1.16 2000/03/26 15:21:40 charnier Exp
   68  * via OpenBSD: if_vlan.c,v 1.4 2000/05/15 19:15:00 chris Exp
   69  */
   70 
   71 /*
   72  * if_vlan.c - pseudo-device driver for IEEE 802.1Q virtual LANs.  Might be
   73  * extended some day to also handle IEEE 802.1P priority tagging.  This is
   74  * sort of sneaky in the implementation, since we need to pretend to be
   75  * enough of an Ethernet implementation to make ARP work.  The way we do
   76  * this is by telling everyone that we are an Ethernet interface, and then
   77  * catch the packets that ether_output() left on our output queue when it
   78  * calls if_start(), rewrite them for use by the real outgoing interface,
   79  * and ask it to send them.
   80  *
   81  * TODO:
   82  *
   83  *      - Need some way to notify vlan interfaces when the parent
   84  *        interface changes MTU.
   85  */
   86 
   87 #include <sys/cdefs.h>
   88 __KERNEL_RCSID(0, "$NetBSD: if_vlan.c,v 1.52 2006/11/16 01:33:40 christos Exp $");
   89 
   90 #include "opt_inet.h"
   91 #include "bpfilter.h"
   92 
   93 #include <sys/param.h>
   94 #include <sys/kernel.h>
   95 #include <sys/mbuf.h>
   96 #include <sys/queue.h>
   97 #include <sys/socket.h>
   98 #include <sys/sockio.h>
   99 #include <sys/systm.h>
  100 #include <sys/proc.h>
  101 #include <sys/kauth.h>
  102 
  103 #if NBPFILTER > 0
  104 #include <net/bpf.h>
  105 #endif
  106 #include <net/if.h>
  107 #include <net/if_dl.h>
  108 #include <net/if_types.h>
  109 #include <net/if_ether.h>
  110 #include <net/if_vlanvar.h>
  111 
  112 #ifdef INET
  113 #include <netinet/in.h>
  114 #include <netinet/if_inarp.h>
  115 #endif
  116 
  117 struct vlan_mc_entry {
  118         LIST_ENTRY(vlan_mc_entry)       mc_entries;
  119         /*
  120          * A key to identify this entry.  The mc_addr below can't be
  121          * used since multiple sockaddr may mapped into the same
  122          * ether_multi (e.g., AF_UNSPEC).
  123          */
  124         union {
  125                 struct ether_multi      *mcu_enm;
  126         } mc_u;
  127         struct sockaddr_storage         mc_addr;
  128 };
  129 
  130 #define mc_enm          mc_u.mcu_enm
  131 
  132 struct ifvlan {
  133         union {
  134                 struct ethercom ifvu_ec;
  135         } ifv_u;
  136         struct ifnet *ifv_p;    /* parent interface of this vlan */
  137         struct ifv_linkmib {
  138                 const struct vlan_multisw *ifvm_msw;
  139                 int     ifvm_encaplen;  /* encapsulation length */
  140                 int     ifvm_mtufudge;  /* MTU fudged by this much */
  141                 int     ifvm_mintu;     /* min transmission unit */
  142                 u_int16_t ifvm_proto;   /* encapsulation ethertype */
  143                 u_int16_t ifvm_tag;     /* tag to apply on packets */
  144         } ifv_mib;
  145         LIST_HEAD(__vlan_mchead, vlan_mc_entry) ifv_mc_listhead;
  146         LIST_ENTRY(ifvlan) ifv_list;
  147         int ifv_flags;
  148 };
  149 
  150 #define IFVF_PROMISC    0x01            /* promiscuous mode enabled */
  151 
  152 #define ifv_ec          ifv_u.ifvu_ec
  153 
  154 #define ifv_if          ifv_ec.ec_if
  155 
  156 #define ifv_msw         ifv_mib.ifvm_msw
  157 #define ifv_encaplen    ifv_mib.ifvm_encaplen
  158 #define ifv_mtufudge    ifv_mib.ifvm_mtufudge
  159 #define ifv_mintu       ifv_mib.ifvm_mintu
  160 #define ifv_tag         ifv_mib.ifvm_tag
  161 
  162 struct vlan_multisw {
  163         int     (*vmsw_addmulti)(struct ifvlan *, struct ifreq *);
  164         int     (*vmsw_delmulti)(struct ifvlan *, struct ifreq *);
  165         void    (*vmsw_purgemulti)(struct ifvlan *);
  166 };
  167 
  168 static int      vlan_ether_addmulti(struct ifvlan *, struct ifreq *);
  169 static int      vlan_ether_delmulti(struct ifvlan *, struct ifreq *);
  170 static void     vlan_ether_purgemulti(struct ifvlan *);
  171 
  172 const struct vlan_multisw vlan_ether_multisw = {
  173         vlan_ether_addmulti,
  174         vlan_ether_delmulti,
  175         vlan_ether_purgemulti,
  176 };
  177 
  178 static int      vlan_clone_create(struct if_clone *, int);
  179 static int      vlan_clone_destroy(struct ifnet *);
  180 static int      vlan_config(struct ifvlan *, struct ifnet *);
  181 static int      vlan_ioctl(struct ifnet *, u_long, caddr_t);
  182 static void     vlan_start(struct ifnet *);
  183 static void     vlan_unconfig(struct ifnet *);
  184 
  185 void            vlanattach(int);
  186 
  187 /* XXX This should be a hash table with the tag as the basis of the key. */
  188 static LIST_HEAD(, ifvlan) ifv_list;
  189 
  190 struct if_clone vlan_cloner =
  191     IF_CLONE_INITIALIZER("vlan", vlan_clone_create, vlan_clone_destroy);
  192 
  193 /* Used to pad ethernet frames with < ETHER_MIN_LEN bytes */
  194 static char vlan_zero_pad_buff[ETHER_MIN_LEN];
  195 
  196 void
  197 vlanattach(int n)
  198 {
  199 
  200         LIST_INIT(&ifv_list);
  201         if_clone_attach(&vlan_cloner);
  202 }
  203 
  204 static void
  205 vlan_reset_linkname(struct ifnet *ifp)
  206 {
  207 
  208         /*
  209          * We start out with a "802.1Q VLAN" type and zero-length
  210          * addresses.  When we attach to a parent interface, we
  211          * inherit its type, address length, address, and data link
  212          * type.
  213          */
  214 
  215         ifp->if_type = IFT_L2VLAN;
  216         ifp->if_addrlen = 0;
  217         ifp->if_dlt = DLT_NULL;
  218         if_alloc_sadl(ifp);
  219 }
  220 
  221 static int
  222 vlan_clone_create(struct if_clone *ifc, int unit)
  223 {
  224         struct ifvlan *ifv;
  225         struct ifnet *ifp;
  226         int s;
  227 
  228         ifv = malloc(sizeof(struct ifvlan), M_DEVBUF, M_WAITOK);
  229         memset(ifv, 0, sizeof(struct ifvlan));
  230         ifp = &ifv->ifv_if;
  231         LIST_INIT(&ifv->ifv_mc_listhead);
  232 
  233         s = splnet();
  234         LIST_INSERT_HEAD(&ifv_list, ifv, ifv_list);
  235         splx(s);
  236 
  237         snprintf(ifp->if_xname, sizeof(ifp->if_xname), "%s%d", ifc->ifc_name,
  238             unit);
  239         ifp->if_softc = ifv;
  240         ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
  241         ifp->if_start = vlan_start;
  242         ifp->if_ioctl = vlan_ioctl;
  243         IFQ_SET_READY(&ifp->if_snd);
  244 
  245         if_attach(ifp);
  246         vlan_reset_linkname(ifp);
  247 
  248         return (0);
  249 }
  250 
  251 static int
  252 vlan_clone_destroy(struct ifnet *ifp)
  253 {
  254         struct ifvlan *ifv = ifp->if_softc;
  255         int s;
  256 
  257         s = splnet();
  258         LIST_REMOVE(ifv, ifv_list);
  259         vlan_unconfig(ifp);
  260         splx(s);
  261 
  262         if_detach(ifp);
  263         free(ifv, M_DEVBUF);
  264 
  265         return (0);
  266 }
  267 
  268 /*
  269  * Configure a VLAN interface.  Must be called at splnet().
  270  */
  271 static int
  272 vlan_config(struct ifvlan *ifv, struct ifnet *p)
  273 {
  274         struct ifnet *ifp = &ifv->ifv_if;
  275         int error;
  276 
  277         if (ifv->ifv_p != NULL)
  278                 return (EBUSY);
  279 
  280         switch (p->if_type) {
  281         case IFT_ETHER:
  282             {
  283                 struct ethercom *ec = (void *) p;
  284 
  285                 ifv->ifv_msw = &vlan_ether_multisw;
  286                 ifv->ifv_encaplen = ETHER_VLAN_ENCAP_LEN;
  287                 ifv->ifv_mintu = ETHERMIN;
  288 
  289                 /*
  290                  * If the parent supports the VLAN_MTU capability,
  291                  * i.e. can Tx/Rx larger than ETHER_MAX_LEN frames,
  292                  * enable it.
  293                  */
  294                 if (ec->ec_nvlans++ == 0 &&
  295                     (ec->ec_capabilities & ETHERCAP_VLAN_MTU) != 0) {
  296                         /*
  297                          * Enable Tx/Rx of VLAN-sized frames.
  298                          */
  299                         ec->ec_capenable |= ETHERCAP_VLAN_MTU;
  300                         if (p->if_flags & IFF_UP) {
  301                                 struct ifreq ifr;
  302 
  303                                 ifr.ifr_flags = p->if_flags;
  304                                 error = (*p->if_ioctl)(p, SIOCSIFFLAGS,
  305                                     (caddr_t) &ifr);
  306                                 if (error) {
  307                                         if (ec->ec_nvlans-- == 1)
  308                                                 ec->ec_capenable &=
  309                                                     ~ETHERCAP_VLAN_MTU;
  310                                         return (error);
  311                                 }
  312                         }
  313                         ifv->ifv_mtufudge = 0;
  314                 } else if ((ec->ec_capabilities & ETHERCAP_VLAN_MTU) == 0) {
  315                         /*
  316                          * Fudge the MTU by the encapsulation size.  This
  317                          * makes us incompatible with strictly compliant
  318                          * 802.1Q implementations, but allows us to use
  319                          * the feature with other NetBSD implementations,
  320                          * which might still be useful.
  321                          */
  322                         ifv->ifv_mtufudge = ifv->ifv_encaplen;
  323                 }
  324 
  325                 /*
  326                  * If the parent interface can do hardware-assisted
  327                  * VLAN encapsulation, then propagate its hardware-
  328                  * assisted checksumming flags.
  329                  */
  330                 if (ec->ec_capabilities & ETHERCAP_VLAN_HWTAGGING)
  331                         ifp->if_capabilities = p->if_capabilities &
  332                             (IFCAP_CSUM_IPv4_Tx|IFCAP_CSUM_IPv4_Rx|
  333                              IFCAP_CSUM_TCPv4_Tx|IFCAP_CSUM_TCPv4_Rx|
  334                              IFCAP_CSUM_UDPv4_Tx|IFCAP_CSUM_UDPv4_Rx|
  335                              IFCAP_CSUM_TCPv6_Tx|IFCAP_CSUM_TCPv6_Rx|
  336                              IFCAP_CSUM_UDPv6_Tx|IFCAP_CSUM_UDPv6_Rx);
  337 
  338                 /*
  339                  * We inherit the parent's Ethernet address.
  340                  */
  341                 ether_ifattach(ifp, LLADDR(p->if_sadl));
  342                 ifp->if_hdrlen = sizeof(struct ether_vlan_header); /* XXX? */
  343                 break;
  344             }
  345 
  346         default:
  347                 return (EPROTONOSUPPORT);
  348         }
  349 
  350         ifv->ifv_p = p;
  351         ifv->ifv_if.if_mtu = p->if_mtu - ifv->ifv_mtufudge;
  352         ifv->ifv_if.if_flags = p->if_flags &
  353             (IFF_UP | IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
  354 
  355         /*
  356          * Inherit the if_type from the parent.  This allows us
  357          * to participate in bridges of that type.
  358          */
  359         ifv->ifv_if.if_type = p->if_type;
  360 
  361         return (0);
  362 }
  363 
  364 /*
  365  * Unconfigure a VLAN interface.  Must be called at splnet().
  366  */
  367 static void
  368 vlan_unconfig(struct ifnet *ifp)
  369 {
  370         struct ifvlan *ifv = ifp->if_softc;
  371 
  372         if (ifv->ifv_p == NULL)
  373                 return;
  374 
  375         /*
  376          * Since the interface is being unconfigured, we need to empty the
  377          * list of multicast groups that we may have joined while we were
  378          * alive and remove them from the parent's list also.
  379          */
  380         (*ifv->ifv_msw->vmsw_purgemulti)(ifv);
  381 
  382         /* Disconnect from parent. */
  383         switch (ifv->ifv_p->if_type) {
  384         case IFT_ETHER:
  385             {
  386                 struct ethercom *ec = (void *) ifv->ifv_p;
  387 
  388                 if (ec->ec_nvlans-- == 1) {
  389                         /*
  390                          * Disable Tx/Rx of VLAN-sized frames.
  391                          */
  392                         ec->ec_capenable &= ~ETHERCAP_VLAN_MTU;
  393                         if (ifv->ifv_p->if_flags & IFF_UP) {
  394                                 struct ifreq ifr;
  395 
  396                                 ifr.ifr_flags = ifv->ifv_p->if_flags;
  397                                 (void) (*ifv->ifv_p->if_ioctl)(ifv->ifv_p,
  398                                     SIOCSIFFLAGS, (caddr_t) &ifr);
  399                         }
  400                 }
  401 
  402                 ether_ifdetach(ifp);
  403                 vlan_reset_linkname(ifp);
  404                 break;
  405             }
  406 
  407 #ifdef DIAGNOSTIC
  408         default:
  409                 panic("vlan_unconfig: impossible");
  410 #endif
  411         }
  412 
  413         ifv->ifv_p = NULL;
  414         ifv->ifv_if.if_mtu = 0;
  415         ifv->ifv_flags = 0;
  416 
  417         if_down(ifp);
  418         ifp->if_flags &= ~(IFF_UP|IFF_RUNNING);
  419         ifp->if_capabilities = 0;
  420 }
  421 
  422 /*
  423  * Called when a parent interface is detaching; destroy any VLAN
  424  * configuration for the parent interface.
  425  */
  426 void
  427 vlan_ifdetach(struct ifnet *p)
  428 {
  429         struct ifvlan *ifv;
  430         int s;
  431 
  432         s = splnet();
  433 
  434         for (ifv = LIST_FIRST(&ifv_list); ifv != NULL;
  435              ifv = LIST_NEXT(ifv, ifv_list)) {
  436                 if (ifv->ifv_p == p)
  437                         vlan_unconfig(&ifv->ifv_if);
  438         }
  439 
  440         splx(s);
  441 }
  442 
  443 static int
  444 vlan_set_promisc(struct ifnet *ifp)
  445 {
  446         struct ifvlan *ifv = ifp->if_softc;
  447         int error = 0;
  448 
  449         if ((ifp->if_flags & IFF_PROMISC) != 0) {
  450                 if ((ifv->ifv_flags & IFVF_PROMISC) == 0) {
  451                         error = ifpromisc(ifv->ifv_p, 1);
  452                         if (error == 0)
  453                                 ifv->ifv_flags |= IFVF_PROMISC;
  454                 }
  455         } else {
  456                 if ((ifv->ifv_flags & IFVF_PROMISC) != 0) {
  457                         error = ifpromisc(ifv->ifv_p, 0);
  458                         if (error == 0)
  459                                 ifv->ifv_flags &= ~IFVF_PROMISC;
  460                 }
  461         }
  462 
  463         return (error);
  464 }
  465 
  466 static int
  467 vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
  468 {
  469         struct lwp *l = curlwp; /* XXX */
  470         struct ifvlan *ifv = ifp->if_softc;
  471         struct ifaddr *ifa = (struct ifaddr *) data;
  472         struct ifreq *ifr = (struct ifreq *) data;
  473         struct ifnet *pr;
  474         struct vlanreq vlr;
  475         struct sockaddr *sa;
  476         int s, error = 0;
  477 
  478         s = splnet();
  479 
  480         switch (cmd) {
  481         case SIOCSIFADDR:
  482                 if (ifv->ifv_p != NULL) {
  483                         ifp->if_flags |= IFF_UP;
  484 
  485                         switch (ifa->ifa_addr->sa_family) {
  486 #ifdef INET
  487                         case AF_INET:
  488                                 arp_ifinit(ifp, ifa);
  489                                 break;
  490 #endif
  491                         default:
  492                                 break;
  493                         }
  494                 } else {
  495                         error = EINVAL;
  496                 }
  497                 break;
  498 
  499         case SIOCGIFADDR:
  500                 sa = (struct sockaddr *)&ifr->ifr_data;
  501                 memcpy(sa->sa_data, LLADDR(ifp->if_sadl), ifp->if_addrlen);
  502                 break;
  503 
  504         case SIOCSIFMTU:
  505                 if (ifv->ifv_p != NULL) {
  506                         if (ifr->ifr_mtu >
  507                              (ifv->ifv_p->if_mtu - ifv->ifv_mtufudge) ||
  508                             ifr->ifr_mtu <
  509                              (ifv->ifv_mintu - ifv->ifv_mtufudge))
  510                                 error = EINVAL;
  511                         else
  512                                 ifp->if_mtu = ifr->ifr_mtu;
  513                 } else
  514                         error = EINVAL;
  515                 break;
  516 
  517         case SIOCSETVLAN:
  518                 if ((error = kauth_authorize_network(l->l_cred,
  519                     KAUTH_NETWORK_INTERFACE,
  520                     KAUTH_REQ_NETWORK_INTERFACE_SETPRIV, ifp, (void *)cmd,
  521                     NULL)) != 0)
  522                         break;
  523                 if ((error = copyin(ifr->ifr_data, &vlr, sizeof(vlr))) != 0)
  524                         break;
  525                 if (vlr.vlr_parent[0] == '\0') {
  526                         vlan_unconfig(ifp);
  527                         break;
  528                 }
  529                 if (vlr.vlr_tag != EVL_VLANOFTAG(vlr.vlr_tag)) {
  530                         error = EINVAL;          /* check for valid tag */
  531                         break;
  532                 }
  533                 if ((pr = ifunit(vlr.vlr_parent)) == 0) {
  534                         error = ENOENT;
  535                         break;
  536                 }
  537                 if ((error = vlan_config(ifv, pr)) != 0)
  538                         break;
  539                 ifv->ifv_tag = vlr.vlr_tag;
  540                 ifp->if_flags |= IFF_RUNNING;
  541 
  542                 /* Update promiscuous mode, if necessary. */
  543                 vlan_set_promisc(ifp);
  544                 break;
  545 
  546         case SIOCGETVLAN:
  547                 memset(&vlr, 0, sizeof(vlr));
  548                 if (ifv->ifv_p != NULL) {
  549                         snprintf(vlr.vlr_parent, sizeof(vlr.vlr_parent), "%s",
  550                             ifv->ifv_p->if_xname);
  551                         vlr.vlr_tag = ifv->ifv_tag;
  552                 }
  553                 error = copyout(&vlr, ifr->ifr_data, sizeof(vlr));
  554                 break;
  555 
  556         case SIOCSIFFLAGS:
  557                 /*
  558                  * For promiscuous mode, we enable promiscuous mode on
  559                  * the parent if we need promiscuous on the VLAN interface.
  560                  */
  561                 if (ifv->ifv_p != NULL)
  562                         error = vlan_set_promisc(ifp);
  563                 break;
  564 
  565         case SIOCADDMULTI:
  566                 error = (ifv->ifv_p != NULL) ?
  567                     (*ifv->ifv_msw->vmsw_addmulti)(ifv, ifr) : EINVAL;
  568                 break;
  569 
  570         case SIOCDELMULTI:
  571                 error = (ifv->ifv_p != NULL) ?
  572                     (*ifv->ifv_msw->vmsw_delmulti)(ifv, ifr) : EINVAL;
  573                 break;
  574 
  575         default:
  576                 error = EINVAL;
  577         }
  578 
  579         splx(s);
  580 
  581         return (error);
  582 }
  583 
  584 static int
  585 vlan_ether_addmulti(struct ifvlan *ifv, struct ifreq *ifr)
  586 {
  587         struct vlan_mc_entry *mc;
  588         u_int8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN];
  589         int error;
  590 
  591         if (ifr->ifr_addr.sa_len > sizeof(struct sockaddr_storage))
  592                 return (EINVAL);
  593 
  594         error = ether_addmulti(ifr, &ifv->ifv_ec);
  595         if (error != ENETRESET)
  596                 return (error);
  597 
  598         /*
  599          * This is new multicast address.  We have to tell parent
  600          * about it.  Also, remember this multicast address so that
  601          * we can delete them on unconfigure.
  602          */
  603         MALLOC(mc, struct vlan_mc_entry *, sizeof(struct vlan_mc_entry),
  604             M_DEVBUF, M_NOWAIT);
  605         if (mc == NULL) {
  606                 error = ENOMEM;
  607                 goto alloc_failed;
  608         }
  609 
  610         /*
  611          * As ether_addmulti() returns ENETRESET, following two
  612          * statement shouldn't fail.
  613          */
  614         (void)ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi);
  615         ETHER_LOOKUP_MULTI(addrlo, addrhi, &ifv->ifv_ec, mc->mc_enm);
  616         memcpy(&mc->mc_addr, &ifr->ifr_addr, ifr->ifr_addr.sa_len);
  617         LIST_INSERT_HEAD(&ifv->ifv_mc_listhead, mc, mc_entries);
  618 
  619         error = (*ifv->ifv_p->if_ioctl)(ifv->ifv_p, SIOCADDMULTI,
  620             (caddr_t)ifr);
  621         if (error != 0)
  622                 goto ioctl_failed;
  623         return (error);
  624 
  625  ioctl_failed:
  626         LIST_REMOVE(mc, mc_entries);
  627         FREE(mc, M_DEVBUF);
  628  alloc_failed:
  629         (void)ether_delmulti(ifr, &ifv->ifv_ec);
  630         return (error);
  631 }
  632 
  633 static int
  634 vlan_ether_delmulti(struct ifvlan *ifv, struct ifreq *ifr)
  635 {
  636         struct ether_multi *enm;
  637         struct vlan_mc_entry *mc;
  638         u_int8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN];
  639         int error;
  640 
  641         /*
  642          * Find a key to lookup vlan_mc_entry.  We have to do this
  643          * before calling ether_delmulti for obvious reason.
  644          */
  645         if ((error = ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi)) != 0)
  646                 return (error);
  647         ETHER_LOOKUP_MULTI(addrlo, addrhi, &ifv->ifv_ec, enm);
  648 
  649         error = ether_delmulti(ifr, &ifv->ifv_ec);
  650         if (error != ENETRESET)
  651                 return (error);
  652 
  653         /* We no longer use this multicast address.  Tell parent so. */
  654         error = (*ifv->ifv_p->if_ioctl)(ifv->ifv_p, SIOCDELMULTI,
  655             (caddr_t)ifr);
  656         if (error == 0) {
  657                 /* And forget about this address. */
  658                 for (mc = LIST_FIRST(&ifv->ifv_mc_listhead); mc != NULL;
  659                     mc = LIST_NEXT(mc, mc_entries)) {
  660                         if (mc->mc_enm == enm) {
  661                                 LIST_REMOVE(mc, mc_entries);
  662                                 FREE(mc, M_DEVBUF);
  663                                 break;
  664                         }
  665                 }
  666                 KASSERT(mc != NULL);
  667         } else
  668                 (void)ether_addmulti(ifr, &ifv->ifv_ec);
  669         return (error);
  670 }
  671 
  672 /*
  673  * Delete any multicast address we have asked to add from parent
  674  * interface.  Called when the vlan is being unconfigured.
  675  */
  676 static void
  677 vlan_ether_purgemulti(struct ifvlan *ifv)
  678 {
  679         struct ifnet *ifp = ifv->ifv_p;         /* Parent. */
  680         struct vlan_mc_entry *mc;
  681         union {
  682                 struct ifreq ifreq;
  683                 struct {
  684                         char ifr_name[IFNAMSIZ];
  685                         struct sockaddr_storage ifr_ss;
  686                 } ifreq_storage;
  687         } ifreq;
  688         struct ifreq *ifr = &ifreq.ifreq;
  689 
  690         memcpy(ifr->ifr_name, ifp->if_xname, IFNAMSIZ);
  691         while ((mc = LIST_FIRST(&ifv->ifv_mc_listhead)) != NULL) {
  692                 memcpy(&ifr->ifr_addr, &mc->mc_addr, mc->mc_addr.ss_len);
  693                 (void)(*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)ifr);
  694                 LIST_REMOVE(mc, mc_entries);
  695                 FREE(mc, M_DEVBUF);
  696         }
  697 }
  698 
  699 static void
  700 vlan_start(struct ifnet *ifp)
  701 {
  702         struct ifvlan *ifv = ifp->if_softc;
  703         struct ifnet *p = ifv->ifv_p;
  704         struct ethercom *ec = (void *) ifv->ifv_p;
  705         struct mbuf *m;
  706         int error;
  707         ALTQ_DECL(struct altq_pktattr pktattr;)
  708 
  709         ifp->if_flags |= IFF_OACTIVE;
  710 
  711         for (;;) {
  712                 IFQ_DEQUEUE(&ifp->if_snd, m);
  713                 if (m == NULL)
  714                         break;
  715 
  716 #ifdef ALTQ
  717                 /*
  718                  * If ALTQ is enabled on the parent interface, do
  719                  * classification; the queueing discipline might
  720                  * not require classification, but might require
  721                  * the address family/header pointer in the pktattr.
  722                  */
  723                 if (ALTQ_IS_ENABLED(&p->if_snd)) {
  724                         switch (p->if_type) {
  725                         case IFT_ETHER:
  726                                 altq_etherclassify(&p->if_snd, m, &pktattr);
  727                                 break;
  728 #ifdef DIAGNOSTIC
  729                         default:
  730                                 panic("vlan_start: impossible (altq)");
  731 #endif
  732                         }
  733                 }
  734 #endif /* ALTQ */
  735 
  736 #if NBPFILTER > 0
  737                 if (ifp->if_bpf)
  738                         bpf_mtap(ifp->if_bpf, m);
  739 #endif
  740                 /*
  741                  * If the parent can insert the tag itself, just mark
  742                  * the tag in the mbuf header.
  743                  */
  744                 if (ec->ec_capabilities & ETHERCAP_VLAN_HWTAGGING) {
  745                         struct m_tag *mtag;
  746 
  747                         mtag = m_tag_get(PACKET_TAG_VLAN, sizeof(u_int),
  748                             M_NOWAIT);
  749                         if (mtag == NULL) {
  750                                 ifp->if_oerrors++;
  751                                 m_freem(m);
  752                                 continue;
  753                         }
  754                         *(u_int *)(mtag + 1) = ifv->ifv_tag;
  755                         m_tag_prepend(m, mtag);
  756                 } else {
  757                         /*
  758                          * insert the tag ourselves
  759                          */
  760                         M_PREPEND(m, ifv->ifv_encaplen, M_DONTWAIT);
  761                         if (m == NULL) {
  762                                 printf("%s: unable to prepend encap header",
  763                                     ifv->ifv_p->if_xname);
  764                                 ifp->if_oerrors++;
  765                                 continue;
  766                         }
  767 
  768                         switch (p->if_type) {
  769                         case IFT_ETHER:
  770                             {
  771                                 struct ether_vlan_header *evl;
  772 
  773                                 if (m->m_len < sizeof(struct ether_vlan_header))
  774                                         m = m_pullup(m,
  775                                             sizeof(struct ether_vlan_header));
  776                                 if (m == NULL) {
  777                                         printf("%s: unable to pullup encap "
  778                                             "header", ifv->ifv_p->if_xname);
  779                                         ifp->if_oerrors++;
  780                                         continue;
  781                                 }
  782 
  783                                 /*
  784                                  * Transform the Ethernet header into an
  785                                  * Ethernet header with 802.1Q encapsulation.
  786                                  */
  787                                 memmove(mtod(m, caddr_t),
  788                                     mtod(m, caddr_t) + ifv->ifv_encaplen,
  789                                     sizeof(struct ether_header));
  790                                 evl = mtod(m, struct ether_vlan_header *);
  791                                 evl->evl_proto = evl->evl_encap_proto;
  792                                 evl->evl_encap_proto = htons(ETHERTYPE_VLAN);
  793                                 evl->evl_tag = htons(ifv->ifv_tag);
  794 
  795                                 /*
  796                                  * To cater for VLAN-aware layer 2 ethernet
  797                                  * switches which may need to strip the tag
  798                                  * before forwarding the packet, make sure
  799                                  * the packet+tag is at least 68 bytes long.
  800                                  * This is necessary because our parent will
  801                                  * only pad to 64 bytes (ETHER_MIN_LEN) and
  802                                  * some switches will not pad by themselves
  803                                  * after deleting a tag.
  804                                  */
  805                                 if (m->m_pkthdr.len <
  806                                     (ETHER_MIN_LEN + ETHER_VLAN_ENCAP_LEN)) {
  807                                         m_copyback(m, m->m_pkthdr.len,
  808                                             (ETHER_MIN_LEN +
  809                                              ETHER_VLAN_ENCAP_LEN) -
  810                                              m->m_pkthdr.len,
  811                                             vlan_zero_pad_buff);
  812                                 }
  813                                 break;
  814                             }
  815 
  816 #ifdef DIAGNOSTIC
  817                         default:
  818                                 panic("vlan_start: impossible");
  819 #endif
  820                         }
  821                 }
  822 
  823                 /*
  824                  * Send it, precisely as the parent's output routine
  825                  * would have.  We are already running at splnet.
  826                  */
  827                 IFQ_ENQUEUE(&p->if_snd, m, &pktattr, error);
  828                 if (error) {
  829                         /* mbuf is already freed */
  830                         ifp->if_oerrors++;
  831                         continue;
  832                 }
  833 
  834                 ifp->if_opackets++;
  835                 if ((p->if_flags & (IFF_RUNNING|IFF_OACTIVE)) == IFF_RUNNING)
  836                         (*p->if_start)(p);
  837         }
  838 
  839         ifp->if_flags &= ~IFF_OACTIVE;
  840 }
  841 
  842 /*
  843  * Given an Ethernet frame, find a valid vlan interface corresponding to the
  844  * given source interface and tag, then run the real packet through the
  845  * parent's input routine.
  846  */
  847 void
  848 vlan_input(struct ifnet *ifp, struct mbuf *m)
  849 {
  850         struct ifvlan *ifv;
  851         u_int tag;
  852         struct m_tag *mtag;
  853 
  854         mtag = m_tag_find(m, PACKET_TAG_VLAN, NULL);
  855         if (mtag != NULL) {
  856                 /* m contains a normal ethernet frame, the tag is in mtag */
  857                 tag = EVL_VLANOFTAG(*(u_int *)(mtag + 1));
  858                 m_tag_delete(m, mtag);
  859         } else {
  860                 switch (ifp->if_type) {
  861                 case IFT_ETHER:
  862                     {
  863                         struct ether_vlan_header *evl;
  864 
  865                         if (m->m_len < sizeof(struct ether_vlan_header) &&
  866                             (m = m_pullup(m,
  867                              sizeof(struct ether_vlan_header))) == NULL) {
  868                                 printf("%s: no memory for VLAN header, "
  869                                     "dropping packet.\n", ifp->if_xname);
  870                                 return;
  871                         }
  872                         evl = mtod(m, struct ether_vlan_header *);
  873                         KASSERT(ntohs(evl->evl_encap_proto) == ETHERTYPE_VLAN);
  874 
  875                         tag = EVL_VLANOFTAG(ntohs(evl->evl_tag));
  876 
  877                         /*
  878                          * Restore the original ethertype.  We'll remove
  879                          * the encapsulation after we've found the vlan
  880                          * interface corresponding to the tag.
  881                          */
  882                         evl->evl_encap_proto = evl->evl_proto;
  883                         break;
  884                     }
  885 
  886                 default:
  887                         tag = (u_int) -1;       /* XXX GCC */
  888 #ifdef DIAGNOSTIC
  889                         panic("vlan_input: impossible");
  890 #endif
  891                 }
  892         }
  893 
  894         for (ifv = LIST_FIRST(&ifv_list); ifv != NULL;
  895             ifv = LIST_NEXT(ifv, ifv_list))
  896                 if (ifp == ifv->ifv_p && tag == ifv->ifv_tag)
  897                         break;
  898 
  899         if (ifv == NULL ||
  900             (ifv->ifv_if.if_flags & (IFF_UP|IFF_RUNNING)) !=
  901              (IFF_UP|IFF_RUNNING)) {
  902                 m_freem(m);
  903                 ifp->if_noproto++;
  904                 return;
  905         }
  906 
  907         /*
  908          * Now, remove the encapsulation header.  The original
  909          * header has already been fixed up above.
  910          */
  911         if (mtag == NULL) {
  912                 memmove(mtod(m, caddr_t) + ifv->ifv_encaplen,
  913                     mtod(m, caddr_t), sizeof(struct ether_header));
  914                 m_adj(m, ifv->ifv_encaplen);
  915         }
  916 
  917         m->m_pkthdr.rcvif = &ifv->ifv_if;
  918         ifv->ifv_if.if_ipackets++;
  919 
  920 #if NBPFILTER > 0
  921         if (ifv->ifv_if.if_bpf)
  922                 bpf_mtap(ifv->ifv_if.if_bpf, m);
  923 #endif
  924 
  925         /* Pass it back through the parent's input routine. */
  926         (*ifp->if_input)(&ifv->ifv_if, m);
  927 }

Cache object: 1983bc5bf61a526289426fde284b05be


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