]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/netinet6/icmp6.c
xnu-1228.7.58.tar.gz
[apple/xnu.git] / bsd / netinet6 / icmp6.c
index 1291a6ea653449b1ccb640cfd8f8914488226b1f..11b39041314d81dd9dc3b8e8b024799f57f64f52 100644 (file)
@@ -1,4 +1,5 @@
-/*     $KAME: icmp6.c,v 1.77 2000/04/13 11:31:39 itojun Exp $  */
+/*     $FreeBSD: src/sys/netinet6/icmp6.c,v 1.6.2.6 2001/07/10 09:44:16 ume Exp $      */
+/*     $KAME: icmp6.c,v 1.211 2001/04/04 05:56:20 itojun Exp $ */
 
 /*
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
  *     @(#)ip_icmp.c   8.2 (Berkeley) 1/4/94
  */
 
-#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined(__NetBSD__)
-#include "opt_inet.h"
-#ifdef __NetBSD__      /*XXX*/
-#include "opt_ipsec.h"
-#endif
-#endif
 
 #include <sys/param.h>
 #include <sys/systm.h>
+#include <sys/lock.h>
 #include <sys/malloc.h>
 #include <sys/mbuf.h>
 #include <sys/protosw.h>
 
 #include <netinet/in.h>
 #include <netinet/in_var.h>
-#if defined(__OpenBSD__) || (defined(__bsdi__) && _BSDI_VERSION >= 199802)
-#include <netinet/in_systm.h>
-#include <netinet/ip.h>
-#endif
 #include <netinet/ip6.h>
 #include <netinet6/ip6_var.h>
 #include <netinet/icmp6.h>
 #include <netinet6/mld6_var.h>
-#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) && !defined(__OpenBSD__) && !(defined(__bsdi__) && _BSDI_VERSION >= 199802) && !defined(__APPLE__)
-#include <netinet6/in6_pcb.h>
-#else
 #include <netinet/in_pcb.h>
-#endif
+#include <netinet6/in6_pcb.h>
 #include <netinet6/nd6.h>
 #include <netinet6/in6_ifattach.h>
 #include <netinet6/ip6protosw.h>
 
-#ifdef __OpenBSD__ /*KAME IPSEC*/
-#undef IPSEC
-#endif
-
 #if IPSEC
 #include <netinet6/ipsec.h>
 #include <netkey/key.h>
-#include <netkey/key_debug.h>
-#endif
 
-#include "faith.h"
+extern int ipsec_bypass;
+#endif
 
 #include <net/net_osdep.h>
 
@@ -125,54 +109,104 @@ extern struct domain inet6domain;
 extern struct ip6protosw inet6sw[];
 extern struct ip6protosw *ip6_protox[];
 
+extern u_long rip_sendspace;
+extern u_long rip_recvspace;
+
 struct icmp6stat icmp6stat;
 
-#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) && !defined(__APPLE__)
-extern struct in6pcb rawin6pcb;
-#else
 extern struct inpcbhead ripcb;
-#endif
-extern u_int icmp6errratelim;
+extern int icmp6errppslim;
+static int icmp6errpps_count = 0;
+static struct timeval icmp6errppslim_last;
 extern int icmp6_nodeinfo;
-#if defined(__NetBSD__) || defined(__OpenBSD__)
-static struct rttimer_queue *icmp6_mtudisc_timeout_q = NULL;
-extern int pmtu_expire;
-#endif
-
-#ifndef HAVE_NRL_INPCB
-static int icmp6_rip6_input __P((struct mbuf **, int));
-#endif
-static void icmp6_mtudisc_update __P((struct in6_addr *, struct icmp6_hdr *,
-                                     struct mbuf *));
-static int icmp6_ratelimit __P((const struct in6_addr *, const int, const int));
-static const char *icmp6_redirect_diag __P((struct in6_addr *,
-       struct in6_addr *, struct in6_addr *));
-static struct mbuf * ni6_input __P((struct mbuf *, int));
-static int ni6_addrs __P((struct icmp6_nodeinfo *, struct mbuf *,
-                         struct ifnet **));
-static int ni6_store_addrs __P((struct icmp6_nodeinfo *, struct icmp6_nodeinfo *,
-                               struct ifnet *, int));
-#if defined(__NetBSD__) || defined(__OpenBSD__)
-static struct rtentry *icmp6_mtudisc_clone __P((struct sockaddr *));
-static void icmp6_mtudisc_timeout __P((struct rtentry *, struct rttimer *));
-#endif
+extern struct inpcbinfo ripcbinfo;
+extern lck_mtx_t *ip6_mutex; 
+extern lck_mtx_t *nd6_mutex; 
+
+static void icmp6_errcount(struct icmp6errstat *, int, int);
+static int icmp6_rip6_input(struct mbuf **, int);
+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 *);
+#ifndef HAVE_PPSRATECHECK
+static int ppsratecheck(struct timeval *, int *, int);
+#endif
+static struct mbuf *ni6_input(struct mbuf *, int);
+static struct mbuf *ni6_nametodns(const char *, int, int);
+static int ni6_dnsmatch(const char *, int, const char *, int);
+static int ni6_addrs(struct icmp6_nodeinfo *, 
+                         struct ifnet **, char *);
+static int ni6_store_addrs(struct icmp6_nodeinfo *, struct icmp6_nodeinfo *,
+                               struct ifnet *, int);
+static int icmp6_notify_error(struct mbuf *, int, int, int);
 
 #ifdef COMPAT_RFC1885
 static struct route_in6 icmp6_reflect_rt;
 #endif
-static struct timeval icmp6_nextsend = {0, 0};
 
-#if MIP6
-int (*mip6_icmp6_input_hook)(struct mbuf *m, int off) = NULL;
-#endif /* MIP6 */
 
 void
 icmp6_init()
 {
        mld6_init();
-#if defined(__NetBSD__) || defined(__OpenBSD__)
-       icmp6_mtudisc_timeout_q = rt_timer_queue_create(pmtu_expire);
-#endif
+}
+
+static void
+icmp6_errcount(stat, type, code)
+       struct icmp6errstat *stat;
+       int type, code;
+{
+       switch (type) {
+       case ICMP6_DST_UNREACH:
+               switch (code) {
+               case ICMP6_DST_UNREACH_NOROUTE:
+                       stat->icp6errs_dst_unreach_noroute++;
+                       return;
+               case ICMP6_DST_UNREACH_ADMIN:
+                       stat->icp6errs_dst_unreach_admin++;
+                       return;
+               case ICMP6_DST_UNREACH_BEYONDSCOPE:
+                       stat->icp6errs_dst_unreach_beyondscope++;
+                       return;
+               case ICMP6_DST_UNREACH_ADDR:
+                       stat->icp6errs_dst_unreach_addr++;
+                       return;
+               case ICMP6_DST_UNREACH_NOPORT:
+                       stat->icp6errs_dst_unreach_noport++;
+                       return;
+               }
+               break;
+       case ICMP6_PACKET_TOO_BIG:
+               stat->icp6errs_packet_too_big++;
+               return;
+       case ICMP6_TIME_EXCEEDED:
+               switch (code) {
+               case ICMP6_TIME_EXCEED_TRANSIT:
+                       stat->icp6errs_time_exceed_transit++;
+                       return;
+               case ICMP6_TIME_EXCEED_REASSEMBLY:
+                       stat->icp6errs_time_exceed_reassembly++;
+                       return;
+               }
+               break;
+       case ICMP6_PARAM_PROB:
+               switch (code) {
+               case ICMP6_PARAMPROB_HEADER:
+                       stat->icp6errs_paramprob_header++;
+                       return;
+               case ICMP6_PARAMPROB_NEXTHEADER:
+                       stat->icp6errs_paramprob_nextheader++;
+                       return;
+               case ICMP6_PARAMPROB_OPTION:
+                       stat->icp6errs_paramprob_option++;
+                       return;
+               }
+               break;
+       case ND_REDIRECT:
+               stat->icp6errs_redirect++;
+               return;
+       }
+       stat->icp6errs_unknown++;
 }
 
 /*
@@ -191,6 +225,10 @@ icmp6_error(m, type, code, param)
 
        icmp6stat.icp6s_error++;
 
+       lck_mtx_assert(ip6_mutex, LCK_MTX_ASSERT_NOTOWNED);
+       /* count per-type-code statistics */
+       icmp6_errcount(&icmp6stat.icp6s_outerrhist, type, code);
+
 #ifdef M_DECRYPTED     /*not openbsd*/
        if (m->m_flags & M_DECRYPTED) {
                icmp6stat.icp6s_canterror++;
@@ -199,7 +237,7 @@ icmp6_error(m, type, code, param)
 #endif
 
 #ifndef PULLDOWN_TEST
-       IP6_EXTHDR_CHECK(m, 0, sizeof(struct ip6_hdr), );
+       IP6_EXTHDR_CHECK(m, 0, sizeof(struct ip6_hdr), return);
 #else
        if (m->m_len < sizeof(struct ip6_hdr)) {
                m = m_pullup(m, sizeof(struct ip6_hdr));
@@ -236,7 +274,7 @@ icmp6_error(m, type, code, param)
                struct icmp6_hdr *icp;
 
 #ifndef PULLDOWN_TEST
-               IP6_EXTHDR_CHECK(m, 0, off + sizeof(struct icmp6_hdr), );
+               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,
@@ -282,7 +320,7 @@ icmp6_error(m, type, code, param)
        if (m && m->m_len < preplen)
                m = m_pullup(m, preplen);
        if (m == NULL) {
-               printf("ENOBUFS in icmp6_error %d\n", __LINE__);
+               nd6log((LOG_DEBUG, "ENOBUFS in icmp6_error %d\n", __LINE__));
                return;
        }
 
@@ -300,8 +338,17 @@ icmp6_error(m, type, code, param)
        icmp6->icmp6_code = code;
        icmp6->icmp6_pptr = htonl((u_int32_t)param);
 
+       /*
+        * icmp6_reflect() is designed to be in the input path.
+        * icmp6_error() can be called from both input and outut path,
+        * and if we are in output path rcvif could contain bogus value.
+        * clear m->m_pkthdr.rcvif for safety, we should have enough scope
+        * information in ip header (nip6).
+        */
+       m->m_pkthdr.rcvif = NULL;
+
        icmp6stat.icp6s_outhist[type]++;
-       icmp6_reflect(m, sizeof(struct ip6_hdr)); /*header order: IPv6 - ICMPv6*/
+       icmp6_reflect(m, sizeof(struct ip6_hdr)); /* header order: IPv6 - ICMPv6 */
 
        return;
 
@@ -316,9 +363,9 @@ icmp6_error(m, type, code, param)
  * Process a received ICMP6 message.
  */
 int
-icmp6_input(mp, offp, proto)
+icmp6_input(mp, offp)
        struct mbuf **mp;
-       int *offp, proto;
+       int *offp;
 {
        struct mbuf *m = *mp, *n;
        struct ip6_hdr *ip6, *nip6;
@@ -326,11 +373,10 @@ icmp6_input(mp, offp, proto)
        int off = *offp;
        int icmp6len = m->m_pkthdr.len - *offp;
        int code, sum, noff;
-       struct sockaddr_in6 icmp6src;
 
 #ifndef PULLDOWN_TEST
-       IP6_EXTHDR_CHECK(m, off, sizeof(struct icmp6_hdr), IPPROTO_DONE);
-       /* m might change if M_LOOP. So, call mtod after this */
+       IP6_EXTHDR_CHECK(m, off, sizeof(struct icmp6_hdr), return IPPROTO_DONE);
+       /* m might change if M_LOOP.  So, call mtod after this */
 #endif
 
        /*
@@ -359,17 +405,15 @@ icmp6_input(mp, offp, proto)
        code = icmp6->icmp6_code;
 
        if ((sum = in6_cksum(m, IPPROTO_ICMPV6, off, icmp6len)) != 0) {
-               log(LOG_ERR,
+               nd6log((LOG_ERR,
                    "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;
        }
 
 #if defined(NFAITH) && 0 < NFAITH
-       if (m->m_pkthdr.rcvif && m->m_pkthdr.rcvif->if_type == IFT_FAITH) {
+       if (faithprefix(&ip6->ip6_dst)) {
                /*
                 * Deliver very specific ICMP6 type only.
                 * This is important to deilver TOOBIG.  Otherwise PMTUD
@@ -386,34 +430,12 @@ icmp6_input(mp, offp, proto)
        }
 #endif
 
-#if IPSEC
-       /* drop it if it does not match the default policy */
-       if (ipsec6_in_reject(m, NULL)) {
-               ipsecstat.in_polvio++;
-               goto freeit;
-       }
-#endif
-
        icmp6stat.icp6s_inhist[icmp6->icmp6_type]++;
        icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_msg);
        if (icmp6->icmp6_type < ICMP6_INFOMSG_MASK)
                icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_error);
 
-#if MIP6
-       /*
-        * Mobile IPv6
-        *
-        * Check for ICMP errors and modifications and extensions to Router
-        * Advertisement.
-        */
-       if (mip6_icmp6_input_hook) {
-               if ((*mip6_icmp6_input_hook)(m, off) != 0)
-                       goto freeit;
-       }
-#endif /* MIP6 */
-       
        switch (icmp6->icmp6_type) {
-               
        case ICMP6_DST_UNREACH:
                icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_dstunreach);
                switch (code) {
@@ -507,14 +529,11 @@ icmp6_input(mp, offp, proto)
                         * always copy the length we specified.
                         */
                        if (maxlen >= MCLBYTES) {
-#if DIAGNOSTIC
-                               printf("MCLBYTES too small\n");
-#endif
                                /* Give up remote */
                                m_freem(n0);
                                break;
                        }
-                       MGETHDR(n, M_DONTWAIT, n0->m_type);
+                       MGETHDR(n, M_DONTWAIT, n0->m_type);     /* MAC-OK */
                        if (n && maxlen >= MHLEN) {
                                MCLGET(n, M_DONTWAIT);
                                if ((n->m_flags & M_EXT) == 0) {
@@ -548,7 +567,8 @@ icmp6_input(mp, offp, proto)
                        n0->m_flags &= ~M_PKTHDR;
                } else {
                        nip6 = mtod(n, struct ip6_hdr *);
-                       nicmp6 = (struct icmp6_hdr *)((caddr_t)nip6 + off);
+                       IP6_EXTHDR_GET(nicmp6, struct icmp6_hdr *, n, off,
+                               sizeof(*nicmp6));
                        noff = off;
                }
                nicmp6->icmp6_type = ICMP6_ECHO_REPLY;
@@ -596,88 +616,31 @@ icmp6_input(mp, offp, proto)
                /* XXX: per-interface statistics? */
                break;          /* just pass it to applications */
 
-       case ICMP6_WRUREQUEST:  /* ICMP6_FQDN_QUERY */
-           {
-               enum { WRU, FQDN } mode;
-
-               if (code != 0)
-                       goto badcode;
+       case ICMP6_NI_QUERY:
                if (!icmp6_nodeinfo)
                        break;
 
-               if (icmp6len == sizeof(struct icmp6_hdr) + 4)
-                       mode = WRU;
-               else if (icmp6len >= sizeof(struct icmp6_hdr) + 8) /* XXX */
-                       mode = FQDN;
-               else
+               /* By RFC 4620 refuse to answer queries from global scope addresses */ 
+               if ((icmp6_nodeinfo & 8) != 8 && in6_addrscope(&ip6->ip6_src) == IPV6_ADDR_SCOPE_GLOBAL)
+                       break;
+
+               if (icmp6len < sizeof(struct icmp6_nodeinfo))
                        goto badlen;
 
-#if defined( __FreeBSD__) || defined (__APPLE__)
-#define hostnamelen    strlen(hostname)
-#endif
-               if (mode == FQDN) {
 #ifndef PULLDOWN_TEST
-                       IP6_EXTHDR_CHECK(m, off, sizeof(struct icmp6_nodeinfo),
-                                        IPPROTO_DONE);
-#endif
-                       n = m_copy(m, 0, M_COPYALL);
-                       if (n)
-                               n = ni6_input(n, off);
-                       if (n)
-                               noff = sizeof(struct ip6_hdr);
-               } else {
-                       u_char *p;
-                       int maxlen, maxhlen;
-
-                       maxlen = sizeof(*nip6) + sizeof(*nicmp6) + 4;
-                       if (maxlen >= MCLBYTES) {
-#if DIAGNOSTIC
-                               printf("MCLBYTES too small\n");
+               IP6_EXTHDR_CHECK(m, off, sizeof(struct icmp6_nodeinfo),
+                                return IPPROTO_DONE);
 #endif
-                               /* Give up remote */
-                               break;
-                       }
-                       MGETHDR(n, M_DONTWAIT, m->m_type);
-                       if (n && maxlen > MHLEN) {
-                               MCLGET(n, M_DONTWAIT);
-                               if ((n->m_flags & M_EXT) == 0) {
-                                       m_free(n);
-                                       n = NULL;
-                               }
-                       }
-                       if (n == NULL) {
-                               /* Give up remote */
-                               break;
-                       }
-                       n->m_len = 0;
-                       maxhlen = M_TRAILINGSPACE(n) - maxlen;
-                       if (maxhlen > hostnamelen)
-                               maxhlen = hostnamelen;
-                       /*
-                        * Copy IPv6 and ICMPv6 only.
-                        */
-                       nip6 = mtod(n, struct ip6_hdr *);
-                       bcopy(ip6, nip6, sizeof(struct ip6_hdr));
-                       nicmp6 = (struct icmp6_hdr *)(nip6 + 1);
-                       bcopy(icmp6, nicmp6, sizeof(struct icmp6_hdr));
-                       p = (u_char *)(nicmp6 + 1);
-                       bzero(p, 4);
-                       bcopy(hostname, p + 4, maxhlen);
-                       noff = sizeof(struct ip6_hdr);
-                       M_COPY_PKTHDR(n, m); /* just for recvif */
-                       n->m_pkthdr.len = n->m_len = sizeof(struct ip6_hdr) +
-                               sizeof(struct icmp6_hdr) + 4 + maxhlen;
-                       nicmp6->icmp6_type = ICMP6_WRUREPLY;
-                       nicmp6->icmp6_code = 0;
-               }
-#undef hostnamelen
+               n = m_copy(m, 0, M_COPYALL);
+               if (n)
+                       n = ni6_input(n, off);
                if (n) {
+                       noff = sizeof(struct ip6_hdr);
                        icmp6stat.icp6s_reflect++;
                        icmp6stat.icp6s_outhist[ICMP6_WRUREPLY]++;
                        icmp6_reflect(n, noff);
                }
                break;
-           }
 
        case ICMP6_WRUREPLY:
                if (code != 0)
@@ -773,10 +736,11 @@ icmp6_input(mp, offp, proto)
                break;
 
        default:
-               printf("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);
+               nd6log((LOG_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));
                if (icmp6->icmp6_type < ICMP6_ECHO_REQUEST) {
                        /* ICMPv6 error: MUST deliver it by spec... */
                        code = PRC_NCMDS;
@@ -786,32 +750,63 @@ icmp6_input(mp, offp, proto)
                        break;
                }
        deliver:
-               if (icmp6len < sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr)) {
-                       icmp6stat.icp6s_tooshort++;
-                       goto freeit;
+               if (icmp6_notify_error(m, off, icmp6len, code)) {
+                       /* In this case, m should've been freed. */
+                       return(IPPROTO_DONE);
                }
+               break;
+
+       badcode:
+               icmp6stat.icp6s_badcode++;
+               break;
+
+       badlen:
+               icmp6stat.icp6s_badlen++;
+               break;
+       }
+
+       /* deliver the packet to appropriate sockets */
+       icmp6_rip6_input(&m, *offp);
+
+       return IPPROTO_DONE;
+
+ freeit:
+       m_freem(m);
+       return IPPROTO_DONE;
+}
+
+static int
+icmp6_notify_error(m, off, icmp6len, code)
+       struct mbuf *m;
+       int off, icmp6len, code;
+{
+       struct icmp6_hdr *icmp6;
+       struct ip6_hdr *eip6;
+       u_int32_t notifymtu;
+       struct sockaddr_in6 icmp6src, icmp6dst;
+
+       if (icmp6len < sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr)) {
+               icmp6stat.icp6s_tooshort++;
+               goto freeit;
+       }
 #ifndef PULLDOWN_TEST
-               IP6_EXTHDR_CHECK(m, off,
-                       sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr),
-                       IPPROTO_DONE);
-               icmp6 = (struct icmp6_hdr *)(mtod(m, caddr_t) + off);
+       IP6_EXTHDR_CHECK(m, off,
+                        sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr),
+                        return -1);
+       icmp6 = (struct icmp6_hdr *)(mtod(m, caddr_t) + off);
 #else
-               IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off,
-                       sizeof(*icmp6) + sizeof(struct ip6_hdr));
-               if (icmp6 == NULL) {
-                       icmp6stat.icp6s_tooshort++;
-                       return IPPROTO_DONE;
-               }
+       IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off,
+                      sizeof(*icmp6) + sizeof(struct ip6_hdr));
+       if (icmp6 == NULL) {
+               icmp6stat.icp6s_tooshort++;
+               return(-1);
+       }
 #endif
-               bzero(&icmp6src, sizeof(icmp6src));
-               icmp6src.sin6_len = sizeof(struct sockaddr_in6);
-               icmp6src.sin6_family = AF_INET6;
-               icmp6src.sin6_addr = ((struct ip6_hdr *)(icmp6 + 1))->ip6_dst;
+       eip6 = (struct ip6_hdr *)(icmp6 + 1);
 
-               /* Detect the upper level protocol */
-           {
-               void (*ctlfunc) __P((int, struct sockaddr *, void *));
-               struct ip6_hdr *eip6 = (struct ip6_hdr *)(icmp6 + 1);
+       /* Detect the upper level protocol */
+       {
+               void (*ctlfunc)(int, struct sockaddr *, void *);
                u_int8_t nxt = eip6->ip6_nxt;
                int eoff = off + sizeof(struct icmp6_hdr) +
                        sizeof(struct ip6_hdr);
@@ -823,25 +818,25 @@ icmp6_input(mp, offp, proto)
                struct ip6_rthdr0 *rth0;
                int rthlen;
 
-               while (1) { /* XXX: should avoid inf. loop explicitly? */
+               while (1) { /* XXX: should avoid infinite loop explicitly? */
                        struct ip6_ext *eh;
 
-                       switch(nxt) {
+                       switch (nxt) {
                        case IPPROTO_HOPOPTS:
                        case IPPROTO_DSTOPTS:
                        case IPPROTO_AH:
 #ifndef PULLDOWN_TEST
                                IP6_EXTHDR_CHECK(m, 0, eoff +
                                                 sizeof(struct ip6_ext),
-                                                IPPROTO_DONE);
+                                                return -1);
                                eh = (struct ip6_ext *)(mtod(m, caddr_t)
                                                        + eoff);
 #else
                                IP6_EXTHDR_GET(eh, struct ip6_ext *, m,
-                                       eoff, sizeof(*eh));
+                                              eoff, sizeof(*eh));
                                if (eh == NULL) {
                                        icmp6stat.icp6s_tooshort++;
-                                       return IPPROTO_DONE;
+                                       return(-1);
                                }
 #endif
                                
@@ -862,15 +857,15 @@ icmp6_input(mp, offp, proto)
                                 */
 #ifndef PULLDOWN_TEST
                                IP6_EXTHDR_CHECK(m, 0, eoff + sizeof(*rth),
-                                                IPPROTO_DONE);
+                                                return -1);
                                rth = (struct ip6_rthdr *)(mtod(m, caddr_t)
                                                           + eoff);
 #else
                                IP6_EXTHDR_GET(rth, struct ip6_rthdr *, m,
-                                       eoff, sizeof(*rth));
+                                              eoff, sizeof(*rth));
                                if (rth == NULL) {
                                        icmp6stat.icp6s_tooshort++;
-                                       return IPPROTO_DONE;
+                                       return(-1);
                                }
 #endif
                                rthlen = (rth->ip6r_len + 1) << 3;
@@ -888,7 +883,7 @@ icmp6_input(mp, offp, proto)
 
 #ifndef PULLDOWN_TEST
                                        IP6_EXTHDR_CHECK(m, 0, eoff + rthlen,
-                                                        IPPROTO_DONE);
+                                                        return -1);
                                        rth0 = (struct ip6_rthdr0 *)(mtod(m, caddr_t) + eoff);
 #else
                                        IP6_EXTHDR_GET(rth0,
@@ -896,7 +891,7 @@ icmp6_input(mp, offp, proto)
                                                       eoff, rthlen);
                                        if (rth0 == NULL) {
                                                icmp6stat.icp6s_tooshort++;
-                                               return IPPROTO_DONE;
+                                               return(-1);
                                        }
 #endif
                                        /* just ignore a bogus header */
@@ -911,15 +906,15 @@ icmp6_input(mp, offp, proto)
 #ifndef PULLDOWN_TEST
                                IP6_EXTHDR_CHECK(m, 0, eoff +
                                                 sizeof(struct ip6_frag),
-                                                IPPROTO_DONE);
+                                                return -1);
                                fh = (struct ip6_frag *)(mtod(m, caddr_t)
                                                         + eoff);
 #else
                                IP6_EXTHDR_GET(fh, struct ip6_frag *, m,
-                                       eoff, sizeof(*fh));
+                                              eoff, sizeof(*fh));
                                if (fh == NULL) {
                                        icmp6stat.icp6s_tooshort++;
-                                       return IPPROTO_DONE;
+                                       return(-1);
                                }
 #endif
                                /*
@@ -937,7 +932,7 @@ icmp6_input(mp, offp, proto)
                        default:
                                /*
                                 * This case includes ESP and the No Next
-                                * Header. In such cases going to the notify
+                                * Header.  In such cases going to the notify
                                 * label does not have any meaning
                                 * (i.e. ctlfunc will be NULL), but we go
                                 * anyway since we might have to update
@@ -946,95 +941,116 @@ icmp6_input(mp, offp, proto)
                                goto notify;
                        }
                }
-           notify:
+         notify:
 #ifndef PULLDOWN_TEST
                icmp6 = (struct icmp6_hdr *)(mtod(m, caddr_t) + off);
 #else
                IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off,
-                       sizeof(*icmp6) + sizeof(struct ip6_hdr));
+                              sizeof(*icmp6) + sizeof(struct ip6_hdr));
                if (icmp6 == NULL) {
                        icmp6stat.icp6s_tooshort++;
-                       return IPPROTO_DONE;
+                       return(-1);
+               }
+#endif
+
+               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)
+                       icmp6dst.sin6_addr = eip6->ip6_dst;
+               else
+                       icmp6dst.sin6_addr = *finaldst;
+               icmp6dst.sin6_scope_id = in6_addr2scopeid(m->m_pkthdr.rcvif,
+                                                         &icmp6dst.sin6_addr);
+#ifndef SCOPEDROUTING
+               if (in6_embedscope(&icmp6dst.sin6_addr, &icmp6dst,
+                                  NULL, NULL)) {
+                       /* should be impossbile */
+                       nd6log((LOG_DEBUG,
+                           "icmp6_notify_error: in6_embedscope failed\n"));
+                       goto freeit;
+               }
+#endif
+
+               /*
+                * retrieve parameters from the inner IPv6 header, and convert
+                * them into sockaddr structures.
+                */
+               bzero(&icmp6src, sizeof(icmp6src));
+               icmp6src.sin6_len = sizeof(struct sockaddr_in6);
+               icmp6src.sin6_family = AF_INET6;
+               icmp6src.sin6_addr = eip6->ip6_src;
+               icmp6src.sin6_scope_id = in6_addr2scopeid(m->m_pkthdr.rcvif,
+                                                         &icmp6src.sin6_addr);
+#ifndef SCOPEDROUTING
+               if (in6_embedscope(&icmp6src.sin6_addr, &icmp6src,
+                                  NULL, NULL)) {
+                       /* should be impossbile */
+                       nd6log((LOG_DEBUG,
+                           "icmp6_notify_error: in6_embedscope failed\n"));
+                       goto freeit;
                }
 #endif
+               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_src = &icmp6src;
+               ip6cp.ip6c_nxt = nxt;
+
                if (icmp6type == ICMP6_PACKET_TOO_BIG) {
-                       if (finaldst == NULL)
-                               finaldst = &((struct ip6_hdr *)(icmp6 + 1))->ip6_dst;
-                       icmp6_mtudisc_update(finaldst, icmp6, m);
+                       notifymtu = ntohl(icmp6->icmp6_mtu);
+                       ip6cp.ip6c_cmdarg = (void *)&notifymtu;
+                       icmp6_mtudisc_update(&ip6cp, 1);        /*XXX*/
                }
 
-               ctlfunc = (void (*) __P((int, struct sockaddr *, void *)))
+               ctlfunc = (void (*)(int, struct sockaddr *, void *))
                        (ip6_protox[nxt]->pr_ctlinput);
                if (ctlfunc) {
-                       ip6cp.ip6c_m = m;
-                       ip6cp.ip6c_ip6 = (struct ip6_hdr *)(icmp6 + 1);
-                       ip6cp.ip6c_off = eoff;
-                       (*ctlfunc)(code, (struct sockaddr *)&icmp6src, &ip6cp);
+                       (void) (*ctlfunc)(code, (struct sockaddr *)&icmp6dst,
+                                         &ip6cp);
                }
-           }
-               break;
-
-       badcode:
-               icmp6stat.icp6s_badcode++;
-               break;
-
-       badlen:
-               icmp6stat.icp6s_badlen++;
-               break;
        }
+       return(0);
 
-#if HAVE_NRL_INPCB
-       rip6_input(&m, offp, IPPROTO_ICMPV6);
-#else
-       icmp6_rip6_input(&m, *offp);
-#endif
-       return IPPROTO_DONE;
-
- freeit:
+  freeit:
        m_freem(m);
-       return IPPROTO_DONE;
+       return(-1);
 }
 
-static void
-icmp6_mtudisc_update(dst, icmp6, m)
-       struct in6_addr *dst;
-       struct icmp6_hdr *icmp6;/* we can assume the validity of the pointer */
-       struct mbuf *m; /* currently unused but added for scoped addrs */
+void
+icmp6_mtudisc_update(ip6cp, validated)
+       struct ip6ctlparam *ip6cp;
+       int validated;
 {
+       struct in6_addr *dst = ip6cp->ip6c_finaldst;
+       struct icmp6_hdr *icmp6 = ip6cp->ip6c_icmp6;
+       struct mbuf *m = ip6cp->ip6c_m; /* will be necessary for scope issue */
        u_int mtu = ntohl(icmp6->icmp6_mtu);
        struct rtentry *rt = NULL;
        struct sockaddr_in6 sin6;
-#ifdef __bsdi__
-       struct route_in6 ro6;
-#endif
+
+       if (!validated)
+               return;
 
        bzero(&sin6, sizeof(sin6));
        sin6.sin6_family = PF_INET6;
        sin6.sin6_len = sizeof(struct sockaddr_in6);
        sin6.sin6_addr = *dst;
-       /* sin6.sin6_scope_id = XXX: should be set if DST is a scoped addr */
-#if defined(__NetBSD__) || defined(__OpenBSD__)
-       rt = rtalloc1((struct sockaddr *)&sin6, 1);     /*clone*/
-       if (!rt || (rt->rt_flags & RTF_HOST) == 0) {
-               if (rt)
-                       RTFREE(rt);
-               rt = icmp6_mtudisc_clone((struct sockaddr *)&sin6);
+       /* XXX normally, this won't happen */
+       if (IN6_IS_ADDR_LINKLOCAL(dst)) {
+               sin6.sin6_addr.s6_addr16[1] =
+                   htons(m->m_pkthdr.rcvif->if_index);
        }
-#else
-#if defined(__FreeBSD__) || defined (__APPLE__)
-       rt = rtalloc1((struct sockaddr *)&sin6, 0,
-                     RTF_CLONING | RTF_PRCLONING);
-#else
-#ifdef __bsdi__
-       bcopy(&sin6, &ro6.ro_dst, sizeof(struct sockaddr_in6));
-       ro6.ro_rt = 0;
-       rtcalloc((struct route *)&ro6);
-       rt = ro6.ro_rt;
-#else
-#error no case for this particular operating system
-#endif
-#endif
-#endif
+       /* sin6.sin6_scope_id = XXX: should be set if DST is a scoped addr */
+       rt = rtalloc1((struct sockaddr *)&sin6, 0, RTF_CLONING | RTF_PRCLONING);
 
        if (rt && (rt->rt_flags & RTF_HOST)
            && !(rt->rt_rmx.rmx_locks & RTV_MTU)) {
@@ -1043,23 +1059,26 @@ icmp6_mtudisc_update(dst, icmp6, m)
                        rt->rt_rmx.rmx_locks |= RTV_MTU;
                } else if (mtu < rt->rt_ifp->if_mtu &&
                           rt->rt_rmx.rmx_mtu > mtu) {
+                       icmp6stat.icp6s_pmtuchg++;
                        rt->rt_rmx.rmx_mtu = mtu;
                }
        }
        if (rt)
-               RTFREE(rt);
+               rtfree(rt);
 }
 
 /*
- * Process a Node Information Query
+ * Process a Node Information Query packet, based on
+ * draft-ietf-ipngwg-icmp-name-lookups-07.
+ * 
+ * Spec incompatibilities:
+ * - IPv6 Subject address handling
+ * - IPv4 Subject address handling support missing
+ * - Proxy reply (answer even if it's not for me)
+ * - joins NI group address at in6_ifattach() time only, does not cope
+ *   with hostname changes by sethostname(3)
  */
-#if defined(__FreeBSD__) || defined (__APPLE__)
 #define hostnamelen    strlen(hostname)
-#endif
-#ifndef offsetof               /* XXX */
-#define        offsetof(type, member)  ((size_t)(&((type *)0)->member))
-#endif
-
 static struct mbuf *
 ni6_input(m, off)
        struct mbuf *m;
@@ -1068,11 +1087,19 @@ ni6_input(m, off)
        struct icmp6_nodeinfo *ni6, *nni6;
        struct mbuf *n = NULL;
        u_int16_t qtype;
+       int subjlen;
        int replylen = sizeof(struct ip6_hdr) + sizeof(struct icmp6_nodeinfo);
        struct ni_reply_fqdn *fqdn;
        int addrs;              /* for NI_QTYPE_NODEADDR */
        struct ifnet *ifp = NULL; /* for NI_QTYPE_NODEADDR */
+       struct sockaddr_in6 sin6; /* double meaning; ip6_dst and subjectaddr */
+       struct sockaddr_in6 sin6_d; /* XXX: we should retrieve this from m_aux */
+       struct ip6_hdr *ip6;
+       int oldfqdn = 0;        /* if 1, return pascal string (03 draft) */
+       char *subj = NULL;
+       struct in6_ifaddr *ia6 = NULL;
 
+       ip6 = mtod(m, struct ip6_hdr *);
 #ifndef PULLDOWN_TEST
        ni6 = (struct icmp6_nodeinfo *)(mtod(m, caddr_t) + off);
 #else
@@ -1082,54 +1109,212 @@ ni6_input(m, off)
                return NULL;
        }
 #endif
+
+       /*
+        * Validate IPv6 destination address.
+        *
+        * The Responder must discard the Query without further processing
+        * unless it is one of the Responder's unicast or anycast addresses, or
+        * a link-local scope multicast address which the Responder has joined.
+        * [icmp-name-lookups-07, Section 4.]
+        */
+       bzero(&sin6, sizeof(sin6));
+       sin6.sin6_family = AF_INET6;
+       sin6.sin6_len = sizeof(struct sockaddr_in6);
+       bcopy(&ip6->ip6_dst, &sin6.sin6_addr, sizeof(sin6.sin6_addr));
+       /* XXX scopeid */
+       if ((ia6 = (struct in6_ifaddr *)ifa_ifwithaddr((struct sockaddr *)&sin6)) != NULL) {
+               /* unicast/anycast, fine */
+               if ((ia6->ia6_flags & IN6_IFF_TEMPORARY) != 0 &&
+                   (icmp6_nodeinfo & 4) == 0) {
+                   ifafree(&ia6->ia_ifa);
+                   ia6 = NULL;
+                       nd6log((LOG_DEBUG, "ni6_input: ignore node info to "
+                               "a temporary address in %s:%d",
+                              __FILE__, __LINE__));
+                       goto bad;
+               }
+               ifafree(&ia6->ia_ifa);
+               ia6 = NULL;
+       } else if (IN6_IS_ADDR_MC_LINKLOCAL(&sin6.sin6_addr))
+               ; /* link-local multicast, fine */
+       else
+               goto bad;
+
+       /* validate query Subject field. */
        qtype = ntohs(ni6->ni_qtype);
+       subjlen = m->m_pkthdr.len - off - sizeof(struct icmp6_nodeinfo);
+       switch (qtype) {
+       case NI_QTYPE_NOOP:
+       case NI_QTYPE_SUPTYPES:
+               /* 07 draft */
+               if (ni6->ni_code == ICMP6_NI_SUBJ_FQDN && subjlen == 0)
+                       break;
+               /* FALLTHROUGH */
+       case NI_QTYPE_FQDN:
+       case NI_QTYPE_NODEADDR:
+               switch (ni6->ni_code) {
+               case ICMP6_NI_SUBJ_IPV6:
+#if ICMP6_NI_SUBJ_IPV6 != 0
+               case 0:
+#endif
+                       /*
+                        * backward compatibility - try to accept 03 draft
+                        * format, where no Subject is present.
+                        */
+                       if (qtype == NI_QTYPE_FQDN && ni6->ni_code == 0 &&
+                           subjlen == 0) {
+                               oldfqdn++;
+                               break;
+                       }
+#if ICMP6_NI_SUBJ_IPV6 != 0
+                       if (ni6->ni_code != ICMP6_NI_SUBJ_IPV6)
+                               goto bad;
+#endif
 
-       switch(qtype) {
-        case NI_QTYPE_NOOP:
-                break;         /* no reply data */
-        case NI_QTYPE_SUPTYPES:
-                goto bad;      /* xxx: to be implemented */
-                break;
-        case NI_QTYPE_FQDN:
-                replylen += offsetof(struct ni_reply_fqdn, ni_fqdn_name) +
-                        hostnamelen;
-                break;
-        case NI_QTYPE_NODEADDR:
-                addrs = ni6_addrs(ni6, m, &ifp);
-                if ((replylen += addrs * sizeof(struct in6_addr)) > MCLBYTES)
-                        replylen = MCLBYTES; /* XXX: we'll truncate later */
-               
-                break;
-        default:
-                /*
-                 * XXX: We must return a reply with the ICMP6 code
-                 * `unknown Qtype' in this case. However we regard the case
-                 * as an FQDN query for backward compatibility.
-                 * Older versions set a random value to this field,
-                 * so it rarely varies in the defined qtypes.
-                 * But the mechanism is not reliable...
-                 * maybe we should obsolete older versions.
-                 */
-                qtype = NI_QTYPE_FQDN;
-                replylen += offsetof(struct ni_reply_fqdn, ni_fqdn_name) +
-                        hostnamelen;
-                break;
-       }
-
-       /* allocate a mbuf to reply. */
-       MGETHDR(n, M_DONTWAIT, m->m_type);
+                       if (subjlen != sizeof(sin6.sin6_addr))
+                               goto bad;
+
+                       /*
+                        * Validate Subject address.
+                        *
+                        * Not sure what exactly "address belongs to the node"
+                        * means in the spec, is it just unicast, or what?
+                        *
+                        * At this moment we consider Subject address as
+                        * "belong to the node" if the Subject address equals
+                        * to the IPv6 destination address; validation for
+                        * IPv6 destination address should have done enough
+                        * check for us.
+                        *
+                        * We do not do proxy at this moment.
+                        */
+                       /* m_pulldown instead of copy? */
+                       m_copydata(m, off + sizeof(struct icmp6_nodeinfo),
+                           subjlen, (caddr_t)&sin6.sin6_addr);
+                       sin6.sin6_scope_id = in6_addr2scopeid(m->m_pkthdr.rcvif,
+                                                             &sin6.sin6_addr);
+#ifndef SCOPEDROUTING
+                       in6_embedscope(&sin6.sin6_addr, &sin6, NULL, NULL);
+#endif
+                       bzero(&sin6_d, sizeof(sin6_d));
+                       sin6_d.sin6_family = AF_INET6; /* not used, actually */
+                       sin6_d.sin6_len = sizeof(sin6_d); /* ditto */
+                       sin6_d.sin6_addr = ip6->ip6_dst;
+                       sin6_d.sin6_scope_id = in6_addr2scopeid(m->m_pkthdr.rcvif,
+                                                               &ip6->ip6_dst);
+#ifndef SCOPEDROUTING
+                       in6_embedscope(&sin6_d.sin6_addr, &sin6_d, NULL, NULL);
+#endif
+                       subj = (char *)&sin6;
+                       if (SA6_ARE_ADDR_EQUAL(&sin6, &sin6_d))
+                               break;
+
+                       /*
+                        * XXX if we are to allow other cases, we should really
+                        * be careful about scope here.
+                        * basically, we should disallow queries toward IPv6
+                        * destination X with subject Y, if scope(X) > scope(Y).
+                        * if we allow scope(X) > scope(Y), it will result in
+                        * information leakage across scope boundary.
+                        */
+                       goto bad;
+
+               case ICMP6_NI_SUBJ_FQDN:
+                       /*
+                        * Validate Subject name with gethostname(3).
+                        *
+                        * The behavior may need some debate, since:
+                        * - we are not sure if the node has FQDN as
+                        *   hostname (returned by gethostname(3)).
+                        * - the code does wildcard match for truncated names.
+                        *   however, we are not sure if we want to perform
+                        *   wildcard match, if gethostname(3) side has
+                        *   truncated hostname.
+                        */
+                       n = ni6_nametodns(hostname, hostnamelen, 0);
+                       if (!n || n->m_next || n->m_len == 0)
+                               goto bad;
+                       IP6_EXTHDR_GET(subj, char *, m,
+                           off + sizeof(struct icmp6_nodeinfo), subjlen);
+                       if (subj == NULL)
+                               goto bad;
+                       if (!ni6_dnsmatch(subj, subjlen, mtod(n, const char *),
+                                       n->m_len)) {
+                               goto bad;
+                       }
+                       m_freem(n);
+                       n = NULL;
+                       break;
+
+               case ICMP6_NI_SUBJ_IPV4:        /* XXX: to be implemented? */
+               default:
+                       goto bad;
+               }
+               break;
+       }
+
+       /* refuse based on configuration.  XXX ICMP6_NI_REFUSED? */
+       switch (qtype) {
+       case NI_QTYPE_FQDN:
+               if ((icmp6_nodeinfo & 1) == 0)
+                       goto bad;
+               break;
+       case NI_QTYPE_NODEADDR:
+               if ((icmp6_nodeinfo & 2) == 0)
+                       goto bad;
+               break;
+       }
+
+       /* guess reply length */
+       switch (qtype) {
+       case NI_QTYPE_NOOP:
+               break;          /* no reply data */
+       case NI_QTYPE_SUPTYPES:
+               replylen += sizeof(u_int32_t);
+               break;
+       case NI_QTYPE_FQDN:
+               /* XXX will append an mbuf */
+               replylen += offsetof(struct ni_reply_fqdn, ni_fqdn_namelen);
+               break;
+       case NI_QTYPE_NODEADDR:
+               addrs = ni6_addrs(ni6, &ifp, subj);
+               if ((replylen += addrs * (sizeof(struct in6_addr) +
+                                         sizeof(u_int32_t))) > MCLBYTES)
+                       replylen = MCLBYTES; /* XXX: will truncate pkt later */
+               break;
+       default:
+               /*
+                * XXX: We must return a reply with the ICMP6 code
+                * `unknown Qtype' in this case. However we regard the case
+                * as an FQDN query for backward compatibility.
+                * Older versions set a random value to this field,
+                * so it rarely varies in the defined qtypes.
+                * But the mechanism is not reliable...
+                * maybe we should obsolete older versions.
+                */
+               qtype = NI_QTYPE_FQDN;
+               /* XXX will append an mbuf */
+               replylen += offsetof(struct ni_reply_fqdn, ni_fqdn_namelen);
+               oldfqdn++;
+               break;
+       }
+
+       /* allocate an mbuf to reply. */
+       MGETHDR(n, M_DONTWAIT, m->m_type);      /* MAC-OK */
        if (n == NULL) {
                m_freem(m);
                return(NULL);
        }
        M_COPY_PKTHDR(n, m); /* just for recvif */
        if (replylen > MHLEN) {
-               if (replylen > MCLBYTES)
-                        /*
-                         * XXX: should we try to allocate more? But MCLBYTES is
-                         * probably much larger than IPV6_MMTU...
-                         */
+               if (replylen > MCLBYTES) {
+                       /*
+                        * XXX: should we try to allocate more? But MCLBYTES
+                        * is probably much larger than IPV6_MMTU...
+                        */
                        goto bad;
+               }
                MCLGET(n, M_DONTWAIT);
                if ((n->m_flags & M_EXT) == 0) {
                        goto bad;
@@ -1144,49 +1329,57 @@ ni6_input(m, off)
 
        /* qtype dependent procedure */
        switch (qtype) {
-        case NI_QTYPE_NOOP:
-                nni6->ni_flags = 0;
-                break;
-        case NI_QTYPE_SUPTYPES:
-                goto bad;      /* xxx: to be implemented */
-                break;
-        case NI_QTYPE_FQDN:
-                if (hostnamelen > 255) { /* XXX: rare case, but may happen */
-                        printf("ni6_input: "
-                               "hostname length(%d) is too large for reply\n",
-                               hostnamelen);
-                        goto bad;
-                }
-                fqdn = (struct ni_reply_fqdn *)(mtod(n, caddr_t) +
-                                                sizeof(struct ip6_hdr) +
-                                                sizeof(struct icmp6_nodeinfo));
-                nni6->ni_flags = 0; /* XXX: meaningless TTL */
-                fqdn->ni_fqdn_ttl = 0; /* ditto. */
-                fqdn->ni_fqdn_namelen = hostnamelen;
-                bcopy(hostname, &fqdn->ni_fqdn_name[0], hostnamelen);
-                break;
-        case NI_QTYPE_NODEADDR:
-        {
-                int lenlim, copied;
-
-                if (n->m_flags & M_EXT)
-                        lenlim = MCLBYTES - sizeof(struct ip6_hdr) -
-                                sizeof(struct icmp6_nodeinfo);
-                else
-                        lenlim = MHLEN - sizeof(struct ip6_hdr) -
-                                sizeof(struct icmp6_nodeinfo);
-                copied = ni6_store_addrs(ni6, nni6, ifp, lenlim);
-                /* XXX: reset mbuf length */
-                n->m_pkthdr.len = n->m_len = sizeof(struct ip6_hdr) +
-                        sizeof(struct icmp6_nodeinfo) + copied;
-                break;
-        }
-        default:
-                break;         /* XXX impossible! */
+       case NI_QTYPE_NOOP:
+               nni6->ni_code = ICMP6_NI_SUCCESS;
+               nni6->ni_flags = 0;
+               break;
+       case NI_QTYPE_SUPTYPES:
+       {
+               u_int32_t v;
+               nni6->ni_code = ICMP6_NI_SUCCESS;
+               nni6->ni_flags = htons(0x0000); /* raw bitmap */
+               /* supports NOOP, SUPTYPES, FQDN, and NODEADDR */
+               v = (u_int32_t)htonl(0x0000000f);
+               bcopy(&v, nni6 + 1, sizeof(u_int32_t));
+               break;
+       }
+       case NI_QTYPE_FQDN:
+               nni6->ni_code = ICMP6_NI_SUCCESS;
+               fqdn = (struct ni_reply_fqdn *)(mtod(n, caddr_t) +
+                                               sizeof(struct ip6_hdr) +
+                                               sizeof(struct icmp6_nodeinfo));
+               nni6->ni_flags = 0; /* XXX: meaningless TTL */
+               fqdn->ni_fqdn_ttl = 0;  /* ditto. */
+               /*
+                * XXX do we really have FQDN in variable "hostname"?
+                */
+               n->m_next = ni6_nametodns(hostname, hostnamelen, oldfqdn);
+               if (n->m_next == NULL)
+                       goto bad;
+               /* XXX we assume that n->m_next is not a chain */
+               if (n->m_next->m_next != NULL)
+                       goto bad;
+               n->m_pkthdr.len += n->m_next->m_len;
+               break;
+       case NI_QTYPE_NODEADDR:
+       {
+               int lenlim, copied;
+
+               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);
+               copied = ni6_store_addrs(ni6, nni6, ifp, lenlim);
+               /* XXX: reset mbuf length */
+               n->m_pkthdr.len = n->m_len = sizeof(struct ip6_hdr) +
+                       sizeof(struct icmp6_nodeinfo) + copied;
+               break;
+       }
+       default:
+               break;          /* XXX impossible! */
        }
 
        nni6->ni_type = ICMP6_NI_REPLY;
-       nni6->ni_code = ICMP6_NI_SUCESS;
        m_freem(m);
        return(n);
 
@@ -1198,41 +1391,217 @@ ni6_input(m, off)
 }
 #undef hostnamelen
 
+/*
+ * make a mbuf with DNS-encoded string.  no compression support.
+ *
+ * XXX names with less than 2 dots (like "foo" or "foo.section") will be
+ * treated as truncated name (two \0 at the end).  this is a wild guess.
+ */
+static struct mbuf *
+ni6_nametodns(name, namelen, old)
+       const char *name;
+       int namelen;
+       int old;        /* return pascal string if non-zero */
+{
+       struct mbuf *m;
+       char *cp, *ep;
+       const char *p, *q;
+       int i, len, nterm;
+
+       if (old)
+               len = namelen + 1;
+       else
+               len = MCLBYTES;
+
+       /* because MAXHOSTNAMELEN is usually 256, we use cluster mbuf */
+       MGET(m, M_DONTWAIT, MT_DATA);
+       if (m && len > MLEN) {
+               MCLGET(m, M_DONTWAIT);
+               if ((m->m_flags & M_EXT) == 0)
+                       goto fail;
+       }
+       if (!m)
+               goto fail;
+       m->m_next = NULL;
+
+       if (old) {
+               m->m_len = len;
+               *mtod(m, char *) = namelen;
+               bcopy(name, mtod(m, char *) + 1, namelen);
+               return m;
+       } else {
+               m->m_len = 0;
+               cp = mtod(m, char *);
+               ep = mtod(m, char *) + M_TRAILINGSPACE(m);
+
+               /* if not certain about my name, return empty buffer */
+               if (namelen == 0)
+                       return m;
+
+               /*
+                * guess if it looks like shortened hostname, or FQDN.
+                * shortened hostname needs two trailing "\0".
+                */
+               i = 0;
+               for (p = name; p < name + namelen; p++) {
+                       if (*p && *p == '.')
+                               i++;
+               }
+               if (i < 2)
+                       nterm = 2;
+               else
+                       nterm = 1;
+
+               p = name;
+               while (cp < ep && p < name + namelen) {
+                       i = 0;
+                       for (q = p; q < name + namelen && *q && *q != '.'; q++)
+                               i++;
+                       /* result does not fit into mbuf */
+                       if (cp + i + 1 >= ep)
+                               goto fail;
+                       /*
+                        * DNS label length restriction, RFC1035 page 8.
+                        * "i == 0" case is included here to avoid returning
+                        * 0-length label on "foo..bar".
+                        */
+                       if (i <= 0 || i >= 64)
+                               goto fail;
+                       *cp++ = i;
+                       bcopy(p, cp, i);
+                       cp += i;
+                       p = q;
+                       if (p < name + namelen && *p == '.')
+                               p++;
+               }
+               /* termination */
+               if (cp + nterm >= ep)
+                       goto fail;
+               while (nterm-- > 0)
+                       *cp++ = '\0';
+               m->m_len = cp - mtod(m, char *);
+               return m;
+       }
+
+       panic("should not reach here");
+       /* NOTREACHED */
+
+ fail:
+       if (m)
+               m_freem(m);
+       return NULL;
+}
+
+/*
+ * check if two DNS-encoded string matches.  takes care of truncated
+ * form (with \0\0 at the end).  no compression support.
+ * XXX upper/lowercase match (see RFC2065)
+ */
+static int
+ni6_dnsmatch(a, alen, b, blen)
+       const char *a;
+       int alen;
+       const char *b;
+       int blen;
+{
+       const char *a0, *b0;
+       int l;
+
+       /* simplest case - need validation? */
+       if (alen == blen && bcmp(a, b, alen) == 0)
+               return 1;
+
+       a0 = a;
+       b0 = b;
+
+       /* termination is mandatory */
+       if (alen < 2 || blen < 2)
+               return 0;
+       if (a0[alen - 1] != '\0' || b0[blen - 1] != '\0')
+               return 0;
+       alen--;
+       blen--;
+
+       while (a - a0 < alen && b - b0 < blen) {
+               if (a - a0 + 1 > alen || b - b0 + 1 > blen)
+                       return 0;
+
+               if ((signed char)a[0] < 0 || (signed char)b[0] < 0)
+                       return 0;
+               /* we don't support compression yet */
+               if (a[0] >= 64 || b[0] >= 64)
+                       return 0;
+
+               /* truncated case */
+               if (a[0] == 0 && a - a0 == alen - 1)
+                       return 1;
+               if (b[0] == 0 && b - b0 == blen - 1)
+                       return 1;
+               if (a[0] == 0 || b[0] == 0)
+                       return 0;
+
+               if (a[0] != b[0])
+                       return 0;
+               l = a[0];
+               if (a - a0 + 1 + l > alen || b - b0 + 1 + l > blen)
+                       return 0;
+               if (bcmp(a + 1, b + 1, l) != 0)
+                       return 0;
+
+               a += 1 + l;
+               b += 1 + l;
+       }
+
+       if (a - a0 == alen && b - b0 == blen)
+               return 1;
+       else
+               return 0;
+}
+
 /*
  * calculate the number of addresses to be returned in the node info reply.
  */
 static int
-ni6_addrs(ni6, m, ifpp)
+ni6_addrs(ni6, ifpp, subj)
        struct icmp6_nodeinfo *ni6;
-       struct mbuf *m;
        struct ifnet **ifpp;
+       char *subj;
 {
-       register struct ifnet *ifp;
-       register struct in6_ifaddr *ifa6;
-       register struct ifaddr *ifa;
-       struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
+       struct ifnet *ifp;
+       struct in6_ifaddr *ifa6;
+       struct ifaddr *ifa;
+       struct sockaddr_in6 *subj_ip6 = NULL; /* XXX pedant */
        int addrs = 0, addrsofif, iffound = 0;
-
-#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
-       for (ifp = ifnet; ifp; ifp = ifp->if_next)
-#else
-       for (ifp = TAILQ_FIRST(&ifnet); ifp; ifp = TAILQ_NEXT(ifp, if_list))
-#endif
-       {
+       int niflags = ni6->ni_flags;
+
+       if ((niflags & NI_NODEADDR_FLAG_ALL) == 0) {
+               switch (ni6->ni_code) {
+               case ICMP6_NI_SUBJ_IPV6:
+                       if (subj == NULL) /* must be impossible... */
+                               return(0);
+                       subj_ip6 = (struct sockaddr_in6 *)subj;
+                       break;
+               default:
+                       /*
+                        * XXX: we only support IPv6 subject address for
+                        * this Qtype.
+                        */
+                       return(0);
+               }
+       }
+
+       ifnet_head_lock_shared();
+       TAILQ_FOREACH(ifp, &ifnet_head, if_list) {
                addrsofif = 0;
-#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
-               for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
-#else
-               for (ifa = ifp->if_addrlist.tqh_first; ifa;
-                    ifa = ifa->ifa_list.tqe_next)
-#endif
+               ifnet_lock_shared(ifp);
+               TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list)
                {
                        if (ifa->ifa_addr->sa_family != AF_INET6)
                                continue;
                        ifa6 = (struct in6_ifaddr *)ifa;
 
-                       if (!(ni6->ni_flags & NI_NODEADDR_FLAG_ALL) &&
-                           IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst,
+                       if ((niflags & NI_NODEADDR_FLAG_ALL) == 0 &&
+                           IN6_ARE_ADDR_EQUAL(&subj_ip6->sin6_addr,
                                               &ifa6->ia_addr.sin6_addr))
                                iffound = 1;
 
@@ -1241,44 +1610,52 @@ ni6_addrs(ni6, m, ifpp)
                         * Node Information proxy, since they represent
                         * addresses of IPv4-only nodes, which perforce do
                         * not implement this protocol.
-                        * [icmp-name-lookups-05]
+                        * [icmp-name-lookups-07, Section 5.4]
                         * So we don't support NI_NODEADDR_FLAG_COMPAT in
                         * this function at this moment.
                         */
 
-                       if (ifa6->ia6_flags & IN6_IFF_ANYCAST)
-                               continue; /* we need only unicast addresses */
-
-                       if ((ni6->ni_flags & (NI_NODEADDR_FLAG_LINKLOCAL |
-                                             NI_NODEADDR_FLAG_SITELOCAL |
-                                             NI_NODEADDR_FLAG_GLOBAL)) == 0)
-                               continue;
-
                        /* What do we have to do about ::1? */
-                       switch(in6_addrscope(&ifa6->ia_addr.sin6_addr)) {
-                        case IPV6_ADDR_SCOPE_LINKLOCAL:
-                               if (ni6->ni_flags & NI_NODEADDR_FLAG_LINKLOCAL)
-                                       addrsofif++;
+                       switch (in6_addrscope(&ifa6->ia_addr.sin6_addr)) {
+                       case IPV6_ADDR_SCOPE_LINKLOCAL:
+                               if ((niflags & NI_NODEADDR_FLAG_LINKLOCAL) == 0)
+                                       continue;
+                               break;
+                       case IPV6_ADDR_SCOPE_SITELOCAL:
+                               if ((niflags & NI_NODEADDR_FLAG_SITELOCAL) == 0)
+                                       continue;
                                break;
-                        case IPV6_ADDR_SCOPE_SITELOCAL:
-                               if (ni6->ni_flags & NI_NODEADDR_FLAG_SITELOCAL)
-                                       addrsofif++;
+                       case IPV6_ADDR_SCOPE_GLOBAL:
+                               if ((niflags & NI_NODEADDR_FLAG_GLOBAL) == 0)
+                                       continue;
                                break;
-                        case IPV6_ADDR_SCOPE_GLOBAL:
-                                if (ni6->ni_flags & NI_NODEADDR_FLAG_GLOBAL)
-                                        addrsofif++;
-                                break;
-                        default:
-                                continue;
+                       default:
+                               continue;
                        }
+
+                       /*
+                        * check if anycast is okay.
+                        * XXX: just experimental.  not in the spec.
+                        */
+                       if ((ifa6->ia6_flags & IN6_IFF_ANYCAST) != 0 &&
+                           (niflags & NI_NODEADDR_FLAG_ANYCAST) == 0)
+                               continue; /* we need only unicast addresses */
+                       if ((ifa6->ia6_flags & IN6_IFF_TEMPORARY) != 0 &&
+                           (icmp6_nodeinfo & 4) == 0) {
+                               continue;
+                       }
+                       addrsofif++; /* count the address */
                }
+               ifnet_lock_done(ifp);
                if (iffound) {
                        *ifpp = ifp;
+                       ifnet_head_done();
                        return(addrsofif);
                }
 
                addrs += addrsofif;
        }
+       ifnet_head_done();
 
        return(addrs);
 }
@@ -1289,98 +1666,152 @@ ni6_store_addrs(ni6, nni6, ifp0, resid)
        struct ifnet *ifp0;
        int resid;
 {
-#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
-       register struct ifnet *ifp = ifp0 ? ifp0 : ifnet;
-#else
-       register struct ifnet *ifp = ifp0 ? ifp0 : TAILQ_FIRST(&ifnet);
-#endif
-       register struct in6_ifaddr *ifa6;
-       register struct ifaddr *ifa;
-       int docopy, copied = 0;
+       struct ifnet *ifp = ifp0;
+       struct in6_ifaddr *ifa6;
+       struct ifaddr *ifa;
+       struct ifnet *ifp_dep = NULL;
+       int copied = 0, allow_deprecated = 0;
        u_char *cp = (u_char *)(nni6 + 1);
+       int niflags = ni6->ni_flags;
+       u_int32_t ltime;
+       struct timeval timenow;
 
-       if (ifp0 == NULL && !(ni6->ni_flags & NI_NODEADDR_FLAG_ALL))
+       getmicrotime(&timenow);
+
+       if (ifp0 == NULL && !(niflags & NI_NODEADDR_FLAG_ALL))
                return(0);      /* needless to copy */
 
-#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
-       for (; ifp; ifp = ifp->if_next)
-#else
-       for (; ifp; ifp = TAILQ_NEXT(ifp, if_list))
-#endif
-       {
-#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
-               for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
-#else
+  again:
+
+       ifnet_head_lock_shared();
+       if (ifp == NULL) ifp = TAILQ_FIRST(&ifnet_head);
+       
+       for (; ifp; ifp = TAILQ_NEXT(ifp, if_list)) {
+               ifnet_lock_shared(ifp);
                for (ifa = ifp->if_addrlist.tqh_first; ifa;
                     ifa = ifa->ifa_list.tqe_next)
-#endif
                {
-                       docopy = 0;
-
                        if (ifa->ifa_addr->sa_family != AF_INET6)
                                continue;
                        ifa6 = (struct in6_ifaddr *)ifa;
 
-                       if (ifa6->ia6_flags & IN6_IFF_ANYCAST) {
-                               /* just experimental. not in the spec. */
-                               if (ni6->ni_flags & NI_NODEADDR_FLAG_ANYCAST)
-                                       docopy = 1;
-                               else
-                                       continue;
-                       }
-                       else {  /* unicast address */
-                               if (ni6->ni_flags & NI_NODEADDR_FLAG_ANYCAST)
-                                       continue;
-                               else
-                                       docopy = 1;
+                       if ((ifa6->ia6_flags & IN6_IFF_DEPRECATED) != 0 &&
+                           allow_deprecated == 0) {
+                               /*
+                                * prefererred address should be put before
+                                * deprecated addresses.
+                                */
+
+                               /* record the interface for later search */
+                               if (ifp_dep == NULL)
+                                       ifp_dep = ifp;
+
+                               continue;
                        }
+                       else if ((ifa6->ia6_flags & IN6_IFF_DEPRECATED) == 0 &&
+                                allow_deprecated != 0)
+                               continue; /* we now collect deprecated addrs */
 
                        /* What do we have to do about ::1? */
-                       switch(in6_addrscope(&ifa6->ia_addr.sin6_addr)) {
-                        case IPV6_ADDR_SCOPE_LINKLOCAL:
-                               if (ni6->ni_flags & NI_NODEADDR_FLAG_LINKLOCAL)
-                                       docopy = 1;
+                       switch (in6_addrscope(&ifa6->ia_addr.sin6_addr)) {
+                       case IPV6_ADDR_SCOPE_LINKLOCAL:
+                               if ((niflags & NI_NODEADDR_FLAG_LINKLOCAL) == 0)
+                                       continue;
                                break;
-                        case IPV6_ADDR_SCOPE_SITELOCAL:
-                               if (ni6->ni_flags & NI_NODEADDR_FLAG_SITELOCAL)
-                                       docopy = 1;
+                       case IPV6_ADDR_SCOPE_SITELOCAL:
+                               if ((niflags & NI_NODEADDR_FLAG_SITELOCAL) == 0)
+                                       continue;
+                               break;
+                       case IPV6_ADDR_SCOPE_GLOBAL:
+                               if ((niflags & NI_NODEADDR_FLAG_GLOBAL) == 0)
+                                       continue;
                                break;
-                        case IPV6_ADDR_SCOPE_GLOBAL:
-                                if (ni6->ni_flags & NI_NODEADDR_FLAG_GLOBAL)
-                                        docopy = 1;
-                                break;
-                        default:
-                                continue;
+                       default:
+                               continue;
                        }
 
-                       if (docopy) {
-                               if (resid < sizeof(struct in6_addr)) {
-                                       /*
-                                        * We give up much more copy.
-                                        * Set the truncate flag and return.
-                                        */
-                                       nni6->ni_flags |=
-                                               NI_NODEADDR_FLAG_TRUNCATE;
-                                       return(copied);
-                               }
-                               bcopy(&ifa6->ia_addr.sin6_addr, cp,
-                                     sizeof(struct in6_addr));
-                               /* XXX: KAME link-local hack; remove ifindex */
-                               if (IN6_IS_ADDR_LINKLOCAL(&ifa6->ia_addr.sin6_addr))
-                                       ((struct in6_addr *)cp)->s6_addr16[1] = 0;
-                               cp += sizeof(struct in6_addr);
-                               resid -= sizeof(struct in6_addr);
-                               copied += sizeof(struct in6_addr);
+                       /*
+                        * check if anycast is okay.
+                        * XXX: just experimental. not in the spec.
+                        */
+                       if ((ifa6->ia6_flags & IN6_IFF_ANYCAST) != 0 &&
+                           (niflags & NI_NODEADDR_FLAG_ANYCAST) == 0)
+                               continue;
+                       if ((ifa6->ia6_flags & IN6_IFF_TEMPORARY) != 0 &&
+                           (icmp6_nodeinfo & 4) == 0) {
+                               continue;
+                       }
+
+                       /* now we can copy the address */
+                       if (resid < sizeof(struct in6_addr) +
+                           sizeof(u_int32_t)) {
+                               /*
+                                * We give up much more copy.
+                                * Set the truncate flag and return.
+                                */
+                               nni6->ni_flags |=
+                                       NI_NODEADDR_FLAG_TRUNCATE;
+                               ifnet_lock_done(ifp);
+                               ifnet_head_done();
+                               return(copied);
+                       }
+
+                       /*
+                        * Set the TTL of the address.
+                        * The TTL value should be one of the following
+                        * according to the specification:
+                        *
+                        * 1. The remaining lifetime of a DHCP lease on the
+                        *    address, or
+                        * 2. The remaining Valid Lifetime of a prefix from
+                        *    which the address was derived through Stateless
+                        *    Autoconfiguration.
+                        *
+                        * Note that we currently do not support stateful
+                        * address configuration by DHCPv6, so the former
+                        * case can't happen.
+                        */
+                       if (ifa6->ia6_lifetime.ia6t_expire == 0)
+                               ltime = ND6_INFINITE_LIFETIME;
+                       else {
+                               if (ifa6->ia6_lifetime.ia6t_expire >
+                                   timenow.tv_sec)
+                                       ltime = htonl(ifa6->ia6_lifetime.ia6t_expire - timenow.tv_sec);
+                               else
+                                       ltime = 0;
                        }
+                       
+                       bcopy(&ltime, cp, sizeof(u_int32_t));
+                       cp += sizeof(u_int32_t);
+
+                       /* copy the address itself */
+                       bcopy(&ifa6->ia_addr.sin6_addr, cp,
+                             sizeof(struct in6_addr));
+                       /* XXX: KAME link-local hack; remove ifindex */
+                       if (IN6_IS_ADDR_LINKLOCAL(&ifa6->ia_addr.sin6_addr))
+                               ((struct in6_addr *)cp)->s6_addr16[1] = 0;
+                       cp += sizeof(struct in6_addr);
+                       
+                       resid -= (sizeof(struct in6_addr) + sizeof(u_int32_t));
+                       copied += (sizeof(struct in6_addr) +
+                                  sizeof(u_int32_t));
                }
+               ifnet_lock_done(ifp);
                if (ifp0)       /* we need search only on the specified IF */
                        break;
        }
+       ifnet_head_done();
+
+       if (allow_deprecated == 0 && ifp_dep != NULL) {
+               ifp = ifp_dep;
+               allow_deprecated = 1;
+
+               goto again;
+       }
 
        return(copied);
 }
 
-#ifndef HAVE_NRL_INPCB
 /*
  * XXX almost dup'ed code with rip6_input.
  */
@@ -1390,12 +1821,12 @@ icmp6_rip6_input(mp, off)
        int     off;
 {
        struct mbuf *m = *mp;
-       register struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
-       register struct in6pcb *in6p;
+       struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
+       struct in6pcb *in6p;
        struct in6pcb *last = NULL;
        struct sockaddr_in6 rip6src;
        struct icmp6_hdr *icmp6;
-       struct ip6_recvpktopts opts;
+       struct mbuf *opts = NULL;
 
 #ifndef PULLDOWN_TEST
        /* this is assumed to be safe. */
@@ -1408,30 +1839,19 @@ icmp6_rip6_input(mp, off)
        }
 #endif
 
-       bzero(&opts, sizeof(opts));
        bzero(&rip6src, sizeof(rip6src));
        rip6src.sin6_len = sizeof(struct sockaddr_in6);
        rip6src.sin6_family = AF_INET6;
-       rip6src.sin6_addr = ip6->ip6_src;
-       if (IN6_IS_SCOPE_LINKLOCAL(&rip6src.sin6_addr))
-               rip6src.sin6_addr.s6_addr16[1] = 0;
-       if (m->m_pkthdr.rcvif) {
-               if (IN6_IS_SCOPE_LINKLOCAL(&rip6src.sin6_addr))
-                       rip6src.sin6_scope_id = m->m_pkthdr.rcvif->if_index;
-               else
-                       rip6src.sin6_scope_id = 0;
-       } else
-               rip6src.sin6_scope_id = 0;
+       /* KAME hack: recover scopeid */
+       (void)in6_recoverscope(&rip6src, &ip6->ip6_src, m->m_pkthdr.rcvif);
 
-#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__)
+       lck_rw_lock_shared(ripcbinfo.mtx);
        LIST_FOREACH(in6p, &ripcb, inp_list)
-#else
-       for (in6p = rawin6pcb.in6p_next;
-            in6p != &rawin6pcb; in6p = in6p->in6p_next)
-#endif
        {
-#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__)
-               if ((in6p->inp_vflag & INP_IPV6) == NULL)
+               if ((in6p->inp_vflag & INP_IPV6) == 0)
+                       continue;
+#if HAVE_NRL_INPCB
+               if (!(in6p->in6p_flags & INP_IPV6))
                        continue;
 #endif
                if (in6p->in6p_ip6_nxt != IPPROTO_ICMPV6)
@@ -1450,45 +1870,35 @@ icmp6_rip6_input(mp, off)
                        struct  mbuf *n;
                        if ((n = m_copy(m, 0, (int)M_COPYALL)) != NULL) {
                                if (last->in6p_flags & IN6P_CONTROLOPTS)
-                                       ip6_savecontrol(last, ip6, n, &opts,
-                                                       NULL);
+                                       ip6_savecontrol(last, &opts, ip6, n);
                                /* strip intermediate headers */
                                m_adj(n, off);
                                if (sbappendaddr(&last->in6p_socket->so_rcv,
                                                 (struct sockaddr *)&rip6src,
-                                                n, opts.head) == 0) {
-                                       /* should notify about lost packet */
-                                       m_freem(n);
-                                       if (opts.head) {
-                                               m_freem(opts.head);
-                                       }
-                               } else
+                                                n, opts, NULL) != 0) {
                                        sorwakeup(last->in6p_socket);
-                               bzero(&opts, sizeof(opts));
+                               }
+                               opts = NULL;
                        }
                }
                last = in6p;
        }
+       lck_rw_done(ripcbinfo.mtx);
        if (last) {
                if (last->in6p_flags & IN6P_CONTROLOPTS)
-                       ip6_savecontrol(last, ip6, m, &opts, NULL);
+                       ip6_savecontrol(last, &opts, ip6, m);
                /* strip intermediate headers */
                m_adj(m, off);
                if (sbappendaddr(&last->in6p_socket->so_rcv,
-                                (struct sockaddr *)&rip6src,
-                                m, opts.head) == 0) {
-                       m_freem(m);
-                       if (opts.head)
-                               m_freem(opts.head);
-               } else
+                                (struct sockaddr *)&rip6src, m, opts, NULL) != 0) {
                        sorwakeup(last->in6p_socket);
+               }
        } else {
                m_freem(m);
                ip6stat.ip6s_delivered--;
        }
        return IPPROTO_DONE;
 }
-#endif /*OpenBSD*/
 
 /*
  * Reflect the ip6 packet back to the source.
@@ -1502,10 +1912,11 @@ icmp6_reflect(m, off)
        struct ip6_hdr *ip6;
        struct icmp6_hdr *icmp6;
        struct in6_ifaddr *ia;
-       struct in6_addr t, *src = 0;
+       struct in6_addr t, src_storage, *src = 0;
        int plen;
        int type, code;
        struct ifnet *outif = NULL;
+       struct sockaddr_in6 sa6_src, sa6_dst;
 #ifdef COMPAT_RFC1885
        int mtu = IPV6_MMTU;
        struct sockaddr_in6 *sin6 = &icmp6_reflect_rt.ro_dst;
@@ -1513,9 +1924,10 @@ icmp6_reflect(m, off)
 
        /* too short to reflect */
        if (off < sizeof(struct ip6_hdr)) {
-               printf("sanity fail: off=%lx, sizeof(ip6)=%lx in %s:%d\n",
-                      (u_long)off, (u_long)sizeof(struct ip6_hdr),
-                      __FILE__, __LINE__);
+               nd6log((LOG_DEBUG,
+                   "sanity fail: off=%lx, sizeof(ip6)=%lx in %s:%d\n",
+                   (u_long)off, (u_long)sizeof(struct ip6_hdr),
+                   __FILE__, __LINE__));
                goto bad;
        }
 
@@ -1523,6 +1935,10 @@ icmp6_reflect(m, off)
         * If there are extra headers between IPv6 and ICMPv6, strip
         * off that header first.
         */
+#if DIAGNOSTIC
+       if (sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr) > MHLEN)
+               panic("assumption failed in icmp6_reflect");
+#endif
        if (off > sizeof(struct ip6_hdr)) {
                size_t l;
                struct ip6_hdr nip6;
@@ -1558,12 +1974,24 @@ icmp6_reflect(m, off)
         */
        ip6->ip6_dst = ip6->ip6_src;
 
-       /* XXX hack for link-local addresses */
-       if (IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst))
-               ip6->ip6_dst.s6_addr16[1] =
-                       htons(m->m_pkthdr.rcvif->if_index);
-       if (IN6_IS_ADDR_LINKLOCAL(&t))
-               t.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index);
+       /*
+        * XXX: make sure to embed scope zone information, using
+        * already embedded IDs or the received interface (if any).
+        * Note that rcvif may be NULL.
+        * TODO: scoped routing case (XXX).
+        */
+       bzero(&sa6_src, sizeof(sa6_src));
+       sa6_src.sin6_family = AF_INET6;
+       sa6_src.sin6_len = sizeof(sa6_src);
+       sa6_src.sin6_addr = ip6->ip6_dst;
+       in6_recoverscope(&sa6_src, &ip6->ip6_dst, m->m_pkthdr.rcvif);
+       in6_embedscope(&ip6->ip6_dst, &sa6_src, NULL, NULL);
+       bzero(&sa6_dst, sizeof(sa6_dst));
+       sa6_dst.sin6_family = AF_INET6;
+       sa6_dst.sin6_len = sizeof(sa6_dst);
+       sa6_dst.sin6_addr = t;
+       in6_recoverscope(&sa6_dst, &t, m->m_pkthdr.rcvif);
+       in6_embedscope(&t, &sa6_dst, NULL, NULL);
 
 #ifdef COMPAT_RFC1885
        /*
@@ -1575,12 +2003,7 @@ icmp6_reflect(m, off)
        if (icmp6_reflect_rt.ro_rt == 0 ||
            ! (IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, &ip6->ip6_dst))) {
                if (icmp6_reflect_rt.ro_rt) {
-#if defined(__FreeBSD__) || defined(__APPLE__)
-                       RTFREE(icmp6_reflect_rt.ro_rt);
-#endif
-#ifdef __bsdi__
                        rtfree(icmp6_reflect_rt.ro_rt);
-#endif
                        icmp6_reflect_rt.ro_rt = 0;
                }
                bzero(sin6, sizeof(*sin6));
@@ -1588,12 +2011,8 @@ icmp6_reflect(m, off)
                sin6->sin6_len = sizeof(struct sockaddr_in6);
                sin6->sin6_addr = ip6->ip6_dst;
 
-#if defined(__FreeBSD__) || defined(__APPLE__)
                rtalloc_ign((struct route *)&icmp6_reflect_rt.ro_rt,
                            RTF_PRCLONING);
-#else
-               rtalloc((struct route *)&icmp6_reflect_rt.ro_rt);
-#endif
        }
 
        if (icmp6_reflect_rt.ro_rt == 0)
@@ -1611,37 +2030,47 @@ icmp6_reflect(m, off)
        /*
         * If the incoming packet was addressed directly to us(i.e. unicast),
         * use dst as the src for the reply.
-        * The IN6_IFF_NOTREADY case would be VERY rare, but is possible when
+        * The IN6_IFF_NOTREADY case would be VERY rare, but is possible
         * (for example) when we encounter an error while forwarding procedure
         * destined to a duplicated address of ours.
         */
-       for (ia = in6_ifaddr; ia; ia = ia->ia_next)
+       lck_mtx_lock(nd6_mutex);
+       for (ia = in6_ifaddrs; ia; ia = ia->ia_next)
                if (IN6_ARE_ADDR_EQUAL(&t, &ia->ia_addr.sin6_addr) &&
                    (ia->ia6_flags & (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY)) == 0) {
                        src = &t;
                        break;
                }
+       lck_mtx_unlock(nd6_mutex);
        if (ia == NULL && IN6_IS_ADDR_LINKLOCAL(&t) && (m->m_flags & M_LOOP)) {
                /*
                 * This is the case if the dst is our link-local address
-                * and the sender is also ourseleves.
+                * and the sender is also ourselves.
                 */
                src = &t;
        }
 
-       if (src == 0)
+       if (src == 0) {
+               int e;
+               struct route_in6 ro;
+
                /*
                 * This case matches to multicasts, our anycast, or unicasts
-                * that we do not own. Select a source address which has the
-                * same scope.
-                * XXX: for (non link-local) multicast addresses, this might
-                * not be a good choice.
+                * that we do not own.  Select a source address based on the
+                * source address of the erroneous packet.
                 */
-               if ((ia = in6_ifawithscope(m->m_pkthdr.rcvif, &t)) != 0)
-                       src = &IA6_SIN6(ia)->sin6_addr;
-
-       if (src == 0)
-               goto bad;
+               bzero(&ro, sizeof(ro));
+               src = in6_selectsrc(&sa6_src, NULL, NULL, &ro, NULL, &src_storage, &e);
+               if (ro.ro_rt)
+                       rtfree(ro.ro_rt); /* XXX: we could use this */
+               if (src == NULL) {
+                       nd6log((LOG_DEBUG,
+                           "icmp6_reflect: source can't be determined: "
+                           "dst=%s, error=%d\n",
+                           ip6_sprintf(&sa6_src.sin6_addr), e));
+                       goto bad;
+               }
+       }
 
        ip6->ip6_src = *src;
 
@@ -1652,26 +2081,28 @@ icmp6_reflect(m, off)
        if (m->m_pkthdr.rcvif) {
                /* XXX: This may not be the outgoing interface */
                ip6->ip6_hlim = nd_ifinfo[m->m_pkthdr.rcvif->if_index].chlim;
-       }
+       } else
+               ip6->ip6_hlim = ip6_defhlim;
 
        icmp6->icmp6_cksum = 0;
        icmp6->icmp6_cksum = in6_cksum(m, IPPROTO_ICMPV6,
                                        sizeof(struct ip6_hdr), plen);
 
        /*
-        * xxx option handling
+        * XXX option handling
         */
 
        m->m_flags &= ~(M_BCAST|M_MCAST);
 #if IPSEC
        /* Don't lookup socket */
-       ipsec_setsocket(m, NULL);
+       if (ipsec_bypass == 0)
+               (void)ipsec_setsocket(m, NULL);
 #endif /*IPSEC*/
 
 #ifdef COMPAT_RFC1885
-       ip6_output(m, NULL, &icmp6_reflect_rt, 0, NULL, &outif);
+       ip6_output(m, NULL, &icmp6_reflect_rt, 0, NULL, &outif, 0);
 #else
-       ip6_output(m, NULL, NULL, 0, NULL, &outif);
+       ip6_output(m, NULL, NULL, 0, NULL, &outif, 0);
 #endif
        if (outif)
                icmp6_ifoutstat_inc(outif, type, code);
@@ -1686,6 +2117,7 @@ icmp6_reflect(m, off)
 void
 icmp6_fasttimo()
 {
+
        mld6_fasttimeo();
 }
 
@@ -1696,19 +2128,14 @@ icmp6_redirect_diag(src6, dst6, tgt6)
        struct in6_addr *tgt6;
 {
        static char buf[1024];
-#if !defined(__OpenBSD__) && !defined(__bsdi__)
        snprintf(buf, sizeof(buf), "(src=%s dst=%s tgt=%s)",
                ip6_sprintf(src6), ip6_sprintf(dst6), ip6_sprintf(tgt6));
-#else
-       sprintf(buf, "(src=%s dst=%s tgt=%s)",
-               ip6_sprintf(src6), ip6_sprintf(dst6), ip6_sprintf(tgt6));
-#endif
        return buf;
 }
 
 void
 icmp6_redirect_input(m, off)
-       register struct mbuf *m;
+       struct mbuf *m;
        int off;
 {
        struct ifnet *ifp = m->m_pkthdr.rcvif;
@@ -1737,7 +2164,7 @@ icmp6_redirect_input(m, off)
                goto freeit;
 
 #ifndef PULLDOWN_TEST
-       IP6_EXTHDR_CHECK(m, off, icmp6len,);
+       IP6_EXTHDR_CHECK(m, off, icmp6len, return);
        nd_rd = (struct nd_redirect *)((caddr_t)ip6 + off);
 #else
        IP6_EXTHDR_GET(nd_rd, struct nd_redirect *, m, off, icmp6len);
@@ -1756,17 +2183,17 @@ icmp6_redirect_input(m, off)
 
        /* validation */
        if (!IN6_IS_ADDR_LINKLOCAL(&src6)) {
-               log(LOG_ERR,
+               nd6log((LOG_ERR,
                        "ICMP6 redirect sent from %s rejected; "
-                       "must be from linklocal\n", ip6_sprintf(&src6));
-               goto freeit;
+                       "must be from linklocal\n", ip6_sprintf(&src6)));
+               goto bad;
        }
        if (ip6->ip6_hlim != 255) {
-               log(LOG_ERR,
+               nd6log((LOG_ERR,
                        "ICMP6 redirect sent from %s rejected; "
                        "hlim=%d (must be 255)\n",
-                       ip6_sprintf(&src6), ip6->ip6_hlim);
-               goto freeit;
+                       ip6_sprintf(&src6), ip6->ip6_hlim));
+               goto bad;
        }
     {
        /* ip6->ip6_src must be equal to gw for icmp6->icmp6_reddst */
@@ -1777,39 +2204,45 @@ icmp6_redirect_input(m, off)
        sin6.sin6_family = AF_INET6;
        sin6.sin6_len = sizeof(struct sockaddr_in6);
        bcopy(&reddst6, &sin6.sin6_addr, sizeof(reddst6));
-       rt = rtalloc1((struct sockaddr *)&sin6, 0
-#if defined(__FreeBSD__) || defined (__APPLE__)
-                     , 0UL
-#endif
-                     );
+       rt = rtalloc1((struct sockaddr *)&sin6, 0, 0UL);
        if (rt) {
+               if (rt->rt_gateway == NULL ||
+                   rt->rt_gateway->sa_family != AF_INET6) {
+                       nd6log((LOG_ERR,
+                           "ICMP6 redirect rejected; no route "
+                           "with inet6 gateway found for redirect dst: %s\n",
+                           icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
+                       rtfree(rt);
+                       goto bad;
+               }
+
                gw6 = &(((struct sockaddr_in6 *)rt->rt_gateway)->sin6_addr);
                if (bcmp(&src6, gw6, sizeof(struct in6_addr)) != 0) {
-                       log(LOG_ERR,
+                       nd6log((LOG_ERR,
                                "ICMP6 redirect rejected; "
                                "not equal to gw-for-src=%s (must be same): "
                                "%s\n",
                                ip6_sprintf(gw6),
-                               icmp6_redirect_diag(&src6, &reddst6, &redtgt6));
-                       RTFREE(rt);
-                       goto freeit;
+                               icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
+                       rtfree(rt);
+                       goto bad;
                }
        } else {
-               log(LOG_ERR,
+               nd6log((LOG_ERR,
                        "ICMP6 redirect rejected; "
                        "no route found for redirect dst: %s\n",
-                       icmp6_redirect_diag(&src6, &reddst6, &redtgt6));
-               goto freeit;
+                       icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
+               goto bad;
        }
-       RTFREE(rt);
+       rtfree(rt);
        rt = NULL;
     }
        if (IN6_IS_ADDR_MULTICAST(&reddst6)) {
-               log(LOG_ERR,
+               nd6log((LOG_ERR,
                        "ICMP6 redirect rejected; "
                        "redirect dst must be unicast: %s\n",
-                       icmp6_redirect_diag(&src6, &reddst6, &redtgt6));
-               goto freeit;
+                       icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
+               goto bad;
        }
 
        is_router = is_onlink = 0;
@@ -1818,20 +2251,21 @@ icmp6_redirect_input(m, off)
        if (bcmp(&redtgt6, &reddst6, sizeof(redtgt6)) == 0)
                is_onlink = 1;  /* on-link destination case */
        if (!is_router && !is_onlink) {
-               log(LOG_ERR,
+               nd6log((LOG_ERR,
                        "ICMP6 redirect rejected; "
                        "neither router case nor onlink case: %s\n",
-                       icmp6_redirect_diag(&src6, &reddst6, &redtgt6));
-               goto freeit;
+                       icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
+               goto bad;
        }
        /* validation passed */
 
        icmp6len -= sizeof(*nd_rd);
        nd6_option_init(nd_rd + 1, icmp6len, &ndopts);
        if (nd6_options(&ndopts) < 0) {
-               log(LOG_INFO, "icmp6_redirect_input: "
+               nd6log((LOG_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;
        }
 
@@ -1846,25 +2280,23 @@ icmp6_redirect_input(m, off)
        }
 
        if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) {
-               log(LOG_INFO,
+               nd6log((LOG_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;
        }
 
        /* RFC 2461 8.3 */
        nd6_cache_lladdr(ifp, &redtgt6, lladdr, lladdrlen, ND_REDIRECT,
                         is_onlink ? ND_REDIRECT_ONLINK : ND_REDIRECT_ROUTER);
 
-       if (!is_onlink) {       /* better router case. perform rtredirect. */
+       if (!is_onlink) {       /* better router case.  perform rtredirect. */
                /* perform rtredirect */
                struct sockaddr_in6 sdst;
                struct sockaddr_in6 sgw;
                struct sockaddr_in6 ssrc;
-#ifdef __bsdi__
-               extern int icmp_redirtimeout;   /*XXX*/
-#endif
 
                bzero(&sdst, sizeof(sdst));
                bzero(&sgw, sizeof(sgw));
@@ -1878,41 +2310,17 @@ icmp6_redirect_input(m, off)
                rtredirect((struct sockaddr *)&sdst, (struct sockaddr *)&sgw,
                           (struct sockaddr *)NULL, RTF_GATEWAY | RTF_HOST,
                           (struct sockaddr *)&ssrc,
-#ifdef __bsdi__
-                          icmp_redirtimeout
-#else
-                          (struct rtentry **)NULL
-#endif /*__FreeBSD__, __NetBSD__, __bsdi__*/
-                          );
+                          (struct rtentry **)NULL);
        }
        /* finally update cached route in each socket via pfctlinput */
     {
        struct sockaddr_in6 sdst;
-#if 1
-#else
-       struct ip6protosw *pr;
-#endif
 
        bzero(&sdst, sizeof(sdst));
        sdst.sin6_family = AF_INET6;
        sdst.sin6_len = sizeof(struct sockaddr_in6);
        bcopy(&reddst6, &sdst.sin6_addr, sizeof(struct in6_addr));
-#if 1
        pfctlinput(PRC_REDIRECT_HOST, (struct sockaddr *)&sdst);
-#else
-       /*
-        * do not use pfctlinput() here, we have different prototype for
-        * xx_ctlinput() in ip6proto.
-        */
-       for (pr = (struct ip6protosw *)inet6domain.dom_protosw;
-            pr < (struct ip6protosw *)inet6domain.dom_protoswNPROTOSW;
-            pr++) {
-               if (pr->pr_ctlinput) {
-                       (*pr->pr_ctlinput)(PRC_REDIRECT_HOST,
-                               (struct sockaddr *)&sdst, NULL, NULL, 0);
-               }
-       }
-#endif
 #if IPSEC
        key_sa_routechange((struct sockaddr *)&sdst);
 #endif
@@ -1920,6 +2328,11 @@ icmp6_redirect_input(m, off)
 
  freeit:
        m_freem(m);
+       return;
+
+ bad:
+       icmp6stat.icp6s_badredirect++;
+       m_freem(m);
 }
 
 void
@@ -1928,7 +2341,7 @@ icmp6_redirect_output(m0, rt)
        struct rtentry *rt;
 {
        struct ifnet *ifp;      /* my outgoing interface */
-       struct in6_addr *ifp_ll6;
+       struct in6_addr ifp_ll6;
        struct in6_addr *router_ll6;
        struct ip6_hdr *sip6;   /* m0 as struct ip6_hdr */
        struct mbuf *m = NULL;  /* newly allocated one */
@@ -1937,15 +2350,18 @@ icmp6_redirect_output(m0, rt)
        size_t maxlen;
        u_char *p;
        struct ifnet *outif = NULL;
+       struct sockaddr_in6 src_sa;
 
-       /* if we are not router, we don't send icmp6 redirect */
-       if (!ip6_forwarding || ip6_accept_rtadv)
-               goto fail;
+       icmp6_errcount(&icmp6stat.icp6s_outerrhist, ND_REDIRECT, 0);
 
        /* sanity check */
        if (!m0 || !rt || !(rt->rt_flags & RTF_UP) || !(ifp = rt->rt_ifp))
                goto fail;
 
+       /* if we are not router, we don't send icmp6 redirect */
+       if (!ip6_forwarding || ip6_accept_rtadv || (ifp->if_eflags & IFEF_ACCEPT_RTADVD))
+               goto fail;
+
        /*
         * Address check:
         *  the source address must identify a neighbor, and
@@ -1953,7 +2369,13 @@ icmp6_redirect_output(m0, rt)
         *  [RFC 2461, sec 8.2]
         */
        sip6 = mtod(m0, struct ip6_hdr *);
-       if (nd6_is_addr_neighbor(&sip6->ip6_src, ifp) == 0)
+       bzero(&src_sa, sizeof(src_sa));
+       src_sa.sin6_family = AF_INET6;
+       src_sa.sin6_len = sizeof(src_sa);
+       src_sa.sin6_addr = sip6->ip6_src;
+       /* we don't currently use sin6_scope_id, but eventually use it */
+       src_sa.sin6_scope_id = in6_addr2scopeid(ifp, &sip6->ip6_src);
+       if (nd6_is_addr_neighbor(&src_sa, ifp, 0) == 0)
                goto fail;
        if (IN6_IS_ADDR_MULTICAST(&sip6->ip6_dst))
                goto fail;      /* what should we do here? */
@@ -1970,12 +2392,14 @@ icmp6_redirect_output(m0, rt)
 #if IPV6_MMTU >= MCLBYTES
 # error assumption failed about IPV6_MMTU and MCLBYTES
 #endif
-       MGETHDR(m, M_DONTWAIT, MT_HEADER);
+       MGETHDR(m, M_DONTWAIT, MT_HEADER);      /* MAC-OK */
        if (m && IPV6_MMTU >= MHLEN)
                MCLGET(m, M_DONTWAIT);
        if (!m)
                goto fail;
-       maxlen = (m->m_flags & M_EXT) ? MCLBYTES : MHLEN;
+       m->m_pkthdr.rcvif = NULL;
+       m->m_len = 0;
+       maxlen = M_TRAILINGSPACE(m);
        maxlen = min(IPV6_MMTU, maxlen);
        /* just for safety */
        if (maxlen < sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr) +
@@ -1990,7 +2414,8 @@ icmp6_redirect_output(m0, rt)
                                                 IN6_IFF_NOTREADY|
                                                 IN6_IFF_ANYCAST)) == NULL)
                        goto fail;
-               ifp_ll6 = &ia->ia_addr.sin6_addr;
+               ifp_ll6 = ia->ia_addr.sin6_addr;
+               ifafree(&ia->ia_ifa);
        }
 
        /* get ip6 linklocal address for the router. */
@@ -2012,7 +2437,7 @@ icmp6_redirect_output(m0, rt)
        ip6->ip6_nxt = IPPROTO_ICMPV6;
        ip6->ip6_hlim = 255;
        /* ip6->ip6_src must be linklocal addr for my outgoing if. */
-       bcopy(ifp_ll6, &ip6->ip6_src, sizeof(struct in6_addr));
+       bcopy(&ifp_ll6, &ip6->ip6_src, sizeof(struct in6_addr));
        bcopy(&sip6->ip6_src, &ip6->ip6_dst, sizeof(struct in6_addr));
 
        /* ND Redirect */
@@ -2052,11 +2477,11 @@ icmp6_redirect_output(m0, rt)
        struct nd_opt_hdr *nd_opt;
        char *lladdr;
 
-       rt_router = nd6_lookup(router_ll6, 0, ifp);
+       rt_router = nd6_lookup(router_ll6, 0, ifp, 0);
        if (!rt_router)
                goto nolladdropt;
        len = sizeof(*nd_opt) + ifp->if_addrlen;
-       len = (len + 7) & ~7;   /*round by 8*/
+       len = (len + 7) & ~7;   /* round by 8 */
        /* safety check */
        if (len + (p - (u_char *)ip6) > maxlen)
                goto nolladdropt;
@@ -2078,7 +2503,7 @@ nolladdropt:;
        m->m_pkthdr.len = m->m_len = p - (u_char *)ip6;
 
        /* just to be safe */
-#if M_DECRYPTED        /*not openbsd*/
+#ifdef M_DECRYPTED     /*not openbsd*/
        if (m0->m_flags & M_DECRYPTED)
                goto noredhdropt;
 #endif
@@ -2180,9 +2605,10 @@ noredhdropt:;
        /* send the packet to outside... */
 #if IPSEC
        /* Don't lookup socket */
-       ipsec_setsocket(m, NULL);
+       if (ipsec_bypass == 0)
+               (void)ipsec_setsocket(m, NULL);
 #endif /*IPSEC*/
-       ip6_output(m, NULL, NULL, 0, NULL, &outif);
+       ip6_output(m, NULL, NULL, 0, NULL, &outif, 0);
        if (outif) {
                icmp6_ifstat_inc(outif, ifs6_out_msg);
                icmp6_ifstat_inc(outif, ifs6_out_redirect);
@@ -2198,30 +2624,22 @@ fail:
                m_freem(m0);
 }
 
-#ifndef HAVE_NRL_INPCB
+#if HAVE_NRL_INPCB
+#define sotoin6pcb     sotoinpcb
+#define in6pcb         inpcb
+#define in6p_icmp6filt inp_icmp6filt
+#endif
 /*
  * ICMPv6 socket option processing.
- *
- * NOTE: for OSes that use NRL inpcb (bsdi4/openbsd), do not forget to modify
- * sys/netinet6/raw_ipv6.c:rip6_ctloutput().
  */
 int
-#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__)
 icmp6_ctloutput(so, sopt)
        struct socket *so;
        struct sockopt *sopt;
-#else
-icmp6_ctloutput(op, so, level, optname, mp)
-       int op;
-       struct socket *so;
-       int level, optname;
-       struct mbuf **mp;
-#endif
 {
        int error = 0;
        int optlen;
-#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__)
-       register struct inpcb *inp = sotoinpcb(so);
+       struct inpcb *inp = sotoinpcb(so);
        int level, op, optname;
 
        if (sopt) {
@@ -2231,22 +2649,12 @@ icmp6_ctloutput(op, so, level, optname, mp)
                optlen = sopt->sopt_valsize;
        } else
                level = op = optname = optlen = 0;
-#else
-       register struct in6pcb *in6p = sotoin6pcb(so);
-       register struct mbuf *m = *mp;
-
-       optlen = m ? m->m_len : 0;
-#endif
 
        if (level != IPPROTO_ICMPV6) {
-#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) && !defined(__APPLE__)
-               if (op == PRCO_SETOPT && m)
-                       (void)m_free(m);
-#endif
                return EINVAL;
        }
 
-       switch(op) {
+       switch (op) {
        case PRCO_SETOPT:
                switch (optname) {
                case ICMP6_FILTER:
@@ -2257,23 +2665,12 @@ icmp6_ctloutput(op, so, level, optname, mp)
                                error = EMSGSIZE;
                                break;
                        }
-#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__)
                        if (inp->in6p_icmp6filt == NULL) {
                                error = EINVAL;
                                break;
                        }
                        error = sooptcopyin(sopt, inp->in6p_icmp6filt, optlen,
                                optlen);
-#else
-                       p = mtod(m, struct icmp6_filter *);
-                       if (!p || !in6p->in6p_icmp6filt) {
-                               error = EINVAL;
-                               break;
-                       }
-                       bcopy(p, in6p->in6p_icmp6filt,
-                               sizeof(struct icmp6_filter));
-                       error = 0;
-#endif
                        break;
                    }
 
@@ -2281,37 +2678,18 @@ icmp6_ctloutput(op, so, level, optname, mp)
                        error = ENOPROTOOPT;
                        break;
                }
-#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) && !defined(__APPLE__)
-               if (m)
-                       (void)m_freem(m);
-#endif         
                break;
 
        case PRCO_GETOPT:
                switch (optname) {
                case ICMP6_FILTER:
                    {
-#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__)
                        if (inp->in6p_icmp6filt == NULL) {
                                error = EINVAL;
                                break;
                        }
                        error = sooptcopyout(sopt, inp->in6p_icmp6filt,
                                sizeof(struct icmp6_filter));
-#else
-                       struct icmp6_filter *p;
-
-                       if (!in6p->in6p_icmp6filt) {
-                               error = EINVAL;
-                               break;
-                       }
-                       *mp = m = m_get(M_WAIT, MT_SOOPTS);
-                       m->m_len = sizeof(struct icmp6_filter);
-                       p = mtod(m, struct icmp6_filter *);
-                       bcopy(in6p->in6p_icmp6filt, p,
-                               sizeof(struct icmp6_filter));
-                       error = 0;
-#endif
                        break;
                    }
 
@@ -2324,201 +2702,256 @@ icmp6_ctloutput(op, so, level, optname, mp)
 
        return(error);
 }
-#endif /*NRL inpcb*/
+#if HAVE_NRL_INPCB
+#undef sotoin6pcb
+#undef in6pcb
+#undef in6p_icmp6filt
+#endif
 
 /*
- * Perform rate limit check.
- * Returns 0 if it is okay to send the icmp6 packet.
- * Returns 1 if the router SHOULD NOT send this icmp6 packet due to rate
- * limitation.
- *
- * XXX per-destination/type check necessary?
+ * ICMPv6 socket datagram option processing.
  */
-static int
-icmp6_ratelimit(dst, type, code)
-       const struct in6_addr *dst;     /* not used at this moment */
-       const int type;                 /* not used at this moment */
-       const int code;                 /* not used at this moment */
+int
+icmp6_dgram_ctloutput(struct socket *so, struct sockopt *sopt)
 {
-       struct timeval tp;
-       long sec_diff, usec_diff;
-
-       /* If we are not doing rate limitation, it is always okay to send */
-       if (!icmp6errratelim)
-               return 0;
+       if (so->so_uid == 0)
+               return icmp6_ctloutput(so, sopt);
 
-#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__)
-       microtime(&tp);
-       tp.tv_sec = time_second;
-#else
-       tp = time;
-#endif
-       if (tp.tv_sec < icmp6_nextsend.tv_sec
-        || (tp.tv_sec == icmp6_nextsend.tv_sec
-         && tp.tv_usec < icmp6_nextsend.tv_usec)) {
-               /* The packet is subject to rate limit */
-               return 1;
+       if (sopt->sopt_level == IPPROTO_ICMPV6) {
+               switch (sopt->sopt_name) {
+                       case ICMP6_FILTER:
+                               return icmp6_ctloutput(so, sopt);
+                       default:
+                               return EPERM;
+               }
        }
-       sec_diff = icmp6errratelim / 1000000;
-       usec_diff = icmp6errratelim % 1000000;
-       icmp6_nextsend.tv_sec = tp.tv_sec + sec_diff;
-       if ((tp.tv_usec = tp.tv_usec + usec_diff) >= 1000000) {
-               icmp6_nextsend.tv_sec++;
-               icmp6_nextsend.tv_usec -= 1000000;
+       
+       if (sopt->sopt_level != IPPROTO_IPV6)
+               return EINVAL;
+               
+       switch (sopt->sopt_name) {
+               case IPV6_PKTOPTIONS:
+               case IPV6_UNICAST_HOPS:
+               case IPV6_CHECKSUM:
+               case IPV6_FAITH:
+               case IPV6_V6ONLY:
+               case IPV6_PKTINFO:
+               case IPV6_HOPLIMIT:
+               case IPV6_HOPOPTS:
+               case IPV6_DSTOPTS:
+               case IPV6_RTHDR:
+               case IPV6_MULTICAST_IF:
+               case IPV6_MULTICAST_HOPS:
+               case IPV6_MULTICAST_LOOP:
+               case IPV6_JOIN_GROUP:
+               case IPV6_LEAVE_GROUP:
+               case IPV6_PORTRANGE:
+               case IPV6_IPSEC_POLICY:
+                       return ip6_ctloutput(so, sopt);
+               
+               default:
+                       return EPERM;
+                       
+               
        }
-
-       /* it is okay to send this */
-       return 0;
 }
 
-#if defined(__NetBSD__) || defined(__OpenBSD__)
-static struct rtentry *
-icmp6_mtudisc_clone(dst)
-       struct sockaddr *dst;
+__private_extern__ int
+icmp6_dgram_send(struct socket *so, __unused int flags, struct mbuf *m, struct sockaddr *nam,
+         struct mbuf *control, __unused struct proc *p)
 {
-       struct rtentry *rt;
-       int    error;
+       int error = 0;
+       struct inpcb *inp = sotoinpcb(so);
+       struct sockaddr_in6 tmp;
+       struct sockaddr_in6 *dst;
+       struct icmp6_hdr *icmp6;
 
-       rt = rtalloc1(dst, 1);
-       if (rt == 0)
-               return NULL;
+       if (so->so_uid == 0)
+               return rip6_output(m, so, (struct sockaddr_in6 *) nam, control);
 
-       /* If we didn't get a host route, allocate one */
-       if ((rt->rt_flags & RTF_HOST) == 0) {
-               struct rtentry *nrt;
+       /* always copy sockaddr to avoid overwrites */
+       if (so->so_state & SS_ISCONNECTED) {
+               if (nam) {
+                               m_freem(m);
+                               return EISCONN;
+               }
+               /* 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) {
+                               m_freem(m);
+                               return ENOTCONN;
+               }
+               tmp = *(struct sockaddr_in6 *)nam;
+               dst = &tmp;
+       }
 
-               error = rtrequest((int) RTM_ADD, dst,
-                   (struct sockaddr *) rt->rt_gateway,
-                   (struct sockaddr *) 0,
-                   RTF_GATEWAY | RTF_HOST | RTF_DYNAMIC, &nrt);
-               if (error) {
-                       rtfree(rt);
-                       rtfree(nrt);
-                       return NULL;
+       /*
+        * For an ICMPv6 packet, we should know its type and code
+        */
+       if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) {
+               if (m->m_len < sizeof(struct icmp6_hdr) &&
+                       (m = m_pullup(m, sizeof(struct icmp6_hdr))) == NULL) {
+                               error = ENOBUFS;
+                               goto bad;
+               }
+               icmp6 = mtod(m, struct icmp6_hdr *);
+       
+               /*
+                * Allow only to send echo request type 128 with code 0
+                * See RFC 2463 for Echo Request Message format
+                */
+               if (icmp6->icmp6_type != 128 || icmp6->icmp6_code != 0) {
+                       error = EPERM;
+                       goto bad;
                }
-               nrt->rt_rmx = rt->rt_rmx;
-               rtfree(rt);
-               rt = nrt;
        }
-       error = rt_timer_add(rt, icmp6_mtudisc_timeout,
-                       icmp6_mtudisc_timeout_q);
-       if (error) {
-               rtfree(rt);
-               return NULL;
+
+#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 rt;      /* caller need to call rtfree() */
+       return rip6_output(m, so, (struct sockaddr_in6 *) nam, control);
+bad:
+       m_freem(m);
+       return error;
 }
 
-static void
-icmp6_mtudisc_timeout(rt, r)
-       struct rtentry *rt;
-       struct rttimer *r;
+/* Like rip6_attach but without root privilege enforcement */
+__private_extern__ int
+icmp6_dgram_attach(struct socket *so, int proto, struct proc *p)
 {
-       if (rt == NULL)
-               panic("icmp6_mtudisc_timeout: bad route to timeout");
-       if ((rt->rt_flags & (RTF_DYNAMIC | RTF_HOST)) ==
-           (RTF_DYNAMIC | RTF_HOST)) {
-               rtrequest((int) RTM_DELETE, (struct sockaddr *)rt_key(rt),
-                   rt->rt_gateway, rt_mask(rt), rt->rt_flags, 0);
-       } else {
-               if ((rt->rt_rmx.rmx_locks & RTV_MTU) == 0) {
-                       rt->rt_rmx.rmx_mtu = 0;
-               }
-       }
-}
-#endif /*__NetBSD__ || __OpenBSD__*/
+        struct inpcb *inp;
+        int error;
 
-#ifdef __bsdi__
-void
-icmp6_mtuexpire(rt, rtt)
-       struct rtentry *rt;
-       struct rttimer *rtt;
-{
-       rt->rt_flags |= RTF_PROBEMTU;
-       Free(rtt);
+        inp = sotoinpcb(so);
+        if (inp)
+                panic("icmp6_dgram_attach");
+               
+               if (proto != IPPROTO_ICMPV6)
+                       return EINVAL;
+                       
+        error = soreserve(so, rip_sendspace, rip_recvspace);
+        if (error)
+                return error;
+        error = in_pcballoc(so, &ripcbinfo, p);
+        if (error)
+                return error;
+        inp = (struct inpcb *)so->so_pcb;
+        inp->inp_vflag |= INP_IPV6;
+        inp->in6p_ip6_nxt = IPPROTO_ICMPV6;
+        inp->in6p_hops = -1;    /* use kernel default */
+        inp->in6p_cksum = -1;
+        MALLOC(inp->in6p_icmp6filt, struct icmp6_filter *,
+               sizeof(struct icmp6_filter), M_PCB, M_WAITOK);
+        if (inp->in6p_icmp6filt == NULL)
+                return (ENOMEM);
+        ICMP6_FILTER_SETPASSALL(inp->in6p_icmp6filt);
+        return 0;
 }
 
-int *icmp6_sysvars[] = ICMPV6CTL_VARS;
 
-int
-icmp6_sysctl(name, namelen, oldp, oldlenp, newp, newlen)
-       int *name;
-       u_int namelen;
-       void *oldp;
-       size_t *oldlenp;
-       void *newp;
-       size_t newlen;
+
+#ifndef HAVE_PPSRATECHECK
+#ifndef timersub
+#define        timersub(tvp, uvp, vvp)                                         \
+       do {                                                            \
+               (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec;          \
+               (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec;       \
+               if ((vvp)->tv_usec < 0) {                               \
+                       (vvp)->tv_sec--;                                \
+                       (vvp)->tv_usec += 1000000;                      \
+               }                                                       \
+       } while (0)
+#endif
+
+/*
+ * ppsratecheck(): packets (or events) per second limitation.
+ */
+static int
+ppsratecheck(lasttime, curpps, maxpps)
+       struct timeval *lasttime;
+       int *curpps;
+       int maxpps;     /* maximum pps allowed */
 {
-       if (name[0] >= ICMPV6CTL_MAXID)
-               return (EOPNOTSUPP);
-       switch (name[0]) {
-#if 0
-       ICMPV6CTL_ND6_PRUNE:
-       ICMPV6CTL_ND6_DELAY:
-       ICMPV6CTL_ND6_UMAXTRIES:
-       ICMPV6CTL_ND6_MMAXTRIES:
-       ICMPV6CTL_ND6_USELOOPBACK:
-               /* need to check the value. */
+       struct timeval tv, delta;
+       int rv;
+
+       microtime(&tv);
+
+       timersub(&tv, lasttime, &delta);
+
+       /*
+        * Check for 0,0 so that the message will be seen at least once.
+        * If more than one second has passed since the last update of
+        * lasttime, reset the counter.
+        *
+        * we do increment *curpps even in *curpps < maxpps case, as some may
+        * try to use *curpps for stat purposes as well.
+        */
+       if ((lasttime->tv_sec == 0 && lasttime->tv_usec == 0) ||
+           delta.tv_sec >= 1) {
+               *lasttime = tv;
+               *curpps = 0;
+               rv = 1;
+       } else if (maxpps < 0)
+               rv = 1;
+       else if (*curpps < maxpps)
+               rv = 1;
+       else
+               rv = 0;
+
+#if 1 /* DIAGNOSTIC? */
+       /* be careful about wrap-around */
+       if (*curpps + 1 > 0)
+               *curpps = *curpps + 1;
+#else
+       /*
+        * assume that there's not too many calls to this function.
+        * not sure if the assumption holds, as it depends on *caller's*
+        * behavior, not the behavior of this function.
+        * IMHO it is wrong to make assumption on the caller's behavior,
+        * so the above #if is #if 1, not #ifdef DIAGNOSTIC.
+        */
+       *curpps = *curpps + 1;
 #endif
-       case ICMPV6CTL_STATS:
-               return sysctl_rdtrunc(oldp, oldlenp, newp, &icmp6stat,
-                   sizeof(icmp6stat));
 
-       default:
-               return (sysctl_int_arr(icmp6_sysvars, name, namelen,
-                   oldp, oldlenp, newp, newlen));
-       }
+       return (rv);
 }
-#endif /*__bsdi__*/
+#endif
 
-#if defined(__NetBSD__) || defined(__OpenBSD__)
-#include <vm/vm.h>
-#include <sys/sysctl.h>
-int
-icmp6_sysctl(name, namelen, oldp, oldlenp, newp, newlen)
-       int *name;
-       u_int namelen;
-       void *oldp;
-       size_t *oldlenp;
-       void *newp;
-       size_t newlen;
+/*
+ * Perform rate limit check.
+ * Returns 0 if it is okay to send the icmp6 packet.
+ * Returns 1 if the router SHOULD NOT send this icmp6 packet due to rate
+ * limitation.
+ *
+ * XXX per-destination/type check necessary?
+ */
+static int
+icmp6_ratelimit(
+       __unused const struct in6_addr *dst,    /* not used at this moment */
+       __unused const int type,                        /* not used at this moment */
+       __unused const int code)                        /* not used at this moment */
 {
+       int ret;
 
-       /* All sysctl names at this level are terminal. */
-       if (namelen != 1)
-               return ENOTDIR;
-
-       switch (name[0]) {
-
-       case ICMPV6CTL_REDIRACCEPT:
-               return sysctl_int(oldp, oldlenp, newp, newlen,
-                               &icmp6_rediraccept);
-       case ICMPV6CTL_REDIRTIMEOUT:
-               return sysctl_int(oldp, oldlenp, newp, newlen,
-                               &icmp6_redirtimeout);
-       case ICMPV6CTL_STATS:
-               return sysctl_rdstruct(oldp, oldlenp, newp,
-                               &icmp6stat, sizeof(icmp6stat));
-       case ICMPV6CTL_ERRRATELIMIT:
-               return sysctl_int(oldp, oldlenp, newp, newlen,
-                                 &icmp6errratelim);
-       case ICMPV6CTL_ND6_PRUNE:
-               return sysctl_int(oldp, oldlenp, newp, newlen, &nd6_prune);
-       case ICMPV6CTL_ND6_DELAY:
-               return sysctl_int(oldp, oldlenp, newp, newlen, &nd6_delay);
-       case ICMPV6CTL_ND6_UMAXTRIES:
-               return sysctl_int(oldp, oldlenp, newp, newlen, &nd6_umaxtries);
-       case ICMPV6CTL_ND6_MMAXTRIES:
-               return sysctl_int(oldp, oldlenp, newp, newlen, &nd6_mmaxtries);
-       case ICMPV6CTL_ND6_USELOOPBACK:
-               return sysctl_int(oldp, oldlenp, newp, newlen,
-                               &nd6_useloopback);
-       case ICMPV6CTL_NODEINFO:
-               return sysctl_int(oldp, oldlenp, newp, newlen, &icmp6_nodeinfo);
-       default:
-               return ENOPROTOOPT;
+       ret = 0;        /* okay to send */
+
+       /* PPS limit */
+       if (!ppsratecheck(&icmp6errppslim_last, &icmp6errpps_count,
+           icmp6errppslim)) {
+               /* The packet is subject to rate limit */
+               ret++;
        }
-       /* NOTREACHED */
+
+       return ret;
 }
-#endif /* __NetBSD__ */
+