tech-net archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Sending from tentative/duplicate/detached addresses



Hi List

RFC4862 5.5.4 states
   An invalid address MUST NOT be used
   as a source address in outgoing communications and MUST NOT be
   recognized as a destination on a receiving interface.

Currently we do the later, but not not entirely the former.

This is because we now set DETACHED for all addresses when the link
state goes down and set TENTATIVE for all addresses when the link state
comes back up. This makes it possible to bind to a perfectly valid
address, but the address then becomes invalid (DETACHED, TENTATIVE,
DULICATED). Currently, we still send from these addresses.

The attached patch checks that the source address is still valid in
ip{,6}_output. If DETACHED or TENTATIVE and the protocol is TCP then we
just drop the packet. Otherwise, if the source address is invalid in any
way, we return EADDRNOTAVAIL.

Extra eyes on the inet ip_output change would be nice because it moves
the "grab 1st address from the interface if no src" logic up and removes
the same (duplicated I think) logic from the mulitcast section.

Roy
Index: sys/netinet/if_arp.c
===================================================================
RCS file: /cvsroot/src/sys/netinet/if_arp.c,v
retrieving revision 1.223
diff -p -u -r1.223 if_arp.c
--- sys/netinet/if_arp.c	7 Sep 2016 13:01:39 -0000	1.223
+++ sys/netinet/if_arp.c	7 Sep 2016 16:38:34 -0000
@@ -149,12 +149,10 @@ static int	arp_maxhold = 1;	/* number of
 
 int		ip_dad_count = PROBE_NUM;
 #ifdef ARP_DEBUG
-static int	arp_debug = 1;
+int		arp_debug = 1;
 #else
-static int	arp_debug = 0;
+int		arp_debug = 0;
 #endif
-#define arplog(level, fmt, args...) \
-	do { if (arp_debug) log(level, "%s: " fmt, __func__, ##args);} while (0)
 
 static	void arp_init(void);
 
Index: sys/netinet/in_var.h
===================================================================
RCS file: /cvsroot/src/sys/netinet/in_var.h,v
retrieving revision 1.79
diff -p -u -r1.79 in_var.h
--- sys/netinet/in_var.h	1 Aug 2016 03:15:30 -0000	1.79
+++ sys/netinet/in_var.h	7 Sep 2016 16:38:35 -0000
@@ -375,6 +375,9 @@ struct in_multi {
 extern pktqueue_t *ip_pktq;
 
 extern int ip_dad_count;		/* Duplicate Address Detection probes */
+extern int arp_debug;
+#define arplog(level, fmt, args...) \
+	do { if (arp_debug) log(level, "%s: " fmt, __func__, ##args);} while (0)
 
 /*
  * Structure used by functions below to remember position when stepping
Index: sys/netinet/ip_output.c
===================================================================
RCS file: /cvsroot/src/sys/netinet/ip_output.c,v
retrieving revision 1.260
diff -p -u -r1.260 ip_output.c
--- sys/netinet/ip_output.c	1 Aug 2016 03:15:30 -0000	1.260
+++ sys/netinet/ip_output.c	7 Sep 2016 16:38:37 -0000
@@ -153,6 +153,7 @@ static struct mbuf *ip_insertoptions(str
 static struct ifnet *ip_multicast_if(struct in_addr *, int *);
 static void ip_mloopback(struct ifnet *, struct mbuf *,
     const struct sockaddr_in *);
+static int ip_ifaddrvalid(const struct in_addr);
 
 extern pfil_head_t *inet_pfil_hook;			/* XXX */
 
@@ -377,6 +378,44 @@ ip_output(struct mbuf *m0, struct mbuf *
 	}
 	rtmtu_nolock = rt && (rt->rt_rmx.rmx_locks & RTV_MTU) == 0;
 
+	/*
+	 * If source address not specified yet, use address
+	 * of outgoing interface.
+	 */
+	if (in_nullhost(ip->ip_src)) {
+		struct ifaddr *xifa;
+
+		xifa = &ia->ia_ifa;
+		if (xifa->ifa_getifa != NULL) {
+			ia4_release(ia, &psref_ia);
+			/* FIXME NOMPSAFE */
+			ia = ifatoia((*xifa->ifa_getifa)(xifa, rdst));
+			if (ia == NULL) {
+				error = EADDRNOTAVAIL;
+				goto bad;
+			}
+			ia4_acquire(ia, &psref_ia);
+		}
+		ip->ip_src = ia->ia_addr.sin_addr;
+	}
+
+	/* Ensure we only sent from a valid address. */
+	if ((error = ip_ifaddrvalid(ip->ip_src)) != 0) {
+		arplog(LOG_ERR,
+		    "refusing to send from invalid address %s (pid %d)",
+		    in_fmtaddr(ip->ip_src), curproc->p_pid);
+		IP_STATINC(IP_STAT_ODROPPED);
+		if (error == 1 && ip->ip_p == IPPROTO_TCP)
+			/* Address exists, but is tentative or detached.
+			 * We can't send from it because it's invalid,
+			 * so we drop the packet and continue ...
+			 * TCP will timeout eventually. */
+			error = 0;
+		else
+			error = EADDRNOTAVAIL;
+		goto bad;
+	}
+
 	if (IN_MULTICAST(ip->ip_dst.s_addr) ||
 	    (ip->ip_dst.s_addr == INADDR_BROADCAST)) {
 		bool inmgroup;
@@ -413,34 +452,6 @@ ip_output(struct mbuf *m0, struct mbuf *
 			error = ENETUNREACH;
 			goto bad;
 		}
-		/*
-		 * If source address not specified yet, use an address
-		 * of outgoing interface.
-		 */
-		if (in_nullhost(ip->ip_src)) {
-			struct in_ifaddr *xia;
-			struct ifaddr *xifa;
-			struct psref _psref;
-
-			xia = in_get_ia_from_ifp_psref(ifp, &_psref);
-			if (!xia) {
-				error = EADDRNOTAVAIL;
-				goto bad;
-			}
-			xifa = &xia->ia_ifa;
-			if (xifa->ifa_getifa != NULL) {
-				ia4_release(xia, &_psref);
-				/* FIXME NOMPSAFE */
-				xia = ifatoia((*xifa->ifa_getifa)(xifa, rdst));
-				if (xia == NULL) {
-					error = EADDRNOTAVAIL;
-					goto bad;
-				}
-				ia4_acquire(xia, &_psref);
-			}
-			ip->ip_src = xia->ia_addr.sin_addr;
-			ia4_release(xia, &_psref);
-		}
 
 		inmgroup = in_multi_group(ip->ip_dst, ifp, flags);
 		if (inmgroup && (imo == NULL || imo->imo_multicast_loop)) {
@@ -491,27 +502,6 @@ ip_output(struct mbuf *m0, struct mbuf *
 	}
 
 	/*
-	 * If source address not specified yet, use address
-	 * of outgoing interface.
-	 */
-	if (in_nullhost(ip->ip_src)) {
-		struct ifaddr *xifa;
-
-		xifa = &ia->ia_ifa;
-		if (xifa->ifa_getifa != NULL) {
-			ia4_release(ia, &psref_ia);
-			/* FIXME NOMPSAFE */
-			ia = ifatoia((*xifa->ifa_getifa)(xifa, rdst));
-			if (ia == NULL) {
-				error = EADDRNOTAVAIL;
-				goto bad;
-			}
-			ia4_acquire(ia, &psref_ia);
-		}
-		ip->ip_src = ia->ia_addr.sin_addr;
-	}
-
-	/*
 	 * packets with Class-D address as source are not valid per
 	 * RFC 1112
 	 */
@@ -1850,3 +1840,38 @@ ip_mloopback(struct ifnet *ifp, struct m
 	KERNEL_UNLOCK_ONE(NULL);
 #endif
 }
+
+/*
+ * Ensure sending address is valid.
+ * Returns 0 on success, -1 if an error should be sent back or 1
+ * if the packet could be dropped without error (protocol dependent).
+ */
+static int
+ip_ifaddrvalid(const struct in_addr addr)
+{
+	struct sockaddr_in sin;
+	int s, error;
+	struct ifaddr *ifa;
+	struct in_ifaddr *ia;
+
+	if (addr.s_addr == INADDR_ANY)
+		return 0;
+
+	memset(&sin, 0, sizeof(sin));
+	sin.sin_family = AF_INET;
+	sin.sin_len = sizeof(sin);
+	sin.sin_addr = addr;
+
+	s = pserialize_read_enter();
+	ifa = ifa_ifwithaddr(sintosa(&sin));
+	if ((ia = ifatoia(ifa)) == NULL ||
+	    ia->ia4_flags & IN_IFF_DUPLICATED)
+		error = -1;
+	else if (ia->ia4_flags & (IN_IFF_TENTATIVE | IN_IFF_DETACHED))
+		error = 1;
+	else
+		error = 0;
+	pserialize_read_exit(s);
+
+	return error;
+}
Index: sys/netinet6/ip6_output.c
===================================================================
RCS file: /cvsroot/src/sys/netinet6/ip6_output.c,v
retrieving revision 1.173
diff -p -u -r1.173 ip6_output.c
--- sys/netinet6/ip6_output.c	1 Aug 2016 03:15:31 -0000	1.173
+++ sys/netinet6/ip6_output.c	7 Sep 2016 16:38:40 -0000
@@ -77,6 +77,7 @@ __KERNEL_RCSID(0, "$NetBSD: ip6_output.c
 #include <sys/protosw.h>
 #include <sys/socket.h>
 #include <sys/socketvar.h>
+#include <sys/syslog.h>
 #include <sys/systm.h>
 #include <sys/proc.h>
 #include <sys/kauth.h>
@@ -135,6 +136,7 @@ static int ip6_splithdr(struct mbuf *, s
 static int ip6_getpmtu(struct route *, struct route *, struct ifnet *,
     const struct in6_addr *, u_long *, int *);
 static int copypktopts(struct ip6_pktopts *, struct ip6_pktopts *, int);
+static int ip6_ifaddrvalid(const struct in6_addr *);
 
 #ifdef RFC2292
 static int ip6_pcbopts(struct ip6_pktopts **, struct socket *, struct sockopt *);
@@ -550,6 +552,22 @@ ip6_output(
 
 	/* scope check is done. */
 
+	/* Ensure we only sent from a valid address. */
+	if ((error = ip6_ifaddrvalid(&src0)) != 0) {
+		nd6log(LOG_ERR,
+		    "refusing to send from invalid address %s (pid %d)\n",
+		    ip6_sprintf(&src0), curproc->p_pid);
+		if (error == 1 && ip6->ip6_nxt == IPPROTO_TCP)
+			/* Address exists, but is tentative or detached.
+			 * We can't send from it because it's invalid,
+			 * so we drop the packet and continue ...
+			 * TCP will timeout eventually. */
+			error = 0;
+		else
+			error = EADDRNOTAVAIL;
+		goto bad;
+	}
+
 	if (rt == NULL || IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
 		dst = satocsin6(rtcache_getdst(ro));
 		KASSERT(dst != NULL);
@@ -3250,3 +3268,38 @@ ip6_optlen(struct in6pcb *in6p)
 	return len;
 #undef elen
 }
+
+/*
+ * Ensure sending address is valid.
+ * Returns 0 on success, -1 if an error should be sent back or 1
+ * if the packet could be dropped without error (protocol dependent).
+ */
+static int
+ip6_ifaddrvalid(const struct in6_addr *addr)
+{
+	struct sockaddr_in6 sin6;
+	int s, error;
+	struct ifaddr *ifa;
+	struct in6_ifaddr *ia6;
+
+	if (IN6_IS_ADDR_UNSPECIFIED(addr))
+		return 0;
+
+	memset(&sin6, 0, sizeof(sin6));
+	sin6.sin6_family = AF_INET6;
+	sin6.sin6_len = sizeof(sin6);
+	sin6.sin6_addr = *addr;
+
+	s = pserialize_read_enter();
+	ifa = ifa_ifwithaddr(sin6tosa(&sin6));
+	if ((ia6 = ifatoia6(ifa)) == NULL ||
+	    ia6->ia6_flags & (IN6_IFF_ANYCAST | IN6_IFF_DUPLICATED))
+		error = -1;
+	else if (ia6->ia6_flags & (IN6_IFF_TENTATIVE | IN6_IFF_DETACHED))
+		error = 1;
+	else
+		error = 0;
+	pserialize_read_exit(s);
+
+	return error;
+}


Home | Main Index | Thread Index | Old Index