root/net/if_vlan.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. vlanattach
  2. vlan_clone_create
  3. vlan_clone_destroy
  4. vlan_ifdetach
  5. vlan_start
  6. vlan_input
  7. vlan_config
  8. vlan_unconfig
  9. vlan_vlandev_state
  10. vlan_set_promisc
  11. vlan_ioctl
  12. vlan_ether_addmulti
  13. vlan_ether_delmulti
  14. vlan_ether_purgemulti

    1 /*      $OpenBSD: if_vlan.c,v 1.70 2007/06/06 14:05:58 henning Exp $    */
    2 
    3 /*
    4  * Copyright 1998 Massachusetts Institute of Technology
    5  *
    6  * Permission to use, copy, modify, and distribute this software and
    7  * its documentation for any purpose and without fee is hereby
    8  * granted, provided that both the above copyright notice and this
    9  * permission notice appear in all copies, that both the above
   10  * copyright notice and this permission notice appear in all
   11  * supporting documentation, and that the name of M.I.T. not be used
   12  * in advertising or publicity pertaining to distribution of the
   13  * software without specific, written prior permission.  M.I.T. makes
   14  * no representations about the suitability of this software for any
   15  * purpose.  It is provided "as is" without express or implied
   16  * warranty.
   17  * 
   18  * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''.  M.I.T. DISCLAIMS
   19  * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
   20  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
   21  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
   22  * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   25  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   26  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   27  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   28  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   29  * SUCH DAMAGE.
   30  *
   31  * $FreeBSD: src/sys/net/if_vlan.c,v 1.16 2000/03/26 15:21:40 charnier Exp $
   32  */
   33 
   34 /*
   35  * if_vlan.c - pseudo-device driver for IEEE 802.1Q virtual LANs.
   36  * Might be extended some day to also handle IEEE 802.1p priority
   37  * tagging.  This is sort of sneaky in the implementation, since
   38  * we need to pretend to be enough of an Ethernet implementation
   39  * to make arp work.  The way we do this is by telling everyone
   40  * that we are an Ethernet, and then catch the packets that
   41  * ether_output() left on our output queue when it calls
   42  * if_start(), rewrite them for use by the real outgoing interface,
   43  * and ask it to send them.
   44  *
   45  * Some devices support 802.1Q tag insertion in firmware.  The
   46  * vlan interface behavior changes when the IFCAP_VLAN_HWTAGGING
   47  * capability is set on the parent.  In this case, vlan_start()
   48  * will not modify the ethernet header.
   49  */
   50 
   51 #include "vlan.h"
   52 
   53 #include <sys/param.h>
   54 #include <sys/kernel.h>
   55 #include <sys/malloc.h>
   56 #include <sys/mbuf.h>
   57 #include <sys/queue.h>
   58 #include <sys/socket.h>
   59 #include <sys/sockio.h>
   60 #include <sys/sysctl.h>
   61 #include <sys/systm.h>
   62 #include <sys/proc.h>
   63 
   64 #include "bpfilter.h"
   65 #if NBPFILTER > 0
   66 #include <net/bpf.h>
   67 #endif
   68 
   69 #include <net/if.h>
   70 #include <net/if_dl.h>
   71 #include <net/if_types.h>
   72 
   73 #ifdef INET
   74 #include <netinet/in.h>
   75 #include <netinet/if_ether.h>
   76 #endif
   77 
   78 #include <net/if_vlan_var.h>
   79 
   80 extern struct   ifaddr  **ifnet_addrs;
   81 u_long vlan_tagmask;
   82 
   83 #define TAG_HASH_SIZE   32
   84 #define TAG_HASH(tag)   (tag & vlan_tagmask)
   85 LIST_HEAD(, ifvlan)     *vlan_tagh;
   86 
   87 void    vlan_start (struct ifnet *ifp);
   88 int     vlan_ioctl (struct ifnet *ifp, u_long cmd, caddr_t addr);
   89 int     vlan_unconfig (struct ifnet *ifp);
   90 int     vlan_config (struct ifvlan *, struct ifnet *, u_int16_t);
   91 void    vlan_vlandev_state(void *);
   92 void    vlanattach (int count);
   93 int     vlan_set_promisc (struct ifnet *ifp);
   94 int     vlan_ether_addmulti(struct ifvlan *, struct ifreq *);
   95 int     vlan_ether_delmulti(struct ifvlan *, struct ifreq *);
   96 void    vlan_ether_purgemulti(struct ifvlan *);
   97 int     vlan_clone_create(struct if_clone *, int);
   98 int     vlan_clone_destroy(struct ifnet *);
   99 void    vlan_ifdetach(void *);
  100 
  101 struct if_clone vlan_cloner =
  102     IF_CLONE_INITIALIZER("vlan", vlan_clone_create, vlan_clone_destroy);
  103 
  104 /* ARGSUSED */
  105 void
  106 vlanattach(int count)
  107 {
  108         vlan_tagh = hashinit(TAG_HASH_SIZE, M_DEVBUF, M_NOWAIT, &vlan_tagmask);
  109         if (vlan_tagh == NULL)
  110                 panic("vlanattach: hashinit");
  111 
  112         if_clone_attach(&vlan_cloner);
  113 }
  114 
  115 int
  116 vlan_clone_create(struct if_clone *ifc, int unit)
  117 {
  118         struct ifvlan *ifv;
  119         struct ifnet *ifp;
  120 
  121         ifv = malloc(sizeof(*ifv), M_DEVBUF, M_NOWAIT);
  122         if (!ifv)
  123                 return (ENOMEM);
  124         bzero(ifv, sizeof(*ifv));
  125 
  126         LIST_INIT(&ifv->vlan_mc_listhead);
  127         ifp = &ifv->ifv_if;
  128         ifp->if_softc = ifv;
  129         snprintf(ifp->if_xname, sizeof ifp->if_xname, "%s%d", ifc->ifc_name,
  130             unit);
  131         /* NB: flags are not set here */
  132         /* NB: mtu is not set here */
  133 
  134         ifp->if_start = vlan_start;
  135         ifp->if_ioctl = vlan_ioctl;
  136         ifp->if_output = ether_output;
  137         IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen);
  138         IFQ_SET_READY(&ifp->if_snd);
  139         if_attach(ifp);
  140         ether_ifattach(ifp);
  141         /* Now undo some of the damage... */
  142         ifp->if_type = IFT_L2VLAN;
  143         ifp->if_hdrlen = EVL_ENCAPLEN;
  144 
  145         return (0);
  146 }
  147 
  148 int
  149 vlan_clone_destroy(struct ifnet *ifp)
  150 {
  151         struct ifvlan *ifv = ifp->if_softc;
  152 
  153         vlan_unconfig(ifp);
  154         ether_ifdetach(ifp);
  155         if_detach(ifp);
  156 
  157         free(ifv, M_DEVBUF);
  158         return (0);
  159 }
  160 
  161 void
  162 vlan_ifdetach(void *ptr)
  163 {
  164         struct ifvlan *ifv = (struct ifvlan *)ptr;
  165         /*
  166          * Destroy the vlan interface because the parent has been
  167          * detached. Set the dh_cookie to NULL because we're running
  168          * inside of dohooks which is told to disestablish the hook
  169          * for us (otherwise we would kill the TAILQ element...).
  170          */
  171         ifv->dh_cookie = NULL;
  172         vlan_clone_destroy(&ifv->ifv_if);
  173 }
  174 
  175 void
  176 vlan_start(struct ifnet *ifp)
  177 {
  178         struct ifvlan *ifv;
  179         struct ifnet *p;
  180         struct mbuf *m;
  181         int error;
  182 
  183         ifv = ifp->if_softc;
  184         p = ifv->ifv_p;
  185 
  186         ifp->if_flags |= IFF_OACTIVE;
  187         for (;;) {
  188                 IFQ_DEQUEUE(&ifp->if_snd, m);
  189                 if (m == NULL)
  190                         break;
  191 
  192                 if ((p->if_flags & (IFF_UP|IFF_RUNNING)) !=
  193                     (IFF_UP|IFF_RUNNING)) {
  194                         IF_DROP(&p->if_snd);
  195                                 /* XXX stats */
  196                         ifp->if_oerrors++;
  197                         m_freem(m);
  198                         continue;
  199                 }
  200 
  201 #if NBPFILTER > 0
  202                 if (ifp->if_bpf)
  203                         bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
  204 #endif
  205 
  206                 /*
  207                  * If the IFCAP_VLAN_HWTAGGING capability is set on the parent,
  208                  * it can do VLAN tag insertion itself and doesn't require us
  209                  * to create a special header for it. In this case, we just pass
  210                  * the packet along. However, we need some way to tell the
  211                  * interface where the packet came from so that it knows how
  212                  * to find the VLAN tag to use, so we set the rcvif in the
  213                  * mbuf header to our ifnet.
  214                  *
  215                  * Note: we also set the M_PROTO1 flag in the mbuf to let
  216                  * the parent driver know that the rcvif pointer is really
  217                  * valid. We need to do this because sometimes mbufs will
  218                  * be allocated by other parts of the system that contain
  219                  * garbage in the rcvif pointer. Using the M_PROTO1 flag
  220                  * lets the driver perform a proper sanity check and avoid
  221                  * following potentially bogus rcvif pointers off into
  222                  * never-never land.
  223                  */
  224                 if (p->if_capabilities & IFCAP_VLAN_HWTAGGING) {
  225                         m->m_pkthdr.rcvif = ifp;
  226                         m->m_flags |= M_PROTO1;
  227                 } else {
  228                         struct ether_vlan_header evh;
  229 
  230                         m_copydata(m, 0, ETHER_HDR_LEN, (caddr_t)&evh);
  231                         evh.evl_proto = evh.evl_encap_proto;
  232                         evh.evl_encap_proto = htons(ETHERTYPE_VLAN);
  233                         evh.evl_tag = htons(ifv->ifv_tag +
  234                             (ifv->ifv_prio << EVL_PRIO_BITS));
  235 
  236                         m_adj(m, ETHER_HDR_LEN);
  237                         M_PREPEND(m, sizeof(evh), M_DONTWAIT);
  238                         if (m == NULL) {
  239                                 ifp->if_oerrors++;
  240                                 continue;
  241                         }
  242 
  243                         m_copyback(m, 0, sizeof(evh), &evh);
  244                 }
  245 
  246                 /*
  247                  * Send it, precisely as ether_output() would have.
  248                  * We are already running at splnet.
  249                  */
  250                 p->if_obytes += m->m_pkthdr.len;
  251                 if (m->m_flags & M_MCAST)
  252                         p->if_omcasts++;
  253                 IFQ_ENQUEUE(&p->if_snd, m, NULL, error);
  254                 if (error) {
  255                         /* mbuf is already freed */
  256                         ifp->if_oerrors++;
  257                         continue;
  258                 }
  259 
  260                 ifp->if_opackets++;
  261                 if ((p->if_flags & (IFF_RUNNING|IFF_OACTIVE)) == IFF_RUNNING)
  262                         p->if_start(p);
  263         }
  264         ifp->if_flags &= ~IFF_OACTIVE;
  265 
  266         return;
  267 }
  268 
  269 /*
  270  * vlan_input() returns 0 if it has consumed the packet, 1 otherwise.
  271  */
  272 int
  273 vlan_input(eh, m)
  274         struct ether_header *eh;
  275         struct mbuf *m;
  276 {
  277         struct ifvlan *ifv;
  278         u_int tag;
  279         struct ifnet *ifp = m->m_pkthdr.rcvif;
  280 
  281         if (m->m_len < EVL_ENCAPLEN &&
  282             (m = m_pullup(m, EVL_ENCAPLEN)) == NULL) {
  283                 ifp->if_ierrors++;
  284                 return (0);
  285         }
  286 
  287         tag = EVL_VLANOFTAG(ntohs(*mtod(m, u_int16_t *)));
  288 
  289         LIST_FOREACH(ifv, &vlan_tagh[TAG_HASH(tag)], ifv_list) {
  290                 if (m->m_pkthdr.rcvif == ifv->ifv_p && tag == ifv->ifv_tag)
  291                         break;
  292         }
  293         if (ifv == NULL)
  294                 return (1);
  295 
  296         if ((ifv->ifv_if.if_flags & (IFF_UP|IFF_RUNNING)) !=
  297             (IFF_UP|IFF_RUNNING)) {
  298                 m_freem(m);
  299                 return (0);
  300         }
  301 
  302         /*
  303          * Having found a valid vlan interface corresponding to
  304          * the given source interface and vlan tag, remove the
  305          * encapsulation, and run the real packet through
  306          * ether_input() a second time (it had better be
  307          * reentrant!).
  308          */
  309         m->m_pkthdr.rcvif = &ifv->ifv_if;
  310         eh->ether_type = mtod(m, u_int16_t *)[1];
  311         m->m_len -= EVL_ENCAPLEN;
  312         m->m_data += EVL_ENCAPLEN;
  313         m->m_pkthdr.len -= EVL_ENCAPLEN;
  314 
  315 #if NBPFILTER > 0
  316         if (ifv->ifv_if.if_bpf)
  317                 bpf_mtap_hdr(ifv->ifv_if.if_bpf, (char *)eh, ETHER_HDR_LEN,
  318                     m, BPF_DIRECTION_IN);
  319 #endif
  320         ifv->ifv_if.if_ipackets++;
  321         ether_input(&ifv->ifv_if, eh, m);
  322 
  323         return (0);
  324 }
  325 
  326 int
  327 vlan_config(struct ifvlan *ifv, struct ifnet *p, u_int16_t tag)
  328 {
  329         struct ifaddr *ifa1, *ifa2;
  330         struct sockaddr_dl *sdl1, *sdl2;
  331         int s;
  332 
  333         if (p->if_type != IFT_ETHER)
  334                 return EPROTONOSUPPORT;
  335         if (ifv->ifv_p == p && ifv->ifv_tag == tag) /* noop */
  336                 return (0);
  337         if (ifv->ifv_p)
  338                 return EBUSY;
  339 
  340         ifv->ifv_p = p;
  341 
  342         if (p->if_capabilities & IFCAP_VLAN_MTU)
  343                 ifv->ifv_if.if_mtu = p->if_mtu;
  344         else {
  345                 /*
  346                  * This will be incompatible with strict
  347                  * 802.1Q implementations
  348                  */
  349                 ifv->ifv_if.if_mtu = p->if_mtu - EVL_ENCAPLEN;
  350 #ifdef DIAGNOSTIC
  351                 printf("%s: initialized with non-standard mtu %lu (parent %s)\n",
  352                     ifv->ifv_if.if_xname, ifv->ifv_if.if_mtu,
  353                     ifv->ifv_p->if_xname);
  354 #endif
  355         }
  356 
  357         ifv->ifv_if.if_flags = p->if_flags &
  358             (IFF_UP | IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
  359 
  360         /*
  361          * Inherit the if_type from the parent.  This allows us to
  362          * participate in bridges of that type.
  363          */
  364         ifv->ifv_if.if_type = p->if_type;
  365 
  366         /*
  367          * Inherit baudrate from the parent.  An SNMP agent would use this
  368          * information.
  369          */
  370         ifv->ifv_if.if_baudrate = p->if_baudrate;
  371 
  372         /*
  373          * If the parent interface can do hardware-assisted
  374          * VLAN encapsulation, then propagate its hardware-
  375          * assisted checksumming flags.
  376          *
  377          * If the card cannot handle hardware tagging, it cannot
  378          * possibly compute the correct checksums for tagged packets.
  379          *
  380          * This brings up another possibility, do cards exist which
  381          * have all of these capabilities but cannot utilize them together?
  382          */
  383         if (p->if_capabilities & IFCAP_VLAN_HWTAGGING)
  384                 ifv->ifv_if.if_capabilities = p->if_capabilities &
  385                     (IFCAP_CSUM_IPv4|IFCAP_CSUM_TCPv4|
  386                     IFCAP_CSUM_UDPv4);
  387                 /* (IFCAP_CSUM_TCPv6|IFCAP_CSUM_UDPv6); */
  388 
  389         /*
  390          * Set up our ``Ethernet address'' to reflect the underlying
  391          * physical interface's.
  392          */
  393         ifa1 = ifnet_addrs[ifv->ifv_if.if_index];
  394         ifa2 = ifnet_addrs[p->if_index];
  395         sdl1 = (struct sockaddr_dl *)ifa1->ifa_addr;
  396         sdl2 = (struct sockaddr_dl *)ifa2->ifa_addr;
  397         sdl1->sdl_type = IFT_ETHER;
  398         sdl1->sdl_alen = ETHER_ADDR_LEN;
  399         bcopy(LLADDR(sdl2), LLADDR(sdl1), ETHER_ADDR_LEN);
  400         bcopy(LLADDR(sdl2), ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN);
  401 
  402         ifv->ifv_tag = tag;
  403         s = splnet();
  404         LIST_INSERT_HEAD(&vlan_tagh[TAG_HASH(tag)], ifv, ifv_list);
  405 
  406         /* Register callback for physical link state changes */
  407         ifv->lh_cookie = hook_establish(p->if_linkstatehooks, 1,
  408             vlan_vlandev_state, ifv);
  409 
  410         /* Register callback if parent wants to unregister */
  411         ifv->dh_cookie = hook_establish(p->if_detachhooks, 1,
  412             vlan_ifdetach, ifv);
  413 
  414         vlan_vlandev_state(ifv);
  415         splx(s);
  416 
  417         return 0;
  418 }
  419 
  420 int
  421 vlan_unconfig(struct ifnet *ifp)
  422 {
  423         struct ifaddr *ifa;
  424         struct sockaddr_dl *sdl;
  425         struct ifvlan *ifv;
  426         struct ifnet *p;
  427         struct ifreq *ifr, *ifr_p;
  428         int s;
  429 
  430         ifv = ifp->if_softc;
  431         p = ifv->ifv_p;
  432         if (p == NULL)
  433                 return 0;
  434 
  435         ifr = (struct ifreq *)&ifp->if_data;
  436         ifr_p = (struct ifreq *)&ifv->ifv_p->if_data;
  437 
  438         s = splnet();
  439         LIST_REMOVE(ifv, ifv_list);
  440         if (ifv->lh_cookie != NULL)
  441                 hook_disestablish(p->if_linkstatehooks, ifv->lh_cookie);
  442         /* The cookie is NULL if disestablished externally */
  443         if (ifv->dh_cookie != NULL)
  444                 hook_disestablish(p->if_detachhooks, ifv->dh_cookie);
  445         splx(s);
  446 
  447         /*
  448          * Since the interface is being unconfigured, we need to
  449          * empty the list of multicast groups that we may have joined
  450          * while we were alive and remove them from the parent's list
  451          * as well.
  452          */
  453         vlan_ether_purgemulti(ifv);
  454 
  455         /* Disconnect from parent. */
  456         ifv->ifv_p = NULL;
  457         ifv->ifv_if.if_mtu = ETHERMTU;
  458 
  459         /* Clear our MAC address. */
  460         ifa = ifnet_addrs[ifv->ifv_if.if_index];
  461         sdl = (struct sockaddr_dl *)ifa->ifa_addr;
  462         sdl->sdl_type = IFT_ETHER;
  463         sdl->sdl_alen = ETHER_ADDR_LEN;
  464         bzero(LLADDR(sdl), ETHER_ADDR_LEN);
  465         bzero(ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN);
  466 
  467         return 0;
  468 }
  469 
  470 void
  471 vlan_vlandev_state(void *v)
  472 {
  473         struct ifvlan *ifv = v;
  474 
  475         if (ifv->ifv_if.if_link_state == ifv->ifv_p->if_link_state)
  476                 return;
  477 
  478         ifv->ifv_if.if_link_state = ifv->ifv_p->if_link_state;
  479         ifv->ifv_if.if_baudrate = ifv->ifv_p->if_baudrate;
  480         if_link_state_change(&ifv->ifv_if);
  481 }
  482 
  483 int
  484 vlan_set_promisc(struct ifnet *ifp)
  485 {
  486         struct ifvlan *ifv = ifp->if_softc;
  487         int error = 0;
  488 
  489         if ((ifp->if_flags & IFF_PROMISC) != 0) {
  490                 if ((ifv->ifv_flags & IFVF_PROMISC) == 0) {
  491                         error = ifpromisc(ifv->ifv_p, 1);
  492                         if (error == 0)
  493                                 ifv->ifv_flags |= IFVF_PROMISC;
  494                 }
  495         } else {
  496                 if ((ifv->ifv_flags & IFVF_PROMISC) != 0) {
  497                         error = ifpromisc(ifv->ifv_p, 0);
  498                         if (error == 0)
  499                                 ifv->ifv_flags &= ~IFVF_PROMISC;
  500                 }
  501         }
  502 
  503         return (0);
  504 }
  505 
  506 int
  507 vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
  508 {
  509         struct proc *p = curproc;       /* XXX */
  510         struct ifaddr *ifa;
  511         struct ifnet *pr;
  512         struct ifreq *ifr;
  513         struct ifvlan *ifv;
  514         struct vlanreq vlr;
  515         int error = 0, p_mtu = 0, s;
  516 
  517         ifr = (struct ifreq *)data;
  518         ifa = (struct ifaddr *)data;
  519         ifv = ifp->if_softc;
  520 
  521         switch (cmd) {
  522         case SIOCSIFADDR:
  523                 if (ifv->ifv_p != NULL) {
  524                         ifp->if_flags |= IFF_UP;
  525 
  526                         switch (ifa->ifa_addr->sa_family) {
  527 #ifdef INET
  528                         case AF_INET:
  529                                 arp_ifinit(&ifv->ifv_ac, ifa);
  530                                 break;
  531 #endif
  532                         default:
  533                                 break;
  534                         }
  535                 } else {
  536                         error = EINVAL;
  537                 }
  538                 break;
  539 
  540         case SIOCGIFADDR:
  541                 {
  542                         struct sockaddr *sa;
  543 
  544                         sa = (struct sockaddr *) &ifr->ifr_data;
  545                         bcopy(((struct arpcom *)ifp->if_softc)->ac_enaddr,
  546                             (caddr_t) sa->sa_data, ETHER_ADDR_LEN);
  547                 }
  548                 break;
  549 
  550         case SIOCSIFMTU:
  551                 if (ifv->ifv_p != NULL) {
  552                         if (ifv->ifv_p->if_capabilities & IFCAP_VLAN_MTU)
  553                                 p_mtu = ifv->ifv_p->if_mtu;
  554                         else
  555                                 p_mtu = ifv->ifv_p->if_mtu - EVL_ENCAPLEN;
  556                         
  557                         if (ifr->ifr_mtu > p_mtu || ifr->ifr_mtu < ETHERMIN)
  558                                 error = EINVAL;
  559                         else
  560                                 ifp->if_mtu = ifr->ifr_mtu;
  561                 } else
  562                         error = EINVAL;
  563 
  564                 break;
  565 
  566         case SIOCSETVLAN:
  567                 if ((error = suser(p, 0)) != 0)
  568                         break;
  569                 if ((error = copyin(ifr->ifr_data, &vlr, sizeof vlr)))
  570                         break;
  571                 if (vlr.vlr_parent[0] == '\0') {
  572                         s = splnet();
  573                         vlan_unconfig(ifp);
  574                         if (ifp->if_flags & IFF_UP)
  575                                 if_down(ifp);
  576                         ifp->if_flags &= ~IFF_RUNNING;
  577                         splx(s);
  578                         break;
  579                 }
  580                 pr = ifunit(vlr.vlr_parent);
  581                 if (pr == NULL) {
  582                         error = ENOENT;
  583                         break;
  584                 }
  585                 /*
  586                  * Don't let the caller set up a VLAN tag with
  587                  * anything except VLID bits.
  588                  */
  589                 if (vlr.vlr_tag & ~EVL_VLID_MASK) {
  590                         error = EINVAL;
  591                         break;
  592                 }
  593                 error = vlan_config(ifv, pr, vlr.vlr_tag);
  594                 if (error)
  595                         break;
  596                 ifp->if_flags |= IFF_RUNNING;
  597 
  598                 /* Update promiscuous mode, if necessary. */
  599                 vlan_set_promisc(ifp);
  600                 break;
  601                 
  602         case SIOCGETVLAN:
  603                 bzero(&vlr, sizeof vlr);
  604                 if (ifv->ifv_p) {
  605                         snprintf(vlr.vlr_parent, sizeof(vlr.vlr_parent),
  606                             "%s", ifv->ifv_p->if_xname);
  607                         vlr.vlr_tag = ifv->ifv_tag;
  608                 }
  609                 error = copyout(&vlr, ifr->ifr_data, sizeof vlr);
  610                 break;
  611         case SIOCSETVLANPRIO:
  612                 if ((error = suser(p, 0)) != 0)
  613                         break;
  614                 if ((error = copyin(ifr->ifr_data, &vlr, sizeof vlr)))
  615                         break;
  616                 /*
  617                  * Don't let the caller set up a VLAN priority
  618                  * outside the range 0-7
  619                  */
  620                 if (vlr.vlr_tag > EVL_PRIO_MAX) {
  621                         error = EINVAL;
  622                         break;
  623                 }
  624                 ifv->ifv_prio = vlr.vlr_tag;
  625                 break;
  626         case SIOCGETVLANPRIO:
  627                 bzero(&vlr, sizeof vlr);
  628                 if (ifv->ifv_p)
  629                         strlcpy(vlr.vlr_parent, ifv->ifv_p->if_xname,
  630                             sizeof(vlr.vlr_parent));
  631                 vlr.vlr_tag = ifv->ifv_prio;
  632                 error = copyout(&vlr, ifr->ifr_data, sizeof vlr);
  633                 break;
  634         case SIOCSIFFLAGS:
  635                 /*
  636                  * For promiscuous mode, we enable promiscuous mode on
  637                  * the parent if we need promiscuous on the VLAN interface.
  638                  */
  639                 if (ifv->ifv_p != NULL)
  640                         error = vlan_set_promisc(ifp);
  641                 break;
  642 
  643         case SIOCADDMULTI:
  644                 error = (ifv->ifv_p != NULL) ?
  645                     vlan_ether_addmulti(ifv, ifr) : EINVAL;
  646                 break;
  647 
  648         case SIOCDELMULTI:
  649                 error = (ifv->ifv_p != NULL) ?
  650                     vlan_ether_delmulti(ifv, ifr) : EINVAL;
  651                 break;
  652         default:
  653                 error = EINVAL;
  654         }
  655         return error;
  656 }
  657 
  658 
  659 int
  660 vlan_ether_addmulti(struct ifvlan *ifv, struct ifreq *ifr)
  661 {
  662         struct ifnet *ifp = ifv->ifv_p;         /* Parent. */
  663         struct vlan_mc_entry *mc;
  664         u_int8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN];
  665         int error;
  666 
  667         /* XXX: sa_len is too small for such comparison
  668         if (ifr->ifr_addr.sa_len > sizeof(struct sockaddr_storage))
  669                 return (EINVAL);
  670         */
  671 
  672         error = ether_addmulti(ifr, (struct arpcom *)&ifv->ifv_ac);
  673         if (error != ENETRESET)
  674                 return (error);
  675 
  676         /*
  677          * This is new multicast address.  We have to tell parent
  678          * about it.  Also, remember this multicast address so that
  679          * we can delete them on unconfigure.
  680          */
  681         MALLOC(mc, struct vlan_mc_entry *, sizeof(struct vlan_mc_entry),
  682             M_DEVBUF, M_NOWAIT);
  683         if (mc == NULL) {
  684                 error = ENOMEM;
  685                 goto alloc_failed;
  686         }
  687 
  688         /*
  689          * As ether_addmulti() returns ENETRESET, following two
  690          * statement shouldn't fail.
  691          */
  692         (void)ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi);
  693         ETHER_LOOKUP_MULTI(addrlo, addrhi, &ifv->ifv_ac, mc->mc_enm);
  694         memcpy(&mc->mc_addr, &ifr->ifr_addr, ifr->ifr_addr.sa_len);
  695         LIST_INSERT_HEAD(&ifv->vlan_mc_listhead, mc, mc_entries);
  696 
  697         error = (*ifp->if_ioctl)(ifp, SIOCADDMULTI, (caddr_t)ifr);
  698         if (error != 0)
  699                 goto ioctl_failed;
  700 
  701         return (error);
  702 
  703  ioctl_failed:
  704         LIST_REMOVE(mc, mc_entries);
  705         FREE(mc, M_DEVBUF);
  706  alloc_failed:
  707         (void)ether_delmulti(ifr, (struct arpcom *)&ifv->ifv_ac);
  708 
  709         return (error);
  710 }
  711 
  712 int
  713 vlan_ether_delmulti(struct ifvlan *ifv, struct ifreq *ifr)
  714 {
  715         struct ifnet *ifp = ifv->ifv_p;         /* Parent. */
  716         struct ether_multi *enm;
  717         struct vlan_mc_entry *mc;
  718         u_int8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN];
  719         int error;
  720 
  721         /*
  722          * Find a key to lookup vlan_mc_entry.  We have to do this
  723          * before calling ether_delmulti for obvious reason.
  724          */
  725         if ((error = ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi)) != 0)
  726                 return (error);
  727         ETHER_LOOKUP_MULTI(addrlo, addrhi, &ifv->ifv_ac, enm);
  728         if (enm == NULL)
  729                 return (EINVAL);
  730 
  731         LIST_FOREACH(mc, &ifv->vlan_mc_listhead, mc_entries)
  732                 if (mc->mc_enm == enm)
  733                         break;
  734 
  735         /* We won't delete entries we didn't add */
  736         if (mc == NULL)
  737                 return (EINVAL);
  738 
  739         error = ether_delmulti(ifr, (struct arpcom *)&ifv->ifv_ac);
  740         if (error != ENETRESET)
  741                 return (error);
  742 
  743         /* We no longer use this multicast address.  Tell parent so. */
  744         error = (*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)ifr);
  745         if (error == 0) {
  746                 /* And forget about this address. */
  747                 LIST_REMOVE(mc, mc_entries);
  748                 FREE(mc, M_DEVBUF);
  749         } else
  750                 (void)ether_addmulti(ifr, (struct arpcom *)&ifv->ifv_ac);
  751         return (error);
  752 }
  753 
  754 /*
  755  * Delete any multicast address we have asked to add from parent
  756  * interface.  Called when the vlan is being unconfigured.
  757  */
  758 void
  759 vlan_ether_purgemulti(struct ifvlan *ifv)
  760 {
  761         struct ifnet *ifp = ifv->ifv_p;         /* Parent. */
  762         struct vlan_mc_entry *mc;
  763         union {
  764                 struct ifreq ifreq;
  765                 struct {
  766                         char ifr_name[IFNAMSIZ];
  767                         struct sockaddr_storage ifr_ss;
  768                 } ifreq_storage;
  769         } ifreq;
  770         struct ifreq *ifr = &ifreq.ifreq;
  771 
  772         memcpy(ifr->ifr_name, ifp->if_xname, IFNAMSIZ);
  773         while ((mc = LIST_FIRST(&ifv->vlan_mc_listhead)) != NULL) {
  774                 memcpy(&ifr->ifr_addr, &mc->mc_addr, mc->mc_addr.ss_len);
  775                 (void)(*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)ifr);
  776                 LIST_REMOVE(mc, mc_entries);
  777                 FREE(mc, M_DEVBUF);
  778         }
  779 }

/* [<][>][^][v][top][bottom][index][help] */