Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files. =================================================================== RCS file: /ftp/cvs/cvsroot/src/sys/netinet/ip_input.c,v rcsdiff: /ftp/cvs/cvsroot/src/sys/netinet/ip_input.c,v: warning: Unknown phrases like `commitid ...;' are present. retrieving revision 1.135.2.6 retrieving revision 1.177 diff -u -p -r1.135.2.6 -r1.177 --- src/sys/netinet/ip_input.c 2002/10/10 18:43:59 1.135.2.6 +++ src/sys/netinet/ip_input.c 2003/09/06 00:24:54 1.177 @@ -1,4 +1,4 @@ -/* $NetBSD: ip_input.c,v 1.135.2.6 2002/10/10 18:43:59 jdolecek Exp $ */ +/* $NetBSD: ip_input.c,v 1.177 2003/09/06 00:24:54 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -78,11 +78,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors + * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * @@ -102,12 +98,13 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: ip_input.c,v 1.135.2.6 2002/10/10 18:43:59 jdolecek Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ip_input.c,v 1.177 2003/09/06 00:24:54 itojun Exp $"); #include "opt_gateway.h" #include "opt_pfil_hooks.h" #include "opt_ipsec.h" #include "opt_mrouting.h" +#include "opt_mbuftrace.h" #include "opt_inet_csum.h" #include @@ -150,6 +147,10 @@ __KERNEL_RCSID(0, "$NetBSD: ip_input.c,v #include #include #endif +#ifdef FAST_IPSEC +#include +#include +#endif /* FAST_IPSEC*/ #ifndef IPFORWARDING #ifdef GATEWAY @@ -196,6 +197,21 @@ int ip_mtudisc_timeout = IPMTUDISCTIMEOU #ifdef DIAGNOSTIC int ipprintfs = 0; #endif +/* + * XXX - Setting ip_checkinterface mostly implements the receive side of + * the Strong ES model described in RFC 1122, but since the routing table + * and transmit implementation do not implement the Strong ES model, + * setting this to 1 results in an odd hybrid. + * + * XXX - ip_checkinterface currently must be disabled if you use ipnat + * to translate the destination address to another local interface. + * + * XXX - ip_checkinterface must be disabled if you add IP aliases + * to the loopback interface instead of the interface where the + * packets for those addresses are received. + */ +int ip_checkinterface = 0; + struct rttimer_queue *ip_mtudisc_timeout_q = NULL; @@ -205,6 +221,9 @@ u_long in_ifaddrhash; /* size of hash int in_ifaddrentries; /* total number of addrs */ struct in_ifaddrhead in_ifaddr; struct in_ifaddrhashhead *in_ifaddrhashtbl; +u_long in_multihash; /* size of hash table - 1 */ +int in_multientries; /* total number of addrs */ +struct in_multihashhead *in_multihashtbl; struct ifqueue ipintrq; struct ipstat ipstat; u_int16_t ip_id; @@ -257,14 +276,14 @@ do { \ printf("%s:%d: ipq already locked\n", __FILE__, __LINE__); \ panic("ipq_lock"); \ } \ -} while (0) +} while (/*CONSTCOND*/ 0) #define IPQ_LOCK_CHECK() \ do { \ if (ipq_locked == 0) { \ printf("%s:%d: ipq lock not held\n", __FILE__, __LINE__); \ panic("ipq lock check"); \ } \ -} while (0) +} while (/*CONSTCOND*/ 0) #else #define IPQ_LOCK() (void) ipq_lock_try() #define IPQ_LOCK_CHECK() /* nothing */ @@ -272,6 +291,7 @@ do { \ #define IPQ_UNLOCK() ipq_unlock() +struct pool inmulti_pool; struct pool ipqent_pool; #ifdef INET_CSUM_COUNTERS @@ -309,6 +329,11 @@ static struct ip_srcrt { static void save_rte __P((u_char *, struct in_addr)); +#ifdef MBUFTRACE +struct mowner ip_rx_mowner = { "internet", "rx" }; +struct mowner ip_tx_mowner = { "internet", "tx" }; +#endif + /* * IP initialization: fill in IP protocol switch table. * All protocols not implemented in kernel go to raw IP protocol handler. @@ -319,6 +344,8 @@ ip_init() struct protosw *pr; int i; + pool_init(&inmulti_pool, sizeof(struct in_multi), 0, 0, 0, "inmltpl", + NULL); pool_init(&ipqent_pool, sizeof(struct ipqent), 0, 0, 0, "ipqepl", NULL); @@ -338,9 +365,9 @@ ip_init() TAILQ_INIT(&in_ifaddr); in_ifaddrhashtbl = hashinit(IN_IFADDR_HASH_SIZE, HASH_LIST, M_IFADDR, M_WAITOK, &in_ifaddrhash); - if (ip_mtudisc != 0) - ip_mtudisc_timeout_q = - rt_timer_queue_create(ip_mtudisc_timeout); + in_multihashtbl = hashinit(IN_IFADDR_HASH_SIZE, HASH_LIST, M_IPMADDR, + M_WAITOK, &in_multihash); + ip_mtudisc_timeout_q = rt_timer_queue_create(ip_mtudisc_timeout); #ifdef GATEWAY ipflow_init(); #endif @@ -360,6 +387,11 @@ ip_init() evcnt_attach_static(&ip_hwcsum_ok); evcnt_attach_static(&ip_swcsum); #endif /* INET_CSUM_COUNTERS */ + +#ifdef MBUFTRACE + MOWNER_ATTACH(&ip_tx_mowner); + MOWNER_ATTACH(&ip_rx_mowner); +#endif /* MBUFTRACE */ } struct sockaddr_in ipaddr = { sizeof(ipaddr), AF_INET }; @@ -380,6 +412,7 @@ ipintr() splx(s); if (m == 0) return; + MCLAIM(m, &ip_rx_mowner); ip_input(m); } } @@ -398,7 +431,16 @@ ip_input(struct mbuf *m) struct ipqent *ipqe; int hlen = 0, mff, len; int downmatch; + int checkif; + int srcrt = 0; +#ifdef FAST_IPSEC + struct m_tag *mtag; + struct tdb_ident *tdbi; + struct secpolicy *sp; + int s, error; +#endif /* FAST_IPSEC */ + MCLAIM(m, &ip_rx_mowner); #ifdef DIAGNOSTIC if ((m->m_flags & M_PKTHDR) == 0) panic("ipintr no HDR"); @@ -413,6 +455,7 @@ ip_input(struct mbuf *m) m->m_flags &= ~M_AUTHIPDGM; } #endif + /* * If no IP addresses have been set yet but the interfaces * are receiving, can't do anything with incoming packets yet. @@ -553,13 +596,17 @@ ip_input(struct mbuf *m) if (1) #endif { + struct in_addr odst; + + odst = ip->ip_dst; if (pfil_run_hooks(&inet_pfil_hook, &m, m->m_pkthdr.rcvif, - PFIL_IN) != 0) - return; + PFIL_IN) != 0) + return; if (m == NULL) return; ip = mtod(m, struct ip *); hlen = ip->ip_hl << 2; + srcrt = (odst.s_addr != ip->ip_dst.s_addr); } #endif /* PFIL_HOOKS */ @@ -582,6 +629,26 @@ ip_input(struct mbuf *m) return; /* + * Enable a consistency check between the destination address + * and the arrival interface for a unicast packet (the RFC 1122 + * strong ES model) if IP forwarding is disabled and the packet + * is not locally generated. + * + * XXX - Checking also should be disabled if the destination + * address is ipnat'ed to a different interface. + * + * XXX - Checking is incompatible with IP aliases added + * to the loopback interface instead of the interface where + * the packets are received. + * + * XXX - We need to add a per ifaddr flag for this so that + * we get finer grain control. + */ + checkif = ip_checkinterface && (ipforwarding == 0) && + (m->m_pkthdr.rcvif != NULL) && + ((m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) == 0); + + /* * Check our list of addresses, to see if the packet is for us. * * Traditional 4.4BSD did not consult IFF_UP at all. @@ -591,6 +658,8 @@ ip_input(struct mbuf *m) downmatch = 0; LIST_FOREACH(ia, &IN_IFADDR_HASH(ip->ip_dst.s_addr), ia_hash) { if (in_hosteq(ia->ia_addr.sin_addr, ip->ip_dst)) { + if (checkif && ia->ia_ifp != m->m_pkthdr.rcvif) + continue; if ((ia->ia_ifp->if_flags & IFF_UP) != 0) break; else @@ -703,8 +772,36 @@ ip_input(struct mbuf *m) goto bad; } #endif +#ifdef FAST_IPSEC + mtag = m_tag_find(m, PACKET_TAG_IPSEC_IN_DONE, NULL); + s = splsoftnet(); + if (mtag != NULL) { + tdbi = (struct tdb_ident *)(mtag + 1); + sp = ipsec_getpolicy(tdbi, IPSEC_DIR_INBOUND); + } else { + sp = ipsec_getpolicybyaddr(m, IPSEC_DIR_INBOUND, + IP_FORWARDING, &error); + } + if (sp == NULL) { /* NB: can happen if error */ + splx(s); + /*XXX error stat???*/ + DPRINTF(("ip_input: no SP for forwarding\n")); /*XXX*/ + goto bad; + } - ip_forward(m, 0); + /* + * Check security policy against packet attributes. + */ + error = ipsec_in_reject(sp, m); + KEY_FREESP(&sp); + splx(s); + if (error) { + ipstat.ips_cantforward++; + goto bad; + } +#endif /* FAST_IPSEC */ + + ip_forward(m, srcrt); } return; @@ -791,7 +888,7 @@ found: IPQ_UNLOCK(); } -#ifdef IPSEC +#if defined(IPSEC) /* * enforce IPsec policy checking if we are seeing last header. * note that we do not visit this with protocols with pcb layer @@ -803,6 +900,45 @@ found: goto bad; } #endif +#if FAST_IPSEC + /* + * enforce IPsec policy checking if we are seeing last header. + * note that we do not visit this with protocols with pcb layer + * code - like udp/tcp/raw ip. + */ + if ((inetsw[ip_protox[ip->ip_p]].pr_flags & PR_LASTHDR) != 0) { + /* + * Check if the packet has already had IPsec processing + * done. If so, then just pass it along. This tag gets + * set during AH, ESP, etc. input handling, before the + * packet is returned to the ip input queue for delivery. + */ + mtag = m_tag_find(m, PACKET_TAG_IPSEC_IN_DONE, NULL); + s = splsoftnet(); + if (mtag != NULL) { + tdbi = (struct tdb_ident *)(mtag + 1); + sp = ipsec_getpolicy(tdbi, IPSEC_DIR_INBOUND); + } else { + sp = ipsec_getpolicybyaddr(m, IPSEC_DIR_INBOUND, + IP_FORWARDING, &error); + } + if (sp != NULL) { + /* + * Check security policy against packet attributes. + */ + error = ipsec_in_reject(sp, m); + KEY_FREESP(&sp); + } else { + /* XXX error stat??? */ + error = EINVAL; +DPRINTF(("ip_input: no SP, packet discarded\n"));/*XXX*/ + goto bad; + } + splx(s); + if (error) + goto bad; + } +#endif /* FAST_IPSEC */ /* * Switch out to protocol's input routine. @@ -1391,6 +1527,7 @@ ip_srcroute() if (m == 0) return ((struct mbuf *)0); + MCLAIM(m, &inetdomain.dom_mowner); #define OPTSIZ (sizeof(ip_srcrt.nop) + sizeof(ip_srcrt.srcopt)) /* length is (nhops+1)*sizeof(addr) + sizeof(nop + srcrt header) */ @@ -1506,11 +1643,16 @@ ip_forward(m, srcrt) struct mbuf *mcopy; n_long dest; struct ifnet *destifp; -#ifdef IPSEC +#if defined(IPSEC) || defined(FAST_IPSEC) struct ifnet dummyifp; #endif /* + * We are now in the output path. + */ + MCLAIM(m, &ip_tx_mowner); + + /* * Clear any in-bound checksum flags for this packet. */ m->m_pkthdr.csum_flags = 0; @@ -1594,12 +1736,10 @@ ip_forward(m, srcrt) } } -#ifdef IPSEC - /* Don't lookup socket in forwarding case */ - (void)ipsec_setsocket(m, NULL); -#endif error = ip_output(m, (struct mbuf *)0, &ipforward_rt, - (IP_FORWARDING | (ip_directedbcast ? IP_ALLOWBROADCAST : 0)), 0); + (IP_FORWARDING | (ip_directedbcast ? IP_ALLOWBROADCAST : 0)), + (struct ip_moptions *)NULL, (struct socket *)NULL); + if (error) ipstat.ips_cantforward++; else { @@ -1639,7 +1779,7 @@ ip_forward(m, srcrt) case EMSGSIZE: type = ICMP_UNREACH; code = ICMP_UNREACH_NEEDFRAG; -#ifndef IPSEC +#if !defined(IPSEC) && !defined(FAST_IPSEC) if (ipforward_rt.ro_rt) destifp = ipforward_rt.ro_rt->rt_ifp; #else @@ -1656,17 +1796,15 @@ ip_forward(m, srcrt) struct route *ro; sp = ipsec4_getpolicybyaddr(mcopy, - IPSEC_DIR_OUTBOUND, - IP_FORWARDING, - &ipsecerror); + IPSEC_DIR_OUTBOUND, IP_FORWARDING, + &ipsecerror); if (sp == NULL) destifp = ipforward_rt.ro_rt->rt_ifp; else { /* count IPsec header size */ ipsechdr = ipsec4_hdrsiz(mcopy, - IPSEC_DIR_OUTBOUND, - NULL); + IPSEC_DIR_OUTBOUND, NULL); /* * find the correct route for outer IPv4 @@ -1692,7 +1830,11 @@ ip_forward(m, srcrt) } } +#ifdef IPSEC key_freesp(sp); +#else + KEY_FREESP(&sp); +#endif } } #endif /*IPSEC*/ @@ -1828,13 +1970,8 @@ ip_sysctl(name, namelen, oldp, oldlenp, case IPCTL_MTUDISC: error = sysctl_int(oldp, oldlenp, newp, newlen, &ip_mtudisc); - if (ip_mtudisc != 0 && ip_mtudisc_timeout_q == NULL) { - ip_mtudisc_timeout_q = - rt_timer_queue_create(ip_mtudisc_timeout); - } else if (ip_mtudisc == 0 && ip_mtudisc_timeout_q != NULL) { - rt_timer_queue_destroy(ip_mtudisc_timeout_q, TRUE); - ip_mtudisc_timeout_q = NULL; - } + if (error == 0 && ip_mtudisc == 0) + rt_timer_queue_remove_all(ip_mtudisc_timeout_q, TRUE); return error; case IPCTL_ANONPORTMIN: old = anonportmin; @@ -1870,7 +2007,7 @@ ip_sysctl(name, namelen, oldp, oldlenp, ip_mtudisc_timeout = old; return (EINVAL); } - if (ip_mtudisc_timeout_q != NULL) + if (error == 0) rt_timer_queue_change(ip_mtudisc_timeout_q, ip_mtudisc_timeout); return (error); @@ -1931,6 +2068,9 @@ ip_sysctl(name, namelen, oldp, oldlenp, return (sysctl_int(oldp, oldlenp, newp, newlen, &ip_maxfragpackets)); + case IPCTL_CHECKINTERFACE: + return (sysctl_int(oldp, oldlenp, newp, newlen, + &ip_checkinterface)); default: return (EOPNOTSUPP); }