diff options
Diffstat (limited to 'freebsd/sys/net/if_vlan.c')
-rw-r--r-- | freebsd/sys/net/if_vlan.c | 401 |
1 files changed, 247 insertions, 154 deletions
diff --git a/freebsd/sys/net/if_vlan.c b/freebsd/sys/net/if_vlan.c index 7d08e298..8a93565b 100644 --- a/freebsd/sys/net/if_vlan.c +++ b/freebsd/sys/net/if_vlan.c @@ -2,6 +2,10 @@ /*- * Copyright 1998 Massachusetts Institute of Technology + * Copyright 2012 ADARA Networks, Inc. + * + * Portions of this software were developed by Robert N. M. Watson under + * contract to ADARA Networks, Inc. * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby @@ -31,8 +35,7 @@ /* * if_vlan.c - pseudo-device driver for IEEE 802.1Q virtual LANs. - * Might be extended some day to also handle IEEE 802.1p priority - * tagging. This is sort of sneaky in the implementation, since + * This is sort of sneaky in the implementation, since * 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 @@ -47,12 +50,14 @@ __FBSDID("$FreeBSD$"); #include <rtems/bsd/local/opt_vlan.h> #include <rtems/bsd/sys/param.h> +#include <sys/eventhandler.h> #include <sys/kernel.h> #include <rtems/bsd/sys/lock.h> #include <sys/malloc.h> #include <sys/mbuf.h> #include <sys/module.h> -#include <sys/rwlock.h> +#include <sys/rmlock.h> +#include <sys/priv.h> #include <sys/queue.h> #include <sys/socket.h> #include <sys/sockio.h> @@ -63,6 +68,7 @@ __FBSDID("$FreeBSD$"); #include <net/bpf.h> #include <net/ethernet.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_clone.h> #include <net/if_dl.h> #include <net/if_types.h> @@ -74,7 +80,6 @@ __FBSDID("$FreeBSD$"); #include <netinet/if_ether.h> #endif -#define VLANNAME "vlan" #define VLAN_DEF_HWIDTH 4 #define VLAN_IFFLAGS (IFF_BROADCAST | IFF_MULTICAST) @@ -85,7 +90,7 @@ LIST_HEAD(ifvlanhead, ifvlan); struct ifvlantrunk { struct ifnet *parent; /* parent interface of this trunk */ - struct rwlock rw; + struct rmlock lock; #ifdef VLAN_ARRAY #define VLAN_ARRAY_SIZE (EVL_VLID_MASK + 1) struct ifvlan *vlans[VLAN_ARRAY_SIZE]; /* static table */ @@ -105,9 +110,9 @@ struct vlan_mc_entry { struct ifvlan { struct ifvlantrunk *ifv_trunk; struct ifnet *ifv_ifp; - void *ifv_cookie; #define TRUNK(ifv) ((ifv)->ifv_trunk) #define PARENT(ifv) ((ifv)->ifv_trunk->parent) + void *ifv_cookie; int ifv_pflags; /* special flags we have set on parent */ struct ifv_linkmib { int ifvm_encaplen; /* encapsulation length */ @@ -115,6 +120,8 @@ struct ifvlan { int ifvm_mintu; /* min transmission unit */ uint16_t ifvm_proto; /* encapsulation ethertype */ uint16_t ifvm_tag; /* tag to apply on packets leaving if */ + uint16_t ifvm_vid; /* VLAN ID */ + uint8_t ifvm_pcp; /* Priority Code Point (PCP). */ } ifv_mib; SLIST_HEAD(, vlan_mc_entry) vlan_mc_listhead; #ifndef VLAN_ARRAY @@ -123,6 +130,8 @@ struct ifvlan { }; #define ifv_proto ifv_mib.ifvm_proto #define ifv_tag ifv_mib.ifvm_tag +#define ifv_vid ifv_mib.ifvm_vid +#define ifv_pcp ifv_mib.ifvm_pcp #define ifv_encaplen ifv_mib.ifvm_encaplen #define ifv_mtufudge ifv_mib.ifvm_mtufudge #define ifv_mintu ifv_mib.ifvm_mintu @@ -143,11 +152,22 @@ static SYSCTL_NODE(_net_link, IFT_L2VLAN, vlan, CTLFLAG_RW, 0, static SYSCTL_NODE(_net_link_vlan, PF_LINK, link, CTLFLAG_RW, 0, "for consistency"); -static int soft_pad = 0; -SYSCTL_INT(_net_link_vlan, OID_AUTO, soft_pad, CTLFLAG_RW, &soft_pad, 0, - "pad short frames before tagging"); +static VNET_DEFINE(int, soft_pad); +#define V_soft_pad VNET(soft_pad) +SYSCTL_INT(_net_link_vlan, OID_AUTO, soft_pad, CTLFLAG_RW | CTLFLAG_VNET, + &VNET_NAME(soft_pad), 0, "pad short frames before tagging"); + +/* + * For now, make preserving PCP via an mbuf tag optional, as it increases + * per-packet memory allocations and frees. In the future, it would be + * preferable to reuse ether_vtag for this, or similar. + */ +static int vlan_mtag_pcp = 0; +SYSCTL_INT(_net_link_vlan, OID_AUTO, mtag_pcp, CTLFLAG_RW, &vlan_mtag_pcp, 0, + "Retain VLAN PCP information as packets are passed up the stack"); -static MALLOC_DEFINE(M_VLAN, VLANNAME, "802.1Q Virtual LAN Interface"); +static const char vlanname[] = "vlan"; +static MALLOC_DEFINE(M_VLAN, vlanname, "802.1Q Virtual LAN Interface"); static eventhandler_tag ifdetach_tag; static eventhandler_tag iflladdr_tag; @@ -156,7 +176,7 @@ static eventhandler_tag iflladdr_tag; * We have a global mutex, that is used to serialize configuration * changes and isn't used in normal packet delivery. * - * We also have a per-trunk rwlock, that is locked shared on packet + * We also have a per-trunk rmlock(9), that is locked shared on packet * processing and exclusive when configuration is changed. * * The VLAN_ARRAY substitutes the dynamic hash with a static array @@ -170,14 +190,15 @@ static struct sx ifv_lock; #define VLAN_LOCK_ASSERT() sx_assert(&ifv_lock, SA_LOCKED) #define VLAN_LOCK() sx_xlock(&ifv_lock) #define VLAN_UNLOCK() sx_xunlock(&ifv_lock) -#define TRUNK_LOCK_INIT(trunk) rw_init(&(trunk)->rw, VLANNAME) -#define TRUNK_LOCK_DESTROY(trunk) rw_destroy(&(trunk)->rw) -#define TRUNK_LOCK(trunk) rw_wlock(&(trunk)->rw) -#define TRUNK_UNLOCK(trunk) rw_wunlock(&(trunk)->rw) -#define TRUNK_LOCK_ASSERT(trunk) rw_assert(&(trunk)->rw, RA_WLOCKED) -#define TRUNK_RLOCK(trunk) rw_rlock(&(trunk)->rw) -#define TRUNK_RUNLOCK(trunk) rw_runlock(&(trunk)->rw) -#define TRUNK_LOCK_RASSERT(trunk) rw_assert(&(trunk)->rw, RA_RLOCKED) +#define TRUNK_LOCK_INIT(trunk) rm_init(&(trunk)->lock, vlanname) +#define TRUNK_LOCK_DESTROY(trunk) rm_destroy(&(trunk)->lock) +#define TRUNK_LOCK(trunk) rm_wlock(&(trunk)->lock) +#define TRUNK_UNLOCK(trunk) rm_wunlock(&(trunk)->lock) +#define TRUNK_LOCK_ASSERT(trunk) rm_assert(&(trunk)->lock, RA_WLOCKED) +#define TRUNK_RLOCK(trunk) rm_rlock(&(trunk)->lock, &tracker) +#define TRUNK_RUNLOCK(trunk) rm_runlock(&(trunk)->lock, &tracker) +#define TRUNK_LOCK_RASSERT(trunk) rm_assert(&(trunk)->lock, RA_RLOCKED) +#define TRUNK_LOCK_READER struct rm_priotracker tracker #ifndef VLAN_ARRAY static void vlan_inithash(struct ifvlantrunk *trunk); @@ -186,7 +207,7 @@ static int vlan_inshash(struct ifvlantrunk *trunk, struct ifvlan *ifv); static int vlan_remhash(struct ifvlantrunk *trunk, struct ifvlan *ifv); static void vlan_growhash(struct ifvlantrunk *trunk, int howmuch); static __inline struct ifvlan * vlan_gethash(struct ifvlantrunk *trunk, - uint16_t tag); + uint16_t vid); #endif static void trunk_destroy(struct ifvlantrunk *trunk); @@ -206,8 +227,7 @@ static void vlan_link_state(struct ifnet *ifp); static void vlan_capabilities(struct ifvlan *ifv); static void vlan_trunk_capabilities(struct ifnet *ifp); -static struct ifnet *vlan_clone_match_ethertag(struct if_clone *, - const char *, int *); +static struct ifnet *vlan_clone_match_ethervid(const char *, int *); static int vlan_clone_match(struct if_clone *, const char *); static int vlan_clone_create(struct if_clone *, char *, size_t, caddr_t); static int vlan_clone_destroy(struct if_clone *, struct ifnet *); @@ -215,11 +235,10 @@ static int vlan_clone_destroy(struct if_clone *, struct ifnet *); static void vlan_ifdetach(void *arg, struct ifnet *ifp); static void vlan_iflladdr(void *arg, struct ifnet *ifp); -static struct if_clone vlan_cloner = IFC_CLONE_INITIALIZER(VLANNAME, NULL, - IF_MAXUNIT, NULL, vlan_clone_match, vlan_clone_create, vlan_clone_destroy); +static struct if_clone *vlan_cloner; #ifdef VIMAGE -static VNET_DEFINE(struct if_clone, vlan_cloner); +static VNET_DEFINE(struct if_clone *, vlan_cloner); #define V_vlan_cloner VNET(vlan_cloner) #endif @@ -274,9 +293,9 @@ vlan_inshash(struct ifvlantrunk *trunk, struct ifvlan *ifv) KASSERT(trunk->hwidth > 0, ("%s: hwidth not positive", __func__)); b = 1 << trunk->hwidth; - i = HASH(ifv->ifv_tag, trunk->hmask); + i = HASH(ifv->ifv_vid, trunk->hmask); LIST_FOREACH(ifv2, &trunk->hash[i], ifv_list) - if (ifv->ifv_tag == ifv2->ifv_tag) + if (ifv->ifv_vid == ifv2->ifv_vid) return (EEXIST); /* @@ -286,7 +305,7 @@ vlan_inshash(struct ifvlantrunk *trunk, struct ifvlan *ifv) */ if (trunk->refcnt > (b * b) / 2) { vlan_growhash(trunk, 1); - i = HASH(ifv->ifv_tag, trunk->hmask); + i = HASH(ifv->ifv_vid, trunk->hmask); } LIST_INSERT_HEAD(&trunk->hash[i], ifv, ifv_list); trunk->refcnt++; @@ -304,7 +323,7 @@ vlan_remhash(struct ifvlantrunk *trunk, struct ifvlan *ifv) KASSERT(trunk->hwidth > 0, ("%s: hwidth not positive", __func__)); b = 1 << trunk->hwidth; - i = HASH(ifv->ifv_tag, trunk->hmask); + i = HASH(ifv->ifv_vid, trunk->hmask); LIST_FOREACH(ifv2, &trunk->hash[i], ifv_list) if (ifv2 == ifv) { trunk->refcnt--; @@ -356,7 +375,7 @@ vlan_growhash(struct ifvlantrunk *trunk, int howmuch) for (i = 0; i < n; i++) while ((ifv = LIST_FIRST(&trunk->hash[i])) != NULL) { LIST_REMOVE(ifv, ifv_list); - j = HASH(ifv->ifv_tag, n2 - 1); + j = HASH(ifv->ifv_vid, n2 - 1); LIST_INSERT_HEAD(&hash2[j], ifv, ifv_list); } free(trunk->hash, M_VLAN); @@ -370,14 +389,14 @@ vlan_growhash(struct ifvlantrunk *trunk, int howmuch) } static __inline struct ifvlan * -vlan_gethash(struct ifvlantrunk *trunk, uint16_t tag) +vlan_gethash(struct ifvlantrunk *trunk, uint16_t vid) { struct ifvlan *ifv; TRUNK_LOCK_RASSERT(trunk); - LIST_FOREACH(ifv, &trunk->hash[HASH(tag, trunk->hmask)], ifv_list) - if (ifv->ifv_tag == tag) + LIST_FOREACH(ifv, &trunk->hash[HASH(vid, trunk->hmask)], ifv_list) + if (ifv->ifv_vid == vid) return (ifv); return (NULL); } @@ -401,19 +420,19 @@ vlan_dumphash(struct ifvlantrunk *trunk) #else static __inline struct ifvlan * -vlan_gethash(struct ifvlantrunk *trunk, uint16_t tag) +vlan_gethash(struct ifvlantrunk *trunk, uint16_t vid) { - return trunk->vlans[tag]; + return trunk->vlans[vid]; } static __inline int vlan_inshash(struct ifvlantrunk *trunk, struct ifvlan *ifv) { - if (trunk->vlans[ifv->ifv_tag] != NULL) + if (trunk->vlans[ifv->ifv_vid] != NULL) return EEXIST; - trunk->vlans[ifv->ifv_tag] = ifv; + trunk->vlans[ifv->ifv_vid] = ifv; trunk->refcnt++; return (0); @@ -423,7 +442,7 @@ static __inline int vlan_remhash(struct ifvlantrunk *trunk, struct ifvlan *ifv) { - trunk->vlans[ifv->ifv_tag] = NULL; + trunk->vlans[ifv->ifv_vid] = NULL; trunk->refcnt--; return (0); @@ -461,48 +480,48 @@ trunk_destroy(struct ifvlantrunk *trunk) * traffic that it doesn't really want, which ends up being discarded * later by the upper protocol layers. Unfortunately, there's no way * to avoid this: there really is only one physical interface. - * - * XXX: There is a possible race here if more than one thread is - * modifying the multicast state of the vlan interface at the same time. */ static int vlan_setmulti(struct ifnet *ifp) { struct ifnet *ifp_p; - struct ifmultiaddr *ifma, *rifma = NULL; + struct ifmultiaddr *ifma; struct ifvlan *sc; struct vlan_mc_entry *mc; int error; - /*VLAN_LOCK_ASSERT();*/ - /* Find the parent. */ sc = ifp->if_softc; + TRUNK_LOCK_ASSERT(TRUNK(sc)); ifp_p = PARENT(sc); CURVNET_SET_QUIET(ifp_p->if_vnet); /* First, remove any existing filter entries. */ while ((mc = SLIST_FIRST(&sc->vlan_mc_listhead)) != NULL) { - error = if_delmulti(ifp_p, (struct sockaddr *)&mc->mc_addr); - if (error) - return (error); SLIST_REMOVE_HEAD(&sc->vlan_mc_listhead, mc_entries); + (void)if_delmulti(ifp_p, (struct sockaddr *)&mc->mc_addr); free(mc, M_VLAN); } /* Now program new ones. */ + IF_ADDR_WLOCK(ifp); TAILQ_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_NOWAIT); - if (mc == NULL) + if (mc == NULL) { + IF_ADDR_WUNLOCK(ifp); return (ENOMEM); + } bcopy(ifma->ifma_addr, &mc->mc_addr, ifma->ifma_addr->sa_len); mc->mc_addr.sdl_index = ifp_p->if_index; SLIST_INSERT_HEAD(&sc->vlan_mc_listhead, mc, mc_entries); + } + IF_ADDR_WUNLOCK(ifp); + SLIST_FOREACH (mc, &sc->vlan_mc_listhead, mc_entries) { error = if_addmulti(ifp_p, (struct sockaddr *)&mc->mc_addr, - &rifma); + NULL); if (error) return (error); } @@ -625,17 +644,21 @@ vlan_trunkdev(struct ifnet *ifp) } /* - * Return the 16bit vlan tag for this interface. + * Return the 12-bit VLAN VID for this interface, for use by external + * components such as Infiniband. + * + * XXXRW: Note that the function name here is historical; it should be named + * vlan_vid(). */ static int -vlan_tag(struct ifnet *ifp, uint16_t *tagp) +vlan_tag(struct ifnet *ifp, uint16_t *vidp) { struct ifvlan *ifv; if (ifp->if_type != IFT_L2VLAN) return (EINVAL); ifv = ifp->if_softc; - *tagp = ifv->ifv_tag; + *vidp = ifv->ifv_vid; return (0); } @@ -671,20 +694,21 @@ vlan_setcookie(struct ifnet *ifp, void *cookie) } /* - * Return the vlan device present at the specific tag. + * Return the vlan device present at the specific VID. */ static struct ifnet * -vlan_devat(struct ifnet *ifp, uint16_t tag) +vlan_devat(struct ifnet *ifp, uint16_t vid) { struct ifvlantrunk *trunk; struct ifvlan *ifv; + TRUNK_LOCK_READER; trunk = ifp->if_vlantrunk; if (trunk == NULL) return (NULL); ifp = NULL; TRUNK_RLOCK(trunk); - ifv = vlan_gethash(trunk, tag); + ifv = vlan_gethash(trunk, vid); if (ifv) ifp = ifv->ifv_ifp; TRUNK_RUNLOCK(trunk); @@ -692,10 +716,20 @@ vlan_devat(struct ifnet *ifp, uint16_t tag) } /* + * Recalculate the cached VLAN tag exposed via the MIB. + */ +static void +vlan_tag_recalculate(struct ifvlan *ifv) +{ + + ifv->ifv_tag = EVL_MAKETAG(ifv->ifv_vid, ifv->ifv_pcp, 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 + * set here. No one else in the system should be aware of this so * we use an explicit reference here. */ extern void (*vlan_input_p)(struct ifnet *, struct mbuf *); @@ -727,7 +761,8 @@ vlan_modevent(module_t mod, int type, void *data) vlan_tag_p = vlan_tag; vlan_devat_p = vlan_devat; #ifndef VIMAGE - if_clone_attach(&vlan_cloner); + vlan_cloner = if_clone_advanced(vlanname, 0, vlan_clone_match, + vlan_clone_create, vlan_clone_destroy); #endif if (bootverbose) printf("vlan: initialized, using " @@ -741,7 +776,7 @@ vlan_modevent(module_t mod, int type, void *data) break; case MOD_UNLOAD: #ifndef VIMAGE - if_clone_detach(&vlan_cloner); + if_clone_detach(vlan_cloner); #endif EVENTHANDLER_DEREGISTER(ifnet_departure_event, ifdetach_tag); EVENTHANDLER_DEREGISTER(iflladdr_event, iflladdr_tag); @@ -777,8 +812,9 @@ static void vnet_vlan_init(const void *unused __unused) { + vlan_cloner = if_clone_advanced(vlanname, 0, vlan_clone_match, + vlan_clone_create, vlan_clone_destroy); V_vlan_cloner = vlan_cloner; - if_clone_attach(&V_vlan_cloner); } VNET_SYSINIT(vnet_vlan_init, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY, vnet_vlan_init, NULL); @@ -787,46 +823,39 @@ static void vnet_vlan_uninit(const void *unused __unused) { - if_clone_detach(&V_vlan_cloner); + if_clone_detach(V_vlan_cloner); } -VNET_SYSUNINIT(vnet_vlan_uninit, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_FIRST, +VNET_SYSUNINIT(vnet_vlan_uninit, SI_SUB_INIT_IF, SI_ORDER_FIRST, vnet_vlan_uninit, NULL); #endif +/* + * Check for <etherif>.<vlan> style interface names. + */ static struct ifnet * -vlan_clone_match_ethertag(struct if_clone *ifc, const char *name, int *tag) +vlan_clone_match_ethervid(const char *name, int *vidp) { - const char *cp; + char ifname[IFNAMSIZ]; + char *cp; struct ifnet *ifp; - int t; + int vid; - /* Check for <etherif>.<vlan> style interface names. */ - IFNET_RLOCK_NOSLEEP(); - TAILQ_FOREACH(ifp, &V_ifnet, if_link) { - /* - * We can handle non-ethernet hardware types as long as - * they handle the tagging and headers themselves. - */ - if (ifp->if_type != IFT_ETHER && - (ifp->if_capenable & IFCAP_VLAN_HWTAGGING) == 0) - continue; - if (strncmp(ifp->if_xname, name, strlen(ifp->if_xname)) != 0) - continue; - cp = name + strlen(ifp->if_xname); - if (*cp++ != '.') - continue; - if (*cp == '\0') - continue; - t = 0; - for(; *cp >= '0' && *cp <= '9'; cp++) - t = (t * 10) + (*cp - '0'); - if (*cp != '\0') - continue; - if (tag != NULL) - *tag = t; - break; - } - IFNET_RUNLOCK_NOSLEEP(); + strlcpy(ifname, name, IFNAMSIZ); + if ((cp = strchr(ifname, '.')) == NULL) + return (NULL); + *cp = '\0'; + if ((ifp = ifunit(ifname)) == NULL) + return (NULL); + /* Parse VID. */ + if (*++cp == '\0') + return (NULL); + vid = 0; + for(; *cp >= '0' && *cp <= '9'; cp++) + vid = (vid * 10) + (*cp - '0'); + if (*cp != '\0') + return (NULL); + if (vidp != NULL) + *vidp = vid; return (ifp); } @@ -836,10 +865,10 @@ vlan_clone_match(struct if_clone *ifc, const char *name) { const char *cp; - if (vlan_clone_match_ethertag(ifc, name, NULL) != NULL) + if (vlan_clone_match_ethervid(name, NULL) != NULL) return (1); - if (strncmp(VLANNAME, name, strlen(VLANNAME)) != 0) + if (strncmp(vlanname, name, strlen(vlanname)) != 0) return (0); for (cp = name + 4; *cp != '\0'; cp++) { if (*cp < '0' || *cp > '9') @@ -856,7 +885,7 @@ vlan_clone_create(struct if_clone *ifc, char *name, size_t len, caddr_t params) int wildcard; int unit; int error; - int tag; + int vid; int ethertag; struct ifvlan *ifv; struct ifnet *ifp; @@ -873,7 +902,10 @@ vlan_clone_create(struct if_clone *ifc, char *name, size_t len, caddr_t params) * o specify no parameters and get an unattached device that * must be configured separately. * The first technique is preferred; the latter two are - * supported for backwards compatibilty. + * supported for backwards compatibility. + * + * XXXRW: Note historic use of the word "tag" here. New ioctls may be + * called for. */ if (params) { error = copyin(params, &vlr, sizeof(vlr)); @@ -881,31 +913,18 @@ vlan_clone_create(struct if_clone *ifc, char *name, size_t len, caddr_t params) return error; p = ifunit(vlr.vlr_parent); if (p == NULL) - return ENXIO; - /* - * Don't let the caller set up a VLAN tag with - * anything except VLID bits. - */ - if (vlr.vlr_tag & ~EVL_VLID_MASK) - return (EINVAL); + return (ENXIO); error = ifc_name2unit(name, &unit); if (error != 0) return (error); ethertag = 1; - tag = vlr.vlr_tag; + vid = vlr.vlr_tag; wildcard = (unit < 0); - } else if ((p = vlan_clone_match_ethertag(ifc, name, &tag)) != NULL) { + } else if ((p = vlan_clone_match_ethervid(name, &vid)) != NULL) { ethertag = 1; unit = -1; wildcard = 0; - - /* - * Don't let the caller set up a VLAN tag with - * anything except VLID bits. - */ - if (tag & ~EVL_VLID_MASK) - return (EINVAL); } else { ethertag = 0; @@ -937,14 +956,13 @@ vlan_clone_create(struct if_clone *ifc, char *name, size_t len, caddr_t params) return (ENOSPC); } SLIST_INIT(&ifv->vlan_mc_listhead); - ifp->if_softc = ifv; /* * Set the name manually rather than using if_initname because * we don't conform to the default naming convention for interfaces. */ strlcpy(ifp->if_xname, name, IFNAMSIZ); - ifp->if_dname = ifc->ifc_name; + ifp->if_dname = vlanname; ifp->if_dunit = unit; /* NB: flags are not set here */ ifp->if_linkmib = &ifv->ifv_mib; @@ -966,7 +984,7 @@ vlan_clone_create(struct if_clone *ifc, char *name, size_t len, caddr_t params) sdl->sdl_type = IFT_L2VLAN; if (ethertag) { - error = vlan_config(ifv, p, tag); + error = vlan_config(ifv, p, vid); if (error != 0) { /* * Since we've partially failed, we need to back @@ -975,7 +993,7 @@ vlan_clone_create(struct if_clone *ifc, char *name, size_t len, caddr_t params) */ ether_ifdetach(ifp); vlan_unconfig(ifp); - if_free_type(ifp, IFT_ETHER); + if_free(ifp); ifc_free_unit(ifc, unit); free(ifv, M_VLAN); @@ -997,7 +1015,7 @@ vlan_clone_destroy(struct if_clone *ifc, struct ifnet *ifp) ether_ifdetach(ifp); /* first, remove it from system-wide lists */ vlan_unconfig(ifp); /* now it can be unconfigured and freed */ - if_free_type(ifp, IFT_ETHER); + if_free(ifp); free(ifv, M_VLAN); ifc_free_unit(ifc, unit); @@ -1020,6 +1038,8 @@ vlan_transmit(struct ifnet *ifp, struct mbuf *m) { struct ifvlan *ifv; struct ifnet *p; + struct m_tag *mtag; + uint16_t tag; int error, len, mcast; ifv = ifp->if_softc; @@ -1035,7 +1055,7 @@ vlan_transmit(struct ifnet *ifp, struct mbuf *m) */ if (!UP_AND_RUNNING(p)) { m_freem(m); - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); return (ENETDOWN); } @@ -1051,7 +1071,7 @@ vlan_transmit(struct ifnet *ifp, struct mbuf *m) * devices that just discard such runts instead or mishandle * them somehow. */ - if (soft_pad && p->if_type == IFT_ETHER) { + if (V_soft_pad && p->if_type == IFT_ETHER) { static char pad[8]; /* just zeros */ int n; @@ -1062,7 +1082,7 @@ vlan_transmit(struct ifnet *ifp, struct mbuf *m) if (n > 0) { if_printf(ifp, "cannot pad short frame\n"); - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); m_freem(m); return (0); } @@ -1075,14 +1095,19 @@ vlan_transmit(struct ifnet *ifp, struct mbuf *m) * knows how to find the VLAN tag to use, so we attach a * packet tag that holds it. */ + if (vlan_mtag_pcp && (mtag = m_tag_locate(m, MTAG_8021Q, + MTAG_8021Q_PCP_OUT, NULL)) != NULL) + tag = EVL_MAKETAG(ifv->ifv_vid, *(uint8_t *)(mtag + 1), 0); + else + tag = ifv->ifv_tag; if (p->if_capenable & IFCAP_VLAN_HWTAGGING) { - m->m_pkthdr.ether_vtag = ifv->ifv_tag; + m->m_pkthdr.ether_vtag = tag; m->m_flags |= M_VLANTAG; } else { - m = ether_vlanencap(m, ifv->ifv_tag); + m = ether_vlanencap(m, tag); if (m == NULL) { if_printf(ifp, "unable to prepend VLAN header\n"); - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); return (0); } } @@ -1091,12 +1116,12 @@ vlan_transmit(struct ifnet *ifp, struct mbuf *m) * Send it, precisely as ether_output() would have. */ error = (p->if_transmit)(p, m); - if (!error) { - ifp->if_opackets++; - ifp->if_omcasts += mcast; - ifp->if_obytes += len; + if (error == 0) { + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); + if_inc_counter(ifp, IFCOUNTER_OBYTES, len); + if_inc_counter(ifp, IFCOUNTER_OMCASTS, mcast); } else - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); return (error); } @@ -1113,7 +1138,9 @@ vlan_input(struct ifnet *ifp, struct mbuf *m) { struct ifvlantrunk *trunk = ifp->if_vlantrunk; struct ifvlan *ifv; - uint16_t tag; + TRUNK_LOCK_READER; + struct m_tag *mtag; + uint16_t vid, tag; KASSERT(trunk != NULL, ("%s: no trunk", __func__)); @@ -1122,7 +1149,7 @@ vlan_input(struct ifnet *ifp, struct mbuf *m) * Packet is tagged, but m contains a normal * Ethernet frame; the tag is stored out-of-band. */ - tag = EVL_VLANOFTAG(m->m_pkthdr.ether_vtag); + tag = m->m_pkthdr.ether_vtag; m->m_flags &= ~M_VLANTAG; } else { struct ether_vlan_header *evl; @@ -1138,7 +1165,7 @@ vlan_input(struct ifnet *ifp, struct mbuf *m) return; } evl = mtod(m, struct ether_vlan_header *); - tag = EVL_VLANOFTAG(ntohs(evl->evl_tag)); + tag = ntohs(evl->evl_tag); /* * Remove the 802.1q header by copying the Ethernet @@ -1157,43 +1184,75 @@ vlan_input(struct ifnet *ifp, struct mbuf *m) __func__, ifp->if_xname, ifp->if_type); #endif m_freem(m); - ifp->if_noproto++; + if_inc_counter(ifp, IFCOUNTER_NOPROTO, 1); return; } } + vid = EVL_VLANOFTAG(tag); + TRUNK_RLOCK(trunk); - ifv = vlan_gethash(trunk, tag); + ifv = vlan_gethash(trunk, vid); if (ifv == NULL || !UP_AND_RUNNING(ifv->ifv_ifp)) { TRUNK_RUNLOCK(trunk); m_freem(m); - ifp->if_noproto++; + if_inc_counter(ifp, IFCOUNTER_NOPROTO, 1); return; } TRUNK_RUNLOCK(trunk); + if (vlan_mtag_pcp) { + /* + * While uncommon, it is possible that we will find a 802.1q + * packet encapsulated inside another packet that also had an + * 802.1q header. For example, ethernet tunneled over IPSEC + * arriving over ethernet. In that case, we replace the + * existing 802.1q PCP m_tag value. + */ + mtag = m_tag_locate(m, MTAG_8021Q, MTAG_8021Q_PCP_IN, NULL); + if (mtag == NULL) { + mtag = m_tag_alloc(MTAG_8021Q, MTAG_8021Q_PCP_IN, + sizeof(uint8_t), M_NOWAIT); + if (mtag == NULL) { + m_freem(m); + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); + return; + } + m_tag_prepend(m, mtag); + } + *(uint8_t *)(mtag + 1) = EVL_PRIOFTAG(tag); + } + m->m_pkthdr.rcvif = ifv->ifv_ifp; - ifv->ifv_ifp->if_ipackets++; + if_inc_counter(ifv->ifv_ifp, IFCOUNTER_IPACKETS, 1); /* Pass it back through the parent's input routine. */ (*ifp->if_input)(ifv->ifv_ifp, m); } static int -vlan_config(struct ifvlan *ifv, struct ifnet *p, uint16_t tag) +vlan_config(struct ifvlan *ifv, struct ifnet *p, uint16_t vid) { struct ifvlantrunk *trunk; struct ifnet *ifp; int error = 0; - /* VID numbers 0x0 and 0xFFF are reserved */ - if (tag == 0 || tag == 0xFFF) - return (EINVAL); + /* + * We can handle non-ethernet hardware types as long as + * they handle the tagging and headers themselves. + */ if (p->if_type != IFT_ETHER && (p->if_capenable & IFCAP_VLAN_HWTAGGING) == 0) return (EPROTONOSUPPORT); if ((p->if_flags & VLAN_IFFLAGS) != VLAN_IFFLAGS) return (EPROTONOSUPPORT); + /* + * Don't let the caller set up a VLAN VID with + * anything except VLID bits. + * VID numbers 0x0 and 0xFFF are reserved. + */ + if (vid == 0 || vid == 0xFFF || (vid & ~EVL_VLID_MASK)) + return (EINVAL); if (ifv->ifv_trunk) return (EBUSY); @@ -1203,7 +1262,7 @@ vlan_config(struct ifvlan *ifv, struct ifnet *p, uint16_t tag) vlan_inithash(trunk); VLAN_LOCK(); if (p->if_vlantrunk != NULL) { - /* A race that that is very unlikely to be hit. */ + /* A race that is very unlikely to be hit. */ vlan_freehash(trunk); free(trunk, M_VLAN); goto exists; @@ -1219,7 +1278,9 @@ exists: TRUNK_LOCK(trunk); } - ifv->ifv_tag = tag; /* must set this before vlan_inshash() */ + ifv->ifv_vid = vid; /* must set this before vlan_inshash() */ + ifv->ifv_pcp = 0; /* Default: best effort delivery. */ + vlan_tag_recalculate(ifv); error = vlan_inshash(trunk, ifv); if (error) goto done; @@ -1297,7 +1358,7 @@ exists: done: TRUNK_UNLOCK(trunk); if (error == 0) - EVENTHANDLER_INVOKE(vlan_config, p, ifv->ifv_tag); + EVENTHANDLER_INVOKE(vlan_config, p, ifv->ifv_vid); VLAN_UNLOCK(); return (error); @@ -1366,7 +1427,7 @@ vlan_unconfig_locked(struct ifnet *ifp, int departing) * Check if we were the last. */ if (trunk->refcnt == 0) { - trunk->parent->if_vlantrunk = NULL; + parent->if_vlantrunk = NULL; /* * XXXGL: If some ithread has already entered * vlan_input() and is now blocked on the trunk @@ -1393,7 +1454,7 @@ vlan_unconfig_locked(struct ifnet *ifp, int departing) * to cleanup anyway. */ if (parent != NULL) - EVENTHANDLER_INVOKE(vlan_unconfig, parent, ifv->ifv_tag); + EVENTHANDLER_INVOKE(vlan_unconfig, parent, ifv->ifv_vid); } /* Handle a reference counted flag that should be set on the parent as well */ @@ -1494,7 +1555,7 @@ vlan_capabilities(struct ifvlan *ifv) p->if_capenable & IFCAP_VLAN_HWTAGGING) { ifp->if_capenable = p->if_capenable & IFCAP_HWCSUM; ifp->if_hwassist = p->if_hwassist & (CSUM_IP | CSUM_TCP | - CSUM_UDP | CSUM_SCTP | CSUM_FRAGMENT); + CSUM_UDP | CSUM_SCTP); } else { ifp->if_capenable = 0; ifp->if_hwassist = 0; @@ -1562,6 +1623,7 @@ vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) struct ifreq *ifr; struct ifaddr *ifa; struct ifvlan *ifv; + struct ifvlantrunk *trunk; struct vlanreq vlr; int error = 0; @@ -1633,6 +1695,13 @@ vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) case SIOCSETVLAN: #ifdef VIMAGE + /* + * XXXRW/XXXBZ: The goal in these checks is to allow a VLAN + * interface to be delegated to a jail without allowing the + * jail to change what underlying interface/VID it is + * associated with. We are not entirely convinced that this + * is the right way to accomplish that policy goal. + */ if (ifp->if_vnet != ifp->if_home_vnet) { error = EPERM; break; @@ -1650,14 +1719,6 @@ vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 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; - } error = vlan_config(ifv, p, vlr.vlr_tag); if (error) break; @@ -1678,7 +1739,7 @@ vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) if (TRUNK(ifv) != NULL) { strlcpy(vlr.vlr_parent, PARENT(ifv)->if_xname, sizeof(vlr.vlr_parent)); - vlr.vlr_tag = ifv->ifv_tag; + vlr.vlr_tag = ifv->ifv_vid; } VLAN_UNLOCK(); error = copyout(&vlr, ifr->ifr_data, sizeof(vlr)); @@ -1699,8 +1760,40 @@ vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) * If we don't have a parent, just remember the membership for * when we do. */ - if (TRUNK(ifv) != NULL) + trunk = TRUNK(ifv); + if (trunk != NULL) { + TRUNK_LOCK(trunk); error = vlan_setmulti(ifp); + TRUNK_UNLOCK(trunk); + } + break; + + case SIOCGVLANPCP: +#ifdef VIMAGE + if (ifp->if_vnet != ifp->if_home_vnet) { + error = EPERM; + break; + } +#endif + ifr->ifr_vlan_pcp = ifv->ifv_pcp; + break; + + case SIOCSVLANPCP: +#ifdef VIMAGE + if (ifp->if_vnet != ifp->if_home_vnet) { + error = EPERM; + break; + } +#endif + error = priv_check(curthread, PRIV_NET_SETVLANPCP); + if (error) + break; + if (ifr->ifr_vlan_pcp > 7) { + error = EINVAL; + break; + } + ifv->ifv_pcp = ifr->ifr_vlan_pcp; + vlan_tag_recalculate(ifv); break; default: |