]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/netinet/ip_icmp.c
xnu-792.10.96.tar.gz
[apple/xnu.git] / bsd / netinet / ip_icmp.c
index fb94248771ef5e538a0659a028ac11d6c8a51627..030ef068084a7a4863a490ea090f390a60a13e2c 100644 (file)
@@ -3,22 +3,19 @@
  *
  * @APPLE_LICENSE_HEADER_START@
  * 
  *
  * @APPLE_LICENSE_HEADER_START@
  * 
- * Copyright (c) 1999-2003 Apple Computer, Inc.  All Rights Reserved.
+ * The contents of this file constitute Original Code as defined in and
+ * are subject to the Apple Public Source License Version 1.1 (the
+ * "License").  You may not use this file except in compliance with the
+ * License.  Please obtain a copy of the License at
+ * http://www.apple.com/publicsource and read it before using this file.
  * 
  * 
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- * 
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * This Original Code and all software distributed under the License are
+ * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
+ * License for the specific language governing rights and limitations
+ * under the License.
  * 
  * @APPLE_LICENSE_HEADER_END@
  */
  * 
  * @APPLE_LICENSE_HEADER_END@
  */
 #include <netinet/ip_icmp.h>
 #include <netinet/ip_var.h>
 #include <netinet/icmp_var.h>
 #include <netinet/ip_icmp.h>
 #include <netinet/ip_var.h>
 #include <netinet/icmp_var.h>
+#include <netinet/tcp.h>
+#include <netinet/tcp_fsm.h>
+#include <netinet/tcp_seq.h>
+#include <netinet/tcp_timer.h>
+#include <netinet/tcp_var.h>
+#include <netinet/tcpip.h>
 
 #if IPSEC
 #include <netinet6/ipsec.h>
 
 #if IPSEC
 #include <netinet6/ipsec.h>
 #if defined(NFAITH) && NFAITH > 0
 #include "faith.h"
 #include <net/if_types.h>
 #if defined(NFAITH) && NFAITH > 0
 #include "faith.h"
 #include <net/if_types.h>
+#endif
+
+ /* XXX This one should go in sys/mbuf.h. It is used to avoid that
+ * a firewall-generated packet loops forever through the firewall.
+ */
+#ifndef M_SKIP_FIREWALL
+#define M_SKIP_FIREWALL         0x4000
 #endif
 
 /*
 #endif
 
 /*
@@ -102,6 +112,10 @@ static int icmpmaskrepl = 0;
 SYSCTL_INT(_net_inet_icmp, ICMPCTL_MASKREPL, maskrepl, CTLFLAG_RW,
        &icmpmaskrepl, 0, "");
 
 SYSCTL_INT(_net_inet_icmp, ICMPCTL_MASKREPL, maskrepl, CTLFLAG_RW,
        &icmpmaskrepl, 0, "");
 
+static int     icmptimestamp = 0;
+SYSCTL_INT(_net_inet_icmp, ICMPCTL_TIMESTAMP, timestamp, CTLFLAG_RW,
+       &icmptimestamp, 0, "");
+
 static int     drop_redirect = 0;
 SYSCTL_INT(_net_inet_icmp, OID_AUTO, drop_redirect, CTLFLAG_RW, 
        &drop_redirect, 0, "");
 static int     drop_redirect = 0;
 SYSCTL_INT(_net_inet_icmp, OID_AUTO, drop_redirect, CTLFLAG_RW, 
        &drop_redirect, 0, "");
@@ -117,7 +131,7 @@ SYSCTL_INT(_net_inet_icmp, OID_AUTO, log_redirect, CTLFLAG_RW,
  *      variable content is -1 and read-only.
  */     
     
  *      variable content is -1 and read-only.
  */     
     
-static int      icmplim = 100;
+static int      icmplim = 250;
 SYSCTL_INT(_net_inet_icmp, ICMPCTL_ICMPLIM, icmplim, CTLFLAG_RW,
        &icmplim, 0, "");
 #else
 SYSCTL_INT(_net_inet_icmp, ICMPCTL_ICMPLIM, icmplim, CTLFLAG_RW,
        &icmplim, 0, "");
 #else
@@ -141,9 +155,9 @@ SYSCTL_INT(_net_inet_icmp, OID_AUTO, bmcastecho, CTLFLAG_RW,
 int    icmpprintfs = 0;
 #endif
 
 int    icmpprintfs = 0;
 #endif
 
-static void    icmp_reflect __P((struct mbuf *));
-static void    icmp_send __P((struct mbuf *, struct mbuf *));
-static int     ip_next_mtu __P((int, int));
+static void    icmp_reflect(struct mbuf *);
+static void    icmp_send(struct mbuf *, struct mbuf *);
+static int     ip_next_mtu(int, int);
 
 extern struct protosw inetsw[];
 
 
 extern struct protosw inetsw[];
 
@@ -152,11 +166,12 @@ extern    struct protosw inetsw[];
  * in response to bad packet ip.
  */
 void
  * in response to bad packet ip.
  */
 void
-icmp_error(n, type, code, dest, destifp)
-       struct mbuf *n;
-       int type, code;
-       n_long dest;
-       struct ifnet *destifp;
+icmp_error(
+       struct mbuf *n,
+       int type,
+       int code,
+       n_long dest,
+       struct ifnet *destifp)
 {
        register struct ip *oip = mtod(n, struct ip *), *nip;
        register unsigned oiplen = IP_VHL_HL(oip->ip_vhl) << 2;
 {
        register struct ip *oip = mtod(n, struct ip *), *nip;
        register unsigned oiplen = IP_VHL_HL(oip->ip_vhl) << 2;
@@ -192,6 +207,12 @@ icmp_error(n, type, code, dest, destifp)
        m = m_gethdr(M_DONTWAIT, MT_HEADER);
        if (m == NULL)
                goto freeit;
        m = m_gethdr(M_DONTWAIT, MT_HEADER);
        if (m == NULL)
                goto freeit;
+
+       if (n->m_flags & M_SKIP_FIREWALL) {
+               /* set M_SKIP_FIREWALL to skip firewall check, since we're called from firewall */
+               m->m_flags |= M_SKIP_FIREWALL;
+       }
+
        icmplen = min(oiplen + 8, oip->ip_len);
        if (icmplen < sizeof(struct ip)) {
                printf("icmp_error: bad length\n");
        icmplen = min(oiplen + 8, oip->ip_len);
        if (icmplen < sizeof(struct ip)) {
                printf("icmp_error: bad length\n");
@@ -272,8 +293,9 @@ icmp_input(m, hlen)
        int icmplen = ip->ip_len;
        register int i;
        struct in_ifaddr *ia;
        int icmplen = ip->ip_len;
        register int i;
        struct in_ifaddr *ia;
-       void (*ctlfunc) __P((int, struct sockaddr *, void *));
+       void (*ctlfunc)(int, struct sockaddr *, void *);
        int code;
        int code;
+       char ipv4str[MAX_IPv4_STR_LEN];
 
        /*
         * Locate icmp structure in mbuf, and check
 
        /*
         * Locate icmp structure in mbuf, and check
@@ -281,10 +303,12 @@ icmp_input(m, hlen)
         */
 #if ICMPPRINTFS
        if (icmpprintfs) {
         */
 #if ICMPPRINTFS
        if (icmpprintfs) {
-               char buf[4 * sizeof "123"];
-               strcpy(buf, inet_ntoa(ip->ip_src));
+               char buf[MAX_IPv4_STR_LEN];
+
                printf("icmp_input from %s to %s, len %d\n",
                printf("icmp_input from %s to %s, len %d\n",
-                      buf, inet_ntoa(ip->ip_dst), icmplen);
+                      inet_ntop(AF_INET, &ip->ip_src, buf, sizeof(buf)),
+                      inet_ntop(AF_INET, &ip->ip_dst, ipv4str, sizeof(ipv4str)),
+                      icmplen);
        }
 #endif
        if (icmplen < ICMP_MINLEN) {
        }
 #endif
        if (icmplen < ICMP_MINLEN) {
@@ -420,7 +444,7 @@ icmp_input(m, hlen)
                 * notice that the MTU has changed and adapt accordingly.
                 * If no new MTU was suggested, then we guess a new one
                 * less than the current value.  If the new MTU is 
                 * notice that the MTU has changed and adapt accordingly.
                 * If no new MTU was suggested, then we guess a new one
                 * less than the current value.  If the new MTU is 
-                * unreasonably small (arbitrarily set at 296), then
+                * unreasonably small (defined by sysctl tcp_minmss), then
                 * we reset the MTU to the interface value and enable the
                 * lock bit, indicating that we are no longer doing MTU
                 * discovery.
                 * we reset the MTU to the interface value and enable the
                 * lock bit, indicating that we are no longer doing MTU
                 * discovery.
@@ -439,9 +463,11 @@ icmp_input(m, hlen)
                                                          1);
 #if DEBUG_MTUDISC
                                printf("MTU for %s reduced to %d\n",
                                                          1);
 #if DEBUG_MTUDISC
                                printf("MTU for %s reduced to %d\n",
-                                       inet_ntoa(icmpsrc.sin_addr), mtu);
+                                          inet_ntop(AF_INET, &icmpsrc.sin_addr, ipv4str,
+                                                                sizeof(ipv4str)),
+                                          mtu);
 #endif
 #endif
-                               if (mtu < 296) {
+                               if (mtu < max(296, (tcp_minmss + sizeof(struct tcpiphdr)))) {
                                        /* rt->rt_rmx.rmx_mtu =
                                                rt->rt_ifp->if_mtu; */
                                        rt->rt_rmx.rmx_locks |= RTV_MTU;
                                        /* rt->rt_rmx.rmx_mtu =
                                                rt->rt_ifp->if_mtu; */
                                        rt->rt_rmx.rmx_locks |= RTV_MTU;
@@ -483,6 +509,10 @@ icmp_input(m, hlen)
                        goto reflect;
 
        case ICMP_TSTAMP:
                        goto reflect;
 
        case ICMP_TSTAMP:
+
+               if (icmptimestamp == 0)
+                       break;
+
                if (!icmpbmcastecho
                    && (m->m_flags & (M_MCAST | M_BCAST)) != 0) {
                        icmpstat.icps_bmcasttstamp++;
                if (!icmpbmcastecho
                    && (m->m_flags & (M_MCAST | M_BCAST)) != 0) {
                        icmpstat.icps_bmcasttstamp++;
@@ -526,8 +556,11 @@ icmp_input(m, hlen)
                            (struct sockaddr *)&icmpdst, m->m_pkthdr.rcvif);
                if (ia == 0)
                        break;
                            (struct sockaddr *)&icmpdst, m->m_pkthdr.rcvif);
                if (ia == 0)
                        break;
-               if (ia->ia_ifp == 0)
+               if (ia->ia_ifp == 0) {
+                       ifafree(&ia->ia_ifa);
+                       ia = 0;
                        break;
                        break;
+               }
                icp->icmp_type = ICMP_MASKREPLY;
                icp->icmp_mask = ia->ia_sockmask.sin_addr.s_addr;
                if (ip->ip_src.s_addr == 0) {
                icp->icmp_type = ICMP_MASKREPLY;
                icp->icmp_mask = ia->ia_sockmask.sin_addr.s_addr;
                if (ip->ip_src.s_addr == 0) {
@@ -536,6 +569,7 @@ icmp_input(m, hlen)
                        else if (ia->ia_ifp->if_flags & IFF_POINTOPOINT)
                            ip->ip_src = satosin(&ia->ia_dstaddr)->sin_addr;
                }
                        else if (ia->ia_ifp->if_flags & IFF_POINTOPOINT)
                            ip->ip_src = satosin(&ia->ia_dstaddr)->sin_addr;
                }
+               ifafree(&ia->ia_ifa);
 reflect:
                ip->ip_len += hlen;     /* since ip_input deducts this */
                icmpstat.icps_reflect++;
 reflect:
                ip->ip_len += hlen;     /* since ip_input deducts this */
                icmpstat.icps_reflect++;
@@ -579,11 +613,12 @@ reflect:
                icmpdst.sin_addr = icp->icmp_gwaddr;
 #if    ICMPPRINTFS
                if (icmpprintfs) {
                icmpdst.sin_addr = icp->icmp_gwaddr;
 #if    ICMPPRINTFS
                if (icmpprintfs) {
-                       char buf[4 * sizeof "123"];
-                       strcpy(buf, inet_ntoa(icp->icmp_ip.ip_dst));
+                       char buf[MAX_IPv4_STR_LEN];
 
                        printf("redirect dst %s to %s\n",
 
                        printf("redirect dst %s to %s\n",
-                              buf, inet_ntoa(icp->icmp_gwaddr));
+                              inet_ntop(AF_INET, &icp->icmp_ip.ip_dst, buf, sizeof(buf)),
+                              inet_ntop(AF_INET, &icp->icmp_gwaddr, ipv4str,
+                                                sizeof(ipv4str)));
                }
 #endif
                icmpsrc.sin_addr = icp->icmp_ip.ip_dst;
                }
 #endif
                icmpsrc.sin_addr = icp->icmp_ip.ip_dst;
@@ -646,6 +681,7 @@ icmp_reflect(m)
         * or anonymous), use the address which corresponds
         * to the incoming interface.
         */
         * or anonymous), use the address which corresponds
         * to the incoming interface.
         */
+       lck_mtx_lock(rt_mtx);
        for (ia = in_ifaddrhead.tqh_first; ia; ia = ia->ia_link.tqe_next) {
                if (t.s_addr == IA_SIN(ia)->sin_addr.s_addr)
                        break;
        for (ia = in_ifaddrhead.tqh_first; ia; ia = ia->ia_link.tqe_next) {
                if (t.s_addr == IA_SIN(ia)->sin_addr.s_addr)
                        break;
@@ -653,6 +689,8 @@ icmp_reflect(m)
                    t.s_addr == satosin(&ia->ia_broadaddr)->sin_addr.s_addr)
                        break;
        }
                    t.s_addr == satosin(&ia->ia_broadaddr)->sin_addr.s_addr)
                        break;
        }
+       if (ia)
+               ifaref(&ia->ia_ifa);
        icmpdst.sin_addr = t;
        if ((ia == (struct in_ifaddr *)0) && m->m_pkthdr.rcvif)
                ia = (struct in_ifaddr *)ifaof_ifpforaddr(
        icmpdst.sin_addr = t;
        if ((ia == (struct in_ifaddr *)0) && m->m_pkthdr.rcvif)
                ia = (struct in_ifaddr *)ifaof_ifpforaddr(
@@ -661,11 +699,21 @@ icmp_reflect(m)
         * The following happens if the packet was not addressed to us,
         * and was received on an interface with no IP address.
         */
         * The following happens if the packet was not addressed to us,
         * and was received on an interface with no IP address.
         */
-       if (ia == (struct in_ifaddr *)0)
+       if (ia == (struct in_ifaddr *)0) {
                ia = in_ifaddrhead.tqh_first;
                ia = in_ifaddrhead.tqh_first;
+               if (ia == (struct in_ifaddr *)0) {/* no address yet, bail out */
+                       m_freem(m);
+                       lck_mtx_unlock(rt_mtx);
+                       goto done;
+               }
+               ifaref(&ia->ia_ifa);
+       }
+       lck_mtx_unlock(rt_mtx);
        t = IA_SIN(ia)->sin_addr;
        ip->ip_src = t;
        ip->ip_ttl = ip_defttl;
        t = IA_SIN(ia)->sin_addr;
        ip->ip_src = t;
        ip->ip_ttl = ip_defttl;
+       ifafree(&ia->ia_ifa);
+       ia = NULL;
 
        if (optlen > 0) {
                register u_char *cp;
 
        if (optlen > 0) {
                register u_char *cp;
@@ -759,6 +807,7 @@ icmp_send(m, opts)
        register int hlen;
        register struct icmp *icp;
        struct route ro;
        register int hlen;
        register struct icmp *icp;
        struct route ro;
+       char ipv4str[MAX_IPv4_STR_LEN];
 
        hlen = IP_VHL_HL(ip->ip_vhl) << 2;
        m->m_data += hlen;
 
        hlen = IP_VHL_HL(ip->ip_vhl) << 2;
        m->m_data += hlen;
@@ -768,16 +817,17 @@ icmp_send(m, opts)
        icp->icmp_cksum = in_cksum(m, ip->ip_len - hlen);
        m->m_data -= hlen;
        m->m_len += hlen;
        icp->icmp_cksum = in_cksum(m, ip->ip_len - hlen);
        m->m_data -= hlen;
        m->m_len += hlen;
-       m->m_pkthdr.rcvif = (struct ifnet *)0;
+       m->m_pkthdr.rcvif = 0;
        m->m_pkthdr.aux = NULL;
        m->m_pkthdr.csum_data = 0;
        m->m_pkthdr.csum_flags = 0;
 #if ICMPPRINTFS
        if (icmpprintfs) {
        m->m_pkthdr.aux = NULL;
        m->m_pkthdr.csum_data = 0;
        m->m_pkthdr.csum_flags = 0;
 #if ICMPPRINTFS
        if (icmpprintfs) {
-               char buf[4 * sizeof "123"];
-               strcpy(buf, inet_ntoa(ip->ip_dst));
+               char buf[MAX_IPv4_STR_LEN];
+
                printf("icmp_send dst %s src %s\n",
                printf("icmp_send dst %s src %s\n",
-                      buf, inet_ntoa(ip->ip_src));
+                      inet_ntop(AF_INET, &ip->ip_dst, buf, sizeof(buf)),
+                      inet_ntop(AF_INET, &ip->ip_src, ipv4str, sizeof(ipv4str)));
        }
 #endif
        bzero(&ro, sizeof ro);
        }
 #endif
        bzero(&ro, sizeof ro);
@@ -882,7 +932,7 @@ badport_bandlim(int which)
        if (icmplim <= 0 || which > BANDLIM_MAX || which < 0)
                return(0);
 
        if (icmplim <= 0 || which > BANDLIM_MAX || which < 0)
                return(0);
 
-       getmicrotime(&time);
+       getmicrouptime(&time);
 
        secs = time.tv_sec - lticks[which].tv_sec ;
                        
 
        secs = time.tv_sec - lticks[which].tv_sec ;
                        
@@ -948,7 +998,7 @@ __private_extern__ struct pr_usrreqs icmp_dgram_usrreqs = {
         pru_connect2_notsupp, in_control, rip_detach, rip_disconnect,
         pru_listen_notsupp, in_setpeeraddr, pru_rcvd_notsupp,
         pru_rcvoob_notsupp, icmp_dgram_send, pru_sense_null, rip_shutdown,
         pru_connect2_notsupp, in_control, rip_detach, rip_disconnect,
         pru_listen_notsupp, in_setpeeraddr, pru_rcvd_notsupp,
         pru_rcvoob_notsupp, icmp_dgram_send, pru_sense_null, rip_shutdown,
-        in_setsockaddr, sosend, soreceive, sopoll
+        in_setsockaddr, sosend, soreceive, pru_sopoll_notsupp
 };
 
 /* Like rip_attach but without root privilege enforcement */
 };
 
 /* Like rip_attach but without root privilege enforcement */
@@ -1011,6 +1061,7 @@ icmp_dgram_ctloutput(struct socket *so, struct sockopt *sopt)
                case IP_FAITH:
 #endif
                case IP_STRIPHDR:
                case IP_FAITH:
 #endif
                case IP_STRIPHDR:
+               case IP_RECVTTL:
                        error = rip_ctloutput(so, sopt);
                        break;
                
                        error = rip_ctloutput(so, sopt);
                        break;
                
@@ -1047,8 +1098,7 @@ icmp_dgram_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *n
                /* Only IPv4 */
                if (IP_VHL_V(ip->ip_vhl) != 4)
                        goto bad;
                /* Only IPv4 */
                if (IP_VHL_V(ip->ip_vhl) != 4)
                        goto bad;
-               if (hlen < 20 || hlen > 40 || ip->ip_len != m->m_pkthdr.len ||
-                       ip->ip_len > 65535)
+               if (hlen < 20 || hlen > 40 || ip->ip_len != m->m_pkthdr.len)
                        goto bad;
                /* Bogus fragments can tie up peer resources */ 
                if (ip->ip_off != 0)
                        goto bad;
                /* Bogus fragments can tie up peer resources */ 
                if (ip->ip_off != 0)
@@ -1058,12 +1108,22 @@ icmp_dgram_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *n
                        goto bad;
                /* To prevent spoofing, specified source address must be one of ours */
                if (ip->ip_src.s_addr != INADDR_ANY) {
                        goto bad;
                /* To prevent spoofing, specified source address must be one of ours */
                if (ip->ip_src.s_addr != INADDR_ANY) {
-                       if (TAILQ_EMPTY(&in_ifaddrhead))
+                       socket_unlock(so, 0);
+                       lck_mtx_lock(rt_mtx);
+                       if (TAILQ_EMPTY(&in_ifaddrhead)) {
+                               lck_mtx_unlock(rt_mtx);
+                               socket_lock(so, 0);
                                goto bad;
                                goto bad;
+                       }
                        TAILQ_FOREACH(ia, &in_ifaddrhead, ia_link) {
                        TAILQ_FOREACH(ia, &in_ifaddrhead, ia_link) {
-                               if (IA_SIN(ia)->sin_addr.s_addr == ip->ip_src.s_addr)
+                               if (IA_SIN(ia)->sin_addr.s_addr == ip->ip_src.s_addr) {
+                                       lck_mtx_unlock(rt_mtx);
+                                       socket_lock(so, 0);
                                        goto ours;
                                        goto ours;
+                               }
                        }
                        }
+                       lck_mtx_unlock(rt_mtx);
+                       socket_lock(so, 0);
                        goto bad;
                }
 ours:
                        goto bad;
                }
 ours: