1c398230bSWarner Losh /*- 22cc2df49SGarrett Wollman * Copyright 1998 Massachusetts Institute of Technology 32ccbbd06SMarcelo Araujo * Copyright 2012 ADARA Networks, Inc. 4d148c2a2SMatt Joras * Copyright 2017 Dell EMC Isilon 52ccbbd06SMarcelo Araujo * 62ccbbd06SMarcelo Araujo * Portions of this software were developed by Robert N. M. Watson under 72ccbbd06SMarcelo Araujo * contract to ADARA Networks, Inc. 82cc2df49SGarrett Wollman * 92cc2df49SGarrett Wollman * Permission to use, copy, modify, and distribute this software and 102cc2df49SGarrett Wollman * its documentation for any purpose and without fee is hereby 112cc2df49SGarrett Wollman * granted, provided that both the above copyright notice and this 122cc2df49SGarrett Wollman * permission notice appear in all copies, that both the above 132cc2df49SGarrett Wollman * copyright notice and this permission notice appear in all 142cc2df49SGarrett Wollman * supporting documentation, and that the name of M.I.T. not be used 152cc2df49SGarrett Wollman * in advertising or publicity pertaining to distribution of the 162cc2df49SGarrett Wollman * software without specific, written prior permission. M.I.T. makes 172cc2df49SGarrett Wollman * no representations about the suitability of this software for any 182cc2df49SGarrett Wollman * purpose. It is provided "as is" without express or implied 192cc2df49SGarrett Wollman * warranty. 202cc2df49SGarrett Wollman * 212cc2df49SGarrett Wollman * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS 222cc2df49SGarrett Wollman * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, 232cc2df49SGarrett Wollman * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 242cc2df49SGarrett Wollman * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT 252cc2df49SGarrett Wollman * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 262cc2df49SGarrett Wollman * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 272cc2df49SGarrett Wollman * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 282cc2df49SGarrett Wollman * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 292cc2df49SGarrett Wollman * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 302cc2df49SGarrett Wollman * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 312cc2df49SGarrett Wollman * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 322cc2df49SGarrett Wollman * SUCH DAMAGE. 332cc2df49SGarrett Wollman */ 342cc2df49SGarrett Wollman 352cc2df49SGarrett Wollman /* 362cc2df49SGarrett Wollman * if_vlan.c - pseudo-device driver for IEEE 802.1Q virtual LANs. 372ccbbd06SMarcelo Araujo * This is sort of sneaky in the implementation, since 382cc2df49SGarrett Wollman * we need to pretend to be enough of an Ethernet implementation 392cc2df49SGarrett Wollman * to make arp work. The way we do this is by telling everyone 402cc2df49SGarrett Wollman * that we are an Ethernet, and then catch the packets that 41d9b1d615SJohn Baldwin * ether_output() sends to us via if_transmit(), rewrite them for 42d9b1d615SJohn Baldwin * use by the real outgoing interface, and ask it to send them. 432cc2df49SGarrett Wollman */ 442cc2df49SGarrett Wollman 458b2d9181SPyun YongHyeon #include <sys/cdefs.h> 468b2d9181SPyun YongHyeon __FBSDID("$FreeBSD$"); 478b2d9181SPyun YongHyeon 482c5b403eSOleg Bulyzhin #include "opt_inet.h" 4975ee267cSGleb Smirnoff #include "opt_vlan.h" 50f3e7afe2SHans Petter Selasky #include "opt_ratelimit.h" 512cc2df49SGarrett Wollman 522cc2df49SGarrett Wollman #include <sys/param.h> 53c3322cb9SGleb Smirnoff #include <sys/eventhandler.h> 542cc2df49SGarrett Wollman #include <sys/kernel.h> 5575ee267cSGleb Smirnoff #include <sys/lock.h> 56f731f104SBill Paul #include <sys/malloc.h> 572cc2df49SGarrett Wollman #include <sys/mbuf.h> 582b120974SPeter Wemm #include <sys/module.h> 59772b000fSAlexander V. Chernikov #include <sys/rmlock.h> 602ccbbd06SMarcelo Araujo #include <sys/priv.h> 61f731f104SBill Paul #include <sys/queue.h> 622cc2df49SGarrett Wollman #include <sys/socket.h> 632cc2df49SGarrett Wollman #include <sys/sockio.h> 642cc2df49SGarrett Wollman #include <sys/sysctl.h> 652cc2df49SGarrett Wollman #include <sys/systm.h> 66e4cd31ddSJeff Roberson #include <sys/sx.h> 67d148c2a2SMatt Joras #include <sys/taskqueue.h> 682cc2df49SGarrett Wollman 692cc2df49SGarrett Wollman #include <net/bpf.h> 702cc2df49SGarrett Wollman #include <net/ethernet.h> 712cc2df49SGarrett Wollman #include <net/if.h> 7276039bc8SGleb Smirnoff #include <net/if_var.h> 73f889d2efSBrooks Davis #include <net/if_clone.h> 742cc2df49SGarrett Wollman #include <net/if_dl.h> 752cc2df49SGarrett Wollman #include <net/if_types.h> 762cc2df49SGarrett Wollman #include <net/if_vlan_var.h> 774b79449eSBjoern A. Zeeb #include <net/vnet.h> 782cc2df49SGarrett Wollman 792c5b403eSOleg Bulyzhin #ifdef INET 802c5b403eSOleg Bulyzhin #include <netinet/in.h> 812c5b403eSOleg Bulyzhin #include <netinet/if_ether.h> 822c5b403eSOleg Bulyzhin #endif 832c5b403eSOleg Bulyzhin 8475ee267cSGleb Smirnoff #define VLAN_DEF_HWIDTH 4 8564a17d2eSYaroslav Tykhiy #define VLAN_IFFLAGS (IFF_BROADCAST | IFF_MULTICAST) 8675ee267cSGleb Smirnoff 872dc879b3SYaroslav Tykhiy #define UP_AND_RUNNING(ifp) \ 882dc879b3SYaroslav Tykhiy ((ifp)->if_flags & IFF_UP && (ifp)->if_drv_flags & IFF_DRV_RUNNING) 892dc879b3SYaroslav Tykhiy 90b08d611dSMatt Macy CK_SLIST_HEAD(ifvlanhead, ifvlan); 9175ee267cSGleb Smirnoff 9275ee267cSGleb Smirnoff struct ifvlantrunk { 9375ee267cSGleb Smirnoff struct ifnet *parent; /* parent interface of this trunk */ 94b08d611dSMatt Macy struct mtx lock; 9575ee267cSGleb Smirnoff #ifdef VLAN_ARRAY 965cb8c31aSYaroslav Tykhiy #define VLAN_ARRAY_SIZE (EVL_VLID_MASK + 1) 975cb8c31aSYaroslav Tykhiy struct ifvlan *vlans[VLAN_ARRAY_SIZE]; /* static table */ 9875ee267cSGleb Smirnoff #else 9975ee267cSGleb Smirnoff struct ifvlanhead *hash; /* dynamic hash-list table */ 10075ee267cSGleb Smirnoff uint16_t hmask; 10175ee267cSGleb Smirnoff uint16_t hwidth; 10275ee267cSGleb Smirnoff #endif 10375ee267cSGleb Smirnoff int refcnt; 10475ee267cSGleb Smirnoff }; 1059d4fe4b2SBrooks Davis 106*fb3bc596SJohn Baldwin #ifdef RATELIMIT 107*fb3bc596SJohn Baldwin struct vlan_snd_tag { 108*fb3bc596SJohn Baldwin struct m_snd_tag com; 109*fb3bc596SJohn Baldwin struct m_snd_tag *tag; 110*fb3bc596SJohn Baldwin }; 111*fb3bc596SJohn Baldwin 112*fb3bc596SJohn Baldwin static inline struct vlan_snd_tag * 113*fb3bc596SJohn Baldwin mst_to_vst(struct m_snd_tag *mst) 114*fb3bc596SJohn Baldwin { 115*fb3bc596SJohn Baldwin 116*fb3bc596SJohn Baldwin return (__containerof(mst, struct vlan_snd_tag, com)); 117*fb3bc596SJohn Baldwin } 118*fb3bc596SJohn Baldwin #endif 119*fb3bc596SJohn Baldwin 120d148c2a2SMatt Joras /* 121d148c2a2SMatt Joras * This macro provides a facility to iterate over every vlan on a trunk with 122d148c2a2SMatt Joras * the assumption that none will be added/removed during iteration. 123d148c2a2SMatt Joras */ 124d148c2a2SMatt Joras #ifdef VLAN_ARRAY 125d148c2a2SMatt Joras #define VLAN_FOREACH(_ifv, _trunk) \ 126d148c2a2SMatt Joras size_t _i; \ 127d148c2a2SMatt Joras for (_i = 0; _i < VLAN_ARRAY_SIZE; _i++) \ 128d148c2a2SMatt Joras if (((_ifv) = (_trunk)->vlans[_i]) != NULL) 129d148c2a2SMatt Joras #else /* VLAN_ARRAY */ 130d148c2a2SMatt Joras #define VLAN_FOREACH(_ifv, _trunk) \ 131d148c2a2SMatt Joras struct ifvlan *_next; \ 132d148c2a2SMatt Joras size_t _i; \ 133d148c2a2SMatt Joras for (_i = 0; _i < (1 << (_trunk)->hwidth); _i++) \ 134b08d611dSMatt Macy CK_SLIST_FOREACH_SAFE((_ifv), &(_trunk)->hash[_i], ifv_list, _next) 135d148c2a2SMatt Joras #endif /* VLAN_ARRAY */ 136d148c2a2SMatt Joras 137d148c2a2SMatt Joras /* 138d148c2a2SMatt Joras * This macro provides a facility to iterate over every vlan on a trunk while 139d148c2a2SMatt Joras * also modifying the number of vlans on the trunk. The iteration continues 140d148c2a2SMatt Joras * until some condition is met or there are no more vlans on the trunk. 141d148c2a2SMatt Joras */ 142d148c2a2SMatt Joras #ifdef VLAN_ARRAY 143d148c2a2SMatt Joras /* The VLAN_ARRAY case is simple -- just a for loop using the condition. */ 144d148c2a2SMatt Joras #define VLAN_FOREACH_UNTIL_SAFE(_ifv, _trunk, _cond) \ 145d148c2a2SMatt Joras size_t _i; \ 146d148c2a2SMatt Joras for (_i = 0; !(_cond) && _i < VLAN_ARRAY_SIZE; _i++) \ 147d148c2a2SMatt Joras if (((_ifv) = (_trunk)->vlans[_i])) 148d148c2a2SMatt Joras #else /* VLAN_ARRAY */ 149d148c2a2SMatt Joras /* 150d148c2a2SMatt Joras * The hash table case is more complicated. We allow for the hash table to be 151d148c2a2SMatt Joras * modified (i.e. vlans removed) while we are iterating over it. To allow for 152d148c2a2SMatt Joras * this we must restart the iteration every time we "touch" something during 153d148c2a2SMatt Joras * the iteration, since removal will resize the hash table and invalidate our 154d148c2a2SMatt Joras * current position. If acting on the touched element causes the trunk to be 155d148c2a2SMatt Joras * emptied, then iteration also stops. 156d148c2a2SMatt Joras */ 157d148c2a2SMatt Joras #define VLAN_FOREACH_UNTIL_SAFE(_ifv, _trunk, _cond) \ 158d148c2a2SMatt Joras size_t _i; \ 159d148c2a2SMatt Joras bool _touch = false; \ 160d148c2a2SMatt Joras for (_i = 0; \ 161d148c2a2SMatt Joras !(_cond) && _i < (1 << (_trunk)->hwidth); \ 162d148c2a2SMatt Joras _i = (_touch && ((_trunk) != NULL) ? 0 : _i + 1), _touch = false) \ 163b08d611dSMatt Macy if (((_ifv) = CK_SLIST_FIRST(&(_trunk)->hash[_i])) != NULL && \ 164d148c2a2SMatt Joras (_touch = true)) 165d148c2a2SMatt Joras #endif /* VLAN_ARRAY */ 166d148c2a2SMatt Joras 167a3814acfSSam Leffler struct vlan_mc_entry { 168e4cd31ddSJeff Roberson struct sockaddr_dl mc_addr; 169b08d611dSMatt Macy CK_SLIST_ENTRY(vlan_mc_entry) mc_entries; 170c32a9d66SHans Petter Selasky struct epoch_context mc_epoch_ctx; 171a3814acfSSam Leffler }; 172a3814acfSSam Leffler 173a3814acfSSam Leffler struct ifvlan { 17475ee267cSGleb Smirnoff struct ifvlantrunk *ifv_trunk; 175fc74a9f9SBrooks Davis struct ifnet *ifv_ifp; 17675ee267cSGleb Smirnoff #define TRUNK(ifv) ((ifv)->ifv_trunk) 17775ee267cSGleb Smirnoff #define PARENT(ifv) ((ifv)->ifv_trunk->parent) 1786667db31SAlexander V. Chernikov void *ifv_cookie; 1791cf236fbSYaroslav Tykhiy int ifv_pflags; /* special flags we have set on parent */ 180d89baa5aSAlexander Motin int ifv_capenable; 18172755d28SMark Johnston int ifv_encaplen; /* encapsulation length */ 18272755d28SMark Johnston int ifv_mtufudge; /* MTU fudged by this much */ 18372755d28SMark Johnston int ifv_mintu; /* min transmission unit */ 18472755d28SMark Johnston uint16_t ifv_proto; /* encapsulation ethertype */ 18572755d28SMark Johnston uint16_t ifv_tag; /* tag to apply on packets leaving if */ 18672755d28SMark Johnston uint16_t ifv_vid; /* VLAN ID */ 18772755d28SMark Johnston uint8_t ifv_pcp; /* Priority Code Point (PCP). */ 188d148c2a2SMatt Joras struct task lladdr_task; 189b08d611dSMatt Macy CK_SLIST_HEAD(, vlan_mc_entry) vlan_mc_listhead; 190c0cb022bSYaroslav Tykhiy #ifndef VLAN_ARRAY 191b08d611dSMatt Macy CK_SLIST_ENTRY(ifvlan) ifv_list; 192c0cb022bSYaroslav Tykhiy #endif 193a3814acfSSam Leffler }; 194a3814acfSSam Leffler 19575ee267cSGleb Smirnoff /* Special flags we should propagate to parent. */ 1961cf236fbSYaroslav Tykhiy static struct { 1971cf236fbSYaroslav Tykhiy int flag; 1981cf236fbSYaroslav Tykhiy int (*func)(struct ifnet *, int); 1991cf236fbSYaroslav Tykhiy } vlan_pflags[] = { 2001cf236fbSYaroslav Tykhiy {IFF_PROMISC, ifpromisc}, 2011cf236fbSYaroslav Tykhiy {IFF_ALLMULTI, if_allmulti}, 2021cf236fbSYaroslav Tykhiy {0, NULL} 2031cf236fbSYaroslav Tykhiy }; 204a3814acfSSam Leffler 205f1379734SKonstantin Belousov extern int vlan_mtag_pcp; 2062ccbbd06SMarcelo Araujo 20742a58907SGleb Smirnoff static const char vlanname[] = "vlan"; 20842a58907SGleb Smirnoff static MALLOC_DEFINE(M_VLAN, vlanname, "802.1Q Virtual LAN Interface"); 2092cc2df49SGarrett Wollman 2105cb8c31aSYaroslav Tykhiy static eventhandler_tag ifdetach_tag; 211ea4ca115SAndrew Thompson static eventhandler_tag iflladdr_tag; 2125cb8c31aSYaroslav Tykhiy 2134faedfe8SSam Leffler /* 214b08d611dSMatt Macy * if_vlan uses two module-level synchronizations primitives to allow concurrent 215b08d611dSMatt Macy * modification of vlan interfaces and (mostly) allow for vlans to be destroyed 216b08d611dSMatt Macy * while they are being used for tx/rx. To accomplish this in a way that has 217b08d611dSMatt Macy * acceptable performance and cooperation with other parts of the network stack 218b08d611dSMatt Macy * there is a non-sleepable epoch(9) and an sx(9). 21975ee267cSGleb Smirnoff * 220b08d611dSMatt Macy * The performance-sensitive paths that warrant using the epoch(9) are 221d148c2a2SMatt Joras * vlan_transmit and vlan_input. Both have to check for the vlan interface's 222d148c2a2SMatt Joras * existence using if_vlantrunk, and being in the network tx/rx paths the use 223b08d611dSMatt Macy * of an epoch(9) gives a measureable improvement in performance. 22475ee267cSGleb Smirnoff * 225d148c2a2SMatt Joras * The reason for having an sx(9) is mostly because there are still areas that 226d148c2a2SMatt Joras * must be sleepable and also have safe concurrent access to a vlan interface. 227d148c2a2SMatt Joras * Since the sx(9) exists, it is used by default in most paths unless sleeping 228d148c2a2SMatt Joras * is not permitted, or if it is not clear whether sleeping is permitted. 229d148c2a2SMatt Joras * 2304faedfe8SSam Leffler */ 231d148c2a2SMatt Joras #define _VLAN_SX_ID ifv_sx 232d148c2a2SMatt Joras 233d148c2a2SMatt Joras static struct sx _VLAN_SX_ID; 234d148c2a2SMatt Joras 235d148c2a2SMatt Joras #define VLAN_LOCKING_INIT() \ 236d148c2a2SMatt Joras sx_init(&_VLAN_SX_ID, "vlan_sx") 237d148c2a2SMatt Joras 238d148c2a2SMatt Joras #define VLAN_LOCKING_DESTROY() \ 239d148c2a2SMatt Joras sx_destroy(&_VLAN_SX_ID) 240d148c2a2SMatt Joras 241d148c2a2SMatt Joras #define VLAN_SLOCK() sx_slock(&_VLAN_SX_ID) 242d148c2a2SMatt Joras #define VLAN_SUNLOCK() sx_sunlock(&_VLAN_SX_ID) 243d148c2a2SMatt Joras #define VLAN_XLOCK() sx_xlock(&_VLAN_SX_ID) 244d148c2a2SMatt Joras #define VLAN_XUNLOCK() sx_xunlock(&_VLAN_SX_ID) 245d148c2a2SMatt Joras #define VLAN_SLOCK_ASSERT() sx_assert(&_VLAN_SX_ID, SA_SLOCKED) 246d148c2a2SMatt Joras #define VLAN_XLOCK_ASSERT() sx_assert(&_VLAN_SX_ID, SA_XLOCKED) 247d148c2a2SMatt Joras #define VLAN_SXLOCK_ASSERT() sx_assert(&_VLAN_SX_ID, SA_LOCKED) 248d148c2a2SMatt Joras 249d148c2a2SMatt Joras 250d148c2a2SMatt Joras /* 251b08d611dSMatt Macy * We also have a per-trunk mutex that should be acquired when changing 252b08d611dSMatt Macy * its state. 253d148c2a2SMatt Joras */ 254b08d611dSMatt Macy #define TRUNK_LOCK_INIT(trunk) mtx_init(&(trunk)->lock, vlanname, NULL, MTX_DEF) 255b08d611dSMatt Macy #define TRUNK_LOCK_DESTROY(trunk) mtx_destroy(&(trunk)->lock) 256b08d611dSMatt Macy #define TRUNK_WLOCK(trunk) mtx_lock(&(trunk)->lock) 257b08d611dSMatt Macy #define TRUNK_WUNLOCK(trunk) mtx_unlock(&(trunk)->lock) 258b08d611dSMatt Macy #define TRUNK_LOCK_ASSERT(trunk) MPASS(in_epoch(net_epoch_preempt) || mtx_owned(&(trunk)->lock)) 259b08d611dSMatt Macy #define TRUNK_WLOCK_ASSERT(trunk) mtx_assert(&(trunk)->lock, MA_OWNED); 26075ee267cSGleb Smirnoff 261d148c2a2SMatt Joras /* 262d148c2a2SMatt Joras * The VLAN_ARRAY substitutes the dynamic hash with a static array 263d148c2a2SMatt Joras * with 4096 entries. In theory this can give a boost in processing, 264d148c2a2SMatt Joras * however in practice it does not. Probably this is because the array 265d148c2a2SMatt Joras * is too big to fit into CPU cache. 266d148c2a2SMatt Joras */ 26775ee267cSGleb Smirnoff #ifndef VLAN_ARRAY 26875ee267cSGleb Smirnoff static void vlan_inithash(struct ifvlantrunk *trunk); 26975ee267cSGleb Smirnoff static void vlan_freehash(struct ifvlantrunk *trunk); 27075ee267cSGleb Smirnoff static int vlan_inshash(struct ifvlantrunk *trunk, struct ifvlan *ifv); 27175ee267cSGleb Smirnoff static int vlan_remhash(struct ifvlantrunk *trunk, struct ifvlan *ifv); 27275ee267cSGleb Smirnoff static void vlan_growhash(struct ifvlantrunk *trunk, int howmuch); 27375ee267cSGleb Smirnoff static __inline struct ifvlan * vlan_gethash(struct ifvlantrunk *trunk, 2747983103aSRobert Watson uint16_t vid); 27575ee267cSGleb Smirnoff #endif 27675ee267cSGleb Smirnoff static void trunk_destroy(struct ifvlantrunk *trunk); 2774faedfe8SSam Leffler 278114c608cSYaroslav Tykhiy static void vlan_init(void *foo); 279a3814acfSSam Leffler static void vlan_input(struct ifnet *ifp, struct mbuf *m); 280cfe8b629SGarrett Wollman static int vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr); 281f3e7afe2SHans Petter Selasky #ifdef RATELIMIT 282f3e7afe2SHans Petter Selasky static int vlan_snd_tag_alloc(struct ifnet *, 283f3e7afe2SHans Petter Selasky union if_snd_tag_alloc_params *, struct m_snd_tag **); 284*fb3bc596SJohn Baldwin static int vlan_snd_tag_modify(struct m_snd_tag *, 285*fb3bc596SJohn Baldwin union if_snd_tag_modify_params *); 286*fb3bc596SJohn Baldwin static int vlan_snd_tag_query(struct m_snd_tag *, 287*fb3bc596SJohn Baldwin union if_snd_tag_query_params *); 288fa91f845SRandall Stewart static void vlan_snd_tag_free(struct m_snd_tag *); 289f3e7afe2SHans Petter Selasky #endif 290d9b1d615SJohn Baldwin static void vlan_qflush(struct ifnet *ifp); 2911cf236fbSYaroslav Tykhiy static int vlan_setflag(struct ifnet *ifp, int flag, int status, 2921cf236fbSYaroslav Tykhiy int (*func)(struct ifnet *, int)); 2931cf236fbSYaroslav Tykhiy static int vlan_setflags(struct ifnet *ifp, int status); 294f731f104SBill Paul static int vlan_setmulti(struct ifnet *ifp); 295d9b1d615SJohn Baldwin static int vlan_transmit(struct ifnet *ifp, struct mbuf *m); 2966f359e28SJohn Baldwin static void vlan_unconfig(struct ifnet *ifp); 29728cc4d37SJohn Baldwin static void vlan_unconfig_locked(struct ifnet *ifp, int departing); 29875ee267cSGleb Smirnoff static int vlan_config(struct ifvlan *ifv, struct ifnet *p, uint16_t tag); 299a6fffd6cSBrooks Davis static void vlan_link_state(struct ifnet *ifp); 30075ee267cSGleb Smirnoff static void vlan_capabilities(struct ifvlan *ifv); 30175ee267cSGleb Smirnoff static void vlan_trunk_capabilities(struct ifnet *ifp); 302f731f104SBill Paul 303f941c31aSGleb Smirnoff static struct ifnet *vlan_clone_match_ethervid(const char *, int *); 304f889d2efSBrooks Davis static int vlan_clone_match(struct if_clone *, const char *); 3056b7330e2SSam Leffler static int vlan_clone_create(struct if_clone *, char *, size_t, caddr_t); 306f889d2efSBrooks Davis static int vlan_clone_destroy(struct if_clone *, struct ifnet *); 307f889d2efSBrooks Davis 3085cb8c31aSYaroslav Tykhiy static void vlan_ifdetach(void *arg, struct ifnet *ifp); 309ea4ca115SAndrew Thompson static void vlan_iflladdr(void *arg, struct ifnet *ifp); 3105cb8c31aSYaroslav Tykhiy 311d148c2a2SMatt Joras static void vlan_lladdr_fn(void *arg, int pending); 312d148c2a2SMatt Joras 31342a58907SGleb Smirnoff static struct if_clone *vlan_cloner; 3149d4fe4b2SBrooks Davis 315ccf7ba97SMarko Zec #ifdef VIMAGE 3165f901c92SAndrew Turner VNET_DEFINE_STATIC(struct if_clone *, vlan_cloner); 317ccf7ba97SMarko Zec #define V_vlan_cloner VNET(vlan_cloner) 318ccf7ba97SMarko Zec #endif 319ccf7ba97SMarko Zec 32075ee267cSGleb Smirnoff static void 321c32a9d66SHans Petter Selasky vlan_mc_free(struct epoch_context *ctx) 322c32a9d66SHans Petter Selasky { 323c32a9d66SHans Petter Selasky struct vlan_mc_entry *mc = __containerof(ctx, struct vlan_mc_entry, mc_epoch_ctx); 324c32a9d66SHans Petter Selasky free(mc, M_VLAN); 325c32a9d66SHans Petter Selasky } 326c32a9d66SHans Petter Selasky 327cac30248SOleg Bulyzhin #ifndef VLAN_ARRAY 328cac30248SOleg Bulyzhin #define HASH(n, m) ((((n) >> 8) ^ ((n) >> 4) ^ (n)) & (m)) 329cac30248SOleg Bulyzhin 330c32a9d66SHans Petter Selasky static void 33175ee267cSGleb Smirnoff vlan_inithash(struct ifvlantrunk *trunk) 33275ee267cSGleb Smirnoff { 33375ee267cSGleb Smirnoff int i, n; 33475ee267cSGleb Smirnoff 33575ee267cSGleb Smirnoff /* 33675ee267cSGleb Smirnoff * The trunk must not be locked here since we call malloc(M_WAITOK). 33775ee267cSGleb Smirnoff * It is OK in case this function is called before the trunk struct 33875ee267cSGleb Smirnoff * gets hooked up and becomes visible from other threads. 33975ee267cSGleb Smirnoff */ 34075ee267cSGleb Smirnoff 34175ee267cSGleb Smirnoff KASSERT(trunk->hwidth == 0 && trunk->hash == NULL, 34275ee267cSGleb Smirnoff ("%s: hash already initialized", __func__)); 34375ee267cSGleb Smirnoff 34475ee267cSGleb Smirnoff trunk->hwidth = VLAN_DEF_HWIDTH; 34575ee267cSGleb Smirnoff n = 1 << trunk->hwidth; 34675ee267cSGleb Smirnoff trunk->hmask = n - 1; 34775ee267cSGleb Smirnoff trunk->hash = malloc(sizeof(struct ifvlanhead) * n, M_VLAN, M_WAITOK); 34875ee267cSGleb Smirnoff for (i = 0; i < n; i++) 349b08d611dSMatt Macy CK_SLIST_INIT(&trunk->hash[i]); 35075ee267cSGleb Smirnoff } 35175ee267cSGleb Smirnoff 35275ee267cSGleb Smirnoff static void 35375ee267cSGleb Smirnoff vlan_freehash(struct ifvlantrunk *trunk) 35475ee267cSGleb Smirnoff { 35575ee267cSGleb Smirnoff #ifdef INVARIANTS 35675ee267cSGleb Smirnoff int i; 35775ee267cSGleb Smirnoff 35875ee267cSGleb Smirnoff KASSERT(trunk->hwidth > 0, ("%s: hwidth not positive", __func__)); 35975ee267cSGleb Smirnoff for (i = 0; i < (1 << trunk->hwidth); i++) 360b08d611dSMatt Macy KASSERT(CK_SLIST_EMPTY(&trunk->hash[i]), 36175ee267cSGleb Smirnoff ("%s: hash table not empty", __func__)); 36275ee267cSGleb Smirnoff #endif 36375ee267cSGleb Smirnoff free(trunk->hash, M_VLAN); 36475ee267cSGleb Smirnoff trunk->hash = NULL; 36575ee267cSGleb Smirnoff trunk->hwidth = trunk->hmask = 0; 36675ee267cSGleb Smirnoff } 36775ee267cSGleb Smirnoff 36875ee267cSGleb Smirnoff static int 36975ee267cSGleb Smirnoff vlan_inshash(struct ifvlantrunk *trunk, struct ifvlan *ifv) 37075ee267cSGleb Smirnoff { 37175ee267cSGleb Smirnoff int i, b; 37275ee267cSGleb Smirnoff struct ifvlan *ifv2; 37375ee267cSGleb Smirnoff 374b08d611dSMatt Macy VLAN_XLOCK_ASSERT(); 37575ee267cSGleb Smirnoff KASSERT(trunk->hwidth > 0, ("%s: hwidth not positive", __func__)); 37675ee267cSGleb Smirnoff 37775ee267cSGleb Smirnoff b = 1 << trunk->hwidth; 3787983103aSRobert Watson i = HASH(ifv->ifv_vid, trunk->hmask); 379b08d611dSMatt Macy CK_SLIST_FOREACH(ifv2, &trunk->hash[i], ifv_list) 3807983103aSRobert Watson if (ifv->ifv_vid == ifv2->ifv_vid) 38175ee267cSGleb Smirnoff return (EEXIST); 38275ee267cSGleb Smirnoff 38375ee267cSGleb Smirnoff /* 38475ee267cSGleb Smirnoff * Grow the hash when the number of vlans exceeds half of the number of 38575ee267cSGleb Smirnoff * hash buckets squared. This will make the average linked-list length 38675ee267cSGleb Smirnoff * buckets/2. 38775ee267cSGleb Smirnoff */ 38875ee267cSGleb Smirnoff if (trunk->refcnt > (b * b) / 2) { 38975ee267cSGleb Smirnoff vlan_growhash(trunk, 1); 3907983103aSRobert Watson i = HASH(ifv->ifv_vid, trunk->hmask); 39175ee267cSGleb Smirnoff } 392b08d611dSMatt Macy CK_SLIST_INSERT_HEAD(&trunk->hash[i], ifv, ifv_list); 39375ee267cSGleb Smirnoff trunk->refcnt++; 39475ee267cSGleb Smirnoff 39575ee267cSGleb Smirnoff return (0); 39675ee267cSGleb Smirnoff } 39775ee267cSGleb Smirnoff 39875ee267cSGleb Smirnoff static int 39975ee267cSGleb Smirnoff vlan_remhash(struct ifvlantrunk *trunk, struct ifvlan *ifv) 40075ee267cSGleb Smirnoff { 40175ee267cSGleb Smirnoff int i, b; 40275ee267cSGleb Smirnoff struct ifvlan *ifv2; 40375ee267cSGleb Smirnoff 404b08d611dSMatt Macy VLAN_XLOCK_ASSERT(); 40575ee267cSGleb Smirnoff KASSERT(trunk->hwidth > 0, ("%s: hwidth not positive", __func__)); 40675ee267cSGleb Smirnoff 40775ee267cSGleb Smirnoff b = 1 << trunk->hwidth; 4087983103aSRobert Watson i = HASH(ifv->ifv_vid, trunk->hmask); 409b08d611dSMatt Macy CK_SLIST_FOREACH(ifv2, &trunk->hash[i], ifv_list) 41075ee267cSGleb Smirnoff if (ifv2 == ifv) { 41175ee267cSGleb Smirnoff trunk->refcnt--; 412b08d611dSMatt Macy CK_SLIST_REMOVE(&trunk->hash[i], ifv2, ifvlan, ifv_list); 41375ee267cSGleb Smirnoff if (trunk->refcnt < (b * b) / 2) 41475ee267cSGleb Smirnoff vlan_growhash(trunk, -1); 41575ee267cSGleb Smirnoff return (0); 41675ee267cSGleb Smirnoff } 41775ee267cSGleb Smirnoff 41875ee267cSGleb Smirnoff panic("%s: vlan not found\n", __func__); 41975ee267cSGleb Smirnoff return (ENOENT); /*NOTREACHED*/ 42075ee267cSGleb Smirnoff } 42175ee267cSGleb Smirnoff 42275ee267cSGleb Smirnoff /* 42375ee267cSGleb Smirnoff * Grow the hash larger or smaller if memory permits. 42475ee267cSGleb Smirnoff */ 42575ee267cSGleb Smirnoff static void 42675ee267cSGleb Smirnoff vlan_growhash(struct ifvlantrunk *trunk, int howmuch) 42775ee267cSGleb Smirnoff { 42875ee267cSGleb Smirnoff struct ifvlan *ifv; 42975ee267cSGleb Smirnoff struct ifvlanhead *hash2; 43075ee267cSGleb Smirnoff int hwidth2, i, j, n, n2; 43175ee267cSGleb Smirnoff 432b08d611dSMatt Macy VLAN_XLOCK_ASSERT(); 43375ee267cSGleb Smirnoff KASSERT(trunk->hwidth > 0, ("%s: hwidth not positive", __func__)); 43475ee267cSGleb Smirnoff 43575ee267cSGleb Smirnoff if (howmuch == 0) { 43675ee267cSGleb Smirnoff /* Harmless yet obvious coding error */ 43775ee267cSGleb Smirnoff printf("%s: howmuch is 0\n", __func__); 43875ee267cSGleb Smirnoff return; 43975ee267cSGleb Smirnoff } 44075ee267cSGleb Smirnoff 44175ee267cSGleb Smirnoff hwidth2 = trunk->hwidth + howmuch; 44275ee267cSGleb Smirnoff n = 1 << trunk->hwidth; 44375ee267cSGleb Smirnoff n2 = 1 << hwidth2; 44475ee267cSGleb Smirnoff /* Do not shrink the table below the default */ 44575ee267cSGleb Smirnoff if (hwidth2 < VLAN_DEF_HWIDTH) 44675ee267cSGleb Smirnoff return; 44775ee267cSGleb Smirnoff 448b08d611dSMatt Macy hash2 = malloc(sizeof(struct ifvlanhead) * n2, M_VLAN, M_WAITOK); 44975ee267cSGleb Smirnoff if (hash2 == NULL) { 45075ee267cSGleb Smirnoff printf("%s: out of memory -- hash size not changed\n", 45175ee267cSGleb Smirnoff __func__); 45275ee267cSGleb Smirnoff return; /* We can live with the old hash table */ 45375ee267cSGleb Smirnoff } 45475ee267cSGleb Smirnoff for (j = 0; j < n2; j++) 455b08d611dSMatt Macy CK_SLIST_INIT(&hash2[j]); 45675ee267cSGleb Smirnoff for (i = 0; i < n; i++) 457b08d611dSMatt Macy while ((ifv = CK_SLIST_FIRST(&trunk->hash[i])) != NULL) { 458b08d611dSMatt Macy CK_SLIST_REMOVE(&trunk->hash[i], ifv, ifvlan, ifv_list); 4597983103aSRobert Watson j = HASH(ifv->ifv_vid, n2 - 1); 460b08d611dSMatt Macy CK_SLIST_INSERT_HEAD(&hash2[j], ifv, ifv_list); 46175ee267cSGleb Smirnoff } 462b08d611dSMatt Macy NET_EPOCH_WAIT(); 46375ee267cSGleb Smirnoff free(trunk->hash, M_VLAN); 46475ee267cSGleb Smirnoff trunk->hash = hash2; 46575ee267cSGleb Smirnoff trunk->hwidth = hwidth2; 46675ee267cSGleb Smirnoff trunk->hmask = n2 - 1; 467f84b2d69SYaroslav Tykhiy 468f84b2d69SYaroslav Tykhiy if (bootverbose) 469f84b2d69SYaroslav Tykhiy if_printf(trunk->parent, 470f84b2d69SYaroslav Tykhiy "VLAN hash table resized from %d to %d buckets\n", n, n2); 47175ee267cSGleb Smirnoff } 47275ee267cSGleb Smirnoff 47375ee267cSGleb Smirnoff static __inline struct ifvlan * 4747983103aSRobert Watson vlan_gethash(struct ifvlantrunk *trunk, uint16_t vid) 47575ee267cSGleb Smirnoff { 47675ee267cSGleb Smirnoff struct ifvlan *ifv; 47775ee267cSGleb Smirnoff 478a68cc388SGleb Smirnoff NET_EPOCH_ASSERT(); 47975ee267cSGleb Smirnoff 480b08d611dSMatt Macy CK_SLIST_FOREACH(ifv, &trunk->hash[HASH(vid, trunk->hmask)], ifv_list) 4817983103aSRobert Watson if (ifv->ifv_vid == vid) 48275ee267cSGleb Smirnoff return (ifv); 48375ee267cSGleb Smirnoff return (NULL); 48475ee267cSGleb Smirnoff } 48575ee267cSGleb Smirnoff 48675ee267cSGleb Smirnoff #if 0 48775ee267cSGleb Smirnoff /* Debugging code to view the hashtables. */ 48875ee267cSGleb Smirnoff static void 48975ee267cSGleb Smirnoff vlan_dumphash(struct ifvlantrunk *trunk) 49075ee267cSGleb Smirnoff { 49175ee267cSGleb Smirnoff int i; 49275ee267cSGleb Smirnoff struct ifvlan *ifv; 49375ee267cSGleb Smirnoff 49475ee267cSGleb Smirnoff for (i = 0; i < (1 << trunk->hwidth); i++) { 49575ee267cSGleb Smirnoff printf("%d: ", i); 496b08d611dSMatt Macy CK_SLIST_FOREACH(ifv, &trunk->hash[i], ifv_list) 49775ee267cSGleb Smirnoff printf("%s ", ifv->ifv_ifp->if_xname); 49875ee267cSGleb Smirnoff printf("\n"); 49975ee267cSGleb Smirnoff } 50075ee267cSGleb Smirnoff } 50175ee267cSGleb Smirnoff #endif /* 0 */ 502e4cd31ddSJeff Roberson #else 503e4cd31ddSJeff Roberson 504e4cd31ddSJeff Roberson static __inline struct ifvlan * 5057983103aSRobert Watson vlan_gethash(struct ifvlantrunk *trunk, uint16_t vid) 506e4cd31ddSJeff Roberson { 507e4cd31ddSJeff Roberson 5087983103aSRobert Watson return trunk->vlans[vid]; 509e4cd31ddSJeff Roberson } 510e4cd31ddSJeff Roberson 511e4cd31ddSJeff Roberson static __inline int 512e4cd31ddSJeff Roberson vlan_inshash(struct ifvlantrunk *trunk, struct ifvlan *ifv) 513e4cd31ddSJeff Roberson { 514e4cd31ddSJeff Roberson 5157983103aSRobert Watson if (trunk->vlans[ifv->ifv_vid] != NULL) 516e4cd31ddSJeff Roberson return EEXIST; 5177983103aSRobert Watson trunk->vlans[ifv->ifv_vid] = ifv; 518e4cd31ddSJeff Roberson trunk->refcnt++; 519e4cd31ddSJeff Roberson 520e4cd31ddSJeff Roberson return (0); 521e4cd31ddSJeff Roberson } 522e4cd31ddSJeff Roberson 523e4cd31ddSJeff Roberson static __inline int 524e4cd31ddSJeff Roberson vlan_remhash(struct ifvlantrunk *trunk, struct ifvlan *ifv) 525e4cd31ddSJeff Roberson { 526e4cd31ddSJeff Roberson 5277983103aSRobert Watson trunk->vlans[ifv->ifv_vid] = NULL; 528e4cd31ddSJeff Roberson trunk->refcnt--; 529e4cd31ddSJeff Roberson 530e4cd31ddSJeff Roberson return (0); 531e4cd31ddSJeff Roberson } 532e4cd31ddSJeff Roberson 533e4cd31ddSJeff Roberson static __inline void 534e4cd31ddSJeff Roberson vlan_freehash(struct ifvlantrunk *trunk) 535e4cd31ddSJeff Roberson { 536e4cd31ddSJeff Roberson } 537e4cd31ddSJeff Roberson 538e4cd31ddSJeff Roberson static __inline void 539e4cd31ddSJeff Roberson vlan_inithash(struct ifvlantrunk *trunk) 540e4cd31ddSJeff Roberson { 541e4cd31ddSJeff Roberson } 542e4cd31ddSJeff Roberson 54375ee267cSGleb Smirnoff #endif /* !VLAN_ARRAY */ 54475ee267cSGleb Smirnoff 54575ee267cSGleb Smirnoff static void 54675ee267cSGleb Smirnoff trunk_destroy(struct ifvlantrunk *trunk) 54775ee267cSGleb Smirnoff { 548d148c2a2SMatt Joras VLAN_XLOCK_ASSERT(); 54975ee267cSGleb Smirnoff 55075ee267cSGleb Smirnoff vlan_freehash(trunk); 55175ee267cSGleb Smirnoff trunk->parent->if_vlantrunk = NULL; 55233499e2aSYaroslav Tykhiy TRUNK_LOCK_DESTROY(trunk); 5539bcf3ae4SAlexander Motin if_rele(trunk->parent); 55475ee267cSGleb Smirnoff free(trunk, M_VLAN); 55575ee267cSGleb Smirnoff } 55675ee267cSGleb Smirnoff 557f731f104SBill Paul /* 558f731f104SBill Paul * Program our multicast filter. What we're actually doing is 559f731f104SBill Paul * programming the multicast filter of the parent. This has the 560f731f104SBill Paul * side effect of causing the parent interface to receive multicast 561f731f104SBill Paul * traffic that it doesn't really want, which ends up being discarded 562f731f104SBill Paul * later by the upper protocol layers. Unfortunately, there's no way 563f731f104SBill Paul * to avoid this: there really is only one physical interface. 564f731f104SBill Paul */ 5652b120974SPeter Wemm static int 5662b120974SPeter Wemm vlan_setmulti(struct ifnet *ifp) 567f731f104SBill Paul { 568f731f104SBill Paul struct ifnet *ifp_p; 5692d222cb7SAlexander Motin struct ifmultiaddr *ifma; 570f731f104SBill Paul struct ifvlan *sc; 571c0cb022bSYaroslav Tykhiy struct vlan_mc_entry *mc; 572f731f104SBill Paul int error; 573f731f104SBill Paul 574b08d611dSMatt Macy VLAN_XLOCK_ASSERT(); 575d148c2a2SMatt Joras 576f731f104SBill Paul /* Find the parent. */ 577f731f104SBill Paul sc = ifp->if_softc; 57875ee267cSGleb Smirnoff ifp_p = PARENT(sc); 5791b2a4f7aSBill Fenner 5808b615593SMarko Zec CURVNET_SET_QUIET(ifp_p->if_vnet); 5818b615593SMarko Zec 582f731f104SBill Paul /* First, remove any existing filter entries. */ 583b08d611dSMatt Macy while ((mc = CK_SLIST_FIRST(&sc->vlan_mc_listhead)) != NULL) { 584b08d611dSMatt Macy CK_SLIST_REMOVE_HEAD(&sc->vlan_mc_listhead, mc_entries); 5852d222cb7SAlexander Motin (void)if_delmulti(ifp_p, (struct sockaddr *)&mc->mc_addr); 586c32a9d66SHans Petter Selasky epoch_call(net_epoch_preempt, &mc->mc_epoch_ctx, vlan_mc_free); 587f731f104SBill Paul } 588f731f104SBill Paul 589f731f104SBill Paul /* Now program new ones. */ 5902d222cb7SAlexander Motin IF_ADDR_WLOCK(ifp); 591d7c5a620SMatt Macy CK_STAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 592f731f104SBill Paul if (ifma->ifma_addr->sa_family != AF_LINK) 593f731f104SBill Paul continue; 59429c2dfbeSBruce M Simpson mc = malloc(sizeof(struct vlan_mc_entry), M_VLAN, M_NOWAIT); 5952d222cb7SAlexander Motin if (mc == NULL) { 5962d222cb7SAlexander Motin IF_ADDR_WUNLOCK(ifp); 59729c2dfbeSBruce M Simpson return (ENOMEM); 5982d222cb7SAlexander Motin } 599e4cd31ddSJeff Roberson bcopy(ifma->ifma_addr, &mc->mc_addr, ifma->ifma_addr->sa_len); 600e4cd31ddSJeff Roberson mc->mc_addr.sdl_index = ifp_p->if_index; 601b08d611dSMatt Macy CK_SLIST_INSERT_HEAD(&sc->vlan_mc_listhead, mc, mc_entries); 6022d222cb7SAlexander Motin } 6032d222cb7SAlexander Motin IF_ADDR_WUNLOCK(ifp); 604b08d611dSMatt Macy CK_SLIST_FOREACH (mc, &sc->vlan_mc_listhead, mc_entries) { 605e4cd31ddSJeff Roberson error = if_addmulti(ifp_p, (struct sockaddr *)&mc->mc_addr, 6062d222cb7SAlexander Motin NULL); 607f731f104SBill Paul if (error) 608f731f104SBill Paul return (error); 609f731f104SBill Paul } 610f731f104SBill Paul 6118b615593SMarko Zec CURVNET_RESTORE(); 612f731f104SBill Paul return (0); 613f731f104SBill Paul } 6142cc2df49SGarrett Wollman 615a3814acfSSam Leffler /* 616ea4ca115SAndrew Thompson * A handler for parent interface link layer address changes. 617ea4ca115SAndrew Thompson * If the parent interface link layer address is changed we 618ea4ca115SAndrew Thompson * should also change it on all children vlans. 619ea4ca115SAndrew Thompson */ 620ea4ca115SAndrew Thompson static void 621ea4ca115SAndrew Thompson vlan_iflladdr(void *arg __unused, struct ifnet *ifp) 622ea4ca115SAndrew Thompson { 623a68cc388SGleb Smirnoff struct epoch_tracker et; 624ea4ca115SAndrew Thompson struct ifvlan *ifv; 625d148c2a2SMatt Joras struct ifnet *ifv_ifp; 626d148c2a2SMatt Joras struct ifvlantrunk *trunk; 627d148c2a2SMatt Joras struct sockaddr_dl *sdl; 628ea4ca115SAndrew Thompson 6297b7f772fSGleb Smirnoff /* Need the epoch since this is run on taskqueue_swi. */ 630a68cc388SGleb Smirnoff NET_EPOCH_ENTER(et); 631d148c2a2SMatt Joras trunk = ifp->if_vlantrunk; 632d148c2a2SMatt Joras if (trunk == NULL) { 633a68cc388SGleb Smirnoff NET_EPOCH_EXIT(et); 634ea4ca115SAndrew Thompson return; 635d148c2a2SMatt Joras } 636ea4ca115SAndrew Thompson 637ea4ca115SAndrew Thompson /* 638ea4ca115SAndrew Thompson * OK, it's a trunk. Loop over and change all vlan's lladdrs on it. 639d148c2a2SMatt Joras * We need an exclusive lock here to prevent concurrent SIOCSIFLLADDR 640d148c2a2SMatt Joras * ioctl calls on the parent garbling the lladdr of the child vlan. 641ea4ca115SAndrew Thompson */ 642d148c2a2SMatt Joras TRUNK_WLOCK(trunk); 643d148c2a2SMatt Joras VLAN_FOREACH(ifv, trunk) { 644d148c2a2SMatt Joras /* 645d148c2a2SMatt Joras * Copy new new lladdr into the ifv_ifp, enqueue a task 646d148c2a2SMatt Joras * to actually call if_setlladdr. if_setlladdr needs to 647d148c2a2SMatt Joras * be deferred to a taskqueue because it will call into 648d148c2a2SMatt Joras * the if_vlan ioctl path and try to acquire the global 649d148c2a2SMatt Joras * lock. 650d148c2a2SMatt Joras */ 651d148c2a2SMatt Joras ifv_ifp = ifv->ifv_ifp; 652d148c2a2SMatt Joras bcopy(IF_LLADDR(ifp), IF_LLADDR(ifv_ifp), 653e4cd31ddSJeff Roberson ifp->if_addrlen); 654d148c2a2SMatt Joras sdl = (struct sockaddr_dl *)ifv_ifp->if_addr->ifa_addr; 655d148c2a2SMatt Joras sdl->sdl_alen = ifp->if_addrlen; 656d148c2a2SMatt Joras taskqueue_enqueue(taskqueue_thread, &ifv->lladdr_task); 6576117727bSAndrew Thompson } 658d148c2a2SMatt Joras TRUNK_WUNLOCK(trunk); 659a68cc388SGleb Smirnoff NET_EPOCH_EXIT(et); 660ea4ca115SAndrew Thompson } 661ea4ca115SAndrew Thompson 662ea4ca115SAndrew Thompson /* 6635cb8c31aSYaroslav Tykhiy * A handler for network interface departure events. 6645cb8c31aSYaroslav Tykhiy * Track departure of trunks here so that we don't access invalid 6655cb8c31aSYaroslav Tykhiy * pointers or whatever if a trunk is ripped from under us, e.g., 6665428776eSJohn Baldwin * by ejecting its hot-plug card. However, if an ifnet is simply 6675428776eSJohn Baldwin * being renamed, then there's no need to tear down the state. 6685cb8c31aSYaroslav Tykhiy */ 6695cb8c31aSYaroslav Tykhiy static void 6705cb8c31aSYaroslav Tykhiy vlan_ifdetach(void *arg __unused, struct ifnet *ifp) 6715cb8c31aSYaroslav Tykhiy { 6725cb8c31aSYaroslav Tykhiy struct ifvlan *ifv; 673d148c2a2SMatt Joras struct ifvlantrunk *trunk; 6745cb8c31aSYaroslav Tykhiy 6755428776eSJohn Baldwin /* If the ifnet is just being renamed, don't do anything. */ 6765428776eSJohn Baldwin if (ifp->if_flags & IFF_RENAMING) 6775428776eSJohn Baldwin return; 678d148c2a2SMatt Joras VLAN_XLOCK(); 679d148c2a2SMatt Joras trunk = ifp->if_vlantrunk; 680d148c2a2SMatt Joras if (trunk == NULL) { 681d148c2a2SMatt Joras VLAN_XUNLOCK(); 682d148c2a2SMatt Joras return; 683d148c2a2SMatt Joras } 6845428776eSJohn Baldwin 6855cb8c31aSYaroslav Tykhiy /* 6865cb8c31aSYaroslav Tykhiy * OK, it's a trunk. Loop over and detach all vlan's on it. 6875cb8c31aSYaroslav Tykhiy * Check trunk pointer after each vlan_unconfig() as it will 6885cb8c31aSYaroslav Tykhiy * free it and set to NULL after the last vlan was detached. 6895cb8c31aSYaroslav Tykhiy */ 690d148c2a2SMatt Joras VLAN_FOREACH_UNTIL_SAFE(ifv, ifp->if_vlantrunk, 691d148c2a2SMatt Joras ifp->if_vlantrunk == NULL) 69228cc4d37SJohn Baldwin vlan_unconfig_locked(ifv->ifv_ifp, 1); 693d148c2a2SMatt Joras 6945cb8c31aSYaroslav Tykhiy /* Trunk should have been destroyed in vlan_unconfig(). */ 6955cb8c31aSYaroslav Tykhiy KASSERT(ifp->if_vlantrunk == NULL, ("%s: purge failed", __func__)); 696d148c2a2SMatt Joras VLAN_XUNLOCK(); 6975cb8c31aSYaroslav Tykhiy } 6985cb8c31aSYaroslav Tykhiy 6995cb8c31aSYaroslav Tykhiy /* 700e4cd31ddSJeff Roberson * Return the trunk device for a virtual interface. 701e4cd31ddSJeff Roberson */ 702e4cd31ddSJeff Roberson static struct ifnet * 703e4cd31ddSJeff Roberson vlan_trunkdev(struct ifnet *ifp) 704e4cd31ddSJeff Roberson { 705a68cc388SGleb Smirnoff struct epoch_tracker et; 706e4cd31ddSJeff Roberson struct ifvlan *ifv; 707e4cd31ddSJeff Roberson 708e4cd31ddSJeff Roberson if (ifp->if_type != IFT_L2VLAN) 709e4cd31ddSJeff Roberson return (NULL); 710d148c2a2SMatt Joras 711a68cc388SGleb Smirnoff NET_EPOCH_ENTER(et); 712e4cd31ddSJeff Roberson ifv = ifp->if_softc; 713e4cd31ddSJeff Roberson ifp = NULL; 714e4cd31ddSJeff Roberson if (ifv->ifv_trunk) 715e4cd31ddSJeff Roberson ifp = PARENT(ifv); 716a68cc388SGleb Smirnoff NET_EPOCH_EXIT(et); 717e4cd31ddSJeff Roberson return (ifp); 718e4cd31ddSJeff Roberson } 719e4cd31ddSJeff Roberson 720e4cd31ddSJeff Roberson /* 7217983103aSRobert Watson * Return the 12-bit VLAN VID for this interface, for use by external 7227983103aSRobert Watson * components such as Infiniband. 7237983103aSRobert Watson * 7247983103aSRobert Watson * XXXRW: Note that the function name here is historical; it should be named 7257983103aSRobert Watson * vlan_vid(). 726e4cd31ddSJeff Roberson */ 727e4cd31ddSJeff Roberson static int 7287983103aSRobert Watson vlan_tag(struct ifnet *ifp, uint16_t *vidp) 729e4cd31ddSJeff Roberson { 730e4cd31ddSJeff Roberson struct ifvlan *ifv; 731e4cd31ddSJeff Roberson 732e4cd31ddSJeff Roberson if (ifp->if_type != IFT_L2VLAN) 733e4cd31ddSJeff Roberson return (EINVAL); 734e4cd31ddSJeff Roberson ifv = ifp->if_softc; 7357983103aSRobert Watson *vidp = ifv->ifv_vid; 736e4cd31ddSJeff Roberson return (0); 737e4cd31ddSJeff Roberson } 738e4cd31ddSJeff Roberson 73932d2623aSNavdeep Parhar static int 74032d2623aSNavdeep Parhar vlan_pcp(struct ifnet *ifp, uint16_t *pcpp) 74132d2623aSNavdeep Parhar { 74232d2623aSNavdeep Parhar struct ifvlan *ifv; 74332d2623aSNavdeep Parhar 74432d2623aSNavdeep Parhar if (ifp->if_type != IFT_L2VLAN) 74532d2623aSNavdeep Parhar return (EINVAL); 74632d2623aSNavdeep Parhar ifv = ifp->if_softc; 74732d2623aSNavdeep Parhar *pcpp = ifv->ifv_pcp; 74832d2623aSNavdeep Parhar return (0); 74932d2623aSNavdeep Parhar } 75032d2623aSNavdeep Parhar 751e4cd31ddSJeff Roberson /* 752e4cd31ddSJeff Roberson * Return a driver specific cookie for this interface. Synchronization 753e4cd31ddSJeff Roberson * with setcookie must be provided by the driver. 754e4cd31ddSJeff Roberson */ 755e4cd31ddSJeff Roberson static void * 756e4cd31ddSJeff Roberson vlan_cookie(struct ifnet *ifp) 757e4cd31ddSJeff Roberson { 758e4cd31ddSJeff Roberson struct ifvlan *ifv; 759e4cd31ddSJeff Roberson 760e4cd31ddSJeff Roberson if (ifp->if_type != IFT_L2VLAN) 761e4cd31ddSJeff Roberson return (NULL); 762e4cd31ddSJeff Roberson ifv = ifp->if_softc; 763e4cd31ddSJeff Roberson return (ifv->ifv_cookie); 764e4cd31ddSJeff Roberson } 765e4cd31ddSJeff Roberson 766e4cd31ddSJeff Roberson /* 767e4cd31ddSJeff Roberson * Store a cookie in our softc that drivers can use to store driver 768e4cd31ddSJeff Roberson * private per-instance data in. 769e4cd31ddSJeff Roberson */ 770e4cd31ddSJeff Roberson static int 771e4cd31ddSJeff Roberson vlan_setcookie(struct ifnet *ifp, void *cookie) 772e4cd31ddSJeff Roberson { 773e4cd31ddSJeff Roberson struct ifvlan *ifv; 774e4cd31ddSJeff Roberson 775e4cd31ddSJeff Roberson if (ifp->if_type != IFT_L2VLAN) 776e4cd31ddSJeff Roberson return (EINVAL); 777e4cd31ddSJeff Roberson ifv = ifp->if_softc; 778e4cd31ddSJeff Roberson ifv->ifv_cookie = cookie; 779e4cd31ddSJeff Roberson return (0); 780e4cd31ddSJeff Roberson } 781e4cd31ddSJeff Roberson 782e4cd31ddSJeff Roberson /* 7837983103aSRobert Watson * Return the vlan device present at the specific VID. 784e4cd31ddSJeff Roberson */ 785e4cd31ddSJeff Roberson static struct ifnet * 7867983103aSRobert Watson vlan_devat(struct ifnet *ifp, uint16_t vid) 787e4cd31ddSJeff Roberson { 788a68cc388SGleb Smirnoff struct epoch_tracker et; 789e4cd31ddSJeff Roberson struct ifvlantrunk *trunk; 790e4cd31ddSJeff Roberson struct ifvlan *ifv; 791e4cd31ddSJeff Roberson 792a68cc388SGleb Smirnoff NET_EPOCH_ENTER(et); 793e4cd31ddSJeff Roberson trunk = ifp->if_vlantrunk; 794d148c2a2SMatt Joras if (trunk == NULL) { 795a68cc388SGleb Smirnoff NET_EPOCH_EXIT(et); 796e4cd31ddSJeff Roberson return (NULL); 797d148c2a2SMatt Joras } 798e4cd31ddSJeff Roberson ifp = NULL; 7997983103aSRobert Watson ifv = vlan_gethash(trunk, vid); 800e4cd31ddSJeff Roberson if (ifv) 801e4cd31ddSJeff Roberson ifp = ifv->ifv_ifp; 802a68cc388SGleb Smirnoff NET_EPOCH_EXIT(et); 803e4cd31ddSJeff Roberson return (ifp); 804e4cd31ddSJeff Roberson } 805e4cd31ddSJeff Roberson 806e4cd31ddSJeff Roberson /* 8072ccbbd06SMarcelo Araujo * Recalculate the cached VLAN tag exposed via the MIB. 8082ccbbd06SMarcelo Araujo */ 8092ccbbd06SMarcelo Araujo static void 8102ccbbd06SMarcelo Araujo vlan_tag_recalculate(struct ifvlan *ifv) 8112ccbbd06SMarcelo Araujo { 8122ccbbd06SMarcelo Araujo 8132ccbbd06SMarcelo Araujo ifv->ifv_tag = EVL_MAKETAG(ifv->ifv_vid, ifv->ifv_pcp, 0); 8142ccbbd06SMarcelo Araujo } 8152ccbbd06SMarcelo Araujo 8162ccbbd06SMarcelo Araujo /* 817a3814acfSSam Leffler * VLAN support can be loaded as a module. The only place in the 818a3814acfSSam Leffler * system that's intimately aware of this is ether_input. We hook 819a3814acfSSam Leffler * into this code through vlan_input_p which is defined there and 820a3814acfSSam Leffler * set here. No one else in the system should be aware of this so 821a3814acfSSam Leffler * we use an explicit reference here. 822a3814acfSSam Leffler */ 823a3814acfSSam Leffler extern void (*vlan_input_p)(struct ifnet *, struct mbuf *); 824a3814acfSSam Leffler 825984be3efSGleb Smirnoff /* For if_link_state_change() eyes only... */ 826a6fffd6cSBrooks Davis extern void (*vlan_link_state_p)(struct ifnet *); 827127d7b2dSAndre Oppermann 8282b120974SPeter Wemm static int 8292b120974SPeter Wemm vlan_modevent(module_t mod, int type, void *data) 8302b120974SPeter Wemm { 8319d4fe4b2SBrooks Davis 8322b120974SPeter Wemm switch (type) { 8332b120974SPeter Wemm case MOD_LOAD: 8345cb8c31aSYaroslav Tykhiy ifdetach_tag = EVENTHANDLER_REGISTER(ifnet_departure_event, 8355cb8c31aSYaroslav Tykhiy vlan_ifdetach, NULL, EVENTHANDLER_PRI_ANY); 8365cb8c31aSYaroslav Tykhiy if (ifdetach_tag == NULL) 8375cb8c31aSYaroslav Tykhiy return (ENOMEM); 838ea4ca115SAndrew Thompson iflladdr_tag = EVENTHANDLER_REGISTER(iflladdr_event, 839ea4ca115SAndrew Thompson vlan_iflladdr, NULL, EVENTHANDLER_PRI_ANY); 840ea4ca115SAndrew Thompson if (iflladdr_tag == NULL) 841ea4ca115SAndrew Thompson return (ENOMEM); 842d148c2a2SMatt Joras VLAN_LOCKING_INIT(); 8439d4fe4b2SBrooks Davis vlan_input_p = vlan_input; 844127d7b2dSAndre Oppermann vlan_link_state_p = vlan_link_state; 84575ee267cSGleb Smirnoff vlan_trunk_cap_p = vlan_trunk_capabilities; 846e4cd31ddSJeff Roberson vlan_trunkdev_p = vlan_trunkdev; 847e4cd31ddSJeff Roberson vlan_cookie_p = vlan_cookie; 848e4cd31ddSJeff Roberson vlan_setcookie_p = vlan_setcookie; 849e4cd31ddSJeff Roberson vlan_tag_p = vlan_tag; 85032d2623aSNavdeep Parhar vlan_pcp_p = vlan_pcp; 851e4cd31ddSJeff Roberson vlan_devat_p = vlan_devat; 852ccf7ba97SMarko Zec #ifndef VIMAGE 85342a58907SGleb Smirnoff vlan_cloner = if_clone_advanced(vlanname, 0, vlan_clone_match, 85442a58907SGleb Smirnoff vlan_clone_create, vlan_clone_destroy); 855ccf7ba97SMarko Zec #endif 85625c0f7b3SYaroslav Tykhiy if (bootverbose) 85725c0f7b3SYaroslav Tykhiy printf("vlan: initialized, using " 85825c0f7b3SYaroslav Tykhiy #ifdef VLAN_ARRAY 85925c0f7b3SYaroslav Tykhiy "full-size arrays" 86025c0f7b3SYaroslav Tykhiy #else 86125c0f7b3SYaroslav Tykhiy "hash tables with chaining" 86225c0f7b3SYaroslav Tykhiy #endif 86325c0f7b3SYaroslav Tykhiy 86425c0f7b3SYaroslav Tykhiy "\n"); 8652b120974SPeter Wemm break; 8662b120974SPeter Wemm case MOD_UNLOAD: 867ccf7ba97SMarko Zec #ifndef VIMAGE 86842a58907SGleb Smirnoff if_clone_detach(vlan_cloner); 869ccf7ba97SMarko Zec #endif 8705cb8c31aSYaroslav Tykhiy EVENTHANDLER_DEREGISTER(ifnet_departure_event, ifdetach_tag); 871ea4ca115SAndrew Thompson EVENTHANDLER_DEREGISTER(iflladdr_event, iflladdr_tag); 8729d4fe4b2SBrooks Davis vlan_input_p = NULL; 873127d7b2dSAndre Oppermann vlan_link_state_p = NULL; 87475ee267cSGleb Smirnoff vlan_trunk_cap_p = NULL; 875e4cd31ddSJeff Roberson vlan_trunkdev_p = NULL; 876e4cd31ddSJeff Roberson vlan_tag_p = NULL; 87709fe6320SNavdeep Parhar vlan_cookie_p = NULL; 87809fe6320SNavdeep Parhar vlan_setcookie_p = NULL; 879e4cd31ddSJeff Roberson vlan_devat_p = NULL; 880d148c2a2SMatt Joras VLAN_LOCKING_DESTROY(); 88125c0f7b3SYaroslav Tykhiy if (bootverbose) 88225c0f7b3SYaroslav Tykhiy printf("vlan: unloaded\n"); 8839d4fe4b2SBrooks Davis break; 8843e019deaSPoul-Henning Kamp default: 8853e019deaSPoul-Henning Kamp return (EOPNOTSUPP); 8862b120974SPeter Wemm } 88715a66c21SBruce M Simpson return (0); 8882b120974SPeter Wemm } 8892b120974SPeter Wemm 8902b120974SPeter Wemm static moduledata_t vlan_mod = { 8912b120974SPeter Wemm "if_vlan", 8922b120974SPeter Wemm vlan_modevent, 8939823d527SKevin Lo 0 8942b120974SPeter Wemm }; 8952b120974SPeter Wemm 8962b120974SPeter Wemm DECLARE_MODULE(if_vlan, vlan_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); 89711edc477SEd Maste MODULE_VERSION(if_vlan, 3); 8982cc2df49SGarrett Wollman 899ccf7ba97SMarko Zec #ifdef VIMAGE 900ccf7ba97SMarko Zec static void 901ccf7ba97SMarko Zec vnet_vlan_init(const void *unused __unused) 902ccf7ba97SMarko Zec { 903ccf7ba97SMarko Zec 90442a58907SGleb Smirnoff vlan_cloner = if_clone_advanced(vlanname, 0, vlan_clone_match, 90542a58907SGleb Smirnoff vlan_clone_create, vlan_clone_destroy); 906ccf7ba97SMarko Zec V_vlan_cloner = vlan_cloner; 907ccf7ba97SMarko Zec } 908ccf7ba97SMarko Zec VNET_SYSINIT(vnet_vlan_init, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY, 909ccf7ba97SMarko Zec vnet_vlan_init, NULL); 910ccf7ba97SMarko Zec 911ccf7ba97SMarko Zec static void 912ccf7ba97SMarko Zec vnet_vlan_uninit(const void *unused __unused) 913ccf7ba97SMarko Zec { 914ccf7ba97SMarko Zec 91542a58907SGleb Smirnoff if_clone_detach(V_vlan_cloner); 916ccf7ba97SMarko Zec } 91789856f7eSBjoern A. Zeeb VNET_SYSUNINIT(vnet_vlan_uninit, SI_SUB_INIT_IF, SI_ORDER_FIRST, 918ccf7ba97SMarko Zec vnet_vlan_uninit, NULL); 919ccf7ba97SMarko Zec #endif 920ccf7ba97SMarko Zec 921f941c31aSGleb Smirnoff /* 922f941c31aSGleb Smirnoff * Check for <etherif>.<vlan> style interface names. 923f941c31aSGleb Smirnoff */ 924f889d2efSBrooks Davis static struct ifnet * 925f941c31aSGleb Smirnoff vlan_clone_match_ethervid(const char *name, int *vidp) 9269d4fe4b2SBrooks Davis { 927f941c31aSGleb Smirnoff char ifname[IFNAMSIZ]; 928f941c31aSGleb Smirnoff char *cp; 929f889d2efSBrooks Davis struct ifnet *ifp; 9307983103aSRobert Watson int vid; 931f889d2efSBrooks Davis 932f941c31aSGleb Smirnoff strlcpy(ifname, name, IFNAMSIZ); 933f941c31aSGleb Smirnoff if ((cp = strchr(ifname, '.')) == NULL) 934f941c31aSGleb Smirnoff return (NULL); 935f941c31aSGleb Smirnoff *cp = '\0'; 9369bcf3ae4SAlexander Motin if ((ifp = ifunit_ref(ifname)) == NULL) 937f941c31aSGleb Smirnoff return (NULL); 938f941c31aSGleb Smirnoff /* Parse VID. */ 9399bcf3ae4SAlexander Motin if (*++cp == '\0') { 9409bcf3ae4SAlexander Motin if_rele(ifp); 941f941c31aSGleb Smirnoff return (NULL); 9429bcf3ae4SAlexander Motin } 9437983103aSRobert Watson vid = 0; 944fb92ad4aSJohn Baldwin for(; *cp >= '0' && *cp <= '9'; cp++) 9457983103aSRobert Watson vid = (vid * 10) + (*cp - '0'); 9469bcf3ae4SAlexander Motin if (*cp != '\0') { 9479bcf3ae4SAlexander Motin if_rele(ifp); 948f941c31aSGleb Smirnoff return (NULL); 9499bcf3ae4SAlexander Motin } 9507983103aSRobert Watson if (vidp != NULL) 9517983103aSRobert Watson *vidp = vid; 952f889d2efSBrooks Davis 95315a66c21SBruce M Simpson return (ifp); 954f889d2efSBrooks Davis } 955f889d2efSBrooks Davis 956f889d2efSBrooks Davis static int 957f889d2efSBrooks Davis vlan_clone_match(struct if_clone *ifc, const char *name) 958f889d2efSBrooks Davis { 959f889d2efSBrooks Davis const char *cp; 960f889d2efSBrooks Davis 961f941c31aSGleb Smirnoff if (vlan_clone_match_ethervid(name, NULL) != NULL) 962f889d2efSBrooks Davis return (1); 963f889d2efSBrooks Davis 96442a58907SGleb Smirnoff if (strncmp(vlanname, name, strlen(vlanname)) != 0) 965f889d2efSBrooks Davis return (0); 966f889d2efSBrooks Davis for (cp = name + 4; *cp != '\0'; cp++) { 967f889d2efSBrooks Davis if (*cp < '0' || *cp > '9') 968f889d2efSBrooks Davis return (0); 969f889d2efSBrooks Davis } 970f889d2efSBrooks Davis 971f889d2efSBrooks Davis return (1); 972f889d2efSBrooks Davis } 973f889d2efSBrooks Davis 974f889d2efSBrooks Davis static int 9756b7330e2SSam Leffler vlan_clone_create(struct if_clone *ifc, char *name, size_t len, caddr_t params) 976f889d2efSBrooks Davis { 977f889d2efSBrooks Davis char *dp; 978f889d2efSBrooks Davis int wildcard; 979f889d2efSBrooks Davis int unit; 980f889d2efSBrooks Davis int error; 9817983103aSRobert Watson int vid; 9829d4fe4b2SBrooks Davis struct ifvlan *ifv; 9839d4fe4b2SBrooks Davis struct ifnet *ifp; 984f889d2efSBrooks Davis struct ifnet *p; 9853ba24fdeSJohn Baldwin struct ifaddr *ifa; 9863ba24fdeSJohn Baldwin struct sockaddr_dl *sdl; 9876b7330e2SSam Leffler struct vlanreq vlr; 98865239942SYaroslav Tykhiy static const u_char eaddr[ETHER_ADDR_LEN]; /* 00:00:00:00:00:00 */ 989f889d2efSBrooks Davis 9906b7330e2SSam Leffler /* 9916b7330e2SSam Leffler * There are 3 (ugh) ways to specify the cloned device: 9926b7330e2SSam Leffler * o pass a parameter block with the clone request. 9936b7330e2SSam Leffler * o specify parameters in the text of the clone device name 9946b7330e2SSam Leffler * o specify no parameters and get an unattached device that 9956b7330e2SSam Leffler * must be configured separately. 9966b7330e2SSam Leffler * The first technique is preferred; the latter two are 997a4641f4eSPedro F. Giffuni * supported for backwards compatibility. 9987983103aSRobert Watson * 9997983103aSRobert Watson * XXXRW: Note historic use of the word "tag" here. New ioctls may be 10007983103aSRobert Watson * called for. 10016b7330e2SSam Leffler */ 10026b7330e2SSam Leffler if (params) { 10036b7330e2SSam Leffler error = copyin(params, &vlr, sizeof(vlr)); 10046b7330e2SSam Leffler if (error) 10056b7330e2SSam Leffler return error; 10069bcf3ae4SAlexander Motin p = ifunit_ref(vlr.vlr_parent); 10076b7330e2SSam Leffler if (p == NULL) 1008b1828acfSGleb Smirnoff return (ENXIO); 10096b7330e2SSam Leffler error = ifc_name2unit(name, &unit); 10109bcf3ae4SAlexander Motin if (error != 0) { 10119bcf3ae4SAlexander Motin if_rele(p); 10126b7330e2SSam Leffler return (error); 10139bcf3ae4SAlexander Motin } 10147983103aSRobert Watson vid = vlr.vlr_tag; 10156b7330e2SSam Leffler wildcard = (unit < 0); 1016f941c31aSGleb Smirnoff } else if ((p = vlan_clone_match_ethervid(name, &vid)) != NULL) { 1017f889d2efSBrooks Davis unit = -1; 1018f889d2efSBrooks Davis wildcard = 0; 1019f889d2efSBrooks Davis } else { 10209bcf3ae4SAlexander Motin p = NULL; 1021f889d2efSBrooks Davis error = ifc_name2unit(name, &unit); 1022f889d2efSBrooks Davis if (error != 0) 1023f889d2efSBrooks Davis return (error); 1024f889d2efSBrooks Davis 1025f889d2efSBrooks Davis wildcard = (unit < 0); 1026f889d2efSBrooks Davis } 1027f889d2efSBrooks Davis 1028f889d2efSBrooks Davis error = ifc_alloc_unit(ifc, &unit); 10299bcf3ae4SAlexander Motin if (error != 0) { 10309bcf3ae4SAlexander Motin if (p != NULL) 10319bcf3ae4SAlexander Motin if_rele(p); 1032f889d2efSBrooks Davis return (error); 10339bcf3ae4SAlexander Motin } 1034f889d2efSBrooks Davis 1035f889d2efSBrooks Davis /* In the wildcard case, we need to update the name. */ 1036f889d2efSBrooks Davis if (wildcard) { 1037f889d2efSBrooks Davis for (dp = name; *dp != '\0'; dp++); 1038f889d2efSBrooks Davis if (snprintf(dp, len - (dp-name), "%d", unit) > 1039f889d2efSBrooks Davis len - (dp-name) - 1) { 1040f889d2efSBrooks Davis panic("%s: interface name too long", __func__); 1041f889d2efSBrooks Davis } 1042f889d2efSBrooks Davis } 10439d4fe4b2SBrooks Davis 1044a163d034SWarner Losh ifv = malloc(sizeof(struct ifvlan), M_VLAN, M_WAITOK | M_ZERO); 1045fc74a9f9SBrooks Davis ifp = ifv->ifv_ifp = if_alloc(IFT_ETHER); 1046fc74a9f9SBrooks Davis if (ifp == NULL) { 1047fc74a9f9SBrooks Davis ifc_free_unit(ifc, unit); 1048fc74a9f9SBrooks Davis free(ifv, M_VLAN); 10499bcf3ae4SAlexander Motin if (p != NULL) 10509bcf3ae4SAlexander Motin if_rele(p); 1051fc74a9f9SBrooks Davis return (ENOSPC); 1052fc74a9f9SBrooks Davis } 1053b08d611dSMatt Macy CK_SLIST_INIT(&ifv->vlan_mc_listhead); 10549d4fe4b2SBrooks Davis ifp->if_softc = ifv; 1055f889d2efSBrooks Davis /* 1056cab574d8SYaroslav Tykhiy * Set the name manually rather than using if_initname because 1057f889d2efSBrooks Davis * we don't conform to the default naming convention for interfaces. 1058f889d2efSBrooks Davis */ 1059f889d2efSBrooks Davis strlcpy(ifp->if_xname, name, IFNAMSIZ); 106042a58907SGleb Smirnoff ifp->if_dname = vlanname; 1061f889d2efSBrooks Davis ifp->if_dunit = unit; 10629d4fe4b2SBrooks Davis 1063114c608cSYaroslav Tykhiy ifp->if_init = vlan_init; 1064d9b1d615SJohn Baldwin ifp->if_transmit = vlan_transmit; 1065d9b1d615SJohn Baldwin ifp->if_qflush = vlan_qflush; 10669d4fe4b2SBrooks Davis ifp->if_ioctl = vlan_ioctl; 1067f3e7afe2SHans Petter Selasky #ifdef RATELIMIT 1068f3e7afe2SHans Petter Selasky ifp->if_snd_tag_alloc = vlan_snd_tag_alloc; 1069*fb3bc596SJohn Baldwin ifp->if_snd_tag_modify = vlan_snd_tag_modify; 1070*fb3bc596SJohn Baldwin ifp->if_snd_tag_query = vlan_snd_tag_query; 1071fa91f845SRandall Stewart ifp->if_snd_tag_free = vlan_snd_tag_free; 1072f3e7afe2SHans Petter Selasky #endif 107364a17d2eSYaroslav Tykhiy ifp->if_flags = VLAN_IFFLAGS; 1074fc74a9f9SBrooks Davis ether_ifattach(ifp, eaddr); 10759d4fe4b2SBrooks Davis /* Now undo some of the damage... */ 1076211f625aSBill Fenner ifp->if_baudrate = 0; 1077a3814acfSSam Leffler ifp->if_type = IFT_L2VLAN; 1078a3814acfSSam Leffler ifp->if_hdrlen = ETHER_VLAN_ENCAP_LEN; 10793ba24fdeSJohn Baldwin ifa = ifp->if_addr; 10803ba24fdeSJohn Baldwin sdl = (struct sockaddr_dl *)ifa->ifa_addr; 10813ba24fdeSJohn Baldwin sdl->sdl_type = IFT_L2VLAN; 10829d4fe4b2SBrooks Davis 10839bcf3ae4SAlexander Motin if (p != NULL) { 10847983103aSRobert Watson error = vlan_config(ifv, p, vid); 10859bcf3ae4SAlexander Motin if_rele(p); 1086f889d2efSBrooks Davis if (error != 0) { 1087f889d2efSBrooks Davis /* 108828cc4d37SJohn Baldwin * Since we've partially failed, we need to back 1089f889d2efSBrooks Davis * out all the way, otherwise userland could get 1090f889d2efSBrooks Davis * confused. Thus, we destroy the interface. 1091f889d2efSBrooks Davis */ 1092f889d2efSBrooks Davis ether_ifdetach(ifp); 1093249f4297SYaroslav Tykhiy vlan_unconfig(ifp); 10944b22573aSBrooks Davis if_free(ifp); 109596c8ef3aSMaxim Konovalov ifc_free_unit(ifc, unit); 1096f889d2efSBrooks Davis free(ifv, M_VLAN); 1097f889d2efSBrooks Davis 1098f889d2efSBrooks Davis return (error); 1099f889d2efSBrooks Davis } 1100f889d2efSBrooks Davis } 1101f889d2efSBrooks Davis 11029d4fe4b2SBrooks Davis return (0); 11039d4fe4b2SBrooks Davis } 11049d4fe4b2SBrooks Davis 1105f889d2efSBrooks Davis static int 1106f889d2efSBrooks Davis vlan_clone_destroy(struct if_clone *ifc, struct ifnet *ifp) 11079d4fe4b2SBrooks Davis { 11089d4fe4b2SBrooks Davis struct ifvlan *ifv = ifp->if_softc; 1109114c608cSYaroslav Tykhiy int unit = ifp->if_dunit; 1110b4e9f837SBrooks Davis 1111249f4297SYaroslav Tykhiy ether_ifdetach(ifp); /* first, remove it from system-wide lists */ 1112249f4297SYaroslav Tykhiy vlan_unconfig(ifp); /* now it can be unconfigured and freed */ 1113d148c2a2SMatt Joras /* 1114d148c2a2SMatt Joras * We should have the only reference to the ifv now, so we can now 1115d148c2a2SMatt Joras * drain any remaining lladdr task before freeing the ifnet and the 1116d148c2a2SMatt Joras * ifvlan. 1117d148c2a2SMatt Joras */ 1118d148c2a2SMatt Joras taskqueue_drain(taskqueue_thread, &ifv->lladdr_task); 1119b08d611dSMatt Macy NET_EPOCH_WAIT(); 11204b22573aSBrooks Davis if_free(ifp); 11219d4fe4b2SBrooks Davis free(ifv, M_VLAN); 1122b4e9f837SBrooks Davis ifc_free_unit(ifc, unit); 1123b4e9f837SBrooks Davis 1124f889d2efSBrooks Davis return (0); 11259d4fe4b2SBrooks Davis } 11269d4fe4b2SBrooks Davis 112715a66c21SBruce M Simpson /* 112815a66c21SBruce M Simpson * The ifp->if_init entry point for vlan(4) is a no-op. 112915a66c21SBruce M Simpson */ 11302cc2df49SGarrett Wollman static void 1131114c608cSYaroslav Tykhiy vlan_init(void *foo __unused) 11322cc2df49SGarrett Wollman { 11332cc2df49SGarrett Wollman } 11342cc2df49SGarrett Wollman 11356d3a3ab7SGleb Smirnoff /* 1136d9b1d615SJohn Baldwin * The if_transmit method for vlan(4) interface. 11376d3a3ab7SGleb Smirnoff */ 1138d9b1d615SJohn Baldwin static int 1139d9b1d615SJohn Baldwin vlan_transmit(struct ifnet *ifp, struct mbuf *m) 11402cc2df49SGarrett Wollman { 1141a68cc388SGleb Smirnoff struct epoch_tracker et; 11422cc2df49SGarrett Wollman struct ifvlan *ifv; 11432cc2df49SGarrett Wollman struct ifnet *p; 11441ad7a257SPyun YongHyeon int error, len, mcast; 11452cc2df49SGarrett Wollman 1146a68cc388SGleb Smirnoff NET_EPOCH_ENTER(et); 11472cc2df49SGarrett Wollman ifv = ifp->if_softc; 1148d148c2a2SMatt Joras if (TRUNK(ifv) == NULL) { 1149d148c2a2SMatt Joras if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 1150a68cc388SGleb Smirnoff NET_EPOCH_EXIT(et); 1151d148c2a2SMatt Joras m_freem(m); 1152d148c2a2SMatt Joras return (ENETDOWN); 1153d148c2a2SMatt Joras } 115475ee267cSGleb Smirnoff p = PARENT(ifv); 11551ad7a257SPyun YongHyeon len = m->m_pkthdr.len; 11561ad7a257SPyun YongHyeon mcast = (m->m_flags & (M_MCAST | M_BCAST)) ? 1 : 0; 11572cc2df49SGarrett Wollman 1158a3814acfSSam Leffler BPF_MTAP(ifp, m); 11592cc2df49SGarrett Wollman 1160*fb3bc596SJohn Baldwin #ifdef RATELIMIT 1161*fb3bc596SJohn Baldwin if (m->m_pkthdr.csum_flags & CSUM_SND_TAG) { 1162*fb3bc596SJohn Baldwin struct vlan_snd_tag *vst; 1163*fb3bc596SJohn Baldwin struct m_snd_tag *mst; 1164*fb3bc596SJohn Baldwin 1165*fb3bc596SJohn Baldwin MPASS(m->m_pkthdr.snd_tag->ifp == ifp); 1166*fb3bc596SJohn Baldwin mst = m->m_pkthdr.snd_tag; 1167*fb3bc596SJohn Baldwin vst = mst_to_vst(mst); 1168*fb3bc596SJohn Baldwin if (vst->tag->ifp != p) { 1169*fb3bc596SJohn Baldwin if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 1170*fb3bc596SJohn Baldwin NET_EPOCH_EXIT(et); 1171*fb3bc596SJohn Baldwin m_freem(m); 1172*fb3bc596SJohn Baldwin return (EAGAIN); 1173*fb3bc596SJohn Baldwin } 1174*fb3bc596SJohn Baldwin 1175*fb3bc596SJohn Baldwin m->m_pkthdr.snd_tag = m_snd_tag_ref(vst->tag); 1176*fb3bc596SJohn Baldwin m_snd_tag_rele(mst); 1177*fb3bc596SJohn Baldwin } 1178*fb3bc596SJohn Baldwin #endif 1179*fb3bc596SJohn Baldwin 1180f731f104SBill Paul /* 1181d9b1d615SJohn Baldwin * Do not run parent's if_transmit() if the parent is not up, 118224993214SYaroslav Tykhiy * or parent's driver will cause a system crash. 118324993214SYaroslav Tykhiy */ 11842dc879b3SYaroslav Tykhiy if (!UP_AND_RUNNING(p)) { 1185a58ea6b1SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 1186a68cc388SGleb Smirnoff NET_EPOCH_EXIT(et); 1187d148c2a2SMatt Joras m_freem(m); 11888b20f6cfSHiroki Sato return (ENETDOWN); 118924993214SYaroslav Tykhiy } 119024993214SYaroslav Tykhiy 1191f1379734SKonstantin Belousov if (!ether_8021q_frame(&m, ifp, p, ifv->ifv_vid, ifv->ifv_pcp)) { 1192a58ea6b1SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 1193a68cc388SGleb Smirnoff NET_EPOCH_EXIT(et); 1194d9b1d615SJohn Baldwin return (0); 11954af90a4dSMatthew N. Dodd } 11962cc2df49SGarrett Wollman 11972cc2df49SGarrett Wollman /* 11982cc2df49SGarrett Wollman * Send it, precisely as ether_output() would have. 11992cc2df49SGarrett Wollman */ 1200aea78d20SKip Macy error = (p->if_transmit)(p, m); 1201299153b5SAlexander V. Chernikov if (error == 0) { 1202a58ea6b1SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); 1203a58ea6b1SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_OBYTES, len); 1204a58ea6b1SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_OMCASTS, mcast); 12051ad7a257SPyun YongHyeon } else 1206a58ea6b1SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 1207a68cc388SGleb Smirnoff NET_EPOCH_EXIT(et); 1208d9b1d615SJohn Baldwin return (error); 12092cc2df49SGarrett Wollman } 1210d9b1d615SJohn Baldwin 1211d9b1d615SJohn Baldwin /* 1212d9b1d615SJohn Baldwin * The ifp->if_qflush entry point for vlan(4) is a no-op. 1213d9b1d615SJohn Baldwin */ 1214d9b1d615SJohn Baldwin static void 1215d9b1d615SJohn Baldwin vlan_qflush(struct ifnet *ifp __unused) 1216d9b1d615SJohn Baldwin { 1217f731f104SBill Paul } 1218f731f104SBill Paul 1219a3814acfSSam Leffler static void 1220a3814acfSSam Leffler vlan_input(struct ifnet *ifp, struct mbuf *m) 1221f731f104SBill Paul { 1222a68cc388SGleb Smirnoff struct epoch_tracker et; 1223d148c2a2SMatt Joras struct ifvlantrunk *trunk; 1224f731f104SBill Paul struct ifvlan *ifv; 12252ccbbd06SMarcelo Araujo struct m_tag *mtag; 12262ccbbd06SMarcelo Araujo uint16_t vid, tag; 122775ee267cSGleb Smirnoff 1228a68cc388SGleb Smirnoff NET_EPOCH_ENTER(et); 1229d148c2a2SMatt Joras trunk = ifp->if_vlantrunk; 1230d148c2a2SMatt Joras if (trunk == NULL) { 1231a68cc388SGleb Smirnoff NET_EPOCH_EXIT(et); 1232d148c2a2SMatt Joras m_freem(m); 1233d148c2a2SMatt Joras return; 1234d148c2a2SMatt Joras } 1235a3814acfSSam Leffler 1236f4ec4126SYaroslav Tykhiy if (m->m_flags & M_VLANTAG) { 1237a3814acfSSam Leffler /* 123814e98256SYaroslav Tykhiy * Packet is tagged, but m contains a normal 1239a3814acfSSam Leffler * Ethernet frame; the tag is stored out-of-band. 1240a3814acfSSam Leffler */ 12412ccbbd06SMarcelo Araujo tag = m->m_pkthdr.ether_vtag; 12426ee20ab5SRuslan Ermilov m->m_flags &= ~M_VLANTAG; 1243a3814acfSSam Leffler } else { 124475ee267cSGleb Smirnoff struct ether_vlan_header *evl; 124575ee267cSGleb Smirnoff 124614e98256SYaroslav Tykhiy /* 124714e98256SYaroslav Tykhiy * Packet is tagged in-band as specified by 802.1q. 124814e98256SYaroslav Tykhiy */ 1249a3814acfSSam Leffler switch (ifp->if_type) { 1250a3814acfSSam Leffler case IFT_ETHER: 1251a3814acfSSam Leffler if (m->m_len < sizeof(*evl) && 1252a3814acfSSam Leffler (m = m_pullup(m, sizeof(*evl))) == NULL) { 1253a3814acfSSam Leffler if_printf(ifp, "cannot pullup VLAN header\n"); 1254a68cc388SGleb Smirnoff NET_EPOCH_EXIT(et); 1255a3814acfSSam Leffler return; 1256a3814acfSSam Leffler } 1257a3814acfSSam Leffler evl = mtod(m, struct ether_vlan_header *); 12582ccbbd06SMarcelo Araujo tag = ntohs(evl->evl_tag); 1259db8b5973SYaroslav Tykhiy 1260db8b5973SYaroslav Tykhiy /* 12612dc879b3SYaroslav Tykhiy * Remove the 802.1q header by copying the Ethernet 12622dc879b3SYaroslav Tykhiy * addresses over it and adjusting the beginning of 12632dc879b3SYaroslav Tykhiy * the data in the mbuf. The encapsulated Ethernet 12642dc879b3SYaroslav Tykhiy * type field is already in place. 1265db8b5973SYaroslav Tykhiy */ 12662dc879b3SYaroslav Tykhiy bcopy((char *)evl, (char *)evl + ETHER_VLAN_ENCAP_LEN, 12672dc879b3SYaroslav Tykhiy ETHER_HDR_LEN - ETHER_TYPE_LEN); 12682dc879b3SYaroslav Tykhiy m_adj(m, ETHER_VLAN_ENCAP_LEN); 1269a3814acfSSam Leffler break; 12702dc879b3SYaroslav Tykhiy 1271a3814acfSSam Leffler default: 1272db8b5973SYaroslav Tykhiy #ifdef INVARIANTS 127360c60618SYaroslav Tykhiy panic("%s: %s has unsupported if_type %u", 127460c60618SYaroslav Tykhiy __func__, ifp->if_xname, ifp->if_type); 1275a3814acfSSam Leffler #endif 12763751dddbSGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_NOPROTO, 1); 1277a68cc388SGleb Smirnoff NET_EPOCH_EXIT(et); 1278d148c2a2SMatt Joras m_freem(m); 127960c60618SYaroslav Tykhiy return; 1280a3814acfSSam Leffler } 12817a46ec8fSBrooks Davis } 12827a46ec8fSBrooks Davis 12832ccbbd06SMarcelo Araujo vid = EVL_VLANOFTAG(tag); 12842ccbbd06SMarcelo Araujo 12857983103aSRobert Watson ifv = vlan_gethash(trunk, vid); 12862dc879b3SYaroslav Tykhiy if (ifv == NULL || !UP_AND_RUNNING(ifv->ifv_ifp)) { 1287a68cc388SGleb Smirnoff NET_EPOCH_EXIT(et); 1288b08d611dSMatt Macy if_inc_counter(ifp, IFCOUNTER_NOPROTO, 1); 1289d148c2a2SMatt Joras m_freem(m); 129075ee267cSGleb Smirnoff return; 129175ee267cSGleb Smirnoff } 1292f731f104SBill Paul 12932ccbbd06SMarcelo Araujo if (vlan_mtag_pcp) { 12942ccbbd06SMarcelo Araujo /* 12952ccbbd06SMarcelo Araujo * While uncommon, it is possible that we will find a 802.1q 12962ccbbd06SMarcelo Araujo * packet encapsulated inside another packet that also had an 12972ccbbd06SMarcelo Araujo * 802.1q header. For example, ethernet tunneled over IPSEC 12982ccbbd06SMarcelo Araujo * arriving over ethernet. In that case, we replace the 12992ccbbd06SMarcelo Araujo * existing 802.1q PCP m_tag value. 13002ccbbd06SMarcelo Araujo */ 13012ccbbd06SMarcelo Araujo mtag = m_tag_locate(m, MTAG_8021Q, MTAG_8021Q_PCP_IN, NULL); 13022ccbbd06SMarcelo Araujo if (mtag == NULL) { 13032ccbbd06SMarcelo Araujo mtag = m_tag_alloc(MTAG_8021Q, MTAG_8021Q_PCP_IN, 13042ccbbd06SMarcelo Araujo sizeof(uint8_t), M_NOWAIT); 13052ccbbd06SMarcelo Araujo if (mtag == NULL) { 13062ccbbd06SMarcelo Araujo if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); 1307a68cc388SGleb Smirnoff NET_EPOCH_EXIT(et); 1308d148c2a2SMatt Joras m_freem(m); 13092ccbbd06SMarcelo Araujo return; 13102ccbbd06SMarcelo Araujo } 13112ccbbd06SMarcelo Araujo m_tag_prepend(m, mtag); 13122ccbbd06SMarcelo Araujo } 13132ccbbd06SMarcelo Araujo *(uint8_t *)(mtag + 1) = EVL_PRIOFTAG(tag); 13142ccbbd06SMarcelo Araujo } 13152ccbbd06SMarcelo Araujo 1316fc74a9f9SBrooks Davis m->m_pkthdr.rcvif = ifv->ifv_ifp; 1317c0304424SGleb Smirnoff if_inc_counter(ifv->ifv_ifp, IFCOUNTER_IPACKETS, 1); 1318a68cc388SGleb Smirnoff NET_EPOCH_EXIT(et); 13192cc2df49SGarrett Wollman 1320a3814acfSSam Leffler /* Pass it back through the parent's input routine. */ 1321fdbf1174SMatt Joras (*ifv->ifv_ifp->if_input)(ifv->ifv_ifp, m); 13222cc2df49SGarrett Wollman } 13232cc2df49SGarrett Wollman 1324d148c2a2SMatt Joras static void 1325d148c2a2SMatt Joras vlan_lladdr_fn(void *arg, int pending __unused) 1326d148c2a2SMatt Joras { 1327d148c2a2SMatt Joras struct ifvlan *ifv; 1328d148c2a2SMatt Joras struct ifnet *ifp; 1329d148c2a2SMatt Joras 1330d148c2a2SMatt Joras ifv = (struct ifvlan *)arg; 1331d148c2a2SMatt Joras ifp = ifv->ifv_ifp; 13325191a3aeSKristof Provost 13335191a3aeSKristof Provost CURVNET_SET(ifp->if_vnet); 13345191a3aeSKristof Provost 1335d148c2a2SMatt Joras /* The ifv_ifp already has the lladdr copied in. */ 1336d148c2a2SMatt Joras if_setlladdr(ifp, IF_LLADDR(ifp), ifp->if_addrlen); 13375191a3aeSKristof Provost 13385191a3aeSKristof Provost CURVNET_RESTORE(); 1339d148c2a2SMatt Joras } 1340d148c2a2SMatt Joras 13412cc2df49SGarrett Wollman static int 13427983103aSRobert Watson vlan_config(struct ifvlan *ifv, struct ifnet *p, uint16_t vid) 13432cc2df49SGarrett Wollman { 1344a68cc388SGleb Smirnoff struct epoch_tracker et; 134575ee267cSGleb Smirnoff struct ifvlantrunk *trunk; 13461cf236fbSYaroslav Tykhiy struct ifnet *ifp; 134775ee267cSGleb Smirnoff int error = 0; 13482cc2df49SGarrett Wollman 1349b1828acfSGleb Smirnoff /* 1350b1828acfSGleb Smirnoff * We can handle non-ethernet hardware types as long as 1351b1828acfSGleb Smirnoff * they handle the tagging and headers themselves. 1352b1828acfSGleb Smirnoff */ 1353e4cd31ddSJeff Roberson if (p->if_type != IFT_ETHER && 1354e4cd31ddSJeff Roberson (p->if_capenable & IFCAP_VLAN_HWTAGGING) == 0) 135515a66c21SBruce M Simpson return (EPROTONOSUPPORT); 135664a17d2eSYaroslav Tykhiy if ((p->if_flags & VLAN_IFFLAGS) != VLAN_IFFLAGS) 135764a17d2eSYaroslav Tykhiy return (EPROTONOSUPPORT); 1358b1828acfSGleb Smirnoff /* 1359b1828acfSGleb Smirnoff * Don't let the caller set up a VLAN VID with 1360b1828acfSGleb Smirnoff * anything except VLID bits. 1361b1828acfSGleb Smirnoff * VID numbers 0x0 and 0xFFF are reserved. 1362b1828acfSGleb Smirnoff */ 1363b1828acfSGleb Smirnoff if (vid == 0 || vid == 0xFFF || (vid & ~EVL_VLID_MASK)) 1364b1828acfSGleb Smirnoff return (EINVAL); 136575ee267cSGleb Smirnoff if (ifv->ifv_trunk) 136615a66c21SBruce M Simpson return (EBUSY); 13672cc2df49SGarrett Wollman 1368d148c2a2SMatt Joras VLAN_XLOCK(); 136975ee267cSGleb Smirnoff if (p->if_vlantrunk == NULL) { 137075ee267cSGleb Smirnoff trunk = malloc(sizeof(struct ifvlantrunk), 137175ee267cSGleb Smirnoff M_VLAN, M_WAITOK | M_ZERO); 137275ee267cSGleb Smirnoff vlan_inithash(trunk); 137375ee267cSGleb Smirnoff TRUNK_LOCK_INIT(trunk); 1374d148c2a2SMatt Joras TRUNK_WLOCK(trunk); 137575ee267cSGleb Smirnoff p->if_vlantrunk = trunk; 137675ee267cSGleb Smirnoff trunk->parent = p; 13779bcf3ae4SAlexander Motin if_ref(trunk->parent); 1378b08d611dSMatt Macy TRUNK_WUNLOCK(trunk); 137975ee267cSGleb Smirnoff } else { 138075ee267cSGleb Smirnoff trunk = p->if_vlantrunk; 138175ee267cSGleb Smirnoff } 138275ee267cSGleb Smirnoff 13837983103aSRobert Watson ifv->ifv_vid = vid; /* must set this before vlan_inshash() */ 13842ccbbd06SMarcelo Araujo ifv->ifv_pcp = 0; /* Default: best effort delivery. */ 13852ccbbd06SMarcelo Araujo vlan_tag_recalculate(ifv); 138675ee267cSGleb Smirnoff error = vlan_inshash(trunk, ifv); 138775ee267cSGleb Smirnoff if (error) 138875ee267cSGleb Smirnoff goto done; 138973f2233dSYaroslav Tykhiy ifv->ifv_proto = ETHERTYPE_VLAN; 1390a3814acfSSam Leffler ifv->ifv_encaplen = ETHER_VLAN_ENCAP_LEN; 1391a3814acfSSam Leffler ifv->ifv_mintu = ETHERMIN; 13921cf236fbSYaroslav Tykhiy ifv->ifv_pflags = 0; 1393d89baa5aSAlexander Motin ifv->ifv_capenable = -1; 1394a3814acfSSam Leffler 1395a3814acfSSam Leffler /* 1396a3814acfSSam Leffler * If the parent supports the VLAN_MTU capability, 1397a3814acfSSam Leffler * i.e. can Tx/Rx larger than ETHER_MAX_LEN frames, 1398656acce4SYaroslav Tykhiy * use it. 1399a3814acfSSam Leffler */ 1400656acce4SYaroslav Tykhiy if (p->if_capenable & IFCAP_VLAN_MTU) { 1401656acce4SYaroslav Tykhiy /* 1402656acce4SYaroslav Tykhiy * No need to fudge the MTU since the parent can 1403656acce4SYaroslav Tykhiy * handle extended frames. 1404656acce4SYaroslav Tykhiy */ 1405a3814acfSSam Leffler ifv->ifv_mtufudge = 0; 1406656acce4SYaroslav Tykhiy } else { 1407a3814acfSSam Leffler /* 1408a3814acfSSam Leffler * Fudge the MTU by the encapsulation size. This 1409a3814acfSSam Leffler * makes us incompatible with strictly compliant 1410a3814acfSSam Leffler * 802.1Q implementations, but allows us to use 1411a3814acfSSam Leffler * the feature with other NetBSD implementations, 1412a3814acfSSam Leffler * which might still be useful. 1413a3814acfSSam Leffler */ 1414a3814acfSSam Leffler ifv->ifv_mtufudge = ifv->ifv_encaplen; 1415a3814acfSSam Leffler } 1416a3814acfSSam Leffler 141775ee267cSGleb Smirnoff ifv->ifv_trunk = trunk; 14181cf236fbSYaroslav Tykhiy ifp = ifv->ifv_ifp; 1419e4cd31ddSJeff Roberson /* 1420e4cd31ddSJeff Roberson * Initialize fields from our parent. This duplicates some 1421e4cd31ddSJeff Roberson * work with ether_ifattach() but allows for non-ethernet 1422e4cd31ddSJeff Roberson * interfaces to also work. 1423e4cd31ddSJeff Roberson */ 14241cf236fbSYaroslav Tykhiy ifp->if_mtu = p->if_mtu - ifv->ifv_mtufudge; 142575ee267cSGleb Smirnoff ifp->if_baudrate = p->if_baudrate; 1426e4cd31ddSJeff Roberson ifp->if_output = p->if_output; 1427e4cd31ddSJeff Roberson ifp->if_input = p->if_input; 1428e4cd31ddSJeff Roberson ifp->if_resolvemulti = p->if_resolvemulti; 1429e4cd31ddSJeff Roberson ifp->if_addrlen = p->if_addrlen; 1430e4cd31ddSJeff Roberson ifp->if_broadcastaddr = p->if_broadcastaddr; 143132a52e9eSNavdeep Parhar ifp->if_pcp = ifv->ifv_pcp; 1432e4cd31ddSJeff Roberson 14332cc2df49SGarrett Wollman /* 143424993214SYaroslav Tykhiy * Copy only a selected subset of flags from the parent. 143524993214SYaroslav Tykhiy * Other flags are none of our business. 14362cc2df49SGarrett Wollman */ 143764a17d2eSYaroslav Tykhiy #define VLAN_COPY_FLAGS (IFF_SIMPLEX) 14381cf236fbSYaroslav Tykhiy ifp->if_flags &= ~VLAN_COPY_FLAGS; 14391cf236fbSYaroslav Tykhiy ifp->if_flags |= p->if_flags & VLAN_COPY_FLAGS; 14401cf236fbSYaroslav Tykhiy #undef VLAN_COPY_FLAGS 14411cf236fbSYaroslav Tykhiy 14421cf236fbSYaroslav Tykhiy ifp->if_link_state = p->if_link_state; 14432cc2df49SGarrett Wollman 1444a68cc388SGleb Smirnoff NET_EPOCH_ENTER(et); 144575ee267cSGleb Smirnoff vlan_capabilities(ifv); 1446a68cc388SGleb Smirnoff NET_EPOCH_EXIT(et); 1447a3814acfSSam Leffler 1448a3814acfSSam Leffler /* 1449e4cd31ddSJeff Roberson * Set up our interface address to reflect the underlying 14502cc2df49SGarrett Wollman * physical interface's. 14512cc2df49SGarrett Wollman */ 1452e4cd31ddSJeff Roberson bcopy(IF_LLADDR(p), IF_LLADDR(ifp), p->if_addrlen); 1453e4cd31ddSJeff Roberson ((struct sockaddr_dl *)ifp->if_addr->ifa_addr)->sdl_alen = 1454e4cd31ddSJeff Roberson p->if_addrlen; 14551b2a4f7aSBill Fenner 1456d148c2a2SMatt Joras TASK_INIT(&ifv->lladdr_task, 0, vlan_lladdr_fn, ifv); 14572ada9747SYaroslav Tykhiy 14582ada9747SYaroslav Tykhiy /* We are ready for operation now. */ 14592ada9747SYaroslav Tykhiy ifp->if_drv_flags |= IFF_DRV_RUNNING; 1460d148c2a2SMatt Joras 1461d148c2a2SMatt Joras /* Update flags on the parent, if necessary. */ 1462d148c2a2SMatt Joras vlan_setflags(ifp, 1); 1463b08d611dSMatt Macy 1464d148c2a2SMatt Joras /* 1465b08d611dSMatt Macy * Configure multicast addresses that may already be 1466b08d611dSMatt Macy * joined on the vlan device. 1467d148c2a2SMatt Joras */ 1468b08d611dSMatt Macy (void)vlan_setmulti(ifp); 1469b08d611dSMatt Macy 1470b08d611dSMatt Macy done: 1471c725524cSJack F Vogel if (error == 0) 14727983103aSRobert Watson EVENTHANDLER_INVOKE(vlan_config, p, ifv->ifv_vid); 1473d148c2a2SMatt Joras VLAN_XUNLOCK(); 147475ee267cSGleb Smirnoff 147575ee267cSGleb Smirnoff return (error); 14762cc2df49SGarrett Wollman } 14772cc2df49SGarrett Wollman 14786f359e28SJohn Baldwin static void 1479f731f104SBill Paul vlan_unconfig(struct ifnet *ifp) 1480f731f104SBill Paul { 14815cb8c31aSYaroslav Tykhiy 1482d148c2a2SMatt Joras VLAN_XLOCK(); 148328cc4d37SJohn Baldwin vlan_unconfig_locked(ifp, 0); 1484d148c2a2SMatt Joras VLAN_XUNLOCK(); 14855cb8c31aSYaroslav Tykhiy } 14865cb8c31aSYaroslav Tykhiy 14876f359e28SJohn Baldwin static void 148828cc4d37SJohn Baldwin vlan_unconfig_locked(struct ifnet *ifp, int departing) 14895cb8c31aSYaroslav Tykhiy { 149075ee267cSGleb Smirnoff struct ifvlantrunk *trunk; 1491f731f104SBill Paul struct vlan_mc_entry *mc; 1492f731f104SBill Paul struct ifvlan *ifv; 1493c725524cSJack F Vogel struct ifnet *parent; 149428cc4d37SJohn Baldwin int error; 1495f731f104SBill Paul 1496d148c2a2SMatt Joras VLAN_XLOCK_ASSERT(); 14974faedfe8SSam Leffler 1498f731f104SBill Paul ifv = ifp->if_softc; 149975ee267cSGleb Smirnoff trunk = ifv->ifv_trunk; 150022893351SJack F Vogel parent = NULL; 1501f731f104SBill Paul 150222893351SJack F Vogel if (trunk != NULL) { 150322893351SJack F Vogel parent = trunk->parent; 15041b2a4f7aSBill Fenner 1505f731f104SBill Paul /* 1506f731f104SBill Paul * Since the interface is being unconfigured, we need to 1507f731f104SBill Paul * empty the list of multicast groups that we may have joined 15081b2a4f7aSBill Fenner * while we were alive from the parent's list. 1509f731f104SBill Paul */ 1510b08d611dSMatt Macy while ((mc = CK_SLIST_FIRST(&ifv->vlan_mc_listhead)) != NULL) { 15116f359e28SJohn Baldwin /* 151228cc4d37SJohn Baldwin * If the parent interface is being detached, 1513b90dde2fSJohn Baldwin * all its multicast addresses have already 151428cc4d37SJohn Baldwin * been removed. Warn about errors if 151528cc4d37SJohn Baldwin * if_delmulti() does fail, but don't abort as 151628cc4d37SJohn Baldwin * all callers expect vlan destruction to 151728cc4d37SJohn Baldwin * succeed. 15186f359e28SJohn Baldwin */ 151928cc4d37SJohn Baldwin if (!departing) { 152028cc4d37SJohn Baldwin error = if_delmulti(parent, 1521e4cd31ddSJeff Roberson (struct sockaddr *)&mc->mc_addr); 152228cc4d37SJohn Baldwin if (error) 152328cc4d37SJohn Baldwin if_printf(ifp, 152428cc4d37SJohn Baldwin "Failed to delete multicast address from parent: %d\n", 152528cc4d37SJohn Baldwin error); 152628cc4d37SJohn Baldwin } 1527b08d611dSMatt Macy CK_SLIST_REMOVE_HEAD(&ifv->vlan_mc_listhead, mc_entries); 1528c32a9d66SHans Petter Selasky epoch_call(net_epoch_preempt, &mc->mc_epoch_ctx, vlan_mc_free); 1529f731f104SBill Paul } 1530a3814acfSSam Leffler 15311cf236fbSYaroslav Tykhiy vlan_setflags(ifp, 0); /* clear special flags on parent */ 1532d148c2a2SMatt Joras 153375ee267cSGleb Smirnoff vlan_remhash(trunk, ifv); 153475ee267cSGleb Smirnoff ifv->ifv_trunk = NULL; 153575ee267cSGleb Smirnoff 153675ee267cSGleb Smirnoff /* 153775ee267cSGleb Smirnoff * Check if we were the last. 153875ee267cSGleb Smirnoff */ 153975ee267cSGleb Smirnoff if (trunk->refcnt == 0) { 15402d222cb7SAlexander Motin parent->if_vlantrunk = NULL; 1541b08d611dSMatt Macy NET_EPOCH_WAIT(); 154275ee267cSGleb Smirnoff trunk_destroy(trunk); 1543d148c2a2SMatt Joras } 15441b2a4f7aSBill Fenner } 1545f731f104SBill Paul 1546f731f104SBill Paul /* Disconnect from parent. */ 15471cf236fbSYaroslav Tykhiy if (ifv->ifv_pflags) 15481cf236fbSYaroslav Tykhiy if_printf(ifp, "%s: ifv_pflags unclean\n", __func__); 15495cb8c31aSYaroslav Tykhiy ifp->if_mtu = ETHERMTU; 15505cb8c31aSYaroslav Tykhiy ifp->if_link_state = LINK_STATE_UNKNOWN; 15515cb8c31aSYaroslav Tykhiy ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 1552f731f104SBill Paul 155322893351SJack F Vogel /* 155422893351SJack F Vogel * Only dispatch an event if vlan was 155522893351SJack F Vogel * attached, otherwise there is nothing 155622893351SJack F Vogel * to cleanup anyway. 155722893351SJack F Vogel */ 155822893351SJack F Vogel if (parent != NULL) 15597983103aSRobert Watson EVENTHANDLER_INVOKE(vlan_unconfig, parent, ifv->ifv_vid); 1560f731f104SBill Paul } 1561f731f104SBill Paul 15621cf236fbSYaroslav Tykhiy /* Handle a reference counted flag that should be set on the parent as well */ 1563f731f104SBill Paul static int 15641cf236fbSYaroslav Tykhiy vlan_setflag(struct ifnet *ifp, int flag, int status, 15651cf236fbSYaroslav Tykhiy int (*func)(struct ifnet *, int)) 1566a3814acfSSam Leffler { 15671cf236fbSYaroslav Tykhiy struct ifvlan *ifv; 15681cf236fbSYaroslav Tykhiy int error; 1569a3814acfSSam Leffler 1570d148c2a2SMatt Joras VLAN_SXLOCK_ASSERT(); 1571a3814acfSSam Leffler 15721cf236fbSYaroslav Tykhiy ifv = ifp->if_softc; 15731cf236fbSYaroslav Tykhiy status = status ? (ifp->if_flags & flag) : 0; 15741cf236fbSYaroslav Tykhiy /* Now "status" contains the flag value or 0 */ 15751cf236fbSYaroslav Tykhiy 15761cf236fbSYaroslav Tykhiy /* 15771cf236fbSYaroslav Tykhiy * See if recorded parent's status is different from what 15781cf236fbSYaroslav Tykhiy * we want it to be. If it is, flip it. We record parent's 15791cf236fbSYaroslav Tykhiy * status in ifv_pflags so that we won't clear parent's flag 15801cf236fbSYaroslav Tykhiy * we haven't set. In fact, we don't clear or set parent's 15811cf236fbSYaroslav Tykhiy * flags directly, but get or release references to them. 15821cf236fbSYaroslav Tykhiy * That's why we can be sure that recorded flags still are 15831cf236fbSYaroslav Tykhiy * in accord with actual parent's flags. 15841cf236fbSYaroslav Tykhiy */ 15851cf236fbSYaroslav Tykhiy if (status != (ifv->ifv_pflags & flag)) { 158675ee267cSGleb Smirnoff error = (*func)(PARENT(ifv), status); 15871cf236fbSYaroslav Tykhiy if (error) 1588a3814acfSSam Leffler return (error); 15891cf236fbSYaroslav Tykhiy ifv->ifv_pflags &= ~flag; 15901cf236fbSYaroslav Tykhiy ifv->ifv_pflags |= status; 15911cf236fbSYaroslav Tykhiy } 15921cf236fbSYaroslav Tykhiy return (0); 15931cf236fbSYaroslav Tykhiy } 15941cf236fbSYaroslav Tykhiy 15951cf236fbSYaroslav Tykhiy /* 15961cf236fbSYaroslav Tykhiy * Handle IFF_* flags that require certain changes on the parent: 15971cf236fbSYaroslav Tykhiy * if "status" is true, update parent's flags respective to our if_flags; 15981cf236fbSYaroslav Tykhiy * if "status" is false, forcedly clear the flags set on parent. 15991cf236fbSYaroslav Tykhiy */ 16001cf236fbSYaroslav Tykhiy static int 16011cf236fbSYaroslav Tykhiy vlan_setflags(struct ifnet *ifp, int status) 16021cf236fbSYaroslav Tykhiy { 16031cf236fbSYaroslav Tykhiy int error, i; 16041cf236fbSYaroslav Tykhiy 16051cf236fbSYaroslav Tykhiy for (i = 0; vlan_pflags[i].flag; i++) { 16061cf236fbSYaroslav Tykhiy error = vlan_setflag(ifp, vlan_pflags[i].flag, 16071cf236fbSYaroslav Tykhiy status, vlan_pflags[i].func); 16081cf236fbSYaroslav Tykhiy if (error) 16091cf236fbSYaroslav Tykhiy return (error); 16101cf236fbSYaroslav Tykhiy } 16111cf236fbSYaroslav Tykhiy return (0); 1612a3814acfSSam Leffler } 1613a3814acfSSam Leffler 1614127d7b2dSAndre Oppermann /* Inform all vlans that their parent has changed link state */ 1615127d7b2dSAndre Oppermann static void 1616a6fffd6cSBrooks Davis vlan_link_state(struct ifnet *ifp) 1617127d7b2dSAndre Oppermann { 1618a68cc388SGleb Smirnoff struct epoch_tracker et; 1619d148c2a2SMatt Joras struct ifvlantrunk *trunk; 1620127d7b2dSAndre Oppermann struct ifvlan *ifv; 1621127d7b2dSAndre Oppermann 1622d148c2a2SMatt Joras /* Called from a taskqueue_swi task, so we cannot sleep. */ 1623a68cc388SGleb Smirnoff NET_EPOCH_ENTER(et); 1624d148c2a2SMatt Joras trunk = ifp->if_vlantrunk; 1625d148c2a2SMatt Joras if (trunk == NULL) { 1626a68cc388SGleb Smirnoff NET_EPOCH_EXIT(et); 1627d148c2a2SMatt Joras return; 1628d148c2a2SMatt Joras } 1629d148c2a2SMatt Joras 1630d148c2a2SMatt Joras TRUNK_WLOCK(trunk); 1631d148c2a2SMatt Joras VLAN_FOREACH(ifv, trunk) { 1632aad0be7aSGleb Smirnoff ifv->ifv_ifp->if_baudrate = trunk->parent->if_baudrate; 1633fc74a9f9SBrooks Davis if_link_state_change(ifv->ifv_ifp, 163475ee267cSGleb Smirnoff trunk->parent->if_link_state); 1635127d7b2dSAndre Oppermann } 1636d148c2a2SMatt Joras TRUNK_WUNLOCK(trunk); 1637a68cc388SGleb Smirnoff NET_EPOCH_EXIT(et); 163875ee267cSGleb Smirnoff } 163975ee267cSGleb Smirnoff 164075ee267cSGleb Smirnoff static void 164175ee267cSGleb Smirnoff vlan_capabilities(struct ifvlan *ifv) 164275ee267cSGleb Smirnoff { 1643d148c2a2SMatt Joras struct ifnet *p; 1644d148c2a2SMatt Joras struct ifnet *ifp; 16459fd573c3SHans Petter Selasky struct ifnet_hw_tsomax hw_tsomax; 1646d89baa5aSAlexander Motin int cap = 0, ena = 0, mena; 1647d89baa5aSAlexander Motin u_long hwa = 0; 164875ee267cSGleb Smirnoff 1649d148c2a2SMatt Joras VLAN_SXLOCK_ASSERT(); 1650a68cc388SGleb Smirnoff NET_EPOCH_ASSERT(); 1651d148c2a2SMatt Joras p = PARENT(ifv); 1652d148c2a2SMatt Joras ifp = ifv->ifv_ifp; 165375ee267cSGleb Smirnoff 1654d89baa5aSAlexander Motin /* Mask parent interface enabled capabilities disabled by user. */ 1655d89baa5aSAlexander Motin mena = p->if_capenable & ifv->ifv_capenable; 1656d89baa5aSAlexander Motin 165775ee267cSGleb Smirnoff /* 165875ee267cSGleb Smirnoff * If the parent interface can do checksum offloading 165975ee267cSGleb Smirnoff * on VLANs, then propagate its hardware-assisted 166075ee267cSGleb Smirnoff * checksumming flags. Also assert that checksum 166175ee267cSGleb Smirnoff * offloading requires hardware VLAN tagging. 166275ee267cSGleb Smirnoff */ 166375ee267cSGleb Smirnoff if (p->if_capabilities & IFCAP_VLAN_HWCSUM) 1664d89baa5aSAlexander Motin cap |= p->if_capabilities & (IFCAP_HWCSUM | IFCAP_HWCSUM_IPV6); 166575ee267cSGleb Smirnoff if (p->if_capenable & IFCAP_VLAN_HWCSUM && 166675ee267cSGleb Smirnoff p->if_capenable & IFCAP_VLAN_HWTAGGING) { 1667d89baa5aSAlexander Motin ena |= mena & (IFCAP_HWCSUM | IFCAP_HWCSUM_IPV6); 1668d89baa5aSAlexander Motin if (ena & IFCAP_TXCSUM) 1669d89baa5aSAlexander Motin hwa |= p->if_hwassist & (CSUM_IP | CSUM_TCP | 1670d89baa5aSAlexander Motin CSUM_UDP | CSUM_SCTP); 1671d89baa5aSAlexander Motin if (ena & IFCAP_TXCSUM_IPV6) 1672d89baa5aSAlexander Motin hwa |= p->if_hwassist & (CSUM_TCP_IPV6 | 1673d89baa5aSAlexander Motin CSUM_UDP_IPV6 | CSUM_SCTP_IPV6); 167475ee267cSGleb Smirnoff } 1675d89baa5aSAlexander Motin 16769b76d9cbSPyun YongHyeon /* 16779b76d9cbSPyun YongHyeon * If the parent interface can do TSO on VLANs then 16789b76d9cbSPyun YongHyeon * propagate the hardware-assisted flag. TSO on VLANs 16799b76d9cbSPyun YongHyeon * does not necessarily require hardware VLAN tagging. 16809b76d9cbSPyun YongHyeon */ 16819fd573c3SHans Petter Selasky memset(&hw_tsomax, 0, sizeof(hw_tsomax)); 16829fd573c3SHans Petter Selasky if_hw_tsomax_common(p, &hw_tsomax); 16839fd573c3SHans Petter Selasky if_hw_tsomax_update(ifp, &hw_tsomax); 16849b76d9cbSPyun YongHyeon if (p->if_capabilities & IFCAP_VLAN_HWTSO) 1685d89baa5aSAlexander Motin cap |= p->if_capabilities & IFCAP_TSO; 16869b76d9cbSPyun YongHyeon if (p->if_capenable & IFCAP_VLAN_HWTSO) { 1687d89baa5aSAlexander Motin ena |= mena & IFCAP_TSO; 1688d89baa5aSAlexander Motin if (ena & IFCAP_TSO) 1689d89baa5aSAlexander Motin hwa |= p->if_hwassist & CSUM_TSO; 16909b76d9cbSPyun YongHyeon } 169109fe6320SNavdeep Parhar 169209fe6320SNavdeep Parhar /* 169359150e91SAlexander Motin * If the parent interface can do LRO and checksum offloading on 169459150e91SAlexander Motin * VLANs, then guess it may do LRO on VLANs. False positive here 169559150e91SAlexander Motin * cost nothing, while false negative may lead to some confusions. 169659150e91SAlexander Motin */ 169759150e91SAlexander Motin if (p->if_capabilities & IFCAP_VLAN_HWCSUM) 169859150e91SAlexander Motin cap |= p->if_capabilities & IFCAP_LRO; 169959150e91SAlexander Motin if (p->if_capenable & IFCAP_VLAN_HWCSUM) 170059150e91SAlexander Motin ena |= p->if_capenable & IFCAP_LRO; 170159150e91SAlexander Motin 170259150e91SAlexander Motin /* 170309fe6320SNavdeep Parhar * If the parent interface can offload TCP connections over VLANs then 170409fe6320SNavdeep Parhar * propagate its TOE capability to the VLAN interface. 170509fe6320SNavdeep Parhar * 170609fe6320SNavdeep Parhar * All TOE drivers in the tree today can deal with VLANs. If this 170709fe6320SNavdeep Parhar * changes then IFCAP_VLAN_TOE should be promoted to a full capability 170809fe6320SNavdeep Parhar * with its own bit. 170909fe6320SNavdeep Parhar */ 171009fe6320SNavdeep Parhar #define IFCAP_VLAN_TOE IFCAP_TOE 171109fe6320SNavdeep Parhar if (p->if_capabilities & IFCAP_VLAN_TOE) 1712d89baa5aSAlexander Motin cap |= p->if_capabilities & IFCAP_TOE; 171309fe6320SNavdeep Parhar if (p->if_capenable & IFCAP_VLAN_TOE) { 171409fe6320SNavdeep Parhar TOEDEV(ifp) = TOEDEV(p); 1715d89baa5aSAlexander Motin ena |= mena & IFCAP_TOE; 171609fe6320SNavdeep Parhar } 1717f3e7afe2SHans Petter Selasky 1718d89baa5aSAlexander Motin /* 1719d89baa5aSAlexander Motin * If the parent interface supports dynamic link state, so does the 1720d89baa5aSAlexander Motin * VLAN interface. 1721d89baa5aSAlexander Motin */ 1722d89baa5aSAlexander Motin cap |= (p->if_capabilities & IFCAP_LINKSTATE); 1723d89baa5aSAlexander Motin ena |= (mena & IFCAP_LINKSTATE); 1724d89baa5aSAlexander Motin 1725f3e7afe2SHans Petter Selasky #ifdef RATELIMIT 1726f3e7afe2SHans Petter Selasky /* 1727f3e7afe2SHans Petter Selasky * If the parent interface supports ratelimiting, so does the 1728f3e7afe2SHans Petter Selasky * VLAN interface. 1729f3e7afe2SHans Petter Selasky */ 1730d89baa5aSAlexander Motin cap |= (p->if_capabilities & IFCAP_TXRTLMT); 1731d89baa5aSAlexander Motin ena |= (mena & IFCAP_TXRTLMT); 1732f3e7afe2SHans Petter Selasky #endif 1733d89baa5aSAlexander Motin 1734d89baa5aSAlexander Motin ifp->if_capabilities = cap; 1735d89baa5aSAlexander Motin ifp->if_capenable = ena; 1736d89baa5aSAlexander Motin ifp->if_hwassist = hwa; 173775ee267cSGleb Smirnoff } 173875ee267cSGleb Smirnoff 173975ee267cSGleb Smirnoff static void 174075ee267cSGleb Smirnoff vlan_trunk_capabilities(struct ifnet *ifp) 174175ee267cSGleb Smirnoff { 1742a68cc388SGleb Smirnoff struct epoch_tracker et; 1743d148c2a2SMatt Joras struct ifvlantrunk *trunk; 174475ee267cSGleb Smirnoff struct ifvlan *ifv; 174575ee267cSGleb Smirnoff 1746d148c2a2SMatt Joras VLAN_SLOCK(); 1747d148c2a2SMatt Joras trunk = ifp->if_vlantrunk; 1748d148c2a2SMatt Joras if (trunk == NULL) { 1749d148c2a2SMatt Joras VLAN_SUNLOCK(); 1750d148c2a2SMatt Joras return; 1751d148c2a2SMatt Joras } 1752a68cc388SGleb Smirnoff NET_EPOCH_ENTER(et); 1753d148c2a2SMatt Joras VLAN_FOREACH(ifv, trunk) { 175475ee267cSGleb Smirnoff vlan_capabilities(ifv); 175575ee267cSGleb Smirnoff } 1756a68cc388SGleb Smirnoff NET_EPOCH_EXIT(et); 1757d148c2a2SMatt Joras VLAN_SUNLOCK(); 1758127d7b2dSAndre Oppermann } 1759127d7b2dSAndre Oppermann 1760a3814acfSSam Leffler static int 1761cfe8b629SGarrett Wollman vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 17622cc2df49SGarrett Wollman { 17632cc2df49SGarrett Wollman struct ifnet *p; 17642cc2df49SGarrett Wollman struct ifreq *ifr; 1765e4cd31ddSJeff Roberson struct ifaddr *ifa; 17662cc2df49SGarrett Wollman struct ifvlan *ifv; 17672d222cb7SAlexander Motin struct ifvlantrunk *trunk; 17682cc2df49SGarrett Wollman struct vlanreq vlr; 17692cc2df49SGarrett Wollman int error = 0; 17702cc2df49SGarrett Wollman 17712cc2df49SGarrett Wollman ifr = (struct ifreq *)data; 1772e4cd31ddSJeff Roberson ifa = (struct ifaddr *) data; 17732cc2df49SGarrett Wollman ifv = ifp->if_softc; 17742cc2df49SGarrett Wollman 17752cc2df49SGarrett Wollman switch (cmd) { 1776e4cd31ddSJeff Roberson case SIOCSIFADDR: 1777e4cd31ddSJeff Roberson ifp->if_flags |= IFF_UP; 1778e4cd31ddSJeff Roberson #ifdef INET 1779e4cd31ddSJeff Roberson if (ifa->ifa_addr->sa_family == AF_INET) 1780e4cd31ddSJeff Roberson arp_ifinit(ifp, ifa); 1781e4cd31ddSJeff Roberson #endif 1782e4cd31ddSJeff Roberson break; 1783e4cd31ddSJeff Roberson case SIOCGIFADDR: 178438d958a6SBrooks Davis bcopy(IF_LLADDR(ifp), &ifr->ifr_addr.sa_data[0], 178538d958a6SBrooks Davis ifp->if_addrlen); 1786e4cd31ddSJeff Roberson break; 1787b3cca108SBill Fenner case SIOCGIFMEDIA: 1788d148c2a2SMatt Joras VLAN_SLOCK(); 178975ee267cSGleb Smirnoff if (TRUNK(ifv) != NULL) { 1790d8564efdSEd Maste p = PARENT(ifv); 17919bcf3ae4SAlexander Motin if_ref(p); 1792d8564efdSEd Maste error = (*p->if_ioctl)(p, SIOCGIFMEDIA, data); 17939bcf3ae4SAlexander Motin if_rele(p); 1794b3cca108SBill Fenner /* Limit the result to the parent's current config. */ 1795b3cca108SBill Fenner if (error == 0) { 1796b3cca108SBill Fenner struct ifmediareq *ifmr; 1797b3cca108SBill Fenner 1798b3cca108SBill Fenner ifmr = (struct ifmediareq *)data; 1799b3cca108SBill Fenner if (ifmr->ifm_count >= 1 && ifmr->ifm_ulist) { 1800b3cca108SBill Fenner ifmr->ifm_count = 1; 1801b3cca108SBill Fenner error = copyout(&ifmr->ifm_current, 1802b3cca108SBill Fenner ifmr->ifm_ulist, 1803b3cca108SBill Fenner sizeof(int)); 1804b3cca108SBill Fenner } 1805b3cca108SBill Fenner } 18064faedfe8SSam Leffler } else { 1807b3cca108SBill Fenner error = EINVAL; 18084faedfe8SSam Leffler } 1809d148c2a2SMatt Joras VLAN_SUNLOCK(); 1810b3cca108SBill Fenner break; 1811b3cca108SBill Fenner 1812b3cca108SBill Fenner case SIOCSIFMEDIA: 1813b3cca108SBill Fenner error = EINVAL; 1814b3cca108SBill Fenner break; 1815b3cca108SBill Fenner 18162cc2df49SGarrett Wollman case SIOCSIFMTU: 18172cc2df49SGarrett Wollman /* 18182cc2df49SGarrett Wollman * Set the interface MTU. 18192cc2df49SGarrett Wollman */ 1820d148c2a2SMatt Joras VLAN_SLOCK(); 1821d148c2a2SMatt Joras trunk = TRUNK(ifv); 1822d148c2a2SMatt Joras if (trunk != NULL) { 1823d148c2a2SMatt Joras TRUNK_WLOCK(trunk); 1824a3814acfSSam Leffler if (ifr->ifr_mtu > 182575ee267cSGleb Smirnoff (PARENT(ifv)->if_mtu - ifv->ifv_mtufudge) || 1826a3814acfSSam Leffler ifr->ifr_mtu < 1827a3814acfSSam Leffler (ifv->ifv_mintu - ifv->ifv_mtufudge)) 18282cc2df49SGarrett Wollman error = EINVAL; 1829a3814acfSSam Leffler else 18302cc2df49SGarrett Wollman ifp->if_mtu = ifr->ifr_mtu; 1831d148c2a2SMatt Joras TRUNK_WUNLOCK(trunk); 1832a3814acfSSam Leffler } else 1833a3814acfSSam Leffler error = EINVAL; 1834d148c2a2SMatt Joras VLAN_SUNLOCK(); 18352cc2df49SGarrett Wollman break; 18362cc2df49SGarrett Wollman 18372cc2df49SGarrett Wollman case SIOCSETVLAN: 1838ccf7ba97SMarko Zec #ifdef VIMAGE 183915f6780eSRobert Watson /* 184015f6780eSRobert Watson * XXXRW/XXXBZ: The goal in these checks is to allow a VLAN 184115f6780eSRobert Watson * interface to be delegated to a jail without allowing the 184215f6780eSRobert Watson * jail to change what underlying interface/VID it is 184315f6780eSRobert Watson * associated with. We are not entirely convinced that this 18445a39f779SRobert Watson * is the right way to accomplish that policy goal. 184515f6780eSRobert Watson */ 1846ccf7ba97SMarko Zec if (ifp->if_vnet != ifp->if_home_vnet) { 1847ccf7ba97SMarko Zec error = EPERM; 1848ccf7ba97SMarko Zec break; 1849ccf7ba97SMarko Zec } 1850ccf7ba97SMarko Zec #endif 1851541d96aaSBrooks Davis error = copyin(ifr_data_get_ptr(ifr), &vlr, sizeof(vlr)); 18522cc2df49SGarrett Wollman if (error) 18532cc2df49SGarrett Wollman break; 18542cc2df49SGarrett Wollman if (vlr.vlr_parent[0] == '\0') { 1855f731f104SBill Paul vlan_unconfig(ifp); 18562cc2df49SGarrett Wollman break; 18572cc2df49SGarrett Wollman } 18589bcf3ae4SAlexander Motin p = ifunit_ref(vlr.vlr_parent); 18591bdc73d3SEd Maste if (p == NULL) { 18602cc2df49SGarrett Wollman error = ENOENT; 18612cc2df49SGarrett Wollman break; 18622cc2df49SGarrett Wollman } 186375ee267cSGleb Smirnoff error = vlan_config(ifv, p, vlr.vlr_tag); 18649bcf3ae4SAlexander Motin if_rele(p); 18652cc2df49SGarrett Wollman break; 18662cc2df49SGarrett Wollman 18672cc2df49SGarrett Wollman case SIOCGETVLAN: 1868ccf7ba97SMarko Zec #ifdef VIMAGE 1869ccf7ba97SMarko Zec if (ifp->if_vnet != ifp->if_home_vnet) { 1870ccf7ba97SMarko Zec error = EPERM; 1871ccf7ba97SMarko Zec break; 1872ccf7ba97SMarko Zec } 1873ccf7ba97SMarko Zec #endif 187415a66c21SBruce M Simpson bzero(&vlr, sizeof(vlr)); 1875d148c2a2SMatt Joras VLAN_SLOCK(); 187675ee267cSGleb Smirnoff if (TRUNK(ifv) != NULL) { 187775ee267cSGleb Smirnoff strlcpy(vlr.vlr_parent, PARENT(ifv)->if_xname, 18789bf40edeSBrooks Davis sizeof(vlr.vlr_parent)); 18797983103aSRobert Watson vlr.vlr_tag = ifv->ifv_vid; 18802cc2df49SGarrett Wollman } 1881d148c2a2SMatt Joras VLAN_SUNLOCK(); 1882541d96aaSBrooks Davis error = copyout(&vlr, ifr_data_get_ptr(ifr), sizeof(vlr)); 18832cc2df49SGarrett Wollman break; 18842cc2df49SGarrett Wollman 18852cc2df49SGarrett Wollman case SIOCSIFFLAGS: 18862cc2df49SGarrett Wollman /* 18871cf236fbSYaroslav Tykhiy * We should propagate selected flags to the parent, 18881cf236fbSYaroslav Tykhiy * e.g., promiscuous mode. 18892cc2df49SGarrett Wollman */ 1890d148c2a2SMatt Joras VLAN_XLOCK(); 189175ee267cSGleb Smirnoff if (TRUNK(ifv) != NULL) 18921cf236fbSYaroslav Tykhiy error = vlan_setflags(ifp, 1); 1893d148c2a2SMatt Joras VLAN_XUNLOCK(); 18942cc2df49SGarrett Wollman break; 1895a3814acfSSam Leffler 1896f731f104SBill Paul case SIOCADDMULTI: 1897f731f104SBill Paul case SIOCDELMULTI: 189875ee267cSGleb Smirnoff /* 189975ee267cSGleb Smirnoff * If we don't have a parent, just remember the membership for 190075ee267cSGleb Smirnoff * when we do. 1901d148c2a2SMatt Joras * 1902d148c2a2SMatt Joras * XXX We need the rmlock here to avoid sleeping while 1903d148c2a2SMatt Joras * holding in6_multi_mtx. 190475ee267cSGleb Smirnoff */ 1905b08d611dSMatt Macy VLAN_XLOCK(); 19062d222cb7SAlexander Motin trunk = TRUNK(ifv); 1907b08d611dSMatt Macy if (trunk != NULL) 1908f731f104SBill Paul error = vlan_setmulti(ifp); 1909b08d611dSMatt Macy VLAN_XUNLOCK(); 191075ee267cSGleb Smirnoff 1911b08d611dSMatt Macy break; 19122ccbbd06SMarcelo Araujo case SIOCGVLANPCP: 19132ccbbd06SMarcelo Araujo #ifdef VIMAGE 19142ccbbd06SMarcelo Araujo if (ifp->if_vnet != ifp->if_home_vnet) { 19152ccbbd06SMarcelo Araujo error = EPERM; 19162ccbbd06SMarcelo Araujo break; 19172ccbbd06SMarcelo Araujo } 19182ccbbd06SMarcelo Araujo #endif 19192ccbbd06SMarcelo Araujo ifr->ifr_vlan_pcp = ifv->ifv_pcp; 19202ccbbd06SMarcelo Araujo break; 19212ccbbd06SMarcelo Araujo 19222ccbbd06SMarcelo Araujo case SIOCSVLANPCP: 19232ccbbd06SMarcelo Araujo #ifdef VIMAGE 19242ccbbd06SMarcelo Araujo if (ifp->if_vnet != ifp->if_home_vnet) { 19252ccbbd06SMarcelo Araujo error = EPERM; 19262ccbbd06SMarcelo Araujo break; 19272ccbbd06SMarcelo Araujo } 19282ccbbd06SMarcelo Araujo #endif 19292ccbbd06SMarcelo Araujo error = priv_check(curthread, PRIV_NET_SETVLANPCP); 19302ccbbd06SMarcelo Araujo if (error) 19312ccbbd06SMarcelo Araujo break; 19322ccbbd06SMarcelo Araujo if (ifr->ifr_vlan_pcp > 7) { 19332ccbbd06SMarcelo Araujo error = EINVAL; 19342ccbbd06SMarcelo Araujo break; 19352ccbbd06SMarcelo Araujo } 19362ccbbd06SMarcelo Araujo ifv->ifv_pcp = ifr->ifr_vlan_pcp; 193732a52e9eSNavdeep Parhar ifp->if_pcp = ifv->ifv_pcp; 19382ccbbd06SMarcelo Araujo vlan_tag_recalculate(ifv); 19394a381a9eSHans Petter Selasky /* broadcast event about PCP change */ 19404a381a9eSHans Petter Selasky EVENTHANDLER_INVOKE(ifnet_event, ifp, IFNET_EVENT_PCP); 19412ccbbd06SMarcelo Araujo break; 19422ccbbd06SMarcelo Araujo 1943d89baa5aSAlexander Motin case SIOCSIFCAP: 1944d148c2a2SMatt Joras VLAN_SLOCK(); 1945d89baa5aSAlexander Motin ifv->ifv_capenable = ifr->ifr_reqcap; 1946d89baa5aSAlexander Motin trunk = TRUNK(ifv); 1947d89baa5aSAlexander Motin if (trunk != NULL) { 1948a68cc388SGleb Smirnoff struct epoch_tracker et; 1949a68cc388SGleb Smirnoff 1950a68cc388SGleb Smirnoff NET_EPOCH_ENTER(et); 1951d89baa5aSAlexander Motin vlan_capabilities(ifv); 1952a68cc388SGleb Smirnoff NET_EPOCH_EXIT(et); 1953d89baa5aSAlexander Motin } 1954d148c2a2SMatt Joras VLAN_SUNLOCK(); 1955d89baa5aSAlexander Motin break; 1956d89baa5aSAlexander Motin 19572cc2df49SGarrett Wollman default: 1958e4cd31ddSJeff Roberson error = EINVAL; 1959e4cd31ddSJeff Roberson break; 19602cc2df49SGarrett Wollman } 196115a66c21SBruce M Simpson 196215a66c21SBruce M Simpson return (error); 19632cc2df49SGarrett Wollman } 1964f3e7afe2SHans Petter Selasky 1965f3e7afe2SHans Petter Selasky #ifdef RATELIMIT 1966f3e7afe2SHans Petter Selasky static int 1967f3e7afe2SHans Petter Selasky vlan_snd_tag_alloc(struct ifnet *ifp, 1968f3e7afe2SHans Petter Selasky union if_snd_tag_alloc_params *params, 1969f3e7afe2SHans Petter Selasky struct m_snd_tag **ppmt) 1970f3e7afe2SHans Petter Selasky { 1971*fb3bc596SJohn Baldwin struct epoch_tracker et; 1972*fb3bc596SJohn Baldwin struct vlan_snd_tag *vst; 1973*fb3bc596SJohn Baldwin struct ifvlan *ifv; 1974*fb3bc596SJohn Baldwin struct ifnet *parent; 1975*fb3bc596SJohn Baldwin int error; 1976f3e7afe2SHans Petter Selasky 1977*fb3bc596SJohn Baldwin NET_EPOCH_ENTER(et); 1978*fb3bc596SJohn Baldwin ifv = ifp->if_softc; 1979*fb3bc596SJohn Baldwin if (ifv->ifv_trunk != NULL) 1980*fb3bc596SJohn Baldwin parent = PARENT(ifv); 1981*fb3bc596SJohn Baldwin else 1982*fb3bc596SJohn Baldwin parent = NULL; 1983*fb3bc596SJohn Baldwin if (parent == NULL || parent->if_snd_tag_alloc == NULL) { 1984*fb3bc596SJohn Baldwin NET_EPOCH_EXIT(et); 1985f3e7afe2SHans Petter Selasky return (EOPNOTSUPP); 1986*fb3bc596SJohn Baldwin } 1987*fb3bc596SJohn Baldwin if_ref(parent); 1988*fb3bc596SJohn Baldwin NET_EPOCH_EXIT(et); 1989*fb3bc596SJohn Baldwin 1990*fb3bc596SJohn Baldwin vst = malloc(sizeof(*vst), M_VLAN, M_NOWAIT); 1991*fb3bc596SJohn Baldwin if (vst == NULL) { 1992*fb3bc596SJohn Baldwin if_rele(parent); 1993*fb3bc596SJohn Baldwin return (ENOMEM); 1994*fb3bc596SJohn Baldwin } 1995*fb3bc596SJohn Baldwin 1996*fb3bc596SJohn Baldwin error = parent->if_snd_tag_alloc(parent, params, &vst->tag); 1997*fb3bc596SJohn Baldwin if_rele(parent); 1998*fb3bc596SJohn Baldwin if (error) { 1999*fb3bc596SJohn Baldwin free(vst, M_VLAN); 2000*fb3bc596SJohn Baldwin return (error); 2001*fb3bc596SJohn Baldwin } 2002*fb3bc596SJohn Baldwin 2003*fb3bc596SJohn Baldwin m_snd_tag_init(&vst->com, ifp); 2004*fb3bc596SJohn Baldwin 2005*fb3bc596SJohn Baldwin *ppmt = &vst->com; 2006*fb3bc596SJohn Baldwin return (0); 2007*fb3bc596SJohn Baldwin } 2008*fb3bc596SJohn Baldwin 2009*fb3bc596SJohn Baldwin static int 2010*fb3bc596SJohn Baldwin vlan_snd_tag_modify(struct m_snd_tag *mst, 2011*fb3bc596SJohn Baldwin union if_snd_tag_modify_params *params) 2012*fb3bc596SJohn Baldwin { 2013*fb3bc596SJohn Baldwin struct vlan_snd_tag *vst; 2014*fb3bc596SJohn Baldwin 2015*fb3bc596SJohn Baldwin vst = mst_to_vst(mst); 2016*fb3bc596SJohn Baldwin return (vst->tag->ifp->if_snd_tag_modify(vst->tag, params)); 2017*fb3bc596SJohn Baldwin } 2018*fb3bc596SJohn Baldwin 2019*fb3bc596SJohn Baldwin static int 2020*fb3bc596SJohn Baldwin vlan_snd_tag_query(struct m_snd_tag *mst, 2021*fb3bc596SJohn Baldwin union if_snd_tag_query_params *params) 2022*fb3bc596SJohn Baldwin { 2023*fb3bc596SJohn Baldwin struct vlan_snd_tag *vst; 2024*fb3bc596SJohn Baldwin 2025*fb3bc596SJohn Baldwin vst = mst_to_vst(mst); 2026*fb3bc596SJohn Baldwin return (vst->tag->ifp->if_snd_tag_query(vst->tag, params)); 2027f3e7afe2SHans Petter Selasky } 2028fa91f845SRandall Stewart 2029fa91f845SRandall Stewart static void 2030*fb3bc596SJohn Baldwin vlan_snd_tag_free(struct m_snd_tag *mst) 2031fa91f845SRandall Stewart { 2032*fb3bc596SJohn Baldwin struct vlan_snd_tag *vst; 2033*fb3bc596SJohn Baldwin 2034*fb3bc596SJohn Baldwin vst = mst_to_vst(mst); 2035*fb3bc596SJohn Baldwin m_snd_tag_rele(vst->tag); 2036*fb3bc596SJohn Baldwin free(vst, M_VLAN); 2037fa91f845SRandall Stewart } 2038f3e7afe2SHans Petter Selasky #endif 2039