Index | Thread | Search

From:
Florian Obser <florian@openbsd.org>
Subject:
Re: RFC 6724 rule 5.5
To:
Alexander Bluhm <bluhm@openbsd.org>
Cc:
tech <tech@openbsd.org>
Date:
Tue, 16 Apr 2024 18:23:07 +0200

Download raw body.

Thread
On 2024-04-16 14:28 +02, Florian Obser <florian@openbsd.org> wrote:
> Disregard please, I'll get rid of the nonsensical IFF_LOOPBACK case
> first I think that will make this slightly easier / compacter.

OK?

diff --git sbin/slaacd/engine.c sbin/slaacd/engine.c
index 000a9fcf413..72798552824 100644
--- sbin/slaacd/engine.c
+++ sbin/slaacd/engine.c
@@ -2130,6 +2130,7 @@ configure_address(struct address_proposal *addr_proposal)
 
 	address.if_index = addr_proposal->if_index;
 	memcpy(&address.addr, &addr_proposal->addr, sizeof(address.addr));
+	memcpy(&address.gw, &addr_proposal->from, sizeof(address.gw));
 	memcpy(&address.mask, &addr_proposal->mask, sizeof(address.mask));
 	address.vltime = addr_proposal->vltime;
 	address.pltime = addr_proposal->pltime;
diff --git sbin/slaacd/engine.h sbin/slaacd/engine.h
index 7a8551d2c50..079ef9a64e0 100644
--- sbin/slaacd/engine.h
+++ sbin/slaacd/engine.h
@@ -19,6 +19,7 @@
 struct imsg_configure_address {
 	uint32_t		 if_index;
 	struct sockaddr_in6	 addr;
+	struct sockaddr_in6	 gw;
 	struct in6_addr		 mask;
 	uint32_t		 vltime;
 	uint32_t		 pltime;
diff --git sbin/slaacd/slaacd.c sbin/slaacd/slaacd.c
index 4d1786361f7..d7fcec2601b 100644
--- sbin/slaacd/slaacd.c
+++ sbin/slaacd/slaacd.c
@@ -632,6 +632,8 @@ configure_interface(struct imsg_configure_address *address)
 
 	memcpy(&in6_addreq.ifra_addr, &address->addr,
 	    sizeof(in6_addreq.ifra_addr));
+	memcpy(&in6_addreq.ifra_dstaddr, &address->gw,
+	    sizeof(in6_addreq.ifra_dstaddr));
 	memcpy(&in6_addreq.ifra_prefixmask.sin6_addr, &address->mask,
 	    sizeof(in6_addreq.ifra_prefixmask.sin6_addr));
 	in6_addreq.ifra_prefixmask.sin6_family = AF_INET6;
diff --git sys/netinet6/icmp6.c sys/netinet6/icmp6.c
index 2ec50eb06a2..e3c302969f1 100644
--- sys/netinet6/icmp6.c
+++ sys/netinet6/icmp6.c
@@ -1164,7 +1164,7 @@ icmp6_reflect(struct mbuf **mp, size_t off, struct sockaddr *sa)
 			rtfree(rt);
 			goto bad;
 		}
-		ia6 = in6_ifawithscope(rt->rt_ifa->ifa_ifp, &t, rtableid);
+		ia6 = in6_ifawithscope(rt->rt_ifa->ifa_ifp, &t, rtableid, rt);
 		if (ia6 != NULL)
 			src = &ia6->ia_addr.sin6_addr;
 		if (src == NULL)
diff --git sys/netinet6/in6.c sys/netinet6/in6.c
index 6e185efb2ad..3999479a543 100644
--- sys/netinet6/in6.c
+++ sys/netinet6/in6.c
@@ -562,13 +562,18 @@ in6_update_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra,
 		return (EINVAL);
 
 	/*
-	 * The destination address for a p2p link must have a family
-	 * of AF_UNSPEC or AF_INET6.
+	 * The destination address for a p2p link or the address of the
+	 * announcing router for an autoconf address must have a family of
+	 * AF_UNSPEC or AF_INET6.
 	 */
-	if ((ifp->if_flags & IFF_POINTOPOINT) &&
-	    ifra->ifra_dstaddr.sin6_family != AF_INET6 &&
-	    ifra->ifra_dstaddr.sin6_family != AF_UNSPEC)
-		return (EAFNOSUPPORT);
+	if ((ifp->if_flags & IFF_POINTOPOINT) ||
+	    (ifra->ifra_flags & IN6_IFF_AUTOCONF)) {
+		if (ifra->ifra_dstaddr.sin6_family != AF_INET6 &&
+		    ifra->ifra_dstaddr.sin6_family != AF_UNSPEC)
+			return (EAFNOSUPPORT);
+
+	} else if (ifra->ifra_dstaddr.sin6_family != AF_UNSPEC)
+			return (EINVAL);
 
 	/*
 	 * validate ifra_prefixmask.  don't check sin6_family, netmask
@@ -597,26 +602,14 @@ in6_update_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra,
 		 */
 		plen = in6_mask2len(&ia6->ia_prefixmask.sin6_addr, NULL);
 	}
-	/*
-	 * If the destination address on a p2p interface is specified,
-	 * and the address is a scoped one, validate/set the scope
-	 * zone identifier.
-	 */
+
 	dst6 = ifra->ifra_dstaddr;
-	if ((ifp->if_flags & IFF_POINTOPOINT) &&
-	    (dst6.sin6_family == AF_INET6)) {
+	if (dst6.sin6_family == AF_INET6) {
 		error = in6_check_embed_scope(&dst6, ifp->if_index);
 		if (error)
 			return error;
-	}
-	/*
-	 * The destination address can be specified only for a p2p interface.
-	 * If specified, the corresponding prefix length must be 128.
-	 */
-	if (ifra->ifra_dstaddr.sin6_family == AF_INET6) {
-		if (!(ifp->if_flags & IFF_POINTOPOINT))
-			return (EINVAL);
-		if (plen != 128)
+
+		if ((ifp->if_flags & IFF_POINTOPOINT) && plen != 128)
 			return (EINVAL);
 	}
 	/* lifetime consistency check */
@@ -660,6 +653,11 @@ in6_update_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra,
 		} else {
 			ia6->ia_ifa.ifa_dstaddr = NULL;
 		}
+		if ((ifra->ifra_flags & IN6_IFF_AUTOCONF) == 0)
+			ia6->ia_gwaddr = dst6;
+		else
+			memset(&ia6->ia_gwaddr, 0, sizeof(ia6->ia_gwaddr));
+
 		ia6->ia_ifa.ifa_netmask = sin6tosa(&ia6->ia_prefixmask);
 
 		ia6->ia_ifp = ifp;
@@ -685,8 +683,7 @@ in6_update_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra,
 
 	/*
 	 * If a new destination address is specified, scrub the old one and
-	 * install the new destination.  Note that the interface must be
-	 * p2p (see the check above.)
+	 * install the new destination.
 	 */
 	if ((ifp->if_flags & IFF_POINTOPOINT) && dst6.sin6_family == AF_INET6 &&
 	    !IN6_ARE_ADDR_EQUAL(&dst6.sin6_addr, &ia6->ia_dstaddr.sin6_addr)) {
@@ -705,6 +702,13 @@ in6_update_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra,
 		ia6->ia_dstaddr = dst6;
 	}
 
+	if ((ifra->ifra_flags & IN6_IFF_AUTOCONF) &&
+	    dst6.sin6_family == AF_INET6 &&
+	    !IN6_ARE_ADDR_EQUAL(&dst6.sin6_addr, &ia6->ia_gwaddr.sin6_addr)) {
+		/* Update announcing router */
+		ia6->ia_gwaddr = dst6;
+	}
+
 	/*
 	 * Set lifetimes.  We do not refer to ia6t_expire and ia6t_preferred
 	 * to see if the address is deprecated or invalidated, but initialize
@@ -1328,13 +1332,21 @@ in6_prefixlen2mask(struct in6_addr *maskp, int len)
  * return the best address out of the same scope
  */
 struct in6_ifaddr *
-in6_ifawithscope(struct ifnet *oifp, struct in6_addr *dst, u_int rdomain)
+in6_ifawithscope(struct ifnet *oifp, struct in6_addr *dst, u_int rdomain,
+    struct rtentry *rt)
 {
 	int dst_scope =	in6_addrscope(dst), src_scope, best_scope = 0;
 	int blen = -1;
 	struct ifaddr *ifa;
 	struct ifnet *ifp;
 	struct in6_ifaddr *ia6_best = NULL;
+	struct in6_addr *gw6 = NULL;
+
+	if (rt) {
+		if (rt->rt_gateway != NULL &&
+		    rt->rt_gateway->sa_family == AF_INET6)
+			gw6 = &(satosin6(rt->rt_gateway)->sin6_addr);
+	}
 
 	if (oifp == NULL) {
 		printf("%s: output interface is not specified\n", __func__);
@@ -1459,8 +1471,16 @@ in6_ifawithscope(struct ifnet *oifp, struct in6_addr *dst, u_int rdomain)
 			/*
 			 * Rule 5.5: Prefer addresses in a prefix advertised
 			 * by the next-hop.
-			 * We do not track this information.
 			 */
+			if (gw6) {
+				struct in6_addr *in6_bestgw, *in6_newgw;
+
+				in6_bestgw = &ia6_best->ia_gwaddr.sin6_addr;
+				in6_newgw = &ifatoia6(ifa)->ia_gwaddr.sin6_addr;
+				if (!IN6_ARE_ADDR_EQUAL(in6_bestgw, gw6) &&
+				    IN6_ARE_ADDR_EQUAL(in6_newgw, gw6))
+					goto replace;
+			}
 
 			/*
 			 * Rule 6: Prefer matching label.
diff --git sys/netinet6/in6.h sys/netinet6/in6.h
index 642a24b1999..db5896beb79 100644
--- sys/netinet6/in6.h
+++ sys/netinet6/in6.h
@@ -404,6 +404,7 @@ struct sockaddr_in6;
 struct ifaddr;
 struct in6_ifaddr;
 struct ifnet;
+struct rtentry;
 
 void	ipv6_input(struct ifnet *, struct mbuf *);
 struct mbuf *
@@ -413,7 +414,8 @@ int	in6_cksum(struct mbuf *, uint8_t, uint32_t, uint32_t);
 void	in6_proto_cksum_out(struct mbuf *, struct ifnet *);
 int	in6_localaddr(struct in6_addr *);
 int	in6_addrscope(struct in6_addr *);
-struct	in6_ifaddr *in6_ifawithscope(struct ifnet *, struct in6_addr *, u_int);
+struct	in6_ifaddr *in6_ifawithscope(struct ifnet *, struct in6_addr *, u_int,
+	    struct rtentry *);
 int	in6_mask2len(struct in6_addr *, u_char *);
 int	in6_nam2sin6(const struct mbuf *, struct sockaddr_in6 **);
 int	in6_sa2sin6(struct sockaddr *, struct sockaddr_in6 **);
diff --git sys/netinet6/in6_src.c sys/netinet6/in6_src.c
index d6163d254c0..db123c22a2b 100644
--- sys/netinet6/in6_src.c
+++ sys/netinet6/in6_src.c
@@ -162,7 +162,7 @@ in6_pcbselsrc(const struct in6_addr **in6src, struct sockaddr_in6 *dstsock,
 		if (ifp == NULL)
 			return (ENXIO); /* XXX: better error? */
 
-		ia6 = in6_ifawithscope(ifp, dst, rtableid);
+		ia6 = in6_ifawithscope(ifp, dst, rtableid, NULL);
 		if_put(ifp);
 
 		if (ia6 == NULL)
@@ -192,7 +192,7 @@ in6_pcbselsrc(const struct in6_addr **in6src, struct sockaddr_in6 *dstsock,
 	if (rt != NULL) {
 		ifp = if_get(rt->rt_ifidx);
 		if (ifp != NULL) {
-			ia6 = in6_ifawithscope(ifp, dst, rtableid);
+			ia6 = in6_ifawithscope(ifp, dst, rtableid, rt);
 			if_put(ifp);
 		}
 		if (ia6 == NULL) /* xxx scope error ?*/
@@ -256,7 +256,7 @@ in6_selectsrc(const struct in6_addr **in6src, struct sockaddr_in6 *dstsock,
 		if (ifp == NULL)
 			return (ENXIO); /* XXX: better error? */
 
-		ia6 = in6_ifawithscope(ifp, dst, rtableid);
+		ia6 = in6_ifawithscope(ifp, dst, rtableid, NULL);
 		if_put(ifp);
 
 		if (ia6 == NULL)
@@ -280,7 +280,7 @@ in6_selectsrc(const struct in6_addr **in6src, struct sockaddr_in6 *dstsock,
 			ifp = if_get(htons(dstsock->sin6_scope_id));
 
 		if (ifp) {
-			ia6 = in6_ifawithscope(ifp, dst, rtableid);
+			ia6 = in6_ifawithscope(ifp, dst, rtableid, NULL);
 			if_put(ifp);
 
 			if (ia6 == NULL)
diff --git sys/netinet6/in6_var.h sys/netinet6/in6_var.h
index 1323eee209a..a97a1d463f7 100644
--- sys/netinet6/in6_var.h
+++ sys/netinet6/in6_var.h
@@ -93,6 +93,7 @@ struct	in6_ifaddr {
 #define	ia_flags	ia_ifa.ifa_flags
 
 	struct	sockaddr_in6 ia_addr;	/* interface address */
+	struct	sockaddr_in6 ia_gwaddr; /* router we learned address from */
 	struct	sockaddr_in6 ia_dstaddr; /* space for destination addr */
 	struct	sockaddr_in6 ia_prefixmask; /* prefix mask */
 	TAILQ_ENTRY(in6_ifaddr) ia_list;	/* list of IP6 addresses */


-- 
In my defence, I have been left unsupervised.