]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/netinet/ip_icmp.c
xnu-3789.51.2.tar.gz
[apple/xnu.git] / bsd / netinet / ip_icmp.c
index ba2869a505cd0a30e15247e0b025fb1cf09fb5ec..1e28775259a4068926618ca728c7c6286c118167 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2013 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2016 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  * 
  */
 
 struct icmpstat icmpstat;
-SYSCTL_STRUCT(_net_inet_icmp, ICMPCTL_STATS, stats, CTLFLAG_RD | CTLFLAG_LOCKED,
-       &icmpstat, icmpstat, "");
+SYSCTL_STRUCT(_net_inet_icmp, ICMPCTL_STATS, stats,
+    CTLFLAG_RD | CTLFLAG_LOCKED,
+    &icmpstat, icmpstat, "");
 
 static int     icmpmaskrepl = 0;
-SYSCTL_INT(_net_inet_icmp, ICMPCTL_MASKREPL, maskrepl, CTLFLAG_RW | CTLFLAG_LOCKED,
-       &icmpmaskrepl, 0, "");
+SYSCTL_INT(_net_inet_icmp, ICMPCTL_MASKREPL, maskrepl,
+    CTLFLAG_RW | CTLFLAG_LOCKED,
+    &icmpmaskrepl, 0, "");
 
 static int     icmptimestamp = 0;
-SYSCTL_INT(_net_inet_icmp, ICMPCTL_TIMESTAMP, timestamp, CTLFLAG_RW | CTLFLAG_LOCKED,
-       &icmptimestamp, 0, "");
+SYSCTL_INT(_net_inet_icmp, ICMPCTL_TIMESTAMP, timestamp,
+    CTLFLAG_RW | CTLFLAG_LOCKED,
+    &icmptimestamp, 0, "");
 
-static int     drop_redirect = 0;
-SYSCTL_INT(_net_inet_icmp, OID_AUTO, drop_redirect, CTLFLAG_RW | CTLFLAG_LOCKED,
-       &drop_redirect, 0, "");
+static int     drop_redirect = 1;
+SYSCTL_INT(_net_inet_icmp, OID_AUTO, drop_redirect,
+    CTLFLAG_RW | CTLFLAG_LOCKED,
+    &drop_redirect, 0, "");
 
 static int     log_redirect = 0;
-SYSCTL_INT(_net_inet_icmp, OID_AUTO, log_redirect, CTLFLAG_RW | CTLFLAG_LOCKED,
-       &log_redirect, 0, "");
+SYSCTL_INT(_net_inet_icmp, OID_AUTO, log_redirect,
+    CTLFLAG_RW | CTLFLAG_LOCKED,
+    &log_redirect, 0, "");
+
+const static int icmp_datalen = 8;
 
 #if ICMP_BANDLIM 
 
@@ -175,9 +182,10 @@ static int icmpbmcastecho = 1;
 SYSCTL_INT(_net_inet_icmp, OID_AUTO, bmcastecho, CTLFLAG_RW | CTLFLAG_LOCKED,
        &icmpbmcastecho, 0, "");
 
-
-#if ICMPPRINTFS
-int    icmpprintfs = 0;
+#if (DEBUG | DEVELOPMENT)
+static int     icmpprintfs = 0;
+SYSCTL_INT(_net_inet_icmp, OID_AUTO, verbose, CTLFLAG_RW | CTLFLAG_LOCKED,
+       &icmpprintfs, 0, "");
 #endif
 
 static void    icmp_reflect(struct mbuf *);
@@ -192,22 +200,22 @@ icmp_error(
        struct mbuf *n,
        int type,
        int code,
-       n_long dest,
+       u_int32_t dest,
        u_int32_t nextmtu)
 {
-       struct ip *oip = mtod(n, struct ip *), *nip;
-       unsigned oiplen;
+       struct ip *oip, *nip;
        struct icmp *icp;
        struct mbuf *m;
-       unsigned icmplen;
+       u_int32_t oiphlen, icmplen, icmpelen, nlen;
 
        /* Expect 32-bit aligned data pointer on strict-align platforms */
        MBUF_STRICT_DATA_ALIGNMENT_CHECK_32(n);
 
-       oiplen = IP_VHL_HL(oip->ip_vhl) << 2;
+       oip = mtod(n, struct ip *);
+       oiphlen = IP_VHL_HL(oip->ip_vhl) << 2;
 
-#if ICMPPRINTFS
-       if (icmpprintfs)
+#if (DEBUG | DEVELOPMENT)
+       if (icmpprintfs > 1)
                printf("icmp_error(0x%llx, %x, %d)\n",
                    (uint64_t)VM_KERNEL_ADDRPERM(oip), type, code);
 #endif
@@ -218,44 +226,92 @@ icmp_error(
         * Don't error if the old packet protocol was ICMP
         * error message, only known informational types.
         */
-       if (oip->ip_off &(IP_MF|IP_DF))
+       if (oip->ip_off & ~(IP_MF|IP_DF))
                goto freeit;
+
        if (oip->ip_p == IPPROTO_ICMP && type != ICMP_REDIRECT &&
-         n->m_len >= oiplen + ICMP_MINLEN &&
-         !ICMP_INFOTYPE(((struct icmp *)(void *)((caddr_t)oip + oiplen))->
+         n->m_len >= oiphlen + ICMP_MINLEN &&
+         !ICMP_INFOTYPE(((struct icmp *)(void *)((caddr_t)oip + oiphlen))->
          icmp_type)) {
                icmpstat.icps_oldicmp++;
                goto freeit;
        }
-       /* Don't send error in response to a multicast or broadcast packet */
+       /*
+        * Don't send error in response to a multicast or
+        * broadcast packet
+        */
        if (n->m_flags & (M_BCAST|M_MCAST))
                goto freeit;
+
+       /*
+        * Calculate the length to quote from original packet and prevent
+        * the ICMP mbuf from overflowing.
+        */
+       nlen = m_length(n);
+       if (oip->ip_p == IPPROTO_TCP) {
+               struct tcphdr *th;
+               u_int16_t tcphlen;
+
+               if (oiphlen + sizeof(struct tcphdr) > n->m_len &&
+                   n->m_next == NULL)
+                       goto stdreply;
+               if (n->m_len < (oiphlen + sizeof(struct tcphdr)) &&
+                   (n = m_pullup(n, (oiphlen + sizeof(struct tcphdr)))) == NULL)
+                       goto freeit;
+
+               th = (struct tcphdr *)(void *)((caddr_t)oip + oiphlen);
+               if (th != ((struct tcphdr *)P2ROUNDDOWN(th,
+                   sizeof(u_int32_t))))
+                       goto freeit;
+               tcphlen = th->th_off << 2;
+               if (tcphlen < sizeof(struct tcphdr))
+                       goto freeit;
+               if (oip->ip_len < (oiphlen + tcphlen))
+                       goto freeit;
+               if ((oiphlen + tcphlen) > n->m_len && n->m_next == NULL)
+                       goto stdreply;
+               if (n->m_len < (oiphlen + tcphlen) &&
+                   (n = m_pullup(n, (oiphlen + tcphlen))) == NULL)
+                       goto freeit;
+
+               icmpelen = max(tcphlen, min(icmp_datalen,
+                   (oip->ip_len - oiphlen)));
+       } else
+stdreply:      icmpelen = max(ICMP_MINLEN, min(icmp_datalen,
+                   (oip->ip_len - oiphlen)));
+
+       icmplen = min(oiphlen + icmpelen, min(nlen, oip->ip_len));
+       if (icmplen < sizeof(struct ip))
+               goto freeit;
        /*
         * First, formulate icmp message
         */
-       m = m_gethdr(M_DONTWAIT, MT_HEADER);    /* MAC-OK */
+       if (MHLEN > (sizeof(struct ip) + ICMP_MINLEN + icmplen))
+               m = m_gethdr(M_DONTWAIT, MT_HEADER);    /* MAC-OK */
+       else
+               m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
+
        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 */
+       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;
        }
 
 #if CONFIG_MACF_NET
        mac_mbuf_label_associate_netlayer(n, m);
 #endif
-       icmplen = min(oiplen + 8, oip->ip_len);
-       if (icmplen < sizeof(struct ip)) {
-               printf("icmp_error: bad length\n");
-               m_free(m);
-               goto freeit;
-       }
-       m->m_len = icmplen + ICMP_MINLEN;
+       m->m_len = icmplen + ICMP_MINLEN; /* for ICMP header and data */
        MH_ALIGN(m, m->m_len);
        icp = mtod(m, struct icmp *);
-       if ((u_int)type > ICMP_MAXTYPE)
-               panic("icmp_error");
+       if ((u_int)type > ICMP_MAXTYPE) {
+               m_freem(m);
+               goto freeit;
+       }
        icmpstat.icps_outhist[type]++;
        icp->icmp_type = type;
        if (type == ICMP_REDIRECT)
@@ -290,8 +346,10 @@ icmp_error(
         * Now, copy old ip header (without options)
         * in front of icmp message.
         */
-       if (m->m_data - sizeof(struct ip) < m->m_pktdat)
-               panic("icmp len");
+       if (m->m_data - sizeof(struct ip) < m->m_pktdat) {
+               m_freem(m);
+               goto freeit;
+       }
        m->m_data -= sizeof(struct ip);
        m->m_len += sizeof(struct ip);
        m->m_pkthdr.len = m->m_len;
@@ -302,6 +360,7 @@ icmp_error(
        nip->ip_vhl = IP_VHL_BORING;
        nip->ip_p = IPPROTO_ICMP;
        nip->ip_tos = 0;
+       nip->ip_off = 0;
        icmp_reflect(m);
 
 freeit:
@@ -332,15 +391,15 @@ icmp_input(struct mbuf *m, int hlen)
         * Locate icmp structure in mbuf, and check
         * that not corrupted and of at least minimum length.
         */
-#if ICMPPRINTFS
-       if (icmpprintfs) {
-               char buf[MAX_IPv4_STR_LEN];
-               char ipv4str[MAX_IPv4_STR_LEN];
-
-               printf("icmp_input from %s to %s, len %d\n",
-                      inet_ntop(AF_INET, &ip->ip_src, buf, sizeof(buf)),
-                      inet_ntop(AF_INET, &ip->ip_dst, ipv4str, sizeof(ipv4str)),
-                      icmplen);
+#if (DEBUG | DEVELOPMENT)
+       if (icmpprintfs  > 2) {
+               char src_str[MAX_IPv4_STR_LEN];
+               char dst_str[MAX_IPv4_STR_LEN];
+
+               inet_ntop(AF_INET, &ip->ip_src, src_str, sizeof(src_str));
+               inet_ntop(AF_INET, &ip->ip_dst, dst_str, sizeof(dst_str));
+               printf("%s: from %s to %s, len %d\n",
+                   __func__, src_str, dst_str, icmplen);
        }
 #endif
        if (icmplen < ICMP_MINLEN) {
@@ -363,8 +422,8 @@ icmp_input(struct mbuf *m, int hlen)
        m->m_len += hlen;
        m->m_data -= hlen;
 
-#if ICMPPRINTFS
-       if (icmpprintfs)
+#if (DEBUG | DEVELOPMENT)
+       if (icmpprintfs > 2)
                printf("icmp_input, type %d code %d\n", icp->icmp_type,
                    icp->icmp_code);
 #endif
@@ -449,8 +508,9 @@ icmp_input(struct mbuf *m, int hlen)
                /*
                 * Problem with datagram; advise higher level routines.
                 */
-               if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp) ||
-                   IP_VHL_HL(icp->icmp_ip.ip_vhl) < (sizeof(struct ip) >> 2)) {
+               if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp)
+                   || IP_VHL_HL(icp->icmp_ip.ip_vhl) <
+                   (sizeof(struct ip) >> 2)) {
                        icmpstat.icps_badlen++;
                        goto freeit;
                }
@@ -462,14 +522,15 @@ icmp_input(struct mbuf *m, int hlen)
                /* Discard ICMP's in response to multicast packets */
                if (IN_MULTICAST(ntohl(icp->icmp_ip.ip_dst.s_addr)))
                        goto badcode;
-#if ICMPPRINTFS
-               if (icmpprintfs)
-                       printf("deliver to protocol %d\n", icp->icmp_ip.ip_p);
+#if (DEBUG | DEVELOPMENT)
+               if (icmpprintfs > 2)
+                       printf("deliver to protocol %d\n",
+                           icp->icmp_ip.ip_p);
 #endif
                icmpsrc.sin_addr = icp->icmp_ip.ip_dst;
 
                /*
-                * XXX if the packet contains [IPv4 AH TCP], we can't make a
+                * if the packet contains [IPv4 AH TCP], we can't make a
                 * notification to TCP layer.
                 */
                ctlfunc = ip_protox[icp->icmp_ip.ip_p]->pr_ctlinput;
@@ -483,11 +544,36 @@ icmp_input(struct mbuf *m, int hlen)
                break;
 
        case ICMP_ECHO:
-               if (!icmpbmcastecho
-                   && (m->m_flags & (M_MCAST | M_BCAST)) != 0) {
+               if ((m->m_flags & (M_MCAST | M_BCAST))) {               
+                       if (icmpbmcastecho == 0) {
+                               icmpstat.icps_bmcastecho++;
+                               break;
+                       }
+               }
+               
+               /*
+                * rdar://18644769
+                * Do not reply when the destination is link local multicast or broadcast
+                * and the source is not from a directly connected subnet
+                */
+               if ((IN_LOCAL_GROUP(ntohl(ip->ip_dst.s_addr)) ||
+                   in_broadcast(ip->ip_dst, m->m_pkthdr.rcvif)) &&
+                   in_localaddr(ip->ip_src) == 0) {
                        icmpstat.icps_bmcastecho++;
+#if (DEBUG | DEVELOPMENT)
+                       if (icmpprintfs > 0) {
+                               char src_str[MAX_IPv4_STR_LEN];
+                               char dst_str[MAX_IPv4_STR_LEN];
+
+                               inet_ntop(AF_INET, &ip->ip_src, src_str, sizeof(src_str));
+                               inet_ntop(AF_INET, &ip->ip_dst, dst_str, sizeof(dst_str));
+                               printf("%s: non local (B|M)CAST %s to %s, len %d\n",
+                                   __func__, src_str, dst_str, icmplen);
+                       }
+#endif
                        break;
                }
+
                icp->icmp_type = ICMP_ECHOREPLY;
 #if ICMP_BANDLIM
                if (badport_bandlim(BANDLIM_ICMP_ECHO) < 0)
@@ -497,7 +583,6 @@ icmp_input(struct mbuf *m, int hlen)
                        goto reflect;
 
        case ICMP_TSTAMP:
-
                if (icmptimestamp == 0)
                        break;
 
@@ -601,14 +686,14 @@ reflect:
                 */
                icmpgw.sin_addr = ip->ip_src;
                icmpdst.sin_addr = icp->icmp_gwaddr;
-#if    ICMPPRINTFS
-               if (icmpprintfs) {
-                       char buf[MAX_IPv4_STR_LEN];
-
-                       printf("redirect dst %s to %s\n",
-                              inet_ntop(AF_INET, &icp->icmp_ip.ip_dst, buf, sizeof(buf)),
-                              inet_ntop(AF_INET, &icp->icmp_gwaddr, ipv4str,
-                                                sizeof(ipv4str)));
+#if (DEBUG | DEVELOPMENT)
+               if (icmpprintfs > 0) {
+                       char dst_str[MAX_IPv4_STR_LEN];
+                       char gw_str[MAX_IPv4_STR_LEN];
+
+                       inet_ntop(AF_INET, &icp->icmp_ip.ip_dst, dst_str, sizeof(dst_str));
+                       inet_ntop(AF_INET, &icp->icmp_gwaddr, gw_str, sizeof(gw_str));
+                       printf("%s: redirect dst %s to %s\n", __func__, dst_str, gw_str);
                }
 #endif
                icmpsrc.sin_addr = icp->icmp_ip.ip_dst;
@@ -748,8 +833,8 @@ match:
                        mtod(opts, struct in_addr *)->s_addr = 0;
                }
                if (opts) {
-#if ICMPPRINTFS
-                   if (icmpprintfs)
+#if (DEBUG | DEVELOPMENT)
+                   if (icmpprintfs > 1)
                            printf("icmp_reflect optlen %d rt %d => ",
                                optlen, opts->m_len);
 #endif
@@ -786,8 +871,8 @@ match:
                                    opts->m_len++;
                            }
                    }
-#if ICMPPRINTFS
-                   if (icmpprintfs)
+#if (DEBUG | DEVELOPMENT)
+                   if (icmpprintfs > 1)
                            printf("%d\n", opts->m_len);
 #endif
                }
@@ -823,7 +908,8 @@ icmp_send(struct mbuf *m, struct mbuf *opts)
        struct icmp *icp;
        struct route ro;
        struct ip_out_args ipoa = { IFSCOPE_NONE, { 0 },
-           IPOAF_SELECT_SRCIF | IPOAF_BOUND_SRCADDR, 0 };
+           IPOAF_SELECT_SRCIF | IPOAF_BOUND_SRCADDR, 0,
+           SO_TC_UNSPEC, _NET_SERVICE_TYPE_UNSPEC };
 
        if (!(m->m_pkthdr.pkt_flags & PKTF_LOOP) && m->m_pkthdr.rcvif != NULL) {
                ipoa.ipoa_boundif = m->m_pkthdr.rcvif->if_index;
@@ -841,14 +927,14 @@ icmp_send(struct mbuf *m, struct mbuf *opts)
        m->m_pkthdr.rcvif = NULL;
        m->m_pkthdr.csum_data = 0;
        m->m_pkthdr.csum_flags = 0;
-#if ICMPPRINTFS
-       if (icmpprintfs) {
-               char buf[MAX_IPv4_STR_LEN];
-               char ipv4str[MAX_IPv4_STR_LEN];
-
-               printf("icmp_send dst %s src %s\n",
-                      inet_ntop(AF_INET, &ip->ip_dst, buf, sizeof(buf)),
-                      inet_ntop(AF_INET, &ip->ip_src, ipv4str, sizeof(ipv4str)));
+#if (DEBUG | DEVELOPMENT)
+       if (icmpprintfs > 2) {
+               char src_str[MAX_IPv4_STR_LEN];
+               char dst_str[MAX_IPv4_STR_LEN];
+
+               inet_ntop(AF_INET, &ip->ip_src, src_str, sizeof(src_str));
+               inet_ntop(AF_INET, &ip->ip_dst, dst_str, sizeof(dst_str));
+               printf("%s: dst %s src %s\n", __func__, dst_str, src_str);
        }
 #endif
        bzero(&ro, sizeof ro);
@@ -856,7 +942,7 @@ icmp_send(struct mbuf *m, struct mbuf *opts)
        ROUTE_RELEASE(&ro);
 }
 
-n_time
+u_int32_t
 iptime(void)
 {
        struct timeval atv;
@@ -1080,9 +1166,6 @@ icmp_dgram_ctloutput(struct socket *so, struct sockopt *sopt)
                case IP_STRIPHDR:
                case IP_RECVTTL:
                case IP_BOUND_IF:
-#if CONFIG_FORCE_OUT_IFP
-                case IP_FORCE_OUT_IFP:
-#endif
                case IP_NO_IFT_CELLULAR:
                        error = rip_ctloutput(so, sopt);
                        break;