Changeset View
Changeset View
Standalone View
Standalone View
head/sys/net/if_vlan.c
Show All 11 Lines | |||||
* permission notice appear in all copies, that both the above | * permission notice appear in all copies, that both the above | ||||
* copyright notice and this permission notice appear in all | * copyright notice and this permission notice appear in all | ||||
* supporting documentation, and that the name of M.I.T. not be used | * supporting documentation, and that the name of M.I.T. not be used | ||||
* in advertising or publicity pertaining to distribution of the | * in advertising or publicity pertaining to distribution of the | ||||
* software without specific, written prior permission. M.I.T. makes | * software without specific, written prior permission. M.I.T. makes | ||||
* no representations about the suitability of this software for any | * no representations about the suitability of this software for any | ||||
* purpose. It is provided "as is" without express or implied | * purpose. It is provided "as is" without express or implied | ||||
* warranty. | * warranty. | ||||
* | * | ||||
* THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS | * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS | ||||
* ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, | * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, | ||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT | ||||
* SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | ||||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||||
▲ Show 20 Lines • Show All 151 Lines • ▼ Show 20 Lines | struct vlan_mc_entry { | ||||
CK_SLIST_ENTRY(vlan_mc_entry) mc_entries; | CK_SLIST_ENTRY(vlan_mc_entry) mc_entries; | ||||
struct epoch_context mc_epoch_ctx; | struct epoch_context mc_epoch_ctx; | ||||
}; | }; | ||||
struct ifvlan { | struct ifvlan { | ||||
struct ifvlantrunk *ifv_trunk; | struct ifvlantrunk *ifv_trunk; | ||||
struct ifnet *ifv_ifp; | struct ifnet *ifv_ifp; | ||||
#define TRUNK(ifv) ((ifv)->ifv_trunk) | #define TRUNK(ifv) ((ifv)->ifv_trunk) | ||||
#define PARENT(ifv) ((ifv)->ifv_trunk->parent) | #define PARENT(ifv) (TRUNK(ifv)->parent) | ||||
void *ifv_cookie; | void *ifv_cookie; | ||||
int ifv_pflags; /* special flags we have set on parent */ | int ifv_pflags; /* special flags we have set on parent */ | ||||
int ifv_capenable; | int ifv_capenable; | ||||
int ifv_encaplen; /* encapsulation length */ | int ifv_encaplen; /* encapsulation length */ | ||||
int ifv_mtufudge; /* MTU fudged by this much */ | int ifv_mtufudge; /* MTU fudged by this much */ | ||||
int ifv_mintu; /* min transmission unit */ | int ifv_mintu; /* min transmission unit */ | ||||
uint16_t ifv_proto; /* encapsulation ethertype */ | struct ether_8021q_tag ifv_qtag; | ||||
uint16_t ifv_tag; /* tag to apply on packets leaving if */ | #define ifv_proto ifv_qtag.proto | ||||
uint16_t ifv_vid; /* VLAN ID */ | #define ifv_vid ifv_qtag.vid | ||||
uint8_t ifv_pcp; /* Priority Code Point (PCP). */ | #define ifv_pcp ifv_qtag.pcp | ||||
struct task lladdr_task; | struct task lladdr_task; | ||||
CK_SLIST_HEAD(, vlan_mc_entry) vlan_mc_listhead; | CK_SLIST_HEAD(, vlan_mc_entry) vlan_mc_listhead; | ||||
#ifndef VLAN_ARRAY | #ifndef VLAN_ARRAY | ||||
CK_SLIST_ENTRY(ifvlan) ifv_list; | CK_SLIST_ENTRY(ifvlan) ifv_list; | ||||
#endif | #endif | ||||
}; | }; | ||||
/* Special flags we should propagate to parent. */ | /* Special flags we should propagate to parent. */ | ||||
Show All 10 Lines | |||||
static const char vlanname[] = "vlan"; | static const char vlanname[] = "vlan"; | ||||
static MALLOC_DEFINE(M_VLAN, vlanname, "802.1Q Virtual LAN Interface"); | static MALLOC_DEFINE(M_VLAN, vlanname, "802.1Q Virtual LAN Interface"); | ||||
static eventhandler_tag ifdetach_tag; | static eventhandler_tag ifdetach_tag; | ||||
static eventhandler_tag iflladdr_tag; | static eventhandler_tag iflladdr_tag; | ||||
/* | /* | ||||
* if_vlan uses two module-level synchronizations primitives to allow concurrent | * if_vlan uses two module-level synchronizations primitives to allow concurrent | ||||
* modification of vlan interfaces and (mostly) allow for vlans to be destroyed | * modification of vlan interfaces and (mostly) allow for vlans to be destroyed | ||||
* while they are being used for tx/rx. To accomplish this in a way that has | * while they are being used for tx/rx. To accomplish this in a way that has | ||||
* acceptable performance and cooperation with other parts of the network stack | * acceptable performance and cooperation with other parts of the network stack | ||||
* there is a non-sleepable epoch(9) and an sx(9). | * there is a non-sleepable epoch(9) and an sx(9). | ||||
* | * | ||||
* The performance-sensitive paths that warrant using the epoch(9) are | * The performance-sensitive paths that warrant using the epoch(9) are | ||||
* vlan_transmit and vlan_input. Both have to check for the vlan interface's | * vlan_transmit and vlan_input. Both have to check for the vlan interface's | ||||
* existence using if_vlantrunk, and being in the network tx/rx paths the use | * existence using if_vlantrunk, and being in the network tx/rx paths the use | ||||
* of an epoch(9) gives a measureable improvement in performance. | * of an epoch(9) gives a measureable improvement in performance. | ||||
* | * | ||||
* The reason for having an sx(9) is mostly because there are still areas that | * The reason for having an sx(9) is mostly because there are still areas that | ||||
* must be sleepable and also have safe concurrent access to a vlan interface. | * must be sleepable and also have safe concurrent access to a vlan interface. | ||||
* Since the sx(9) exists, it is used by default in most paths unless sleeping | * Since the sx(9) exists, it is used by default in most paths unless sleeping | ||||
* is not permitted, or if it is not clear whether sleeping is permitted. | * is not permitted, or if it is not clear whether sleeping is permitted. | ||||
* | * | ||||
*/ | */ | ||||
#define _VLAN_SX_ID ifv_sx | #define _VLAN_SX_ID ifv_sx | ||||
static struct sx _VLAN_SX_ID; | static struct sx _VLAN_SX_ID; | ||||
#define VLAN_LOCKING_INIT() \ | #define VLAN_LOCKING_INIT() \ | ||||
sx_init(&_VLAN_SX_ID, "vlan_sx") | sx_init_flags(&_VLAN_SX_ID, "vlan_sx", SX_RECURSE) | ||||
#define VLAN_LOCKING_DESTROY() \ | #define VLAN_LOCKING_DESTROY() \ | ||||
sx_destroy(&_VLAN_SX_ID) | sx_destroy(&_VLAN_SX_ID) | ||||
#define VLAN_SLOCK() sx_slock(&_VLAN_SX_ID) | #define VLAN_SLOCK() sx_slock(&_VLAN_SX_ID) | ||||
#define VLAN_SUNLOCK() sx_sunlock(&_VLAN_SX_ID) | #define VLAN_SUNLOCK() sx_sunlock(&_VLAN_SX_ID) | ||||
#define VLAN_XLOCK() sx_xlock(&_VLAN_SX_ID) | #define VLAN_XLOCK() sx_xlock(&_VLAN_SX_ID) | ||||
#define VLAN_XUNLOCK() sx_xunlock(&_VLAN_SX_ID) | #define VLAN_XUNLOCK() sx_xunlock(&_VLAN_SX_ID) | ||||
▲ Show 20 Lines • Show All 45 Lines • ▼ Show 20 Lines | static int vlan_setflag(struct ifnet *ifp, int flag, int status, | ||||
int (*func)(struct ifnet *, int)); | int (*func)(struct ifnet *, int)); | ||||
static int vlan_setflags(struct ifnet *ifp, int status); | static int vlan_setflags(struct ifnet *ifp, int status); | ||||
static int vlan_setmulti(struct ifnet *ifp); | static int vlan_setmulti(struct ifnet *ifp); | ||||
static int vlan_transmit(struct ifnet *ifp, struct mbuf *m); | static int vlan_transmit(struct ifnet *ifp, struct mbuf *m); | ||||
static int vlan_output(struct ifnet *ifp, struct mbuf *m, | static int vlan_output(struct ifnet *ifp, struct mbuf *m, | ||||
const struct sockaddr *dst, struct route *ro); | const struct sockaddr *dst, struct route *ro); | ||||
static void vlan_unconfig(struct ifnet *ifp); | static void vlan_unconfig(struct ifnet *ifp); | ||||
static void vlan_unconfig_locked(struct ifnet *ifp, int departing); | static void vlan_unconfig_locked(struct ifnet *ifp, int departing); | ||||
static int vlan_config(struct ifvlan *ifv, struct ifnet *p, uint16_t tag); | static int vlan_config(struct ifvlan *ifv, struct ifnet *p, uint16_t tag, | ||||
uint16_t proto); | |||||
static void vlan_link_state(struct ifnet *ifp); | static void vlan_link_state(struct ifnet *ifp); | ||||
static void vlan_capabilities(struct ifvlan *ifv); | static void vlan_capabilities(struct ifvlan *ifv); | ||||
static void vlan_trunk_capabilities(struct ifnet *ifp); | static void vlan_trunk_capabilities(struct ifnet *ifp); | ||||
static struct ifnet *vlan_clone_match_ethervid(const char *, int *); | static struct ifnet *vlan_clone_match_ethervid(const char *, int *); | ||||
static int vlan_clone_match(struct if_clone *, const char *); | static int vlan_clone_match(struct if_clone *, const char *); | ||||
static int vlan_clone_create(struct if_clone *, char *, size_t, caddr_t); | static int vlan_clone_create(struct if_clone *, char *, size_t, caddr_t); | ||||
static int vlan_clone_destroy(struct if_clone *, struct ifnet *); | static int vlan_clone_destroy(struct if_clone *, struct ifnet *); | ||||
▲ Show 20 Lines • Show All 437 Lines • ▼ Show 20 Lines | if (ifp->if_type != IFT_L2VLAN) | ||||
return (EINVAL); | return (EINVAL); | ||||
ifv = ifp->if_softc; | ifv = ifp->if_softc; | ||||
*pcpp = ifv->ifv_pcp; | *pcpp = ifv->ifv_pcp; | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* Return a driver specific cookie for this interface. Synchronization | * Return a driver specific cookie for this interface. Synchronization | ||||
* with setcookie must be provided by the driver. | * with setcookie must be provided by the driver. | ||||
*/ | */ | ||||
static void * | static void * | ||||
vlan_cookie(struct ifnet *ifp) | vlan_cookie(struct ifnet *ifp) | ||||
{ | { | ||||
struct ifvlan *ifv; | struct ifvlan *ifv; | ||||
if (ifp->if_type != IFT_L2VLAN) | if (ifp->if_type != IFT_L2VLAN) | ||||
return (NULL); | return (NULL); | ||||
Show All 34 Lines | vlan_devat(struct ifnet *ifp, uint16_t vid) | ||||
ifp = NULL; | ifp = NULL; | ||||
ifv = vlan_gethash(trunk, vid); | ifv = vlan_gethash(trunk, vid); | ||||
if (ifv) | if (ifv) | ||||
ifp = ifv->ifv_ifp; | ifp = ifv->ifv_ifp; | ||||
return (ifp); | return (ifp); | ||||
} | } | ||||
/* | /* | ||||
* Recalculate the cached VLAN tag exposed via the MIB. | |||||
*/ | |||||
static void | |||||
vlan_tag_recalculate(struct ifvlan *ifv) | |||||
{ | |||||
ifv->ifv_tag = EVL_MAKETAG(ifv->ifv_vid, ifv->ifv_pcp, 0); | |||||
} | |||||
/* | |||||
* VLAN support can be loaded as a module. The only place in the | * VLAN support can be loaded as a module. The only place in the | ||||
* system that's intimately aware of this is ether_input. We hook | * system that's intimately aware of this is ether_input. We hook | ||||
* into this code through vlan_input_p which is defined there and | * into this code through vlan_input_p which is defined there and | ||||
* set here. No one else in the system should be aware of this so | * set here. No one else in the system should be aware of this so | ||||
* we use an explicit reference here. | * we use an explicit reference here. | ||||
*/ | */ | ||||
extern void (*vlan_input_p)(struct ifnet *, struct mbuf *); | extern void (*vlan_input_p)(struct ifnet *, struct mbuf *); | ||||
Show All 30 Lines | |||||
#endif | #endif | ||||
if (bootverbose) | if (bootverbose) | ||||
printf("vlan: initialized, using " | printf("vlan: initialized, using " | ||||
#ifdef VLAN_ARRAY | #ifdef VLAN_ARRAY | ||||
"full-size arrays" | "full-size arrays" | ||||
#else | #else | ||||
"hash tables with chaining" | "hash tables with chaining" | ||||
#endif | #endif | ||||
"\n"); | "\n"); | ||||
break; | break; | ||||
case MOD_UNLOAD: | case MOD_UNLOAD: | ||||
#ifndef VIMAGE | #ifndef VIMAGE | ||||
if_clone_detach(vlan_cloner); | if_clone_detach(vlan_cloner); | ||||
#endif | #endif | ||||
EVENTHANDLER_DEREGISTER(ifnet_departure_event, ifdetach_tag); | EVENTHANDLER_DEREGISTER(ifnet_departure_event, ifdetach_tag); | ||||
EVENTHANDLER_DEREGISTER(iflladdr_event, iflladdr_tag); | EVENTHANDLER_DEREGISTER(iflladdr_event, iflladdr_tag); | ||||
▲ Show 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | vnet_vlan_uninit(const void *unused __unused) | ||||
if_clone_detach(V_vlan_cloner); | if_clone_detach(V_vlan_cloner); | ||||
} | } | ||||
VNET_SYSUNINIT(vnet_vlan_uninit, SI_SUB_INIT_IF, SI_ORDER_ANY, | VNET_SYSUNINIT(vnet_vlan_uninit, SI_SUB_INIT_IF, SI_ORDER_ANY, | ||||
vnet_vlan_uninit, NULL); | vnet_vlan_uninit, NULL); | ||||
#endif | #endif | ||||
/* | /* | ||||
* Check for <etherif>.<vlan> style interface names. | * Check for <etherif>.<vlan>[.<vlan> ...] style interface names. | ||||
*/ | */ | ||||
static struct ifnet * | static struct ifnet * | ||||
vlan_clone_match_ethervid(const char *name, int *vidp) | vlan_clone_match_ethervid(const char *name, int *vidp) | ||||
{ | { | ||||
char ifname[IFNAMSIZ]; | char ifname[IFNAMSIZ]; | ||||
char *cp; | char *cp; | ||||
struct ifnet *ifp; | struct ifnet *ifp; | ||||
int vid; | int vid; | ||||
strlcpy(ifname, name, IFNAMSIZ); | strlcpy(ifname, name, IFNAMSIZ); | ||||
if ((cp = strchr(ifname, '.')) == NULL) | if ((cp = strrchr(ifname, '.')) == NULL) | ||||
return (NULL); | return (NULL); | ||||
*cp = '\0'; | *cp = '\0'; | ||||
if ((ifp = ifunit_ref(ifname)) == NULL) | if ((ifp = ifunit_ref(ifname)) == NULL) | ||||
return (NULL); | return (NULL); | ||||
/* Parse VID. */ | /* Parse VID. */ | ||||
if (*++cp == '\0') { | if (*++cp == '\0') { | ||||
if_rele(ifp); | if_rele(ifp); | ||||
return (NULL); | return (NULL); | ||||
Show All 36 Lines | |||||
static int | static int | ||||
vlan_clone_create(struct if_clone *ifc, char *name, size_t len, caddr_t params) | vlan_clone_create(struct if_clone *ifc, char *name, size_t len, caddr_t params) | ||||
{ | { | ||||
char *dp; | char *dp; | ||||
int wildcard; | int wildcard; | ||||
int unit; | int unit; | ||||
int error; | int error; | ||||
int vid; | int vid; | ||||
uint16_t proto; | |||||
struct ifvlan *ifv; | struct ifvlan *ifv; | ||||
struct ifnet *ifp; | struct ifnet *ifp; | ||||
struct ifnet *p; | struct ifnet *p; | ||||
struct ifaddr *ifa; | struct ifaddr *ifa; | ||||
struct sockaddr_dl *sdl; | struct sockaddr_dl *sdl; | ||||
struct vlanreq vlr; | struct vlanreq vlr; | ||||
static const u_char eaddr[ETHER_ADDR_LEN]; /* 00:00:00:00:00:00 */ | static const u_char eaddr[ETHER_ADDR_LEN]; /* 00:00:00:00:00:00 */ | ||||
proto = ETHERTYPE_VLAN; | |||||
/* | /* | ||||
* There are 3 (ugh) ways to specify the cloned device: | * There are two ways to specify the cloned device: | ||||
* o pass a parameter block with the clone request. | * o pass a parameter block with the clone request. | ||||
* o specify parameters in the text of the clone device name | |||||
* o specify no parameters and get an unattached device that | * o specify no parameters and get an unattached device that | ||||
* must be configured separately. | * must be configured separately. | ||||
* The first technique is preferred; the latter two are | * The first technique is preferred; the latter is supported | ||||
* supported for backwards compatibility. | * for backwards compatibility. | ||||
* | * | ||||
* XXXRW: Note historic use of the word "tag" here. New ioctls may be | * XXXRW: Note historic use of the word "tag" here. New ioctls may be | ||||
* called for. | * called for. | ||||
*/ | */ | ||||
if (params) { | if (params) { | ||||
error = copyin(params, &vlr, sizeof(vlr)); | error = copyin(params, &vlr, sizeof(vlr)); | ||||
if (error) | if (error) | ||||
return error; | return error; | ||||
p = ifunit_ref(vlr.vlr_parent); | p = ifunit_ref(vlr.vlr_parent); | ||||
if (p == NULL) | if (p == NULL) | ||||
return (ENXIO); | return (ENXIO); | ||||
error = ifc_name2unit(name, &unit); | error = ifc_name2unit(name, &unit); | ||||
if (error != 0) { | if (error != 0) { | ||||
if_rele(p); | if_rele(p); | ||||
return (error); | return (error); | ||||
} | } | ||||
vid = vlr.vlr_tag; | vid = vlr.vlr_tag; | ||||
proto = vlr.vlr_proto; | |||||
wildcard = (unit < 0); | wildcard = (unit < 0); | ||||
} else if ((p = vlan_clone_match_ethervid(name, &vid)) != NULL) { | |||||
unit = -1; | |||||
wildcard = 0; | |||||
} else { | } else { | ||||
p = NULL; | p = NULL; | ||||
error = ifc_name2unit(name, &unit); | error = ifc_name2unit(name, &unit); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
wildcard = (unit < 0); | wildcard = (unit < 0); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 49 Lines • ▼ Show 20 Lines | #endif | ||||
ifp->if_baudrate = 0; | ifp->if_baudrate = 0; | ||||
ifp->if_type = IFT_L2VLAN; | ifp->if_type = IFT_L2VLAN; | ||||
ifp->if_hdrlen = ETHER_VLAN_ENCAP_LEN; | ifp->if_hdrlen = ETHER_VLAN_ENCAP_LEN; | ||||
ifa = ifp->if_addr; | ifa = ifp->if_addr; | ||||
sdl = (struct sockaddr_dl *)ifa->ifa_addr; | sdl = (struct sockaddr_dl *)ifa->ifa_addr; | ||||
sdl->sdl_type = IFT_L2VLAN; | sdl->sdl_type = IFT_L2VLAN; | ||||
if (p != NULL) { | if (p != NULL) { | ||||
error = vlan_config(ifv, p, vid); | error = vlan_config(ifv, p, vid, proto); | ||||
if_rele(p); | if_rele(p); | ||||
if (error != 0) { | if (error != 0) { | ||||
/* | /* | ||||
* Since we've partially failed, we need to back | * Since we've partially failed, we need to back | ||||
* out all the way, otherwise userland could get | * out all the way, otherwise userland could get | ||||
* confused. Thus, we destroy the interface. | * confused. Thus, we destroy the interface. | ||||
*/ | */ | ||||
ether_ifdetach(ifp); | ether_ifdetach(ifp); | ||||
vlan_unconfig(ifp); | vlan_unconfig(ifp); | ||||
if_free(ifp); | if_free(ifp); | ||||
ifc_free_unit(ifc, unit); | ifc_free_unit(ifc, unit); | ||||
free(ifv, M_VLAN); | free(ifv, M_VLAN); | ||||
return (error); | return (error); | ||||
} | } | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
vlan_clone_destroy(struct if_clone *ifc, struct ifnet *ifp) | vlan_clone_destroy(struct if_clone *ifc, struct ifnet *ifp) | ||||
{ | { | ||||
struct ifvlan *ifv = ifp->if_softc; | struct ifvlan *ifv = ifp->if_softc; | ||||
int unit = ifp->if_dunit; | |||||
if (ifp->if_vlantrunk) | |||||
return (EBUSY); | |||||
ether_ifdetach(ifp); /* first, remove it from system-wide lists */ | ether_ifdetach(ifp); /* first, remove it from system-wide lists */ | ||||
vlan_unconfig(ifp); /* now it can be unconfigured and freed */ | vlan_unconfig(ifp); /* now it can be unconfigured and freed */ | ||||
/* | /* | ||||
* We should have the only reference to the ifv now, so we can now | * We should have the only reference to the ifv now, so we can now | ||||
* drain any remaining lladdr task before freeing the ifnet and the | * drain any remaining lladdr task before freeing the ifnet and the | ||||
* ifvlan. | * ifvlan. | ||||
*/ | */ | ||||
taskqueue_drain(taskqueue_thread, &ifv->lladdr_task); | taskqueue_drain(taskqueue_thread, &ifv->lladdr_task); | ||||
NET_EPOCH_WAIT(); | NET_EPOCH_WAIT(); | ||||
if_free(ifp); | if_free(ifp); | ||||
free(ifv, M_VLAN); | free(ifv, M_VLAN); | ||||
ifc_free_unit(ifc, unit); | ifc_free_unit(ifc, ifp->if_dunit); | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* The ifp->if_init entry point for vlan(4) is a no-op. | * The ifp->if_init entry point for vlan(4) is a no-op. | ||||
*/ | */ | ||||
static void | static void | ||||
▲ Show 20 Lines • Show All 49 Lines • ▼ Show 20 Lines | #endif | ||||
* or parent's driver will cause a system crash. | * or parent's driver will cause a system crash. | ||||
*/ | */ | ||||
if (!UP_AND_RUNNING(p)) { | if (!UP_AND_RUNNING(p)) { | ||||
if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); | if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); | ||||
m_freem(m); | m_freem(m); | ||||
return (ENETDOWN); | return (ENETDOWN); | ||||
} | } | ||||
if (!ether_8021q_frame(&m, ifp, p, ifv->ifv_vid, ifv->ifv_pcp)) { | if (!ether_8021q_frame(&m, ifp, p, &ifv->ifv_qtag)) { | ||||
if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); | if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* Send it, precisely as ether_output() would have. | * Send it, precisely as ether_output() would have. | ||||
*/ | */ | ||||
error = (p->if_transmit)(p, m); | error = (p->if_transmit)(p, m); | ||||
Show All 10 Lines | |||||
vlan_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst, | vlan_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst, | ||||
struct route *ro) | struct route *ro) | ||||
{ | { | ||||
struct ifvlan *ifv; | struct ifvlan *ifv; | ||||
struct ifnet *p; | struct ifnet *p; | ||||
NET_EPOCH_ASSERT(); | NET_EPOCH_ASSERT(); | ||||
/* | |||||
* Find the first non-VLAN parent interface. | |||||
*/ | |||||
ifv = ifp->if_softc; | ifv = ifp->if_softc; | ||||
do { | |||||
if (TRUNK(ifv) == NULL) { | if (TRUNK(ifv) == NULL) { | ||||
m_freem(m); | m_freem(m); | ||||
return (ENETDOWN); | return (ENETDOWN); | ||||
} | } | ||||
p = PARENT(ifv); | p = PARENT(ifv); | ||||
ifv = p->if_softc; | |||||
} while (p->if_type == IFT_L2VLAN); | |||||
return p->if_output(ifp, m, dst, ro); | return p->if_output(ifp, m, dst, ro); | ||||
} | } | ||||
/* | /* | ||||
* The ifp->if_qflush entry point for vlan(4) is a no-op. | * The ifp->if_qflush entry point for vlan(4) is a no-op. | ||||
*/ | */ | ||||
static void | static void | ||||
vlan_qflush(struct ifnet *ifp __unused) | vlan_qflush(struct ifnet *ifp __unused) | ||||
▲ Show 20 Lines • Show All 112 Lines • ▼ Show 20 Lines | vlan_lladdr_fn(void *arg, int pending __unused) | ||||
/* The ifv_ifp already has the lladdr copied in. */ | /* The ifv_ifp already has the lladdr copied in. */ | ||||
if_setlladdr(ifp, IF_LLADDR(ifp), ifp->if_addrlen); | if_setlladdr(ifp, IF_LLADDR(ifp), ifp->if_addrlen); | ||||
CURVNET_RESTORE(); | CURVNET_RESTORE(); | ||||
} | } | ||||
static int | static int | ||||
vlan_config(struct ifvlan *ifv, struct ifnet *p, uint16_t vid) | vlan_config(struct ifvlan *ifv, struct ifnet *p, uint16_t vid, | ||||
uint16_t proto) | |||||
{ | { | ||||
struct epoch_tracker et; | struct epoch_tracker et; | ||||
struct ifvlantrunk *trunk; | struct ifvlantrunk *trunk; | ||||
struct ifnet *ifp; | struct ifnet *ifp; | ||||
int error = 0; | int error = 0; | ||||
/* | /* | ||||
* We can handle non-ethernet hardware types as long as | * We can handle non-ethernet hardware types as long as | ||||
* they handle the tagging and headers themselves. | * they handle the tagging and headers themselves. | ||||
*/ | */ | ||||
if (p->if_type != IFT_ETHER && | if (p->if_type != IFT_ETHER && | ||||
p->if_type != IFT_L2VLAN && | |||||
(p->if_capenable & IFCAP_VLAN_HWTAGGING) == 0) | (p->if_capenable & IFCAP_VLAN_HWTAGGING) == 0) | ||||
return (EPROTONOSUPPORT); | return (EPROTONOSUPPORT); | ||||
if ((p->if_flags & VLAN_IFFLAGS) != VLAN_IFFLAGS) | if ((p->if_flags & VLAN_IFFLAGS) != VLAN_IFFLAGS) | ||||
return (EPROTONOSUPPORT); | return (EPROTONOSUPPORT); | ||||
/* | /* | ||||
* Don't let the caller set up a VLAN VID with | * Don't let the caller set up a VLAN VID with | ||||
* anything except VLID bits. | * anything except VLID bits. | ||||
* VID numbers 0x0 and 0xFFF are reserved. | * VID numbers 0x0 and 0xFFF are reserved. | ||||
Show All 15 Lines | if (p->if_vlantrunk == NULL) { | ||||
if_ref(trunk->parent); | if_ref(trunk->parent); | ||||
TRUNK_WUNLOCK(trunk); | TRUNK_WUNLOCK(trunk); | ||||
} else { | } else { | ||||
trunk = p->if_vlantrunk; | trunk = p->if_vlantrunk; | ||||
} | } | ||||
ifv->ifv_vid = vid; /* must set this before vlan_inshash() */ | ifv->ifv_vid = vid; /* must set this before vlan_inshash() */ | ||||
ifv->ifv_pcp = 0; /* Default: best effort delivery. */ | ifv->ifv_pcp = 0; /* Default: best effort delivery. */ | ||||
vlan_tag_recalculate(ifv); | |||||
error = vlan_inshash(trunk, ifv); | error = vlan_inshash(trunk, ifv); | ||||
if (error) | if (error) | ||||
goto done; | goto done; | ||||
ifv->ifv_proto = ETHERTYPE_VLAN; | ifv->ifv_proto = proto; | ||||
ifv->ifv_encaplen = ETHER_VLAN_ENCAP_LEN; | ifv->ifv_encaplen = ETHER_VLAN_ENCAP_LEN; | ||||
ifv->ifv_mintu = ETHERMIN; | ifv->ifv_mintu = ETHERMIN; | ||||
ifv->ifv_pflags = 0; | ifv->ifv_pflags = 0; | ||||
ifv->ifv_capenable = -1; | ifv->ifv_capenable = -1; | ||||
/* | /* | ||||
* If the parent supports the VLAN_MTU capability, | * If the parent supports the VLAN_MTU capability, | ||||
* i.e. can Tx/Rx larger than ETHER_MAX_LEN frames, | * i.e. can Tx/Rx larger than ETHER_MAX_LEN frames, | ||||
▲ Show 20 Lines • Show All 494 Lines • ▼ Show 20 Lines | if (vlr.vlr_parent[0] == '\0') { | ||||
break; | break; | ||||
} | } | ||||
p = ifunit_ref(vlr.vlr_parent); | p = ifunit_ref(vlr.vlr_parent); | ||||
if (p == NULL) { | if (p == NULL) { | ||||
error = ENOENT; | error = ENOENT; | ||||
break; | break; | ||||
} | } | ||||
oldmtu = ifp->if_mtu; | oldmtu = ifp->if_mtu; | ||||
error = vlan_config(ifv, p, vlr.vlr_tag); | error = vlan_config(ifv, p, vlr.vlr_tag, vlr.vlr_proto); | ||||
if_rele(p); | if_rele(p); | ||||
/* | /* | ||||
* VLAN MTU may change during addition of the vlandev. | * VLAN MTU may change during addition of the vlandev. | ||||
* If it did, do network layer specific procedure. | * If it did, do network layer specific procedure. | ||||
*/ | */ | ||||
if (ifp->if_mtu != oldmtu) { | if (ifp->if_mtu != oldmtu) { | ||||
#ifdef INET6 | #ifdef INET6 | ||||
Show All 11 Lines | #ifdef VIMAGE | ||||
} | } | ||||
#endif | #endif | ||||
bzero(&vlr, sizeof(vlr)); | bzero(&vlr, sizeof(vlr)); | ||||
VLAN_SLOCK(); | VLAN_SLOCK(); | ||||
if (TRUNK(ifv) != NULL) { | if (TRUNK(ifv) != NULL) { | ||||
strlcpy(vlr.vlr_parent, PARENT(ifv)->if_xname, | strlcpy(vlr.vlr_parent, PARENT(ifv)->if_xname, | ||||
sizeof(vlr.vlr_parent)); | sizeof(vlr.vlr_parent)); | ||||
vlr.vlr_tag = ifv->ifv_vid; | vlr.vlr_tag = ifv->ifv_vid; | ||||
vlr.vlr_proto = ifv->ifv_proto; | |||||
} | } | ||||
VLAN_SUNLOCK(); | VLAN_SUNLOCK(); | ||||
error = copyout(&vlr, ifr_data_get_ptr(ifr), sizeof(vlr)); | error = copyout(&vlr, ifr_data_get_ptr(ifr), sizeof(vlr)); | ||||
break; | break; | ||||
case SIOCSIFFLAGS: | case SIOCSIFFLAGS: | ||||
/* | /* | ||||
* We should propagate selected flags to the parent, | * We should propagate selected flags to the parent, | ||||
* e.g., promiscuous mode. | * e.g., promiscuous mode. | ||||
*/ | */ | ||||
VLAN_XLOCK(); | VLAN_XLOCK(); | ||||
if (TRUNK(ifv) != NULL) | if (TRUNK(ifv) != NULL) | ||||
error = vlan_setflags(ifp, 1); | error = vlan_setflags(ifp, 1); | ||||
Show All 37 Lines | #endif | ||||
if (error) | if (error) | ||||
break; | break; | ||||
if (ifr->ifr_vlan_pcp > 7) { | if (ifr->ifr_vlan_pcp > 7) { | ||||
error = EINVAL; | error = EINVAL; | ||||
break; | break; | ||||
} | } | ||||
ifv->ifv_pcp = ifr->ifr_vlan_pcp; | ifv->ifv_pcp = ifr->ifr_vlan_pcp; | ||||
ifp->if_pcp = ifv->ifv_pcp; | ifp->if_pcp = ifv->ifv_pcp; | ||||
vlan_tag_recalculate(ifv); | |||||
/* broadcast event about PCP change */ | /* broadcast event about PCP change */ | ||||
EVENTHANDLER_INVOKE(ifnet_event, ifp, IFNET_EVENT_PCP); | EVENTHANDLER_INVOKE(ifnet_event, ifp, IFNET_EVENT_PCP); | ||||
break; | break; | ||||
case SIOCSIFCAP: | case SIOCSIFCAP: | ||||
VLAN_SLOCK(); | VLAN_SLOCK(); | ||||
ifv->ifv_capenable = ifr->ifr_reqcap; | ifv->ifv_capenable = ifr->ifr_reqcap; | ||||
trunk = TRUNK(ifv); | trunk = TRUNK(ifv); | ||||
▲ Show 20 Lines • Show All 92 Lines • Show Last 20 Lines |