git: e437991fc936 - main - netinet6: factor interface addition code to the dedicated function

From: Alexander V. Chernikov <melifaro_at_FreeBSD.org>
Date: Tue, 27 Sep 2022 13:23:42 UTC
The branch main has been updated by melifaro:

URL: https://cgit.FreeBSD.org/src/commit/?id=e437991fc9361bce1b7dca999dbe3769321f01da

commit e437991fc9361bce1b7dca999dbe3769321f01da
Author:     Alexander V. Chernikov <melifaro@FreeBSD.org>
AuthorDate: 2022-09-26 13:49:02 +0000
Commit:     Alexander V. Chernikov <melifaro@FreeBSD.org>
CommitDate: 2022-09-27 13:23:34 +0000

    netinet6: factor interface addition code to the dedicated function
    
    Summary:
    Move SIOCAIFADDR_IN6 (current "primary" ioctl to add an IPv6
     interface address) handling code to the dedicated in6_addifaddr()
     function and make it a part of KPI. This allows in-kernel users to
     add/delete interfaces addresses without relying on ioctl interface.
    
    Subscribers: imp, ae, glebius
    
    Differential Revision: https://reviews.freebsd.org/D36713
---
 sys/netinet6/in6.c     | 288 +++++++++++++++++++++++++------------------------
 sys/netinet6/in6_var.h |   1 +
 2 files changed, 151 insertions(+), 138 deletions(-)

diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c
index ccdf71cfc01c..0a00ea6b8be4 100644
--- a/sys/netinet6/in6.c
+++ b/sys/netinet6/in6.c
@@ -252,15 +252,15 @@ in6_control(struct socket *so, u_long cmd, void *data,
 	struct	in6_ifaddr *ia = NULL;
 	struct	in6_aliasreq *ifra = (struct in6_aliasreq *)data;
 	struct sockaddr_in6 *sa6;
-	int carp_attached = 0;
 	int error;
-	u_long ocmd = cmd;
 
 	/*
 	 * Compat to make pre-10.x ifconfig(8) operable.
 	 */
-	if (cmd == OSIOCAIFADDR_IN6)
+	if (cmd == OSIOCAIFADDR_IN6) {
 		cmd = SIOCAIFADDR_IN6;
+		ifra->ifra_vhid = 0;
+	}
 
 	switch (cmd) {
 	case SIOCGETSGCNT_IN6:
@@ -560,142 +560,9 @@ in6_control(struct socket *so, u_long cmd, void *data,
 		break;
 
 	case SIOCAIFADDR_IN6:
-	{
-		struct nd_prefixctl pr0;
-		struct nd_prefix *pr;
-
-		/*
-		 * first, make or update the interface address structure,
-		 * and link it to the list.
-		 */
-		if ((error = in6_update_ifa(ifp, ifra, ia, 0)) != 0)
-			goto out;
-		if (ia != NULL) {
-			if (ia->ia_ifa.ifa_carp)
-				(*carp_detach_p)(&ia->ia_ifa, true);
-			ifa_free(&ia->ia_ifa);
-		}
-		if ((ia = in6ifa_ifpwithaddr(ifp, &ifra->ifra_addr.sin6_addr))
-		    == NULL) {
-			/*
-			 * this can happen when the user specify the 0 valid
-			 * lifetime.
-			 */
-			break;
-		}
-
-		if (cmd == ocmd && ifra->ifra_vhid > 0) {
-			if (carp_attach_p != NULL)
-				error = (*carp_attach_p)(&ia->ia_ifa,
-				    ifra->ifra_vhid);
-			else
-				error = EPROTONOSUPPORT;
-			if (error)
-				goto out;
-			else
-				carp_attached = 1;
-		}
-
-		/*
-		 * then, make the prefix on-link on the interface.
-		 * XXX: we'd rather create the prefix before the address, but
-		 * we need at least one address to install the corresponding
-		 * interface route, so we configure the address first.
-		 */
-
-		/*
-		 * convert mask to prefix length (prefixmask has already
-		 * been validated in in6_update_ifa().
-		 */
-		bzero(&pr0, sizeof(pr0));
-		pr0.ndpr_ifp = ifp;
-		pr0.ndpr_plen = in6_mask2len(&ifra->ifra_prefixmask.sin6_addr,
-		    NULL);
-		if (pr0.ndpr_plen == 128) {
-			/* we don't need to install a host route. */
-			goto aifaddr_out;
-		}
-		pr0.ndpr_prefix = ifra->ifra_addr;
-		/* apply the mask for safety. */
-		IN6_MASK_ADDR(&pr0.ndpr_prefix.sin6_addr,
-		    &ifra->ifra_prefixmask.sin6_addr);
-
-		/*
-		 * XXX: since we don't have an API to set prefix (not address)
-		 * lifetimes, we just use the same lifetimes as addresses.
-		 * The (temporarily) installed lifetimes can be overridden by
-		 * later advertised RAs (when accept_rtadv is non 0), which is
-		 * an intended behavior.
-		 */
-		pr0.ndpr_raf_onlink = 1; /* should be configurable? */
-		pr0.ndpr_raf_auto =
-		    ((ifra->ifra_flags & IN6_IFF_AUTOCONF) != 0);
-		pr0.ndpr_vltime = ifra->ifra_lifetime.ia6t_vltime;
-		pr0.ndpr_pltime = ifra->ifra_lifetime.ia6t_pltime;
-
-		/* add the prefix if not yet. */
-		if ((pr = nd6_prefix_lookup(&pr0)) == NULL) {
-			/*
-			 * nd6_prelist_add will install the corresponding
-			 * interface route.
-			 */
-			if ((error = nd6_prelist_add(&pr0, NULL, &pr)) != 0) {
-				if (carp_attached)
-					(*carp_detach_p)(&ia->ia_ifa, false);
-				goto out;
-			}
-		}
-
-		/* relate the address to the prefix */
-		if (ia->ia6_ndpr == NULL) {
-			ia->ia6_ndpr = pr;
-			pr->ndpr_addrcnt++;
-
-			/*
-			 * If this is the first autoconf address from the
-			 * prefix, create a temporary address as well
-			 * (when required).
-			 */
-			if ((ia->ia6_flags & IN6_IFF_AUTOCONF) &&
-			    V_ip6_use_tempaddr && pr->ndpr_addrcnt == 1) {
-				int e;
-				if ((e = in6_tmpifadd(ia, 1, 0)) != 0) {
-					log(LOG_NOTICE, "in6_control: failed "
-					    "to create a temporary address, "
-					    "errno=%d\n", e);
-				}
-			}
-		}
-		nd6_prefix_rele(pr);
-
-		/*
-		 * this might affect the status of autoconfigured addresses,
-		 * that is, this address might make other addresses detached.
-		 */
-		pfxlist_onlink_check();
-
-aifaddr_out:
-		/*
-		 * Try to clear the flag when a new IPv6 address is added
-		 * onto an IFDISABLED interface and it succeeds.
-		 */
-		if (ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED) {
-			struct in6_ndireq nd;
-
-			memset(&nd, 0, sizeof(nd));
-			nd.ndi.flags = ND_IFINFO(ifp)->flags;
-			nd.ndi.flags &= ~ND6_IFF_IFDISABLED;
-			if (nd6_ioctl(SIOCSIFINFO_FLAGS, (caddr_t)&nd, ifp) < 0)
-				log(LOG_NOTICE, "SIOCAIFADDR_IN6: "
-				    "SIOCSIFINFO_FLAGS for -ifdisabled "
-				    "failed.");
-			/*
-			 * Ignore failure of clearing the flag intentionally.
-			 * The failure means address duplication was detected.
-			 */
-		}
+		error = in6_addifaddr(ifp, ifra, ia);
+		ia = NULL;
 		break;
-	}
 
 	case SIOCDIFADDR_IN6:
 		in6_purgeifaddr(ia);
@@ -1324,6 +1191,151 @@ ifa_is_p2p(struct in6_ifaddr *ia)
 	return (false);
 }
 
+int
+in6_addifaddr(struct ifnet *ifp, struct in6_aliasreq *ifra, struct in6_ifaddr *ia)
+{
+	struct nd_prefixctl pr0;
+	struct nd_prefix *pr;
+	int carp_attached = 0;
+	int error;
+
+	/*
+	 * first, make or update the interface address structure,
+	 * and link it to the list.
+	 */
+	if ((error = in6_update_ifa(ifp, ifra, ia, 0)) != 0)
+		goto out;
+	if (ia != NULL) {
+		if (ia->ia_ifa.ifa_carp)
+			(*carp_detach_p)(&ia->ia_ifa, true);
+		ifa_free(&ia->ia_ifa);
+	}
+	if ((ia = in6ifa_ifpwithaddr(ifp, &ifra->ifra_addr.sin6_addr)) == NULL) {
+		/*
+		 * this can happen when the user specify the 0 valid
+		 * lifetime.
+		 */
+		return (0);
+	}
+
+	if (ifra->ifra_vhid > 0) {
+		if (carp_attach_p != NULL)
+			error = (*carp_attach_p)(&ia->ia_ifa,
+			    ifra->ifra_vhid);
+		else
+			error = EPROTONOSUPPORT;
+		if (error)
+			goto out;
+		else
+			carp_attached = 1;
+	}
+
+	/*
+	 * then, make the prefix on-link on the interface.
+	 * XXX: we'd rather create the prefix before the address, but
+	 * we need at least one address to install the corresponding
+	 * interface route, so we configure the address first.
+	 */
+
+	/*
+	 * convert mask to prefix length (prefixmask has already
+	 * been validated in in6_update_ifa().
+	 */
+	bzero(&pr0, sizeof(pr0));
+	pr0.ndpr_ifp = ifp;
+	pr0.ndpr_plen = in6_mask2len(&ifra->ifra_prefixmask.sin6_addr,
+	    NULL);
+	if (pr0.ndpr_plen == 128) {
+		/* we don't need to install a host route. */
+		goto aifaddr_out;
+	}
+	pr0.ndpr_prefix = ifra->ifra_addr;
+	/* apply the mask for safety. */
+	IN6_MASK_ADDR(&pr0.ndpr_prefix.sin6_addr,
+	    &ifra->ifra_prefixmask.sin6_addr);
+
+	/*
+	 * XXX: since we don't have an API to set prefix (not address)
+	 * lifetimes, we just use the same lifetimes as addresses.
+	 * The (temporarily) installed lifetimes can be overridden by
+	 * later advertised RAs (when accept_rtadv is non 0), which is
+	 * an intended behavior.
+	 */
+	pr0.ndpr_raf_onlink = 1; /* should be configurable? */
+	pr0.ndpr_raf_auto =
+	    ((ifra->ifra_flags & IN6_IFF_AUTOCONF) != 0);
+	pr0.ndpr_vltime = ifra->ifra_lifetime.ia6t_vltime;
+	pr0.ndpr_pltime = ifra->ifra_lifetime.ia6t_pltime;
+
+	/* add the prefix if not yet. */
+	if ((pr = nd6_prefix_lookup(&pr0)) == NULL) {
+		/*
+		 * nd6_prelist_add will install the corresponding
+		 * interface route.
+		 */
+		if ((error = nd6_prelist_add(&pr0, NULL, &pr)) != 0) {
+			if (carp_attached)
+				(*carp_detach_p)(&ia->ia_ifa, false);
+			goto out;
+		}
+	}
+
+	/* relate the address to the prefix */
+	if (ia->ia6_ndpr == NULL) {
+		ia->ia6_ndpr = pr;
+		pr->ndpr_addrcnt++;
+
+		/*
+		 * If this is the first autoconf address from the
+		 * prefix, create a temporary address as well
+		 * (when required).
+		 */
+		if ((ia->ia6_flags & IN6_IFF_AUTOCONF) &&
+		    V_ip6_use_tempaddr && pr->ndpr_addrcnt == 1) {
+			int e;
+			if ((e = in6_tmpifadd(ia, 1, 0)) != 0) {
+				log(LOG_NOTICE, "in6_control: failed "
+				    "to create a temporary address, "
+				    "errno=%d\n", e);
+			}
+		}
+	}
+	nd6_prefix_rele(pr);
+
+	/*
+	 * this might affect the status of autoconfigured addresses,
+	 * that is, this address might make other addresses detached.
+	 */
+	pfxlist_onlink_check();
+
+aifaddr_out:
+	/*
+	 * Try to clear the flag when a new IPv6 address is added
+	 * onto an IFDISABLED interface and it succeeds.
+	 */
+	if (ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED) {
+		struct in6_ndireq nd;
+
+		memset(&nd, 0, sizeof(nd));
+		nd.ndi.flags = ND_IFINFO(ifp)->flags;
+		nd.ndi.flags &= ~ND6_IFF_IFDISABLED;
+		if (nd6_ioctl(SIOCSIFINFO_FLAGS, (caddr_t)&nd, ifp) < 0)
+			log(LOG_NOTICE, "SIOCAIFADDR_IN6: "
+			    "SIOCSIFINFO_FLAGS for -ifdisabled "
+			    "failed.");
+		/*
+		 * Ignore failure of clearing the flag intentionally.
+		 * The failure means address duplication was detected.
+		 */
+	}
+	error = 0;
+
+out:
+	if (ia != NULL)
+		ifa_free(&ia->ia_ifa);
+	return (error);
+}
+
 void
 in6_purgeaddr(struct ifaddr *ifa)
 {
diff --git a/sys/netinet6/in6_var.h b/sys/netinet6/in6_var.h
index 6af0e54ccb75..b40b426d1332 100644
--- a/sys/netinet6/in6_var.h
+++ b/sys/netinet6/in6_var.h
@@ -889,6 +889,7 @@ int	in6_update_ifa(struct ifnet *, struct in6_aliasreq *,
 	struct in6_ifaddr *, int);
 void	in6_prepare_ifra(struct in6_aliasreq *, const struct in6_addr *,
 	const struct in6_addr *);
+int	in6_addifaddr(struct ifnet *, struct in6_aliasreq *, struct in6_ifaddr *);
 void	in6_purgeaddr(struct ifaddr *);
 void	in6_purgeifaddr(struct in6_ifaddr *);
 int	in6if_do_dad(struct ifnet *);