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$
   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 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  * XXX It's incorrect to assume that we must always kludge up
   45  * headers on the physical device's behalf: some devices support
   46  * VLAN tag insertion and extraction in firmware. For these cases,
   47  * one can change the behavior of the vlan interface by setting
   48  * the LINK0 flag on it (that is setting the vlan interface's LINK0
   49  * flag, _not_ the parent's LINK0 flag; we try to leave the parent
   50  * alone). If the interface has the LINK0 flag set, then it will
   51  * not modify the ethernet header on output, because the parent
   52  * can do that for itself. On input, the parent can call vlan_input_tag()
   53  * directly in order to supply us with an incoming mbuf and the vlan
   54  * tag value that goes with it.
   55  */
   56 
   57 #ifndef NVLAN
   58 #include "vlan.h"
   59 #endif
   60 #include "opt_inet.h"
   61 
   62 #include <sys/param.h>
   63 #include <sys/kernel.h>
   64 #include <sys/malloc.h>
   65 #include <sys/mbuf.h>
   66 #include <sys/module.h>
   67 #include <sys/queue.h>
   68 #include <sys/socket.h>
   69 #include <sys/sockio.h>
   70 #include <sys/sysctl.h>
   71 #include <sys/systm.h>
   72 #include <machine/bus.h>        /* XXX: Shouldn't really be required! */
   73 #include <sys/rman.h>
   74 
   75 #include <net/bpf.h>
   76 #include <net/ethernet.h>
   77 #include <net/if.h>
   78 #include <net/if_arp.h>
   79 #include <net/if_dl.h>
   80 #include <net/if_types.h>
   81 #include <net/if_vlan_var.h>
   82 
   83 #ifdef INET
   84 #include <netinet/in.h>
   85 #include <netinet/if_ether.h>
   86 #endif
   87 
   88 #define VLANNAME        "vlan"
   89 #define VLAN_MAXUNIT    0x7fff  /* ifp->if_unit is only 15 bits */
   90 
   91 SYSCTL_DECL(_net_link);
   92 SYSCTL_NODE(_net_link, IFT_L2VLAN, vlan, CTLFLAG_RW, 0, "IEEE 802.1Q VLAN");
   93 SYSCTL_NODE(_net_link_vlan, PF_LINK, link, CTLFLAG_RW, 0, "for consistency");
   94 
   95 static MALLOC_DEFINE(M_VLAN, "vlan", "802.1Q Virtual LAN Interface");
   96 static struct rman vlanunits[1];
   97 static LIST_HEAD(, ifvlan) ifv_list;
   98 
   99 static  int vlan_clone_create(struct if_clone *, int *);
  100 static  void vlan_clone_destroy(struct ifnet *);
  101 static  void vlan_start(struct ifnet *ifp);
  102 static  void vlan_ifinit(void *foo);
  103 static  int vlan_input(struct ether_header *eh, struct mbuf *m);
  104 static  int vlan_input_tag(struct ether_header *eh, struct mbuf *m,
  105                 u_int16_t t);
  106 static  int vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr);
  107 static  int vlan_setmulti(struct ifnet *ifp);
  108 static  int vlan_unconfig(struct ifnet *ifp);
  109 static  int vlan_config(struct ifvlan *ifv, struct ifnet *p);
  110 
  111 struct if_clone vlan_cloner =
  112     IF_CLONE_INITIALIZER("vlan", vlan_clone_create, vlan_clone_destroy);
  113 
  114 /*
  115  * Program our multicast filter. What we're actually doing is
  116  * programming the multicast filter of the parent. This has the
  117  * side effect of causing the parent interface to receive multicast
  118  * traffic that it doesn't really want, which ends up being discarded
  119  * later by the upper protocol layers. Unfortunately, there's no way
  120  * to avoid this: there really is only one physical interface.
  121  */
  122 static int
  123 vlan_setmulti(struct ifnet *ifp)
  124 {
  125         struct ifnet            *ifp_p;
  126         struct ifmultiaddr      *ifma, *rifma = NULL;
  127         struct ifvlan           *sc;
  128         struct vlan_mc_entry    *mc = NULL;
  129         struct sockaddr_dl      sdl;
  130         int                     error;
  131 
  132         /* Find the parent. */
  133         sc = ifp->if_softc;
  134         ifp_p = sc->ifv_p;
  135 
  136         /*
  137          * If we don't have a parent, just remember the membership for
  138          * when we do.
  139          */
  140         if (ifp_p == NULL)
  141                 return(0);
  142 
  143         bzero((char *)&sdl, sizeof sdl);
  144         sdl.sdl_len = sizeof sdl;
  145         sdl.sdl_family = AF_LINK;
  146         sdl.sdl_index = ifp_p->if_index;
  147         sdl.sdl_type = IFT_ETHER;
  148         sdl.sdl_alen = ETHER_ADDR_LEN;
  149 
  150         /* First, remove any existing filter entries. */
  151         while(SLIST_FIRST(&sc->vlan_mc_listhead) != NULL) {
  152                 mc = SLIST_FIRST(&sc->vlan_mc_listhead);
  153                 bcopy((char *)&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN);
  154                 error = if_delmulti(ifp_p, (struct sockaddr *)&sdl);
  155                 if (error)
  156                         return(error);
  157                 SLIST_REMOVE_HEAD(&sc->vlan_mc_listhead, mc_entries);
  158                 free(mc, M_VLAN);
  159         }
  160 
  161         /* Now program new ones. */
  162         LIST_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
  163                 if (ifma->ifma_addr->sa_family != AF_LINK)
  164                         continue;
  165                 mc = malloc(sizeof(struct vlan_mc_entry), M_VLAN, M_WAITOK);
  166                 bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
  167                     (char *)&mc->mc_addr, ETHER_ADDR_LEN);
  168                 SLIST_INSERT_HEAD(&sc->vlan_mc_listhead, mc, mc_entries);
  169                 bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
  170                     LLADDR(&sdl), ETHER_ADDR_LEN);
  171                 error = if_addmulti(ifp_p, (struct sockaddr *)&sdl, &rifma);
  172                 if (error)
  173                         return(error);
  174         }
  175 
  176         return(0);
  177 }
  178 
  179 static int
  180 vlan_modevent(module_t mod, int type, void *data) 
  181 { 
  182         int i;
  183         int err;
  184 
  185         switch (type) { 
  186         case MOD_LOAD: 
  187                 vlanunits->rm_type = RMAN_ARRAY;
  188                 vlanunits->rm_descr = "configurable if_vlan units";
  189                 err = rman_init(vlanunits);
  190                 if (err != 0)
  191                         return (err);
  192                 err = rman_manage_region(vlanunits, 0, VLAN_MAXUNIT);
  193                 if (err != 0) {
  194                         printf("%s: vlanunits: rman_manage_region: Failed %d\n",
  195                             VLANNAME, err);
  196                         rman_fini(vlanunits);
  197                         return (err);
  198                 }
  199                 LIST_INIT(&ifv_list);
  200                 vlan_input_p = vlan_input;
  201                 vlan_input_tag_p = vlan_input_tag;
  202                 if_clone_attach(&vlan_cloner);
  203                 for(i = 0; i < NVLAN; i ++) {
  204                         err = vlan_clone_create(&vlan_cloner, &i);
  205                         KASSERT(err == 0,
  206                             ("Unexpected error creating initial VLANs"));
  207                 }
  208                 break; 
  209         case MOD_UNLOAD: 
  210                 if_clone_detach(&vlan_cloner);
  211                 vlan_input_p = NULL;
  212                 vlan_input_tag_p = NULL;
  213                 while (!LIST_EMPTY(&ifv_list))
  214                         vlan_clone_destroy(&LIST_FIRST(&ifv_list)->ifv_if);
  215                 err = rman_fini(vlanunits);
  216                 if (err != 0)
  217                          return (err);
  218                 break;
  219         } 
  220         return 0; 
  221 } 
  222 
  223 static moduledata_t vlan_mod = { 
  224         "if_vlan", 
  225         vlan_modevent, 
  226         0
  227 }; 
  228 
  229 DECLARE_MODULE(if_vlan, vlan_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
  230 
  231 static int
  232 vlan_clone_create(struct if_clone *ifc, int *unit)
  233 {
  234         struct resource *r;
  235         struct ifvlan *ifv;
  236         struct ifnet *ifp;
  237         int s;
  238 
  239         if (*unit > VLAN_MAXUNIT)
  240                 return (ENXIO);
  241 
  242         if (*unit < 0) {
  243                 r  = rman_reserve_resource(vlanunits, 0, VLAN_MAXUNIT, 1,
  244                     RF_ALLOCATED | RF_ACTIVE, NULL);
  245                 if (r == NULL)
  246                         return (ENOSPC);
  247                 *unit = rman_get_start(r);
  248         } else {
  249                 r  = rman_reserve_resource(vlanunits, *unit, *unit, 1,
  250                     RF_ALLOCATED | RF_ACTIVE, NULL);
  251                 if (r == NULL)
  252                         return (EEXIST);
  253         }
  254 
  255         ifv = malloc(sizeof(struct ifvlan), M_VLAN, M_WAITOK);
  256         memset(ifv, 0, sizeof(struct ifvlan));
  257         ifp = &ifv->ifv_if;
  258         SLIST_INIT(&ifv->vlan_mc_listhead);
  259 
  260         s = splnet();
  261         LIST_INSERT_HEAD(&ifv_list, ifv, ifv_list);
  262         splx(s);
  263 
  264         ifp->if_softc = ifv;
  265         ifp->if_name = "vlan";
  266         ifp->if_unit = *unit;
  267         ifv->r_unit = r;
  268         /* NB: flags are not set here */
  269         ifp->if_linkmib = &ifv->ifv_mib;
  270         ifp->if_linkmiblen = sizeof ifv->ifv_mib;
  271         /* NB: mtu is not set here */
  272 
  273         ifp->if_init = vlan_ifinit;
  274         ifp->if_start = vlan_start;
  275         ifp->if_ioctl = vlan_ioctl;
  276         ifp->if_output = ether_output;
  277         ifp->if_snd.ifq_maxlen = ifqmaxlen;
  278         ether_ifattach(ifp, ETHER_BPF_SUPPORTED);
  279         /* Now undo some of the damage... */
  280         ifp->if_data.ifi_type = IFT_L2VLAN;
  281         ifp->if_data.ifi_hdrlen = EVL_ENCAPLEN;
  282 
  283         return (0);
  284 }
  285 
  286 static void
  287 vlan_clone_destroy(struct ifnet *ifp)
  288 {
  289         struct ifvlan *ifv = ifp->if_softc;
  290         int s;
  291         int err;
  292 
  293         s = splnet();
  294         LIST_REMOVE(ifv, ifv_list);
  295         vlan_unconfig(ifp);
  296         splx(s);
  297 
  298         ether_ifdetach(ifp, ETHER_BPF_SUPPORTED);
  299 
  300         err = rman_release_resource(ifv->r_unit);
  301         KASSERT(err == 0, ("Unexpected error freeing resource"));
  302         free(ifv, M_VLAN);
  303 }
  304 
  305 static void
  306 vlan_ifinit(void *foo)
  307 {
  308         return;
  309 }
  310 
  311 static void
  312 vlan_start(struct ifnet *ifp)
  313 {
  314         struct ifvlan *ifv;
  315         struct ifnet *p;
  316         struct ether_vlan_header *evl;
  317         struct mbuf *m;
  318 
  319         ifv = ifp->if_softc;
  320         p = ifv->ifv_p;
  321 
  322         ifp->if_flags |= IFF_OACTIVE;
  323         for (;;) {
  324                 IF_DEQUEUE(&ifp->if_snd, m);
  325                 if (m == 0)
  326                         break;
  327                 if (ifp->if_bpf)
  328                         bpf_mtap(ifp, m);
  329 
  330                 /*
  331                  * Do not run parent's if_start() if the parent is not up,
  332                  * or parent's driver will cause a system crash.
  333                  */
  334                 if ((p->if_flags & (IFF_UP | IFF_RUNNING)) !=
  335                                         (IFF_UP | IFF_RUNNING)) {
  336                         m_freem(m);
  337                         ifp->if_data.ifi_collisions++;
  338                         continue;
  339                 }
  340 
  341                 /*
  342                  * If the LINK0 flag is set, it means the underlying interface
  343                  * can do VLAN tag insertion itself and doesn't require us to
  344                  * create a special header for it. In this case, we just pass
  345                  * the packet along. However, we need some way to tell the
  346                  * interface where the packet came from so that it knows how
  347                  * to find the VLAN tag to use, so we set the rcvif in the
  348                  * mbuf header to our ifnet.
  349                  *
  350                  * Note: we also set the M_PROTO1 flag in the mbuf to let
  351                  * the parent driver know that the rcvif pointer is really
  352                  * valid. We need to do this because sometimes mbufs will
  353                  * be allocated by other parts of the system that contain
  354                  * garbage in the rcvif pointer. Using the M_PROTO1 flag
  355                  * lets the driver perform a proper sanity check and avoid
  356                  * following potentially bogus rcvif pointers off into
  357                  * never-never land.
  358                  */
  359                 if (ifp->if_flags & IFF_LINK0) {
  360                         m->m_pkthdr.rcvif = ifp;
  361                         m->m_flags |= M_PROTO1;
  362                 } else {
  363                         M_PREPEND(m, EVL_ENCAPLEN, M_DONTWAIT);
  364                         if (m == NULL) {
  365                                 printf("vlan%d: M_PREPEND failed", ifp->if_unit);
  366                                 ifp->if_ierrors++;
  367                                 continue;
  368                         }
  369                         /* M_PREPEND takes care of m_len, m_pkthdr.len for us */
  370 
  371                         m = m_pullup(m, ETHER_HDR_LEN + EVL_ENCAPLEN);
  372                         if (m == NULL) {
  373                                 printf("vlan%d: m_pullup failed", ifp->if_unit);
  374                                 ifp->if_ierrors++;
  375                                 continue;
  376                         }
  377 
  378                         /*
  379                          * Transform the Ethernet header into an Ethernet header
  380                          * with 802.1Q encapsulation.
  381                          */
  382                         bcopy(mtod(m, char *) + EVL_ENCAPLEN, mtod(m, char *),
  383                               sizeof(struct ether_header));
  384                         evl = mtod(m, struct ether_vlan_header *);
  385                         evl->evl_proto = evl->evl_encap_proto;
  386                         evl->evl_encap_proto = htons(ETHERTYPE_VLAN);
  387                         evl->evl_tag = htons(ifv->ifv_tag);
  388 #ifdef DEBUG
  389                         printf("vlan_start: %*D\n", sizeof *evl,
  390                             (char *)evl, ":");
  391 #endif
  392                 }
  393 
  394                 /*
  395                  * Send it, precisely as ether_output() would have.
  396                  * We are already running at splimp.
  397                  */
  398                 if (IF_QFULL(&p->if_snd)) {
  399                         IF_DROP(&p->if_snd);
  400                                 /* XXX stats */
  401                         ifp->if_oerrors++;
  402                         m_freem(m);
  403                         continue;
  404                 }
  405                 IF_ENQUEUE(&p->if_snd, m);
  406                 ifp->if_opackets++;
  407                 p->if_obytes += m->m_pkthdr.len;
  408                 if (m->m_flags & M_MCAST)
  409                         p->if_omcasts++;
  410                 if ((p->if_flags & IFF_OACTIVE) == 0)
  411                         p->if_start(p);
  412         }
  413         ifp->if_flags &= ~IFF_OACTIVE;
  414 
  415         return;
  416 }
  417 
  418 static int
  419 vlan_input_tag(struct ether_header *eh, struct mbuf *m, u_int16_t t)
  420 {
  421         struct ifvlan *ifv;
  422 
  423         /*
  424          * Fake up a header and send the packet to the physical interface's
  425          * bpf tap if active.
  426          */
  427         if (m->m_pkthdr.rcvif->if_bpf != NULL) {
  428                 struct m_hdr mh;
  429                 struct ether_vlan_header evh;
  430 
  431                 bcopy(eh, &evh, 2*ETHER_ADDR_LEN);
  432                 evh.evl_encap_proto = htons(ETHERTYPE_VLAN);
  433                 evh.evl_tag = htons(t);
  434                 evh.evl_proto = eh->ether_type;
  435 
  436                 /* This kludge is OK; BPF treats the "mbuf" as read-only */
  437                 mh.mh_next = m;
  438                 mh.mh_data = (char *)&evh;
  439                 mh.mh_len = ETHER_HDR_LEN + EVL_ENCAPLEN;
  440                 bpf_mtap(m->m_pkthdr.rcvif, (struct mbuf *)&mh);
  441         }
  442 
  443         for (ifv = LIST_FIRST(&ifv_list); ifv != NULL;
  444             ifv = LIST_NEXT(ifv, ifv_list)) {
  445                 if (m->m_pkthdr.rcvif == ifv->ifv_p
  446                     && ifv->ifv_tag == EVL_VLANOFTAG(t))
  447                         break;
  448         }
  449 
  450         if (ifv == NULL || (ifv->ifv_if.if_flags & IFF_UP) == 0) {
  451                 m_freem(m);
  452                 return -1;      /* So the parent can take note */
  453         }
  454 
  455         /*
  456          * Having found a valid vlan interface corresponding to
  457          * the given source interface and vlan tag, run the
  458          * the real packet through ether_input().
  459          */
  460         m->m_pkthdr.rcvif = &ifv->ifv_if;
  461 
  462         ifv->ifv_if.if_ipackets++;
  463         ether_input(&ifv->ifv_if, eh, m);
  464         return 0;
  465 }
  466 
  467 static int
  468 vlan_input(struct ether_header *eh, struct mbuf *m)
  469 {
  470         struct ifvlan *ifv;
  471 
  472         for (ifv = LIST_FIRST(&ifv_list); ifv != NULL;
  473             ifv = LIST_NEXT(ifv, ifv_list)) {
  474                 if (m->m_pkthdr.rcvif == ifv->ifv_p
  475                     && (EVL_VLANOFTAG(ntohs(*mtod(m, u_int16_t *)))
  476                         == ifv->ifv_tag))
  477                         break;
  478         }
  479 
  480         if (ifv == NULL || (ifv->ifv_if.if_flags & IFF_UP) == 0) {
  481                 m->m_pkthdr.rcvif->if_noproto++;
  482                 m_freem(m);
  483                 return -1;      /* so ether_input can take note */
  484         }
  485 
  486         /*
  487          * Having found a valid vlan interface corresponding to
  488          * the given source interface and vlan tag, remove the
  489          * encapsulation, and run the real packet through
  490          * ether_input() a second time (it had better be
  491          * reentrant!).
  492          */
  493         m->m_pkthdr.rcvif = &ifv->ifv_if;
  494         eh->ether_type = mtod(m, u_int16_t *)[1];
  495         m->m_data += EVL_ENCAPLEN;
  496         m->m_len -= EVL_ENCAPLEN;
  497         m->m_pkthdr.len -= EVL_ENCAPLEN;
  498 
  499         ifv->ifv_if.if_ipackets++;
  500         ether_input(&ifv->ifv_if, eh, m);
  501         return 0;
  502 }
  503 
  504 static int
  505 vlan_config(struct ifvlan *ifv, struct ifnet *p)
  506 {
  507         struct ifaddr *ifa1, *ifa2;
  508         struct sockaddr_dl *sdl1, *sdl2;
  509 
  510         if (p->if_data.ifi_type != IFT_ETHER)
  511                 return EPROTONOSUPPORT;
  512         if (ifv->ifv_p)
  513                 return EBUSY;
  514         ifv->ifv_p = p;
  515         if (p->if_data.ifi_hdrlen == sizeof(struct ether_vlan_header))
  516                 ifv->ifv_if.if_mtu = p->if_mtu;
  517         else
  518                 ifv->ifv_if.if_mtu = p->if_data.ifi_mtu - EVL_ENCAPLEN;
  519 
  520         /*
  521          * Copy only a selected subset of flags from the parent.
  522          * Other flags are none of our business.
  523          */
  524         ifv->ifv_if.if_flags = (p->if_flags &
  525             (IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX | IFF_POINTOPOINT));
  526 
  527         /*
  528          * Set up our ``Ethernet address'' to reflect the underlying
  529          * physical interface's.
  530          */
  531         ifa1 = ifnet_addrs[ifv->ifv_if.if_index - 1];
  532         ifa2 = ifnet_addrs[p->if_index - 1];
  533         sdl1 = (struct sockaddr_dl *)ifa1->ifa_addr;
  534         sdl2 = (struct sockaddr_dl *)ifa2->ifa_addr;
  535         sdl1->sdl_type = IFT_ETHER;
  536         sdl1->sdl_alen = ETHER_ADDR_LEN;
  537         bcopy(LLADDR(sdl2), LLADDR(sdl1), ETHER_ADDR_LEN);
  538         bcopy(LLADDR(sdl2), ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN);
  539 
  540         /*
  541          * Configure multicast addresses that may already be
  542          * joined on the vlan device.
  543          */
  544         (void)vlan_setmulti(&ifv->ifv_if);
  545 
  546         return 0;
  547 }
  548 
  549 static int
  550 vlan_unconfig(struct ifnet *ifp)
  551 {
  552         struct ifaddr *ifa;
  553         struct sockaddr_dl *sdl;
  554         struct vlan_mc_entry *mc;
  555         struct ifvlan *ifv;
  556         struct ifnet *p;
  557         int error;
  558 
  559         ifv = ifp->if_softc;
  560         p = ifv->ifv_p;
  561 
  562         if (p) {
  563                 struct sockaddr_dl sdl;
  564 
  565                 /*
  566                  * Since the interface is being unconfigured, we need to
  567                  * empty the list of multicast groups that we may have joined
  568                  * while we were alive from the parent's list.
  569                  */
  570                 bzero((char *)&sdl, sizeof sdl);
  571                 sdl.sdl_len = sizeof sdl;
  572                 sdl.sdl_family = AF_LINK;
  573                 sdl.sdl_index = p->if_index;
  574                 sdl.sdl_type = IFT_ETHER;
  575                 sdl.sdl_alen = ETHER_ADDR_LEN;
  576 
  577                 while(SLIST_FIRST(&ifv->vlan_mc_listhead) != NULL) {
  578                         mc = SLIST_FIRST(&ifv->vlan_mc_listhead);
  579                         bcopy((char *)&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN);
  580                         error = if_delmulti(p, (struct sockaddr *)&sdl);
  581                         if (error)
  582                                 return(error);
  583                         SLIST_REMOVE_HEAD(&ifv->vlan_mc_listhead, mc_entries);
  584                         free(mc, M_VLAN);
  585                 }
  586         }
  587 
  588         /* Disconnect from parent. */
  589         ifv->ifv_p = NULL;
  590         ifv->ifv_if.if_mtu = ETHERMTU;
  591 
  592         /* Clear our MAC address. */
  593         ifa = ifnet_addrs[ifv->ifv_if.if_index - 1];
  594         sdl = (struct sockaddr_dl *)ifa->ifa_addr;
  595         sdl->sdl_type = IFT_ETHER;
  596         sdl->sdl_alen = ETHER_ADDR_LEN;
  597         bzero(LLADDR(sdl), ETHER_ADDR_LEN);
  598         bzero(ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN);
  599 
  600         return 0;
  601 }
  602 
  603 static int
  604 vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
  605 {
  606         struct ifaddr *ifa;
  607         struct ifnet *p;
  608         struct ifreq *ifr;
  609         struct ifvlan *ifv;
  610         struct vlanreq vlr;
  611         int error = 0;
  612 
  613         ifr = (struct ifreq *)data;
  614         ifa = (struct ifaddr *)data;
  615         ifv = ifp->if_softc;
  616 
  617         switch (cmd) {
  618         case SIOCSIFADDR:
  619                 ifp->if_flags |= IFF_UP;
  620 
  621                 switch (ifa->ifa_addr->sa_family) {
  622 #ifdef INET
  623                 case AF_INET:
  624                         arp_ifinit(&ifv->ifv_if, ifa);
  625                         break;
  626 #endif
  627                 default:
  628                         break;
  629                 }
  630                 break;
  631 
  632         case SIOCGIFADDR:
  633                 {
  634                         struct sockaddr *sa;
  635 
  636                         sa = (struct sockaddr *) &ifr->ifr_data;
  637                         bcopy(((struct arpcom *)ifp->if_softc)->ac_enaddr,
  638                               (caddr_t) sa->sa_data, ETHER_ADDR_LEN);
  639                 }
  640                 break;
  641 
  642         case SIOCGIFMEDIA:
  643                 if (ifv->ifv_p != NULL) {
  644                         error = (ifv->ifv_p->if_ioctl)(ifv->ifv_p, SIOCGIFMEDIA, data);
  645                         /* Limit the result to the parent's current config. */
  646                         if (error == 0) {
  647                                 struct ifmediareq *ifmr;
  648 
  649                                 ifmr = (struct ifmediareq *) data;
  650                                 if (ifmr->ifm_count >= 1 && ifmr->ifm_ulist) {
  651                                         ifmr->ifm_count = 1;
  652                                         error = copyout(&ifmr->ifm_current,
  653                                                 ifmr->ifm_ulist, 
  654                                                 sizeof(int));
  655                                 }
  656                         }
  657                 } else
  658                         error = EINVAL;
  659                 break;
  660 
  661         case SIOCSIFMEDIA:
  662                 error = EINVAL;
  663                 break;
  664 
  665         case SIOCSIFMTU:
  666                 /*
  667                  * Set the interface MTU.
  668                  * This is bogus. The underlying interface might support
  669                  * jumbo frames.
  670                  */
  671                 if (ifr->ifr_mtu > ETHERMTU) {
  672                         error = EINVAL;
  673                 } else {
  674                         ifp->if_mtu = ifr->ifr_mtu;
  675                 }
  676                 break;
  677 
  678         case SIOCSETVLAN:
  679                 error = copyin(ifr->ifr_data, &vlr, sizeof vlr);
  680                 if (error)
  681                         break;
  682                 if (vlr.vlr_tag & ~EVL_VLID_MASK) {
  683                         error = EINVAL;
  684                         break;
  685                 }
  686                 if (vlr.vlr_parent[0] == '\0') {
  687                         vlan_unconfig(ifp);
  688                         if (ifp->if_flags & IFF_UP) {
  689                                 int s = splimp();
  690                                 if_down(ifp);
  691                                 splx(s);
  692                         }               
  693                         ifp->if_flags &= ~IFF_RUNNING;
  694                         break;
  695                 }
  696                 p = ifunit(vlr.vlr_parent);
  697                 if (p == 0) {
  698                         error = ENOENT;
  699                         break;
  700                 }
  701                 error = vlan_config(ifv, p);
  702                 if (error)
  703                         break;
  704                 ifv->ifv_tag = vlr.vlr_tag;
  705                 ifp->if_flags |= IFF_RUNNING;
  706                 break;
  707                 
  708         case SIOCGETVLAN:
  709                 bzero(&vlr, sizeof vlr);
  710                 if (ifv->ifv_p) {
  711                         snprintf(vlr.vlr_parent, sizeof(vlr.vlr_parent),
  712                             "%s%d", ifv->ifv_p->if_name, ifv->ifv_p->if_unit);
  713                         vlr.vlr_tag = ifv->ifv_tag;
  714                 }
  715                 error = copyout(&vlr, ifr->ifr_data, sizeof vlr);
  716                 break;
  717                 
  718         case SIOCSIFFLAGS:
  719                 /*
  720                  * We don't support promiscuous mode
  721                  * right now because it would require help from the
  722                  * underlying drivers, which hasn't been implemented.
  723                  */
  724                 if (ifr->ifr_flags & (IFF_PROMISC)) {
  725                         ifp->if_flags &= ~(IFF_PROMISC);
  726                         error = EINVAL;
  727                 }
  728                 break;
  729         case SIOCADDMULTI:
  730         case SIOCDELMULTI:
  731                 error = vlan_setmulti(ifp);
  732                 break;
  733         default:
  734                 error = EINVAL;
  735         }
  736         return error;
  737 }

Cache object: d99323fd3929890ca2fa84c41ab0e253


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