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.150 retrieving revision 1.168 diff -u -p -r1.150 -r1.168 --- src/sys/netinet/ip_input.c 2002/05/12 20:33:50 1.150 +++ src/sys/netinet/ip_input.c 2003/06/30 01:21:11 1.168 @@ -1,9 +1,9 @@ -/* $NetBSD: ip_input.c,v 1.150 2002/05/12 20:33:50 matt Exp $ */ +/* $NetBSD: ip_input.c,v 1.168 2003/06/30 01:21:11 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -15,7 +15,7 @@ * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -102,12 +102,13 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: ip_input.c,v 1.150 2002/05/12 20:33:50 matt Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ip_input.c,v 1.168 2003/06/30 01:21:11 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 @@ -168,7 +169,7 @@ __KERNEL_RCSID(0, "$NetBSD: ip_input.c,v #define IPALLOWSRCRT 1 /* allow source-routed packets */ #endif #ifndef IPMTUDISC -#define IPMTUDISC 0 +#define IPMTUDISC 1 #endif #ifndef IPMTUDISCTIMEOUT #define IPMTUDISCTIMEOUT (10 * 60) /* as per RFC 1191 */ @@ -192,10 +193,25 @@ int ip_forwsrcrt = IPFORWSRCRT; int ip_directedbcast = IPDIRECTEDBCAST; int ip_allowsrcrt = IPALLOWSRCRT; int ip_mtudisc = IPMTUDISC; -u_int ip_mtudisc_timeout = IPMTUDISCTIMEOUT; +int ip_mtudisc_timeout = IPMTUDISCTIMEOUT; #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,10 @@ 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_multihead in_multi; +struct in_multihashhead *in_multihashtbl; struct ifqueue ipintrq; struct ipstat ipstat; u_int16_t ip_id; @@ -257,14 +277,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 +292,7 @@ do { \ #define IPQ_UNLOCK() ipq_unlock() +struct pool inmulti_pool; struct pool ipqent_pool; #ifdef INET_CSUM_COUNTERS @@ -309,6 +330,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 +345,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 +366,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 +388,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 +413,7 @@ ipintr() splx(s); if (m == 0) return; + MCLAIM(m, &ip_rx_mowner); ip_input(m); } } @@ -398,7 +432,9 @@ ip_input(struct mbuf *m) struct ipqent *ipqe; int hlen = 0, mff, len; int downmatch; + int checkif; + MCLAIM(m, &ip_rx_mowner); #ifdef DIAGNOSTIC if ((m->m_flags & M_PKTHDR) == 0) panic("ipintr no HDR"); @@ -413,6 +449,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. @@ -420,10 +457,24 @@ ip_input(struct mbuf *m) if (TAILQ_FIRST(&in_ifaddr) == 0) goto bad; ipstat.ips_total++; - if (m->m_len < sizeof (struct ip) && - (m = m_pullup(m, sizeof (struct ip))) == 0) { - ipstat.ips_toosmall++; - return; + /* + * If the IP header is not aligned, slurp it up into a new + * mbuf with space for link headers, in the event we forward + * it. Otherwise, if it is aligned, make sure the entire + * base IP header is in the first mbuf of the chain. + */ + if (IP_HDR_ALIGNED_P(mtod(m, caddr_t)) == 0) { + if ((m = m_copyup(m, sizeof(struct ip), + (max_linkhdr + 3) & ~3)) == NULL) { + /* XXXJRT new stat, please */ + ipstat.ips_toosmall++; + return; + } + } else if (__predict_false(m->m_len < sizeof (struct ip))) { + if ((m = m_pullup(m, sizeof (struct ip))) == NULL) { + ipstat.ips_toosmall++; + return; + } } ip = mtod(m, struct ip *); if (ip->ip_v != IPVERSION) { @@ -540,8 +591,8 @@ ip_input(struct mbuf *m) #endif { 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 *); @@ -558,12 +609,6 @@ ip_input(struct mbuf *m) #endif /* - * Convert fields to host representation. - */ - NTOHS(ip->ip_len); - NTOHS(ip->ip_off); - - /* * Process options and, if not destined for us, * ship it on. ip_dooptions returns 1 when an * error was detected (causing an icmp message @@ -574,6 +619,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. @@ -583,6 +648,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 @@ -708,7 +775,15 @@ ours: * if the packet was previously fragmented, * but it's not worth the time; just let them time out.) */ - if (ip->ip_off & ~(IP_DF|IP_RF)) { + if (ip->ip_off & ~htons(IP_DF|IP_RF)) { + if (M_READONLY(m)) { + if ((m = m_pullup(m, hlen)) == NULL) { + ipstat.ips_toosmall++; + goto bad; + } + ip = mtod(m, struct ip *); + } + /* * Look for queue of fragments * of this datagram. @@ -728,27 +803,28 @@ found: * set ipqe_mff if more fragments are expected, * convert offset of this to bytes. */ - ip->ip_len -= hlen; - mff = (ip->ip_off & IP_MF) != 0; + ip->ip_len = htons(ntohs(ip->ip_len) - hlen); + mff = (ip->ip_off & htons(IP_MF)) != 0; if (mff) { /* * Make sure that fragments have a data length * that's a non-zero multiple of 8 bytes. */ - if (ip->ip_len == 0 || (ip->ip_len & 0x7) != 0) { + if (ntohs(ip->ip_len) == 0 || + (ntohs(ip->ip_len) & 0x7) != 0) { ipstat.ips_badfrags++; IPQ_UNLOCK(); goto bad; } } - ip->ip_off <<= 3; + ip->ip_off = htons((ntohs(ip->ip_off) & IP_OFFMASK) << 3); /* * If datagram marked as having more fragments * or if this is not the first fragment, * attempt reassembly; if it succeeds, proceed. */ - if (mff || ip->ip_off) { + if (mff || ip->ip_off != htons(0)) { ipstat.ips_fragments++; ipqe = pool_get(&ipqent_pool, PR_NOWAIT); if (ipqe == NULL) { @@ -767,7 +843,7 @@ found: ipstat.ips_reassembled++; ip = mtod(m, struct ip *); hlen = ip->ip_hl << 2; - ip->ip_len += hlen; + ip->ip_len = htons(ntohs(ip->ip_len) + hlen); } else if (fp) ip_freef(fp); @@ -792,7 +868,7 @@ found: */ #if IFA_STATS if (ia && ip) - ia->ia_ifa.ifa_data.ifad_inbytes += ip->ip_len; + ia->ia_ifa.ifa_data.ifad_inbytes += ntohs(ip->ip_len); #endif ipstat.ips_delivered++; { @@ -872,7 +948,7 @@ ip_reass(ipqe, fp) */ for (p = NULL, q = TAILQ_FIRST(&fp->ipq_fragq); q != NULL; p = q, q = TAILQ_NEXT(q, ipqe_q)) - if (q->ipqe_ip->ip_off > ipqe->ipqe_ip->ip_off) + if (ntohs(q->ipqe_ip->ip_off) > ntohs(ipqe->ipqe_ip->ip_off)) break; /* @@ -881,14 +957,16 @@ ip_reass(ipqe, fp) * segment. If it provides all of our data, drop us. */ if (p != NULL) { - i = p->ipqe_ip->ip_off + p->ipqe_ip->ip_len - - ipqe->ipqe_ip->ip_off; + i = ntohs(p->ipqe_ip->ip_off) + ntohs(p->ipqe_ip->ip_len) - + ntohs(ipqe->ipqe_ip->ip_off); if (i > 0) { - if (i >= ipqe->ipqe_ip->ip_len) + if (i >= ntohs(ipqe->ipqe_ip->ip_len)) goto dropfrag; m_adj(ipqe->ipqe_m, i); - ipqe->ipqe_ip->ip_off += i; - ipqe->ipqe_ip->ip_len -= i; + ipqe->ipqe_ip->ip_off = + htons(ntohs(ipqe->ipqe_ip->ip_off) + i); + ipqe->ipqe_ip->ip_len = + htons(ntohs(ipqe->ipqe_ip->ip_len) - i); } } @@ -896,13 +974,16 @@ ip_reass(ipqe, fp) * While we overlap succeeding segments trim them or, * if they are completely covered, dequeue them. */ - for (; q != NULL && ipqe->ipqe_ip->ip_off + ipqe->ipqe_ip->ip_len > - q->ipqe_ip->ip_off; q = nq) { - i = (ipqe->ipqe_ip->ip_off + ipqe->ipqe_ip->ip_len) - - q->ipqe_ip->ip_off; - if (i < q->ipqe_ip->ip_len) { - q->ipqe_ip->ip_len -= i; - q->ipqe_ip->ip_off += i; + for (; q != NULL && + ntohs(ipqe->ipqe_ip->ip_off) + ntohs(ipqe->ipqe_ip->ip_len) > + ntohs(q->ipqe_ip->ip_off); q = nq) { + i = (ntohs(ipqe->ipqe_ip->ip_off) + + ntohs(ipqe->ipqe_ip->ip_len)) - ntohs(q->ipqe_ip->ip_off); + if (i < ntohs(q->ipqe_ip->ip_len)) { + q->ipqe_ip->ip_len = + htons(ntohs(q->ipqe_ip->ip_len) - i); + q->ipqe_ip->ip_off = + htons(ntohs(q->ipqe_ip->ip_off) + i); m_adj(q->ipqe_m, i); break; } @@ -925,9 +1006,9 @@ insert: next = 0; for (p = NULL, q = TAILQ_FIRST(&fp->ipq_fragq); q != NULL; p = q, q = TAILQ_NEXT(q, ipqe_q)) { - if (q->ipqe_ip->ip_off != next) + if (ntohs(q->ipqe_ip->ip_off) != next) return (0); - next += q->ipqe_ip->ip_len; + next += ntohs(q->ipqe_ip->ip_len); } if (p->ipqe_mff) return (0); @@ -962,7 +1043,7 @@ insert: * dequeue and discard fragment reassembly header. * Make header visible. */ - ip->ip_len = next; + ip->ip_len = htons(next); ip->ip_src = fp->ipq_src; ip->ip_dst = fp->ipq_dst; LIST_REMOVE(fp, ipq_q); @@ -1369,6 +1450,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) */ @@ -1445,7 +1527,7 @@ ip_stripoptions(m, mopt) m->m_len -= olen; if (m->m_flags & M_PKTHDR) m->m_pkthdr.len -= olen; - ip->ip_len -= olen; + ip->ip_len = htons(ntohs(ip->ip_len) - olen); ip->ip_hl = sizeof (struct ip) >> 2; } @@ -1489,6 +1571,11 @@ ip_forward(m, srcrt) #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; @@ -1535,7 +1622,7 @@ ip_forward(m, srcrt) * we need to generate an ICMP message to the src. * Pullup to avoid sharing mbuf cluster between m and mcopy. */ - mcopy = m_copym(m, 0, imin((int)ip->ip_len, 68), M_DONTWAIT); + mcopy = m_copym(m, 0, imin(ntohs(ip->ip_len), 68), M_DONTWAIT); if (mcopy) mcopy = m_pullup(mcopy, ip->ip_hl << 2); @@ -1662,6 +1749,8 @@ ip_forward(m, srcrt) ro = &sp->req->sav->sah->sa_route; if (ro->ro_rt && ro->ro_rt->rt_ifp) { dummyifp.if_mtu = + ro->ro_rt->rt_rmx.rmx_mtu ? + ro->ro_rt->rt_rmx.rmx_mtu : ro->ro_rt->rt_ifp->if_mtu; dummyifp.if_mtu -= ipsechdr; destifp = &dummyifp; @@ -1804,13 +1893,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; @@ -1839,10 +1923,15 @@ ip_sysctl(name, namelen, oldp, oldlenp, } return (error); case IPCTL_MTUDISCTIMEOUT: + old = ip_mtudisc_timeout; error = sysctl_int(oldp, oldlenp, newp, newlen, &ip_mtudisc_timeout); - if (ip_mtudisc_timeout_q != NULL) - rt_timer_queue_change(ip_mtudisc_timeout_q, + if (ip_mtudisc_timeout < 0) { + ip_mtudisc_timeout = old; + return (EINVAL); + } + if (error == 0) + rt_timer_queue_change(ip_mtudisc_timeout_q, ip_mtudisc_timeout); return (error); #ifdef GATEWAY @@ -1863,13 +1952,13 @@ ip_sysctl(name, namelen, oldp, oldlenp, &hostzeroisbroadcast)); #if NGIF > 0 case IPCTL_GIF_TTL: - return(sysctl_int(oldp, oldlenp, newp, newlen, + return (sysctl_int(oldp, oldlenp, newp, newlen, &ip_gif_ttl)); #endif #if NGRE > 0 case IPCTL_GRE_TTL: - return(sysctl_int(oldp, oldlenp, newp, newlen, + return (sysctl_int(oldp, oldlenp, newp, newlen, &ip_gre_ttl)); #endif @@ -1902,6 +1991,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); }