/*
- * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
- * The contents of this file constitute Original Code as defined in and
- * are subject to the Apple Public Source License Version 1.1 (the
- * "License"). You may not use this file except in compliance with the
- * License. Please obtain a copy of the License at
- * http://www.apple.com/publicsource and read it before using this file.
+ * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
*
- * This Original Code and all software distributed under the License are
- * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
- * License for the specific language governing rights and limitations
- * under the License.
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
+ * $FreeBSD: src/sys/net/if_vlan.c,v 1.54 2003/10/31 18:32:08 brooks Exp $
*/
/*
* we need to pretend to be enough of an Ethernet implementation
* to make arp work. The way we do this is by telling everyone
* that we are an Ethernet, and then catch the packets that
- * ether_output() left on our output queue queue when it calls
+ * ether_output() left on our output queue when it calls
* if_start(), rewrite them for use by the real outgoing interface,
* and ask it to send them.
- *
- *
- * XXX It's incorrect to assume that we must always kludge up
- * headers on the physical device's behalf: some devices support
- * VLAN tag insersion and extraction in firmware. For these cases,
- * one can change the behavior of the vlan interface by setting
- * the LINK0 flag on it (that is setting the vlan interface's LINK0
- * flag, _not_ the parent's LINK0 flag; we try to leave the parent
- * alone). If the interface as the LINK0 flag set, then it will
- * not modify the ethernet header on output because the parent
- * can do that for itself. On input, the parent can call vlan_input_tag()
- * directly in order to supply us with an incoming mbuf and the vlan
- * tag value that goes with it.
*/
-#include "vlan.h"
-#if NVLAN > 0
-#include "opt_inet.h"
-#include "bpfilter.h"
#include <sys/param.h>
#include <sys/kernel.h>
+#include <sys/malloc.h>
#include <sys/mbuf.h>
+#include <sys/queue.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/sysctl.h>
#include <sys/systm.h>
+#include <sys/kern_event.h>
-#if NBPFILTER > 0
#include <net/bpf.h>
-#endif
#include <net/ethernet.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <net/if_types.h>
#include <net/if_vlan_var.h>
-#if INET
+#include <net/dlil.h>
+
+#ifdef INET
#include <netinet/in.h>
#include <netinet/if_ether.h>
#endif
+#include <net/if_media.h>
+
+#define ETHER_VLAN_ENCAP_LEN 4 /* len of 802.1Q VLAN encapsulation */
+#define IF_MAXUNIT 0x7fff /* historical value */
+
+#define IFP2AC(p) ((struct arpcom *)p)
+
+#define VLAN_PROTO_FAMILY 0x766c616e /* 'vlan' */
+
+#define VLANNAME "vlan"
+
+typedef int (bpf_callback_func)(struct ifnet *, struct mbuf *);
+typedef int (if_set_bpf_tap_func)(struct ifnet *ifp, int mode, bpf_callback_func * func);
+
+struct vlan_mc_entry {
+ struct ether_addr mc_addr;
+ SLIST_ENTRY(vlan_mc_entry) mc_entries;
+};
+
+struct ifvlan {
+ char ifv_name[IFNAMSIZ]; /* our unique id */
+ struct ifnet *ifv_ifp; /* our interface */
+ struct ifnet *ifv_p; /* parent interface of this vlan */
+ struct ifv_linkmib {
+ int ifvm_parent;
+ int ifvm_encaplen; /* encapsulation length */
+ int ifvm_mtufudge; /* MTU fudged by this much */
+ int ifvm_mintu; /* min transmission unit */
+ u_int16_t ifvm_proto; /* encapsulation ethertype */
+ u_int16_t ifvm_tag; /* tag to apply on packets leaving if */
+ } ifv_mib;
+ SLIST_HEAD(__vlan_mchead, vlan_mc_entry) vlan_mc_listhead;
+ LIST_ENTRY(ifvlan) ifv_list;
+ int ifv_flags;
+ int ifv_detaching;
+ u_long ifv_filter_id;
+ int ifv_filter_valid;
+ bpf_callback_func * ifv_bpf_input;
+ bpf_callback_func * ifv_bpf_output;
+};
+
+#define ifv_tag ifv_mib.ifvm_tag
+#define ifv_encaplen ifv_mib.ifvm_encaplen
+#define ifv_mtufudge ifv_mib.ifvm_mtufudge
+#define ifv_mintu ifv_mib.ifvm_mintu
+
+#define IFVF_PROMISC 0x01 /* promiscuous mode enabled */
+
+#if 0
SYSCTL_DECL(_net_link);
-SYSCTL_NODE(_net_link, IFT_8021_VLAN, vlan, CTLFLAG_RW, 0, "IEEE 802.1Q VLAN");
+SYSCTL_NODE(_net_link, IFT_L2VLAN, vlan, CTLFLAG_RW, 0, "IEEE 802.1Q VLAN");
SYSCTL_NODE(_net_link_vlan, PF_LINK, link, CTLFLAG_RW, 0, "for consistency");
+#endif 0
-u_int vlan_proto = ETHERTYPE_VLAN;
-SYSCTL_INT(_net_link_vlan_link, VLANCTL_PROTO, proto, CTLFLAG_RW, &vlan_proto,
- 0, "Ethernet protocol used for VLAN encapsulation");
+#define M_VLAN M_DEVBUF
-static struct ifvlan ifv_softc[NVLAN];
+MALLOC_DEFINE(M_VLAN, VLANNAME, "802.1Q Virtual LAN Interface");
-static void vlan_start(struct ifnet *ifp);
+static LIST_HEAD(, ifvlan) ifv_list;
+
+#if 0
+/*
+ * Locking: one lock is used to guard both the ifv_list and modification
+ * to vlan data structures. We are rather conservative here; probably
+ * more than necessary.
+ */
+static struct mtx ifv_mtx;
+#define VLAN_LOCK_INIT() mtx_init(&ifv_mtx, VLANNAME, NULL, MTX_DEF)
+#define VLAN_LOCK_DESTROY() mtx_destroy(&ifv_mtx)
+#define VLAN_LOCK_ASSERT() mtx_assert(&ifv_mtx, MA_OWNED)
+#define VLAN_LOCK() mtx_lock(&ifv_mtx)
+#define VLAN_UNLOCK() mtx_unlock(&ifv_mtx)
+#else
+#define VLAN_LOCK_INIT()
+#define VLAN_LOCK_DESTROY()
+#define VLAN_LOCK_ASSERT()
+#define VLAN_LOCK()
+#define VLAN_UNLOCK()
+#endif 0
+
+static int vlan_clone_create(struct if_clone *, int);
+static void vlan_clone_destroy(struct ifnet *);
+static int vlan_output(struct ifnet *ifp, struct mbuf *m);
static void vlan_ifinit(void *foo);
-static int vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr);
+static int vlan_ioctl(struct ifnet *ifp, u_long cmd, void * addr);
+static int vlan_set_bpf_tap(struct ifnet * ifp, int mode,
+ bpf_callback_func * func);
+static int vlan_attach_protocol(struct ifnet *ifp);
+static int vlan_detach_protocol(struct ifnet *ifp);
+static int vlan_attach_filter(struct ifnet * ifp, u_long * filter_id);
+static int vlan_detach_filter(u_long filter_id);
static int vlan_setmulti(struct ifnet *ifp);
static int vlan_unconfig(struct ifnet *ifp);
-static int vlan_config(struct ifvlan *ifv, struct ifnet *p);
+static int vlan_config(struct ifvlan *ifv, struct ifnet *p, int tag);
+static int vlan_if_free(struct ifnet * ifp);
+
+static struct if_clone vlan_cloner = IF_CLONE_INITIALIZER(VLANNAME,
+ vlan_clone_create, vlan_clone_destroy, 0, IF_MAXUNIT);
+
+static if_set_bpf_tap_func nop_if_bpf;
+static int nop_if_free(struct ifnet *);
+static int nop_if_ioctl(struct ifnet *, u_long, void *);
+static int nop_if_output(struct ifnet * ifp, struct mbuf * m);
+
+static void interface_link_event(struct ifnet * ifp, u_long event_code);
+
+static __inline__ void
+vlan_bpf_output(struct ifnet * ifp, struct mbuf * m,
+ bpf_callback_func func)
+{
+ if (func != NULL) {
+ func(ifp, m);
+ }
+ return;
+}
+
+static __inline__ void
+vlan_bpf_input(struct ifnet * ifp, struct mbuf * m,
+ bpf_callback_func func, char * frame_header,
+ int frame_header_len, int encap_len)
+{
+ if (func != NULL) {
+ if (encap_len > 0) {
+ /* present the right header to bpf */
+ bcopy(frame_header, frame_header + encap_len, frame_header_len);
+ }
+ m->m_data -= frame_header_len;
+ m->m_len += frame_header_len;
+ func(ifp, m);
+ m->m_data += frame_header_len;
+ m->m_len -= frame_header_len;
+ if (encap_len > 0) {
+ /* restore the header */
+ bcopy(frame_header + encap_len, frame_header, frame_header_len);
+ }
+ }
+ return;
+}
+
+static struct ifaddr *
+ifaddr_byindex(unsigned int i)
+{
+ if (i > if_index || i == 0) {
+ return (NULL);
+ }
+ return (ifnet_addrs[i - 1]);
+}
/*
* Program our multicast filter. What we're actually doing is
* later by the upper protocol layers. Unfortunately, there's no way
* to avoid this: there really is only one physical interface.
*/
-static int vlan_setmulti(struct ifnet *ifp)
+static int
+vlan_setmulti(struct ifnet *ifp)
{
- struct ifnet *ifp_p;
- struct ifmultiaddr *ifma, *rifma = NULL;
- struct ifvlan *sc;
- struct vlan_mc_entry *mc = NULL;
- struct sockaddr_dl sdl;
- int error;
+ struct ifnet *p;
+ struct ifmultiaddr *ifma, *rifma = NULL;
+ struct ifvlan *sc;
+ struct vlan_mc_entry *mc = NULL;
+ struct sockaddr_dl sdl;
+ int error;
- /* Find the parent. */
- sc = ifp->if_softc;
- ifp_p = sc->ifv_p;
+ /* Find the parent. */
+ sc = ifp->if_private;
+ p = sc->ifv_p;
+ if (p == NULL) {
+ /* no parent, so no need to program the multicast filter */
+ return (0);
+ }
- sdl.sdl_len = ETHER_ADDR_LEN;
- sdl.sdl_family = AF_LINK;
+ bzero((char *)&sdl, sizeof sdl);
+ sdl.sdl_len = sizeof sdl;
+ sdl.sdl_family = AF_LINK;
+ sdl.sdl_index = p->if_index;
+ sdl.sdl_type = IFT_ETHER;
+ sdl.sdl_alen = ETHER_ADDR_LEN;
- /* First, remove any existing filter entries. */
- while(sc->vlan_mc_listhead.slh_first != NULL) {
- mc = sc->vlan_mc_listhead.slh_first;
- bcopy((char *)&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN);
- error = if_delmulti(ifp_p, (struct sockaddr *)&sdl);
- if (error)
- return(error);
- SLIST_REMOVE_HEAD(&sc->vlan_mc_listhead, mc_entries);
- FREE(mc, M_DEVBUF);
- }
+ /* First, remove any existing filter entries. */
+ while (SLIST_FIRST(&sc->vlan_mc_listhead) != NULL) {
+ mc = SLIST_FIRST(&sc->vlan_mc_listhead);
+ bcopy((char *)&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN);
+ error = if_delmulti(p, (struct sockaddr *)&sdl);
+ if (error)
+ return(error);
+ SLIST_REMOVE_HEAD(&sc->vlan_mc_listhead, mc_entries);
+ FREE(mc, M_VLAN);
+ }
+
+ /* Now program new ones. */
+ LIST_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+ mc = _MALLOC(sizeof(struct vlan_mc_entry), M_VLAN, M_WAITOK);
+ bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
+ (char *)&mc->mc_addr, ETHER_ADDR_LEN);
+ SLIST_INSERT_HEAD(&sc->vlan_mc_listhead, mc, mc_entries);
+ bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
+ LLADDR(&sdl), ETHER_ADDR_LEN);
+ error = if_addmulti(p, (struct sockaddr *)&sdl, &rifma);
+ if (error)
+ return(error);
+ }
- /* Now program new ones. */
- for (ifma = ifp->if_multiaddrs.lh_first;
- ifma != NULL;ifma = ifma->ifma_link.le_next) {
- if (ifma->ifma_addr->sa_family != AF_LINK)
- continue;
- mc = _MALLOC(sizeof(struct vlan_mc_entry), M_DEVBUF, M_WAITOK);
- if (mc == NULL)
- return (ENOMEM);
- bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
- (char *)&mc->mc_addr, ETHER_ADDR_LEN);
- SLIST_INSERT_HEAD(&sc->vlan_mc_listhead, mc, mc_entries);
- error = if_addmulti(ifp_p, (struct sockaddr *)&sdl, &rifma);
- if (error)
- return(error);
+ return(0);
+}
+
+#if 0
+/*
+ * VLAN support can be loaded as a module. The only place in the
+ * system that's intimately aware of this is ether_input. We hook
+ * into this code through vlan_input_p which is defined there and
+ * set here. Noone else in the system should be aware of this so
+ * we use an explicit reference here.
+ *
+ * NB: Noone should ever need to check if vlan_input_p is null or
+ * not. This is because interfaces have a count of the number
+ * of active vlans (if_nvlans) and this should never be bumped
+ * except by vlan_config--which is in this module so therefore
+ * the module must be loaded and vlan_input_p must be non-NULL.
+ */
+extern void (*vlan_input_p)(struct ifnet *, struct mbuf *);
+
+static int
+vlan_modevent(module_t mod, int type, void *data)
+{
+
+ switch (type) {
+ case MOD_LOAD:
+ LIST_INIT(&ifv_list);
+ VLAN_LOCK_INIT();
+ vlan_input_p = vlan_input;
+ if_clone_attach(&vlan_cloner);
+ break;
+ case MOD_UNLOAD:
+ if_clone_detach(&vlan_cloner);
+ vlan_input_p = NULL;
+ while (!LIST_EMPTY(&ifv_list))
+ vlan_clone_destroy(LIST_FIRST(&ifv_list)->ifv_ifp);
+ VLAN_LOCK_DESTROY();
+ break;
+ }
+ return 0;
+}
+
+static moduledata_t vlan_mod = {
+ "if_vlan",
+ vlan_modevent,
+ 0
+};
+
+DECLARE_MODULE(if_vlan, vlan_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
+
+#endif 0
+
+static struct ifvlan *
+vlan_lookup_ifp_and_tag(struct ifnet * ifp, int tag)
+{
+ struct ifvlan * ifv;
+
+ LIST_FOREACH(ifv, &ifv_list, ifv_list) {
+ if (ifp == ifv->ifv_p && tag == ifv->ifv_tag) {
+ return (ifv);
}
+ }
+ return (NULL);
+}
- return(0);
+static struct ifvlan *
+vlan_lookup_ifp(struct ifnet * ifp)
+{
+ struct ifvlan * ifv;
+
+ LIST_FOREACH(ifv, &ifv_list, ifv_list) {
+ if (ifp == ifv->ifv_p) {
+ return (ifv);
+ }
+ }
+ return (NULL);
}
static void
-vlaninit(void *dummy)
-{
- int i;
-
- for (i = 0; i < NVLAN; i++) {
- struct ifnet *ifp = &ifv_softc[i].ifv_if;
-
- ifp->if_softc = &ifv_softc[i];
- ifp->if_name = "vlan";
- ifp->if_family = APPLE_IF_FAM_VLAN;
- ifp->if_unit = i;
- /* NB: flags are not set here */
- ifp->if_linkmib = &ifv_softc[i].ifv_mib;
- ifp->if_linkmiblen = sizeof ifv_softc[i].ifv_mib;
- /* NB: mtu is not set here */
-
- ifp->if_init = vlan_ifinit;
- ifp->if_start = vlan_start;
- ifp->if_ioctl = vlan_ioctl;
- ifp->if_output = ether_output;
- ifp->if_snd.ifq_maxlen = ifqmaxlen;
- if_attach(ifp);
- ether_ifattach(ifp);
-#if NBPFILTER > 0
- bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header));
-#endif
- /* Now undo some of the damage... */
- ifp->if_data.ifi_type = IFT_8021_VLAN;
- ifp->if_data.ifi_hdrlen = EVL_ENCAPLEN;
- ifp->if_resolvemulti = 0;
- }
+vlan_clone_attach(void)
+{
+ if_clone_attach(&vlan_cloner);
+ return;
+}
+
+static int
+vlan_clone_create(struct if_clone *ifc, int unit)
+{
+ int error;
+ struct ifvlan *ifv;
+ struct ifnet *ifp;
+
+ ifv = _MALLOC(sizeof(struct ifvlan), M_VLAN, M_WAITOK);
+ bzero(ifv, sizeof(struct ifvlan));
+ SLIST_INIT(&ifv->vlan_mc_listhead);
+
+ /* use the interface name as the unique id for ifp recycle */
+ if (snprintf(ifv->ifv_name, sizeof(ifv->ifv_name), "%s%d",
+ ifc->ifc_name, unit) >= sizeof(ifv->ifv_name)) {
+ FREE(ifv, M_VLAN);
+ return (EINVAL);
+ }
+ error = dlil_if_acquire(APPLE_IF_FAM_VLAN,
+ ifv->ifv_name,
+ strlen(ifv->ifv_name),
+ &ifp);
+ if (error) {
+ FREE(ifv, M_VLAN);
+ return (error);
+ }
+ ifv->ifv_ifp = ifp;
+ ifp->if_private = ifv;
+ ifp->if_name = (char *)ifc->ifc_name;
+ ifp->if_unit = unit;
+ ifp->if_family = APPLE_IF_FAM_VLAN;
+
+#if 0
+ /* NB: flags are not set here */
+ ifp->if_linkmib = &ifv->ifv_mib;
+ ifp->if_linkmiblen = sizeof ifv->ifv_mib;
+ /* NB: mtu is not set here */
+#endif 0
+
+ ifp->if_ioctl = vlan_ioctl;
+ ifp->if_set_bpf_tap = vlan_set_bpf_tap;
+ ifp->if_free = nop_if_free;
+ ifp->if_output = nop_if_output;
+ ifp->if_hwassist = 0;
+ ifp->if_addrlen = ETHER_ADDR_LEN; /* XXX ethernet specific */
+ ifp->if_baudrate = 0;
+ ifp->if_type = IFT_L2VLAN;
+ ifp->if_hdrlen = ETHER_VLAN_ENCAP_LEN;
+ error = dlil_if_attach(ifp);
+ if (error) {
+ dlil_if_release(ifp);
+ FREE(ifv, M_VLAN);
+ return (error);
+ }
+
+ /* attach as ethernet */
+ bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header));
+
+ VLAN_LOCK();
+ LIST_INSERT_HEAD(&ifv_list, ifv, ifv_list);
+ VLAN_UNLOCK();
+
+ return (0);
}
-PSEUDO_SET(vlaninit, if_vlan);
static void
-vlan_ifinit(void *foo)
+vlan_remove(struct ifvlan * ifv)
{
- return;
+ VLAN_LOCK_ASSERT();
+ ifv->ifv_detaching = 1;
+ vlan_unconfig(ifv->ifv_ifp);
+ LIST_REMOVE(ifv, ifv_list);
+ return;
}
static void
-vlan_start(struct ifnet *ifp)
-{
- struct ifvlan *ifv;
- struct ifnet *p;
- struct ether_vlan_header *evl;
- struct mbuf *m;
-
- ifv = ifp->if_softc;
- p = ifv->ifv_p;
-
- ifp->if_flags |= IFF_OACTIVE;
- for (;;) {
- IF_DEQUEUE(&ifp->if_snd, m);
- if (m == 0)
- break;
-#if NBPFILTER > 0
- if (ifp->if_bpf)
- bpf_mtap(ifp, m);
-#endif /* NBPFILTER > 0 */
-
- /*
- * If the LINK0 flag is set, it means the underlying interface
- * can do VLAN tag insertion itself and doesn't require us to
- * create a special header for it. In this case, we just pass
- * the packet along. However, we need some way to tell the
- * interface where the packet came from so that it knows how
- * to find the VLAN tag to use, so we set the rcvif in the
- * mbuf header to our ifnet.
- *
- * Note: we also set the M_PROTO1 flag in the mbuf to let
- * the parent driver know that the rcvif pointer is really
- * valid. We need to do this because sometimes mbufs will
- * be allocated by other parts of the system that contain
- * garbage in the rcvif pointer. Using the M_PROTO1 flag
- * lets the driver perform a proper sanity check and avoid
- * following potentially bogus rcvif pointers off into
- * never-never land.
- */
- if (ifp->if_flags & IFF_LINK0) {
- m->m_pkthdr.rcvif = ifp;
- m->m_flags |= M_PROTO1;
- } else {
- M_PREPEND(m, EVL_ENCAPLEN, M_DONTWAIT);
- if (m == 0)
- continue;
- /* M_PREPEND takes care of m_len, m_pkthdr.len for us */
-
- /*
- * Transform the Ethernet header into an Ethernet header
- * with 802.1Q encapsulation.
- */
- bcopy(mtod(m, char *) + EVL_ENCAPLEN, mtod(m, char *),
- sizeof(struct ether_header));
- evl = mtod(m, struct ether_vlan_header *);
- evl->evl_proto = evl->evl_encap_proto;
- evl->evl_encap_proto = htons(vlan_proto);
- evl->evl_tag = htons(ifv->ifv_tag);
-#ifdef DEBUG
- printf("vlan_start: %*D\n", sizeof *evl,
- (char *)evl, ":");
-#endif
- }
+vlan_if_detach(struct ifnet * ifp)
+{
+ ifp->if_output = nop_if_output;
+ ifp->if_ioctl = nop_if_ioctl;
+ ifp->if_set_bpf_tap = &nop_if_bpf;
+ if (dlil_if_detach(ifp) == DLIL_WAIT_FOR_FREE) {
+ ifp->if_free = vlan_if_free;
+ } else {
+ vlan_if_free(ifp);
+ }
+ return;
+}
- /*
- * Send it, precisely as ether_output() would have.
- * We are already running at splimp.
- */
- if (IF_QFULL(&p->if_snd)) {
- IF_DROP(&p->if_snd);
- /* XXX stats */
- ifp->if_oerrors++;
- m_freem(m);
- continue;
- }
- IF_ENQUEUE(&p->if_snd, m);
- if ((p->if_flags & IFF_OACTIVE) == 0) {
- p->if_start(p);
- ifp->if_opackets++;
- }
- }
- ifp->if_flags &= ~IFF_OACTIVE;
+static void
+vlan_clone_destroy(struct ifnet *ifp)
+{
+ struct ifvlan *ifv = ifp->if_private;
+ if (ifv == NULL || ifp->if_type != IFT_L2VLAN) {
+ return;
+ }
+ VLAN_LOCK();
+ if (ifv->ifv_detaching) {
+ VLAN_UNLOCK();
return;
+ }
+ vlan_remove(ifv);
+ VLAN_UNLOCK();
+ vlan_if_detach(ifp);
+ return;
}
-void
-vlan_input_tag(struct ether_header *eh, struct mbuf *m, u_int16_t t)
+static int
+vlan_set_bpf_tap(struct ifnet * ifp, int mode, bpf_callback_func * func)
{
- int i;
- struct ifvlan *ifv;
+ struct ifvlan *ifv = ifp->if_private;
- for (i = 0; i < NVLAN; i++) {
- ifv = &ifv_softc[i];
- if (ifv->ifv_tag == t)
- break;
- }
+ switch (mode) {
+ case BPF_TAP_DISABLE:
+ ifv->ifv_bpf_input = ifv->ifv_bpf_output = NULL;
+ break;
- if (i >= NVLAN || (ifv->ifv_if.if_flags & IFF_UP) == 0) {
- m_freem(m);
- ifv->ifv_p->if_data.ifi_noproto++;
- return;
- }
+ case BPF_TAP_INPUT:
+ ifv->ifv_bpf_input = func;
+ break;
+
+ case BPF_TAP_OUTPUT:
+ ifv->ifv_bpf_output = func;
+ break;
+
+ case BPF_TAP_INPUT_OUTPUT:
+ ifv->ifv_bpf_input = ifv->ifv_bpf_output = func;
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static void
+vlan_ifinit(void *foo)
+{
+ return;
+}
+
+static int
+vlan_output(struct ifnet *ifp, struct mbuf *m)
+{
+ struct ifvlan *ifv;
+ struct ifnet *p;
+ struct ether_vlan_header *evl;
+ int soft_vlan;
+ ifv = ifp->if_private;
+ p = ifv->ifv_p;
+ if (p == NULL) {
+ return (nop_if_output(ifp, m));
+ }
+ if (m == 0) {
+ printf("%s: NULL output mbuf\n", ifv->ifv_name);
+ return (EINVAL);
+ }
+ if ((m->m_flags & M_PKTHDR) == 0) {
+ printf("%s: M_PKTHDR bit not set\n", ifv->ifv_name);
+ m_freem(m);
+ return (EINVAL);
+ }
+ ifp->if_obytes += m->m_pkthdr.len;
+ ifp->if_opackets++;
+ soft_vlan = (p->if_hwassist & IF_HWASSIST_VLAN_TAGGING) == 0;
+ vlan_bpf_output(ifp, m, ifv->ifv_bpf_output);
+
+ /* do not run parent's if_output() if the parent is not up */
+ if ((p->if_flags & (IFF_UP | IFF_RUNNING)) != (IFF_UP | IFF_RUNNING)) {
+ m_freem(m);
+ ifp->if_collisions++;
+ return (0);
+ }
+ /*
+ * If underlying interface can do VLAN tag insertion itself,
+ * just pass the packet along. However, we need some way to
+ * tell the interface where the packet came from so that it
+ * knows how to find the VLAN tag to use. We use a field in
+ * the mbuf header to store the VLAN tag, and a bit in the
+ * csum_flags field to mark the field as valid.
+ */
+ if (soft_vlan == 0) {
+ m->m_pkthdr.csum_flags |= CSUM_VLAN_TAG_VALID;
+ m->m_pkthdr.vlan_tag = ifv->ifv_tag;
+ } else {
+ M_PREPEND(m, ifv->ifv_encaplen, M_DONTWAIT);
+ if (m == NULL) {
+ printf("%s: unable to prepend VLAN header\n",
+ ifv->ifv_name);
+ ifp->if_ierrors++;
+ return (0);
+ }
+ /* M_PREPEND takes care of m_len, m_pkthdr.len for us */
+ if (m->m_len < sizeof(*evl)) {
+ m = m_pullup(m, sizeof(*evl));
+ if (m == NULL) {
+ printf("%s: cannot pullup VLAN header\n",
+ ifv->ifv_name);
+ ifp->if_ierrors++;
+ return (0);
+ }
+ }
+
/*
- * Having found a valid vlan interface corresponding to
- * the given source interface and vlan tag, run the
- * the real packet through ethert_input().
+ * Transform the Ethernet header into an Ethernet header
+ * with 802.1Q encapsulation.
*/
- m->m_pkthdr.rcvif = &ifv->ifv_if;
-
-#if NBPFILTER > 0
- if (ifv->ifv_if.if_bpf) {
- /*
- * Do the usual BPF fakery. Note that we don't support
- * promiscuous mode here, since it would require the
- * drivers to know about VLANs and we're not ready for
- * that yet.
- */
- struct mbuf m0;
- m0.m_next = m;
- m0.m_len = sizeof(struct ether_header);
- m0.m_data = (char *)eh;
- bpf_mtap(&ifv->ifv_if, &m0);
- }
-#endif
- ifv->ifv_if.if_ipackets++;
- ether_input(&ifv->ifv_if, eh, m);
- return;
+ bcopy(mtod(m, char *) + ifv->ifv_encaplen,
+ mtod(m, char *), ETHER_HDR_LEN);
+ evl = mtod(m, struct ether_vlan_header *);
+ evl->evl_proto = evl->evl_encap_proto;
+ evl->evl_encap_proto = htons(ETHERTYPE_VLAN);
+ evl->evl_tag = htons(ifv->ifv_tag);
+ m->m_pkthdr.len += ifv->ifv_encaplen;
+ }
+
+ /*
+ * Send it, precisely as ether_output() would have.
+ * We are already running at splimp.
+ */
+ return ((*p->if_output)(p, m));
}
-int
-vlan_input(struct ether_header *eh, struct mbuf *m)
+extern int
+vlan_demux(struct ifnet * ifp, struct mbuf * m,
+ char * frame_header, struct if_proto * * proto)
{
- int i;
- struct ifvlan *ifv;
+ register struct ether_header *eh = (struct ether_header *)frame_header;
+ struct ether_vlan_header *evl;
+ struct ifvlan *ifv = NULL;
+ int soft_vlan = 0;
+ u_int tag;
- for (i = 0; i < NVLAN; i++) {
- ifv = &ifv_softc[i];
- if (m->m_pkthdr.rcvif == ifv->ifv_p
- && (EVL_VLANOFTAG(ntohs(*mtod(m, u_int16_t *)))
- == ifv->ifv_tag))
- break;
- }
+ if (m->m_pkthdr.csum_flags & CSUM_VLAN_TAG_VALID) {
+ /*
+ * Packet is tagged, m contains a normal
+ * Ethernet frame; the tag is stored out-of-band.
+ */
+ m->m_pkthdr.csum_flags &= ~CSUM_VLAN_TAG_VALID;
+ tag = EVL_VLANOFTAG(m->m_pkthdr.vlan_tag);
+ m->m_pkthdr.vlan_tag = 0;
+ } else {
+ soft_vlan = 1;
- if (i >= NVLAN || (ifv->ifv_if.if_flags & IFF_UP) == 0) {
+ switch (ifp->if_type) {
+ case IFT_ETHER:
+ if (m->m_len < ETHER_VLAN_ENCAP_LEN) {
m_freem(m);
- return -1; /* so ether_input can take note */
- }
+ return (EJUSTRETURN);
+ }
+ evl = (struct ether_vlan_header *)frame_header;
+ if (ntohs(evl->evl_proto) == ETHERTYPE_VLAN) {
+ /* don't allow VLAN within VLAN */
+ m_freem(m);
+ return (EJUSTRETURN);
+ }
+ tag = EVL_VLANOFTAG(ntohs(evl->evl_tag));
+ /*
+ * Restore the original ethertype. We'll remove
+ * the encapsulation after we've found the vlan
+ * interface corresponding to the tag.
+ */
+ evl->evl_encap_proto = evl->evl_proto;
+ break;
+ default:
+ printf("vlan_demux: unsupported if type %u",
+ ifp->if_type);
+ m_freem(m);
+ return (EJUSTRETURN);
+ break;
+ }
+ }
+ if (tag != 0) {
+ if (ifp->if_nvlans == 0) {
+ /* don't bother looking through the VLAN list */
+ m_freem(m);
+ ifp->if_noproto++;
+ return (EJUSTRETURN);
+ }
+ VLAN_LOCK();
+ ifv = vlan_lookup_ifp_and_tag(ifp, tag);
+ if (ifv == NULL || (ifv->ifv_ifp->if_flags & IFF_UP) == 0) {
+ VLAN_UNLOCK();
+ m_freem(m);
+ ifp->if_noproto++;
+ return (EJUSTRETURN);
+ }
+ VLAN_UNLOCK(); /* XXX extend below? */
+ }
+ if (soft_vlan) {
/*
- * Having found a valid vlan interface corresponding to
- * the given source interface and vlan tag, remove the
- * encapsulation, and run the real packet through
- * ether_input() a second time (it had better be
- * reentrant!).
+ * Packet had an in-line encapsulation header;
+ * remove it. The original header has already
+ * been fixed up above.
*/
- m->m_pkthdr.rcvif = &ifv->ifv_if;
- eh->ether_type = mtod(m, u_int16_t *)[1];
- m->m_data += EVL_ENCAPLEN;
- m->m_len -= EVL_ENCAPLEN;
- m->m_pkthdr.len -= EVL_ENCAPLEN;
-
-#if NBPFILTER > 0
- if (ifv->ifv_if.if_bpf) {
- /*
- * Do the usual BPF fakery. Note that we don't support
- * promiscuous mode here, since it would require the
- * drivers to know about VLANs and we're not ready for
- * that yet.
- */
- struct mbuf m0;
- m0.m_next = m;
- m0.m_len = sizeof(struct ether_header);
- m0.m_data = (char *)eh;
- bpf_mtap(&ifv->ifv_if, &m0);
- }
-#endif
- ifv->ifv_if.if_ipackets++;
- ether_input(&ifv->ifv_if, eh, m);
- return 0;
+ m->m_len -= ETHER_VLAN_ENCAP_LEN;
+ m->m_data += ETHER_VLAN_ENCAP_LEN;
+ m->m_pkthdr.len -= ETHER_VLAN_ENCAP_LEN;
+ m->m_pkthdr.csum_flags = 0; /* can't trust hardware checksum */
+ }
+ if (tag != 0) {
+ /* we found a vlan interface above, so send it up */
+ m->m_pkthdr.rcvif = ifv->ifv_ifp;
+ ifv->ifv_ifp->if_ipackets++;
+ ifv->ifv_ifp->if_ibytes += m->m_pkthdr.len;
+
+ vlan_bpf_input(ifv->ifv_ifp, m, ifv->ifv_bpf_input, frame_header,
+ ETHER_HDR_LEN, soft_vlan ? ETHER_VLAN_ENCAP_LEN : 0);
+
+ /* Pass it back through the parent's demux routine. */
+ return ((*ifp->if_demux)(ifv->ifv_ifp, m, frame_header, proto));
+ }
+ /* Pass it back through calling demux routine. */
+ return ((*ifp->if_demux)(ifp, m, frame_header, proto));
}
static int
-vlan_config(struct ifvlan *ifv, struct ifnet *p)
+vlan_config(struct ifvlan *ifv, struct ifnet *p, int tag)
{
- struct ifaddr *ifa1, *ifa2;
- struct sockaddr_dl *sdl1, *sdl2;
+ struct ifnet * ifp;
+ struct ifaddr *ifa1, *ifa2;
+ struct sockaddr_dl *sdl1, *sdl2;
+ int supports_vlan_mtu = 0;
- if (p->if_data.ifi_type != IFT_ETHER)
- return EPROTONOSUPPORT;
- if (ifv->ifv_p)
- return EBUSY;
- ifv->ifv_p = p;
- if (p->if_data.ifi_hdrlen == sizeof(struct ether_vlan_header))
- ifv->ifv_if.if_mtu = p->if_mtu;
- else
- ifv->ifv_if.if_mtu = p->if_data.ifi_mtu - EVL_ENCAPLEN;
+ VLAN_LOCK_ASSERT();
+ if (p->if_data.ifi_type != IFT_ETHER)
+ return EPROTONOSUPPORT;
+ if (ifv->ifv_p != NULL || ifv->ifv_detaching) {
+ return EBUSY;
+ }
+ if (vlan_lookup_ifp_and_tag(p, tag) != NULL) {
+ /* already a VLAN with that tag on this interface */
+ return (EADDRINUSE);
+ }
+ ifp = ifv->ifv_ifp;
+ ifv->ifv_encaplen = ETHER_VLAN_ENCAP_LEN;
+ ifv->ifv_mintu = ETHERMIN;
+ ifv->ifv_flags = 0;
- /*
- * Preserve the state of the LINK0 flag for ourselves.
- */
- ifv->ifv_if.if_flags = (p->if_flags & ~(IFF_LINK0));
+ /*
+ * If the parent supports the VLAN_MTU capability,
+ * i.e. can Tx/Rx larger than ETHER_MAX_LEN frames,
+ * enable it.
+ */
+ if (p->if_hwassist & (IF_HWASSIST_VLAN_MTU | IF_HWASSIST_VLAN_TAGGING)) {
+ supports_vlan_mtu = 1;
+ }
+ if (p->if_nvlans == 0) {
+ u_long dltag;
+ u_long filter_id;
+ int error;
+
+ /* attach our VLAN "interface filter" to the interface */
+ error = vlan_attach_filter(p, &filter_id);
+ if (error) {
+ return (error);
+ }
+
+ /* attach our VLAN "protocol" to the interface */
+ error = vlan_attach_protocol(p);
+ if (error) {
+ (void)vlan_detach_filter(filter_id);
+ return (error);
+ }
+ ifv->ifv_filter_id = filter_id;
+ ifv->ifv_filter_valid = TRUE;
+#if 0
+ if (supports_vlan_mtu) {
+ /*
+ * Enable Tx/Rx of VLAN-sized frames.
+ */
+ p->if_capenable |= IFCAP_VLAN_MTU;
+ if (p->if_flags & IFF_UP) {
+ struct ifreq ifr;
+ int error;
+
+ ifr.ifr_flags = p->if_flags;
+ error = (*p->if_ioctl)(p, SIOCSIFFLAGS,
+ (caddr_t) &ifr);
+ if (error) {
+ if (p->if_nvlans == 0)
+ p->if_capenable &= ~IFCAP_VLAN_MTU;
+ return (error);
+ }
+ }
+ }
+#endif 0
+ } else {
+ struct ifvlan * other_ifv;
+ other_ifv = vlan_lookup_ifp(p);
+ if (other_ifv == NULL) {
+ printf("vlan: other_ifv can't be NULL\n");
+ return (EINVAL);
+ }
+ ifv->ifv_filter_id = other_ifv->ifv_filter_id;
+ ifv->ifv_filter_valid = TRUE;
+ }
+ p->if_nvlans++;
+ if (supports_vlan_mtu) {
+ ifv->ifv_mtufudge = 0;
+ } else {
/*
- * Set up our ``Ethernet address'' to reflect the underlying
- * physical interface's.
+ * Fudge the MTU by the encapsulation size. This
+ * makes us incompatible with strictly compliant
+ * 802.1Q implementations, but allows us to use
+ * the feature with other NetBSD implementations,
+ * which might still be useful.
*/
- ifa1 = ifnet_addrs[ifv->ifv_if.if_index - 1];
- ifa2 = ifnet_addrs[p->if_index - 1];
- sdl1 = (struct sockaddr_dl *)ifa1->ifa_addr;
- sdl2 = (struct sockaddr_dl *)ifa2->ifa_addr;
- sdl1->sdl_type = IFT_ETHER;
- sdl1->sdl_alen = ETHER_ADDR_LEN;
- bcopy(LLADDR(sdl2), LLADDR(sdl1), ETHER_ADDR_LEN);
- bcopy(LLADDR(sdl2), ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN);
- return 0;
+ ifv->ifv_mtufudge = ifv->ifv_encaplen;
+ }
+
+ ifv->ifv_p = p;
+ ifp->if_mtu = p->if_mtu - ifv->ifv_mtufudge;
+ /*
+ * Copy only a selected subset of flags from the parent.
+ * Other flags are none of our business.
+ */
+ ifp->if_flags |= (p->if_flags &
+ (IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX));
+ /*
+ * If the parent interface can do hardware-assisted
+ * VLAN encapsulation, then propagate its hardware-
+ * assisted checksumming flags.
+ */
+ if (p->if_hwassist & IF_HWASSIST_VLAN_TAGGING) {
+ ifp->if_hwassist |= IF_HWASSIST_CSUM_FLAGS(p->if_hwassist);
+ }
+ /*
+ * Set up our ``Ethernet address'' to reflect the underlying
+ * physical interface's.
+ */
+ ifa1 = ifaddr_byindex(ifp->if_index);
+ ifa2 = ifaddr_byindex(p->if_index);
+ sdl1 = (struct sockaddr_dl *)ifa1->ifa_addr;
+ sdl2 = (struct sockaddr_dl *)ifa2->ifa_addr;
+ sdl1->sdl_type = IFT_ETHER;
+ sdl1->sdl_alen = ETHER_ADDR_LEN;
+ bcopy(LLADDR(sdl2), LLADDR(sdl1), ETHER_ADDR_LEN);
+ bcopy(LLADDR(sdl2), IFP2AC(ifp)->ac_enaddr, ETHER_ADDR_LEN);
+
+ /*
+ * Configure multicast addresses that may already be
+ * joined on the vlan device.
+ */
+ (void)vlan_setmulti(ifp);
+ ifp->if_output = vlan_output;
+ ifv->ifv_tag = tag;
+
+ return 0;
+}
+
+static void
+vlan_link_event(struct ifnet * ifp, struct ifnet * p)
+{
+ struct ifmediareq ifmr;
+
+ /* generate a link event based on the state of the underlying interface */
+ bzero(&ifmr, sizeof(ifmr));
+ snprintf(ifmr.ifm_name, sizeof(ifmr.ifm_name),
+ "%s%d", p->if_name, p->if_unit);
+ if ((*p->if_ioctl)(p, SIOCGIFMEDIA, (caddr_t)&ifmr) == 0
+ && ifmr.ifm_count > 0 && ifmr.ifm_status & IFM_AVALID) {
+ u_long event;
+
+ event = (ifmr.ifm_status & IFM_ACTIVE)
+ ? KEV_DL_LINK_ON : KEV_DL_LINK_OFF;
+ interface_link_event(ifp, event);
+ }
+ return;
}
static int
vlan_unconfig(struct ifnet *ifp)
{
- struct ifaddr *ifa;
- struct sockaddr_dl *sdl;
- struct vlan_mc_entry *mc;
- struct ifvlan *ifv;
- struct ifnet *p;
- int error;
+ struct ifaddr *ifa;
+ struct sockaddr_dl *sdl;
+ struct vlan_mc_entry *mc;
+ struct ifvlan *ifv;
+ struct ifnet *p;
+ int error;
+
+ VLAN_LOCK_ASSERT();
+
+ ifv = ifp->if_private;
- ifv = ifp->if_softc;
- p = ifv->ifv_p;
+ /* Disconnect from parent. */
+ p = ifv->ifv_p;
+ ifv->ifv_p = NULL;
+
+ if (p != NULL) {
+ struct sockaddr_dl sdl;
/*
- * Since the interface is being unconfigured, we need to
+ * Since the interface is being unconfigured, we need to
* empty the list of multicast groups that we may have joined
- * while we were alive and remove them from the parent's list
- * as well.
+ * while we were alive from the parent's list.
*/
- while(ifv->vlan_mc_listhead.slh_first != NULL) {
- struct sockaddr_dl sdl;
-
- sdl.sdl_len = ETHER_ADDR_LEN;
- sdl.sdl_family = AF_LINK;
- mc = ifv->vlan_mc_listhead.slh_first;
- bcopy((char *)&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN);
- error = if_delmulti(p, (struct sockaddr *)&sdl);
- error = if_delmulti(ifp, (struct sockaddr *)&sdl);
- if (error)
- return(error);
- SLIST_REMOVE_HEAD(&ifv->vlan_mc_listhead, mc_entries);
- FREE(mc, M_DEVBUF);
+ bzero((char *)&sdl, sizeof sdl);
+ sdl.sdl_len = sizeof sdl;
+ sdl.sdl_family = AF_LINK;
+ sdl.sdl_index = p->if_index;
+ sdl.sdl_type = IFT_ETHER;
+ sdl.sdl_alen = ETHER_ADDR_LEN;
+
+ while (SLIST_FIRST(&ifv->vlan_mc_listhead) != NULL) {
+ mc = SLIST_FIRST(&ifv->vlan_mc_listhead);
+ bcopy((char *)&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN);
+ error = if_delmulti(p, (struct sockaddr *)&sdl);
+ if (error) {
+ printf("vlan_unconfig: if_delmulti %s failed, %d\n",
+ ifv->ifv_name, error);
+ }
+ SLIST_REMOVE_HEAD(&ifv->vlan_mc_listhead, mc_entries);
+ FREE(mc, M_VLAN);
+ }
+ p->if_nvlans--;
+ if (p->if_nvlans == 0) {
+ /* detach our VLAN "protocol" from the interface */
+ if (ifv->ifv_filter_valid) {
+ (void)vlan_detach_filter(ifv->ifv_filter_id);
+ }
+ (void)vlan_detach_protocol(p);
+#if 0
+ /*
+ * Disable Tx/Rx of VLAN-sized frames.
+ */
+ p->if_capenable &= ~IFCAP_VLAN_MTU;
+ if (p->if_flags & IFF_UP) {
+ struct ifreq ifr;
+
+ ifr.ifr_flags = p->if_flags;
+ (*p->if_ioctl)(p, SIOCSIFFLAGS, (caddr_t) &ifr);
+ }
+#endif 0
}
+ }
- /* Disconnect from parent. */
- ifv->ifv_p = NULL;
- ifv->ifv_if.if_mtu = ETHERMTU;
+ /* return to the state we were in before SETVLAN */
+ ifp->if_mtu = 0;
+ ifp->if_flags &= ~(IFF_BROADCAST | IFF_MULTICAST
+ | IFF_SIMPLEX | IFF_RUNNING);
+ ifv->ifv_ifp->if_hwassist = 0;
+ ifv->ifv_flags = 0;
+ ifv->ifv_ifp->if_output = nop_if_output;
+ ifv->ifv_mtufudge = 0;
+ ifv->ifv_filter_valid = FALSE;
- /* Clear our MAC address. */
- ifa = ifnet_addrs[ifv->ifv_if.if_index - 1];
- sdl = (struct sockaddr_dl *)ifa->ifa_addr;
- sdl->sdl_type = IFT_ETHER;
- sdl->sdl_alen = ETHER_ADDR_LEN;
- bzero(LLADDR(sdl), ETHER_ADDR_LEN);
- bzero(ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN);
+ /* Clear our MAC address. */
+ ifa = ifaddr_byindex(ifv->ifv_ifp->if_index);
+ sdl = (struct sockaddr_dl *)(ifa->ifa_addr);
+ sdl->sdl_type = IFT_L2VLAN;
+ sdl->sdl_alen = 0;
+ bzero(LLADDR(sdl), ETHER_ADDR_LEN);
+ bzero(IFP2AC(ifv->ifv_ifp)->ac_enaddr, ETHER_ADDR_LEN);
- return 0;
+ /* send a link down event */
+ if (p != NULL) {
+ interface_link_event(ifv->ifv_ifp, KEV_DL_LINK_OFF);
+ }
+ return 0;
}
static int
-vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
-{
- struct ifaddr *ifa;
- struct ifnet *p;
- struct ifreq *ifr;
- struct ifvlan *ifv;
- struct vlanreq vlr;
- int error = 0;
-
- ifr = (struct ifreq *)data;
- ifa = (struct ifaddr *)data;
- ifv = ifp->if_softc;
-
- switch (cmd) {
- case SIOCSIFADDR:
- ifp->if_flags |= IFF_UP;
-
- switch (ifa->ifa_addr->sa_family) {
-#if INET
- case AF_INET:
- arp_ifinit(&ifv->ifv_ac, ifa);
- break;
-#endif
- default:
- break;
- }
- break;
+vlan_set_promisc(struct ifnet *ifp)
+{
+ struct ifvlan *ifv = ifp->if_private;
+ int error = 0;
- case SIOCGIFADDR:
- {
- struct sockaddr *sa;
+ if ((ifp->if_flags & IFF_PROMISC) != 0) {
+ if ((ifv->ifv_flags & IFVF_PROMISC) == 0) {
+ error = ifpromisc(ifv->ifv_p, 1);
+ if (error == 0)
+ ifv->ifv_flags |= IFVF_PROMISC;
+ }
+ } else {
+ if ((ifv->ifv_flags & IFVF_PROMISC) != 0) {
+ error = ifpromisc(ifv->ifv_p, 0);
+ if (error == 0)
+ ifv->ifv_flags &= ~IFVF_PROMISC;
+ }
+ }
- sa = (struct sockaddr *) &ifr->ifr_data;
- bcopy(((struct arpcom *)ifp->if_softc)->ac_enaddr,
- (caddr_t) sa->sa_data, ETHER_ADDR_LEN);
- }
- break;
-
- case SIOCSIFMTU:
- /*
- * Set the interface MTU.
- * This is bogus. The underlying interface might support
- * jumbo frames.
- */
- if (ifr->ifr_mtu > ETHERMTU) {
- error = EINVAL;
- } else {
- ifp->if_mtu = ifr->ifr_mtu;
- }
- break;
-
- case SIOCSETVLAN:
- error = copyin(ifr->ifr_data, &vlr, sizeof vlr);
- if (error)
- break;
- if (vlr.vlr_parent[0] == '\0') {
- vlan_unconfig(ifp);
- if_down(ifp);
- ifp->if_flags = 0;
- break;
- }
- p = ifunit(vlr.vlr_parent);
- if (p == 0) {
- error = ENOENT;
- break;
+ return (error);
+}
+
+static int
+vlan_ioctl(struct ifnet *ifp, u_long cmd, void * data)
+{
+ struct ifaddr *ifa;
+ struct ifnet *p;
+ struct ifreq *ifr;
+ struct ifvlan *ifv;
+ struct vlanreq vlr;
+ int error = 0;
+
+ ifr = (struct ifreq *)data;
+ ifa = (struct ifaddr *)data;
+ ifv = (struct ifvlan *)ifp->if_private;
+
+ switch (cmd) {
+ case SIOCSIFADDR:
+ ifp->if_flags |= IFF_UP;
+ break;
+
+ case SIOCGIFMEDIA:
+ VLAN_LOCK();
+ if (ifv->ifv_p != NULL) {
+ error = (*ifv->ifv_p->if_ioctl)(ifv->ifv_p,
+ SIOCGIFMEDIA, data);
+ VLAN_UNLOCK();
+ /* Limit the result to the parent's current config. */
+ if (error == 0) {
+ struct ifmediareq *ifmr;
+
+ ifmr = (struct ifmediareq *) data;
+ if (ifmr->ifm_count >= 1 && ifmr->ifm_ulist) {
+ ifmr->ifm_count = 1;
+ error = copyout(&ifmr->ifm_current,
+ ifmr->ifm_ulist,
+ sizeof(int));
}
- error = vlan_config(ifv, p);
- if (error)
- break;
- ifv->ifv_tag = vlr.vlr_tag;
- break;
+ }
+ } else {
+ struct ifmediareq *ifmr;
+ VLAN_UNLOCK();
+
+ ifmr = (struct ifmediareq *) data;
+ ifmr->ifm_current = 0;
+ ifmr->ifm_mask = 0;
+ ifmr->ifm_status = IFM_AVALID;
+ ifmr->ifm_active = 0;
+ ifmr->ifm_count = 1;
+ if (ifmr->ifm_ulist) {
+ error = copyout(&ifmr->ifm_current,
+ ifmr->ifm_ulist,
+ sizeof(int));
+ }
+ error = 0;
+ }
+ break;
+
+ case SIOCSIFMEDIA:
+ error = EINVAL;
+ break;
+
+ case SIOCSIFMTU:
+ /*
+ * Set the interface MTU.
+ */
+ VLAN_LOCK();
+ if (ifv->ifv_p != NULL) {
+ if (ifr->ifr_mtu > (ifv->ifv_p->if_mtu - ifv->ifv_mtufudge)
+ || ifr->ifr_mtu < (ifv->ifv_mintu - ifv->ifv_mtufudge)) {
+ error = EINVAL;
+ } else {
+ ifp->if_mtu = ifr->ifr_mtu;
+ }
+ } else {
+ error = EINVAL;
+ }
+ VLAN_UNLOCK();
+ break;
+
+ case SIOCSETVLAN:
+ error = copyin(ifr->ifr_data, &vlr, sizeof(vlr));
+ if (error)
+ break;
+ if (vlr.vlr_parent[0] == '\0') {
+ VLAN_LOCK();
+ vlan_unconfig(ifp);
+#if 0
+ if (ifp->if_flags & IFF_UP)
+ if_down(ifp);
+ ifp->if_flags &= ~IFF_RUNNING;
+#endif 0
+ VLAN_UNLOCK();
+ break;
+ }
+ p = ifunit(vlr.vlr_parent);
+ if (p == 0) {
+ error = ENOENT;
+ break;
+ }
+ /*
+ * Don't let the caller set up a VLAN tag with
+ * anything except VLID bits.
+ */
+ if (vlr.vlr_tag & ~EVL_VLID_MASK) {
+ error = EINVAL;
+ break;
+ }
+ VLAN_LOCK();
+ error = vlan_config(ifv, p, vlr.vlr_tag);
+ if (error) {
+ VLAN_UNLOCK();
+ break;
+ }
+ ifp->if_flags |= IFF_RUNNING;
+ VLAN_UNLOCK();
+
+ /* Update promiscuous mode, if necessary. */
+ vlan_set_promisc(ifp);
+
+ /* generate a link event */
+ vlan_link_event(ifp, p);
+ break;
- case SIOCGETVLAN:
- bzero(&vlr, sizeof vlr);
- if (ifv->ifv_p) {
- snprintf(vlr.vlr_parent, sizeof(vlr.vlr_parent),
- "%s%d", ifv->ifv_p->if_name, ifv->ifv_p->if_unit);
- vlr.vlr_tag = ifv->ifv_tag;
- }
- error = copyout(&vlr, ifr->ifr_data, sizeof vlr);
- break;
+ case SIOCGETVLAN:
+ bzero(&vlr, sizeof vlr);
+ VLAN_LOCK();
+ if (ifv->ifv_p != NULL) {
+ snprintf(vlr.vlr_parent, sizeof(vlr.vlr_parent),
+ "%s%d", ifv->ifv_p->if_name,
+ ifv->ifv_p->if_unit);
+ vlr.vlr_tag = ifv->ifv_tag;
+ }
+ VLAN_UNLOCK();
+ error = copyout(&vlr, ifr->ifr_data, sizeof vlr);
+ break;
- case SIOCSIFFLAGS:
- /*
- * We don't support promiscuous mode
- * right now because it would require help from the
- * underlying drivers, which hasn't been implemented.
- */
- if (ifr->ifr_flags & (IFF_PROMISC)) {
- ifp->if_flags &= ~(IFF_PROMISC);
- error = EINVAL;
- }
- break;
- case SIOCADDMULTI:
- case SIOCDELMULTI:
- error = vlan_setmulti(ifp);
- break;
- default:
- error = EINVAL;
+ case SIOCSIFFLAGS:
+ /*
+ * For promiscuous mode, we enable promiscuous mode on
+ * the parent if we need promiscuous on the VLAN interface.
+ */
+ if (ifv->ifv_p != NULL)
+ error = vlan_set_promisc(ifp);
+ break;
+
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ error = vlan_setmulti(ifp);
+ break;
+ default:
+ error = EOPNOTSUPP;
+ }
+ return error;
+}
+
+static int
+nop_if_ioctl(struct ifnet * ifp, u_long cmd, void * data)
+{
+ return EOPNOTSUPP;
+}
+
+static int
+nop_if_bpf(struct ifnet *ifp, int mode, bpf_callback_func * func)
+{
+ return ENODEV;
+}
+
+static int
+nop_if_free(struct ifnet * ifp)
+{
+ return 0;
+}
+
+static int
+nop_if_output(struct ifnet * ifp, struct mbuf * m)
+{
+ if (m != NULL) {
+ m_freem_list(m);
+ }
+ return 0;
+}
+
+static int
+vlan_if_free(struct ifnet * ifp)
+{
+ struct ifvlan *ifv;
+
+ if (ifp == NULL) {
+ return 0;
+ }
+ ifv = (struct ifvlan *)ifp->if_private;
+ if (ifv == NULL) {
+ return 0;
+ }
+ ifp->if_private = NULL;
+ dlil_if_release(ifp);
+ FREE(ifv, M_VLAN);
+ return 0;
+}
+
+/*
+ * Function: vlan_if_filter_detach
+ * Purpose:
+ * Destroy all vlan interfaces that refer to the interface
+ */
+static int
+vlan_if_filter_detach(caddr_t cookie)
+{
+ struct ifnet * ifp;
+ struct ifvlan * ifv;
+ struct ifnet * p = (struct ifnet *)cookie;
+
+ VLAN_LOCK();
+ while (TRUE) {
+ ifv = vlan_lookup_ifp(p);
+ if (ifv == NULL) {
+ break;
+ }
+ if (ifv->ifv_detaching) {
+ continue;
+ }
+ /* make sure we don't invoke vlan_detach_filter */
+ ifv->ifv_filter_valid = FALSE;
+ vlan_remove(ifv);
+ ifp = ifv->ifv_ifp;
+ VLAN_UNLOCK();
+ vlan_if_detach(ifp);
+ VLAN_LOCK();
+ }
+ VLAN_UNLOCK();
+ return (0);
+}
+
+/*
+ * Function: vlan_attach_filter
+ * Purpose:
+ * We attach an interface filter to detect when the underlying interface
+ * goes away. We are forced to do that because dlil does not call our
+ * protocol's dl_event function for KEV_DL_IF_DETACHING.
+ */
+
+static int
+vlan_attach_filter(struct ifnet * ifp, u_long * filter_id)
+{
+ int error;
+ struct dlil_if_flt_str filt;
+
+ bzero(&filt, sizeof(filt));
+ filt.filter_detach = vlan_if_filter_detach;
+ filt.cookie = (caddr_t)ifp;
+ error = dlil_attach_interface_filter(ifp, &filt, filter_id,
+ DLIL_LAST_FILTER);
+ if (error) {
+ printf("vlan: dlil_attach_interface_filter(%s%d) failed, %d\n",
+ ifp->if_name, ifp->if_unit, error);
+ }
+ return (error);
+}
+
+/*
+ * Function: vlan_detach_filter
+ * Purpose:
+ * Remove our interface filter.
+ */
+static int
+vlan_detach_filter(u_long filter_id)
+{
+ int error;
+
+ error = dlil_detach_filter(filter_id);
+ if (error) {
+ printf("vlan: dlil_detach_filter failed, %d\n", error);
+ }
+ return (error);
+}
+
+/*
+ * Function: vlan_proto_input
+ * Purpose:
+ * This function is never called. We aren't allowed to leave the
+ * function pointer NULL, so this function simply free's the mbuf.
+ */
+static int
+vlan_proto_input(m, frame_header, ifp, dl_tag, sync_ok)
+ struct mbuf *m;
+ char *frame_header;
+ struct ifnet *ifp;
+ u_long dl_tag;
+ int sync_ok;
+{
+ m_freem(m);
+ return (EJUSTRETURN);
+}
+
+static struct ifnet *
+find_if_name_unit(const char * if_name, int unit)
+{
+ struct ifnet * ifp;
+
+ TAILQ_FOREACH(ifp, &ifnet, if_link) {
+ if (strcmp(if_name, ifp->if_name) == 0 && unit == ifp->if_unit) {
+ return (ifp);
+ }
+ }
+ return (ifp);
+}
+
+static void
+interface_link_event(struct ifnet * ifp, u_long event_code)
+{
+ struct {
+ struct kern_event_msg header;
+ u_long unit;
+ char if_name[IFNAMSIZ];
+ } event;
+
+ event.header.total_size = sizeof(event);
+ event.header.vendor_code = KEV_VENDOR_APPLE;
+ event.header.kev_class = KEV_NETWORK_CLASS;
+ event.header.kev_subclass = KEV_DL_SUBCLASS;
+ event.header.event_code = event_code;
+ event.header.event_data[0] = ifp->if_family;
+ event.unit = (u_long) ifp->if_unit;
+ strncpy(event.if_name, ifp->if_name, IFNAMSIZ);
+ dlil_event(ifp, &event.header);
+ return;
+}
+
+static void
+parent_link_event(struct ifnet * p, u_long event_code)
+{
+ struct ifvlan * ifv;
+
+ LIST_FOREACH(ifv, &ifv_list, ifv_list) {
+ if (p == ifv->ifv_p) {
+ interface_link_event(ifv->ifv_ifp, event_code);
+ }
+ }
+ return;
+
+}
+
+/*
+ * Function: vlan_dl_event
+ * Purpose:
+ * Process DLIL events that interest us. Currently, that is
+ * just the interface UP and DOWN. Ideally, this would also
+ * include the KEV_DL_IF_DETACH{ING} messages, which would eliminate
+ * the need for an interface filter.
+ */
+static int
+vlan_dl_event(struct kern_event_msg * event, u_long dl_tag)
+{
+ struct ifnet * p;
+ struct net_event_data * net_event;
+
+ if (event->vendor_code != KEV_VENDOR_APPLE
+ || event->kev_class != KEV_NETWORK_CLASS
+ || event->kev_subclass != KEV_DL_SUBCLASS) {
+ goto done;
+ }
+ net_event = (struct net_event_data *)(event->event_data);
+ switch (event->event_code) {
+ case KEV_DL_LINK_OFF:
+ case KEV_DL_LINK_ON:
+ p = find_if_name_unit(net_event->if_name, net_event->if_unit);
+ if (p != NULL) {
+ parent_link_event(p, event->event_code);
}
- return error;
+ break;
+#if 0
+ case KEV_DL_IF_DETACHING:
+ case KEV_DL_IF_DETACHED:
+ /* we don't get these, unfortunately */
+ break;
+#endif 0
+ default:
+ break;
+ }
+
+ done:
+ return (0);
+}
+
+/*
+ * Function: vlan_attach_protocol
+ * Purpose:
+ * Attach a DLIL protocol to the interface, using the ETHERTYPE_VLAN
+ * demux ether type. We're not a real protocol, we'll never receive
+ * any packets because they're intercepted by ether_demux before
+ * our input routine would be called.
+ *
+ * The reasons for attaching a protocol to the interface are:
+ * 1) add a protocol reference to the interface so that the underlying
+ * interface automatically gets marked up while we're attached
+ * 2) receive link status events which we can propagate to our
+ * VLAN interfaces.
+ */
+static int
+vlan_attach_protocol(struct ifnet *ifp)
+{
+ struct dlil_demux_desc desc;
+ u_long dl_tag;
+ u_short en_native = ETHERTYPE_VLAN;
+ int error;
+ int i;
+ struct dlil_proto_reg_str reg;
+
+ TAILQ_INIT(®.demux_desc_head);
+ desc.type = DLIL_DESC_RAW;
+ desc.variants.bitmask.proto_id_length = 0;
+ desc.variants.bitmask.proto_id = 0;
+ desc.variants.bitmask.proto_id_mask = 0;
+ desc.native_type = (char *) &en_native;
+ TAILQ_INSERT_TAIL(®.demux_desc_head, &desc, next);
+ reg.interface_family = ifp->if_family;
+ reg.unit_number = ifp->if_unit;
+ reg.input = vlan_proto_input;
+ reg.pre_output = 0;
+ reg.event = vlan_dl_event;
+ reg.offer = 0;
+ reg.ioctl = 0;
+ reg.default_proto = 0;
+ reg.protocol_family = VLAN_PROTO_FAMILY;
+
+ error = dlil_attach_protocol(®, &dl_tag);
+ if (error) {
+ printf("vlan_proto_attach(%s%d) dlil_attach_protocol failed, %d\n",
+ ifp->if_name, ifp->if_unit, error);
+ }
+ return (error);
+}
+
+/*
+ * Function: vlan_detach_protocol
+ * Purpose:
+ * Detach our DLIL protocol from an interface
+ */
+static int
+vlan_detach_protocol(struct ifnet *ifp)
+{
+ u_long dl_tag;
+ int error;
+
+ error = dlil_find_dltag(ifp->if_family, ifp->if_unit,
+ VLAN_PROTO_FAMILY, &dl_tag);
+ if (error) {
+ printf("vlan_proto_detach(%s%d) dlil_find_dltag failed, %d\n",
+ ifp->if_name, ifp->if_unit, error);
+ } else {
+ error = dlil_detach_protocol(dl_tag);
+ if (error) {
+ printf("vlan_proto_detach(%s%d) dlil_detach_protocol failed, %d\n",
+ ifp->if_name, ifp->if_unit, error);
+ }
+ }
+ return (error);
+}
+
+/*
+ * DLIL interface family functions
+ * We use the ethernet dlil functions, since that's all we support.
+ * If we wanted to handle multiple LAN types (tokenring, etc.), we'd
+ * call the appropriate routines for that LAN type instead of hard-coding
+ * ethernet.
+ */
+extern int ether_add_if(struct ifnet *ifp);
+extern int ether_del_if(struct ifnet *ifp);
+extern int ether_init_if(struct ifnet *ifp);
+extern int ether_add_proto(struct ddesc_head_str *desc_head,
+ struct if_proto *proto, u_long dl_tag);
+extern int ether_del_proto(struct if_proto *proto, u_long dl_tag);
+extern int ether_ifmod_ioctl(struct ifnet *ifp, u_long command,
+ caddr_t data);
+extern int ether_del_proto(struct if_proto *proto, u_long dl_tag);
+extern int ether_add_proto(struct ddesc_head_str *desc_head, struct if_proto *proto, u_long dl_tag);
+
+extern int ether_attach_inet(struct ifnet *ifp, u_long *dl_tag);
+extern int ether_detach_inet(struct ifnet *ifp, u_long dl_tag);
+extern int ether_attach_inet6(struct ifnet *ifp, u_long *dl_tag);
+extern int ether_detach_inet6(struct ifnet *ifp, u_long dl_tag);
+
+static int
+vlan_attach_inet(struct ifnet *ifp, u_long *dl_tag)
+{
+ return (ether_attach_inet(ifp, dl_tag));
+}
+
+static int
+vlan_detach_inet(struct ifnet *ifp, u_long dl_tag)
+{
+ return (ether_detach_inet(ifp, dl_tag));
+}
+
+static int
+vlan_attach_inet6(struct ifnet *ifp, u_long *dl_tag)
+{
+ return (ether_attach_inet6(ifp, dl_tag));
+}
+
+static int
+vlan_detach_inet6(struct ifnet *ifp, u_long dl_tag)
+{
+ return (ether_detach_inet6(ifp, dl_tag));
+}
+
+static int
+vlan_add_if(struct ifnet *ifp)
+{
+ return (ether_add_if(ifp));
}
-#endif /* NVLAN > 0 */
+static int
+vlan_del_if(struct ifnet *ifp)
+{
+ return (ether_del_if(ifp));
+}
+
+static int
+vlan_init_if(struct ifnet *ifp)
+{
+ return (0);
+}
+
+static int
+vlan_shutdown()
+{
+ return 0;
+}
+
+__private_extern__ int
+vlan_family_init()
+{
+ int i, error=0;
+ struct dlil_ifmod_reg_str ifmod_reg;
+ struct dlil_protomod_reg_str vlan_protoreg;
+
+#if 0
+ /* VLAN family is built-in, called from ether_family_init */
+ thread_funnel_switch(KERNEL_FUNNEL, NETWORK_FUNNEL);
+#endif 0
+
+ bzero(&ifmod_reg, sizeof(ifmod_reg));
+ ifmod_reg.add_if = vlan_add_if;
+ ifmod_reg.del_if = vlan_del_if;
+ ifmod_reg.init_if = vlan_init_if;
+ ifmod_reg.add_proto = ether_add_proto;
+ ifmod_reg.del_proto = ether_del_proto;
+ ifmod_reg.ifmod_ioctl = ether_ifmod_ioctl;
+ ifmod_reg.shutdown = vlan_shutdown;
+
+ if (dlil_reg_if_modules(APPLE_IF_FAM_VLAN, &ifmod_reg)) {
+ printf("WARNING: vlan_family_init -- "
+ "Can't register if family modules\n");
+ error = EIO;
+ goto done;
+ }
+
+ /* Register protocol registration functions */
+ bzero(&vlan_protoreg, sizeof(vlan_protoreg));
+ vlan_protoreg.attach_proto = vlan_attach_inet;
+ vlan_protoreg.detach_proto = vlan_detach_inet;
+
+ if (error = dlil_reg_proto_module(PF_INET, APPLE_IF_FAM_VLAN,
+ &vlan_protoreg) != 0) {
+ kprintf("dlil_reg_proto_module failed for AF_INET6 error=%d\n",
+ error);
+ goto done;
+ }
+ vlan_protoreg.attach_proto = vlan_attach_inet6;
+ vlan_protoreg.detach_proto = vlan_detach_inet6;
+
+ if (error = dlil_reg_proto_module(PF_INET6, APPLE_IF_FAM_VLAN,
+ &vlan_protoreg) != 0) {
+ kprintf("dlil_reg_proto_module failed for AF_INET6 error=%d\n",
+ error);
+ goto done;
+ }
+ vlan_clone_attach();
+
+ done:
+#if 0
+ thread_funnel_switch(NETWORK_FUNNEL, KERNEL_FUNNEL);
+#endif 0
+ return (error);
+}