Changeset View
Changeset View
Standalone View
Standalone View
sys/net/if_vlan.c
Show First 20 Lines • Show All 320 Lines • ▼ Show 20 Lines | |||||
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, | static int vlan_clone_create(struct if_clone *, char *, size_t, | ||||
struct ifc_data *, struct ifnet **); | struct ifc_data *, struct ifnet **); | ||||
static int vlan_clone_destroy(struct if_clone *, struct ifnet *, uint32_t); | static int vlan_clone_destroy(struct if_clone *, struct ifnet *, uint32_t); | ||||
static int vlan_clone_vmove(struct if_clone *, struct ifnet *, | |||||
struct ifc_data *, struct ifnet **); | |||||
static void vlan_ifdetach(void *arg, struct ifnet *ifp); | static void vlan_ifdetach(void *arg, struct ifnet *ifp); | ||||
static void vlan_iflladdr(void *arg, struct ifnet *ifp); | static void vlan_iflladdr(void *arg, struct ifnet *ifp); | ||||
static void vlan_ifevent(void *arg, struct ifnet *ifp, int event); | static void vlan_ifevent(void *arg, struct ifnet *ifp, int event); | ||||
static void vlan_lladdr_fn(void *arg, int pending); | static void vlan_lladdr_fn(void *arg, int pending); | ||||
static struct if_clone *vlan_cloner; | static struct if_clone *vlan_cloner; | ||||
▲ Show 20 Lines • Show All 565 Lines • ▼ Show 20 Lines | |||||
/* For if_link_state_change() eyes only... */ | /* For if_link_state_change() eyes only... */ | ||||
extern void (*vlan_link_state_p)(struct ifnet *); | extern void (*vlan_link_state_p)(struct ifnet *); | ||||
static struct if_clone_addreq vlan_addreq = { | static struct if_clone_addreq vlan_addreq = { | ||||
.match_f = vlan_clone_match, | .match_f = vlan_clone_match, | ||||
.create_f = vlan_clone_create, | .create_f = vlan_clone_create, | ||||
.destroy_f = vlan_clone_destroy, | .destroy_f = vlan_clone_destroy, | ||||
.vmove_f = vlan_clone_vmove, | |||||
}; | }; | ||||
static int | static int | ||||
vlan_modevent(module_t mod, int type, void *data) | vlan_modevent(module_t mod, int type, void *data) | ||||
{ | { | ||||
switch (type) { | switch (type) { | ||||
case MOD_LOAD: | case MOD_LOAD: | ||||
▲ Show 20 Lines • Show All 138 Lines • ▼ Show 20 Lines | vlan_clone_match(struct if_clone *ifc, const char *name) | ||||
for (cp = name + 4; *cp != '\0'; cp++) { | for (cp = name + 4; *cp != '\0'; cp++) { | ||||
if (*cp < '0' || *cp > '9') | if (*cp < '0' || *cp > '9') | ||||
return (0); | return (0); | ||||
} | } | ||||
return (1); | return (1); | ||||
} | } | ||||
struct vlanparams { | |||||
struct ifnet *parent; | |||||
int vid; | |||||
uint16_t proto; | |||||
uint8_t pcp; | |||||
}; | |||||
static int | static int | ||||
vlan_create_specific(struct if_clone *ifc, char *name, int unit, | |||||
struct vlanparams *vp, struct ifnet **ifpp); | |||||
static int | |||||
vlan_clone_create(struct if_clone *ifc, char *name, size_t len, | vlan_clone_create(struct if_clone *ifc, char *name, size_t len, | ||||
struct ifc_data *ifd, struct ifnet **ifpp) | struct ifc_data *ifd, struct ifnet **ifpp) | ||||
{ | { | ||||
char *dp; | char *dp; | ||||
bool wildcard = false; | bool wildcard = false; | ||||
bool subinterface = false; | bool subinterface = false; | ||||
int unit; | int unit; | ||||
int error; | int error; | ||||
int vid = 0; | int vid = 0; | ||||
uint16_t proto = ETHERTYPE_VLAN; | uint16_t proto = ETHERTYPE_VLAN; | ||||
struct ifvlan *ifv; | |||||
struct ifnet *ifp; | |||||
struct ifnet *p = NULL; | struct ifnet *p = NULL; | ||||
struct ifaddr *ifa; | |||||
struct sockaddr_dl *sdl; | |||||
struct vlanreq vlr; | struct vlanreq vlr; | ||||
static const u_char eaddr[ETHER_ADDR_LEN]; /* 00:00:00:00:00:00 */ | |||||
/* | /* | ||||
* There are three ways to specify the cloned device: | * There are three 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 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. | ||||
▲ Show 20 Lines • Show All 64 Lines • ▼ Show 20 Lines | #endif | ||||
if (wildcard) { | if (wildcard) { | ||||
for (dp = name; *dp != '\0'; dp++); | for (dp = name; *dp != '\0'; dp++); | ||||
if (snprintf(dp, len - (dp-name), "%d", unit) > | if (snprintf(dp, len - (dp-name), "%d", unit) > | ||||
len - (dp-name) - 1) { | len - (dp-name) - 1) { | ||||
panic("%s: interface name too long", __func__); | panic("%s: interface name too long", __func__); | ||||
} | } | ||||
} | } | ||||
ifv = malloc(sizeof(struct ifvlan), M_VLAN, M_WAITOK | M_ZERO); | struct vlanparams vp = { | ||||
ifp = ifv->ifv_ifp = if_alloc(IFT_ETHER); | .parent = p, | ||||
if (ifp == NULL) { | .vid = vid, | ||||
.proto = proto, | |||||
}; | |||||
error = vlan_create_specific(ifc, name, unit, &vp, ifpp); | |||||
if (error != 0) { | |||||
if (!subinterface) | if (!subinterface) | ||||
ifc_free_unit(ifc, unit); | ifc_free_unit(ifc, unit); | ||||
free(ifv, M_VLAN); | } | ||||
if (p != NULL) | if (p != NULL) | ||||
if_rele(p); | if_rele(p); | ||||
return (error); | |||||
} | |||||
static int | |||||
vlan_create_specific(struct if_clone *ifc, char *name, int unit, | |||||
struct vlanparams *vp, struct ifnet **ifpp) | |||||
{ | |||||
struct ifvlan *ifv; | |||||
struct ifnet *ifp; | |||||
struct ifaddr *ifa; | |||||
struct sockaddr_dl *sdl; | |||||
static const u_char eaddr[ETHER_ADDR_LEN]; /* 00:00:00:00:00:00 */ | |||||
int error; | |||||
ifv = malloc(sizeof(struct ifvlan), M_VLAN, M_WAITOK | M_ZERO); | |||||
ifp = ifv->ifv_ifp = if_alloc(IFT_ETHER); | |||||
if (ifp == NULL) { | |||||
free(ifv, M_VLAN); | |||||
return (ENOSPC); | return (ENOSPC); | ||||
} | } | ||||
CK_SLIST_INIT(&ifv->vlan_mc_listhead); | CK_SLIST_INIT(&ifv->vlan_mc_listhead); | ||||
ifp->if_softc = ifv; | ifp->if_softc = ifv; | ||||
/* | /* | ||||
* Set the name manually rather than using if_initname because | * Set the name manually rather than using if_initname because | ||||
* we don't conform to the default naming convention for interfaces. | * we don't conform to the default naming convention for interfaces. | ||||
*/ | */ | ||||
Show All 22 Lines | #endif | ||||
/* Now undo some of the damage... */ | /* Now undo some of the damage... */ | ||||
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 (vp->parent != NULL) { | ||||
error = vlan_config(ifv, p, vid, proto); | error = vlan_config(ifv, vp->parent, vp->vid, vp->proto); | ||||
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); | ||||
if (!subinterface) | |||||
ifc_free_unit(ifc, unit); | |||||
free(ifv, M_VLAN); | free(ifv, M_VLAN); | ||||
return (error); | return (error); | ||||
} | } | ||||
} | } | ||||
*ifpp = ifp; | *ifpp = ifp; | ||||
return (0); | return (0); | ||||
Show All 23 Lines | #endif | ||||
if_free(ifp); | if_free(ifp); | ||||
free(ifv, M_VLAN); | free(ifv, M_VLAN); | ||||
if (unit != IF_DUNIT_NONE) | if (unit != IF_DUNIT_NONE) | ||||
ifc_free_unit(ifc, unit); | ifc_free_unit(ifc, unit); | ||||
return (0); | return (0); | ||||
} | } | ||||
static int | |||||
vlan_clone_vmove(struct if_clone *ifc, struct ifnet *ifp_src, | |||||
struct ifc_data *ifd, struct ifnet **ifpp) | |||||
{ | |||||
struct ifc_vparam_data vdata = {}; | |||||
struct ifvlan *ifv; | |||||
int error; | |||||
VLAN_XLOCK(); | |||||
/* Copy necessary data to re-create the interface */ | |||||
ifc_save_vparams(ifp_src, &vdata); | |||||
ifv = ifp_src->if_softc; | |||||
struct vlanparams vp = { | |||||
.parent = (ifv->ifv_trunk != NULL) ? ifv->ifv_trunk->parent : NULL, | |||||
.vid = ifv->ifv_vid, | |||||
.proto = ifv->ifv_proto, | |||||
}; | |||||
error = if_clone_destroyif(ifc, ifp_src); | |||||
VLAN_XUNLOCK(); | |||||
ifp_src = NULL; | |||||
if (error != 0) | |||||
return (error); | |||||
CURVNET_SET_QUIET(ifd->vnet); | |||||
VLAN_XLOCK(); | |||||
error = vlan_create_specific(ifc, vdata.ifname, | |||||
vdata.unit, &vp, ifpp); | |||||
VLAN_XUNLOCK(); | |||||
ifc_apply_vparams(*ifpp, &vdata); | |||||
CURVNET_RESTORE(); | |||||
return (error); | |||||
} | |||||
/* | /* | ||||
* 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 | ||||
vlan_init(void *foo __unused) | vlan_init(void *foo __unused) | ||||
{ | { | ||||
} | } | ||||
▲ Show 20 Lines • Show All 245 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_locked(struct ifvlan *ifv, struct ifnet *p, uint16_t vid, | ||||
uint16_t proto) | 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; | ||||
/* | /* | ||||
Show All 13 Lines | vlan_config_locked(struct ifvlan *ifv, struct ifnet *p, uint16_t vid, | ||||
*/ | */ | ||||
if (vid == 0 || vid == 0xFFF || (vid & ~EVL_VLID_MASK)) | if (vid == 0 || vid == 0xFFF || (vid & ~EVL_VLID_MASK)) | ||||
return (EINVAL); | return (EINVAL); | ||||
if (ifv->ifv_trunk) { | if (ifv->ifv_trunk) { | ||||
trunk = ifv->ifv_trunk; | trunk = ifv->ifv_trunk; | ||||
if (trunk->parent != p) | if (trunk->parent != p) | ||||
return (EBUSY); | return (EBUSY); | ||||
VLAN_XLOCK(); | |||||
ifv->ifv_proto = proto; | ifv->ifv_proto = proto; | ||||
if (ifv->ifv_vid != vid) { | if (ifv->ifv_vid != vid) { | ||||
/* Re-hash */ | /* Re-hash */ | ||||
vlan_remhash(trunk, ifv); | vlan_remhash(trunk, ifv); | ||||
ifv->ifv_vid = vid; | ifv->ifv_vid = vid; | ||||
error = vlan_inshash(trunk, ifv); | error = vlan_inshash(trunk, ifv); | ||||
} | } | ||||
/* Will unlock */ | return (error); | ||||
goto done; | |||||
} | } | ||||
VLAN_XLOCK(); | |||||
if (p->if_vlantrunk == NULL) { | if (p->if_vlantrunk == NULL) { | ||||
trunk = malloc(sizeof(struct ifvlantrunk), | trunk = malloc(sizeof(struct ifvlantrunk), | ||||
M_VLAN, M_WAITOK | M_ZERO); | M_VLAN, M_WAITOK | M_ZERO); | ||||
vlan_inithash(trunk); | vlan_inithash(trunk); | ||||
TRUNK_LOCK_INIT(trunk); | TRUNK_LOCK_INIT(trunk); | ||||
TRUNK_WLOCK(trunk); | TRUNK_WLOCK(trunk); | ||||
p->if_vlantrunk = trunk; | p->if_vlantrunk = trunk; | ||||
trunk->parent = p; | trunk->parent = p; | ||||
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. */ | ||||
error = vlan_inshash(trunk, ifv); | error = vlan_inshash(trunk, ifv); | ||||
if (error) | if (error != 0) | ||||
goto done; | return (error); | ||||
ifv->ifv_proto = proto; | 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, | ||||
▲ Show 20 Lines • Show All 78 Lines • ▼ Show 20 Lines | #undef VLAN_COPY_FLAGS | ||||
vlan_setflags(ifp, 1); | vlan_setflags(ifp, 1); | ||||
/* | /* | ||||
* Configure multicast addresses that may already be | * Configure multicast addresses that may already be | ||||
* joined on the vlan device. | * joined on the vlan device. | ||||
*/ | */ | ||||
(void)vlan_setmulti(ifp); | (void)vlan_setmulti(ifp); | ||||
done: | return (error); | ||||
} | |||||
static int | |||||
vlan_config(struct ifvlan *ifv, struct ifnet *p, uint16_t vid, | |||||
uint16_t proto) | |||||
{ | |||||
VLAN_XLOCK(); | |||||
int error = vlan_config_locked(ifv, p, vid, proto); | |||||
if (error == 0) | if (error == 0) | ||||
EVENTHANDLER_INVOKE(vlan_config, p, ifv->ifv_vid); | EVENTHANDLER_INVOKE(vlan_config, p, ifv->ifv_vid); | ||||
VLAN_XUNLOCK(); | VLAN_XUNLOCK(); | ||||
return (error); | return (error); | ||||
} | } | ||||
static void | static void | ||||
▲ Show 20 Lines • Show All 669 Lines • Show Last 20 Lines |