Index: net/if.c =================================================================== --- net/if.c (revision 216918) +++ net/if.c (working copy) @@ -1865,6 +1865,11 @@ void (*vlan_link_state_p)(struct ifnet *); /* XXX: private from if_vlan */ void (*vlan_trunk_cap_p)(struct ifnet *); /* XXX: private from if_vlan */ +struct ifnet *(*vlan_trunkdev_p)(struct ifnet *); +struct ifnet *(*vlan_devat_p)(struct ifnet *, uint16_t); +int (*vlan_tag_p)(struct ifnet *, uint16_t *); +int (*vlan_setcookie_p)(struct ifnet *, void *); +void *(*vlan_cookie_p)(struct ifnet *); /* * Handle a change in the interface link state. To avoid LORs Index: net/if_vlan_var.h =================================================================== --- net/if_vlan_var.h (revision 216918) +++ net/if_vlan_var.h (working copy) @@ -131,7 +131,25 @@ (*vlan_trunk_cap_p)(_ifp); \ } while (0) +#define VLAN_TRUNKDEV(_ifp) \ + (_ifp)->if_type == IFT_L2VLAN ? (*vlan_trunkdev_p)((_ifp)) : NULL +#define VLAN_TAG(_ifp, _tag) \ + (_ifp)->if_type == IFT_L2VLAN ? (*vlan_tag_p)((_ifp), (_tag)) : EINVAL +#define VLAN_COOKIE(_ifp) \ + (_ifp)->if_type == IFT_L2VLAN ? (*vlan_cookie_p)((_ifp)) : NULL +#define VLAN_SETCOOKIE(_ifp, _cookie) \ + (_ifp)->if_type == IFT_L2VLAN ? \ + (*vlan_setcookie_p)((_ifp), (_cookie)) : EINVAL +#define VLAN_DEVAT(_ifp, _tag) \ + (_ifp)->if_vlantrunk != NULL ? (*vlan_devat_p)((_ifp), (_tag)) : NULL + extern void (*vlan_trunk_cap_p)(struct ifnet *); +extern struct ifnet *(*vlan_trunkdev_p)(struct ifnet *); +extern struct ifnet *(*vlan_devat_p)(struct ifnet *, uint16_t); +extern int (*vlan_tag_p)(struct ifnet *, uint16_t *); +extern int (*vlan_setcookie_p)(struct ifnet *, void *); +extern void *(*vlan_cookie_p)(struct ifnet *); + #endif /* _KERNEL */ #endif /* _NET_IF_VLAN_VAR_H_ */ Index: net/if_vlan.c =================================================================== --- net/if_vlan.c (revision 216918) +++ net/if_vlan.c (working copy) @@ -56,6 +56,7 @@ #include #include #include +#include #include #include @@ -90,13 +91,14 @@ }; struct vlan_mc_entry { - struct ether_addr mc_addr; + struct sockaddr_dl mc_addr; SLIST_ENTRY(vlan_mc_entry) mc_entries; }; 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) int ifv_pflags; /* special flags we have set on parent */ @@ -153,12 +155,12 @@ * however on practice it does not. Probably this is because array * is too big to fit into CPU cache. */ -static struct mtx ifv_mtx; -#define VLAN_LOCK_INIT() mtx_init(&ifv_mtx, "vlan_global", 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) +static struct sx ifv_lock; +#define VLAN_LOCK_INIT() sx_init(&ifv_lock, "vlan_global") +#define VLAN_LOCK_DESTROY() sx_destroy(&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) @@ -386,6 +388,47 @@ } } #endif /* 0 */ +#else + +static __inline struct ifvlan * +vlan_gethash(struct ifvlantrunk *trunk, uint16_t tag) +{ + + return trunk->vlans[tag]; +} + +static __inline int +vlan_inshash(struct ifvlantrunk *trunk, struct ifvlan *ifv) +{ + + if (trunk->vlans[ifv->ifv_tag] != NULL) + return EEXIST; + trunk->vlans[ifv->ifv_tag] = ifv; + trunk->refcnt++; + + return (0); +} + +static __inline int +vlan_remhash(struct ifvlantrunk *trunk, struct ifvlan *ifv) +{ + + trunk->vlans[ifv->ifv_tag] = NULL; + trunk->refcnt--; + + return (0); +} + +static __inline void +vlan_freehash(struct ifvlantrunk *trunk) +{ +} + +static __inline void +vlan_inithash(struct ifvlantrunk *trunk) +{ +} + #endif /* !VLAN_ARRAY */ static void @@ -394,9 +437,7 @@ VLAN_LOCK_ASSERT(); TRUNK_LOCK(trunk); -#ifndef VLAN_ARRAY vlan_freehash(trunk); -#endif trunk->parent->if_vlantrunk = NULL; TRUNK_UNLOCK(trunk); TRUNK_LOCK_DESTROY(trunk); @@ -421,7 +462,6 @@ struct ifmultiaddr *ifma, *rifma = NULL; struct ifvlan *sc; struct vlan_mc_entry *mc; - struct sockaddr_dl sdl; int error; /*VLAN_LOCK_ASSERT();*/ @@ -432,17 +472,9 @@ CURVNET_SET_QUIET(ifp_p->if_vnet); - bzero((char *)&sdl, sizeof(sdl)); - sdl.sdl_len = sizeof(sdl); - sdl.sdl_family = AF_LINK; - sdl.sdl_index = ifp_p->if_index; - sdl.sdl_type = IFT_ETHER; - sdl.sdl_alen = ETHER_ADDR_LEN; - /* First, remove any existing filter entries. */ while ((mc = SLIST_FIRST(&sc->vlan_mc_listhead)) != NULL) { - bcopy((char *)&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN); - error = if_delmulti(ifp_p, (struct sockaddr *)&sdl); + error = if_delmulti(ifp_p, (struct sockaddr *)&mc->mc_addr); if (error) return (error); SLIST_REMOVE_HEAD(&sc->vlan_mc_listhead, mc_entries); @@ -456,12 +488,11 @@ mc = malloc(sizeof(struct vlan_mc_entry), M_VLAN, M_NOWAIT); if (mc == NULL) return (ENOMEM); - bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), - (char *)&mc->mc_addr, ETHER_ADDR_LEN); + 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); - bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), - LLADDR(&sdl), ETHER_ADDR_LEN); - error = if_addmulti(ifp_p, (struct sockaddr *)&sdl, &rifma); + error = if_addmulti(ifp_p, (struct sockaddr *)&mc->mc_addr, + &rifma); if (error) return (error); } @@ -503,7 +534,8 @@ LIST_FOREACH_SAFE(ifv, &ifp->if_vlantrunk->hash[i], ifv_list, next) { #endif /* VLAN_ARRAY */ VLAN_UNLOCK(); - if_setlladdr(ifv->ifv_ifp, IF_LLADDR(ifp), ETHER_ADDR_LEN); + if_setlladdr(ifv->ifv_ifp, IF_LLADDR(ifp), + ifp->if_addrlen); VLAN_LOCK(); } VLAN_UNLOCK(); @@ -563,6 +595,75 @@ VLAN_UNLOCK(); } +static struct ifnet * +vlan_trunkdev(struct ifnet *ifp) +{ + struct ifvlan *ifv; + + if (ifp->if_type != IFT_L2VLAN) + return (NULL); + ifv = ifp->if_softc; + ifp = NULL; + VLAN_LOCK(); + if (ifv->ifv_trunk) + ifp = PARENT(ifv); + VLAN_UNLOCK(); + return (ifp); +} + +static int +vlan_tag(struct ifnet *ifp, uint16_t *tagp) +{ + struct ifvlan *ifv; + + if (ifp->if_type != IFT_L2VLAN) + return (EINVAL); + ifv = ifp->if_softc; + *tagp = ifv->ifv_tag; + return (0); +} + +static void * +vlan_cookie(struct ifnet *ifp) +{ + struct ifvlan *ifv; + + if (ifp->if_type != IFT_L2VLAN) + return (NULL); + ifv = ifp->if_softc; + return (ifv->ifv_cookie); +} + +static int +vlan_setcookie(struct ifnet *ifp, void *cookie) +{ + struct ifvlan *ifv; + + if (ifp->if_type != IFT_L2VLAN) + return (EINVAL); + ifv = ifp->if_softc; + ifv->ifv_cookie = cookie; + return (0); +} + +static struct ifnet * +vlan_devat(struct ifnet *ifp, uint16_t tag) +{ + struct ifvlantrunk *trunk; + struct ifvlan *ifv; + + trunk = ifp->if_vlantrunk; + if (trunk == NULL) + return (NULL); + ifp = NULL; + TRUNK_RLOCK(trunk); + ifv = vlan_gethash(trunk, tag); + if (ifv) + ifp = ifv->ifv_ifp; + TRUNK_RUNLOCK(trunk); + return (ifp); +} + /* * 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 @@ -593,6 +694,11 @@ vlan_input_p = vlan_input; vlan_link_state_p = vlan_link_state; vlan_trunk_cap_p = vlan_trunk_capabilities; + vlan_trunkdev_p = vlan_trunkdev; + vlan_cookie_p = vlan_cookie; + vlan_setcookie_p = vlan_setcookie; + vlan_tag_p = vlan_tag; + vlan_devat_p = vlan_devat; #ifndef VIMAGE if_clone_attach(&vlan_cloner); #endif @@ -615,6 +721,11 @@ vlan_input_p = NULL; vlan_link_state_p = NULL; vlan_trunk_cap_p = NULL; + vlan_trunkdev_p = NULL; + vlan_tag_p = NULL; + vlan_cookie_p = vlan_cookie; + vlan_setcookie_p = vlan_setcookie; + vlan_devat_p = NULL; VLAN_LOCK_DESTROY(); if (bootverbose) printf("vlan: unloaded\n"); @@ -665,7 +776,12 @@ /* Check for . style interface names. */ IFNET_RLOCK_NOSLEEP(); TAILQ_FOREACH(ifp, &V_ifnet, if_link) { - if (ifp->if_type != IFT_ETHER) + /* + * 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; @@ -916,7 +1032,7 @@ * devices that just discard such runts instead or mishandle * them somehow. */ - if (soft_pad) { + if (soft_pad && p->if_type == IFT_ETHER) { static char pad[8]; /* just zeros */ int n; @@ -1020,11 +1136,7 @@ } TRUNK_RLOCK(trunk); -#ifdef VLAN_ARRAY - ifv = trunk->vlans[tag]; -#else ifv = vlan_gethash(trunk, tag); -#endif if (ifv == NULL || !UP_AND_RUNNING(ifv->ifv_ifp)) { TRUNK_RUNLOCK(trunk); m_freem(m); @@ -1050,7 +1162,8 @@ /* VID numbers 0x0 and 0xFFF are reserved */ if (tag == 0 || tag == 0xFFF) return (EINVAL); - if (p->if_type != IFT_ETHER) + 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); @@ -1060,15 +1173,11 @@ if (p->if_vlantrunk == NULL) { trunk = malloc(sizeof(struct ifvlantrunk), M_VLAN, M_WAITOK | M_ZERO); -#ifndef VLAN_ARRAY vlan_inithash(trunk); -#endif VLAN_LOCK(); if (p->if_vlantrunk != NULL) { /* A race that that is very unlikely to be hit. */ -#ifndef VLAN_ARRAY vlan_freehash(trunk); -#endif free(trunk, M_VLAN); goto exists; } @@ -1084,18 +1193,9 @@ } ifv->ifv_tag = tag; /* must set this before vlan_inshash() */ -#ifdef VLAN_ARRAY - if (trunk->vlans[tag] != NULL) { - error = EEXIST; - goto done; - } - trunk->vlans[tag] = ifv; - trunk->refcnt++; -#else error = vlan_inshash(trunk, ifv); if (error) goto done; -#endif ifv->ifv_proto = ETHERTYPE_VLAN; ifv->ifv_encaplen = ETHER_VLAN_ENCAP_LEN; ifv->ifv_mintu = ETHERMIN; @@ -1125,8 +1225,17 @@ ifv->ifv_trunk = trunk; ifp = ifv->ifv_ifp; + /* + * Initialize fields from our parent. + */ ifp->if_mtu = p->if_mtu - ifv->ifv_mtufudge; ifp->if_baudrate = p->if_baudrate; + ifp->if_output = p->if_output; + ifp->if_input = p->if_input; + ifp->if_resolvemulti = p->if_resolvemulti; + ifp->if_addrlen = p->if_addrlen; + ifp->if_broadcastaddr = p->if_broadcastaddr; + /* * Copy only a selected subset of flags from the parent. * Other flags are none of our business. @@ -1141,10 +1250,12 @@ vlan_capabilities(ifv); /* - * Set up our ``Ethernet address'' to reflect the underlying + * Set up our interface address to reflect the underlying * physical interface's. */ - bcopy(IF_LLADDR(p), IF_LLADDR(ifp), ETHER_ADDR_LEN); + bcopy(IF_LLADDR(p), IF_LLADDR(ifp), p->if_addrlen); + ((struct sockaddr_dl *)ifp->if_addr->ifa_addr)->sdl_alen = + p->if_addrlen; /* * Configure multicast addresses that may already be @@ -1187,7 +1298,6 @@ parent = NULL; if (trunk != NULL) { - struct sockaddr_dl sdl; TRUNK_LOCK(trunk); parent = trunk->parent; @@ -1197,17 +1307,7 @@ * empty the list of multicast groups that we may have joined * while we were alive from the parent's list. */ - bzero((char *)&sdl, sizeof(sdl)); - sdl.sdl_len = sizeof(sdl); - sdl.sdl_family = AF_LINK; - sdl.sdl_index = parent->if_index; - sdl.sdl_type = IFT_ETHER; - sdl.sdl_alen = ETHER_ADDR_LEN; - while ((mc = SLIST_FIRST(&ifv->vlan_mc_listhead)) != NULL) { - bcopy((char *)&mc->mc_addr, LLADDR(&sdl), - ETHER_ADDR_LEN); - /* * This may fail if the parent interface is * being detached. Regardless, we should do a @@ -1215,18 +1315,14 @@ * as possible as all callers expect vlan * destruction to succeed. */ - (void)if_delmulti(parent, (struct sockaddr *)&sdl); + (void)if_delmulti(parent, + (struct sockaddr *)&mc->mc_addr); SLIST_REMOVE_HEAD(&ifv->vlan_mc_listhead, mc_entries); free(mc, M_VLAN); } vlan_setflags(ifp, 0); /* clear special flags on parent */ -#ifdef VLAN_ARRAY - trunk->vlans[ifv->ifv_tag] = NULL; - trunk->refcnt--; -#else vlan_remhash(trunk, ifv); -#endif ifv->ifv_trunk = NULL; /* @@ -1407,14 +1503,31 @@ { struct ifnet *p; struct ifreq *ifr; + struct ifaddr *ifa; 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; +#ifdef INET + if (ifa->ifa_addr->sa_family == AF_INET) + arp_ifinit(ifp, ifa); +#endif + break; + case SIOCGIFADDR: + { + struct sockaddr *sa; + + sa = (struct sockaddr *)&ifr->ifr_data; + bcopy(IF_LLADDR(ifp), sa->sa_data, ifp->if_addrlen); + } + break; case SIOCGIFMEDIA: VLAN_LOCK(); if (TRUNK(ifv) != NULL) { @@ -1534,7 +1647,8 @@ break; default: - error = ether_ioctl(ifp, cmd, data); + error = EINVAL; + break; } return (error);