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.349 retrieving revision 1.369 diff -u -p -r1.349 -r1.369 --- src/sys/netinet/ip_input.c 2017/02/07 02:38:08 1.349 +++ src/sys/netinet/ip_input.c 2018/02/05 14:23:38 1.369 @@ -1,4 +1,4 @@ -/* $NetBSD: ip_input.c,v 1.349 2017/02/07 02:38:08 ozaki-r Exp $ */ +/* $NetBSD: ip_input.c,v 1.369 2018/02/05 14:23:38 maxv Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -29,7 +29,7 @@ * SUCH DAMAGE. */ -/*- +/* * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * @@ -91,11 +91,10 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: ip_input.c,v 1.349 2017/02/07 02:38:08 ozaki-r Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ip_input.c,v 1.369 2018/02/05 14:23:38 maxv Exp $"); #ifdef _KERNEL_OPT #include "opt_inet.h" -#include "opt_compat_netbsd.h" #include "opt_gateway.h" #include "opt_ipsec.h" #include "opt_mrouting.h" @@ -162,10 +161,10 @@ __KERNEL_RCSID(0, "$NetBSD: ip_input.c,v #define IPSENDREDIRECTS 1 #endif #ifndef IPFORWSRCRT -#define IPFORWSRCRT 1 /* forward source-routed packets */ +#define IPFORWSRCRT 0 /* forward source-routed packets */ #endif #ifndef IPALLOWSRCRT -#define IPALLOWSRCRT 1 /* allow source-routed packets */ +#define IPALLOWSRCRT 0 /* allow source-routed packets */ #endif #ifndef IPMTUDISC #define IPMTUDISC 1 @@ -174,11 +173,6 @@ __KERNEL_RCSID(0, "$NetBSD: ip_input.c,v #define IPMTUDISCTIMEOUT (10 * 60) /* as per RFC 1191 */ #endif -#ifdef COMPAT_50 -#include -#include -#endif - /* * Note: DIRECTED_BROADCAST is handled this way so that previous * configuration using this option will Just Work. @@ -198,9 +192,6 @@ int ip_directedbcast = IPDIRECTEDBCAST; int ip_allowsrcrt = IPALLOWSRCRT; int ip_mtudisc = IPMTUDISC; int ip_mtudisc_timeout = IPMTUDISCTIMEOUT; -#ifdef DIAGNOSTIC -int ipprintfs = 0; -#endif int ip_do_randomid = 0; @@ -253,30 +244,23 @@ EVCNT_ATTACH_STATIC(ip_swcsum); #endif /* INET_CSUM_COUNTERS */ /* - * We need to save the IP options in case a protocol wants to respond + * Used to save the IP options in case a protocol wants to respond * to an incoming packet over the same route if the packet got here * using IP source routing. This allows connection establishment and * maintenance when the remote end is on a network that is not known * to us. */ - -static int ip_nhops = 0; - -static struct ip_srcrt { - struct in_addr dst; /* final destination */ - char nop; /* one NOP to align */ - char srcopt[IPOPT_OFFSET + 1]; /* OPTVAL, OLEN and OFFSET */ - struct in_addr route[MAX_IPOPTLEN/sizeof(struct in_addr)]; -} ip_srcrt; +struct ip_srcrt { + int isr_nhops; /* number of hops */ + struct in_addr isr_dst; /* final destination */ + char isr_nop; /* one NOP to align */ + char isr_hdr[IPOPT_OFFSET + 1]; /* OPTVAL, OLEN & OFFSET */ + struct in_addr isr_routes[MAX_IPOPTLEN/sizeof(struct in_addr)]; +}; static int ip_drainwanted; -struct sockaddr_in ipaddr = { - .sin_len = sizeof(ipaddr), - .sin_family = AF_INET, -}; - -static void save_rte(u_char *, struct in_addr); +static void save_rte(struct mbuf *, u_char *, struct in_addr); #ifdef MBUFTRACE struct mowner ip_rx_mowner = MOWNER_INIT("internet", "rx"); @@ -349,10 +333,8 @@ ip_init(void) #endif /* MBUFTRACE */ ipstat_percpu = percpu_alloc(sizeof(uint64_t) * IP_NSTATS); - ipforward_rt_percpu = percpu_alloc(sizeof(struct route)); - if (ipforward_rt_percpu == NULL) - panic("failed to allocate ipforward_rt_percpu"); + ip_mtudisc_timeout_q = rt_timer_queue_create(ip_mtudisc_timeout); } static struct in_ifaddr * @@ -386,11 +368,14 @@ ip_match_our_address(struct ifnet *ifp, continue; if (checkif && ia->ia_ifp != ifp) continue; - if ((ia->ia_ifp->if_flags & IFF_UP) != 0 && - (ia->ia4_flags & IN_IFF_DETACHED) == 0) - break; - else + if ((ia->ia_ifp->if_flags & IFF_UP) == 0) { (*downmatch)++; + continue; + } + if (ia->ia4_flags & IN_IFF_DETACHED && + (ifp->if_flags & IFF_LOOPBACK) == 0) + continue; + break; } } @@ -407,7 +392,10 @@ ip_match_our_address_broadcast(struct if if (ifa->ifa_addr->sa_family != AF_INET) continue; ia = ifatoia(ifa); - if (ia->ia4_flags & (IN_IFF_NOTREADY | IN_IFF_DETACHED)) + if (ia->ia4_flags & IN_IFF_NOTREADY) + continue; + if (ia->ia4_flags & IN_IFF_DETACHED && + (ifp->if_flags & IFF_LOOPBACK) == 0) continue; if (in_hosteq(ip->ip_dst, ia->ia_broadaddr.sin_addr) || in_hosteq(ip->ip_dst, ia->ia_netbroadcast) || @@ -441,15 +429,11 @@ ipintr(void *arg __unused) KASSERT(cpu_softintr_p()); -#ifndef NET_MPSAFE - mutex_enter(softnet_lock); -#endif + SOFTNET_LOCK_UNLESS_NET_MPSAFE(); while ((m = pktq_dequeue(ip_pktq)) != NULL) { ip_input(m); } -#ifndef NET_MPSAFE - mutex_exit(softnet_lock); -#endif + SOFTNET_UNLOCK_UNLESS_NET_MPSAFE(); } /* @@ -495,13 +479,13 @@ ip_input(struct mbuf *m) */ if (IP_HDR_ALIGNED_P(mtod(m, void *)) == 0) { if ((m = m_copyup(m, sizeof(struct ip), - (max_linkhdr + 3) & ~3)) == NULL) { + (max_linkhdr + 3) & ~3)) == NULL) { /* XXXJRT new stat, please */ IP_STATINC(IP_STAT_TOOSMALL); goto out; } - } else if (__predict_false(m->m_len < sizeof (struct ip))) { - if ((m = m_pullup(m, sizeof (struct ip))) == NULL) { + } else if (__predict_false(m->m_len < sizeof(struct ip))) { + if ((m = m_pullup(m, sizeof(struct ip))) == NULL) { IP_STATINC(IP_STAT_TOOSMALL); goto out; } @@ -543,8 +527,7 @@ ip_input(struct mbuf *m) } switch (m->m_pkthdr.csum_flags & - ((ifp->if_csum_flags_rx & M_CSUM_IPv4) | - M_CSUM_IPv4_BAD)) { + ((ifp->if_csum_flags_rx & M_CSUM_IPv4) | M_CSUM_IPv4_BAD)) { case M_CSUM_IPv4|M_CSUM_IPv4_BAD: INET_CSUM_COUNTER_INCR(&ip_hwcsum_bad); IP_STATINC(IP_STAT_BADSUM); @@ -583,10 +566,9 @@ ip_input(struct mbuf *m) } /* - * Check that the amount of data in the buffers - * is as at least much as the IP header would have us expect. - * Trim mbufs if longer than we expect. - * Drop packet if shorter than we expect. + * Check that the amount of data in the buffers is at least as much + * as the IP header would have us expect. Trim mbufs if longer than + * we expect. Drop packet if shorter than we expect. */ if (m->m_pkthdr.len < len) { IP_STATINC(IP_STAT_TOOSHORT); @@ -667,8 +649,7 @@ ip_input(struct mbuf *m) * error was detected (causing an icmp message * to be sent and the original packet to be freed). */ - ip_nhops = 0; /* for source routed packets */ - if (hlen > sizeof (struct ip) && ip_dooptions(m)) { + if (hlen > sizeof(struct ip) && ip_dooptions(m)) { m = NULL; goto out; } @@ -768,15 +749,12 @@ ip_input(struct mbuf *m) return; } #ifdef IPSEC - /* Perform IPsec, if any. */ + /* Check the security policy (SP) for the packet */ if (ipsec_used) { - SOFTNET_LOCK(); if (ipsec4_input(m, IP_FORWARDING | (ip_directedbcast ? IP_ALLOWBROADCAST : 0)) != 0) { - SOFTNET_UNLOCK(); goto out; } - SOFTNET_UNLOCK(); } #endif ip_forward(m, srcrt, ifp); @@ -805,7 +783,7 @@ ours: } /* * Reassembly is done, we have the final packet. - * Updated cached data in local variable(s). + * Update cached data in local variable(s). */ ip = mtod(m, struct ip *); hlen = ip->ip_hl << 2; @@ -819,12 +797,9 @@ ours: */ if (ipsec_used && (inetsw[ip_protox[ip->ip_p]].pr_flags & PR_LASTHDR) != 0) { - SOFTNET_LOCK(); if (ipsec4_input(m, 0) != 0) { - SOFTNET_UNLOCK(); goto out; } - SOFTNET_UNLOCK(); } #endif @@ -848,9 +823,7 @@ ours: const int off = hlen, nh = ip->ip_p; - SOFTNET_LOCK(); (*inetsw[ip_protox[nh]].pr_input)(m, off, nh); - SOFTNET_UNLOCK(); return; out: @@ -866,17 +839,11 @@ void ip_slowtimo(void) { -#ifndef NET_MPSAFE - mutex_enter(softnet_lock); - KERNEL_LOCK(1, NULL); -#endif + SOFTNET_KERNEL_LOCK_UNLESS_NET_MPSAFE(); ip_reass_slowtimo(); -#ifndef NET_MPSAFE - KERNEL_UNLOCK_ONE(NULL); - mutex_exit(softnet_lock); -#endif + SOFTNET_KERNEL_UNLOCK_UNLESS_NET_MPSAFE(); } /* @@ -906,14 +873,19 @@ ip_dooptions(struct mbuf *m) struct ip_timestamp *ipt; struct in_ifaddr *ia; int opt, optlen, cnt, off, code, type = ICMP_PARAMPROB, forward = 0; + int srr_present, rr_present, ts_present; struct in_addr dst; n_time ntime; struct ifaddr *ifa = NULL; int s; + srr_present = 0; + rr_present = 0; + ts_present = 0; + dst = ip->ip_dst; cp = (u_char *)(ip + 1); - cnt = (ip->ip_hl << 2) - sizeof (struct ip); + cnt = (ip->ip_hl << 2) - sizeof(struct ip); for (; cnt > 0; cnt -= optlen, cp += optlen) { opt = cp[IPOPT_OPTVAL]; if (opt == IPOPT_EOL) @@ -948,11 +920,20 @@ ip_dooptions(struct mbuf *m) case IPOPT_LSRR: case IPOPT_SSRR: { struct psref psref; + struct sockaddr_in ipaddr = { + .sin_len = sizeof(ipaddr), + .sin_family = AF_INET, + }; + if (ip_allowsrcrt == 0) { type = ICMP_UNREACH; code = ICMP_UNREACH_NET_PROHIB; goto bad; } + if (srr_present++) { + code = &cp[IPOPT_OPTVAL] - (u_char *)ip; + goto bad; + } if (optlen < IPOPT_OFFSET + sizeof(*cp)) { code = &cp[IPOPT_OLEN] - (u_char *)ip; goto bad; @@ -985,7 +966,7 @@ ip_dooptions(struct mbuf *m) /* * End of source route. Should be for us. */ - save_rte(cp, ip->ip_src); + save_rte(m, cp, ip->ip_src); break; } /* @@ -1022,6 +1003,15 @@ ip_dooptions(struct mbuf *m) case IPOPT_RR: { struct psref psref; + struct sockaddr_in ipaddr = { + .sin_len = sizeof(ipaddr), + .sin_family = AF_INET, + }; + + if (rr_present++) { + code = &cp[IPOPT_OPTVAL] - (u_char *)ip; + goto bad; + } if (optlen < IPOPT_OFFSET + sizeof(*cp)) { code = &cp[IPOPT_OLEN] - (u_char *)ip; goto bad; @@ -1063,6 +1053,10 @@ ip_dooptions(struct mbuf *m) case IPOPT_TS: code = cp - (u_char *)ip; ipt = (struct ip_timestamp *)cp; + if (ts_present++) { + code = &cp[IPOPT_OPTVAL] - (u_char *)ip; + goto bad; + } if (ipt->ipt_len < 4 || ipt->ipt_len > 40) { code = (u_char *)&ipt->ipt_len - (u_char *)ip; goto bad; @@ -1071,7 +1065,7 @@ ip_dooptions(struct mbuf *m) code = (u_char *)&ipt->ipt_ptr - (u_char *)ip; goto bad; } - if (ipt->ipt_ptr > ipt->ipt_len - sizeof (int32_t)) { + if (ipt->ipt_ptr > ipt->ipt_len - sizeof(int32_t)) { if (++ipt->ipt_oflw == 0) { code = (u_char *)&ipt->ipt_ptr - (u_char *)ip; @@ -1088,6 +1082,10 @@ ip_dooptions(struct mbuf *m) case IPOPT_TS_TSANDADDR: { struct ifnet *rcvif; int _s, _ss; + struct sockaddr_in ipaddr = { + .sin_len = sizeof(ipaddr), + .sin_family = AF_INET, + }; if (ipt->ipt_ptr - 1 + sizeof(n_time) + sizeof(struct in_addr) > ipt->ipt_len) { @@ -1115,7 +1113,12 @@ ip_dooptions(struct mbuf *m) break; } - case IPOPT_TS_PRESPEC: + case IPOPT_TS_PRESPEC: { + struct sockaddr_in ipaddr = { + .sin_len = sizeof(ipaddr), + .sin_family = AF_INET, + }; + if (ipt->ipt_ptr - 1 + sizeof(n_time) + sizeof(struct in_addr) > ipt->ipt_len) { code = (u_char *)&ipt->ipt_ptr - @@ -1133,6 +1136,7 @@ ip_dooptions(struct mbuf *m) pserialize_read_exit(s); ipt->ipt_ptr += sizeof(struct in_addr); break; + } default: /* XXX can't take &ipt->ipt_flg */ @@ -1209,16 +1213,25 @@ ip_rtaddr(struct in_addr dst, struct psr * up later by ip_srcroute if the receiver is interested. */ static void -save_rte(u_char *option, struct in_addr dst) +save_rte(struct mbuf *m, u_char *option, struct in_addr dst) { + struct ip_srcrt *isr; + struct m_tag *mtag; unsigned olen; olen = option[IPOPT_OLEN]; - if (olen > sizeof(ip_srcrt) - (1 + sizeof(dst))) + if (olen > sizeof(isr->isr_hdr) + sizeof(isr->isr_routes)) return; - memcpy((void *)ip_srcrt.srcopt, (void *)option, olen); - ip_nhops = (olen - IPOPT_OFFSET - 1) / sizeof(struct in_addr); - ip_srcrt.dst = dst; + + mtag = m_tag_get(PACKET_TAG_SRCROUTE, sizeof(*isr), M_NOWAIT); + if (mtag == NULL) + return; + isr = (struct ip_srcrt *)(mtag + 1); + + memcpy(isr->isr_hdr, option, olen); + isr->isr_nhops = (olen - IPOPT_OFFSET - 1) / sizeof(struct in_addr); + isr->isr_dst = dst; + m_tag_prepend(m, mtag); } /* @@ -1227,36 +1240,43 @@ save_rte(u_char *option, struct in_addr * The first hop is placed before the options, will be removed later. */ struct mbuf * -ip_srcroute(void) +ip_srcroute(struct mbuf *m0) { struct in_addr *p, *q; struct mbuf *m; + struct ip_srcrt *isr; + struct m_tag *mtag; - if (ip_nhops == 0) + mtag = m_tag_find(m0, PACKET_TAG_SRCROUTE, NULL); + if (mtag == NULL) return NULL; + isr = (struct ip_srcrt *)(mtag + 1); + + if (isr->isr_nhops == 0) + return NULL; + m = m_get(M_DONTWAIT, MT_SOOPTS); - if (m == 0) + if (m == NULL) return NULL; MCLAIM(m, &inetdomain.dom_mowner); -#define OPTSIZ (sizeof(ip_srcrt.nop) + sizeof(ip_srcrt.srcopt)) +#define OPTSIZ (sizeof(isr->isr_nop) + sizeof(isr->isr_hdr)) - /* length is (nhops+1)*sizeof(addr) + sizeof(nop + srcrt header) */ - m->m_len = ip_nhops * sizeof(struct in_addr) + sizeof(struct in_addr) + - OPTSIZ; + /* length is (nhops+1)*sizeof(addr) + sizeof(nop + header) */ + m->m_len = (isr->isr_nhops + 1) * sizeof(struct in_addr) + OPTSIZ; /* * First save first hop for return route */ - p = &ip_srcrt.route[ip_nhops - 1]; + p = &(isr->isr_routes[isr->isr_nhops - 1]); *(mtod(m, struct in_addr *)) = *p--; /* * Copy option fields and padding (nop) to mbuf. */ - ip_srcrt.nop = IPOPT_NOP; - ip_srcrt.srcopt[IPOPT_OFFSET] = IPOPT_MINOFF; - memmove(mtod(m, char *) + sizeof(struct in_addr), &ip_srcrt.nop, + isr->isr_nop = IPOPT_NOP; + isr->isr_hdr[IPOPT_OFFSET] = IPOPT_MINOFF; + memmove(mtod(m, char *) + sizeof(struct in_addr), &isr->isr_nop, OPTSIZ); q = (struct in_addr *)(mtod(m, char *) + sizeof(struct in_addr) + OPTSIZ); @@ -1265,14 +1285,15 @@ ip_srcroute(void) * Record return path as an IP source route, * reversing the path (pointers are now aligned). */ - while (p >= ip_srcrt.route) { + while (p >= isr->isr_routes) { *q++ = *p--; } /* * Last hop goes to final destination. */ - *q = ip_srcrt.dst; - return (m); + *q = isr->isr_dst; + m_tag_delete(m0, mtag); + return m; } const int inetctlerrmap[PRC_NCMDS] = { @@ -1494,55 +1515,39 @@ ip_savecontrol(struct inpcb *inp, struct struct mbuf *m) { struct socket *so = inp->inp_socket; - ifnet_t *ifp; int inpflags = inp->inp_flags; - struct psref psref; - ifp = m_get_rcvif_psref(m, &psref); - if (__predict_false(ifp == NULL)) - return; /* XXX should report error? */ + if (SOOPT_TIMESTAMP(so->so_options)) + mp = sbsavetimestamp(so->so_options, m, mp); - if (so->so_options & SO_TIMESTAMP -#ifdef SO_OTIMESTAMP - || so->so_options & SO_OTIMESTAMP -#endif - ) { - struct timeval tv; - - microtime(&tv); -#ifdef SO_OTIMESTAMP - if (so->so_options & SO_OTIMESTAMP) { - struct timeval50 tv50; - timeval_to_timeval50(&tv, &tv50); - *mp = sbcreatecontrol((void *) &tv50, sizeof(tv50), - SCM_OTIMESTAMP, SOL_SOCKET); - } else -#endif - *mp = sbcreatecontrol((void *) &tv, sizeof(tv), - SCM_TIMESTAMP, SOL_SOCKET); - if (*mp) - mp = &(*mp)->m_next; - } if (inpflags & INP_RECVDSTADDR) { - *mp = sbcreatecontrol((void *) &ip->ip_dst, + *mp = sbcreatecontrol(&ip->ip_dst, sizeof(struct in_addr), IP_RECVDSTADDR, IPPROTO_IP); if (*mp) mp = &(*mp)->m_next; } - if (inpflags & INP_RECVPKTINFO) { - struct in_pktinfo ipi; - ipi.ipi_addr = ip->ip_src; - ipi.ipi_ifindex = ifp->if_index; - *mp = sbcreatecontrol((void *) &ipi, - sizeof(ipi), IP_RECVPKTINFO, IPPROTO_IP); + + if (inpflags & INP_RECVTTL) { + *mp = sbcreatecontrol(&ip->ip_ttl, + sizeof(uint8_t), IP_RECVTTL, IPPROTO_IP); if (*mp) mp = &(*mp)->m_next; } - if (inpflags & INP_PKTINFO) { + + struct psref psref; + ifnet_t *ifp = m_get_rcvif_psref(m, &psref); + if (__predict_false(ifp == NULL)) { +#ifdef DIAGNOSTIC + printf("%s: missing receive interface\n", __func__); +#endif + return; /* XXX should report error? */ + } + + if (inpflags & INP_RECVPKTINFO) { struct in_pktinfo ipi; ipi.ipi_addr = ip->ip_dst; ipi.ipi_ifindex = ifp->if_index; - *mp = sbcreatecontrol((void *) &ipi, + *mp = sbcreatecontrol(&ipi, sizeof(ipi), IP_PKTINFO, IPPROTO_IP); if (*mp) mp = &(*mp)->m_next; @@ -1556,12 +1561,6 @@ ip_savecontrol(struct inpcb *inp, struct if (*mp) mp = &(*mp)->m_next; } - if (inpflags & INP_RECVTTL) { - *mp = sbcreatecontrol((void *) &ip->ip_ttl, - sizeof(uint8_t), IP_RECVTTL, IPPROTO_IP); - if (*mp) - mp = &(*mp)->m_next; - } m_put_rcvif_psref(ifp, &psref); } @@ -1601,24 +1600,25 @@ sysctl_net_inet_ip_pmtudto(SYSCTLFN_ARGS int error, tmp; struct sysctlnode node; + icmp_mtudisc_lock(); + node = *rnode; tmp = ip_mtudisc_timeout; node.sysctl_data = &tmp; error = sysctl_lookup(SYSCTLFN_CALL(&node)); if (error || newp == NULL) - return (error); - if (tmp < 0) - return (EINVAL); - - /* XXX NOMPSAFE still need softnet_lock */ - mutex_enter(softnet_lock); + goto out; + if (tmp < 0) { + error = EINVAL; + goto out; + } ip_mtudisc_timeout = tmp; rt_timer_queue_change(ip_mtudisc_timeout_q, ip_mtudisc_timeout); - - mutex_exit(softnet_lock); - - return (0); + error = 0; +out: + icmp_mtudisc_unlock(); + return error; } static int