Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 18 Jan 2014 15:52:53 +0000 (UTC)
From:      "Alexander V. Chernikov" <melifaro@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r260851 - head/sys/netinet6
Message-ID:  <201401181552.s0IFqr3k021464@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: melifaro
Date: Sat Jan 18 15:52:52 2014
New Revision: 260851
URL: http://svnweb.freebsd.org/changeset/base/260851

Log:
  Split in6_update_ifa() into smaller pieces leaving functionality intact.
  
  Discussed with:	ae
  MFC after:	2 weeks

Modified:
  head/sys/netinet6/in6.c

Modified: head/sys/netinet6/in6.c
==============================================================================
--- head/sys/netinet6/in6.c	Sat Jan 18 14:47:34 2014	(r260850)
+++ head/sys/netinet6/in6.c	Sat Jan 18 15:52:52 2014	(r260851)
@@ -139,6 +139,15 @@ static void in6_unlink_ifa(struct in6_if
 
 int	(*faithprefix_p)(struct in6_addr *);
 
+static int in6_validate_ifra(struct ifnet *, struct in6_aliasreq *,
+    struct in6_ifaddr *, int);
+static struct in6_ifaddr *in6_alloc_ifa(struct ifnet *,
+    struct in6_aliasreq *, int flags);
+static int in6_update_ifa_internal(struct ifnet *, struct in6_aliasreq *,
+    struct in6_ifaddr *, int, int);
+static int in6_setup_ifa(struct ifnet *, struct in6_aliasreq *,
+    struct in6_ifaddr *, int);
+
 #define ifa2ia6(ifa)	((struct in6_ifaddr *)(ifa))
 #define ia62ifa(ia6)	(&((ia6)->ia_ifa))
 
@@ -1001,11 +1010,39 @@ int
 in6_update_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra,
     struct in6_ifaddr *ia, int flags)
 {
-	int error = 0, hostIsNew = 0, plen = -1;
+	int error, hostIsNew = 0;
+
+	if ((error = in6_validate_ifra(ifp, ifra, ia, flags)) != 0)
+		return (error);
+
+	if (ia == NULL) {
+		hostIsNew = 1;
+		if ((ia = in6_alloc_ifa(ifp, ifra, flags)) == NULL)
+			return (ENOBUFS);
+	}
+
+	error = in6_update_ifa_internal(ifp, ifra, ia, hostIsNew, flags);
+	if (error != 0) {
+		if (hostIsNew != 0) {
+			in6_unlink_ifa(ia, ifp);
+			ifa_free(&ia->ia_ifa);
+		}
+		return (error);
+	}
+
+	if (hostIsNew)
+		error = in6_setup_ifa(ifp, ifra, ia, flags);
+
+	return (error);
+}
+
+static int
+in6_validate_ifra(struct ifnet *ifp, struct in6_aliasreq *ifra,
+    struct in6_ifaddr *ia, int flags)
+{
+	int plen = -1;
 	struct sockaddr_in6 dst6;
 	struct in6_addrlifetime *lt;
-	struct in6_multi *in6m_sol;
-	int delay;
 	char ip6buf[INET6_ADDRSTRLEN];
 
 	/* Validate parameters */
@@ -1072,6 +1109,9 @@ in6_update_ifa(struct ifnet *ifp, struct
 		if (sa6_embedscope(&dst6, 0))
 			return (EINVAL); /* XXX: should be impossible */
 	}
+	/* Modify original ifra_dstaddr to reflect changes */
+	ifra->ifra_dstaddr = dst6;
+
 	/*
 	 * The destination address can be specified only for a p2p or a
 	 * loopback interface.  If specified, the corresponding prefix length
@@ -1107,12 +1147,36 @@ in6_update_ifa(struct ifnet *ifp, struct
 			return (0); /* there's nothing to do */
 	}
 
+	/* Check prefix mask */
+	if (ia != NULL && ifra->ifra_prefixmask.sin6_len != 0) {
+		/*
+		 * We prohibit changing the prefix length of an existing
+		 * address, because
+		 * + such an operation should be rare in IPv6, and
+		 * + the operation would confuse prefix management.
+		 */
+		if (ia->ia_prefixmask.sin6_len != 0 &&
+		    in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL) != plen) {
+			nd6log((LOG_INFO, "in6_validate_ifa: the prefix length "
+			    "of an existing %s address should not be changed\n",
+			    ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr)));
+
+			return (EINVAL);
+		}
+	}
+
+	return (0);
+}
+
 	/*
 	 * If this is a new address, allocate a new ifaddr and link it
 	 * into chains.
 	 */
-	if (ia == NULL) {
-		hostIsNew = 1;
+static struct in6_ifaddr *
+in6_alloc_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra, int flags)
+{
+	struct in6_ifaddr *ia;
+
 		/*
 		 * When in6_update_ifa() is called in a process of a received
 		 * RA, it is called under an interrupt context.  So, we should
@@ -1120,7 +1184,7 @@ in6_update_ifa(struct ifnet *ifp, struct
 		 */
 		ia = (struct in6_ifaddr *)ifa_alloc(sizeof(*ia), M_NOWAIT);
 		if (ia == NULL)
-			return (ENOBUFS);
+			return (NULL);
 		LIST_INIT(&ia->ia6_memberships);
 		/* Initialize the address and masks, and put time stamp */
 		ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr;
@@ -1150,52 +1214,28 @@ in6_update_ifa(struct ifnet *ifp, struct
 		LIST_INSERT_HEAD(IN6ADDR_HASH(&ifra->ifra_addr.sin6_addr),
 		    ia, ia6_hash);
 		IN6_IFADDR_WUNLOCK();
-	}
+	
+	return (ia);
+}
+
+static int
+in6_update_ifa_internal(struct ifnet *ifp, struct in6_aliasreq *ifra,
+    struct in6_ifaddr *ia, int hostIsNew, int flags)
+{
+	struct sockaddr_in6 *pdst;
+	int error;
+	char ip6buf[INET6_ADDRSTRLEN];
 
 	/* update timestamp */
 	ia->ia6_updatetime = time_uptime;
 
 	/* set prefix mask */
-	if (ifra->ifra_prefixmask.sin6_len) {
-		/*
-		 * We prohibit changing the prefix length of an existing
-		 * address, because
-		 * + such an operation should be rare in IPv6, and
-		 * + the operation would confuse prefix management.
-		 */
-		if (ia->ia_prefixmask.sin6_len &&
-		    in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL) != plen) {
-			nd6log((LOG_INFO, "in6_update_ifa: the prefix length of an"
-			    " existing (%s) address should not be changed\n",
-			    ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr)));
-			error = EINVAL;
-			goto unlink;
-		}
+	if (ifra->ifra_prefixmask.sin6_len != 0) {
 		ia->ia_prefixmask = ifra->ifra_prefixmask;
 		ia->ia_prefixmask.sin6_family = AF_INET6;
 	}
 
 	/*
-	 * If a new destination address is specified, scrub the old one and
-	 * install the new destination.  Note that the interface must be
-	 * p2p or loopback (see the check above.)
-	 */
-	if (dst6.sin6_family == AF_INET6 &&
-	    !IN6_ARE_ADDR_EQUAL(&dst6.sin6_addr, &ia->ia_dstaddr.sin6_addr)) {
-		int e;
-
-		if ((ia->ia_flags & IFA_ROUTE) != 0 &&
-		    (e = rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST)) != 0) {
-			nd6log((LOG_ERR, "in6_update_ifa: failed to remove "
-			    "a route to the old destination: %s\n",
-			    ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr)));
-			/* proceed anyway... */
-		} else
-			ia->ia_flags &= ~IFA_ROUTE;
-		ia->ia_dstaddr = dst6;
-	}
-
-	/*
 	 * Set lifetimes.  We do not refer to ia6t_expire and ia6t_preferred
 	 * to see if the address is deprecated or invalidated, but initialize
 	 * these members for applications.
@@ -1212,14 +1252,6 @@ in6_update_ifa(struct ifnet *ifp, struct
 	} else
 		ia->ia6_lifetime.ia6t_preferred = 0;
 
-	/* reset the interface and routing table appropriately. */
-	if ((error = in6_ifinit(ifp, ia, &ifra->ifra_addr, hostIsNew)) != 0)
-		goto unlink;
-
-	/*
-	 * configure address flags.
-	 */
-	ia->ia6_flags = ifra->ifra_flags;
 	/*
 	 * backward compatibility - if IN6_IFF_DEPRECATED is set from the
 	 * userland, make it deprecated.
@@ -1228,6 +1260,12 @@ in6_update_ifa(struct ifnet *ifp, struct
 		ia->ia6_lifetime.ia6t_pltime = 0;
 		ia->ia6_lifetime.ia6t_preferred = time_uptime;
 	}
+
+	/*
+	 * configure address flags.
+	 */
+	ia->ia6_flags = ifra->ifra_flags;
+
 	/*
 	 * Make the address tentative before joining multicast addresses,
 	 * so that corresponding MLD responses would not have a tentative
@@ -1242,10 +1280,44 @@ in6_update_ifa(struct ifnet *ifp, struct
 		ia->ia6_flags |= IN6_IFF_TENTATIVE;
 
 	/*
-	 * We are done if we have simply modified an existing address.
+	 * If a new destination address is specified, scrub the old one and
+	 * install the new destination.  Note that the interface must be
+	 * p2p or loopback (see the check above.)
 	 */
-	if (!hostIsNew)
-		return (error);
+	pdst = &ifra->ifra_dstaddr;
+	if (pdst->sin6_family == AF_INET6 &&
+	    !IN6_ARE_ADDR_EQUAL(&pdst->sin6_addr, &ia->ia_dstaddr.sin6_addr)) {
+		if ((ia->ia_flags & IFA_ROUTE) != 0 &&
+		    (rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST) != 0)) {
+			nd6log((LOG_ERR, "in6_update_ifa_internal: failed to "
+			    "remove a route to the old destination: %s\n",
+			    ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr)));
+			/* proceed anyway... */
+		} else
+			ia->ia_flags &= ~IFA_ROUTE;
+		ia->ia_dstaddr = *pdst;
+	}
+
+	/* reset the interface and routing table appropriately. */
+	if ((error = in6_ifinit(ifp, ia, &ifra->ifra_addr, hostIsNew)) != 0) {
+	/*
+	 * XXX: if a change of an existing address failed, keep the entry
+	 * anyway.
+	 */
+	}
+
+	return (error);
+}
+
+static int
+in6_setup_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra,
+    struct in6_ifaddr *ia, int flags)
+{
+	struct in6_multi *in6m_sol;
+	int error = 0;
+
+	/* Add local address to lltable, if necessary (ex. on p2p link). */
+	in6_ifaddloop(&(ia->ia_ifa));
 
 	/*
 	 * Beyond this point, we should call in6_purgeaddr upon an error,
@@ -1256,8 +1328,11 @@ in6_update_ifa(struct ifnet *ifp, struct
 	in6m_sol = NULL;
 	if ((ifp->if_flags & IFF_MULTICAST) != 0) {
 		error = in6_update_ifa_join_mc(ifp, ifra, ia, flags, &in6m_sol);
-		if (error)
-			goto cleanup;
+		if (error != 0) {
+			ifa_free(&ia->ia_ifa);
+			in6_purgeaddr(&ia->ia_ifa);
+			return (error);
+		}
 	}
 
 	/*
@@ -1267,7 +1342,7 @@ in6_update_ifa(struct ifnet *ifp, struct
 	if (in6if_do_dad(ifp) && ((ifra->ifra_flags & IN6_IFF_NODAD) == 0) &&
 	    (ia->ia6_flags & IN6_IFF_TENTATIVE))
 	{
-		int mindelay, maxdelay;
+		int delay, mindelay, maxdelay;
 
 		delay = 0;
 		if ((flags & IN6_IFAUPDATE_DADDELAY)) {
@@ -1298,26 +1373,8 @@ in6_update_ifa(struct ifnet *ifp, struct
 		nd6_dad_start((struct ifaddr *)ia, delay);
 	}
 
-	KASSERT(hostIsNew, ("in6_update_ifa: !hostIsNew"));
 	ifa_free(&ia->ia_ifa);
 	return (error);
-
-  unlink:
-	/*
-	 * XXX: if a change of an existing address failed, keep the entry
-	 * anyway.
-	 */
-	if (hostIsNew) {
-		in6_unlink_ifa(ia, ifp);
-		ifa_free(&ia->ia_ifa);
-	}
-	return (error);
-
-  cleanup:
-	KASSERT(hostIsNew, ("in6_update_ifa: cleanup: !hostIsNew"));
-	ifa_free(&ia->ia_ifa);
-	in6_purgeaddr(&ia->ia_ifa);
-	return error;
 }
 
 /*
@@ -1644,10 +1701,6 @@ in6_ifinit(struct ifnet *ifp, struct in6
 			ia->ia_flags |= IFA_RTSELF;
 	}
 
-	/* Add local address to lltable, if necessary (ex. on p2p link). */
-	if (newhost)
-		in6_ifaddloop(&(ia->ia_ifa));
-
 	return (error);
 }
 



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