summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authoryar <yar@FreeBSD.org>2005-10-03 02:24:21 +0000
committeryar <yar@FreeBSD.org>2005-10-03 02:24:21 +0000
commit7c0072fa916a66a828aa2dd8753f585060eb49de (patch)
tree2169309d59617ac1d0c11c7a29a070222ab6dc65 /sys
parent99e7b443ad74d6a46a5350fe9d9ecfe4bb26315d (diff)
downloadFreeBSD-src-7c0072fa916a66a828aa2dd8753f585060eb49de.zip
FreeBSD-src-7c0072fa916a66a828aa2dd8753f585060eb49de.tar.gz
Improve handling flags that must be propagated
to the parent interface, such as IFF_PROMISC and IFF_ALLMULTI. In addition, vlan(4) gains ability to migrate from one parent to another w/o losing its own flags. PR: kern/81978 MFC after: 2 weeks
Diffstat (limited to 'sys')
-rw-r--r--sys/net/if_vlan.c121
1 files changed, 84 insertions, 37 deletions
diff --git a/sys/net/if_vlan.c b/sys/net/if_vlan.c
index 81ddce8..009d0ca 100644
--- a/sys/net/if_vlan.c
+++ b/sys/net/if_vlan.c
@@ -78,6 +78,7 @@ struct vlan_mc_entry {
struct ifvlan {
struct ifnet *ifv_ifp;
struct ifnet *ifv_p; /* parent inteface of this vlan */
+ int ifv_pflags; /* special flags we have set on parent */
struct ifv_linkmib {
int ifvm_parent;
int ifvm_encaplen; /* encapsulation length */
@@ -88,14 +89,21 @@ struct ifvlan {
} ifv_mib;
SLIST_HEAD(__vlan_mchead, vlan_mc_entry) vlan_mc_listhead;
LIST_ENTRY(ifvlan) ifv_list;
- int ifv_flags;
};
#define ifv_tag ifv_mib.ifvm_tag
#define ifv_encaplen ifv_mib.ifvm_encaplen
#define ifv_mtufudge ifv_mib.ifvm_mtufudge
#define ifv_mintu ifv_mib.ifvm_mintu
-#define IFVF_PROMISC 0x01 /* promiscuous mode enabled */
+/* Special flags we should propagate to parent */
+static struct {
+ int flag;
+ int (*func)(struct ifnet *, int);
+} vlan_pflags[] = {
+ {IFF_PROMISC, ifpromisc},
+ {IFF_ALLMULTI, if_allmulti},
+ {0, NULL}
+};
SYSCTL_DECL(_net_link);
SYSCTL_NODE(_net_link, IFT_L2VLAN, vlan, CTLFLAG_RW, 0, "IEEE 802.1Q VLAN");
@@ -120,11 +128,13 @@ static void vlan_start(struct ifnet *ifp);
static void vlan_ifinit(void *foo);
static void vlan_input(struct ifnet *ifp, struct mbuf *m);
static int vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr);
+static int vlan_setflag(struct ifnet *ifp, int flag, int status,
+ int (*func)(struct ifnet *, int));
+static int vlan_setflags(struct ifnet *ifp, int status);
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);
static struct ifnet *vlan_clone_match_ethertag(struct if_clone *,
const char *, int *);
@@ -416,8 +426,8 @@ vlan_clone_create(struct if_clone *ifc, char *name, size_t len)
ifp->if_drv_flags |= IFF_DRV_RUNNING;
VLAN_UNLOCK();
- /* Update promiscuous mode, if necessary. */
- vlan_set_promisc(ifp);
+ /* Update flags on the parent, if necessary. */
+ vlan_setflags(ifp, 1);
}
return (0);
@@ -651,18 +661,19 @@ static int
vlan_config(struct ifvlan *ifv, struct ifnet *p)
{
struct ifaddr *ifa1, *ifa2;
+ struct ifnet *ifp;
struct sockaddr_dl *sdl1, *sdl2;
VLAN_LOCK_ASSERT();
- if (p->if_data.ifi_type != IFT_ETHER)
+ if (p->if_type != IFT_ETHER)
return (EPROTONOSUPPORT);
if (ifv->ifv_p)
return (EBUSY);
ifv->ifv_encaplen = ETHER_VLAN_ENCAP_LEN;
ifv->ifv_mintu = ETHERMIN;
- ifv->ifv_flags = 0;
+ ifv->ifv_pflags = 0;
/*
* The active VLAN counter on the parent is used
@@ -694,14 +705,19 @@ vlan_config(struct ifvlan *ifv, struct ifnet *p)
}
ifv->ifv_p = p;
- ifv->ifv_ifp->if_mtu = p->if_mtu - ifv->ifv_mtufudge;
+ ifp = ifv->ifv_ifp;
+ ifp->if_mtu = p->if_mtu - ifv->ifv_mtufudge;
/*
* Copy only a selected subset of flags from the parent.
* Other flags are none of our business.
*/
- ifv->ifv_ifp->if_flags = (p->if_flags &
- (IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX | IFF_POINTOPOINT));
- ifv->ifv_ifp->if_link_state = p->if_link_state;
+#define VLAN_COPY_FLAGS \
+ (IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX | IFF_POINTOPOINT)
+ ifp->if_flags &= ~VLAN_COPY_FLAGS;
+ ifp->if_flags |= p->if_flags & VLAN_COPY_FLAGS;
+#undef VLAN_COPY_FLAGS
+
+ ifp->if_link_state = p->if_link_state;
#if 0
/*
@@ -715,27 +731,27 @@ vlan_config(struct ifvlan *ifv, struct ifnet *p)
* assisted checksumming flags.
*/
if (p->if_capabilities & IFCAP_VLAN_HWTAGGING)
- ifv->ifv_ifpif_capabilities |= p->if_capabilities & IFCAP_HWCSUM;
+ ifp->if_capabilities |= p->if_capabilities & IFCAP_HWCSUM;
#endif
/*
* Set up our ``Ethernet address'' to reflect the underlying
* physical interface's.
*/
- ifa1 = ifaddr_byindex(ifv->ifv_ifp->if_index);
+ 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);
- bcopy(LLADDR(sdl2), IFP2ENADDR(ifv->ifv_ifp), ETHER_ADDR_LEN);
+ bcopy(LLADDR(sdl2), IFP2ENADDR(ifp), ETHER_ADDR_LEN);
/*
* Configure multicast addresses that may already be
* joined on the vlan device.
*/
- (void)vlan_setmulti(ifv->ifv_ifp); /* XXX: VLAN lock held */
+ (void)vlan_setmulti(ifp); /* XXX: VLAN lock held */
return (0);
}
@@ -781,13 +797,15 @@ vlan_unconfig(struct ifnet *ifp)
free(mc, M_VLAN);
}
+ vlan_setflags(ifp, 0); /* clear special flags on parent */
p->if_nvlans--;
}
/* Disconnect from parent. */
+ if (ifv->ifv_pflags)
+ if_printf(ifp, "%s: ifv_pflags unclean\n", __func__);
ifv->ifv_p = NULL;
ifv->ifv_ifp->if_mtu = ETHERMTU; /* XXX why not 0? */
- ifv->ifv_flags = 0;
ifv->ifv_ifp->if_link_state = LINK_STATE_UNKNOWN;
/* Clear our MAC address. */
@@ -801,27 +819,56 @@ vlan_unconfig(struct ifnet *ifp)
return (0);
}
+/* Handle a reference counted flag that should be set on the parent as well */
static int
-vlan_set_promisc(struct ifnet *ifp)
+vlan_setflag(struct ifnet *ifp, int flag, int status,
+ int (*func)(struct ifnet *, int))
{
- struct ifvlan *ifv = ifp->if_softc;
- int error = 0;
+ struct ifvlan *ifv;
+ int error;
- if ((ifp->if_flags & IFF_PROMISC) != 0) {
- if ((ifv->ifv_flags & IFVF_PROMISC) == 0) {
- error = ifpromisc(ifv->ifv_p, 1);
- if (error == 0)
- ifv->ifv_flags |= IFVF_PROMISC;
- }
- } else {
- if ((ifv->ifv_flags & IFVF_PROMISC) != 0) {
- error = ifpromisc(ifv->ifv_p, 0);
- if (error == 0)
- ifv->ifv_flags &= ~IFVF_PROMISC;
- }
+ /* XXX VLAN_LOCK_ASSERT(); */
+
+ ifv = ifp->if_softc;
+ status = status ? (ifp->if_flags & flag) : 0;
+ /* Now "status" contains the flag value or 0 */
+
+ /*
+ * See if recorded parent's status is different from what
+ * we want it to be. If it is, flip it. We record parent's
+ * status in ifv_pflags so that we won't clear parent's flag
+ * we haven't set. In fact, we don't clear or set parent's
+ * flags directly, but get or release references to them.
+ * That's why we can be sure that recorded flags still are
+ * in accord with actual parent's flags.
+ */
+ if (status != (ifv->ifv_pflags & flag)) {
+ error = (*func)(ifv->ifv_p, status);
+ if (error)
+ return (error);
+ ifv->ifv_pflags &= ~flag;
+ ifv->ifv_pflags |= status;
}
+ return (0);
+}
- return (error);
+/*
+ * Handle IFF_* flags that require certain changes on the parent:
+ * if "status" is true, update parent's flags respective to our if_flags;
+ * if "status" is false, forcedly clear the flags set on parent.
+ */
+static int
+vlan_setflags(struct ifnet *ifp, int status)
+{
+ int error, i;
+
+ for (i = 0; vlan_pflags[i].flag; i++) {
+ error = vlan_setflag(ifp, vlan_pflags[i].flag,
+ status, vlan_pflags[i].func);
+ if (error)
+ return (error);
+ }
+ return (0);
}
/* Inform all vlans that their parent has changed link state */
@@ -960,8 +1007,8 @@ vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
ifp->if_drv_flags |= IFF_DRV_RUNNING;
VLAN_UNLOCK();
- /* Update promiscuous mode, if necessary. */
- vlan_set_promisc(ifp);
+ /* Update flags on the parent, if necessary. */
+ vlan_setflags(ifp, 1);
break;
case SIOCGETVLAN:
@@ -978,11 +1025,11 @@ vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
case SIOCSIFFLAGS:
/*
- * For promiscuous mode, we enable promiscuous mode on
- * the parent if we need promiscuous on the VLAN interface.
+ * We should propagate selected flags to the parent,
+ * e.g., promiscuous mode.
*/
if (ifv->ifv_p != NULL)
- error = vlan_set_promisc(ifp);
+ error = vlan_setflags(ifp, 1);
break;
case SIOCADDMULTI:
OpenPOWER on IntegriCloud