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 /*-
    2  * Copyright 1998 Massachusetts Institute of Technology
    3  *
    4  * Permission to use, copy, modify, and distribute this software and
    5  * its documentation for any purpose and without fee is hereby
    6  * granted, provided that both the above copyright notice and this
    7  * permission notice appear in all copies, that both the above
    8  * copyright notice and this permission notice appear in all
    9  * supporting documentation, and that the name of M.I.T. not be used
   10  * in advertising or publicity pertaining to distribution of the
   11  * software without specific, written prior permission.  M.I.T. makes
   12  * no representations about the suitability of this software for any
   13  * purpose.  It is provided "as is" without express or implied
   14  * warranty.
   15  * 
   16  * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''.  M.I.T. DISCLAIMS
   17  * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
   18  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
   19  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
   20  * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   21  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   22  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   23  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   25  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   26  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   27  * SUCH DAMAGE.
   28  *
   29  * $FreeBSD: src/sys/net/if_vlan.c,v 1.73.2.2 2005/01/31 23:26:23 imp Exp $
   30  */
   31 
   32 /*
   33  * if_vlan.c - pseudo-device driver for IEEE 802.1Q virtual LANs.
   34  * Might be extended some day to also handle IEEE 802.1p priority
   35  * tagging.  This is sort of sneaky in the implementation, since
   36  * we need to pretend to be enough of an Ethernet implementation
   37  * to make arp work.  The way we do this is by telling everyone
   38  * that we are an Ethernet, and then catch the packets that
   39  * ether_output() left on our output queue when it calls
   40  * if_start(), rewrite them for use by the real outgoing interface,
   41  * and ask it to send them.
   42  */
   43 
   44 #include "opt_inet.h"
   45 
   46 #include <sys/param.h>
   47 #include <sys/kernel.h>
   48 #include <sys/malloc.h>
   49 #include <sys/mbuf.h>
   50 #include <sys/module.h>
   51 #include <sys/queue.h>
   52 #include <sys/socket.h>
   53 #include <sys/sockio.h>
   54 #include <sys/sysctl.h>
   55 #include <sys/systm.h>
   56 
   57 #include <net/bpf.h>
   58 #include <net/ethernet.h>
   59 #include <net/if.h>
   60 #include <net/if_clone.h>
   61 #include <net/if_arp.h>
   62 #include <net/if_dl.h>
   63 #include <net/if_types.h>
   64 #include <net/if_vlan_var.h>
   65 #include <net/route.h>
   66 
   67 #ifdef INET
   68 #include <netinet/in.h>
   69 #include <netinet/if_ether.h>
   70 #endif
   71 
   72 #define VLANNAME        "vlan"
   73 
   74 struct vlan_mc_entry {
   75         struct ether_addr               mc_addr;
   76         SLIST_ENTRY(vlan_mc_entry)      mc_entries;
   77 };
   78 
   79 struct  ifvlan {
   80         struct  arpcom ifv_ac;  /* make this an interface */
   81         struct  ifnet *ifv_p;   /* parent inteface of this vlan */
   82         struct  ifv_linkmib {
   83                 int     ifvm_parent;
   84                 int     ifvm_encaplen;  /* encapsulation length */
   85                 int     ifvm_mtufudge;  /* MTU fudged by this much */
   86                 int     ifvm_mintu;     /* min transmission unit */
   87                 u_int16_t ifvm_proto; /* encapsulation ethertype */
   88                 u_int16_t ifvm_tag; /* tag to apply on packets leaving if */
   89         }       ifv_mib;
   90         SLIST_HEAD(__vlan_mchead, vlan_mc_entry)        vlan_mc_listhead;
   91         LIST_ENTRY(ifvlan) ifv_list;
   92         int     ifv_flags;
   93 };
   94 #define ifv_if  ifv_ac.ac_if
   95 #define ifv_tag ifv_mib.ifvm_tag
   96 #define ifv_encaplen    ifv_mib.ifvm_encaplen
   97 #define ifv_mtufudge    ifv_mib.ifvm_mtufudge
   98 #define ifv_mintu       ifv_mib.ifvm_mintu
   99 
  100 #define IFVF_PROMISC    0x01            /* promiscuous mode enabled */
  101 
  102 SYSCTL_DECL(_net_link);
  103 SYSCTL_NODE(_net_link, IFT_L2VLAN, vlan, CTLFLAG_RW, 0, "IEEE 802.1Q VLAN");
  104 SYSCTL_NODE(_net_link_vlan, PF_LINK, link, CTLFLAG_RW, 0, "for consistency");
  105 
  106 static MALLOC_DEFINE(M_VLAN, VLANNAME, "802.1Q Virtual LAN Interface");
  107 static LIST_HEAD(, ifvlan) ifv_list;
  108 
  109 /*
  110  * Locking: one lock is used to guard both the ifv_list and modification
  111  * to vlan data structures.  We are rather conservative here; probably
  112  * more than necessary.
  113  */
  114 static struct mtx ifv_mtx;
  115 #define VLAN_LOCK_INIT()        mtx_init(&ifv_mtx, VLANNAME, NULL, MTX_DEF)
  116 #define VLAN_LOCK_DESTROY()     mtx_destroy(&ifv_mtx)
  117 #define VLAN_LOCK_ASSERT()      mtx_assert(&ifv_mtx, MA_OWNED)
  118 #define VLAN_LOCK()     mtx_lock(&ifv_mtx)
  119 #define VLAN_UNLOCK()   mtx_unlock(&ifv_mtx)
  120 
  121 static  void vlan_start(struct ifnet *ifp);
  122 static  void vlan_ifinit(void *foo);
  123 static  void vlan_input(struct ifnet *ifp, struct mbuf *m);
  124 static  int vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr);
  125 static  int vlan_setmulti(struct ifnet *ifp);
  126 static  int vlan_unconfig(struct ifnet *ifp);
  127 static  int vlan_config(struct ifvlan *ifv, struct ifnet *p);
  128 static  void vlan_link_state(struct ifnet *ifp, int link);
  129 static  int vlan_set_promisc(struct ifnet *ifp);
  130 
  131 static  struct ifnet *vlan_clone_match_ethertag(struct if_clone *,
  132     const char *, int *);
  133 static  int vlan_clone_match(struct if_clone *, const char *);
  134 static  int vlan_clone_create(struct if_clone *, char *, size_t);
  135 static  int vlan_clone_destroy(struct if_clone *, struct ifnet *);
  136 
  137 static  struct if_clone vlan_cloner = IFC_CLONE_INITIALIZER(VLANNAME, NULL,
  138     IF_MAXUNIT, NULL, vlan_clone_match, vlan_clone_create, vlan_clone_destroy);
  139 
  140 /*
  141  * Program our multicast filter. What we're actually doing is
  142  * programming the multicast filter of the parent. This has the
  143  * side effect of causing the parent interface to receive multicast
  144  * traffic that it doesn't really want, which ends up being discarded
  145  * later by the upper protocol layers. Unfortunately, there's no way
  146  * to avoid this: there really is only one physical interface.
  147  *
  148  * XXX: There is a possible race here if more than one thread is
  149  *      modifying the multicast state of the vlan interface at the same time.
  150  */
  151 static int
  152 vlan_setmulti(struct ifnet *ifp)
  153 {
  154         struct ifnet            *ifp_p;
  155         struct ifmultiaddr      *ifma, *rifma = NULL;
  156         struct ifvlan           *sc;
  157         struct vlan_mc_entry    *mc = NULL;
  158         struct sockaddr_dl      sdl;
  159         int                     error;
  160 
  161         /*VLAN_LOCK_ASSERT();*/
  162 
  163         /* Find the parent. */
  164         sc = ifp->if_softc;
  165         ifp_p = sc->ifv_p;
  166 
  167         /*
  168          * If we don't have a parent, just remember the membership for
  169          * when we do.
  170          */
  171         if (ifp_p == NULL)
  172                 return (0);
  173 
  174         bzero((char *)&sdl, sizeof(sdl));
  175         sdl.sdl_len = sizeof(sdl);
  176         sdl.sdl_family = AF_LINK;
  177         sdl.sdl_index = ifp_p->if_index;
  178         sdl.sdl_type = IFT_ETHER;
  179         sdl.sdl_alen = ETHER_ADDR_LEN;
  180 
  181         /* First, remove any existing filter entries. */
  182         while (SLIST_FIRST(&sc->vlan_mc_listhead) != NULL) {
  183                 mc = SLIST_FIRST(&sc->vlan_mc_listhead);
  184                 bcopy((char *)&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN);
  185                 error = if_delmulti(ifp_p, (struct sockaddr *)&sdl);
  186                 if (error)
  187                         return (error);
  188                 SLIST_REMOVE_HEAD(&sc->vlan_mc_listhead, mc_entries);
  189                 free(mc, M_VLAN);
  190         }
  191 
  192         /* Now program new ones. */
  193         TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
  194                 if (ifma->ifma_addr->sa_family != AF_LINK)
  195                         continue;
  196                 mc = malloc(sizeof(struct vlan_mc_entry), M_VLAN, M_NOWAIT);
  197                 if (mc == NULL)
  198                         return (ENOMEM);
  199                 bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
  200                     (char *)&mc->mc_addr, ETHER_ADDR_LEN);
  201                 SLIST_INSERT_HEAD(&sc->vlan_mc_listhead, mc, mc_entries);
  202                 bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
  203                     LLADDR(&sdl), ETHER_ADDR_LEN);
  204                 error = if_addmulti(ifp_p, (struct sockaddr *)&sdl, &rifma);
  205                 if (error)
  206                         return (error);
  207         }
  208 
  209         return (0);
  210 }
  211 
  212 /*
  213  * VLAN support can be loaded as a module.  The only place in the
  214  * system that's intimately aware of this is ether_input.  We hook
  215  * into this code through vlan_input_p which is defined there and
  216  * set here.  Noone else in the system should be aware of this so
  217  * we use an explicit reference here.
  218  *
  219  * NB: Noone should ever need to check if vlan_input_p is null or
  220  *     not.  This is because interfaces have a count of the number
  221  *     of active vlans (if_nvlans) and this should never be bumped
  222  *     except by vlan_config--which is in this module so therefore
  223  *     the module must be loaded and vlan_input_p must be non-NULL.
  224  */
  225 extern  void (*vlan_input_p)(struct ifnet *, struct mbuf *);
  226 
  227 /* For MII eyes only... */
  228 extern  void (*vlan_link_state_p)(struct ifnet *, int);
  229 
  230 static int
  231 vlan_modevent(module_t mod, int type, void *data)
  232 {
  233 
  234         switch (type) {
  235         case MOD_LOAD:
  236                 LIST_INIT(&ifv_list);
  237                 VLAN_LOCK_INIT();
  238                 vlan_input_p = vlan_input;
  239                 vlan_link_state_p = vlan_link_state;
  240                 if_clone_attach(&vlan_cloner);
  241                 break;
  242         case MOD_UNLOAD:
  243                 if_clone_detach(&vlan_cloner);
  244                 vlan_input_p = NULL;
  245                 vlan_link_state_p = NULL;
  246                 while (!LIST_EMPTY(&ifv_list))
  247                         vlan_clone_destroy(&vlan_cloner,
  248                             &LIST_FIRST(&ifv_list)->ifv_if);
  249                 VLAN_LOCK_DESTROY();
  250                 break;
  251         default:
  252                 return (EOPNOTSUPP);
  253         }
  254         return (0);
  255 }
  256 
  257 static moduledata_t vlan_mod = {
  258         "if_vlan",
  259         vlan_modevent,
  260         0
  261 };
  262 
  263 DECLARE_MODULE(if_vlan, vlan_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
  264 MODULE_DEPEND(if_vlan, miibus, 1, 1, 1);
  265 
  266 static struct ifnet *
  267 vlan_clone_match_ethertag(struct if_clone *ifc, const char *name, int *tag)
  268 {
  269         const char *cp;
  270         struct ifnet *ifp;
  271         int t = 0;
  272 
  273         /* Check for <etherif>.<vlan> style interface names. */
  274         IFNET_RLOCK();
  275         TAILQ_FOREACH(ifp, &ifnet, if_link) {
  276                 if (ifp->if_type != IFT_ETHER)
  277                         continue;
  278                 if (strncmp(ifp->if_xname, name, strlen(ifp->if_xname)) != 0)
  279                         continue;
  280                 cp = name + strlen(ifp->if_xname);
  281                 if (*cp != '.')
  282                         continue;
  283                 for(; *cp != '\0'; cp++) {
  284                         if (*cp < '' || *cp > '9')
  285                                 continue;
  286                         t = (t * 10) + (*cp - '');
  287                 }
  288                 if (tag != NULL)
  289                         *tag = t;
  290                 break;
  291         }
  292         IFNET_RUNLOCK();
  293 
  294         return (ifp);
  295 }
  296 
  297 static int
  298 vlan_clone_match(struct if_clone *ifc, const char *name)
  299 {
  300         const char *cp;
  301 
  302         if (vlan_clone_match_ethertag(ifc, name, NULL) != NULL)
  303                 return (1);
  304 
  305         if (strncmp(VLANNAME, name, strlen(VLANNAME)) != 0)
  306                 return (0);
  307         for (cp = name + 4; *cp != '\0'; cp++) {
  308                 if (*cp < '' || *cp > '9')
  309                         return (0);
  310         }
  311 
  312         return (1);
  313 }
  314 
  315 static int
  316 vlan_clone_create(struct if_clone *ifc, char *name, size_t len)
  317 {
  318         char *dp;
  319         int wildcard;
  320         int unit;
  321         int error;
  322         int tag;
  323         int ethertag;
  324         struct ifvlan *ifv;
  325         struct ifnet *ifp;
  326         struct ifnet *p;
  327 
  328         if ((p = vlan_clone_match_ethertag(ifc, name, &tag)) != NULL) {
  329                 ethertag = 1;
  330                 unit = -1;
  331                 wildcard = 0;
  332 
  333                 /*
  334                  * Don't let the caller set up a VLAN tag with
  335                  * anything except VLID bits.
  336                  */
  337                 if (tag & ~EVL_VLID_MASK)
  338                         return (EINVAL);
  339         } else {
  340                 ethertag = 0;
  341 
  342                 error = ifc_name2unit(name, &unit);
  343                 if (error != 0)
  344                         return (error);
  345 
  346                 wildcard = (unit < 0);
  347         }
  348 
  349         error = ifc_alloc_unit(ifc, &unit);
  350         if (error != 0)
  351                 return (error);
  352 
  353         /* In the wildcard case, we need to update the name. */
  354         if (wildcard) {
  355                 for (dp = name; *dp != '\0'; dp++);
  356                 if (snprintf(dp, len - (dp-name), "%d", unit) >
  357                     len - (dp-name) - 1) {
  358                         panic("%s: interface name too long", __func__);
  359                 }
  360         }
  361 
  362         ifv = malloc(sizeof(struct ifvlan), M_VLAN, M_WAITOK | M_ZERO);
  363         ifp = &ifv->ifv_if;
  364         SLIST_INIT(&ifv->vlan_mc_listhead);
  365 
  366         ifp->if_softc = ifv;
  367         /*
  368          * Set the name manually rather than using if_initname because
  369          * we don't conform to the default naming convention for interfaces.
  370          */
  371         strlcpy(ifp->if_xname, name, IFNAMSIZ);
  372         ifp->if_dname = ifc->ifc_name;
  373         ifp->if_dunit = unit;
  374         /* NB: flags are not set here */
  375         ifp->if_linkmib = &ifv->ifv_mib;
  376         ifp->if_linkmiblen = sizeof(ifv->ifv_mib);
  377         /* NB: mtu is not set here */
  378 
  379         ifp->if_init = vlan_ifinit;
  380         ifp->if_start = vlan_start;
  381         ifp->if_ioctl = vlan_ioctl;
  382         ifp->if_snd.ifq_maxlen = ifqmaxlen;
  383         ether_ifattach(ifp, ifv->ifv_ac.ac_enaddr);
  384         /* Now undo some of the damage... */
  385         ifp->if_baudrate = 0;
  386         ifp->if_type = IFT_L2VLAN;
  387         ifp->if_hdrlen = ETHER_VLAN_ENCAP_LEN;
  388 
  389         VLAN_LOCK();
  390         LIST_INSERT_HEAD(&ifv_list, ifv, ifv_list);
  391         VLAN_UNLOCK();
  392 
  393         if (ethertag) {
  394                 VLAN_LOCK();
  395                 error = vlan_config(ifv, p);
  396                 if (error != 0) {
  397                         /*
  398                          * Since we've partialy failed, we need to back
  399                          * out all the way, otherwise userland could get
  400                          * confused.  Thus, we destroy the interface.
  401                          */
  402                         LIST_REMOVE(ifv, ifv_list);
  403                         vlan_unconfig(ifp);
  404                         VLAN_UNLOCK();
  405                         ether_ifdetach(ifp);
  406                         free(ifv, M_VLAN);
  407 
  408                         return (error);
  409                 }
  410                 ifv->ifv_tag = tag;
  411                 ifp->if_flags |= IFF_RUNNING;
  412                 VLAN_UNLOCK();
  413 
  414                 /* Update promiscuous mode, if necessary. */
  415                 vlan_set_promisc(ifp);
  416         }
  417 
  418         return (0);
  419 }
  420 
  421 static int
  422 vlan_clone_destroy(struct if_clone *ifc, struct ifnet *ifp)
  423 {
  424         int unit;
  425         struct ifvlan *ifv = ifp->if_softc;
  426 
  427         unit = ifp->if_dunit;
  428 
  429         VLAN_LOCK();
  430         LIST_REMOVE(ifv, ifv_list);
  431         vlan_unconfig(ifp);
  432         VLAN_UNLOCK();
  433 
  434         ether_ifdetach(ifp);
  435 
  436         free(ifv, M_VLAN);
  437 
  438         ifc_free_unit(ifc, unit);
  439 
  440         return (0);
  441 }
  442 
  443 /*
  444  * The ifp->if_init entry point for vlan(4) is a no-op.
  445  */
  446 static void
  447 vlan_ifinit(void *foo)
  448 {
  449 
  450 }
  451 
  452 static void
  453 vlan_start(struct ifnet *ifp)
  454 {
  455         struct ifvlan *ifv;
  456         struct ifnet *p;
  457         struct ether_vlan_header *evl;
  458         struct mbuf *m;
  459         int error;
  460 
  461         ifv = ifp->if_softc;
  462         p = ifv->ifv_p;
  463 
  464         ifp->if_flags |= IFF_OACTIVE;
  465         for (;;) {
  466                 IF_DEQUEUE(&ifp->if_snd, m);
  467                 if (m == 0)
  468                         break;
  469                 BPF_MTAP(ifp, m);
  470 
  471                 /*
  472                  * Do not run parent's if_start() if the parent is not up,
  473                  * or parent's driver will cause a system crash.
  474                  */
  475                 if ((p->if_flags & (IFF_UP | IFF_RUNNING)) !=
  476                                         (IFF_UP | IFF_RUNNING)) {
  477                         m_freem(m);
  478                         ifp->if_collisions++;
  479                         continue;
  480                 }
  481 
  482                 /*
  483                  * If underlying interface can do VLAN tag insertion itself,
  484                  * just pass the packet along. However, we need some way to
  485                  * tell the interface where the packet came from so that it
  486                  * knows how to find the VLAN tag to use, so we attach a
  487                  * packet tag that holds it.
  488                  */
  489                 if (p->if_capenable & IFCAP_VLAN_HWTAGGING) {
  490                         struct m_tag *mtag = m_tag_alloc(MTAG_VLAN,
  491                                                          MTAG_VLAN_TAG,
  492                                                          sizeof(u_int),
  493                                                          M_NOWAIT);
  494                         if (mtag == NULL) {
  495                                 ifp->if_oerrors++;
  496                                 m_freem(m);
  497                                 continue;
  498                         }
  499                         *(u_int*)(mtag + 1) = ifv->ifv_tag;
  500                         m_tag_prepend(m, mtag);
  501                 } else {
  502                         M_PREPEND(m, ifv->ifv_encaplen, M_DONTWAIT);
  503                         if (m == NULL) {
  504                                 if_printf(ifp,
  505                                     "unable to prepend VLAN header\n");
  506                                 ifp->if_oerrors++;
  507                                 continue;
  508                         }
  509                         /* M_PREPEND takes care of m_len, m_pkthdr.len for us */
  510 
  511                         if (m->m_len < sizeof(*evl)) {
  512                                 m = m_pullup(m, sizeof(*evl));
  513                                 if (m == NULL) {
  514                                         if_printf(ifp,
  515                                             "cannot pullup VLAN header\n");
  516                                         ifp->if_oerrors++;
  517                                         continue;
  518                                 }
  519                         }
  520 
  521                         /*
  522                          * Transform the Ethernet header into an Ethernet header
  523                          * with 802.1Q encapsulation.
  524                          */
  525                         bcopy(mtod(m, char *) + ifv->ifv_encaplen,
  526                               mtod(m, char *), ETHER_HDR_LEN);
  527                         evl = mtod(m, struct ether_vlan_header *);
  528                         evl->evl_proto = evl->evl_encap_proto;
  529                         evl->evl_encap_proto = htons(ETHERTYPE_VLAN);
  530                         evl->evl_tag = htons(ifv->ifv_tag);
  531 #ifdef DEBUG
  532                         printf("vlan_start: %*D\n", (int)sizeof(*evl),
  533                             (unsigned char *)evl, ":");
  534 #endif
  535                 }
  536 
  537                 /*
  538                  * Send it, precisely as ether_output() would have.
  539                  * We are already running at splimp.
  540                  */
  541                 IFQ_HANDOFF(p, m, error);
  542                 if (!error)
  543                         ifp->if_opackets++;
  544                 else
  545                         ifp->if_oerrors++;
  546         }
  547         ifp->if_flags &= ~IFF_OACTIVE;
  548 }
  549 
  550 static void
  551 vlan_input(struct ifnet *ifp, struct mbuf *m)
  552 {
  553         struct ether_vlan_header *evl;
  554         struct ifvlan *ifv;
  555         struct m_tag *mtag;
  556         u_int tag;
  557 
  558         mtag = m_tag_locate(m, MTAG_VLAN, MTAG_VLAN_TAG, NULL);
  559         if (mtag != NULL) {
  560                 /*
  561                  * Packet is tagged, m contains a normal
  562                  * Ethernet frame; the tag is stored out-of-band.
  563                  */
  564                 tag = EVL_VLANOFTAG(VLAN_TAG_VALUE(mtag));
  565                 m_tag_delete(m, mtag);
  566         } else {
  567                 switch (ifp->if_type) {
  568                 case IFT_ETHER:
  569                         if (m->m_len < sizeof(*evl) &&
  570                             (m = m_pullup(m, sizeof(*evl))) == NULL) {
  571                                 if_printf(ifp, "cannot pullup VLAN header\n");
  572                                 return;
  573                         }
  574                         evl = mtod(m, struct ether_vlan_header *);
  575                         KASSERT(ntohs(evl->evl_encap_proto) == ETHERTYPE_VLAN,
  576                                 ("vlan_input: bad encapsulated protocols (%u)",
  577                                  ntohs(evl->evl_encap_proto)));
  578 
  579                         tag = EVL_VLANOFTAG(ntohs(evl->evl_tag));
  580 
  581                         /*
  582                          * Restore the original ethertype.  We'll remove
  583                          * the encapsulation after we've found the vlan
  584                          * interface corresponding to the tag.
  585                          */
  586                         evl->evl_encap_proto = evl->evl_proto;
  587                         break;
  588                 default:
  589                         tag = (u_int) -1;
  590 #ifdef DIAGNOSTIC
  591                         panic("vlan_input: unsupported if type %u",
  592                             ifp->if_type);
  593 #endif
  594                         break;
  595                 }
  596         }
  597 
  598         VLAN_LOCK();
  599         LIST_FOREACH(ifv, &ifv_list, ifv_list)
  600                 if (ifp == ifv->ifv_p && tag == ifv->ifv_tag)
  601                         break;
  602 
  603         if (ifv == NULL || (ifv->ifv_if.if_flags & IFF_UP) == 0) {
  604                 VLAN_UNLOCK();
  605                 m_freem(m);
  606                 ifp->if_noproto++;
  607 #ifdef DEBUG
  608                 printf("vlan_input: tag %d, no interface\n", tag);
  609 #endif
  610                 return;
  611         }
  612         VLAN_UNLOCK();          /* XXX extend below? */
  613 #ifdef DEBUG
  614         printf("vlan_input: tag %d, parent %s\n", tag, ifv->ifv_p->if_xname);
  615 #endif
  616 
  617         if (mtag == NULL) {
  618                 /*
  619                  * Packet had an in-line encapsulation header;
  620                  * remove it.  The original header has already
  621                  * been fixed up above.
  622                  */
  623                 bcopy(mtod(m, caddr_t),
  624                       mtod(m, caddr_t) + ETHER_VLAN_ENCAP_LEN,
  625                       ETHER_HDR_LEN);
  626                 m_adj(m, ETHER_VLAN_ENCAP_LEN);
  627         }
  628 
  629         m->m_pkthdr.rcvif = &ifv->ifv_if;
  630         ifv->ifv_if.if_ipackets++;
  631 
  632         /* Pass it back through the parent's input routine. */
  633         (*ifp->if_input)(&ifv->ifv_if, m);
  634 }
  635 
  636 static int
  637 vlan_config(struct ifvlan *ifv, struct ifnet *p)
  638 {
  639         struct ifaddr *ifa1, *ifa2;
  640         struct sockaddr_dl *sdl1, *sdl2;
  641 
  642         VLAN_LOCK_ASSERT();
  643 
  644         if (p->if_data.ifi_type != IFT_ETHER)
  645                 return (EPROTONOSUPPORT);
  646         if (ifv->ifv_p)
  647                 return (EBUSY);
  648 
  649         ifv->ifv_encaplen = ETHER_VLAN_ENCAP_LEN;
  650         ifv->ifv_mintu = ETHERMIN;
  651         ifv->ifv_flags = 0;
  652 
  653         /*
  654          * The active VLAN counter on the parent is used
  655          * at various places to see if there is a vlan(4)
  656          * attached to this physical interface.
  657          */
  658         p->if_nvlans++;
  659 
  660         /*
  661          * If the parent supports the VLAN_MTU capability,
  662          * i.e. can Tx/Rx larger than ETHER_MAX_LEN frames,
  663          * use it.
  664          */
  665         if (p->if_capenable & IFCAP_VLAN_MTU) {
  666                 /*
  667                  * No need to fudge the MTU since the parent can
  668                  * handle extended frames.
  669                  */
  670                 ifv->ifv_mtufudge = 0;
  671         } else {
  672                 /*
  673                  * Fudge the MTU by the encapsulation size.  This
  674                  * makes us incompatible with strictly compliant
  675                  * 802.1Q implementations, but allows us to use
  676                  * the feature with other NetBSD implementations,
  677                  * which might still be useful.
  678                  */
  679                 ifv->ifv_mtufudge = ifv->ifv_encaplen;
  680         }
  681 
  682         ifv->ifv_p = p;
  683         ifv->ifv_if.if_mtu = p->if_mtu - ifv->ifv_mtufudge;
  684         /*
  685          * Copy only a selected subset of flags from the parent.
  686          * Other flags are none of our business.
  687          */
  688         ifv->ifv_if.if_flags = (p->if_flags &
  689             (IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX | IFF_POINTOPOINT));
  690         ifv->ifv_if.if_link_state = p->if_link_state;
  691 
  692 #if 0
  693         /*
  694          * Not ready yet.  We need notification from the parent
  695          * when hw checksumming flags in its if_capenable change.
  696          * Flags set in if_capabilities only are useless.
  697          */
  698         /*
  699          * If the parent interface can do hardware-assisted
  700          * VLAN encapsulation, then propagate its hardware-
  701          * assisted checksumming flags.
  702          */
  703         if (p->if_capabilities & IFCAP_VLAN_HWTAGGING)
  704                 ifv->ifv_if.if_capabilities |= p->if_capabilities & IFCAP_HWCSUM;
  705 #endif
  706 
  707         /*
  708          * Set up our ``Ethernet address'' to reflect the underlying
  709          * physical interface's.
  710          */
  711         ifa1 = ifaddr_byindex(ifv->ifv_if.if_index);
  712         ifa2 = ifaddr_byindex(p->if_index);
  713         sdl1 = (struct sockaddr_dl *)ifa1->ifa_addr;
  714         sdl2 = (struct sockaddr_dl *)ifa2->ifa_addr;
  715         sdl1->sdl_type = IFT_ETHER;
  716         sdl1->sdl_alen = ETHER_ADDR_LEN;
  717         bcopy(LLADDR(sdl2), LLADDR(sdl1), ETHER_ADDR_LEN);
  718         bcopy(LLADDR(sdl2), ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN);
  719 
  720         /*
  721          * Configure multicast addresses that may already be
  722          * joined on the vlan device.
  723          */
  724         (void)vlan_setmulti(&ifv->ifv_if); /* XXX: VLAN lock held */
  725 
  726         return (0);
  727 }
  728 
  729 static int
  730 vlan_unconfig(struct ifnet *ifp)
  731 {
  732         struct ifaddr *ifa;
  733         struct sockaddr_dl *sdl;
  734         struct vlan_mc_entry *mc;
  735         struct ifvlan *ifv;
  736         struct ifnet *p;
  737         int error;
  738 
  739         VLAN_LOCK_ASSERT();
  740 
  741         ifv = ifp->if_softc;
  742         p = ifv->ifv_p;
  743 
  744         if (p) {
  745                 struct sockaddr_dl sdl;
  746 
  747                 /*
  748                  * Since the interface is being unconfigured, we need to
  749                  * empty the list of multicast groups that we may have joined
  750                  * while we were alive from the parent's list.
  751                  */
  752                 bzero((char *)&sdl, sizeof(sdl));
  753                 sdl.sdl_len = sizeof(sdl);
  754                 sdl.sdl_family = AF_LINK;
  755                 sdl.sdl_index = p->if_index;
  756                 sdl.sdl_type = IFT_ETHER;
  757                 sdl.sdl_alen = ETHER_ADDR_LEN;
  758 
  759                 while(SLIST_FIRST(&ifv->vlan_mc_listhead) != NULL) {
  760                         mc = SLIST_FIRST(&ifv->vlan_mc_listhead);
  761                         bcopy((char *)&mc->mc_addr, LLADDR(&sdl),
  762                             ETHER_ADDR_LEN);
  763                         error = if_delmulti(p, (struct sockaddr *)&sdl);
  764                         if (error)
  765                                 return (error);
  766                         SLIST_REMOVE_HEAD(&ifv->vlan_mc_listhead, mc_entries);
  767                         free(mc, M_VLAN);
  768                 }
  769 
  770                 p->if_nvlans--;
  771         }
  772 
  773         /* Disconnect from parent. */
  774         ifv->ifv_p = NULL;
  775         ifv->ifv_if.if_mtu = ETHERMTU;          /* XXX why not 0? */
  776         ifv->ifv_flags = 0;
  777         ifv->ifv_if.if_link_state = LINK_STATE_UNKNOWN;
  778 
  779         /* Clear our MAC address. */
  780         ifa = ifaddr_byindex(ifv->ifv_if.if_index);
  781         sdl = (struct sockaddr_dl *)ifa->ifa_addr;
  782         sdl->sdl_type = IFT_ETHER;
  783         sdl->sdl_alen = ETHER_ADDR_LEN;
  784         bzero(LLADDR(sdl), ETHER_ADDR_LEN);
  785         bzero(ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN);
  786 
  787         return (0);
  788 }
  789 
  790 static int
  791 vlan_set_promisc(struct ifnet *ifp)
  792 {
  793         struct ifvlan *ifv = ifp->if_softc;
  794         int error = 0;
  795 
  796         if ((ifp->if_flags & IFF_PROMISC) != 0) {
  797                 if ((ifv->ifv_flags & IFVF_PROMISC) == 0) {
  798                         error = ifpromisc(ifv->ifv_p, 1);
  799                         if (error == 0)
  800                                 ifv->ifv_flags |= IFVF_PROMISC;
  801                 }
  802         } else {
  803                 if ((ifv->ifv_flags & IFVF_PROMISC) != 0) {
  804                         error = ifpromisc(ifv->ifv_p, 0);
  805                         if (error == 0)
  806                                 ifv->ifv_flags &= ~IFVF_PROMISC;
  807                 }
  808         }
  809 
  810         return (error);
  811 }
  812 
  813 /* Inform all vlans that their parent has changed link state */
  814 static void
  815 vlan_link_state(struct ifnet *ifp, int link)
  816 {
  817         struct ifvlan *ifv;
  818 
  819         VLAN_LOCK();
  820         LIST_FOREACH(ifv, &ifv_list, ifv_list) {
  821                 if (ifv->ifv_p == ifp) {
  822                         ifv->ifv_if.if_link_state = ifv->ifv_p->if_link_state;
  823                         rt_ifmsg(&(ifv->ifv_if));
  824                         KNOTE_UNLOCKED(&ifp->if_klist, link);
  825                 }
  826         }
  827         VLAN_UNLOCK();
  828 }
  829 
  830 static int
  831 vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
  832 {
  833         struct ifaddr *ifa;
  834         struct ifnet *p;
  835         struct ifreq *ifr;
  836         struct ifvlan *ifv;
  837         struct vlanreq vlr;
  838         int error = 0;
  839 
  840         ifr = (struct ifreq *)data;
  841         ifa = (struct ifaddr *)data;
  842         ifv = ifp->if_softc;
  843 
  844         switch (cmd) {
  845         case SIOCSIFADDR:
  846                 ifp->if_flags |= IFF_UP;
  847 
  848                 switch (ifa->ifa_addr->sa_family) {
  849 #ifdef INET
  850                 case AF_INET:
  851                         arp_ifinit(&ifv->ifv_if, ifa);
  852                         break;
  853 #endif
  854                 default:
  855                         break;
  856                 }
  857                 break;
  858 
  859         case SIOCGIFADDR:
  860                 {
  861                         struct sockaddr *sa;
  862 
  863                         sa = (struct sockaddr *) &ifr->ifr_data;
  864                         bcopy(IFP2AC(ifp)->ac_enaddr, (caddr_t)sa->sa_data,
  865                             ETHER_ADDR_LEN);
  866                 }
  867                 break;
  868 
  869         case SIOCGIFMEDIA:
  870                 VLAN_LOCK();
  871                 if (ifv->ifv_p != NULL) {
  872                         error = (*ifv->ifv_p->if_ioctl)(ifv->ifv_p,
  873                                         SIOCGIFMEDIA, data);
  874                         VLAN_UNLOCK();
  875                         /* Limit the result to the parent's current config. */
  876                         if (error == 0) {
  877                                 struct ifmediareq *ifmr;
  878 
  879                                 ifmr = (struct ifmediareq *)data;
  880                                 if (ifmr->ifm_count >= 1 && ifmr->ifm_ulist) {
  881                                         ifmr->ifm_count = 1;
  882                                         error = copyout(&ifmr->ifm_current,
  883                                                 ifmr->ifm_ulist,
  884                                                 sizeof(int));
  885                                 }
  886                         }
  887                 } else {
  888                         VLAN_UNLOCK();
  889                         error = EINVAL;
  890                 }
  891                 break;
  892 
  893         case SIOCSIFMEDIA:
  894                 error = EINVAL;
  895                 break;
  896 
  897         case SIOCSIFMTU:
  898                 /*
  899                  * Set the interface MTU.
  900                  */
  901                 VLAN_LOCK();
  902                 if (ifv->ifv_p != NULL) {
  903                         if (ifr->ifr_mtu >
  904                              (ifv->ifv_p->if_mtu - ifv->ifv_mtufudge) ||
  905                             ifr->ifr_mtu <
  906                              (ifv->ifv_mintu - ifv->ifv_mtufudge))
  907                                 error = EINVAL;
  908                         else
  909                                 ifp->if_mtu = ifr->ifr_mtu;
  910                 } else
  911                         error = EINVAL;
  912                 VLAN_UNLOCK();
  913                 break;
  914 
  915         case SIOCSETVLAN:
  916                 error = copyin(ifr->ifr_data, &vlr, sizeof(vlr));
  917                 if (error)
  918                         break;
  919                 if (vlr.vlr_parent[0] == '\0') {
  920                         VLAN_LOCK();
  921                         vlan_unconfig(ifp);
  922                         if (ifp->if_flags & IFF_UP)
  923                                 if_down(ifp);
  924                         ifp->if_flags &= ~IFF_RUNNING;
  925                         VLAN_UNLOCK();
  926                         break;
  927                 }
  928                 p = ifunit(vlr.vlr_parent);
  929                 if (p == 0) {
  930                         error = ENOENT;
  931                         break;
  932                 }
  933                 /*
  934                  * Don't let the caller set up a VLAN tag with
  935                  * anything except VLID bits.
  936                  */
  937                 if (vlr.vlr_tag & ~EVL_VLID_MASK) {
  938                         error = EINVAL;
  939                         break;
  940                 }
  941                 VLAN_LOCK();
  942                 error = vlan_config(ifv, p);
  943                 if (error) {
  944                         VLAN_UNLOCK();
  945                         break;
  946                 }
  947                 ifv->ifv_tag = vlr.vlr_tag;
  948                 ifp->if_flags |= IFF_RUNNING;
  949                 VLAN_UNLOCK();
  950 
  951                 /* Update promiscuous mode, if necessary. */
  952                 vlan_set_promisc(ifp);
  953                 break;
  954 
  955         case SIOCGETVLAN:
  956                 bzero(&vlr, sizeof(vlr));
  957                 VLAN_LOCK();
  958                 if (ifv->ifv_p) {
  959                         strlcpy(vlr.vlr_parent, ifv->ifv_p->if_xname,
  960                             sizeof(vlr.vlr_parent));
  961                         vlr.vlr_tag = ifv->ifv_tag;
  962                 }
  963                 VLAN_UNLOCK();
  964                 error = copyout(&vlr, ifr->ifr_data, sizeof(vlr));
  965                 break;
  966                 
  967         case SIOCSIFFLAGS:
  968                 /*
  969                  * For promiscuous mode, we enable promiscuous mode on
  970                  * the parent if we need promiscuous on the VLAN interface.
  971                  */
  972                 if (ifv->ifv_p != NULL)
  973                         error = vlan_set_promisc(ifp);
  974                 break;
  975 
  976         case SIOCADDMULTI:
  977         case SIOCDELMULTI:
  978                 /*VLAN_LOCK();*/
  979                 error = vlan_setmulti(ifp);
  980                 /*VLAN_UNLOCK();*/
  981                 break;
  982         default:
  983                 error = EINVAL;
  984         }
  985 
  986         return (error);
  987 }

Cache object: dbc150864523d521cc9ffcf9b00ebda1


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