summaryrefslogtreecommitdiffstats
path: root/sys/net/if_vlan.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/net/if_vlan.c')
-rw-r--r--sys/net/if_vlan.c151
1 files changed, 142 insertions, 9 deletions
diff --git a/sys/net/if_vlan.c b/sys/net/if_vlan.c
index f9c22d2..9449dec 100644
--- a/sys/net/if_vlan.c
+++ b/sys/net/if_vlan.c
@@ -57,6 +57,7 @@
#include <net/bpf.h>
#include <net/ethernet.h>
#include <net/if.h>
+#include <net/if_clone.h>
#include <net/if_arp.h>
#include <net/if_dl.h>
#include <net/if_types.h>
@@ -117,8 +118,6 @@ static struct mtx ifv_mtx;
#define VLAN_LOCK() mtx_lock(&ifv_mtx)
#define VLAN_UNLOCK() mtx_unlock(&ifv_mtx)
-static int vlan_clone_create(struct if_clone *, int);
-static void vlan_clone_destroy(struct ifnet *);
static void vlan_start(struct ifnet *ifp);
static void vlan_ifinit(void *foo);
static void vlan_input(struct ifnet *ifp, struct mbuf *m);
@@ -127,9 +126,16 @@ static int vlan_setmulti(struct ifnet *ifp);
static int vlan_unconfig(struct ifnet *ifp);
static int vlan_config(struct ifvlan *ifv, struct ifnet *p);
static void vlan_link_state(struct ifnet *ifp, int link);
+static int vlan_set_promisc(struct ifnet *ifp);
-struct if_clone vlan_cloner = IF_CLONE_INITIALIZER(VLANNAME,
- vlan_clone_create, vlan_clone_destroy, 0, IF_MAXUNIT);
+static struct ifnet *vlan_clone_match_ethertag(struct if_clone *,
+ const char *, int *);
+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_destroy(struct if_clone *, struct ifnet *);
+
+struct if_clone vlan_cloner = IFC_CLONE_INITIALIZER(VLANNAME, NULL, IF_MAXUNIT,
+ NULL, vlan_clone_match, vlan_clone_create, vlan_clone_destroy);
/*
* Program our multicast filter. What we're actually doing is
@@ -231,7 +237,8 @@ vlan_modevent(module_t mod, int type, void *data)
vlan_input_p = NULL;
vlan_link_state_p = NULL;
while (!LIST_EMPTY(&ifv_list))
- vlan_clone_destroy(&LIST_FIRST(&ifv_list)->ifv_if);
+ vlan_clone_destroy(&vlan_cloner,
+ &LIST_FIRST(&ifv_list)->ifv_if);
VLAN_LOCK_DESTROY();
break;
}
@@ -247,18 +254,117 @@ static moduledata_t vlan_mod = {
DECLARE_MODULE(if_vlan, vlan_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
MODULE_DEPEND(if_vlan, miibus, 1, 1, 1);
+static struct ifnet *
+vlan_clone_match_ethertag(struct if_clone *ifc, const char *name, int *tag)
+{
+ int t;
+ const char *cp;
+ struct ifnet *ifp;
+
+ t = 0;
+
+ /* Check for <etherif>.<vlan> style interface names. */
+ IFNET_RLOCK();
+ TAILQ_FOREACH(ifp, &ifnet, if_link) {
+ if (ifp->if_type != IFT_ETHER)
+ continue;
+ if (strncmp(ifp->if_xname, name, strlen(ifp->if_xname)) != 0)
+ continue;
+ cp = name + strlen(ifp->if_xname);
+ if (*cp != '.')
+ continue;
+ for(; *cp != '\0'; cp++) {
+ if (*cp < '0' || *cp > '9')
+ continue;
+ t = (t * 10) + (*cp - '0');
+ }
+ if (tag != NULL)
+ *tag = t;
+ break;
+ }
+ IFNET_RUNLOCK();
+
+ return ifp;
+}
+
+static int
+vlan_clone_match(struct if_clone *ifc, const char *name)
+{
+ const char *cp;
+
+ if (vlan_clone_match_ethertag(ifc, name, NULL) != NULL)
+ return (1);
+
+ if (strncmp(VLANNAME, name, strlen(VLANNAME)) != 0)
+ return (0);
+ for (cp = name + 4; *cp != '\0'; cp++) {
+ if (*cp < '0' || *cp > '9')
+ return (0);
+ }
+
+ return (1);
+}
+
static int
-vlan_clone_create(struct if_clone *ifc, int unit)
+vlan_clone_create(struct if_clone *ifc, char *name, size_t len)
{
+ char *dp;
+ int wildcard;
+ int unit;
+ int error;
+ int tag;
+ int ethertag;
struct ifvlan *ifv;
struct ifnet *ifp;
+ struct ifnet *p;
+
+ if ((p = vlan_clone_match_ethertag(ifc, name, &tag)) != NULL) {
+ ethertag = 1;
+ unit = -1;
+ wildcard = 0;
+
+ /*
+ * Don't let the caller set up a VLAN tag with
+ * anything except VLID bits.
+ */
+ if (tag & ~EVL_VLID_MASK) {
+ return (EINVAL);
+ }
+ } else {
+ ethertag = 0;
+
+ error = ifc_name2unit(name, &unit);
+ if (error != 0)
+ return (error);
+
+ wildcard = (unit < 0);
+ }
+
+ error = ifc_alloc_unit(ifc, &unit);
+ if (error != 0)
+ return (error);
+
+ /* In the wildcard case, we need to update the name. */
+ if (wildcard) {
+ for (dp = name; *dp != '\0'; dp++);
+ if (snprintf(dp, len - (dp-name), "%d", unit) >
+ len - (dp-name) - 1) {
+ panic("%s: interface name too long", __func__);
+ }
+ }
ifv = malloc(sizeof(struct ifvlan), M_VLAN, M_WAITOK | M_ZERO);
ifp = &ifv->ifv_if;
SLIST_INIT(&ifv->vlan_mc_listhead);
ifp->if_softc = ifv;
- if_initname(ifp, ifc->ifc_name, unit);
+ /*
+ * Set the name manually rather then using if_initname because
+ * we don't conform to the default naming convention for interfaces.
+ */
+ strlcpy(ifp->if_xname, name, IFNAMSIZ);
+ ifp->if_dname = ifc->ifc_name;
+ ifp->if_dunit = unit;
/* NB: flags are not set here */
ifp->if_linkmib = &ifv->ifv_mib;
ifp->if_linkmiblen = sizeof ifv->ifv_mib;
@@ -278,11 +384,36 @@ vlan_clone_create(struct if_clone *ifc, int unit)
LIST_INSERT_HEAD(&ifv_list, ifv, ifv_list);
VLAN_UNLOCK();
+ if (ethertag) {
+ VLAN_LOCK();
+ error = vlan_config(ifv, p);
+ if (error != 0) {
+ /*
+ * Since we've partialy failed, we need to back
+ * out all the way, otherwise userland could get
+ * confused. Thus, we destroy the interface.
+ */
+ LIST_REMOVE(ifv, ifv_list);
+ vlan_unconfig(ifp);
+ VLAN_UNLOCK();
+ ether_ifdetach(ifp);
+ free(ifv, M_VLAN);
+
+ return (error);
+ }
+ ifv->ifv_tag = tag;
+ ifp->if_flags |= IFF_RUNNING;
+ VLAN_UNLOCK();
+
+ /* Update promiscuous mode, if necessary. */
+ vlan_set_promisc(ifp);
+ }
+
return (0);
}
-static void
-vlan_clone_destroy(struct ifnet *ifp)
+static int
+vlan_clone_destroy(struct if_clone *ifc, struct ifnet *ifp)
{
struct ifvlan *ifv = ifp->if_softc;
@@ -294,6 +425,8 @@ vlan_clone_destroy(struct ifnet *ifp)
ether_ifdetach(ifp);
free(ifv, M_VLAN);
+
+ return (0);
}
static void
OpenPOWER on IntegriCloud