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.37 retrieving revision 1.53.2.4 diff -u -p -r1.37 -r1.53.2.4 --- src/sys/netinet/ip_input.c 1996/09/21 19:44:33 1.37 +++ src/sys/netinet/ip_input.c 1998/11/15 19:18:13 1.53.2.4 @@ -1,4 +1,4 @@ -/* $NetBSD: ip_input.c,v 1.37 1996/09/21 19:44:33 perry Exp $ */ +/* $NetBSD: ip_input.c,v 1.53.2.4 1998/11/15 19:18:13 cgd Exp $ */ /* * Copyright (c) 1982, 1986, 1988, 1993 @@ -35,6 +35,43 @@ * @(#)ip_input.c 8.2 (Berkeley) 1/4/94 */ +/*- + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Public Access Networks Corporation ("Panix"). It was developed under + * contract to Panix by Eric Haszlakiewicz and Thor Lancelot Simon. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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 NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + #include #include #include @@ -42,6 +79,7 @@ #include #include #include +#include #include #include #include @@ -51,7 +89,9 @@ #include #include +#include #include +#include #include #include @@ -61,9 +101,8 @@ #include #include -#ifdef PFIL_HOOKS -#include -#endif /* PFIL_HOOKS */ +/* XXX should really put this in libkern.h */ +#define offsetof(type, member) ((size_t)(&((type *)0)->member)) #ifndef IPFORWARDING #ifdef GATEWAY @@ -76,8 +115,19 @@ #define IPSENDREDIRECTS 1 #endif #ifndef IPFORWSRCRT -#define IPFORWSRCRT 1 /* allow source-routed packets */ +#define IPFORWSRCRT 1 /* forward source-routed packets */ +#endif +#ifndef IPALLOWSRCRT +#define IPALLOWSRCRT 1 /* allow source-routed packets */ #endif +#ifndef IPMTUDISC +#define IPMTUDISC 0 +#endif +#ifndef IPMTUDISCTIMEOUT +#define IPMTUDISCTIMEOUT (10 * 60) /* as per RFC 1191 */ +#endif + + /* * Note: DIRECTED_BROADCAST is handled this way so that previous * configuration using this option will Just Work. @@ -94,15 +144,21 @@ int ipsendredirects = IPSENDREDIRECTS; int ip_defttl = IPDEFTTL; int ip_forwsrcrt = IPFORWSRCRT; int ip_directedbcast = IPDIRECTEDBCAST; +int ip_allowsrcrt = IPALLOWSRCRT; +int ip_mtudisc = IPMTUDISC; +u_int ip_mtudisc_timeout = IPMTUDISCTIMEOUT; #ifdef DIAGNOSTIC int ipprintfs = 0; #endif +struct rttimer_queue *ip_mtudisc_timeout_q = NULL; + extern struct domain inetdomain; extern struct protosw inetsw[]; u_char ip_protox[IPPROTO_MAX]; int ipqmaxlen = IFQ_MAXLEN; struct in_ifaddrhead in_ifaddr; +struct in_ifaddrhashhead *in_ifaddrhashtbl; struct ifqueue ipintrq; /* @@ -146,6 +202,11 @@ ip_init() ip_id = time.tv_sec & 0xffff; ipintrq.ifq_maxlen = ipqmaxlen; TAILQ_INIT(&in_ifaddr); + in_ifaddrhashtbl = + hashinit(IN_IFADDR_HASH_SIZE, M_IFADDR, &in_ifaddrhash); + if (ip_mtudisc != 0) + ip_mtudisc_timeout_q = + rt_timer_queue_create(ip_mtudisc_timeout); } struct sockaddr_in ipaddr = { sizeof(ipaddr), AF_INET }; @@ -162,11 +223,13 @@ ipintr() register struct mbuf *m; register struct ipq *fp; register struct in_ifaddr *ia; + register struct ifaddr *ifa; struct ipqent *ipqe; int hlen = 0, mff, len, s; #ifdef PFIL_HOOKS struct packet_filter_hook *pfh; struct mbuf *m0; + int rv; #endif /* PFIL_HOOKS */ next: @@ -248,11 +311,15 @@ next: * Run through list of hooks for input packets. */ m0 = m; - for (pfh = pfil_hook_get(PFIL_IN); pfh; pfh = pfh->pfil_link.le_next) + for (pfh = pfil_hook_get(PFIL_IN); pfh; pfh = pfh->pfil_link.tqe_next) if (pfh->pfil_func) { - if (pfh->pfil_func(ip, hlen, m->m_pkthdr.rcvif, 0, &m0)) - goto bad; - ip = mtod(m = m0, struct ip *); + rv = pfh->pfil_func(ip, hlen, m->m_pkthdr.rcvif, 0, &m0); + if (rv) + goto next; + m = m0; + if (m == NULL) + goto next; + ip = mtod(m, struct ip *); } #endif /* PFIL_HOOKS */ @@ -269,12 +336,13 @@ next: /* * Check our list of addresses, to see if the packet is for us. */ - for (ia = in_ifaddr.tqh_first; ia; ia = ia->ia_list.tqe_next) { - if (in_hosteq(ip->ip_dst, ia->ia_addr.sin_addr)) - goto ours; - if (((ip_directedbcast == 0) || (ip_directedbcast && - ia->ia_ifp == m->m_pkthdr.rcvif)) && - (ia->ia_ifp->if_flags & IFF_BROADCAST)) { + INADDR_TO_IA(ip->ip_dst, ia); + 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; + ia = ifatoia(ifa); if (in_hosteq(ip->ip_dst, ia->ia_broadaddr.sin_addr) || in_hosteq(ip->ip_dst, ia->ia_netbroadcast) || /* @@ -284,6 +352,12 @@ next: ip->ip_dst.s_addr == ia->ia_subnet || ip->ip_dst.s_addr == ia->ia_net) goto ours; + /* + * An interface with IP address zero accepts + * all packets that arrive on that interface. + */ + if (in_nullhost(ia->ia_addr.sin_addr)) + goto ours; } } if (IN_MULTICAST(ip->ip_dst.s_addr)) { @@ -365,13 +439,6 @@ ours: * but it's not worth the time; just let them time out.) */ if (ip->ip_off & ~(IP_DF|IP_RF)) { - if (m->m_flags & M_EXT) { /* XXX */ - if ((m = m_pullup(m, sizeof (struct ip))) == 0) { - ipstat.ips_toosmall++; - goto next; - } - ip = mtod(m, struct ip *); - } /* * Look for queue of fragments * of this datagram. @@ -418,12 +485,14 @@ found: goto bad; } ipqe->ipqe_mff = mff; + ipqe->ipqe_m = m; ipqe->ipqe_ip = ip; - ip = ip_reass(ipqe, fp); - if (ip == 0) + m = ip_reass(ipqe, fp); + if (m == 0) goto next; ipstat.ips_reassembled++; - m = dtom(ip); + ip = mtod(m, struct ip *); + hlen = ip->ip_hl << 2; } else if (fp) ip_freef(fp); @@ -437,14 +506,6 @@ found: (*inetsw[ip_protox[ip->ip_p]].pr_input)(m, hlen); goto next; bad: -#ifdef PFIL_HOOKS - m0 = m; - for (pfh = pfil_hook_get(PFIL_BAD); pfh; pfh = pfh->pfil_link.le_next) - if (pfh->pfil_func) { - (void)pfh->pfil_func(ip, hlen, m->m_pkthdr.rcvif, 2, &m0); - ip = mtod(m = m0, struct ip *); - } -#endif /* PFIL_HOOKS */ m_freem(m); goto next; } @@ -455,12 +516,12 @@ bad: * reassembly of this datagram already exists, then it * is given as fp; otherwise have to make a chain. */ -struct ip * +struct mbuf * ip_reass(ipqe, fp) register struct ipqent *ipqe; register struct ipq *fp; { - register struct mbuf *m = dtom(ipqe->ipqe_ip); + register struct mbuf *m = ipqe->ipqe_m; register struct ipqent *nq, *p, *q; struct ip *ip; struct mbuf *t; @@ -478,9 +539,10 @@ ip_reass(ipqe, fp) * If first fragment to arrive, create a reassembly queue. */ if (fp == 0) { - if ((t = m_get(M_DONTWAIT, MT_FTABLE)) == NULL) + MALLOC(fp, struct ipq *, sizeof (struct ipq), + M_FTABLE, M_NOWAIT); + if (fp == NULL) goto dropfrag; - fp = mtod(t, struct ipq *); LIST_INSERT_HEAD(&ipq, fp, ipq_q); fp->ipq_ttl = IPFRAGTTL; fp->ipq_p = ipqe->ipqe_ip->ip_p; @@ -511,7 +573,7 @@ ip_reass(ipqe, fp) if (i > 0) { if (i >= ipqe->ipqe_ip->ip_len) goto dropfrag; - m_adj(dtom(ipqe->ipqe_ip), i); + m_adj(ipqe->ipqe_m, i); ipqe->ipqe_ip->ip_off += i; ipqe->ipqe_ip->ip_len -= i; } @@ -528,11 +590,11 @@ ip_reass(ipqe, fp) if (i < q->ipqe_ip->ip_len) { q->ipqe_ip->ip_len -= i; q->ipqe_ip->ip_off += i; - m_adj(dtom(q->ipqe_ip), i); + m_adj(q->ipqe_m, i); break; } nq = q->ipqe_q.le_next; - m_freem(dtom(q->ipqe_ip)); + m_freem(q->ipqe_m); LIST_REMOVE(q, ipqe_q); FREE(q, M_IPQ); } @@ -558,18 +620,24 @@ insert: return (0); /* - * Reassembly is complete; concatenate fragments. + * Reassembly is complete. Check for a bogus message size and + * concatenate fragments. */ q = fp->ipq_fragq.lh_first; ip = q->ipqe_ip; - m = dtom(q->ipqe_ip); + if ((next + (ip->ip_hl << 2)) > IP_MAXPACKET) { + ipstat.ips_toolong++; + ip_freef(fp); + return (0); + } + m = q->ipqe_m; t = m->m_next; m->m_next = 0; m_cat(m, t); nq = q->ipqe_q.le_next; FREE(q, M_IPQ); for (q = nq; q != NULL; q = nq) { - t = dtom(q->ipqe_ip); + t = q->ipqe_m; nq = q->ipqe_q.le_next; FREE(q, M_IPQ); m_cat(m, t); @@ -585,17 +653,17 @@ insert: ip->ip_src = fp->ipq_src; ip->ip_dst = fp->ipq_dst; LIST_REMOVE(fp, ipq_q); - (void) m_free(dtom(fp)); + FREE(fp, M_FTABLE); m->m_len += (ip->ip_hl << 2); m->m_data -= (ip->ip_hl << 2); /* some debugging cruft by sklower, below, will go away soon */ if (m->m_flags & M_PKTHDR) { /* XXX this should be done elsewhere */ register int plen = 0; - for (t = m; m; m = m->m_next) - plen += m->m_len; - t->m_pkthdr.len = plen; + for (t = m; t; t = t->m_next) + plen += t->m_len; + m->m_pkthdr.len = plen; } - return (ip); + return (m); dropfrag: ipstat.ips_fragdropped++; @@ -616,12 +684,12 @@ ip_freef(fp) for (q = fp->ipq_fragq.lh_first; q != NULL; q = p) { p = q->ipqe_q.le_next; - m_freem(dtom(q->ipqe_ip)); + m_freem(q->ipqe_m); LIST_REMOVE(q, ipqe_q); FREE(q, M_IPQ); } LIST_REMOVE(fp, ipq_q); - (void) m_free(dtom(fp)); + FREE(fp, M_FTABLE); } /* @@ -709,6 +777,11 @@ ip_dooptions(m) */ case IPOPT_LSRR: case IPOPT_SSRR: + if (ip_allowsrcrt == 0) { + type = ICMP_UNREACH; + code = ICMP_UNREACH_NET_PROHIB; + goto bad; + } if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) { code = &cp[IPOPT_OFFSET] - (u_char *)ip; goto bad; @@ -1160,6 +1233,66 @@ ip_forward(m, srcrt) icmp_error(mcopy, type, code, dest, destifp); } +void +ip_savecontrol(inp, mp, ip, m) + register struct inpcb *inp; + register struct mbuf **mp; + register struct ip *ip; + register struct mbuf *m; +{ + + if (inp->inp_socket->so_options & SO_TIMESTAMP) { + struct timeval tv; + + microtime(&tv); + *mp = sbcreatecontrol((caddr_t) &tv, sizeof(tv), + SCM_TIMESTAMP, SOL_SOCKET); + if (*mp) + mp = &(*mp)->m_next; + } + if (inp->inp_flags & INP_RECVDSTADDR) { + *mp = sbcreatecontrol((caddr_t) &ip->ip_dst, + sizeof(struct in_addr), IP_RECVDSTADDR, IPPROTO_IP); + if (*mp) + mp = &(*mp)->m_next; + } +#ifdef notyet + /* + * XXX + * Moving these out of udp_input() made them even more broken + * than they already were. + * - fenner@parc.xerox.com + */ + /* options were tossed already */ + if (inp->inp_flags & INP_RECVOPTS) { + *mp = sbcreatecontrol((caddr_t) opts_deleted_above, + sizeof(struct in_addr), IP_RECVOPTS, IPPROTO_IP); + if (*mp) + mp = &(*mp)->m_next; + } + /* ip_srcroute doesn't do what we want here, need to fix */ + if (inp->inp_flags & INP_RECVRETOPTS) { + *mp = sbcreatecontrol((caddr_t) ip_srcroute(), + sizeof(struct in_addr), IP_RECVRETOPTS, IPPROTO_IP); + if (*mp) + mp = &(*mp)->m_next; + } +#endif + if (inp->inp_flags & INP_RECVIF) { + struct sockaddr_dl sdl; + + sdl.sdl_len = offsetof(struct sockaddr_dl, sdl_data[0]); + sdl.sdl_family = AF_LINK; + sdl.sdl_index = m->m_pkthdr.rcvif ? + m->m_pkthdr.rcvif->if_index : 0; + sdl.sdl_nlen = sdl.sdl_alen = sdl.sdl_slen = 0; + *mp = sbcreatecontrol((caddr_t) &sdl, sdl.sdl_len, + IP_RECVIF, IPPROTO_IP); + if (*mp) + mp = &(*mp)->m_next; + } +} + int ip_sysctl(name, namelen, oldp, oldlenp, newp, newlen) int *name; @@ -1169,6 +1302,9 @@ ip_sysctl(name, namelen, oldp, oldlenp, void *newp; size_t newlen; { + extern int subnetsarelocal; + int error; + /* All sysctl names at this level are terminal. */ if (namelen != 1) return (ENOTDIR); @@ -1186,16 +1322,40 @@ ip_sysctl(name, namelen, oldp, oldlenp, return (sysctl_int(oldp, oldlenp, newp, newlen, &ip_mtu)); #endif case IPCTL_FORWSRCRT: - /* - * Don't allow this to change in a secure environment. - */ + /* Don't allow this to change in a secure environment. */ if (securelevel > 0) - return (EPERM); - return (sysctl_int(oldp, oldlenp, newp, newlen, - &ip_forwsrcrt)); + return (sysctl_rdint(oldp, oldlenp, newp, + ip_forwsrcrt)); + else + return (sysctl_int(oldp, oldlenp, newp, newlen, + &ip_forwsrcrt)); case IPCTL_DIRECTEDBCAST: return (sysctl_int(oldp, oldlenp, newp, newlen, &ip_directedbcast)); + case IPCTL_ALLOWSRCRT: + return (sysctl_int(oldp, oldlenp, newp, newlen, + &ip_allowsrcrt)); + case IPCTL_SUBNETSARELOCAL: + return (sysctl_int(oldp, oldlenp, newp, newlen, + &subnetsarelocal)); + 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; + } + return error; + case IPCTL_MTUDISCTIMEOUT: + error = sysctl_int(oldp, oldlenp, newp, newlen, + &ip_mtudisc_timeout); + if (ip_mtudisc_timeout_q != NULL) + rt_timer_queue_change(ip_mtudisc_timeout_q, + ip_mtudisc_timeout); + return (error); default: return (EOPNOTSUPP); }