X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/ff6e181ae92fc6f1e89841290f461d1f2f9badd9..eee3565979933af707c711411001ba11fe406a3c:/bsd/net/if_vlan.c diff --git a/bsd/net/if_vlan.c b/bsd/net/if_vlan.c index 8a5750c55..2dabd32b2 100644 --- a/bsd/net/if_vlan.c +++ b/bsd/net/if_vlan.c @@ -1,14 +1,19 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2003-2016 Apple Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER @@ -18,7 +23,7 @@ * Please see the License for the specific language governing rights and * limitations under the License. * - * @APPLE_LICENSE_HEADER_END@ + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ /* * Copyright 1998 Massachusetts Institute of Technology @@ -74,6 +79,7 @@ #include #include #include +#include #include #include @@ -87,6 +93,9 @@ #include +#include +#include + #include #ifdef INET @@ -96,8 +105,7 @@ #include #include - -#define IF_MAXUNIT 0x7fff /* historical value */ +#include #define VLANNAME "vlan" @@ -114,7 +122,6 @@ my_lck_grp_alloc_init(const char * grp_name) lck_grp_attr_t * grp_attrs; grp_attrs = lck_grp_attr_alloc_init(); - lck_grp_attr_setdefault(grp_attrs); grp = lck_grp_alloc_init(grp_name, grp_attrs); lck_grp_attr_free(grp_attrs); return (grp); @@ -127,7 +134,6 @@ my_lck_mtx_alloc_init(lck_grp_t * lck_grp) lck_mtx_t * lck_mtx; lck_attrs = lck_attr_alloc_init(); - lck_attr_setdefault(lck_attrs); lck_mtx = lck_mtx_alloc_init(lck_grp, lck_attrs); lck_attr_free(lck_attrs); return (lck_mtx); @@ -180,20 +186,30 @@ LIST_HEAD(vlan_parent_list, vlan_parent); struct ifvlan; LIST_HEAD(ifvlan_list, ifvlan); +typedef LIST_ENTRY(vlan_parent) +vlan_parent_entry; +typedef LIST_ENTRY(ifvlan) +ifvlan_entry; + +#define VLP_SIGNATURE 0xfaceface typedef struct vlan_parent { - LIST_ENTRY(vlan_parent) vlp_parent_list;/* list of parents */ + vlan_parent_entry vlp_parent_list;/* list of parents */ struct ifnet * vlp_ifp; /* interface */ struct ifvlan_list vlp_vlan_list; /* list of VLAN's */ -#define VLPF_SUPPORTS_VLAN_MTU 0x1 -#define VLPF_CHANGE_IN_PROGRESS 0x2 -#define VLPF_DETACHING 0x4 +#define VLPF_SUPPORTS_VLAN_MTU 0x00000001 +#define VLPF_CHANGE_IN_PROGRESS 0x00000002 +#define VLPF_DETACHING 0x00000004 +#define VLPF_LINK_EVENT_REQUIRED 0x00000008 u_int32_t vlp_flags; + u_int32_t vlp_event_code; struct ifdevmtu vlp_devmtu; - UInt32 vlp_retain_count; + int32_t vlp_retain_count; + u_int32_t vlp_signature; /* VLP_SIGNATURE */ } vlan_parent, * vlan_parent_ref; +#define IFV_SIGNATURE 0xbeefbeef struct ifvlan { - LIST_ENTRY(ifvlan) ifv_vlan_list; + ifvlan_entry ifv_vlan_list; char ifv_name[IFNAMSIZ]; /* our unique id */ struct ifnet * ifv_ifp; /* our interface */ vlan_parent_ref ifv_vlp; /* parent information */ @@ -210,6 +226,8 @@ struct ifvlan { u_int32_t ifv_flags; bpf_packet_func ifv_bpf_input; bpf_packet_func ifv_bpf_output; + int32_t ifv_retain_count; + u_int32_t ifv_signature; /* IFV_SIGNATURE */ }; typedef struct ifvlan * ifvlan_ref; @@ -225,6 +243,11 @@ static vlan_globals_ref g_vlan; #define ifv_encaplen ifv_mib.ifvm_encaplen #define ifv_mtufudge ifv_mib.ifvm_mtufudge +static void +vlan_parent_retain(vlan_parent_ref vlp); + +static void +vlan_parent_release(vlan_parent_ref vlp); /** ** vlan_parent_ref vlp_flags in-lines @@ -242,13 +265,6 @@ vlan_parent_flags_set_supports_vlan_mtu(vlan_parent_ref vlp) return; } -static __inline__ void -vlan_parent_flags_clear_supports_vlan_mtu(vlan_parent_ref vlp) -{ - vlp->vlp_flags &= ~VLPF_SUPPORTS_VLAN_MTU; - return; -} - static __inline__ int vlan_parent_flags_change_in_progress(vlan_parent_ref vlp) { @@ -282,6 +298,26 @@ vlan_parent_flags_set_detaching(struct vlan_parent * vlp) return; } +static __inline__ int +vlan_parent_flags_link_event_required(vlan_parent_ref vlp) +{ + return ((vlp->vlp_flags & VLPF_LINK_EVENT_REQUIRED) != 0); +} + +static __inline__ void +vlan_parent_flags_set_link_event_required(vlan_parent_ref vlp) +{ + vlp->vlp_flags |= VLPF_LINK_EVENT_REQUIRED; + return; +} + +static __inline__ void +vlan_parent_flags_clear_link_event_required(vlan_parent_ref vlp) +{ + vlp->vlp_flags &= ~VLPF_LINK_EVENT_REQUIRED; + return; +} + /** ** ifvlan_flags in-lines routines @@ -319,13 +355,6 @@ ifvlan_flags_set_ready(ifvlan_ref ifv) return; } -static __inline__ void -ifvlan_flags_clear_ready(ifvlan_ref ifv) -{ - ifv->ifv_flags &= ~IFVF_READY; - return; -} - static __inline__ int ifvlan_flags_detaching(ifvlan_ref ifv) { @@ -341,39 +370,146 @@ ifvlan_flags_set_detaching(ifvlan_ref ifv) #if 0 SYSCTL_DECL(_net_link); -SYSCTL_NODE(_net_link, IFT_L2VLAN, vlan, CTLFLAG_RW, 0, "IEEE 802.1Q VLAN"); -SYSCTL_NODE(_net_link_vlan, PF_LINK, link, CTLFLAG_RW, 0, "for consistency"); -#endif 0 +SYSCTL_NODE(_net_link, IFT_L2VLAN, vlan, CTLFLAG_RW|CTLFLAG_LOCKED, 0, "IEEE 802.1Q VLAN"); +SYSCTL_NODE(_net_link_vlan, PF_LINK, link, CTLFLAG_RW|CTLFLAG_LOCKED, 0, "for consistency"); +#endif #define M_VLAN M_DEVBUF -static int vlan_clone_create(struct if_clone *, int); -static void vlan_clone_destroy(struct ifnet *); -static int vlan_input(struct mbuf *m, char *frame_header, struct ifnet *ifp, - u_long protocol_family, int sync_ok); +static int vlan_clone_create(struct if_clone *, u_int32_t, void *); +static int vlan_clone_destroy(struct ifnet *); +static int vlan_input(ifnet_t ifp, protocol_family_t protocol, + mbuf_t m, char *frame_header); static int vlan_output(struct ifnet *ifp, struct mbuf *m); -static int vlan_ioctl(ifnet_t ifp, u_int32_t cmd, void * addr); +static int vlan_ioctl(ifnet_t ifp, u_long cmd, void * addr); static int vlan_set_bpf_tap(ifnet_t ifp, bpf_tap_mode mode, bpf_packet_func func); static int vlan_attach_protocol(struct ifnet *ifp); static int vlan_detach_protocol(struct ifnet *ifp); static int vlan_setmulti(struct ifnet *ifp); -static int vlan_unconfig(struct ifnet *ifp); +static int vlan_unconfig(ifvlan_ref ifv, int need_to_wait); static int vlan_config(struct ifnet * ifp, struct ifnet * p, int tag); static void vlan_if_free(struct ifnet * ifp); -static void vlan_remove(ifvlan_ref ifv); -static void vlan_if_detach(struct ifnet * ifp); -static int vlan_new_mtu(struct ifnet * ifp, int mtu); +static int vlan_remove(ifvlan_ref ifv, int need_to_wait); static struct if_clone vlan_cloner = IF_CLONE_INITIALIZER(VLANNAME, vlan_clone_create, vlan_clone_destroy, 0, IF_MAXUNIT); -static void interface_link_event(struct ifnet * ifp, u_long event_code); -static void vlan_parent_link_event(vlan_parent_ref vlp, - u_long event_code); -extern int dlil_input_packet(struct ifnet *ifp, struct mbuf *m, char *frame_header); +static void interface_link_event(struct ifnet * ifp, u_int32_t event_code); +static void vlan_parent_link_event(struct ifnet * p, + u_int32_t event_code); + +static int ifvlan_new_mtu(ifvlan_ref ifv, int mtu); + +/** + ** ifvlan_ref routines + **/ +static void +ifvlan_retain(ifvlan_ref ifv) +{ + if (ifv->ifv_signature != IFV_SIGNATURE) { + panic("ifvlan_retain: bad signature\n"); + } + if (ifv->ifv_retain_count == 0) { + panic("ifvlan_retain: retain count is 0\n"); + } + OSIncrementAtomic(&ifv->ifv_retain_count); +} + +static void +ifvlan_release(ifvlan_ref ifv) +{ + u_int32_t old_retain_count; + + if (ifv->ifv_signature != IFV_SIGNATURE) { + panic("ifvlan_release: bad signature\n"); + } + old_retain_count = OSDecrementAtomic(&ifv->ifv_retain_count); + switch (old_retain_count) { + case 0: + panic("ifvlan_release: retain count is 0\n"); + break; + case 1: + if (g_vlan->verbose) { + printf("ifvlan_release(%s)\n", ifv->ifv_name); + } + ifv->ifv_signature = 0; + FREE(ifv, M_VLAN); + break; + default: + break; + } + return; +} + +static vlan_parent_ref +ifvlan_get_vlan_parent_retained(ifvlan_ref ifv) +{ + vlan_parent_ref vlp = ifv->ifv_vlp; + + if (vlp == NULL || vlan_parent_flags_detaching(vlp)) { + return (NULL); + } + vlan_parent_retain(vlp); + return (vlp); +} + +/** + ** ifnet_* routines + **/ + +static ifvlan_ref +ifnet_get_ifvlan(struct ifnet * ifp) +{ + ifvlan_ref ifv; + + ifv = (ifvlan_ref)ifnet_softc(ifp); + return (ifv); +} + +static ifvlan_ref +ifnet_get_ifvlan_retained(struct ifnet * ifp) +{ + ifvlan_ref ifv; + + ifv = ifnet_get_ifvlan(ifp); + if (ifv == NULL) { + return (NULL); + } + if (ifvlan_flags_detaching(ifv)) { + return (NULL); + } + ifvlan_retain(ifv); + return (ifv); +} + +static int +ifnet_ifvlan_vlan_parent_ok(struct ifnet * ifp, ifvlan_ref ifv, + vlan_parent_ref vlp) +{ + ifvlan_ref check_ifv; + + check_ifv = ifnet_get_ifvlan(ifp); + if (check_ifv != ifv || ifvlan_flags_detaching(ifv)) { + /* ifvlan_ref no longer valid */ + return (FALSE); + } + if (ifv->ifv_vlp != vlp) { + /* vlan_parent no longer valid */ + return (FALSE); + } + if (vlan_parent_flags_detaching(vlp)) { + /* parent is detaching */ + return (FALSE); + } + return (TRUE); +} + +/** + ** vlan, etc. routines + **/ static int vlan_globals_init(void) @@ -413,7 +549,7 @@ siocgifdevmtu(struct ifnet * ifp, struct ifdevmtu * ifdm_p) int error; bzero(&ifr, sizeof(ifr)); - error = dlil_ioctl(0, ifp, SIOCGIFDEVMTU, (caddr_t)&ifr); + error = ifnet_ioctl(ifp, 0,SIOCGIFDEVMTU, &ifr); if (error == 0) { *ifdm_p = ifr.ifr_devmtu; } @@ -427,7 +563,7 @@ siocsifaltmtu(struct ifnet * ifp, int mtu) bzero(&ifr, sizeof(ifr)); ifr.ifr_mtu = mtu; - return (dlil_ioctl(0, ifp, SIOCSIFALTMTU, (caddr_t)&ifr)); + return (ifnet_ioctl(ifp, 0, SIOCSIFALTMTU, &ifr)); } static __inline__ void @@ -463,29 +599,29 @@ vlan_bpf_input(struct ifnet * ifp, struct mbuf * m, return; } -static struct ifaddr * -ifaddr_byindex(int i) -{ - if (i > if_index || i == 0) { - return (NULL); - } - return (ifnet_addrs[i - 1]); -} - /** ** vlan_parent synchronization routines **/ -static __inline__ void +static void vlan_parent_retain(vlan_parent_ref vlp) { + if (vlp->vlp_signature != VLP_SIGNATURE) { + panic("vlan_parent_retain: signature is bad\n"); + } + if (vlp->vlp_retain_count == 0) { + panic("vlan_parent_retain: retain count is 0\n"); + } OSIncrementAtomic(&vlp->vlp_retain_count); } -static __inline__ void +static void vlan_parent_release(vlan_parent_ref vlp) { - UInt32 old_retain_count; + u_int32_t old_retain_count; + if (vlp->vlp_signature != VLP_SIGNATURE) { + panic("vlan_parent_release: signature is bad\n"); + } old_retain_count = OSDecrementAtomic(&vlp->vlp_retain_count); switch (old_retain_count) { case 0: @@ -494,9 +630,10 @@ vlan_parent_release(vlan_parent_ref vlp) case 1: if (g_vlan->verbose) { struct ifnet * ifp = vlp->vlp_ifp; - printf("vlan_parent_release(%s%d)\n", ifp->if_name, - ifp->if_unit); + printf("vlan_parent_release(%s%d)\n", ifnet_name(ifp), + ifnet_unit(ifp)); } + vlp->vlp_signature = 0; FREE(vlp, M_VLAN); break; default: @@ -527,7 +664,7 @@ vlan_parent_wait(vlan_parent_ref vlp, const char * msg) if (g_vlan->verbose) { struct ifnet * ifp = vlp->vlp_ifp; - printf("%s%d: %s msleep\n", ifp->if_name, ifp->if_unit, msg); + printf("%s%d: %s msleep\n", ifnet_name(ifp), ifnet_unit(ifp), msg); } waited = 1; (void)msleep(vlp, vlan_lck_mtx, PZERO, msg, 0); @@ -537,7 +674,7 @@ vlan_parent_wait(vlan_parent_ref vlp, const char * msg) if (g_vlan->verbose && waited) { struct ifnet * ifp = vlp->vlp_ifp; - printf("%s: %s woke up\n", ifp->if_name, ifp->if_unit, msg); + printf("%s%d: %s woke up\n", ifnet_name(ifp), ifnet_unit(ifp), msg); } return; } @@ -555,17 +692,38 @@ vlan_parent_wait(vlan_parent_ref vlp, const char * msg) static void vlan_parent_signal(vlan_parent_ref vlp, const char * msg) { + struct ifnet * vlp_ifp = vlp->vlp_ifp; + + if (vlan_parent_flags_link_event_required(vlp)) { + vlan_parent_flags_clear_link_event_required(vlp); + if (!vlan_parent_flags_detaching(vlp)) { + u_int32_t event_code = vlp->vlp_event_code; + ifvlan_ref ifv; + + vlan_unlock(); + + /* we can safely walk the list unlocked */ + LIST_FOREACH(ifv, &vlp->vlp_vlan_list, ifv_vlan_list) { + struct ifnet * ifp = ifv->ifv_ifp; + + interface_link_event(ifp, event_code); + } + if (g_vlan->verbose) { + printf("%s%d: propagated link event to vlans\n", + ifnet_name(vlp_ifp), ifnet_unit(vlp_ifp)); + } + vlan_lock(); + } + } vlan_parent_flags_clear_change_in_progress(vlp); wakeup((caddr_t)vlp); if (g_vlan->verbose) { - struct ifnet * ifp = vlp->vlp_ifp; - - printf("%s%d: %s wakeup\n", ifp->if_name, ifp->if_unit, msg); + printf("%s%d: %s wakeup\n", + ifnet_name(vlp_ifp), ifnet_unit(vlp_ifp), msg); } return; } - /* * Program our multicast filter. What we're actually doing is * programming the multicast filter of the parent. This has the @@ -580,35 +738,22 @@ vlan_setmulti(struct ifnet * ifp) int error = 0; ifvlan_ref ifv; struct ifnet * p; - vlan_parent_ref vlp; + vlan_parent_ref vlp = NULL; vlan_lock(); - ifv = (ifvlan_ref)ifp->if_private; - if (ifv == NULL || ifvlan_flags_detaching(ifv)) { + ifv = ifnet_get_ifvlan_retained(ifp); + if (ifv == NULL) { goto unlock_done; } - vlp = ifv->ifv_vlp; + vlp = ifvlan_get_vlan_parent_retained(ifv); if (vlp == NULL) { /* no parent, no need to program the multicast filter */ goto unlock_done; } - if (vlan_parent_flags_detaching(vlp)) { - goto unlock_done; - } - vlan_parent_retain(vlp); vlan_parent_wait(vlp, "vlan_setmulti"); /* check again, things could have changed */ - ifv = (ifvlan_ref)ifp->if_private; - if (ifv == NULL || ifvlan_flags_detaching(ifv)) { - goto signal_done; - } - if (ifv->ifv_vlp != vlp) { - /* vlan parent changed */ - goto signal_done; - } - if (vlp == NULL) { - /* no parent, no need to program the multicast filter */ + if (ifnet_ifvlan_vlan_parent_ok(ifp, ifv, vlp) == FALSE) { goto signal_done; } p = vlp->vlp_ifp; @@ -624,6 +769,12 @@ vlan_setmulti(struct ifnet * ifp) unlock_done: vlan_unlock(); + if (ifv != NULL) { + ifvlan_release(ifv); + } + if (vlp != NULL) { + vlan_parent_release(vlp); + } return (error); } @@ -680,7 +831,7 @@ vlan_parent_find_max_mtu(vlan_parent_ref vlp, ifvlan_ref exclude_ifv) if (exclude_ifv == ifv) { continue; } - req_mtu = ifv->ifv_ifp->if_mtu + ifv->ifv_mtufudge; + req_mtu = ifnet_mtu(ifv->ifv_ifp) + ifv->ifv_mtufudge; if (req_mtu > max_mtu) { max_mtu = req_mtu; } @@ -701,22 +852,22 @@ vlan_parent_create(struct ifnet * p, vlan_parent_ref * ret_vlp) vlan_parent_ref vlp; *ret_vlp = NULL; - vlp = _MALLOC(sizeof(*vlp), M_VLAN, M_WAITOK); + vlp = _MALLOC(sizeof(*vlp), M_VLAN, M_WAITOK | M_ZERO); if (vlp == NULL) { return (ENOMEM); } - bzero(vlp, sizeof(*vlp)); error = siocgifdevmtu(p, &vlp->vlp_devmtu); if (error != 0) { printf("vlan_parent_create (%s%d): siocgifdevmtu failed, %d\n", - p->if_name, p->if_unit, error); + ifnet_name(p), ifnet_unit(p), error); FREE(vlp, M_VLAN); return (error); } LIST_INIT(&vlp->vlp_vlan_list); vlp->vlp_ifp = p; - vlan_parent_retain(vlp); - if (p->if_hwassist + vlp->vlp_retain_count = 1; + vlp->vlp_signature = VLP_SIGNATURE; + if (ifnet_offload(p) & (IF_HWASSIST_VLAN_MTU | IF_HWASSIST_VLAN_TAGGING)) { vlan_parent_flags_set_supports_vlan_mtu(vlp); } @@ -725,30 +876,57 @@ vlan_parent_create(struct ifnet * p, vlan_parent_ref * ret_vlp) } static void -vlan_parent_remove_all_vlans(vlan_parent_ref vlp) +vlan_parent_remove_all_vlans(struct ifnet * p) { ifvlan_ref ifv; - struct ifnet * p; - - vlan_assert_lock_held(); + int need_vlp_release = 0; + ifvlan_ref next; + vlan_parent_ref vlp; - while ((ifv = LIST_FIRST(&vlp->vlp_vlan_list)) != NULL) { - vlan_remove(ifv); + vlan_lock(); + vlp = parent_list_lookup(p); + if (vlp == NULL || vlan_parent_flags_detaching(vlp)) { + /* no VLAN's */ vlan_unlock(); - vlan_if_detach(ifv->ifv_ifp); - vlan_lock(); + return; + } + vlan_parent_flags_set_detaching(vlp); + vlan_parent_retain(vlp); + vlan_parent_wait(vlp, "vlan_parent_remove_all_vlans"); + need_vlp_release++; + + /* check again */ + if (parent_list_lookup(p) != vlp) { + goto signal_done; + } + + for (ifv = LIST_FIRST(&vlp->vlp_vlan_list); ifv != NULL; ifv = next) { + struct ifnet * ifp = ifv->ifv_ifp; + int removed; + + next = LIST_NEXT(ifv, ifv_vlan_list); + removed = vlan_remove(ifv, FALSE); + if (removed) { + vlan_unlock(); + ifnet_detach(ifp); + vlan_lock(); + } } /* the vlan parent has no more VLAN's */ - p = vlp->vlp_ifp; - ifnet_lock_exclusive(p); - p->if_eflags &= ~IFEF_VLAN; - ifnet_lock_done(p); + ifnet_set_eflags(p, 0, IFEF_VLAN); /* clear IFEF_VLAN */ + LIST_REMOVE(vlp, vlp_parent_list); + need_vlp_release++; /* one for being in the list */ + need_vlp_release++; /* final reference */ + + signal_done: + vlan_parent_signal(vlp, "vlan_parent_remove_all_vlans"); vlan_unlock(); - vlan_parent_release(vlp); - vlan_lock(); + while (need_vlp_release--) { + vlan_parent_release(vlp); + } return; } @@ -775,119 +953,126 @@ vlan_parent_remove_vlan(__unused vlan_parent_ref vlp, ifvlan_ref ifv) return; } -static void +static int vlan_clone_attach(void) { - if_clone_attach(&vlan_cloner); + int error; + + error = if_clone_attach(&vlan_cloner); + if (error != 0) + return error; vlan_lock_init(); - return; + return 0; } static int -vlan_clone_create(struct if_clone *ifc, int unit) +vlan_clone_create(struct if_clone *ifc, u_int32_t unit, __unused void *params) { - int error; - ifvlan_ref ifv; - struct ifnet * ifp; - - error = vlan_globals_init(); - if (error != 0) { - return (error); - } - ifv = _MALLOC(sizeof(struct ifvlan), M_VLAN, M_WAITOK); - bzero(ifv, sizeof(struct ifvlan)); - multicast_list_init(&ifv->ifv_multicast); - - /* use the interface name as the unique id for ifp recycle */ - if ((unsigned int)snprintf(ifv->ifv_name, sizeof(ifv->ifv_name), "%s%d", - ifc->ifc_name, unit) >= sizeof(ifv->ifv_name)) { - FREE(ifv, M_VLAN); - return (EINVAL); - } - error = dlil_if_acquire(APPLE_IF_FAM_VLAN, - ifv->ifv_name, - strlen(ifv->ifv_name), - &ifp); - if (error) { - FREE(ifv, M_VLAN); - return (error); - } - ifp->if_name = ifc->ifc_name; - ifp->if_unit = unit; - ifp->if_family = APPLE_IF_FAM_VLAN; - -#if 0 - /* NB: flags are not set here */ - ifp->if_linkmib = &ifv->ifv_mib; - ifp->if_linkmiblen = sizeof ifv->ifv_mib; - /* NB: mtu is not set here */ -#endif 0 - - ifp->if_ioctl = vlan_ioctl; - ifp->if_set_bpf_tap = vlan_set_bpf_tap; - ifp->if_free = vlan_if_free; - ifp->if_output = vlan_output; - ifp->if_hwassist = 0; - ifp->if_addrlen = ETHER_ADDR_LEN; /* XXX ethernet specific */ - ifp->if_baudrate = 0; - ifp->if_type = IFT_L2VLAN; - ifp->if_hdrlen = ETHER_VLAN_ENCAP_LEN; - - /* XXX ethernet specific */ - ifp->if_broadcast.length = ETHER_ADDR_LEN; - bcopy(etherbroadcastaddr, ifp->if_broadcast.u.buffer, ETHER_ADDR_LEN); - - error = dlil_if_attach(ifp); - if (error) { - dlil_if_release(ifp); - FREE(ifv, M_VLAN); - return (error); - } - ifp->if_private = ifv; - ifv->ifv_ifp = ifp; - - /* attach as ethernet */ - bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); - return (0); + int error; + ifvlan_ref ifv; + ifnet_t ifp; + struct ifnet_init_eparams vlan_init; + + error = vlan_globals_init(); + if (error != 0) { + return (error); + } + ifv = _MALLOC(sizeof(struct ifvlan), M_VLAN, M_WAITOK | M_ZERO); + if (ifv == NULL) + return ENOBUFS; + ifv->ifv_retain_count = 1; + ifv->ifv_signature = IFV_SIGNATURE; + multicast_list_init(&ifv->ifv_multicast); + + /* use the interface name as the unique id for ifp recycle */ + if ((unsigned int) + snprintf(ifv->ifv_name, sizeof(ifv->ifv_name), "%s%d", + ifc->ifc_name, unit) >= sizeof(ifv->ifv_name)) { + ifvlan_release(ifv); + return (EINVAL); + } + + bzero(&vlan_init, sizeof(vlan_init)); + vlan_init.ver = IFNET_INIT_CURRENT_VERSION; + vlan_init.len = sizeof (vlan_init); + vlan_init.flags = IFNET_INIT_LEGACY; + vlan_init.uniqueid = ifv->ifv_name; + vlan_init.uniqueid_len = strlen(ifv->ifv_name); + vlan_init.name = ifc->ifc_name; + vlan_init.unit = unit; + vlan_init.family = IFNET_FAMILY_VLAN; + vlan_init.type = IFT_L2VLAN; + vlan_init.output = vlan_output; + vlan_init.demux = ether_demux; + vlan_init.add_proto = ether_add_proto; + vlan_init.del_proto = ether_del_proto; + vlan_init.check_multi = ether_check_multi; + vlan_init.framer_extended = ether_frameout_extended; + vlan_init.softc = ifv; + vlan_init.ioctl = vlan_ioctl; + vlan_init.set_bpf_tap = vlan_set_bpf_tap; + vlan_init.detach = vlan_if_free; + vlan_init.broadcast_addr = etherbroadcastaddr; + vlan_init.broadcast_len = ETHER_ADDR_LEN; + error = ifnet_allocate_extended(&vlan_init, &ifp); + + if (error) { + ifvlan_release(ifv); + return (error); + } + + ifnet_set_offload(ifp, 0); + ifnet_set_addrlen(ifp, ETHER_ADDR_LEN); /* XXX ethernet specific */ + ifnet_set_baudrate(ifp, 0); + ifnet_set_hdrlen(ifp, ETHER_VLAN_ENCAP_LEN); + + error = ifnet_attach(ifp, NULL); + if (error) { + ifnet_release(ifp); + ifvlan_release(ifv); + return (error); + } + ifv->ifv_ifp = ifp; + + /* attach as ethernet */ + bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); + return (0); } -static void -vlan_remove(ifvlan_ref ifv) +static int +vlan_remove(ifvlan_ref ifv, int need_to_wait) { vlan_assert_lock_held(); + if (ifvlan_flags_detaching(ifv)) { + return (0); + } ifvlan_flags_set_detaching(ifv); - vlan_unconfig(ifv->ifv_ifp); - return; + vlan_unconfig(ifv, need_to_wait); + return (1); } -static void -vlan_if_detach(struct ifnet * ifp) -{ - if (dlil_if_detach(ifp) != DLIL_WAIT_FOR_FREE) { - vlan_if_free(ifp); - } - return; -} -static void +static int vlan_clone_destroy(struct ifnet *ifp) { ifvlan_ref ifv; vlan_lock(); - ifv = ifp->if_private; - if (ifv == NULL || ifp->if_type != IFT_L2VLAN) { + ifv = ifnet_get_ifvlan_retained(ifp); + if (ifv == NULL) { vlan_unlock(); - return; + return 0; } - if (ifvlan_flags_detaching(ifv)) { + if (vlan_remove(ifv, TRUE) == 0) { vlan_unlock(); - return; + ifvlan_release(ifv); + return 0; } - vlan_remove(ifv); vlan_unlock(); - vlan_if_detach(ifp); - return; + ifvlan_release(ifv); + ifnet_detach(ifp); + + return 0; } static int @@ -896,8 +1081,8 @@ vlan_set_bpf_tap(ifnet_t ifp, bpf_tap_mode mode, bpf_packet_func func) ifvlan_ref ifv; vlan_lock(); - ifv = ifp->if_private; - if (ifv == NULL || ifvlan_flags_detaching(ifv)) { + ifv = ifnet_get_ifvlan_retained(ifp); + if (ifv == NULL) { vlan_unlock(); return (ENODEV); } @@ -921,6 +1106,7 @@ vlan_set_bpf_tap(ifnet_t ifp, bpf_tap_mode mode, bpf_packet_func func) break; } vlan_unlock(); + ifvlan_release(ifv); return 0; } @@ -934,7 +1120,9 @@ vlan_output(struct ifnet * ifp, struct mbuf * m) struct ifnet * p; int soft_vlan; u_short tag; - vlan_parent_ref vlp; + vlan_parent_ref vlp = NULL; + int err; + struct flowadv adv = { FADV_SUCCESS }; if (m == 0) { return (0); @@ -944,32 +1132,31 @@ vlan_output(struct ifnet * ifp, struct mbuf * m) return (0); } vlan_lock(); - ifv = (ifvlan_ref)ifp->if_private; - if (ifv == NULL || ifvlan_flags_detaching(ifv) - || ifvlan_flags_ready(ifv) == 0) { - vlan_unlock(); - m_freem_list(m); - return (0); + ifv = ifnet_get_ifvlan_retained(ifp); + if (ifv == NULL || ifvlan_flags_ready(ifv) == 0) { + goto unlock_done; } - vlp = ifv->ifv_vlp; + vlp = ifvlan_get_vlan_parent_retained(ifv); if (vlp == NULL) { - vlan_unlock(); - m_freem_list(m); - return (0); + goto unlock_done; } p = vlp->vlp_ifp; (void)ifnet_stat_increment_out(ifp, 1, m->m_pkthdr.len, 0); - soft_vlan = (p->if_hwassist & IF_HWASSIST_VLAN_TAGGING) == 0; + soft_vlan = (ifnet_offload(p) & IF_HWASSIST_VLAN_TAGGING) == 0; bpf_func = ifv->ifv_bpf_output; tag = ifv->ifv_tag; encaplen = ifv->ifv_encaplen; vlan_unlock(); + + ifvlan_release(ifv); + vlan_parent_release(vlp); + vlan_bpf_output(ifp, m, bpf_func); /* do not run parent's if_output() if the parent is not up */ - if ((p->if_flags & (IFF_UP | IFF_RUNNING)) != (IFF_UP | IFF_RUNNING)) { + if ((ifnet_flags(p) & (IFF_UP | IFF_RUNNING)) != (IFF_UP | IFF_RUNNING)) { m_freem(m); - ifp->if_collisions++; + atomic_add_64(&ifp->if_collisions, 1); return (0); } /* @@ -984,20 +1171,20 @@ vlan_output(struct ifnet * ifp, struct mbuf * m) m->m_pkthdr.csum_flags |= CSUM_VLAN_TAG_VALID; m->m_pkthdr.vlan_tag = tag; } else { - M_PREPEND(m, encaplen, M_DONTWAIT); + M_PREPEND(m, encaplen, M_DONTWAIT, 1); if (m == NULL) { - printf("%s%d: unable to prepend VLAN header\n", ifp->if_name, - ifp->if_unit); - ifp->if_oerrors++; + printf("%s%d: unable to prepend VLAN header\n", ifnet_name(ifp), + ifnet_unit(ifp)); + atomic_add_64(&ifp->if_oerrors, 1); return (0); } /* M_PREPEND takes care of m_len, m_pkthdr.len for us */ if (m->m_len < (int)sizeof(*evl)) { m = m_pullup(m, sizeof(*evl)); if (m == NULL) { - printf("%s%d: unable to pullup VLAN header\n", ifp->if_name, - ifp->if_unit); - ifp->if_oerrors++; + printf("%s%d: unable to pullup VLAN header\n", ifnet_name(ifp), + ifnet_unit(ifp)); + atomic_add_64(&ifp->if_oerrors, 1); return (0); } } @@ -1013,12 +1200,35 @@ vlan_output(struct ifnet * ifp, struct mbuf * m) evl->evl_encap_proto = htons(ETHERTYPE_VLAN); evl->evl_tag = htons(tag); } - return dlil_output(p, 0, m, NULL, NULL, 1); + + err = dlil_output(p, PF_VLAN, m, NULL, NULL, 1, &adv); + + if (err == 0) { + if (adv.code == FADV_FLOW_CONTROLLED) { + err = EQFULL; + } else if (adv.code == FADV_SUSPENDED) { + err = EQSUSPENDED; + } + } + + return (err); + + unlock_done: + vlan_unlock(); + if (ifv != NULL) { + ifvlan_release(ifv); + } + if (vlp != NULL) { + vlan_parent_release(vlp); + } + m_freem_list(m); + return (0); + } static int -vlan_input(struct mbuf * m, char * frame_header, struct ifnet * p, - __unused u_long protocol_family, __unused int sync_ok) +vlan_input(ifnet_t p, __unused protocol_family_t protocol, + mbuf_t m, char *frame_header) { bpf_packet_func bpf_func = NULL; struct ether_vlan_header * evl; @@ -1036,13 +1246,13 @@ vlan_input(struct mbuf * m, char * frame_header, struct ifnet * p, m->m_pkthdr.vlan_tag = 0; } else { soft_vlan = 1; - switch (p->if_type) { + switch (ifnet_type(p)) { case IFT_ETHER: if (m->m_len < ETHER_VLAN_ENCAP_LEN) { m_freem(m); return 0; } - evl = (struct ether_vlan_header *)frame_header; + evl = (struct ether_vlan_header *)(void *)frame_header; if (ntohs(evl->evl_proto) == ETHERTYPE_VLAN) { /* don't allow VLAN within VLAN */ m_freem(m); @@ -1059,16 +1269,15 @@ vlan_input(struct mbuf * m, char * frame_header, struct ifnet * p, break; default: printf("vlan_demux: unsupported if type %u", - p->if_type); + ifnet_type(p)); m_freem(m); return 0; - break; } } if (tag != 0) { ifvlan_ref ifv; - if ((p->if_eflags & IFEF_VLAN) == 0) { + if ((ifnet_eflags(p) & IFEF_VLAN) == 0) { /* don't bother looking through the VLAN list */ m_freem(m); return 0; @@ -1080,7 +1289,7 @@ vlan_input(struct mbuf * m, char * frame_header, struct ifnet * p, } if (ifv == NULL || ifvlan_flags_ready(ifv) == 0 - || (ifp->if_flags & IFF_UP) == 0) { + || (ifnet_flags(ifp) & IFF_UP) == 0) { vlan_unlock(); m_freem(m); return 0; @@ -1101,35 +1310,32 @@ vlan_input(struct mbuf * m, char * frame_header, struct ifnet * p, } if (tag != 0) { m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.pkt_hdr = frame_header; (void)ifnet_stat_increment_in(ifp, 1, m->m_pkthdr.len + ETHER_HDR_LEN, 0); vlan_bpf_input(ifp, m, bpf_func, frame_header, ETHER_HDR_LEN, soft_vlan ? ETHER_VLAN_ENCAP_LEN : 0); /* We found a vlan interface, inject on that interface. */ - dlil_input_packet(ifp, m, frame_header); + dlil_input_packet_list(ifp, m); } else { + m->m_pkthdr.pkt_hdr = frame_header; /* Send priority-tagged packet up through the parent */ - dlil_input_packet(p, m, frame_header); + dlil_input_packet_list(p, m); } return 0; } -#define VLAN_CONFIG_PROGRESS_VLP_RETAINED 0x1 -#define VLAN_CONFIG_PROGRESS_IN_LIST 0x2 - static int vlan_config(struct ifnet * ifp, struct ifnet * p, int tag) { int error; - int first_vlan = 0; + int first_vlan = FALSE; ifvlan_ref ifv = NULL; - struct ifaddr * ifa1; - struct ifaddr * ifa2; - vlan_parent_ref new_vlp = NULL; + int ifv_added = FALSE; int need_vlp_release = 0; - u_int32_t progress = 0; - struct sockaddr_dl *sdl1; - struct sockaddr_dl *sdl2; + vlan_parent_ref new_vlp = NULL; + ifnet_offload_t offload; + u_int16_t parent_flags; vlan_parent_ref vlp = NULL; /* pre-allocate space for vlan_parent, in case we're first */ @@ -1139,14 +1345,19 @@ vlan_config(struct ifnet * ifp, struct ifnet * p, int tag) } vlan_lock(); - ifv = (ifvlan_ref)ifp->if_private; - if (ifv != NULL && ifv->ifv_vlp != NULL) { + ifv = ifnet_get_ifvlan_retained(ifp); + if (ifv == NULL || ifv->ifv_vlp != NULL) { vlan_unlock(); + if (ifv != NULL) { + ifvlan_release(ifv); + } vlan_parent_release(new_vlp); return (EBUSY); } vlp = parent_list_lookup(p); if (vlp != NULL) { + vlan_parent_retain(vlp); + need_vlp_release++; if (vlan_parent_lookup_tag(vlp, tag) != NULL) { /* already a VLAN with that tag on this interface */ error = EADDRINUSE; @@ -1154,28 +1365,38 @@ vlan_config(struct ifnet * ifp, struct ifnet * p, int tag) } } else { + /* one for being in the list */ + vlan_parent_retain(new_vlp); + /* we're the first VLAN on this interface */ LIST_INSERT_HEAD(&g_vlan->parent_list, new_vlp, vlp_parent_list); vlp = new_vlp; + + vlan_parent_retain(vlp); + need_vlp_release++; } /* need to wait to ensure no one else is trying to add/remove */ - vlan_parent_retain(vlp); - progress |= VLAN_CONFIG_PROGRESS_VLP_RETAINED; vlan_parent_wait(vlp, "vlan_config"); - ifv = (ifvlan_ref)ifp->if_private; - if (ifv == NULL) { - error = EOPNOTSUPP; + if (ifnet_get_ifvlan(ifp) != ifv) { + error = EINVAL; + goto signal_done; + } + + /* check again because someone might have gotten in */ + if (parent_list_lookup(p) != vlp) { + error = EBUSY; goto signal_done; } + if (vlan_parent_flags_detaching(vlp) || ifvlan_flags_detaching(ifv) || ifv->ifv_vlp != NULL) { error = EBUSY; goto signal_done; } - /* check again because someone might have gotten in */ + /* check again because someone might have gotten the tag */ if (vlan_parent_lookup_tag(vlp, tag) != NULL) { /* already a VLAN with that tag on this interface */ error = EADDRINUSE; @@ -1183,20 +1404,22 @@ vlan_config(struct ifnet * ifp, struct ifnet * p, int tag) } if (vlan_parent_no_vlans(vlp)) { - first_vlan = 1; + first_vlan = TRUE; } vlan_parent_add_vlan(vlp, ifv, tag); - progress |= VLAN_CONFIG_PROGRESS_IN_LIST; + ifvlan_retain(ifv); /* parent references ifv */ + ifv_added = TRUE; /* check whether bond interface is using parent interface */ ifnet_lock_exclusive(p); - if ((p->if_eflags & IFEF_BOND) != 0) { + if ((ifnet_eflags(p) & IFEF_BOND) != 0) { ifnet_lock_done(p); /* don't allow VLAN over interface that's already part of a bond */ error = EBUSY; goto signal_done; } /* prevent BOND interface from using it */ + /* Can't use ifnet_set_eflags because that would take the lock */ p->if_eflags |= IFEF_VLAN; ifnet_lock_done(p); vlan_unlock(); @@ -1208,11 +1431,6 @@ vlan_config(struct ifnet * ifp, struct ifnet * p, int tag) vlan_lock(); goto signal_done; } - /* mark the parent interface up */ - ifnet_lock_exclusive(p); - p->if_flags |= IFF_UP; - ifnet_lock_done(p); - (void)dlil_ioctl(0, p, SIOCSIFFLAGS, (caddr_t)NULL); } /* configure parent to receive our multicast addresses */ @@ -1225,6 +1443,9 @@ vlan_config(struct ifnet * ifp, struct ifnet * p, int tag) goto signal_done; } + /* set our ethernet address to that of the parent */ + ifnet_set_lladdr_and_type(ifp, IF_LLADDR(p), ETHER_ADDR_LEN, IFT_ETHER); + /* no failures past this point */ vlan_lock(); @@ -1242,33 +1463,22 @@ vlan_config(struct ifnet * ifp, struct ifnet * p, int tag) */ ifv->ifv_mtufudge = ifv->ifv_encaplen; } - ifp->if_mtu = ETHERMTU - ifv->ifv_mtufudge; + ifnet_set_mtu(ifp, ETHERMTU - ifv->ifv_mtufudge); /* * Copy only a selected subset of flags from the parent. * Other flags are none of our business. */ - ifp->if_flags |= (p->if_flags & - (IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX)); - /* - * If the parent interface can do hardware-assisted - * VLAN encapsulation, then propagate its hardware- - * assisted checksumming flags. - */ - if (p->if_hwassist & IF_HWASSIST_VLAN_TAGGING) { - ifp->if_hwassist |= IF_HWASSIST_CSUM_FLAGS(p->if_hwassist); - } + parent_flags = ifnet_flags(p) + & (IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX); + ifnet_set_flags(ifp, parent_flags, + IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX); - /* set our ethernet address to that of the parent */ - ifa1 = ifaddr_byindex(ifp->if_index); - ifa2 = ifaddr_byindex(p->if_index); - sdl1 = (struct sockaddr_dl *)ifa1->ifa_addr; - sdl2 = (struct sockaddr_dl *)ifa2->ifa_addr; - sdl1->sdl_type = IFT_ETHER; - sdl1->sdl_alen = ETHER_ADDR_LEN; - bcopy(LLADDR(sdl2), LLADDR(sdl1), ETHER_ADDR_LEN); - - ifp->if_flags |= IFF_RUNNING; + /* use hwassist bits from parent interface, but exclude VLAN bits */ + offload = ifnet_offload(p) & ~(IFNET_VLAN_TAGGING | IFNET_VLAN_MTU); + ifnet_set_offload(ifp, offload); + + ifnet_set_flags(ifp, IFF_RUNNING, IFF_RUNNING); ifvlan_flags_set_ready(ifv); vlan_parent_signal(vlp, "vlan_config"); vlan_unlock(); @@ -1276,36 +1486,49 @@ vlan_config(struct ifnet * ifp, struct ifnet * p, int tag) /* throw it away, it wasn't needed */ vlan_parent_release(new_vlp); } + if (ifv != NULL) { + ifvlan_release(ifv); + } + if (first_vlan) { + /* mark the parent interface up */ + ifnet_set_flags(p, IFF_UP, IFF_UP); + (void)ifnet_ioctl(p, 0, SIOCSIFFLAGS, (caddr_t)NULL); + } return 0; signal_done: vlan_assert_lock_held(); - vlan_parent_signal(vlp, "vlan_config"); - unlock_done: - if ((progress & VLAN_CONFIG_PROGRESS_IN_LIST) != 0) { + if (ifv_added) { vlan_parent_remove_vlan(vlp, ifv); + if (!vlan_parent_flags_detaching(vlp) && vlan_parent_no_vlans(vlp)) { + /* the vlan parent has no more VLAN's */ + ifnet_set_eflags(p, 0, IFEF_VLAN); + LIST_REMOVE(vlp, vlp_parent_list); + /* release outside of the lock below */ + need_vlp_release++; + + /* one for being in the list */ + need_vlp_release++; + } } - if (!vlan_parent_flags_detaching(vlp) && vlan_parent_no_vlans(vlp)) { - /* the vlan parent has no more VLAN's */ - ifnet_lock_exclusive(p); - p->if_eflags &= ~IFEF_VLAN; - ifnet_lock_done(p); - LIST_REMOVE(vlp, vlp_parent_list); - /* release outside of the lock below */ - need_vlp_release = 1; - } + vlan_parent_signal(vlp, "vlan_config"); + + unlock_done: vlan_unlock(); - if ((progress & VLAN_CONFIG_PROGRESS_VLP_RETAINED) != 0) { - vlan_parent_release(vlp); - } - if (need_vlp_release) { + while (need_vlp_release--) { vlan_parent_release(vlp); } if (new_vlp != vlp) { vlan_parent_release(new_vlp); } + if (ifv != NULL) { + if (ifv_added) { + ifvlan_release(ifv); + } + ifvlan_release(ifv); + } return (error); } @@ -1317,10 +1540,10 @@ vlan_link_event(struct ifnet * ifp, struct ifnet * p) /* generate a link event based on the state of the underlying interface */ bzero(&ifmr, sizeof(ifmr)); snprintf(ifmr.ifm_name, sizeof(ifmr.ifm_name), - "%s%d", p->if_name, p->if_unit); - if ((*p->if_ioctl)(p, SIOCGIFMEDIA, (caddr_t)&ifmr) == 0 + "%s%d", ifnet_name(p), ifnet_unit(p)); + if (ifnet_ioctl(p, 0, SIOCGIFMEDIA, &ifmr) == 0 && ifmr.ifm_count > 0 && ifmr.ifm_status & IFM_AVALID) { - u_long event; + u_int32_t event; event = (ifmr.ifm_status & IFM_ACTIVE) ? KEV_DL_LINK_ON : KEV_DL_LINK_OFF; @@ -1330,38 +1553,36 @@ vlan_link_event(struct ifnet * ifp, struct ifnet * p) } static int -vlan_unconfig(struct ifnet * ifp) +vlan_unconfig(ifvlan_ref ifv, int need_to_wait) { - int error = 0; - struct ifaddr * ifa; - ifvlan_ref ifv; - int last_vlan = 0; + struct ifnet * ifp = ifv->ifv_ifp; + int last_vlan = FALSE; + int need_ifv_release = 0; int need_vlp_release = 0; struct ifnet * p; - struct sockaddr_dl *sdl; vlan_parent_ref vlp; vlan_assert_lock_held(); - ifv = (ifvlan_ref)ifp->if_private; - if (ifv == NULL) { - return (0); - } vlp = ifv->ifv_vlp; if (vlp == NULL) { return (0); } - vlan_parent_retain(vlp); - vlan_parent_wait(vlp, "vlan_unconfig"); + if (need_to_wait) { + need_vlp_release++; + vlan_parent_retain(vlp); + vlan_parent_wait(vlp, "vlan_unconfig"); - /* check again because another thread could be in vlan_unconfig */ - ifv = (ifvlan_ref)ifp->if_private; - if (ifv == NULL) { - goto signal_done; - } - if (ifv->ifv_vlp != vlp) { - /* vlan parent changed */ - goto signal_done; + /* check again because another thread could be in vlan_unconfig */ + if (ifv != ifnet_get_ifvlan(ifp)) { + goto signal_done; + } + if (ifv->ifv_vlp != vlp) { + /* vlan parent changed */ + goto signal_done; + } } + + /* ifv has a reference on vlp, need to remove it */ need_vlp_release++; p = vlp->vlp_ifp; @@ -1369,64 +1590,69 @@ vlan_unconfig(struct ifnet * ifp) if (LIST_NEXT(LIST_FIRST(&vlp->vlp_vlan_list), ifv_vlan_list) == NULL) { if (g_vlan->verbose) { printf("vlan_unconfig: last vlan on %s%d\n", - p->if_name, p->if_unit); + ifnet_name(p), ifnet_unit(p)); } - last_vlan = 1; + last_vlan = TRUE; } /* back-out any effect our mtu might have had on the parent */ - (void)vlan_new_mtu(ifp, ETHERMTU - ifv->ifv_mtufudge); + (void)ifvlan_new_mtu(ifv, ETHERMTU - ifv->ifv_mtufudge); vlan_unlock(); + /* un-join multicast on parent interface */ + (void)multicast_list_remove(&ifv->ifv_multicast); + + /* Clear our MAC address. */ + ifnet_set_lladdr_and_type(ifp, NULL, 0, IFT_L2VLAN); + /* detach VLAN "protocol" */ if (last_vlan) { (void)vlan_detach_protocol(p); } - /* un-join multicast on parent interface */ - (void)multicast_list_remove(&ifv->ifv_multicast); - vlan_lock(); + /* return to the state we were in before SIFVLAN */ + ifnet_set_mtu(ifp, 0); + ifnet_set_flags(ifp, 0, + IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX | IFF_RUNNING); + ifnet_set_offload(ifp, 0); + ifv->ifv_mtufudge = 0; + /* Disconnect from parent. */ vlan_parent_remove_vlan(vlp, ifv); - - /* return to the state we were in before SIFVLAN */ - ifp->if_mtu = 0; - ifp->if_flags &= ~(IFF_BROADCAST | IFF_MULTICAST - | IFF_SIMPLEX | IFF_RUNNING); - ifp->if_hwassist = 0; ifv->ifv_flags = 0; - ifv->ifv_mtufudge = 0; - /* Clear our MAC address. */ - ifa = ifaddr_byindex(ifp->if_index); - sdl = (struct sockaddr_dl *)(ifa->ifa_addr); - sdl->sdl_type = IFT_L2VLAN; - sdl->sdl_alen = 0; - bzero(LLADDR(sdl), ETHER_ADDR_LEN); + /* vlan_parent has reference to ifv, remove it */ + need_ifv_release++; - if (!vlan_parent_flags_detaching(vlp) && vlan_parent_no_vlans(vlp)) { + /* from this point on, no more referencing ifv */ + if (last_vlan && !vlan_parent_flags_detaching(vlp)) { /* the vlan parent has no more VLAN's */ - ifnet_lock_exclusive(p); - p->if_eflags &= ~IFEF_VLAN; - ifnet_lock_done(p); + ifnet_set_eflags(p, 0, IFEF_VLAN); LIST_REMOVE(vlp, vlp_parent_list); + + /* one for being in the list */ + need_vlp_release++; + /* release outside of the lock below */ need_vlp_release++; } signal_done: - vlan_parent_signal(vlp, "vlan_unconfig"); + if (need_to_wait) { + vlan_parent_signal(vlp, "vlan_unconfig"); + } vlan_unlock(); - vlan_parent_release(vlp); /* one because we waited */ - - while (need_vlp_release--) { + while (need_ifv_release--) { + ifvlan_release(ifv); + } + while (need_vlp_release--) { /* references to vlp */ vlan_parent_release(vlp); } vlan_lock(); - return (error); + return (0); } static int @@ -1437,9 +1663,9 @@ vlan_set_promisc(struct ifnet * ifp) vlan_parent_ref vlp; vlan_lock(); - ifv = (ifvlan_ref)ifp->if_private; - if (ifv == NULL || ifvlan_flags_detaching(ifv)) { - error = (ifv == NULL) ? EOPNOTSUPP : EBUSY; + ifv = ifnet_get_ifvlan_retained(ifp); + if (ifv == NULL) { + error = EBUSY; goto done; } @@ -1447,7 +1673,7 @@ vlan_set_promisc(struct ifnet * ifp) if (vlp == NULL) { goto done; } - if ((ifp->if_flags & IFF_PROMISC) != 0) { + if ((ifnet_flags(ifp) & IFF_PROMISC) != 0) { if (!ifvlan_flags_promisc(ifv)) { error = ifnet_set_promiscuous(vlp->vlp_ifp, 1); if (error == 0) { @@ -1464,22 +1690,24 @@ vlan_set_promisc(struct ifnet * ifp) } done: vlan_unlock(); + if (ifv != NULL) { + ifvlan_release(ifv); + } return (error); } static int -vlan_new_mtu(struct ifnet * ifp, int mtu) +ifvlan_new_mtu(ifvlan_ref ifv, int mtu) { struct ifdevmtu * devmtu_p; int error = 0; - ifvlan_ref ifv; + struct ifnet * ifp = ifv->ifv_ifp; int max_mtu; int new_mtu = 0; int req_mtu; vlan_parent_ref vlp; vlan_assert_lock_held(); - ifv = (ifvlan_ref)ifp->if_private; vlp = ifv->ifv_vlp; devmtu_p = &vlp->vlp_devmtu; req_mtu = mtu + ifv->ifv_mtufudge; @@ -1503,7 +1731,7 @@ vlan_new_mtu(struct ifnet * ifp, int mtu) if (new_mtu != 0) { devmtu_p->ifdm_current = new_mtu; } - ifp->if_mtu = mtu; + ifnet_set_mtu(ifp, mtu); } return (error); } @@ -1519,55 +1747,56 @@ vlan_set_mtu(struct ifnet * ifp, int mtu) return (EINVAL); } vlan_lock(); - ifv = (ifvlan_ref)ifp->if_private; - if (ifv == NULL || ifvlan_flags_detaching(ifv)) { + ifv = ifnet_get_ifvlan_retained(ifp); + if (ifv == NULL) { vlan_unlock(); - return ((ifv == NULL) ? EOPNOTSUPP : EBUSY); + return (EBUSY); } - vlp = ifv->ifv_vlp; - if (vlp == NULL || vlan_parent_flags_detaching(vlp)) { + vlp = ifvlan_get_vlan_parent_retained(ifv); + if (vlp == NULL) { vlan_unlock(); + ifvlan_release(ifv); if (mtu != 0) { return (EINVAL); } return (0); } - vlan_parent_retain(vlp); vlan_parent_wait(vlp, "vlan_set_mtu"); /* check again, something might have changed */ - ifv = (ifvlan_ref)ifp->if_private; - if (ifv == NULL || ifvlan_flags_detaching(ifv)) { - error = (ifv == NULL) ? EOPNOTSUPP : EBUSY; + if (ifnet_get_ifvlan(ifp) != ifv + || ifvlan_flags_detaching(ifv)) { + error = EBUSY; goto signal_done; } if (ifv->ifv_vlp != vlp) { /* vlan parent changed */ goto signal_done; } - if (vlp == NULL || vlan_parent_flags_detaching(vlp)) { + if (vlan_parent_flags_detaching(vlp)) { if (mtu != 0) { error = EINVAL; } goto signal_done; } - error = vlan_new_mtu(ifp, mtu); + error = ifvlan_new_mtu(ifv, mtu); signal_done: vlan_parent_signal(vlp, "vlan_set_mtu"); vlan_unlock(); vlan_parent_release(vlp); + ifvlan_release(ifv); return (error); } static int -vlan_ioctl(ifnet_t ifp, u_int32_t cmd, void * data) +vlan_ioctl(ifnet_t ifp, u_long cmd, void * data) { struct ifdevmtu * devmtu_p; int error = 0; struct ifaddr * ifa; - struct ifmediareq64 * ifmr; + struct ifmediareq *ifmr; struct ifreq * ifr; ifvlan_ref ifv; struct ifnet * p; @@ -1576,7 +1805,7 @@ vlan_ioctl(ifnet_t ifp, u_int32_t cmd, void * data) vlan_parent_ref vlp; struct vlanreq vlr; - if (ifp->if_type != IFT_L2VLAN) { + if (ifnet_type(ifp) != IFT_L2VLAN) { return (EOPNOTSUPP); } ifr = (struct ifreq *)data; @@ -1587,25 +1816,25 @@ vlan_ioctl(ifnet_t ifp, u_int32_t cmd, void * data) ifnet_set_flags(ifp, IFF_UP, IFF_UP); break; + case SIOCGIFMEDIA32: case SIOCGIFMEDIA64: - case SIOCGIFMEDIA: vlan_lock(); - ifv = (ifvlan_ref)ifp->if_private; + ifv = (ifvlan_ref)ifnet_softc(ifp); if (ifv == NULL || ifvlan_flags_detaching(ifv)) { vlan_unlock(); return (ifv == NULL ? EOPNOTSUPP : EBUSY); } p = (ifv->ifv_vlp == NULL) ? NULL : ifv->ifv_vlp->vlp_ifp; vlan_unlock(); - ifmr = (struct ifmediareq64 *)data; - user_addr = (cmd == SIOCGIFMEDIA64) - ? ifmr->ifm_ifmu.ifmu_ulist64 - : CAST_USER_ADDR_T(ifmr->ifm_ifmu.ifmu_ulist32); + ifmr = (struct ifmediareq *)data; + user_addr = (cmd == SIOCGIFMEDIA64) ? + ((struct ifmediareq64 *)ifmr)->ifmu_ulist : + CAST_USER_ADDR_T(((struct ifmediareq32 *)ifmr)->ifmu_ulist); if (p != NULL) { - struct ifmediareq64 p_ifmr; + struct ifmediareq p_ifmr; bzero(&p_ifmr, sizeof(p_ifmr)); - error = dlil_ioctl(0, p, SIOCGIFMEDIA, (caddr_t)&p_ifmr); + error = ifnet_ioctl(p, 0, SIOCGIFMEDIA, &p_ifmr); if (error == 0) { ifmr->ifm_active = p_ifmr.ifm_active; ifmr->ifm_current = p_ifmr.ifm_current; @@ -1636,7 +1865,7 @@ vlan_ioctl(ifnet_t ifp, u_int32_t cmd, void * data) case SIOCGIFDEVMTU: vlan_lock(); - ifv = (ifvlan_ref)ifp->if_private; + ifv = (ifvlan_ref)ifnet_softc(ifp); if (ifv == NULL || ifvlan_flags_detaching(ifv)) { vlan_unlock(); return (ifv == NULL ? EOPNOTSUPP : EBUSY); @@ -1645,7 +1874,7 @@ vlan_ioctl(ifnet_t ifp, u_int32_t cmd, void * data) if (vlp != NULL) { int min_mtu = vlp->vlp_devmtu.ifdm_min - ifv->ifv_mtufudge; devmtu_p = &ifr->ifr_devmtu; - devmtu_p->ifdm_current = ifp->if_mtu; + devmtu_p->ifdm_current = ifnet_mtu(ifp); devmtu_p->ifdm_min = max(min_mtu, IF_MINMTU); devmtu_p->ifdm_max = vlp->vlp_devmtu.ifdm_max - ifv->ifv_mtufudge; } @@ -1685,7 +1914,8 @@ vlan_ioctl(ifnet_t ifp, u_int32_t cmd, void * data) break; } /* can't do VLAN over anything but ethernet or ethernet aggregate */ - if (p->if_type != IFT_ETHER && p->if_type != IFT_IEEE8023ADLAG) { + if (ifnet_type(p) != IFT_ETHER + && ifnet_type(p) != IFT_IEEE8023ADLAG) { error = EPROTONOSUPPORT; break; } @@ -1699,17 +1929,20 @@ vlan_ioctl(ifnet_t ifp, u_int32_t cmd, void * data) /* generate a link event based on the state of the parent */ vlan_link_event(ifp, p); - } else { + } + else { + int need_link_event = FALSE; + vlan_lock(); - ifv = (ifvlan_ref)ifp->if_private; + ifv = (ifvlan_ref)ifnet_softc(ifp); if (ifv == NULL || ifvlan_flags_detaching(ifv)) { vlan_unlock(); error = (ifv == NULL ? EOPNOTSUPP : EBUSY); break; } - error = vlan_unconfig(ifp); + need_link_event = vlan_remove(ifv, TRUE); vlan_unlock(); - if (error == 0) { + if (need_link_event) { interface_link_event(ifp, KEV_DL_LINK_OFF); } } @@ -1718,7 +1951,7 @@ vlan_ioctl(ifnet_t ifp, u_int32_t cmd, void * data) case SIOCGIFVLAN: bzero(&vlr, sizeof vlr); vlan_lock(); - ifv = (ifvlan_ref)ifp->if_private; + ifv = (ifvlan_ref)ifnet_softc(ifp); if (ifv == NULL || ifvlan_flags_detaching(ifv)) { vlan_unlock(); return (ifv == NULL ? EOPNOTSUPP : EBUSY); @@ -1728,7 +1961,7 @@ vlan_ioctl(ifnet_t ifp, u_int32_t cmd, void * data) vlan_unlock(); if (p != NULL) { snprintf(vlr.vlr_parent, sizeof(vlr.vlr_parent), - "%s%d", p->if_name, p->if_unit); + "%s%d", ifnet_name(p), ifnet_unit(p)); vlr.vlr_tag = tag; } user_addr = proc_is64bit(current_proc()) @@ -1762,22 +1995,20 @@ vlan_if_free(struct ifnet * ifp) if (ifp == NULL) { return; } - vlan_lock(); - ifv = (ifvlan_ref)ifp->if_private; + ifv = (ifvlan_ref)ifnet_softc(ifp); if (ifv == NULL) { - vlan_unlock(); return; } - ifp->if_private = NULL; - vlan_unlock(); - dlil_if_release(ifp); - FREE(ifv, M_VLAN); + ifvlan_release(ifv); + ifnet_release(ifp); + return; } static void -vlan_event(struct ifnet * p, struct kev_msg * event) +vlan_event(struct ifnet * p, __unused protocol_family_t protocol, + const struct kev_msg * event) { - vlan_parent_ref vlp; + int event_code; /* Check if the interface we are attached to is being detached */ if (event->vendor_code != KEV_VENDOR_APPLE @@ -1785,72 +2016,79 @@ vlan_event(struct ifnet * p, struct kev_msg * event) || event->kev_subclass != KEV_DL_SUBCLASS) { return; } - switch (event->event_code) { - case KEV_DL_IF_DETACHING: + event_code = event->event_code; + switch (event_code) { case KEV_DL_LINK_OFF: case KEV_DL_LINK_ON: + vlan_parent_link_event(p, event_code); break; default: return; } - vlan_lock(); - if ((p->if_eflags & IFEF_VLAN) == 0) { - vlan_unlock(); - /* no VLAN's */ - return; - } - vlp = parent_list_lookup(p); - if (vlp == NULL) { - /* no VLAN's */ - vlan_unlock(); - return; - } - switch (event->event_code) { - case KEV_DL_IF_DETACHING: - vlan_parent_flags_set_detaching(vlp); - vlan_parent_remove_all_vlans(vlp); - break; - - case KEV_DL_LINK_OFF: - case KEV_DL_LINK_ON: - vlan_parent_link_event(vlp, event->event_code); - break; - default: - break; - } - vlan_unlock(); return; } +static errno_t +vlan_detached(ifnet_t p, __unused protocol_family_t protocol) +{ + if (ifnet_is_attached(p, 0) == 0) { + /* if the parent isn't attached, remove all VLANs */ + vlan_parent_remove_all_vlans(p); + } + return (0); +} + static void -interface_link_event(struct ifnet * ifp, u_long event_code) +interface_link_event(struct ifnet * ifp, u_int32_t event_code) { struct { struct kern_event_msg header; - u_long unit; + u_int32_t unit; char if_name[IFNAMSIZ]; } event; + bzero(&event, sizeof(event)); event.header.total_size = sizeof(event); event.header.vendor_code = KEV_VENDOR_APPLE; event.header.kev_class = KEV_NETWORK_CLASS; event.header.kev_subclass = KEV_DL_SUBCLASS; event.header.event_code = event_code; - event.header.event_data[0] = ifp->if_family; - event.unit = (u_long) ifp->if_unit; - strncpy(event.if_name, ifp->if_name, IFNAMSIZ); - dlil_event(ifp, &event.header); + event.header.event_data[0] = ifnet_family(ifp); + event.unit = (u_int32_t) ifnet_unit(ifp); + strlcpy(event.if_name, ifnet_name(ifp), IFNAMSIZ); + ifnet_event(ifp, &event.header); return; } static void -vlan_parent_link_event(vlan_parent_ref vlp, u_long event_code) +vlan_parent_link_event(struct ifnet * p, u_int32_t event_code) { - ifvlan_ref ifv; + vlan_parent_ref vlp; - LIST_FOREACH(ifv, &vlp->vlp_vlan_list, ifv_vlan_list) { - interface_link_event(ifv->ifv_ifp, event_code); + vlan_lock(); + if ((ifnet_eflags(p) & IFEF_VLAN) == 0) { + vlan_unlock(); + /* no VLAN's */ + return; + } + vlp = parent_list_lookup(p); + if (vlp == NULL) { + /* no VLAN's */ + vlan_unlock(); + return; + } + vlan_parent_flags_set_link_event_required(vlp); + vlp->vlp_event_code = event_code; + if (vlan_parent_flags_change_in_progress(vlp)) { + /* don't block waiting to generate an event */ + vlan_unlock(); + return; } + vlan_parent_retain(vlp); + vlan_parent_wait(vlp, "vlan_parent_link_event"); + vlan_parent_signal(vlp, "vlan_parent_link_event"); + vlan_unlock(); + vlan_parent_release(vlp); return; } @@ -1868,20 +2106,17 @@ vlan_parent_link_event(vlan_parent_ref vlp, u_long event_code) static int vlan_attach_protocol(struct ifnet *ifp) { - int error; - struct dlil_proto_reg_str reg; + int error; + struct ifnet_attach_proto_param reg; bzero(®, sizeof(reg)); - TAILQ_INIT(®.demux_desc_head); - reg.interface_family = ifp->if_family; - reg.unit_number = ifp->if_unit; reg.input = vlan_input; reg.event = vlan_event; - reg.protocol_family = PF_VLAN; - error = dlil_attach_protocol(®); + reg.detached = vlan_detached; + error = ifnet_attach_protocol(ifp, PF_VLAN, ®); if (error) { - printf("vlan_proto_attach(%s%d) dlil_attach_protocol failed, %d\n", - ifp->if_name, ifp->if_unit, error); + printf("vlan_proto_attach(%s%d) ifnet_attach_protocol failed, %d\n", + ifnet_name(ifp), ifnet_unit(ifp), error); } return (error); } @@ -1896,10 +2131,10 @@ vlan_detach_protocol(struct ifnet *ifp) { int error; - error = dlil_detach_protocol(ifp, PF_VLAN); + error = ifnet_detach_protocol(ifp, PF_VLAN); if (error) { - printf("vlan_proto_detach(%s%d) dlil_detach_protocol failed, %d\n", - ifp->if_name, ifp->if_unit, error); + printf("vlan_proto_detach(%s%d) ifnet_detach_protocol failed, %d\n", + ifnet_name(ifp), ifnet_unit(ifp), error); } return (error); @@ -1907,96 +2142,65 @@ vlan_detach_protocol(struct ifnet *ifp) /* * DLIL interface family functions - * We use the ethernet dlil functions, since that's all we support. + * We use the ethernet plumb functions, since that's all we support. * If we wanted to handle multiple LAN types (tokenring, etc.), we'd * call the appropriate routines for that LAN type instead of hard-coding * ethernet. */ -extern int ether_add_if(struct ifnet *ifp); -extern int ether_del_if(struct ifnet *ifp); -extern int ether_init_if(struct ifnet *ifp); -extern int ether_add_proto_old(struct ifnet *ifp, u_long protocol_family, - struct ddesc_head_str *desc_head); - -extern int ether_attach_inet(struct ifnet *ifp, u_long protocol_family); -extern int ether_detach_inet(struct ifnet *ifp, u_long protocol_family); -extern int ether_attach_inet6(struct ifnet *ifp, u_long protocol_family); -extern int ether_detach_inet6(struct ifnet *ifp, u_long protocol_family); - -static int -vlan_attach_inet(struct ifnet *ifp, u_long protocol_family) +static errno_t +vlan_attach_inet(struct ifnet *ifp, protocol_family_t protocol_family) { return (ether_attach_inet(ifp, protocol_family)); } -static int -vlan_detach_inet(struct ifnet *ifp, u_long protocol_family) +static void +vlan_detach_inet(struct ifnet *ifp, protocol_family_t protocol_family) { - return (ether_detach_inet(ifp, protocol_family)); + ether_detach_inet(ifp, protocol_family); } -static int -vlan_attach_inet6(struct ifnet *ifp, u_long protocol_family) +#if INET6 +static errno_t +vlan_attach_inet6(struct ifnet *ifp, protocol_family_t protocol_family) { return (ether_attach_inet6(ifp, protocol_family)); } -static int -vlan_detach_inet6(struct ifnet *ifp, u_long protocol_family) -{ - return (ether_detach_inet6(ifp, protocol_family)); -} - -static int -vlan_add_if(struct ifnet *ifp) -{ - return (ether_add_if(ifp)); -} - -static int -vlan_del_if(struct ifnet *ifp) +static void +vlan_detach_inet6(struct ifnet *ifp, protocol_family_t protocol_family) { - return (ether_del_if(ifp)); + ether_detach_inet6(ifp, protocol_family); } - +#endif /* INET6 */ __private_extern__ int vlan_family_init(void) { int error=0; - struct dlil_ifmod_reg_str ifmod_reg; - - bzero(&ifmod_reg, sizeof(ifmod_reg)); - ifmod_reg.add_if = vlan_add_if; - ifmod_reg.del_if = vlan_del_if; - ifmod_reg.init_if = NULL; - ifmod_reg.add_proto = ether_add_proto_old; - ifmod_reg.del_proto = ether_del_proto; - ifmod_reg.ifmod_ioctl = ether_ioctl; - ifmod_reg.shutdown = NULL; - - if (dlil_reg_if_modules(APPLE_IF_FAM_VLAN, &ifmod_reg)) { - printf("WARNING: vlan_family_init -- " - "Can't register if family modules\n"); - error = EIO; - goto done; - } - error = dlil_reg_proto_module(PF_INET, APPLE_IF_FAM_VLAN, - vlan_attach_inet, vlan_detach_inet); + error = proto_register_plumber(PF_INET, IFNET_FAMILY_VLAN, + vlan_attach_inet, vlan_detach_inet); if (error != 0) { - printf("dlil_reg_proto_module failed for AF_INET error=%d\n", + printf("proto_register_plumber failed for AF_INET error=%d\n", error); goto done; } - error = dlil_reg_proto_module(PF_INET6, APPLE_IF_FAM_VLAN, - vlan_attach_inet6, vlan_detach_inet6); +#if INET6 + error = proto_register_plumber(PF_INET6, IFNET_FAMILY_VLAN, + vlan_attach_inet6, vlan_detach_inet6); if (error != 0) { - printf("dlil_reg_proto_module failed for AF_INET6 error=%d\n", + printf("proto_register_plumber failed for AF_INET6 error=%d\n", error); goto done; } - vlan_clone_attach(); +#endif + error = vlan_clone_attach(); + if (error != 0) { + printf("proto_register_plumber failed vlan_clone_attach error=%d\n", + error); + goto done; + } + done: return (error);