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.130.2.4 retrieving revision 1.158 diff -u -p -r1.130.2.4 -r1.158 --- src/sys/netinet/ip_input.c 2001/09/21 22:36:48 1.130.2.4 +++ src/sys/netinet/ip_input.c 2002/09/23 13:43:27 1.158 @@ -1,9 +1,9 @@ -/* $NetBSD: ip_input.c,v 1.130.2.4 2001/09/21 22:36:48 nathanw Exp $ */ +/* $NetBSD: ip_input.c,v 1.158 2002/09/23 13:43:27 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 @@ -101,6 +101,9 @@ * @(#)ip_input.c 8.2 (Berkeley) 1/4/94 */ +#include +__KERNEL_RCSID(0, "$NetBSD: ip_input.c,v 1.158 2002/09/23 13:43:27 itojun Exp $"); + #include "opt_gateway.h" #include "opt_pfil_hooks.h" #include "opt_ipsec.h" @@ -118,11 +121,7 @@ #include #include #include -#include #include - -#include - #include #include @@ -140,6 +139,8 @@ /* just for gif_ttl */ #include #include "gif.h" +#include +#include "gre.h" #ifdef MROUTING #include @@ -167,7 +168,7 @@ #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 */ @@ -191,7 +192,7 @@ 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 @@ -200,6 +201,8 @@ struct rttimer_queue *ip_mtudisc_timeout extern struct domain inetdomain; int ipqmaxlen = IFQ_MAXLEN; +u_long in_ifaddrhash; /* size of hash table - 1 */ +int in_ifaddrentries; /* total number of addrs */ struct in_ifaddrhead in_ifaddr; struct in_ifaddrhashhead *in_ifaddrhashtbl; struct ifqueue ipintrq; @@ -224,7 +227,7 @@ ipq_lock_try() int s; /* - * Use splvm() -- we're bloking things that would cause + * Use splvm() -- we're blocking things that would cause * mbuf allocation. */ s = splvm(); @@ -317,7 +320,7 @@ ip_init() int i; pool_init(&ipqent_pool, sizeof(struct ipqent), 0, 0, 0, "ipqepl", - 0, NULL, NULL, M_IPQ); + NULL); pr = pffindproto(PF_INET, IPPROTO_RAW, SOCK_RAW); if (pr == 0) @@ -336,7 +339,7 @@ ip_init() in_ifaddrhashtbl = hashinit(IN_IFADDR_HASH_SIZE, HASH_LIST, M_IFADDR, M_WAITOK, &in_ifaddrhash); if (ip_mtudisc != 0) - ip_mtudisc_timeout_q = + ip_mtudisc_timeout_q = rt_timer_queue_create(ip_mtudisc_timeout); #ifdef GATEWAY ipflow_init(); @@ -414,13 +417,27 @@ ip_input(struct mbuf *m) * If no IP addresses have been set yet but the interfaces * are receiving, can't do anything with incoming packets yet. */ - if (in_ifaddr.tqh_first == 0) + 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) { @@ -508,7 +525,7 @@ ip_input(struct mbuf *m) } #ifdef IPSEC - /* ipflow (IP fast fowarding) is not compatible with IPsec. */ + /* ipflow (IP fast forwarding) is not compatible with IPsec. */ m->m_flags &= ~M_CANFASTFWD; #else /* @@ -542,6 +559,7 @@ ip_input(struct mbuf *m) if (m == NULL) return; ip = mtod(m, struct ip *); + hlen = ip->ip_hl << 2; } #endif /* PFIL_HOOKS */ @@ -554,12 +572,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 @@ -577,9 +589,7 @@ ip_input(struct mbuf *m) * as not mine. */ downmatch = 0; - for (ia = IN_IFADDR_HASH(ip->ip_dst.s_addr).lh_first; - ia != NULL; - ia = ia->ia_hash.le_next) { + 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 ((ia->ia_ifp->if_flags & IFF_UP) != 0) break; @@ -590,9 +600,9 @@ ip_input(struct mbuf *m) if (ia != NULL) goto ours; if (m->m_pkthdr.rcvif->if_flags & IFF_BROADCAST) { - for (ifa = m->m_pkthdr.rcvif->if_addrlist.tqh_first; - ifa != NULL; ifa = ifa->ifa_list.tqe_next) { - if (ifa->ifa_addr->sa_family != AF_INET) continue; + TAILQ_FOREACH(ifa, &m->m_pkthdr.rcvif->if_addrlist, ifa_list) { + if (ifa->ifa_addr->sa_family != AF_INET) + continue; ia = ifatoia(ifa); if (in_hosteq(ip->ip_dst, ia->ia_broadaddr.sin_addr) || in_hosteq(ip->ip_dst, ia->ia_netbroadcast) || @@ -616,7 +626,7 @@ ip_input(struct mbuf *m) #ifdef MROUTING extern struct socket *ip_mrouter; - if (m->m_flags & M_EXT) { + if (M_READONLY(m)) { if ((m = m_pullup(m, hlen)) == 0) { ipstat.ips_toosmall++; return; @@ -687,6 +697,13 @@ ip_input(struct mbuf *m) ipstat.ips_cantforward++; return; } +#ifdef IPSEC + if (ipsec4_in_reject(m, NULL)) { + ipsecstat.in_polvio++; + goto bad; + } +#endif + ip_forward(m, 0); } return; @@ -699,13 +716,21 @@ 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. */ IPQ_LOCK(); - for (fp = ipq.lh_first; fp != NULL; fp = fp->ipq_q.le_next) + LIST_FOREACH(fp, &ipq, ipq_q) if (ip->ip_id == fp->ipq_id && in_hosteq(ip->ip_src, fp->ipq_src) && in_hosteq(ip->ip_dst, fp->ipq_dst) && @@ -719,27 +744,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) { @@ -758,7 +784,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); @@ -783,7 +809,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++; { @@ -851,7 +877,7 @@ ip_reass(ipqe, fp) fp->ipq_ttl = IPFRAGTTL; fp->ipq_p = ipqe->ipqe_ip->ip_p; fp->ipq_id = ipqe->ipqe_ip->ip_id; - LIST_INIT(&fp->ipq_fragq); + TAILQ_INIT(&fp->ipq_fragq); fp->ipq_src = ipqe->ipqe_ip->ip_src; fp->ipq_dst = ipqe->ipqe_ip->ip_dst; p = NULL; @@ -861,9 +887,9 @@ ip_reass(ipqe, fp) /* * Find a segment which begins after this one does. */ - for (p = NULL, q = fp->ipq_fragq.lh_first; q != NULL; - p = q, q = q->ipqe_q.le_next) - if (q->ipqe_ip->ip_off > ipqe->ipqe_ip->ip_off) + for (p = NULL, q = TAILQ_FIRST(&fp->ipq_fragq); q != NULL; + p = q, q = TAILQ_NEXT(q, ipqe_q)) + if (ntohs(q->ipqe_ip->ip_off) > ntohs(ipqe->ipqe_ip->ip_off)) break; /* @@ -872,14 +898,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); } } @@ -887,19 +915,22 @@ 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; } - nq = q->ipqe_q.le_next; + nq = TAILQ_NEXT(q, ipqe_q); m_freem(q->ipqe_m); - LIST_REMOVE(q, ipqe_q); + TAILQ_REMOVE(&fp->ipq_fragq, q, ipqe_q); pool_put(&ipqent_pool, q); } @@ -909,16 +940,16 @@ insert: * check for complete reassembly. */ if (p == NULL) { - LIST_INSERT_HEAD(&fp->ipq_fragq, ipqe, ipqe_q); + TAILQ_INSERT_HEAD(&fp->ipq_fragq, ipqe, ipqe_q); } else { - LIST_INSERT_AFTER(p, ipqe, ipqe_q); + TAILQ_INSERT_AFTER(&fp->ipq_fragq, p, ipqe, ipqe_q); } next = 0; - for (p = NULL, q = fp->ipq_fragq.lh_first; q != NULL; - p = q, q = q->ipqe_q.le_next) { - if (q->ipqe_ip->ip_off != next) + for (p = NULL, q = TAILQ_FIRST(&fp->ipq_fragq); q != NULL; + p = q, q = TAILQ_NEXT(q, ipqe_q)) { + 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); @@ -927,7 +958,7 @@ insert: * Reassembly is complete. Check for a bogus message size and * concatenate fragments. */ - q = fp->ipq_fragq.lh_first; + q = TAILQ_FIRST(&fp->ipq_fragq); ip = q->ipqe_ip; if ((next + (ip->ip_hl << 2)) > IP_MAXPACKET) { ipstat.ips_toolong++; @@ -938,11 +969,11 @@ insert: t = m->m_next; m->m_next = 0; m_cat(m, t); - nq = q->ipqe_q.le_next; + nq = TAILQ_NEXT(q, ipqe_q); pool_put(&ipqent_pool, q); for (q = nq; q != NULL; q = nq) { t = q->ipqe_m; - nq = q->ipqe_q.le_next; + nq = TAILQ_NEXT(q, ipqe_q); pool_put(&ipqent_pool, q); m_cat(m, t); } @@ -953,7 +984,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); @@ -989,10 +1020,10 @@ ip_freef(fp) IPQ_LOCK_CHECK(); - for (q = fp->ipq_fragq.lh_first; q != NULL; q = p) { - p = q->ipqe_q.le_next; + for (q = TAILQ_FIRST(&fp->ipq_fragq); q != NULL; q = p) { + p = TAILQ_NEXT(q, ipqe_q); m_freem(q->ipqe_m); - LIST_REMOVE(q, ipqe_q); + TAILQ_REMOVE(&fp->ipq_fragq, q, ipqe_q); pool_put(&ipqent_pool, q); } LIST_REMOVE(fp, ipq_q); @@ -1012,8 +1043,8 @@ ip_slowtimo() int s = splsoftnet(); IPQ_LOCK(); - for (fp = ipq.lh_first; fp != NULL; fp = nfp) { - nfp = fp->ipq_q.le_next; + for (fp = LIST_FIRST(&ipq); fp != NULL; fp = nfp) { + nfp = LIST_NEXT(fp, ipq_q); if (--fp->ipq_ttl == 0) { ipstat.ips_fragtimeout++; ip_freef(fp); @@ -1027,8 +1058,8 @@ ip_slowtimo() if (ip_maxfragpackets < 0) ; else { - while (ip_nfragpackets > ip_maxfragpackets && ipq.lh_first) - ip_freef(ipq.lh_first); + while (ip_nfragpackets > ip_maxfragpackets && LIST_FIRST(&ipq)) + ip_freef(LIST_FIRST(&ipq)); } IPQ_UNLOCK(); #ifdef GATEWAY @@ -1051,9 +1082,9 @@ ip_drain() if (ipq_lock_try() == 0) return; - while (ipq.lh_first != NULL) { + while (LIST_FIRST(&ipq) != NULL) { ipstat.ips_fragdropped++; - ip_freef(ipq.lh_first); + ip_freef(LIST_FIRST(&ipq)); } IPQ_UNLOCK(); @@ -1436,11 +1467,11 @@ 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; } -int inetctlerrmap[PRC_NCMDS] = { +const int inetctlerrmap[PRC_NCMDS] = { 0, 0, 0, 0, 0, EMSGSIZE, EHOSTDOWN, EHOSTUNREACH, EHOSTUNREACH, EHOSTUNREACH, ECONNREFUSED, ECONNREFUSED, @@ -1526,7 +1557,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); @@ -1653,6 +1684,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; @@ -1667,9 +1700,21 @@ ip_forward(m, srcrt) break; case ENOBUFS: +#if 1 + /* + * a router should not generate ICMP_SOURCEQUENCH as + * required in RFC1812 Requirements for IP Version 4 Routers. + * source quench could be a big problem under DoS attacks, + * or if the underlying interface is rate-limited. + */ + if (mcopy) + m_freem(mcopy); + return; +#else type = ICMP_SOURCEQUENCH; code = 0; break; +#endif } icmp_error(mcopy, type, code, dest, destifp); } @@ -1784,7 +1829,7 @@ ip_sysctl(name, namelen, oldp, oldlenp, error = sysctl_int(oldp, oldlenp, newp, newlen, &ip_mtudisc); if (ip_mtudisc != 0 && ip_mtudisc_timeout_q == NULL) { - ip_mtudisc_timeout_q = + 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); @@ -1818,10 +1863,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 < 0) { + ip_mtudisc_timeout = old; + return (EINVAL); + } if (ip_mtudisc_timeout_q != NULL) - rt_timer_queue_change(ip_mtudisc_timeout_q, + rt_timer_queue_change(ip_mtudisc_timeout_q, ip_mtudisc_timeout); return (error); #ifdef GATEWAY @@ -1842,10 +1892,16 @@ 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, + &ip_gre_ttl)); +#endif + #ifndef IPNOPRIVPORTS case IPCTL_LOWPORTMIN: old = lowportmin;