]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/netinet6/icmp6.c
xnu-7195.101.1.tar.gz
[apple/xnu.git] / bsd / netinet6 / icmp6.c
index f1d66606e8947ba511fd5772d5e28a8d5a9d8c8e..6e276598c69b8e4f5367cf91491243ed030c21de 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2019 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2020 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  *
@@ -148,6 +148,7 @@ struct icmp6stat icmp6stat;
 
 extern struct inpcbhead ripcb;
 extern int icmp6errppslim;
+extern int icmp6errppslim_random_incr;
 extern int icmp6rappslim;
 static int icmp6errpps_count = 0;
 static int icmp6rapps_count = 0;
@@ -162,7 +163,7 @@ static int icmp6_ratelimit(const struct in6_addr *, const int, const int);
 static const char *icmp6_redirect_diag(struct in6_addr *,
     struct in6_addr *, struct in6_addr *);
 static struct mbuf *ni6_input(struct mbuf *, int);
-static struct mbuf *ni6_nametodns(const char *, int, int);
+static struct mbuf *ni6_nametodns(const char *, uint32_t, int);
 static int ni6_dnsmatch(const char *, int, const char *, int);
 static int ni6_addrs(struct icmp6_nodeinfo *,
     struct ifnet **, char *);
@@ -186,6 +187,11 @@ icmp6_init(struct ip6protosw *pp, struct domain *dp)
        if (!icmp6_initialized) {
                icmp6_initialized = 1;
                mld_init();
+               if (icmp6errppslim >= 0 &&
+                   icmp6errppslim_random_incr > 0 &&
+                   icmp6errppslim <= INT32_MAX - (icmp6errppslim_random_incr + 1)) {
+                       icmp6errppslim += (random() % icmp6errppslim_random_incr) + 1;
+               }
        }
 }
 
@@ -298,7 +304,6 @@ icmp6_error_flag(struct mbuf *m, int type, int code, int param, int flags)
        struct icmp6_hdr *icmp6;
        u_int preplen;
        int off;
-       int nxt;
 
        icmp6stat.icp6s_error++;
 
@@ -355,37 +360,47 @@ icmp6_error_flag(struct mbuf *m, int type, int code, int param, int flags)
        /*
         * If we are about to send ICMPv6 against ICMPv6 error/redirect,
         * don't do it.
+        *
+        * We want to check for that for all ICMP error types, other than
+        * ICMP6_PARAM_PROB when it is being sent in response of first frag
+        * with incomplete header.
+        * That also includes the case when the first frag has incomplete ICMPv6
+        * header. The check below in that case would fail the IP6_EXTHDR_CHECK
+        * and would otherwise prevent us from sending the error back.
         */
-       nxt = -1;
-       off = ip6_lasthdr(m, 0, IPPROTO_IPV6, &nxt);
-       if (off >= 0 && nxt == IPPROTO_ICMPV6) {
-               struct icmp6_hdr *icp;
+       if (type != ICMP6_PARAM_PROB ||
+           code != ICMP6_PARAMPROB_FIRSTFRAG_INCOMP_HDR) {
+               int nxt = -1;
+               off = ip6_lasthdr(m, 0, IPPROTO_IPV6, &nxt);
+               if (off >= 0 && nxt == IPPROTO_ICMPV6) {
+                       struct icmp6_hdr *icp;
 
 #ifndef PULLDOWN_TEST
-               IP6_EXTHDR_CHECK(m, 0, off + sizeof(struct icmp6_hdr), return );
-               icp = (struct icmp6_hdr *)(mtod(m, caddr_t) + off);
+                       IP6_EXTHDR_CHECK(m, 0, off + sizeof(struct icmp6_hdr), return );
+                       icp = (struct icmp6_hdr *)(mtod(m, caddr_t) + off);
 #else
-               IP6_EXTHDR_GET(icp, struct icmp6_hdr *, m, off,
-                   sizeof(*icp));
-               if (icp == NULL) {
-                       icmp6stat.icp6s_tooshort++;
-                       return;
-               }
+                       IP6_EXTHDR_GET(icp, struct icmp6_hdr *, m, off,
+                           sizeof(*icp));
+                       if (icp == NULL) {
+                               icmp6stat.icp6s_tooshort++;
+                               return;
+                       }
 #endif
-               if (icp->icmp6_type < ICMP6_ECHO_REQUEST ||
-                   icp->icmp6_type == ND_REDIRECT) {
-                       /*
-                        * ICMPv6 error
-                        * Special case: for redirect (which is
-                        * informational) we must not send icmp6 error.
-                        */
-                       icmp6stat.icp6s_canterror++;
-                       goto freeit;
+                       if (icp->icmp6_type < ICMP6_ECHO_REQUEST ||
+                           icp->icmp6_type == ND_REDIRECT) {
+                               /*
+                                * ICMPv6 error
+                                * Special case: for redirect (which is
+                                * informational) we must not send icmp6 error.
+                                */
+                               icmp6stat.icp6s_canterror++;
+                               goto freeit;
+                       } else {
+                               /* ICMPv6 informational - send the error */
+                       }
                } else {
-                       /* ICMPv6 informational - send the error */
+                       /* non-ICMPv6 - send the error */
                }
-       } else {
-               /* non-ICMPv6 - send the error */
        }
 
        oip6 = mtod(m, struct ip6_hdr *); /* adjust pointer */
@@ -410,7 +425,7 @@ icmp6_error_flag(struct mbuf *m, int type, int code, int param, int flags)
                m = m_pullup(m, preplen);
        }
        if (m == NULL) {
-               nd6log((LOG_DEBUG, "ENOBUFS in icmp6_error %d\n", __LINE__));
+               nd6log(debug, "ENOBUFS in icmp6_error %d\n", __LINE__);
                return;
        }
 
@@ -422,8 +437,8 @@ icmp6_error_flag(struct mbuf *m, int type, int code, int param, int flags)
        in6_clearscope(&oip6->ip6_dst);
 
        icmp6 = (struct icmp6_hdr *)(nip6 + 1);
-       icmp6->icmp6_type = type;
-       icmp6->icmp6_code = code;
+       icmp6->icmp6_type = (uint8_t)type;
+       icmp6->icmp6_code = (uint8_t)code;
        icmp6->icmp6_pptr = htonl((u_int32_t)param);
 
        /*
@@ -551,9 +566,9 @@ icmp6_input(struct mbuf **mp, int *offp, int proto)
         * calculate the checksum
         */
        if ((sum = in6_cksum(m, IPPROTO_ICMPV6, off, icmp6len)) != 0) {
-               nd6log((LOG_ERR,
+               nd6log(error,
                    "ICMP6 checksum error(%d|%x) %s\n",
-                   icmp6->icmp6_type, sum, ip6_sprintf(&ip6->ip6_src)));
+                   icmp6->icmp6_type, sum, ip6_sprintf(&ip6->ip6_src));
                icmp6stat.icp6s_checksum++;
                goto freeit;
        }
@@ -607,6 +622,10 @@ icmp6_input(struct mbuf **mp, int *offp, int proto)
 
        case ICMP6_PACKET_TOO_BIG:
                icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_pkttoobig);
+               if (ntohl(icmp6->icmp6_mtu) < IPV6_MMTU) {
+                       icmp6stat.icp6s_badpkttoobig++;
+                       goto freeit;
+               }
 
                code = PRC_MSGSIZE;
 
@@ -890,11 +909,11 @@ icmp6_input(struct mbuf **mp, int *offp, int proto)
 
                if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) {
                        /* give up local */
-                       icmp6_redirect_input(m, off);
+                       icmp6_redirect_input(m, off, icmp6len);
                        m = NULL;
                        goto freeit;
                }
-               icmp6_redirect_input(n, off);
+               icmp6_redirect_input(n, off, icmp6len);
                /* m stays. */
                goto rate_limit_checked;
 
@@ -909,11 +928,11 @@ icmp6_input(struct mbuf **mp, int *offp, int proto)
                break;
 
        default:
-               nd6log((LOG_DEBUG,
+               nd6log(debug,
                    "icmp6_input: unknown type %d(src=%s, dst=%s, ifid=%d)\n",
                    icmp6->icmp6_type, ip6_sprintf(&ip6->ip6_src),
                    ip6_sprintf(&ip6->ip6_dst),
-                   m->m_pkthdr.rcvif ? m->m_pkthdr.rcvif->if_index : 0));
+                   m->m_pkthdr.rcvif ? m->m_pkthdr.rcvif->if_index : 0);
                if (icmp6->icmp6_type < ICMP6_ECHO_REQUEST) {
                        /* ICMPv6 error: MUST deliver it by spec... */
                        code = PRC_NCMDS;
@@ -973,6 +992,7 @@ icmp6_notify_error(struct mbuf *m, int off, int icmp6len, int code)
        }
 #endif
        eip6 = (struct ip6_hdr *)(icmp6 + 1);
+       bzero(&icmp6dst, sizeof(icmp6dst));
 
        /* Detect the upper level protocol */
        {
@@ -981,7 +1001,6 @@ icmp6_notify_error(struct mbuf *m, int off, int icmp6len, int code)
                int eoff = off + sizeof(struct icmp6_hdr) +
                    sizeof(struct ip6_hdr);
                struct ip6ctlparam ip6cp;
-               struct in6_addr *finaldst = NULL;
                int icmp6type = icmp6->icmp6_type;
                struct ip6_frag *fh;
                struct ip6_rthdr *rth;
@@ -1067,7 +1086,7 @@ icmp6_notify_error(struct mbuf *m, int off, int icmp6len, int code)
                                        /* just ignore a bogus header */
                                        if ((rth0->ip6r0_len % 2) == 0 &&
                                            (hops = rth0->ip6r0_len / 2)) {
-                                               finaldst = (struct in6_addr *)(void *)(rth0 + 1) + (hops - 1);
+                                               icmp6dst.sin6_addr = *((struct in6_addr *)(void *)(rth0 + 1) + (hops - 1));
                                        }
                                }
                                eoff += rthlen;
@@ -1135,13 +1154,10 @@ notify:
                 */
                eip6 = (struct ip6_hdr *)(icmp6 + 1);
 
-               bzero(&icmp6dst, sizeof(icmp6dst));
                icmp6dst.sin6_len = sizeof(struct sockaddr_in6);
                icmp6dst.sin6_family = AF_INET6;
-               if (finaldst == NULL) {
+               if (IN6_IS_ADDR_UNSPECIFIED(&icmp6dst.sin6_addr)) {
                        icmp6dst.sin6_addr = eip6->ip6_dst;
-               } else {
-                       icmp6dst.sin6_addr = *finaldst;
                }
                if (in6_setscope(&icmp6dst.sin6_addr, m->m_pkthdr.rcvif, NULL)) {
                        goto freeit;
@@ -1156,14 +1172,11 @@ notify:
                icmp6src.sin6_flowinfo =
                    (eip6->ip6_flow & IPV6_FLOWLABEL_MASK);
 
-               if (finaldst == NULL) {
-                       finaldst = &eip6->ip6_dst;
-               }
                ip6cp.ip6c_m = m;
                ip6cp.ip6c_icmp6 = icmp6;
                ip6cp.ip6c_ip6 = (struct ip6_hdr *)(icmp6 + 1);
                ip6cp.ip6c_off = eoff;
-               ip6cp.ip6c_finaldst = finaldst;
+               ip6cp.ip6c_finaldst = &icmp6dst.sin6_addr;
                ip6cp.ip6c_src = &icmp6src;
                ip6cp.ip6c_nxt = nxt;
 
@@ -1213,14 +1226,9 @@ icmp6_mtudisc_update(struct ip6ctlparam *ip6cp, int validated)
                return;
        }
 
-       /*
-        * In case the suggested mtu is less than IPV6_MMTU, we
-        * only need to remember that it was for above mentioned
-        * "alwaysfrag" case.
-        * Try to be as close to the spec as possible.
-        */
+       /* Limit the MTU to the minimum IPv6 MTU */
        if (mtu < IPV6_MMTU) {
-               mtu = IPV6_MMTU - 8;
+               mtu = IPV6_MMTU;
        }
 
        bzero(&sin6, sizeof(sin6));
@@ -1267,7 +1275,7 @@ icmp6_mtudisc_update(struct ip6ctlparam *ip6cp, int validated)
  * - joins NI group address at in6_ifattach() time only, does not cope
  *   with hostname changes by sethostname(3)
  */
-#define hostnamelen     strlen(hostname)
+#define hostnamelen     (uint32_t)strlen(hostname)
 static struct mbuf *
 ni6_input(struct mbuf *m, int off)
 {
@@ -1324,7 +1332,8 @@ ni6_input(struct mbuf *m, int off)
         * [RFC4602, Section 5.]
         */
        if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
-               if (!IN6_IS_ADDR_MC_LINKLOCAL(&ip6->ip6_dst)) {
+               if (!IN6_IS_ADDR_MC_LINKLOCAL(&ip6->ip6_dst) &&
+                   !IN6_IS_ADDR_MC_UNICAST_BASED_LINKLOCAL(&ip6->ip6_dst)) {
                        goto bad;
                }
                /* else it's a link-local multicast, fine */
@@ -1336,9 +1345,8 @@ ni6_input(struct mbuf *m, int off)
                }
                if ((ia6_flags & IN6_IFF_TEMPORARY) &&
                    !(icmp6_nodeinfo & ICMP6_NODEINFO_TMPADDROK)) {
-                       nd6log((LOG_DEBUG, "ni6_input: ignore node info to "
-                           "a temporary address in %s:%d",
-                           __func__, __LINE__));
+                       nd6log(debug, "ni6_input: ignore node info to a temporary address in %s:%d",
+                           __func__, __LINE__);
                        goto bad;
                }
        }
@@ -1353,7 +1361,7 @@ ni6_input(struct mbuf *m, int off)
                if (ni6->ni_code == ICMP6_NI_SUBJ_FQDN && subjlen == 0) {
                        break;
                }
-       /* FALLTHROUGH */
+               OS_FALLTHROUGH;
        case NI_QTYPE_FQDN:
        case NI_QTYPE_NODEADDR:
        case NI_QTYPE_IPV4ADDR:
@@ -1438,7 +1446,9 @@ ni6_input(struct mbuf *m, int off)
                         *   wildcard match, if gethostname(3) side has
                         *   truncated hostname.
                         */
+                       lck_mtx_lock(&hostname_lock);
                        n = ni6_nametodns(hostname, hostnamelen, 0);
+                       lck_mtx_unlock(&hostname_lock);
                        if (!n || n->m_next || n->m_len == 0) {
                                goto bad;
                        }
@@ -1571,7 +1581,9 @@ ni6_input(struct mbuf *m, int off)
                /*
                 * XXX do we really have FQDN in variable "hostname"?
                 */
+               lck_mtx_lock(&hostname_lock);
                n->m_next = ni6_nametodns(hostname, hostnamelen, oldfqdn);
+               lck_mtx_unlock(&hostname_lock);
                if (n->m_next == NULL) {
                        goto bad;
                }
@@ -1588,7 +1600,7 @@ ni6_input(struct mbuf *m, int off)
                nni6->ni_code = ICMP6_NI_SUCCESS;
                n->m_pkthdr.len = n->m_len =
                    sizeof(struct ip6_hdr) + sizeof(struct icmp6_nodeinfo);
-               lenlim = M_TRAILINGSPACE(n);
+               lenlim = (int)M_TRAILINGSPACE(n);
                copied = ni6_store_addrs(ni6, nni6, ifp, lenlim);
                /* XXX: reset mbuf length */
                n->m_pkthdr.len = n->m_len = sizeof(struct ip6_hdr) +
@@ -1627,13 +1639,14 @@ bad:
 static struct mbuf *
 ni6_nametodns(
        const char *name,
-       int namelen,
+       uint32_t namelen,
        int old)        /* return pascal string if non-zero */
 {
        struct mbuf *m;
        char *cp, *ep;
        const char *p, *q;
-       int i, len, nterm;
+       int i, nterm;
+       uint32_t len;
 
        if (old) {
                len = namelen + 1;
@@ -1656,7 +1669,7 @@ ni6_nametodns(
 
        if (old) {
                m->m_len = len;
-               *mtod(m, char *) = namelen;
+               *mtod(m, char *) = (char)namelen;
                bcopy(name, mtod(m, char *) + 1, namelen);
                return m;
        } else {
@@ -1703,7 +1716,7 @@ ni6_nametodns(
                        if (i <= 0 || i >= 64) {
                                goto fail;
                        }
-                       *cp++ = i;
+                       *cp++ = (char)i;
                        bcopy(p, cp, i);
                        cp += i;
                        p = q;
@@ -1718,7 +1731,7 @@ ni6_nametodns(
                while (nterm-- > 0) {
                        *cp++ = '\0';
                }
-               m->m_len = cp - mtod(m, char *);
+               m->m_len = (int32_t)(cp - mtod(m, char *));
                return m;
        }
 
@@ -2060,7 +2073,7 @@ again:
                                ltime = ND6_INFINITE_LIFETIME;
                        } else {
                                if (lt->ia6ti_expire > now) {
-                                       ltime = htonl(lt->ia6ti_expire - now);
+                                       ltime = htonl((uint32_t)(lt->ia6ti_expire - now));
                                } else {
                                        ltime = 0;
                                }
@@ -2170,6 +2183,10 @@ icmp6_rip6_input(struct mbuf **mp, int off)
                        struct  mbuf *n;
                        if ((n = m_copy(m, 0, (int)M_COPYALL)) != NULL) {
                                if ((last->in6p_flags & INP_CONTROLOPTS) != 0 ||
+#if CONTENT_FILTER
+                                   /* Content Filter needs to see local address */
+                                   (last->in6p_socket->so_cfil_db != NULL) ||
+#endif
                                    (last->in6p_socket->so_options & SO_TIMESTAMP) != 0 ||
                                    (last->in6p_socket->so_options & SO_TIMESTAMP_MONOTONIC) != 0 ||
                                    (last->in6p_socket->so_options & SO_TIMESTAMP_CONTINUOUS) != 0) {
@@ -2196,6 +2213,10 @@ icmp6_rip6_input(struct mbuf **mp, int off)
        }
        if (last) {
                if ((last->in6p_flags & INP_CONTROLOPTS) != 0 ||
+#if CONTENT_FILTER
+                   /* Content Filter needs to see local address */
+                   (last->in6p_socket->so_cfil_db != NULL) ||
+#endif
                    (last->in6p_socket->so_options & SO_TIMESTAMP) != 0 ||
                    (last->in6p_socket->so_options & SO_TIMESTAMP_MONOTONIC) != 0 ||
                    (last->in6p_socket->so_options & SO_TIMESTAMP_CONTINUOUS) != 0) {
@@ -2259,10 +2280,10 @@ icmp6_reflect(struct mbuf *m, size_t off)
 
        /* too short to reflect */
        if (off < sizeof(struct ip6_hdr)) {
-               nd6log((LOG_DEBUG,
-                   "sanity fail: off=%lx, sizeof(ip6)=%lx in %s:%d\n",
+               nd6log(debug,
+                   "sanity fail: off=%x, sizeof(ip6)=%x in %s:%d\n",
                    (u_int32_t)off, (u_int32_t)sizeof(struct ip6_hdr),
-                   __func__, __LINE__));
+                   __func__, __LINE__);
                goto bad;
        }
 
@@ -2276,10 +2297,10 @@ icmp6_reflect(struct mbuf *m, size_t off)
 
                l = off - sizeof(struct ip6_hdr);
                m_copydata(m, 0, sizeof(nip6), (caddr_t)&nip6);
-               m_adj(m, l);
+               m_adj(m, (int)l);
                l = sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr);
                if (m->m_len < l) {
-                       if ((m_ip6hdr = m_pulldown(m, 0, l, NULL)) == NULL) {
+                       if ((m_ip6hdr = m_pulldown(m, 0, (int)l, NULL)) == NULL) {
                                return;
                        }
                }
@@ -2288,7 +2309,7 @@ icmp6_reflect(struct mbuf *m, size_t off)
                size_t l;
                l = sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr);
                if (m->m_len < l) {
-                       if ((m_ip6hdr = m_pulldown(m, 0, l, NULL)) == NULL) {
+                       if ((m_ip6hdr = m_pulldown(m, 0, (int)l, NULL)) == NULL) {
                                return;
                        }
                }
@@ -2336,7 +2357,7 @@ icmp6_reflect(struct mbuf *m, size_t off)
         * to search in the ifaddr list.
         */
        lck_rw_lock_shared(&in6_ifaddr_rwlock);
-       for (ia = in6_ifaddrs; ia; ia = ia->ia_next) {
+       TAILQ_FOREACH(ia, IN6ADDR_HASH(&t), ia6_hash) {
                IFA_LOCK(&ia->ia_ifa);
                if (IN6_ARE_ADDR_EQUAL(&t, &ia->ia_addr.sin6_addr) &&
                    (ia->ia6_flags & (IN6_IFF_ANYCAST | IN6_IFF_NOTREADY | IN6_IFF_CLAT46)) == 0) {
@@ -2384,10 +2405,10 @@ icmp6_reflect(struct mbuf *m, size_t off)
                    &src_storage, ip6oa.ip6oa_boundif, &e);
                ROUTE_RELEASE(&ro);
                if (src == NULL) {
-                       nd6log((LOG_DEBUG,
+                       nd6log(debug,
                            "icmp6_reflect: source can't be determined: "
                            "dst=%s, error=%d\n",
-                           ip6_sprintf(&sa6_src.sin6_addr), e));
+                           ip6_sprintf(&sa6_src.sin6_addr), e);
                        goto bad;
                }
        }
@@ -2414,7 +2435,7 @@ icmp6_reflect(struct mbuf *m, size_t off)
                ip6->ip6_hlim = ndi->chlim;
                lck_mtx_unlock(&ndi->lock);
        } else {
-               ip6->ip6_hlim = ip6_defhlim;
+               ip6->ip6_hlim = (uint8_t)ip6_defhlim;
        }
        /* Use the same traffic class as in the request to match IPv4 */
        icmp6->icmp6_cksum = 0;
@@ -2460,49 +2481,57 @@ icmp6_redirect_diag(struct in6_addr *src6,
 }
 
 void
-icmp6_redirect_input(struct mbuf *m, int off)
+icmp6_redirect_input(struct mbuf *m, int off, int icmp6len)
 {
-       struct ifnet *ifp = m->m_pkthdr.rcvif;
-       struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
-       struct nd_redirect *nd_rd;
-       int icmp6len = ntohs(ip6->ip6_plen);
+       struct ifnet *ifp = NULL;
+       struct ip6_hdr *ip6 = NULL;
+       struct nd_redirect *nd_rd = NULL;
        char *lladdr = NULL;
        int lladdrlen = 0;
        u_char *redirhdr = NULL;
        int redirhdrlen = 0;
        struct rtentry *rt = NULL;
-       int is_router;
-       int is_onlink;
-       struct in6_addr src6 = ip6->ip6_src;
-       struct in6_addr redtgt6;
-       struct in6_addr reddst6;
-       union nd_opts ndopts;
-
-       if (!m || !ifp) {
+       int is_router = 0;
+       int is_onlink = 0;
+       struct in6_addr src6 = {};
+       struct in6_addr redtgt6 = {};
+       struct in6_addr reddst6 = {};
+       union nd_opts ndopts = {};
+
+       if (m == NULL) {
                return;
        }
 
+       ifp = m->m_pkthdr.rcvif;
+       if (ifp == NULL) {
+               goto freeit;
+       }
+
        /*
         * If we are an advertising router on this interface,
         * don't update route by icmp6 redirect.
         */
-       if (ifp->if_eflags & IFEF_IPV6_ROUTER) {
+       if (ifp->if_ipv6_router_mode == IPV6_ROUTER_MODE_EXCLUSIVE) {
                goto freeit;
        }
        if (!icmp6_rediraccept) {
                goto freeit;
        }
 
+       ip6 = mtod(m, struct ip6_hdr *);
+       src6 = ip6->ip6_src;
 #ifndef PULLDOWN_TEST
        IP6_EXTHDR_CHECK(m, off, icmp6len, return );
-       nd_rd = (struct nd_redirect *)((caddr_t)ip6 + off);
+       nd_rd = (struct nd_redirect *)(mtod(m, caddr_t) + off);
 #else
        IP6_EXTHDR_GET(nd_rd, struct nd_redirect *, m, off, icmp6len);
        if (nd_rd == NULL) {
                icmp6stat.icp6s_tooshort++;
-               return;
+               goto freeit;
        }
 #endif
+       ip6 = mtod(m, struct ip6_hdr *);
+
        redtgt6 = nd_rd->nd_rd_target;
        reddst6 = nd_rd->nd_rd_dst;
 
@@ -2513,16 +2542,16 @@ icmp6_redirect_input(struct mbuf *m, int off)
 
        /* validation */
        if (!IN6_IS_ADDR_LINKLOCAL(&src6)) {
-               nd6log((LOG_ERR,
+               nd6log(error,
                    "ICMP6 redirect sent from %s rejected; "
-                   "must be from linklocal\n", ip6_sprintf(&src6)));
+                   "must be from linklocal\n", ip6_sprintf(&src6));
                goto bad;
        }
-       if (ip6->ip6_hlim != 255) {
-               nd6log((LOG_ERR,
+       if (ip6->ip6_hlim != IPV6_MAXHLIM) {
+               nd6log(error,
                    "ICMP6 redirect sent from %s rejected; "
                    "hlim=%d (must be 255)\n",
-                   ip6_sprintf(&src6), ip6->ip6_hlim));
+                   ip6_sprintf(&src6), ip6->ip6_hlim);
                goto bad;
        }
        {
@@ -2539,10 +2568,10 @@ icmp6_redirect_input(struct mbuf *m, int off)
                        RT_LOCK(rt);
                        if (rt->rt_gateway == NULL ||
                            rt->rt_gateway->sa_family != AF_INET6) {
-                               nd6log((LOG_ERR,
+                               nd6log(error,
                                    "ICMP6 redirect rejected; no route "
                                    "with inet6 gateway found for redirect dst: %s\n",
-                                   icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
+                                   icmp6_redirect_diag(&src6, &reddst6, &redtgt6));
                                RT_UNLOCK(rt);
                                rtfree(rt);
                                goto bad;
@@ -2551,21 +2580,21 @@ icmp6_redirect_input(struct mbuf *m, int off)
                        gw6 = &(((struct sockaddr_in6 *)(void *)
                            rt->rt_gateway)->sin6_addr);
                        if (bcmp(&src6, gw6, sizeof(struct in6_addr)) != 0) {
-                               nd6log((LOG_ERR,
+                               nd6log(error,
                                    "ICMP6 redirect rejected; "
                                    "not equal to gw-for-src=%s (must be same): "
                                    "%s\n",
                                    ip6_sprintf(gw6),
-                                   icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
+                                   icmp6_redirect_diag(&src6, &reddst6, &redtgt6));
                                RT_UNLOCK(rt);
                                rtfree(rt);
                                goto bad;
                        }
                } else {
-                       nd6log((LOG_ERR,
+                       nd6log(error,
                            "ICMP6 redirect rejected; "
                            "no route found for redirect dst: %s\n",
-                           icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
+                           icmp6_redirect_diag(&src6, &reddst6, &redtgt6));
                        goto bad;
                }
                RT_UNLOCK(rt);
@@ -2573,10 +2602,10 @@ icmp6_redirect_input(struct mbuf *m, int off)
                rt = NULL;
        }
        if (IN6_IS_ADDR_MULTICAST(&reddst6)) {
-               nd6log((LOG_ERR,
+               nd6log(error,
                    "ICMP6 redirect rejected; "
                    "redirect dst must be unicast: %s\n",
-                   icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
+                   icmp6_redirect_diag(&src6, &reddst6, &redtgt6));
                goto bad;
        }
 
@@ -2588,10 +2617,10 @@ icmp6_redirect_input(struct mbuf *m, int off)
                is_onlink = 1;  /* on-link destination case */
        }
        if (!is_router && !is_onlink) {
-               nd6log((LOG_ERR,
+               nd6log(error,
                    "ICMP6 redirect rejected; "
                    "neither router case nor onlink case: %s\n",
-                   icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
+                   icmp6_redirect_diag(&src6, &reddst6, &redtgt6));
                goto bad;
        }
        /* validation passed */
@@ -2599,9 +2628,9 @@ icmp6_redirect_input(struct mbuf *m, int off)
        icmp6len -= sizeof(*nd_rd);
        nd6_option_init(nd_rd + 1, icmp6len, &ndopts);
        if (nd6_options(&ndopts) < 0) {
-               nd6log((LOG_INFO, "icmp6_redirect_input: "
+               nd6log(info, "icmp6_redirect_input: "
                    "invalid ND option, rejected: %s\n",
-                   icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
+                   icmp6_redirect_diag(&src6, &reddst6, &redtgt6));
                /* nd6_options have incremented stats */
                goto freeit;
        }
@@ -2617,11 +2646,11 @@ icmp6_redirect_input(struct mbuf *m, int off)
        }
 
        if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) {
-               nd6log((LOG_INFO,
+               nd6log(info,
                    "icmp6_redirect_input: lladdrlen mismatch for %s "
                    "(if %d, icmp6 packet %d): %s\n",
                    ip6_sprintf(&redtgt6), ifp->if_addrlen, lladdrlen - 2,
-                   icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
+                   icmp6_redirect_diag(&src6, &reddst6, &redtgt6));
                goto bad;
        }
 
@@ -2709,7 +2738,7 @@ icmp6_redirect_output(struct mbuf *m0, struct rtentry *rt)
         * If we are not a router to begin with, or not an advertising
         * router on this interface, don't send icmp6 redirect.
         */
-       if (!ip6_forwarding || !(ifp->if_eflags & IFEF_IPV6_ROUTER)) {
+       if (!ip6_forwarding || ifp->if_ipv6_router_mode != IPV6_ROUTER_MODE_EXCLUSIVE) {
                goto fail;
        }
 
@@ -2759,7 +2788,7 @@ icmp6_redirect_output(struct mbuf *m0, struct rtentry *rt)
        m->m_pkthdr.rcvif = NULL;
        m->m_len = 0;
        maxlen = M_TRAILINGSPACE(m);
-       maxlen = min(IPV6_MMTU, maxlen);
+       maxlen = MIN(IPV6_MMTU, maxlen);
        /* just for safety */
        if (maxlen < sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr) +
            ((sizeof(struct nd_opt_hdr) + ifp->if_addrlen + 7) & ~7)) {
@@ -2799,7 +2828,7 @@ icmp6_redirect_output(struct mbuf *m0, struct rtentry *rt)
        ip6->ip6_vfc |= IPV6_VERSION;
        /* ip6->ip6_plen will be set later */
        ip6->ip6_nxt = IPPROTO_ICMPV6;
-       ip6->ip6_hlim = 255;
+       ip6->ip6_hlim = IPV6_MAXHLIM;
        /* ip6->ip6_src must be linklocal addr for my outgoing if. */
        bcopy(&ifp_ll6, &ip6->ip6_src, sizeof(struct in6_addr));
        bcopy(&sip6->ip6_src, &ip6->ip6_dst, sizeof(struct in6_addr));
@@ -2867,7 +2896,7 @@ icmp6_redirect_output(struct mbuf *m0, struct rtentry *rt)
                    rt_router->rt_gateway) && sdl->sdl_alen) {
                        nd_opt = (struct nd_opt_hdr *)p;
                        nd_opt->nd_opt_type = ND_OPT_TARGET_LINKADDR;
-                       nd_opt->nd_opt_len = len >> 3;
+                       nd_opt->nd_opt_len = (uint8_t)(len >> 3);
                        lladdr = (char *)(nd_opt + 1);
                        bcopy(LLADDR(sdl), lladdr, ifp->if_addrlen);
                        p += len;
@@ -2878,7 +2907,7 @@ icmp6_redirect_output(struct mbuf *m0, struct rtentry *rt)
 
 nolladdropt:;
 
-       m->m_pkthdr.len = m->m_len = p - (u_char *)ip6;
+       m->m_pkthdr.len = m->m_len = (int32_t)(p - (u_char *)ip6);
 
        /* just to be safe */
 #ifdef M_DECRYPTED      /*not openbsd*/
@@ -2899,7 +2928,7 @@ nolladdropt:;
                 * compute the maximum size for icmp6 redirect header option.
                 * XXX room for auth header?
                 */
-               len = maxlen - (p - (u_char *)ip6);
+               len = (int)(maxlen - (p - (u_char *)ip6));
                len &= ~7;
 
                /* This is just for simplicity. */
@@ -2952,9 +2981,9 @@ nolladdropt:;
                nd_opt_rh = (struct nd_opt_rd_hdr *)p;
                bzero(nd_opt_rh, sizeof(*nd_opt_rh));
                nd_opt_rh->nd_opt_rh_type = ND_OPT_REDIRECTED_HEADER;
-               nd_opt_rh->nd_opt_rh_len = len >> 3;
+               nd_opt_rh->nd_opt_rh_len = (uint8_t)(len >> 3);
                p += sizeof(*nd_opt_rh);
-               m->m_pkthdr.len = m->m_len = p - (u_char *)ip6;
+               m->m_pkthdr.len = m->m_len = (int32_t)(p - (u_char *)ip6);
 
                /* connect m0 to m */
                m->m_next = m0;
@@ -2968,7 +2997,7 @@ noredhdropt:;
        in6_clearscope(&nd_rd->nd_rd_target);
        in6_clearscope(&nd_rd->nd_rd_dst);
 
-       ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(struct ip6_hdr));
+       ip6->ip6_plen = htons((uint16_t)(m->m_pkthdr.len - sizeof(struct ip6_hdr)));
 
        nd_rd->nd_rd_cksum = 0;
        nd_rd->nd_rd_cksum
@@ -2977,6 +3006,7 @@ noredhdropt:;
        /* send the packet to outside... */
        ip6oa.ip6oa_boundif = ifp->if_index;
        ip6oa.ip6oa_flags |= IP6OAF_BOUND_IF;
+       ip6oa.ip6oa_flags |= IP6OAF_DONT_FRAG;
 
        ip6_output(m, NULL, NULL, IPV6_OUTARGS, NULL, &outif, &ip6oa);
        if (outif) {
@@ -3007,7 +3037,7 @@ int
 icmp6_ctloutput(struct socket *so, struct sockopt *sopt)
 {
        int error = 0;
-       int optlen;
+       size_t optlen;
        struct inpcb *inp = sotoinpcb(so);
        int level, op, optname;
 
@@ -3068,7 +3098,7 @@ icmp6_ctloutput(struct socket *so, struct sockopt *sopt)
                                break;
                        }
                        error = sooptcopyout(sopt, inp->in6p_icmp6filt,
-                           min(sizeof(struct icmp6_filter), optlen));
+                           MIN(sizeof(struct icmp6_filter), optlen));
                        break;
                }
 
@@ -3150,8 +3180,6 @@ icmp6_dgram_send(struct socket *so, int flags, struct mbuf *m,
 #pragma unused(flags, p)
        int error = 0;
        struct inpcb *inp = sotoinpcb(so);
-       struct sockaddr_in6 tmp;
-       struct sockaddr_in6 *dst = (struct sockaddr_in6 *)(void *)nam;
        struct icmp6_hdr *icmp6;
 
        if (inp == NULL
@@ -3167,28 +3195,6 @@ icmp6_dgram_send(struct socket *so, int flags, struct mbuf *m,
                return rip6_output(m, so, SIN6(nam), control, 0);
        }
 
-       /* always copy sockaddr to avoid overwrites */
-       if (so->so_state & SS_ISCONNECTED) {
-               if (nam != NULL) {
-                       error = EISCONN;
-                       goto bad;
-               }
-               /* XXX */
-               bzero(&tmp, sizeof(tmp));
-               tmp.sin6_family = AF_INET6;
-               tmp.sin6_len = sizeof(struct sockaddr_in6);
-               bcopy(&inp->in6p_faddr, &tmp.sin6_addr,
-                   sizeof(struct in6_addr));
-               dst = &tmp;
-       } else {
-               if (nam == NULL) {
-                       error = ENOTCONN;
-                       goto bad;
-               }
-               tmp = *(struct sockaddr_in6 *)(void *)nam;
-               dst = &tmp;
-       }
-
        /*
         * For an ICMPv6 packet, we should know its type and code
         */
@@ -3217,13 +3223,7 @@ icmp6_dgram_send(struct socket *so, int flags, struct mbuf *m,
                }
        }
 
-#if ENABLE_DEFAULT_SCOPE
-       if (dst->sin6_scope_id == 0) {  /* not change if specified  */
-               dst->sin6_scope_id = scope6_addr2default(&dst->sin6_addr);
-       }
-#endif
-
-       return rip6_output(m, so, dst, control, 0);
+       return rip6_output(m, so, SIN6(nam), control, 0);
 bad:
        VERIFY(error != 0);
 
@@ -3302,8 +3302,17 @@ icmp6_ratelimit(
                }
        } else if (!ppsratecheck(&icmp6errppslim_last, &icmp6errpps_count,
            icmp6errppslim)) {
-               /* The packet is subject to rate limit */
-               ret++;
+               /*
+                * We add some randomness here to still generate ICMPv6 error
+                * post icmp6errppslim limit with a probability that goes down
+                * with increased value of icmp6errpps_count.
+                */
+               if (icmp6errpps_count > 0 && icmp6errppslim > 0 &&
+                   icmp6errpps_count > icmp6errppslim &&
+                   (random() % (icmp6errpps_count - icmp6errppslim)) != 0) {
+                       /* The packet is subject to rate limit */
+                       ret++;
+               }
        }
 
        return ret;