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