diff --git a/sys/net/if_clone.c b/sys/net/if_clone.c --- a/sys/net/if_clone.c +++ b/sys/net/if_clone.c @@ -571,7 +571,7 @@ /* * A utility function to extract unit numbers from interface names of - * the form name###[.###]. + * the form name###. * * Returns 0 on success and an error on failure. */ @@ -582,9 +582,8 @@ int cutoff = INT_MAX / 10; int cutlim = INT_MAX % 10; - if ((cp = strrchr(name, '.')) == NULL) - cp = name; - for (; *cp != '\0' && (*cp < '0' || *cp > '9'); cp++); + for (cp = name; *cp != '\0' && (*cp < '0' || *cp > '9'); cp++) + ; if (*cp == '\0') { *unit = -1; } else if (cp[0] == '0' && cp[1] != '\0') { diff --git a/sys/net/if_vlan.c b/sys/net/if_vlan.c --- a/sys/net/if_vlan.c +++ b/sys/net/if_vlan.c @@ -980,63 +980,86 @@ vlan_clone_create(struct if_clone *ifc, char *name, size_t len, caddr_t params) { char *dp; - int wildcard; + bool wildcard = false; + bool subinterface = false; int unit; int error; - int vid; - uint16_t proto; + int vid = 0; + uint16_t proto = ETHERTYPE_VLAN; struct ifvlan *ifv; struct ifnet *ifp; - struct ifnet *p; + struct ifnet *p = NULL; struct ifaddr *ifa; struct sockaddr_dl *sdl; struct vlanreq vlr; static const u_char eaddr[ETHER_ADDR_LEN]; /* 00:00:00:00:00:00 */ - proto = ETHERTYPE_VLAN; /* - * There are two 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 specify parameters in the text of the clone device name * o specify no parameters and get an unattached device that * must be configured separately. - * The first technique is preferred; the latter is supported + * The first technique is preferred; the latter two are supported * for backwards compatibility. * * XXXRW: Note historic use of the word "tag" here. New ioctls may be * called for. */ + if (params) { error = copyin(params, &vlr, sizeof(vlr)); if (error) return error; + vid = vlr.vlr_tag; + proto = vlr.vlr_proto; + p = ifunit_ref(vlr.vlr_parent); if (p == NULL) return (ENXIO); - error = ifc_name2unit(name, &unit); - if (error != 0) { - if_rele(p); - return (error); - } - vid = vlr.vlr_tag; - proto = vlr.vlr_proto; - wildcard = (unit < 0); - } else { - p = NULL; - error = ifc_name2unit(name, &unit); - if (error != 0) - return (error); + } + + if ((error = ifc_name2unit(name, &unit)) == 0) { + /* + * vlanX interface. Set wildcard to true if the unit number + * is not fixed (-1) + */ wildcard = (unit < 0); + } else { + struct ifnet *p_tmp = vlan_clone_match_ethervid(name, &vid); + if (p_tmp != NULL) { + error = 0; + subinterface = true; + unit = IF_DUNIT_NONE; + wildcard = false; + if (p != NULL) { + if_rele(p_tmp); + if (p != p_tmp) + error = EINVAL; + } else + p = p_tmp; + } else + error = ENXIO; } - error = ifc_alloc_unit(ifc, &unit); if (error != 0) { if (p != NULL) if_rele(p); return (error); } + if (!subinterface) { + /* vlanX interface, mark X as busy or allocate new unit # */ + error = ifc_alloc_unit(ifc, &unit); + if (error != 0) { + if (p != NULL) + if_rele(p); + return (error); + } + } + /* In the wildcard case, we need to update the name. */ if (wildcard) { for (dp = name; *dp != '\0'; dp++); @@ -1049,7 +1072,8 @@ ifv = malloc(sizeof(struct ifvlan), M_VLAN, M_WAITOK | M_ZERO); ifp = ifv->ifv_ifp = if_alloc(IFT_ETHER); if (ifp == NULL) { - ifc_free_unit(ifc, unit); + if (!subinterface) + ifc_free_unit(ifc, unit); free(ifv, M_VLAN); if (p != NULL) if_rele(p); @@ -1099,7 +1123,8 @@ ether_ifdetach(ifp); vlan_unconfig(ifp); if_free(ifp); - ifc_free_unit(ifc, unit); + if (!subinterface) + ifc_free_unit(ifc, unit); free(ifv, M_VLAN); return (error); @@ -1113,6 +1138,7 @@ vlan_clone_destroy(struct if_clone *ifc, struct ifnet *ifp) { struct ifvlan *ifv = ifp->if_softc; + int unit = ifp->if_dunit; if (ifp->if_vlantrunk) return (EBUSY); @@ -1128,7 +1154,8 @@ NET_EPOCH_WAIT(); if_free(ifp); free(ifv, M_VLAN); - ifc_free_unit(ifc, ifp->if_dunit); + if (unit != IF_DUNIT_NONE) + ifc_free_unit(ifc, unit); return (0); }