From ce66a6feadb58e1c9ca87d6c2ab35f9c3fcf7b47 Mon Sep 17 00:00:00 2001
From: David Gwynne <dlg@cvs.openbsd.org>
Date: Tue, 19 Apr 2011 03:47:30 +0000
Subject: reintroduce using the RB tree for local address lookups. this is
 confusing because both addresses and broadcast addresses are put into the
 tree.

there are two types of local address lookup. the first is when the
socket layer wants a local address, the second is in ip_input when
the kernel is figuring out the packet is for it to process or
forward.

ip_input considers local addresses and broadcast addresses as local,
however, the handling of broadcast addresses is different depending
on whether ip_directedbcast is set. if if ip_directbcast is unset
then a packet coming in on any interface to any of the systems
broadcast addresses is considered local, otherwise the broadcast
packet must exist on the interface it was received on.

the code also needs to consider classful broadcast addresses so we
can continue some legacy applications (eg, netbooting old sparcs
that use rarp and bootparam requests to classful broadcast addresses
as per PR6382). this diff maintains that support, but restricts it
to packets that are broadcast on the link layer (eg, ethernet
broadcasted packets), and it only looks up addresses on the local
interface. we now only support classful broadcast addresses on local
interfaces to avoid weird side effects with packets routed to us.

the ip4 socket layer does lookups for local addresses with a wrapper
around the global address tree that rejects matches against broadcast
addresses. we now no longer support bind sockets to broadcast
addresses, no matter what the value of ip_directedbcast is.

ok henning@
testing (and possibly ok) claudio@
---
 sys/netinet/in_pcb.c   |   6 +--
 sys/netinet/ip_input.c | 126 +++++++++++++++++++++++++++++++++----------------
 sys/netinet/ip_var.h   |   4 +-
 sys/netinet/raw_ip.c   |   6 +--
 4 files changed, 94 insertions(+), 48 deletions(-)

(limited to 'sys')

diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c
index ba9a0b6382f..c9981fcc442 100644
--- a/sys/netinet/in_pcb.c
+++ b/sys/netinet/in_pcb.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: in_pcb.c,v 1.115 2011/04/14 08:15:26 claudio Exp $	*/
+/*	$OpenBSD: in_pcb.c,v 1.116 2011/04/19 03:47:29 dlg Exp $	*/
 /*	$NetBSD: in_pcb.c,v 1.25 1996/02/13 23:41:53 christos Exp $	*/
 
 /*
@@ -276,8 +276,8 @@ in_pcbbind(v, nam, p)
 		} else if (sin->sin_addr.s_addr != INADDR_ANY) {
 			sin->sin_port = 0;		/* yech... */
 			if (!(so->so_options & SO_BINDANY) &&
-			    in_iawithaddr(sin->sin_addr, NULL,
-			    inp->inp_rtableid) == 0)
+			    in_iawithaddr(sin->sin_addr,
+			    inp->inp_rtableid) == NULL)
 				return (EADDRNOTAVAIL);
 		}
 		if (lport) {
diff --git a/sys/netinet/ip_input.c b/sys/netinet/ip_input.c
index 72f8227047e..0d71e404e78 100644
--- a/sys/netinet/ip_input.c
+++ b/sys/netinet/ip_input.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: ip_input.c,v 1.190 2011/04/14 08:15:26 claudio Exp $	*/
+/*	$OpenBSD: ip_input.c,v 1.191 2011/04/19 03:47:29 dlg Exp $	*/
 /*	$NetBSD: ip_input.c,v 1.30 1996/03/16 23:53:58 christos Exp $	*/
 
 /*
@@ -144,6 +144,8 @@ struct pool ipq_pool;
 
 struct ipstat ipstat;
 
+int	in_ouraddr(struct in_addr, struct mbuf *);
+
 char *
 inet_ntoa(ina)
 	struct in_addr ina;
@@ -259,7 +261,6 @@ ipv4_input(m)
 {
 	struct ip *ip;
 	struct ipq *fp;
-	struct in_ifaddr *ia;
 	struct ipqent *ipqe;
 	int hlen, mff, len;
 	in_addr_t pfrdr = 0;
@@ -387,25 +388,9 @@ ipv4_input(m)
 	        return;
 	}
 
-	if (m->m_pkthdr.pf.flags & PF_TAG_DIVERTED)
+	if (in_ouraddr(ip->ip_dst, m))
 		goto ours;
 
-#if NPF > 0
-	if (m->m_pkthdr.pf.statekey &&
-	    ((struct pf_state_key *)m->m_pkthdr.pf.statekey)->inp)
-		goto ours;
-
-	/*
-	 * Check our list of addresses, to see if the packet is for us.
-	 * if we have linked state keys it is certainly to be forwarded.
-	 */
-	if (!m->m_pkthdr.pf.statekey ||
-	    !((struct pf_state_key *)m->m_pkthdr.pf.statekey)->reverse)
-#endif
-		if ((ia = in_iawithaddr(ip->ip_dst, m, m->m_pkthdr.rdomain)) !=
-		    NULL && (ia->ia_ifp->if_flags & IFF_UP))
-			goto ours;
-
 	if (IN_MULTICAST(ip->ip_dst.s_addr)) {
 		struct in_multi *inm;
 #ifdef MROUTING
@@ -679,33 +664,94 @@ bad:
 	m_freem(m);
 }
 
-struct in_ifaddr *
-in_iawithaddr(struct in_addr ina, struct mbuf *m, u_int rdomain)
+int
+in_ouraddr(struct in_addr ina, struct mbuf *m)
 {
-	struct in_ifaddr *ia;
+	struct in_ifaddr	*ia;
+	struct sockaddr_in	 sin;
+#if NPF > 0
+	struct pf_state_key	*key;
 
-	rdomain = rtable_l2(rdomain);
-	TAILQ_FOREACH(ia, &in_ifaddr, ia_list) {
-		if (ia->ia_ifp->if_rdomain != rdomain)
-			continue;
-		if (ina.s_addr == ia->ia_addr.sin_addr.s_addr)
-			return ia;
-		/* check ancient classful too, e. g. for rarp-based netboot */
-		if (((ip_directedbcast == 0) || (m && ip_directedbcast &&
-		    ia->ia_ifp == m->m_pkthdr.rcvif)) &&
-		    (ia->ia_ifp->if_flags & IFF_BROADCAST)) {
-			if (ina.s_addr == ia->ia_broadaddr.sin_addr.s_addr ||
+	if (m->m_pkthdr.pf.flags & PF_TAG_DIVERTED)
+		return (1);
+
+	key = (struct pf_state_key *)m->m_pkthdr.pf.statekey;
+	if (key != NULL) {
+		if (key->inp != NULL)
+			return (1);
+
+		/* If we have linked state keys it is certainly forwarded. */
+		if (key->reverse != NULL)
+			return (0);
+	}
+#endif
+
+	bzero(&sin, sizeof(sin));
+	sin.sin_len = sizeof(sin);
+	sin.sin_family = AF_INET;
+	sin.sin_addr = ina;
+	ia = (struct in_ifaddr *)ifa_ifwithaddr(sintosa(&sin),
+	    m->m_pkthdr.rdomain);
+
+	if (ia == NULL) {
+		/*
+		 * No local address or broadcast address found, so check for
+		 * ancient classful broadcast addresses.
+		 * It must have been broadcast on the link layer, and for an
+		 * address on the interface it was received on.
+		 */
+		if (!ISSET(m->m_flags, M_BCAST) ||
+		    !IN_CLASSFULBROADCAST(ina.s_addr, ina.s_addr))
+			return (0);
+
+		/*
+		 * The check in the loop assumes you only rx a packet on an UP
+		 * interface, and that M_BCAST will only be set on a BROADCAST
+		 * interface.
+		 */
+		TAILQ_FOREACH(ia, &in_ifaddr, ia_list) {
+			if (ia->ia_ifp == m->m_pkthdr.rcvif &&
+			    ia->ia_ifp->if_rdomain == m->m_pkthdr.rdomain &&
 			    IN_CLASSFULBROADCAST(ina.s_addr,
-			    ia->ia_addr.sin_addr.s_addr)) {
-				/* Make sure M_BCAST is set */
-				if (m)
-					m->m_flags |= M_BCAST;
-				return ia;
-			}
+			    ia->ia_addr.sin_addr.s_addr))
+				return (1);
 		}
+
+		return (0);
+	}
+
+	if (ina.s_addr != ia->ia_addr.sin_addr.s_addr) {
+		/*
+		 * This matches a broadcast address on one of our interfaces.
+		 * If directedbcast is enabled we only consider it local if it
+		 * is received on the interface with that address.
+		 */
+		if (ip_directedbcast && ia->ia_ifp != m->m_pkthdr.rcvif)
+			return (0);
+
+		/* Make sure M_BCAST is set */
+		if (m)
+			m->m_flags |= M_BCAST;
 	}
 
-	return NULL;
+	return (ISSET(ia->ia_ifp->if_flags, IFF_UP));
+}
+
+struct in_ifaddr *
+in_iawithaddr(struct in_addr ina, u_int rdomain)
+{
+	struct in_ifaddr	*ia;
+	struct sockaddr_in	 sin;
+
+	bzero(&sin, sizeof(sin));
+	sin.sin_len = sizeof(sin);
+	sin.sin_family = AF_INET;
+	sin.sin_addr = ina;
+	ia = (struct in_ifaddr *)ifa_ifwithaddr(sintosa(&sin), rdomain);
+	if (ia == NULL || ina.s_addr == ia->ia_addr.sin_addr.s_addr)
+		return (ia);
+
+	return (NULL);
 }
 
 /*
diff --git a/sys/netinet/ip_var.h b/sys/netinet/ip_var.h
index adacedc2d96..045304469bd 100644
--- a/sys/netinet/ip_var.h
+++ b/sys/netinet/ip_var.h
@@ -1,4 +1,4 @@
-/*	$OpenBSD: ip_var.h,v 1.41 2011/04/14 08:15:26 claudio Exp $	*/
+/*	$OpenBSD: ip_var.h,v 1.42 2011/04/19 03:47:29 dlg Exp $	*/
 /*	$NetBSD: ip_var.h,v 1.16 1996/02/13 23:43:20 christos Exp $	*/
 
 /*
@@ -172,7 +172,7 @@ int	 ip_pcbopts(struct mbuf **, struct mbuf *);
 struct mbuf *
 	 ip_reass(struct ipqent *, struct ipq *);
 struct in_ifaddr *
-	 in_iawithaddr(struct in_addr, struct mbuf *, u_int);
+	 in_iawithaddr(struct in_addr, u_int);
 struct in_ifaddr *
 	 ip_rtaddr(struct in_addr, u_int);
 u_int16_t
diff --git a/sys/netinet/raw_ip.c b/sys/netinet/raw_ip.c
index 951023bbaed..db470c84bb2 100644
--- a/sys/netinet/raw_ip.c
+++ b/sys/netinet/raw_ip.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: raw_ip.c,v 1.53 2011/04/14 08:15:26 claudio Exp $	*/
+/*	$OpenBSD: raw_ip.c,v 1.54 2011/04/19 03:47:29 dlg Exp $	*/
 /*	$NetBSD: raw_ip.c,v 1.25 1996/02/18 18:58:33 christos Exp $	*/
 
 /*
@@ -424,8 +424,8 @@ rip_usrreq(struct socket *so, int req, struct mbuf *m, struct mbuf *nam,
 		     (addr->sin_family != AF_IMPLINK)) ||
 		    (addr->sin_addr.s_addr &&
 		     (!(so->so_options & SO_BINDANY) &&
-		     in_iawithaddr(addr->sin_addr, NULL, inp->inp_rtableid) ==
-		     0))) {
+		     in_iawithaddr(addr->sin_addr, inp->inp_rtableid) ==
+		     NULL))) {
 			error = EADDRNOTAVAIL;
 			break;
 		}
-- 
cgit v1.2.3