Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 23 Jul 2015 19:57:48 +0000 (UTC)
From:      Hiroki Sato <hrs@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-10@freebsd.org
Subject:   svn commit: r285824 - stable/10/sys/net
Message-ID:  <201507231957.t6NJvm8o029370@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: hrs
Date: Thu Jul 23 19:57:47 2015
New Revision: 285824
URL: https://svnweb.freebsd.org/changeset/base/285824

Log:
  MFC r279538:
  
  Fix group membership of cloned interfaces when one is moved by
  if_vmove().
  
  In if_vmove(), if_detach_internal() and if_attach_internal() were
  called in series to detach and reattach the interface.  When
  detaching, if_delgroup() was called and the interface leaves all of
  the group membership.  And then upon attachment, if_addgroup(ifp,
  IFG_ALL) was called and it joined only "all" group again.
  
  This had a problem. Normally, a cloned interface automatically joins
  a group whose name is ifc_name of the cloner in addition to "all"
  upon creation.  However, if_vmove() removed the membership and did
  not restore upon attachment.
  
  Approved by:	re (gjb)

Modified:
  stable/10/sys/net/if.c
  stable/10/sys/net/if_clone.c
  stable/10/sys/net/if_clone.h
Directory Properties:
  stable/10/   (props changed)

Modified: stable/10/sys/net/if.c
==============================================================================
--- stable/10/sys/net/if.c	Thu Jul 23 19:55:46 2015	(r285823)
+++ stable/10/sys/net/if.c	Thu Jul 23 19:57:47 2015	(r285824)
@@ -175,8 +175,8 @@ static void	do_link_state_change(void *,
 static int	if_getgroup(struct ifgroupreq *, struct ifnet *);
 static int	if_getgroupmembers(struct ifgroupreq *);
 static void	if_delgroups(struct ifnet *);
-static void	if_attach_internal(struct ifnet *, int);
-static void	if_detach_internal(struct ifnet *, int);
+static void	if_attach_internal(struct ifnet *, int, struct if_clone *);
+static void	if_detach_internal(struct ifnet *, int, struct if_clone **);
 
 #ifdef INET6
 /*
@@ -571,6 +571,15 @@ ifq_delete(struct ifaltq *ifq)
  * tasks, given that we are moving from one vnet to another an ifnet which
  * has already been fully initialized.
  *
+ * Note that if_detach_internal() removes group membership unconditionally
+ * even when vmove flag is set, and if_attach_internal() adds only IFG_ALL.
+ * Thus, when if_vmove() is applied to a cloned interface, group membership
+ * is lost while a cloned one always joins a group whose name is
+ * ifc->ifc_name.  To recover this after if_detach_internal() and
+ * if_attach_internal(), the cloner should be specified to
+ * if_attach_internal() via ifc.  If it is non-NULL, if_attach_internal()
+ * attempts to join a group whose name is ifc->ifc_name.
+ *
  * XXX:
  *  - The decision to return void and thus require this function to
  *    succeed is questionable.
@@ -581,7 +590,7 @@ void
 if_attach(struct ifnet *ifp)
 {
 
-	if_attach_internal(ifp, 0);
+	if_attach_internal(ifp, 0, NULL);
 }
 
 /*
@@ -636,7 +645,7 @@ if_hw_tsomax_update(struct ifnet *ifp, s
 }
 
 static void
-if_attach_internal(struct ifnet *ifp, int vmove)
+if_attach_internal(struct ifnet *ifp, int vmove, struct if_clone *ifc)
 {
 	unsigned socksize, ifasize;
 	int namelen, masklen;
@@ -655,6 +664,10 @@ if_attach_internal(struct ifnet *ifp, in
 
 	if_addgroup(ifp, IFG_ALL);
 
+	/* Restore group membership for cloned interfaces. */
+	if (vmove && ifc != NULL)
+		if_clone_addgroup(ifp, ifc);
+
 	getmicrotime(&ifp->if_lastchange);
 	ifp->if_data.ifi_epoch = time_uptime;
 	ifp->if_data.ifi_datalen = sizeof(struct if_data);
@@ -877,12 +890,12 @@ if_detach(struct ifnet *ifp)
 {
 
 	CURVNET_SET_QUIET(ifp->if_vnet);
-	if_detach_internal(ifp, 0);
+	if_detach_internal(ifp, 0, NULL);
 	CURVNET_RESTORE();
 }
 
 static void
-if_detach_internal(struct ifnet *ifp, int vmove)
+if_detach_internal(struct ifnet *ifp, int vmove, struct if_clone **ifcp)
 {
 	struct ifaddr *ifa;
 	struct radix_node_head	*rnh;
@@ -911,6 +924,10 @@ if_detach_internal(struct ifnet *ifp, in
 			return; /* XXX this should panic as well? */
 	}
 
+	/* Check if this is a cloned interface or not. */
+	if (vmove && ifcp != NULL)
+		*ifcp = if_clone_findifc(ifp);
+
 	/*
 	 * Remove/wait for pending events.
 	 */
@@ -1016,12 +1033,13 @@ void
 if_vmove(struct ifnet *ifp, struct vnet *new_vnet)
 {
 	u_short idx;
+	struct if_clone *ifc;
 
 	/*
 	 * Detach from current vnet, but preserve LLADDR info, do not
 	 * mark as dead etc. so that the ifnet can be reattached later.
 	 */
-	if_detach_internal(ifp, 1);
+	if_detach_internal(ifp, 1, &ifc);
 
 	/*
 	 * Unlink the ifnet from ifindex_table[] in current vnet, and shrink
@@ -1055,7 +1073,7 @@ if_vmove(struct ifnet *ifp, struct vnet 
 	ifnet_setbyindex_locked(ifp->if_index, ifp);
 	IFNET_WUNLOCK();
 
-	if_attach_internal(ifp, 1);
+	if_attach_internal(ifp, 1, ifc);
 
 	CURVNET_RESTORE();
 }

Modified: stable/10/sys/net/if_clone.c
==============================================================================
--- stable/10/sys/net/if_clone.c	Thu Jul 23 19:55:46 2015	(r285823)
+++ stable/10/sys/net/if_clone.c	Thu Jul 23 19:57:47 2015	(r285824)
@@ -525,6 +525,49 @@ done:
 }
 
 /*
+ * if_clone_findifc() looks up ifnet from the current
+ * cloner list, and returns ifc if found.  Note that ifc_refcnt
+ * is incremented.
+ */
+struct if_clone *
+if_clone_findifc(struct ifnet *ifp)
+{
+	struct if_clone *ifc, *ifc0;
+	struct ifnet *ifcifp;
+
+	ifc0 = NULL;
+	IF_CLONERS_LOCK();
+	LIST_FOREACH(ifc, &V_if_cloners, ifc_list) {
+		IF_CLONE_LOCK(ifc);
+		LIST_FOREACH(ifcifp, &ifc->ifc_iflist, if_clones) {
+			if (ifp == ifcifp) {
+				ifc0 = ifc;
+				IF_CLONE_ADDREF_LOCKED(ifc);
+				break;
+			}
+		}
+		IF_CLONE_UNLOCK(ifc);
+		if (ifc0 != NULL)
+			break;
+	}
+	IF_CLONERS_UNLOCK();
+
+	return (ifc0);
+}
+
+/*
+ * if_clone_addgroup() decrements ifc_refcnt because it is called after
+ * if_clone_findifc().
+ */
+void
+if_clone_addgroup(struct ifnet *ifp, struct if_clone *ifc)
+{
+
+	if_addgroup(ifp, ifc->ifc_name);
+	IF_CLONE_REMREF(ifc);
+}
+
+/*
  * A utility function to extract unit numbers from interface names of
  * the form name###.
  *

Modified: stable/10/sys/net/if_clone.h
==============================================================================
--- stable/10/sys/net/if_clone.h	Thu Jul 23 19:55:46 2015	(r285823)
+++ stable/10/sys/net/if_clone.h	Thu Jul 23 19:57:47 2015	(r285824)
@@ -68,6 +68,8 @@ void	vnet_if_clone_init(void);
 int	if_clone_create(char *, size_t, caddr_t);
 int	if_clone_destroy(const char *);
 int	if_clone_list(struct if_clonereq *);
+struct if_clone *if_clone_findifc(struct ifnet *);
+void	if_clone_addgroup(struct ifnet *, struct if_clone *);
 
 /* The below interface used only by epair(4). */
 int	if_clone_destroyif(struct if_clone *, struct ifnet *);



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201507231957.t6NJvm8o029370>