revisit in6_ifattach(). (1) make it more persistent about initializaing an
authoritojun <itojun@openbsd.org>
Mon, 17 Apr 2000 04:44:50 +0000 (04:44 +0000)
committeritojun <itojun@openbsd.org>
Mon, 17 Apr 2000 04:44:50 +0000 (04:44 +0000)
interface (2) cleanup interface id selection.
run NUD on p2p interface (required by spec for bidir p2p interface).
add "ndp -i interface" (can tweak per-interface ND flag).
(sync with more recent kame)

sys/netinet6/in6.c
sys/netinet6/in6_ifattach.c
sys/netinet6/in6_ifattach.h
sys/netinet6/in6_var.h
sys/netinet6/ip6_input.c
sys/netinet6/nd6.c
sys/netinet6/nd6.h
usr.sbin/ndp/ndp.8
usr.sbin/ndp/ndp.c

index 5e1930c..44dffbf 100644 (file)
@@ -1,5 +1,4 @@
-/*     $OpenBSD: in6.c,v 1.17 2000/03/22 03:48:30 itojun Exp $ */
-/*     $KAME: in6.c,v 1.63 2000/03/21 05:18:38 itojun Exp $    */
+/*     $KAME: in6.c,v 1.75 2000/04/12 03:51:29 itojun Exp $    */
 
 /*
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
 #include <net/if_types.h>
 #include <net/route.h>
 #include "gif.h"
-
 #if NGIF > 0
 #include <net/if_gif.h>
 #endif
-
 #include <net/if_dl.h>
 
 #include <netinet/in.h>
@@ -309,7 +306,7 @@ in6_control(so, cmd, data, ifp, p)
        struct  in6_aliasreq *ifra = (struct in6_aliasreq *)data;
        struct  sockaddr_in6 oldaddr;
 #ifdef COMPAT_IN6IFIOCTL
-       struct sockaddr_in6 net;
+       struct  sockaddr_in6 net;
 #endif
        int error = 0, hostIsNew, prefixIsNew;
        int newifaddr;
@@ -350,6 +347,7 @@ in6_control(so, cmd, data, ifp, p)
        case SIOCSPFXFLUSH_IN6:
        case SIOCSRTRFLUSH_IN6:
        case SIOCSDEFIFACE_IN6:
+       case SIOCSIFINFO_FLAGS:
                if (!privileged)
                        return(EPERM);
                /*fall through*/
@@ -396,8 +394,7 @@ in6_control(so, cmd, data, ifp, p)
                                /* interface ID is not embedded by the user */
                                sa6->sin6_addr.s6_addr16[1] =
                                        htons(ifp->if_index);
-                       }
-                       else if (sa6->sin6_addr.s6_addr16[1] !=
+                       } else if (sa6->sin6_addr.s6_addr16[1] !=
                                    htons(ifp->if_index)) {
                                return(EINVAL); /* ifid is contradict */
                        }
@@ -434,7 +431,7 @@ in6_control(so, cmd, data, ifp, p)
                 * on a single interface, SIOCSIFxxx ioctls are not suitable
                 * and should be unused.
                 */
-#endif 
+#endif
                if (ifra->ifra_addr.sin6_family != AF_INET6)
                        return(EAFNOSUPPORT);
                if (!privileged)
@@ -584,8 +581,7 @@ in6_control(so, cmd, data, ifp, p)
                                /* interface ID is not embedded by the user */
                                ia->ia_dstaddr.sin6_addr.s6_addr16[1]
                                        = htons(ifp->if_index);
-                       }
-                       else if (ia->ia_dstaddr.sin6_addr.s6_addr16[1] !=
+                       } else if (ia->ia_dstaddr.sin6_addr.s6_addr16[1] !=
                                    htons(ifp->if_index)) {
                                ia->ia_dstaddr = oldaddr;
                                return(EINVAL); /* ifid is contradict */
@@ -606,7 +602,7 @@ in6_control(so, cmd, data, ifp, p)
                }
                break;
 
-#endif 
+#endif
        case SIOCGIFALIFETIME_IN6:
                ifr->ifr_ifru.ifru_lifetime = ia->ia6_lifetime;
                break;
@@ -681,7 +677,7 @@ in6_control(so, cmd, data, ifp, p)
                                ia->ia_prefixmask.sin6_addr.s6_addr32[3];
                ia->ia_net = net;
                break;
-#endif 
+#endif
 
        case SIOCAIFADDR_IN6:
                prefixIsNew = 0;
@@ -1267,8 +1263,7 @@ in6_savemkludge(oia)
 
                if (mk->mk_head.lh_first != NULL) {
                        LIST_INSERT_HEAD(&in6_mk, mk, mk_entry);
-               }
-               else {
+               } else {
                        FREE(mk, M_IPMADDR);
                }
        }
@@ -1734,7 +1729,7 @@ in6_ifawithscope(oifp, dst)
        register struct ifnet *oifp;
        register struct in6_addr *dst;
 {
-       int dst_scope = in6_addrscope(dst), src_scope, best_scope;
+       int dst_scope = in6_addrscope(dst), src_scope, best_scope = 0;
        int blen = -1;
        struct ifaddr *ifa;
        struct ifnet *ifp;
@@ -2074,64 +2069,13 @@ in6_if_up(ifp)
 {
        struct ifaddr *ifa;
        struct in6_ifaddr *ia;
-       struct sockaddr_dl *sdl;
-       int type;
-       struct ether_addr ea;
-       int off;
        int dad_delay;          /* delay ticks before DAD output */
 
-       bzero(&ea, sizeof(ea));
-       sdl = NULL;
-
-       for (ifa = ifp->if_addrlist.tqh_first;
-            ifa;
-            ifa = ifa->ifa_list.tqe_next)
-       {
-               if (ifa->ifa_addr->sa_family == AF_INET6
-                && IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr)) {
-                       goto dad;
-               }
-               if (ifa->ifa_addr->sa_family != AF_LINK)
-                       continue;
-               sdl = (struct sockaddr_dl *)ifa->ifa_addr;
-               break;
-       }
-
-       switch (ifp->if_type) {
-       case IFT_LOOP:
-               in6_ifattach(ifp, IN6_IFT_LOOP, NULL, 1);
-               break;
-       case IFT_SLIP:
-       case IFT_PPP:
-       case IFT_DUMMY:
-       case IFT_GIF:
-       case IFT_FAITH:
-               type = IN6_IFT_P2P;
-               in6_ifattach(ifp, type, 0, 1);
-               break;
-       case IFT_ETHER:
-       case IFT_FDDI:
-       case IFT_ATM:
-               type = IN6_IFT_802;
-               if (sdl == NULL)
-                       break;
-               off = sdl->sdl_nlen;
-               if (bcmp(&sdl->sdl_data[off], &ea, sizeof(ea)) != 0)
-                       in6_ifattach(ifp, type, LLADDR(sdl), 0);
-               break;
-       case IFT_ARCNET:
-               type = IN6_IFT_ARCNET;
-               if (sdl == NULL)
-                       break;
-               off = sdl->sdl_nlen;
-               if (sdl->sdl_data[off] != 0)    /* XXX ?: */
-                       in6_ifattach(ifp, type, LLADDR(sdl), 0);
-               break;
-       default:
-               break;
-       }
+       /*
+        * special cases, like 6to4, are handled in in6_ifattach
+        */
+       in6_ifattach(ifp, NULL);
 
-dad:
        dad_delay = 0;
        for (ifa = ifp->if_addrlist.tqh_first;
             ifa;
index 8a981dd..e2d7111 100644 (file)
@@ -1,10 +1,10 @@
-/*     $OpenBSD: in6_ifattach.c,v 1.7 2000/03/02 09:44:28 itojun Exp $ */
-/*     $KAME: in6_ifattach.c,v 1.39 2000/03/02 09:24:45 itojun Exp $   */
+/*     $OpenBSD: in6_ifattach.c,v 1.8 2000/04/17 04:44:50 itojun Exp $ */
+/*     $KAME: in6_ifattach.c,v 1.53 2000/04/16 14:01:42 itojun Exp $   */
 
 /*
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
  * All rights reserved.
- * 
+ *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
@@ -16,7 +16,7 @@
  * 3. Neither the name of the project nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
- * 
+ *
  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 
 #include <net/net_osdep.h>
 
-static struct in6_addr llsol;
-
 struct in6_ifstat **in6_ifstat = NULL;
 struct icmp6_ifstat **icmp6_ifstat = NULL;
 size_t in6_ifstatmax = 0;
 size_t icmp6_ifstatmax = 0;
 unsigned long in6_maxmtu = 0;
 
-int found_first_ifid = 0;
-#define IFID_LEN 8
-static u_int8_t first_ifid[IFID_LEN];
+static int get_rand_ifid __P((struct ifnet *, struct in6_addr *));
+static int get_hw_ifid __P((struct ifnet *, struct in6_addr *));
+static int get_ifid __P((struct ifnet *, struct ifnet *, struct in6_addr *));
+static int in6_ifattach_addaddr __P((struct ifnet *, struct in6_ifaddr *));
+static int in6_ifattach_linklocal __P((struct ifnet *, struct ifnet *));
+static int in6_ifattach_loopback __P((struct ifnet *));
 
-static int laddr_to_eui64 __P((u_int8_t *, u_int8_t *, size_t));
-static int gen_rand_eui64 __P((u_int8_t *));
+#define EUI64_GBIT     0x01
+#define EUI64_UBIT     0x02
+#define EUI64_TO_IFID(in6)     do {(in6)->s6_addr[8] ^= EUI64_UBIT; } while (0)
+#define EUI64_GROUP(in6)       ((in6)->s6_addr[8] & EUI64_GBIT)
+#define EUI64_INDIVIDUAL(in6)  (!EUI64_GROUP(in6))
+#define EUI64_LOCAL(in6)       ((in6)->s6_addr[8] & EUI64_UBIT)
+#define EUI64_UNIVERSAL(in6)   (!EUI64_LOCAL(in6))
 
-static int
-laddr_to_eui64(dst, src, len)
-       u_int8_t *dst;
-       u_int8_t *src;
-       size_t len;
-{
-       static u_int8_t zero[8];
-
-       bzero(zero, sizeof(zero));
-
-       switch (len) {
-       case 6:
-               if (bcmp(zero, src, 6) == 0)
-                       return EINVAL;
-               dst[0] = src[0];
-               dst[1] = src[1];
-               dst[2] = src[2];
-               dst[3] = 0xff;
-               dst[4] = 0xfe;
-               dst[5] = src[3];
-               dst[6] = src[4];
-               dst[7] = src[5];
-               break;
-       case 8:
-               if (bcmp(zero, src, 8) == 0)
-                       return EINVAL;
-               bcopy(src, dst, len);
-               break;
-       default:
-               return EINVAL;
-       }
-
-       return 0;
-}
+#define IFID_LOCAL(in6)                (!EUI64_LOCAL(in6))
+#define IFID_UNIVERSAL(in6)    (!EUI64_UNIVERSAL(in6))
 
 /*
  * Generate a last-resort interface identifier, when the machine has no
  * IEEE802/EUI64 address sources.
- * The address should be random, and should not change across reboot.
+ * The goal here is to get an interface identifier that is
+ * (1) random enough and (2) does not change across reboot.
+ * We currently use MD5(hostname) for it.
  */
 static int
-gen_rand_eui64(dst)
-       u_int8_t *dst;
+get_rand_ifid(ifp, in6)
+       struct ifnet *ifp;
+       struct in6_addr *in6;   /*upper 64bits are preserved */
 {
        MD5_CTX ctxt;
        u_int8_t digest[16];
 
-       /* generate 8bytes of pseudo-random value. */
+#if 0
+       /* we need at least several letters as seed for ifid */
+       if (hostnamelen < 3)
+               return -1;
+#endif
+
+       /* generate 8 bytes of pseudo-random value. */
        bzero(&ctxt, sizeof(ctxt));
        MD5Init(&ctxt);
        MD5Update(&ctxt, hostname, hostnamelen);
        MD5Final(digest, &ctxt);
 
-       /* assumes sizeof(digest) > sizeof(first_ifid) */
-       bcopy(digest, dst, 8);
+       /* assumes sizeof(digest) > sizeof(ifid) */
+       bcopy(digest, &in6->s6_addr[8], 8);
 
        /* make sure to set "u" bit to local, and "g" bit to individual. */
-       dst[0] &= 0xfe;
-       dst[0] |= 0x02;         /* EUI64 "local" */
+       in6->s6_addr[8] &= ~EUI64_GBIT; /* g bit to "individual" */
+       in6->s6_addr[8] |= EUI64_UBIT;  /* u bit to "local" */
+
+       /* convert EUI64 into IPv6 interface identifier */
+       EUI64_TO_IFID(in6);
 
        return 0;
 }
 
 /*
- * Find first ifid on list of interfaces.
- * This is assumed that ifp0's interface token (for example, IEEE802 MAC)
- * is globally unique.  We may need to have a flag parameter in the future.
+ * Get interface identifier for the specified interface.
+ * XXX assumes single sockaddr_dl (AF_LINK address) per an interface
  */
-int
-in6_ifattach_getifid(ifp0)
-       struct ifnet *ifp0;
-{
+static int
+get_hw_ifid(ifp, in6)
        struct ifnet *ifp;
+       struct in6_addr *in6;   /*upper 64bits are preserved */
+{
        struct ifaddr *ifa;
-       u_int8_t *addr = NULL;
-       int addrlen = 0;
        struct sockaddr_dl *sdl;
+       u_int8_t *addr;
+       size_t addrlen;
+       static u_int8_t allzero[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+       static u_int8_t allone[8] =
+               { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+       for (ifa = ifp->if_addrlist.tqh_first;
+            ifa;
+            ifa = ifa->ifa_list.tqe_next)
+       {
+               if (ifa->ifa_addr->sa_family != AF_LINK)
+                       continue;
+               sdl = (struct sockaddr_dl *)ifa->ifa_addr;
+               if (sdl == NULL)
+                       continue;
+               if (sdl->sdl_alen == 0)
+                       continue;
+
+               goto found;
+       }
+
+       return -1;
+
+found:
+       addr = LLADDR(sdl);
+       addrlen = sdl->sdl_alen;
+
+       /* get EUI64 */
+       switch (ifp->if_type) {
+       case IFT_ETHER:
+       case IFT_FDDI:
+       case IFT_ATM:
+               /* IEEE802/EUI64 cases - what others? */
+
+               /* look at IEEE802/EUI64 only */
+               if (addrlen != 8 && addrlen != 6)
+                       return -1;
+
+               /*
+                * check for invalid MAC address - on bsdi, we see it a lot
+                * since wildboar configures all-zero MAC on pccard before
+                * card insertion.
+                */
+               if (bcmp(addr, allzero, addrlen) == 0)
+                       return -1;
+               if (bcmp(addr, allone, addrlen) == 0)
+                       return -1;
+
+               /* make EUI64 address */
+               if (addrlen == 8)
+                       bcopy(addr, &in6->s6_addr[8], 8);
+               else if (addrlen == 6) {
+                       in6->s6_addr[8] = addr[0];
+                       in6->s6_addr[9] = addr[1];
+                       in6->s6_addr[10] = addr[2];
+                       in6->s6_addr[11] = 0xff;
+                       in6->s6_addr[12] = 0xfe;
+                       in6->s6_addr[13] = addr[3];
+                       in6->s6_addr[14] = addr[4];
+                       in6->s6_addr[15] = addr[5];
+               }
+               break;
+
+       case IFT_ARCNET:
+               if (addrlen != 1)
+                       return -1;
+               if (!addr[0])
+                       return -1;
+
+               bzero(&in6->s6_addr[8], 8);
+               in6->s6_addr[15] = addr[0];
+
+               /*
+                * due to insufficient bitwidth, we mark it local.
+                */
+               in6->s6_addr[8] &= ~EUI64_GBIT; /* g bit to "individual" */
+               in6->s6_addr[8] |= EUI64_UBIT;  /* u bit to "local" */
+               break;
+
+       case IFT_GIF:
+#ifdef IFT_STF
+       case IFT_STF:
+#endif
+               /*
+                * mech-06 says: "SHOULD use IPv4 address as ifid source".
+                * however, IPv4 address is not very suitable as unique
+                * identifier source (can be renumbered).
+                * we don't do this.
+                */
+               return -1;
+
+       default:
+               return -1;
+       }
+
+       /* sanity check: g bit must not indicate "group" */
+       if (EUI64_GROUP(in6))
+               return -1;
+
+       /* convert EUI64 into IPv6 interface identifier */
+       EUI64_TO_IFID(in6);
+
+       /*
+        * sanity check: ifid must not be all zero, avoid conflict with
+        * subnet router anycast
+        */
+       if ((in6->s6_addr[8] & ~(EUI64_GBIT | EUI64_UBIT)) == 0x00 &&
+           bcmp(&in6->s6_addr[9], allzero, 7) == 0) {
+               return -1;
+       }
+
+       return 0;
+}
+
+/*
+ * Get interface identifier for the specified interface.  If it is not
+ * available on ifp0, borrow interface identifier from other information
+ * sources.
+ */
+static int
+get_ifid(ifp0, altifp, in6)
+       struct ifnet *ifp0;
+       struct ifnet *altifp;   /*secondary EUI64 source*/
+       struct in6_addr *in6;
+{
+       struct ifnet *ifp;
 
-       if (found_first_ifid)
-               return 0;
+       /* first, try to get it from the interface itself */
+       if (get_hw_ifid(ifp0, in6) == 0) {
+#ifdef ND6_DEBUG
+               printf("%s: got interface identifier from itself\n",
+                   if_name(ifp0));
+#endif
+               goto success;
+       }
 
+       /* try secondary EUI64 source. this basically is for ATM PVC */
+       if (altifp && get_hw_ifid(altifp, in6) == 0) {
+#ifdef ND6_DEBUG
+               printf("%s: got interface identifier from %s\n",
+                   if_name(ifp0), ifname(altifp));
+#endif
+               goto success;
+       }
+
+       /* next, try to get it from some other hardware interface */
        for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_list.tqe_next)
        {
-               if (ifp0 != NULL && ifp0 != ifp)
+               if (ifp == ifp0)
                        continue;
-               for (ifa = ifp->if_addrlist.tqh_first;
-                    ifa;
-                    ifa = ifa->ifa_list.tqe_next)
-               {
-                       if (ifa->ifa_addr->sa_family != AF_LINK)
-                               continue;
-                       sdl = (struct sockaddr_dl *)ifa->ifa_addr;
-                       if (sdl == NULL)
-                               continue;
-                       if (sdl->sdl_alen == 0)
-                               continue;
-                       switch (ifp->if_type) {
-                       case IFT_ETHER:
-                       case IFT_FDDI:
-                       case IFT_ATM:
-                               /* IEEE802/EUI64 cases - what others? */
-                               addr = LLADDR(sdl);
-                               addrlen = sdl->sdl_alen;
-                               /*
-                                * to copy ifid from IEEE802/EUI64 interface,
-                                * u bit of the source needs to be 0.
-                                */
-                               if ((addr[0] & 0x02) != 0)
-                                       break;
-                               goto found;
-                       case IFT_ARCNET:
-                               /*
-                                * ARCnet interface token cannot be used as
-                                * globally unique identifier due to its 
-                                * small bitwidth.
-                                */
-                               break;
-                       default:
-                               break;
-                       }
+               if (get_hw_ifid(ifp, in6) != 0)
+                       continue;
+
+               /*
+                * to borrow ifid from other interface, ifid needs to be
+                * globally unique
+                */
+               if (IFID_UNIVERSAL(in6)) {
+
+#ifdef ND6_DEBUG
+                       printf("%s: borrow interface identifier from %s\n",
+                           if_name(ifp0), if_name(ifp));
+#endif
+                       goto success;
                }
        }
-#ifdef DEBUG
-       printf("in6_ifattach_getifid: failed to get EUI64");
+
+       /* last resort: get from random number source */
+       if (get_rand_ifid(ifp, in6) == 0) {
+#ifdef ND6_DEBUG
+               printf("%s: interface identifier generated by random number\n",
+                   if_name(ifp0));
 #endif
-       return EADDRNOTAVAIL;
+               goto success;
+       }
 
-found:
-       if (laddr_to_eui64(first_ifid, addr, addrlen) == 0)
-               found_first_ifid = 1;
-
-       if (found_first_ifid) {
-               printf("%s: supplying EUI64: "
-                       "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
-                       if_name(ifp),
-                       first_ifid[0], first_ifid[1],
-                       first_ifid[2], first_ifid[3],
-                       first_ifid[4], first_ifid[5],
-                       first_ifid[6], first_ifid[7]);
-
-               /* invert u bit to convert EUI64 to RFC2373 interface ID. */
-               first_ifid[0] ^= 0x02;
-
-               return 0;
+       printf("%s: failed to get interface identifier", if_name(ifp0));
+       return -1;
+
+success:
+#ifdef ND6_DEBUG
+       printf("%s: ifid: "
+               "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
+               if_name(ifp0),
+               in6->s6_addr[8], in6->s6_addr[9],
+               in6->s6_addr[10], in6->s6_addr[11],
+               in6->s6_addr[12], in6->s6_addr[13],
+               in6->s6_addr[14], in6->s6_addr[15]);
+#endif
+       return 0;
+}
+
+/*
+ * configure IPv6 interface address.  XXX code duplicated with in.c
+ */
+static int
+in6_ifattach_addaddr(ifp, ia)
+       struct ifnet *ifp;
+       struct in6_ifaddr *ia;
+{
+       struct in6_ifaddr *oia;
+       struct ifaddr *ifa;
+       int error;
+       int rtflag;
+       struct in6_addr llsol;
+
+       /*
+        * initialize if_addrlist, if we are the very first one
+        */
+       ifa = TAILQ_FIRST(&ifp->if_addrlist);
+       if (ifa == NULL) {
+               TAILQ_INIT(&ifp->if_addrlist);
+       }
+
+       /*
+        * link the interface address to global list
+        */
+       TAILQ_INSERT_TAIL(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list);
+       ia->ia_ifa.ifa_refcnt++;
+
+       /*
+        * Also link into the IPv6 address chain beginning with in6_ifaddr.
+        * kazu opposed it, but itojun & jinmei wanted.
+        */
+       if ((oia = in6_ifaddr) != NULL) {
+               for (; oia->ia_next; oia = oia->ia_next)
+                       continue;
+               oia->ia_next = ia;
+       } else
+               in6_ifaddr = ia;
+       ia->ia_ifa.ifa_refcnt++;
+
+       /*
+        * give the interface a chance to initialize, in case this
+        * is the first address to be added.
+        */
+       if (ifp->if_ioctl != NULL) {
+               int s;
+               s = splimp();
+               error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia);
+               splx(s);
+       } else
+               error = 0;
+       if (error) {
+               switch (error) {
+               case EAFNOSUPPORT:
+                       printf("%s: IPv6 not supported\n", if_name(ifp));
+                       break;
+               default:
+                       printf("%s: SIOCSIFADDR error %d\n", if_name(ifp),
+                           error);
+                       break;
+               }
+
+               /* undo changes */
+               TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list);
+               IFAFREE(&ia->ia_ifa);
+               if (oia)
+                       oia->ia_next = ia->ia_next;
+               else
+                       in6_ifaddr = ia->ia_next;
+               IFAFREE(&ia->ia_ifa);
+               return -1;
+       }
+
+       /* configure link-layer address resolution */
+       rtflag = 0;
+       if (IN6_ARE_ADDR_EQUAL(&ia->ia_prefixmask.sin6_addr, &in6mask128))
+               rtflag = RTF_HOST;
+       else {
+               switch (ifp->if_type) {
+               case IFT_LOOP:
+#ifdef IFT_STF
+               case IFT_STF:
+#endif
+                       rtflag = 0;
+                       break;
+               default:
+                       ia->ia_ifa.ifa_rtrequest = nd6_rtrequest;
+                       ia->ia_ifa.ifa_flags |= RTF_CLONING;
+                       rtflag = RTF_CLONING;
+                       break;
+               }
+       }
+
+       /* add route to the interface. */
+       rtrequest(RTM_ADD,
+                 (struct sockaddr *)&ia->ia_addr,
+                 (struct sockaddr *)&ia->ia_addr,
+                 (struct sockaddr *)&ia->ia_prefixmask,
+                 RTF_UP | rtflag,
+                 (struct rtentry **)0);
+       ia->ia_flags |= IFA_ROUTE;
+
+       if ((rtflag & RTF_CLONING) != 0 &&
+           (ifp->if_flags & IFF_MULTICAST) != 0) {
+               /* Restore saved multicast addresses (if any). */
+               in6_restoremkludge(ia, ifp);
+
+               /*
+                * join solicited multicast address
+                */
+               bzero(&llsol, sizeof(llsol));
+               llsol.s6_addr16[0] = htons(0xff02);
+               llsol.s6_addr16[1] = htons(ifp->if_index);
+               llsol.s6_addr32[1] = 0;
+               llsol.s6_addr32[2] = htonl(1);
+               llsol.s6_addr32[3] = ia->ia_addr.sin6_addr.s6_addr32[3];
+               llsol.s6_addr8[12] = 0xff;
+               (void)in6_addmulti(&llsol, ifp, &error);
+
+               /* XXX should we run DAD on other interface types? */
+               switch (ifp->if_type) {
+#if 1
+               case IFT_ARCNET:
+               case IFT_ETHER:
+               case IFT_FDDI:
+#else
+               default:
+#endif
+                       /* mark the address TENTATIVE, if needed. */
+                       ia->ia6_flags |= IN6_IFF_TENTATIVE;
+                       /* nd6_dad_start() will be called in in6_if_up */
+               }
+       }
+
+       return 0;
+}
+
+static int
+in6_ifattach_linklocal(ifp, altifp)
+       struct ifnet *ifp;
+       struct ifnet *altifp;   /*secondary EUI64 source*/
+{
+       struct in6_ifaddr *ia;
+
+       /*
+        * configure link-local address
+        */
+       ia = (struct in6_ifaddr *)malloc(sizeof(*ia), M_IFADDR, M_WAITOK);
+       bzero((caddr_t)ia, sizeof(*ia));
+       ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr;
+       if (ifp->if_flags & IFF_POINTOPOINT)
+               ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr;
+       else
+               ia->ia_ifa.ifa_dstaddr = NULL;
+       ia->ia_ifa.ifa_netmask = (struct sockaddr *)&ia->ia_prefixmask;
+       ia->ia_ifp = ifp;
+
+       bzero(&ia->ia_prefixmask, sizeof(ia->ia_prefixmask));
+       ia->ia_prefixmask.sin6_len = sizeof(struct sockaddr_in6);
+       ia->ia_prefixmask.sin6_family = AF_INET6;
+       ia->ia_prefixmask.sin6_addr = in6mask64;
+
+       /* just in case */
+       bzero(&ia->ia_dstaddr, sizeof(ia->ia_dstaddr));
+       ia->ia_dstaddr.sin6_len = sizeof(struct sockaddr_in6);
+       ia->ia_dstaddr.sin6_family = AF_INET6;
+
+       bzero(&ia->ia_addr, sizeof(ia->ia_addr));
+       ia->ia_addr.sin6_len = sizeof(struct sockaddr_in6);
+       ia->ia_addr.sin6_family = AF_INET6;
+       ia->ia_addr.sin6_addr.s6_addr16[0] = htons(0xfe80);
+       ia->ia_addr.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
+       ia->ia_addr.sin6_addr.s6_addr32[1] = 0;
+       if (ifp->if_flags & IFF_LOOPBACK) {
+               ia->ia_addr.sin6_addr.s6_addr32[2] = 0;
+               ia->ia_addr.sin6_addr.s6_addr32[3] = htonl(1);
        } else {
-#ifdef DEBUG
-               printf("in6_ifattach_getifid: failed to get EUI64");
+               if (get_ifid(ifp, altifp, &ia->ia_addr.sin6_addr) != 0) {
+#ifdef ND6_DEBUG
+                       printf("%s: no ifid available\n", if_name(ifp));
 #endif
-               return EADDRNOTAVAIL;
+                       free(ia, M_IFADDR);
+                       return -1;
+               }
+       }
+
+       ia->ia_ifa.ifa_metric = ifp->if_metric;
+
+       if (in6_ifattach_addaddr(ifp, ia) != 0) {
+               /* ia will be freed on failure */
+               return -1;
+       }
+
+       return 0;
+}
+
+static int
+in6_ifattach_loopback(ifp)
+       struct ifnet *ifp;      /* must be IFT_LOOP */
+{
+       struct in6_ifaddr *ia;
+
+       /*
+        * configure link-local address
+        */
+       ia = (struct in6_ifaddr *)malloc(sizeof(*ia), M_IFADDR, M_WAITOK);
+       bzero((caddr_t)ia, sizeof(*ia));
+       ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr;
+       ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr;
+       ia->ia_ifa.ifa_netmask = (struct sockaddr *)&ia->ia_prefixmask;
+       ia->ia_ifp = ifp;
+
+       bzero(&ia->ia_prefixmask, sizeof(ia->ia_prefixmask));
+       ia->ia_prefixmask.sin6_len = sizeof(struct sockaddr_in6);
+       ia->ia_prefixmask.sin6_family = AF_INET6;
+       ia->ia_prefixmask.sin6_addr = in6mask128;
+
+       /*
+        * Always initialize ia_dstaddr (= broadcast address) to loopback
+        * address, to make getifaddr happier.
+        *
+        * For BSDI, it is mandatory.  The BSDI version of
+        * ifa_ifwithroute() rejects to add a route to the loopback
+        * interface.  Even for other systems, loopback looks somewhat
+        * special.
+        */
+       bzero(&ia->ia_dstaddr, sizeof(ia->ia_dstaddr));
+       ia->ia_dstaddr.sin6_len = sizeof(struct sockaddr_in6);
+       ia->ia_dstaddr.sin6_family = AF_INET6;
+       ia->ia_dstaddr.sin6_addr = in6addr_loopback;
+
+       bzero(&ia->ia_addr, sizeof(ia->ia_addr));
+       ia->ia_addr.sin6_len = sizeof(struct sockaddr_in6);
+       ia->ia_addr.sin6_family = AF_INET6;
+       ia->ia_addr.sin6_addr = in6addr_loopback;
+
+       ia->ia_ifa.ifa_metric = ifp->if_metric;
+
+       if (in6_ifattach_addaddr(ifp, ia) != 0) {
+               /* ia will be freed on failure */
+               return -1;
        }
+
+       return 0;
 }
 
 /*
  * XXX multiple loopback interface needs more care.  for instance,
  * nodelocal address needs to be configured onto only one of them.
+ * XXX multiple link-local address case
  */
 void
-in6_ifattach(ifp, type, laddr, noloop)
+in6_ifattach(ifp, altifp)
        struct ifnet *ifp;
-       u_int type;
-       caddr_t laddr;
-       /* size_t laddrlen; */
-       int noloop;
+       struct ifnet *altifp;   /* secondary EUI64 source */
 {
        static size_t if_indexlim = 8;
        struct sockaddr_in6 mltaddr;
        struct sockaddr_in6 mltmask;
        struct sockaddr_in6 gate;
        struct sockaddr_in6 mask;
-       struct in6_ifaddr *ia, *ib, *oia;
-       struct ifaddr *ifa;
-       int rtflag = 0;
-
-       if (type == IN6_IFT_P2P && found_first_ifid == 0) {
-               printf("%s: no ifid available for IPv6 link-local address\n",
-                       if_name(ifp));
-#if 0
-               return;
-#else
-               /* last resort */
-               if (gen_rand_eui64(first_ifid) == 0) {
-                       printf("%s: using random value as EUI64: "
-                               "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
-                               if_name(ifp),
-                               first_ifid[0], first_ifid[1],
-                               first_ifid[2], first_ifid[3],
-                               first_ifid[4], first_ifid[5],
-                               first_ifid[6], first_ifid[7]);
-                       /*
-                        * invert u bit to convert EUI64 to RFC2373 interface
-                        * ID.
-                        */
-                       first_ifid[0] ^= 0x02;
-
-                       found_first_ifid = 1;
-               }
-#endif
-       }
-
-       if ((ifp->if_flags & IFF_MULTICAST) == 0) {
-               printf("%s: not multicast capable, IPv6 not enabled\n",
-                       if_name(ifp));
-               return;
-       }
+       struct in6_ifaddr *ia;
+       struct in6_addr in6;
 
        /*
         * We have some arrays that should be indexed by if_index.
@@ -281,8 +592,8 @@ in6_ifattach(ifp, type, laddr, noloop)
         *      struct in6_ifstat **in6_ifstat
         *      struct icmp6_ifstat **icmp6_ifstat
         */
-       if (in6_ifstat == NULL || icmp6_ifstat == NULL
-        || if_index >= if_indexlim) {
+       if (in6_ifstat == NULL || icmp6_ifstat == NULL ||
+           if_index >= if_indexlim) {
                size_t n;
                caddr_t q;
                size_t olim;
@@ -317,155 +628,49 @@ in6_ifattach(ifp, type, laddr, noloop)
        }
 
        /*
-        * To prevent to assign link-local address to PnP network
-        * cards multiple times. 
-        * This is lengthy for P2P and LOOP but works.
+        * quirks based on interface type
         */
-       ifa = TAILQ_FIRST(&ifp->if_addrlist);
-       if (ifa != NULL) {
-               for ( ; ifa; ifa = TAILQ_NEXT(ifa, ifa_list)) {
-                       if (ifa->ifa_addr->sa_family != AF_INET6)
-                               continue;
-                       if (IN6_IS_ADDR_LINKLOCAL(&satosin6(ifa->ifa_addr)->sin6_addr))
-                               return;
-               }
-       } else {
-               TAILQ_INIT(&ifp->if_addrlist);
+       switch (ifp->if_type) {
+#ifdef IFT_STF
+       case IFT_STF:
+               /*
+                * 6to4 interface is a very speical kind of beast.
+                * no multicast, no linklocal (based on 03 draft).
+                */
+               goto statinit;
+#endif
+       default:
+               break;
        }
 
        /*
-        * link-local address
+        * usually, we require multicast capability to the interface
         */
-       ia = (struct in6_ifaddr *)malloc(sizeof(*ia), M_IFADDR, M_WAITOK);
-       bzero((caddr_t)ia, sizeof(*ia));
-       ia->ia_ifa.ifa_addr =    (struct sockaddr *)&ia->ia_addr;
-       if (ifp->if_flags & IFF_POINTOPOINT)
-               ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr;
-       else
-               ia->ia_ifa.ifa_dstaddr = NULL;
-       ia->ia_ifa.ifa_netmask = (struct sockaddr *)&ia->ia_prefixmask;
-       ia->ia_ifp = ifp;
-
-       TAILQ_INSERT_TAIL(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list);
-       ia->ia_ifa.ifa_refcnt++;
+       if ((ifp->if_flags & IFF_MULTICAST) == 0) {
+               printf("%s: not multicast capable, IPv6 not enabled\n",
+                   if_name(ifp));
+               return;
+       }
 
        /*
-        * Also link into the IPv6 address chain beginning with in6_ifaddr.
-        * kazu opposed it, but itojun & jinmei wanted.
+        * assign link-local address, if there's none 
         */
-       if ((oia = in6_ifaddr) != NULL) {
-               for (; oia->ia_next; oia = oia->ia_next)
-                       continue;
-               oia->ia_next = ia;
-       } else
-               in6_ifaddr = ia;
-       ia->ia_ifa.ifa_refcnt++;
-
-       ia->ia_prefixmask.sin6_len = sizeof(struct sockaddr_in6);
-       ia->ia_prefixmask.sin6_family = AF_INET6;
-       ia->ia_prefixmask.sin6_addr = in6mask64;
-
-       bzero(&ia->ia_addr, sizeof(struct sockaddr_in6));
-       ia->ia_addr.sin6_len = sizeof(struct sockaddr_in6);
-       ia->ia_addr.sin6_family = AF_INET6;
-       ia->ia_addr.sin6_addr.s6_addr16[0] = htons(0xfe80);
-       ia->ia_addr.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
-       ia->ia_addr.sin6_addr.s6_addr32[1] = 0;
-
-       switch (type) {
-       case IN6_IFT_LOOP:
-               ia->ia_addr.sin6_addr.s6_addr32[2] = 0;
-               ia->ia_addr.sin6_addr.s6_addr32[3] = htonl(1);
-               break;
-       case IN6_IFT_802:
-               ia->ia_ifa.ifa_rtrequest = nd6_rtrequest;
-               ia->ia_ifa.ifa_flags |= RTF_CLONING;
-               rtflag = RTF_CLONING;
-               /* fall through */
-       case IN6_IFT_P2P802:
-               if (laddr == NULL)
-                       break;
-               /* XXX use laddrlen */
-               if (laddr_to_eui64(&ia->ia_addr.sin6_addr.s6_addr8[8],
-                               laddr, 6) != 0) {
-                       break;
-               }
-               /* invert u bit to convert EUI64 to RFC2373 interface ID. */
-               ia->ia_addr.sin6_addr.s6_addr8[8] ^= 0x02;
-               if (found_first_ifid == 0)
-                       in6_ifattach_getifid(ifp);
-               bzero(&ia->ia_dstaddr, sizeof(struct sockaddr_in6));
-               ia->ia_dstaddr.sin6_len = sizeof(struct sockaddr_in6);
-               ia->ia_dstaddr.sin6_family = AF_INET6;
-               break;
-       case IN6_IFT_P2P:
-               bcopy((caddr_t)first_ifid,
-                     (caddr_t)&ia->ia_addr.sin6_addr.s6_addr8[8],
-                     IFID_LEN);
-               bzero(&ia->ia_dstaddr, sizeof(struct sockaddr_in6));
-               ia->ia_dstaddr.sin6_len = sizeof(struct sockaddr_in6);
-               ia->ia_dstaddr.sin6_family = AF_INET6;
-               break;
-       case IN6_IFT_ARCNET:
-               ia->ia_ifa.ifa_rtrequest = nd6_rtrequest;
-               ia->ia_ifa.ifa_flags |= RTF_CLONING;
-               rtflag = RTF_CLONING;
-               if (laddr == NULL)
-                       break;
-
-               /* make non-global IF id out of link-level address */
-               bzero(&ia->ia_addr.sin6_addr.s6_addr8[8], 7);
-               ia->ia_addr.sin6_addr.s6_addr8[15] = *laddr;
-       }
-
-       ia->ia_ifa.ifa_metric = ifp->if_metric;
-
-       if (ifp->if_ioctl != NULL) {
-               int s;
-               int error;
-
-               /*
-                * give the interface a chance to initialize, in case this
-                * is the first address to be added.
-                */
-               s = splimp();
-               error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia);
-               splx(s);
+       ia = in6ifa_ifpforlinklocal(ifp, 0);
+       if (ia == NULL) {
+               if (in6_ifattach_linklocal(ifp, altifp) != 0)
+                       return;
+               ia = in6ifa_ifpforlinklocal(ifp, 0);
 
-               if (error) {
-                       switch (error) {
-                       case EAFNOSUPPORT:
-                               printf("%s: IPv6 not supported\n",
-                                       if_name(ifp));
-                               break;
-                       default:
-                               printf("%s: SIOCSIFADDR error %d\n",
-                                       if_name(ifp), error);
-                               break;
-                       }
+               if (ia == NULL) {
+                       printf("%s: failed to add link-local address",
+                           if_name(ifp));
 
-                       /* undo changes */
-                       TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list);
-                       IFAFREE(&ia->ia_ifa);
-                       if (oia)
-                               oia->ia_next = ia->ia_next;
-                       else
-                               in6_ifaddr = ia->ia_next;
-                       IFAFREE(&ia->ia_ifa);
-                       return;
+                       /* we can't initialize multicasts without link-local */
+                       goto statinit;
                }
        }
 
-       /* add route to the interface. */
-       rtrequest(RTM_ADD,
-                 (struct sockaddr *)&ia->ia_addr,
-                 (struct sockaddr *)&ia->ia_addr, 
-                 (struct sockaddr *)&ia->ia_prefixmask,
-                 RTF_UP|rtflag,
-                 (struct rtentry **)0);
-       ia->ia_flags |= IFA_ROUTE;
-
-       if (type == IN6_IFT_P2P || type == IN6_IFT_P2P802) {
+       if (ifp->if_flags & IFF_POINTOPOINT) {
                /*
                 * route local address to loopback
                 */
@@ -486,60 +691,30 @@ in6_ifattach(ifp, type, laddr, noloop)
        }
 
        /*
-        * loopback address
+        * assign loopback address for loopback interface
+        * XXX multiple loopback interface case
         */
-       ib = (struct in6_ifaddr *)NULL;
-       if (type == IN6_IFT_LOOP) {
-               ib = (struct in6_ifaddr *)
-                       malloc(sizeof(*ib), M_IFADDR, M_WAITOK);
-               bzero((caddr_t)ib, sizeof(*ib));
-               ib->ia_ifa.ifa_addr = (struct sockaddr *)&ib->ia_addr;
-               ib->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ib->ia_dstaddr;
-               ib->ia_ifa.ifa_netmask = (struct sockaddr *)&ib->ia_prefixmask;
-               ib->ia_ifp = ifp;
-
-               ia->ia_next = ib;
-               TAILQ_INSERT_TAIL(&ifp->if_addrlist, (struct ifaddr *)ib,
-                       ifa_list);
-               ib->ia_ifa.ifa_refcnt++;
-
-               ib->ia_prefixmask.sin6_len = sizeof(struct sockaddr_in6);
-               ib->ia_prefixmask.sin6_family = AF_INET6;
-               ib->ia_prefixmask.sin6_addr = in6mask128;
-               ib->ia_addr.sin6_len = sizeof(struct sockaddr_in6);
-               ib->ia_addr.sin6_family = AF_INET6;
-               ib->ia_addr.sin6_addr = in6addr_loopback;
-
-               /*
-                * Always initialize ia_dstaddr (= broadcast address)
-                * to loopback address, to make getifaddr happier.
-                *
-                * For BSDI, it is mandatory.  The BSDI version of
-                * ifa_ifwithroute() rejects to add a route to the loopback
-                * interface.  Even for other systems, loopback looks somewhat
-                * special.
-                */
-               ib->ia_dstaddr.sin6_len = sizeof(struct sockaddr_in6);
-               ib->ia_dstaddr.sin6_family = AF_INET6;
-               ib->ia_dstaddr.sin6_addr = in6addr_loopback;
-
-               ib->ia_ifa.ifa_metric = ifp->if_metric;
-
-               rtrequest(RTM_ADD,
-                         (struct sockaddr *)&ib->ia_addr,
-                         (struct sockaddr *)&ib->ia_addr, 
-                         (struct sockaddr *)&ib->ia_prefixmask,
-                         RTF_UP|RTF_HOST,
-                         (struct rtentry **)0);
+       in6 = in6addr_loopback;
+       if (ifp->if_flags & IFF_LOOPBACK) {
+               if (in6ifa_ifpwithaddr(ifp, &in6) == NULL) {
+                       if (in6_ifattach_loopback(ifp) != 0)
+                               return;
+               }
+       }
 
-               ib->ia_flags |= IFA_ROUTE;
+#ifdef DIAGNOSTIC
+       if (!ia) {
+               panic("ia == NULL in in6_ifattach");
+               /*NOTREACHED*/
        }
+#endif
 
        /*
         * join multicast
         */
        if (ifp->if_flags & IFF_MULTICAST) {
                int error;      /* not used */
+               struct in6_multi *in6m;
 
                /* Restore saved multicast addresses(if any). */
                in6_restoremkludge(ia, ifp);
@@ -557,41 +732,41 @@ in6_ifattach(ifp, type, laddr, noloop)
                mltaddr.sin6_family = AF_INET6;
                mltaddr.sin6_addr = in6addr_linklocal_allnodes;
                mltaddr.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
-               rtrequest(RTM_ADD,
-                         (struct sockaddr *)&mltaddr,
-                         (struct sockaddr *)&ia->ia_addr, 
-                         (struct sockaddr *)&mltmask,
-                         RTF_UP|RTF_CLONING,  /* xxx */
-                         (struct rtentry **)0);
-               (void)in6_addmulti(&mltaddr.sin6_addr, ifp, &error);
 
-               if (type == IN6_IFT_LOOP) {
-                       /*
-                        * join node-local all-nodes address
-                        */
-                       mltaddr.sin6_addr = in6addr_nodelocal_allnodes;
+               IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m);
+               if (in6m == NULL) {
                        rtrequest(RTM_ADD,
                                  (struct sockaddr *)&mltaddr,
-                                 (struct sockaddr *)&ib->ia_addr, 
+                                 (struct sockaddr *)&ia->ia_addr,
                                  (struct sockaddr *)&mltmask,
-                                 RTF_UP,
+                                 RTF_UP|RTF_CLONING,  /* xxx */
                                  (struct rtentry **)0);
                        (void)in6_addmulti(&mltaddr.sin6_addr, ifp, &error);
-               } else {
+               }
+
+               if (ifp->if_flags & IFF_LOOPBACK) {
+                       in6 = in6addr_loopback;
+                       ia = in6ifa_ifpwithaddr(ifp, &in6);
                        /*
-                        * join solicited multicast address
+                        * join node-local all-nodes address, on loopback
                         */
-                       bzero(&llsol, sizeof(llsol));
-                       llsol.s6_addr16[0] = htons(0xff02);
-                       llsol.s6_addr16[1] = htons(ifp->if_index);
-                       llsol.s6_addr32[1] = 0;
-                       llsol.s6_addr32[2] = htonl(1);
-                       llsol.s6_addr32[3] = ia->ia_addr.sin6_addr.s6_addr32[3];
-                       llsol.s6_addr8[12] = 0xff;
-                       (void)in6_addmulti(&llsol, ifp, &error);
+                       mltaddr.sin6_addr = in6addr_nodelocal_allnodes;
+
+                       IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m);
+                       if (in6m == NULL && ia != NULL) {
+                               rtrequest(RTM_ADD,
+                                         (struct sockaddr *)&mltaddr,
+                                         (struct sockaddr *)&ia->ia_addr,
+                                         (struct sockaddr *)&mltmask,
+                                         RTF_UP,
+                                         (struct rtentry **)0);
+                               (void)in6_addmulti(&mltaddr.sin6_addr, ifp, &error);
+                       }
                }
        }
 
+statinit:;
+
        /* update dynamically. */
        if (in6_maxmtu < ifp->if_mtu)
                in6_maxmtu = ifp->if_mtu;
@@ -609,29 +784,6 @@ in6_ifattach(ifp, type, laddr, noloop)
 
        /* initialize NDP variables */
        nd6_ifattach(ifp);
-
-       /* mark the address TENTATIVE, if needed. */
-       switch (ifp->if_type) {
-       case IFT_ARCNET:
-       case IFT_ETHER:
-       case IFT_FDDI:
-#if 0
-       case IFT_ATM:
-       case IFT_SLIP:
-       case IFT_PPP:
-#endif
-               ia->ia6_flags |= IN6_IFF_TENTATIVE;
-               /* nd6_dad_start() will be called in in6_if_up */
-               break;
-       case IFT_DUMMY:
-       case IFT_GIF:   /*XXX*/
-       case IFT_LOOP:
-       case IFT_FAITH:
-       default:
-               break;
-       }
-
-       return;
 }
 
 /*
@@ -674,7 +826,7 @@ in6_ifdetach(ifp)
                        rtfree(rt);
                        rtrequest(RTM_DELETE,
                                (struct sockaddr *)&ia->ia_addr,
-                               (struct sockaddr *)&ia->ia_addr, 
+                               (struct sockaddr *)&ia->ia_addr,
                                (struct sockaddr *)&ia->ia_prefixmask,
                                rtflags, (struct rtentry **)0);
                }
@@ -691,7 +843,7 @@ in6_ifdetach(ifp)
                                ia = ia->ia_next;
                        if (ia->ia_next)
                                ia->ia_next = oia->ia_next;
-#ifdef DEBUG
+#ifdef ND6_DEBUG
                        else
                                printf("%s: didn't unlink in6ifaddr from "
                                    "list\n", if_name(ifp));
index d7023e7..6985bc9 100644 (file)
@@ -1,9 +1,10 @@
-/*     $OpenBSD: in6_ifattach.h,v 1.1 1999/12/08 06:50:21 itojun Exp $ */
+/*     $OpenBSD: in6_ifattach.h,v 1.2 2000/04/17 04:44:50 itojun Exp $ */
+/*     $KAME: in6_ifattach.h,v 1.9 2000/04/12 05:35:48 itojun Exp $    */
 
 /*
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
  * All rights reserved.
- * 
+ *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
@@ -15,7 +16,7 @@
  * 3. Neither the name of the project nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
- * 
+ *
  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 #define _NETINET6_IN6_IFATTACH_H_
 
 #ifdef _KERNEL
-extern int found_first_ifid;
-
-int in6_ifattach_getifid __P((struct ifnet *));
-void in6_ifattach_p2p __P((void));
-void in6_ifattach __P((struct ifnet *, u_int, caddr_t, int));
+void in6_ifattach __P((struct ifnet *, struct ifnet *));
 void in6_ifdetach __P((struct ifnet *));
 #endif /* _KERNEL */
 
-#define IN6_IFT_LOOP   1
-#define IN6_IFT_P2P    2
-#define IN6_IFT_802    3
-#define IN6_IFT_P2P802 4
-#define IN6_IFT_ARCNET 5
-
 #endif /* _NETINET6_IN6_IFATTACH_H_ */
index 20e3637..c76b625 100644 (file)
@@ -1,10 +1,10 @@
-/*     $OpenBSD: in6_var.h,v 1.9 2000/02/28 11:55:22 itojun Exp $      */
-/*     $KAME: in6_var.h,v 1.29 2000/02/25 05:20:58 itojun Exp $        */
+/*     $OpenBSD: in6_var.h,v 1.10 2000/04/17 04:44:50 itojun Exp $     */
+/*     $KAME: in6_var.h,v 1.31 2000/03/25 07:23:46 sumikawa Exp $      */
 
 /*
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
  * All rights reserved.
- * 
+ *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
@@ -16,7 +16,7 @@
  * 3. Neither the name of the project nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
- * 
+ *
  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
@@ -189,7 +189,7 @@ struct icmp6_ifstat {
        u_quad_t ifs6_in_mlddone;
 
        /*
-        * Output statistics. We should solve unresolved routing problem...  
+        * Output statistics. We should solve unresolved routing problem...
         */
        /* ipv6IfIcmpOutMsgs, total # of output messages */
        u_quad_t ifs6_out_msg;
@@ -365,7 +365,7 @@ struct      in6_rrenumreq {
  */
 #define SIOCSIFDSTADDR_IN6      _IOW('i', 14, struct in6_ifreq)
 #define SIOCSIFNETMASK_IN6      _IOW('i', 22, struct in6_ifreq)
-#endif 
+#endif
 
 #define SIOCGIFDSTADDR_IN6     _IOWR('i', 34, struct in6_ifreq)
 #define SIOCGIFNETMASK_IN6     _IOWR('i', 37, struct in6_ifreq)
@@ -395,6 +395,8 @@ struct      in6_rrenumreq {
 #define SIOCSDEFIFACE_IN6      _IOWR('i', 85, struct in6_ndifreq)
 #define SIOCGDEFIFACE_IN6      _IOWR('i', 86, struct in6_ndifreq)
 
+#define SIOCSIFINFO_FLAGS      _IOWR('i', 87, struct in6_ndireq) /* XXX */
+
 #define SIOCSIFPREFIX_IN6      _IOW('i', 100, struct in6_prefixreq) /* set */
 #define SIOCGIFPREFIX_IN6      _IOWR('i', 101, struct in6_prefixreq) /* get */
 #define SIOCDIFPREFIX_IN6      _IOW('i', 102, struct in6_prefixreq) /* del */
index e39d551..94370a4 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ip6_input.c,v 1.9 2000/03/22 03:50:35 itojun Exp $    */
+/*     $OpenBSD: ip6_input.c,v 1.10 2000/04/17 04:44:50 itojun Exp $   */
 /*     $KAME: ip6_input.c,v 1.72 2000/03/21 09:23:19 itojun Exp $      */
 
 /*
@@ -182,17 +182,13 @@ static void
 ip6_init2(dummy)
        void *dummy;
 {
-       int ret;
-
-       /* get EUI64 from somewhere */
-       ret = in6_ifattach_getifid(NULL);
 
 #if 1
        /*
         * to route local address of p2p link to loopback,
         * assign loopback address first. 
         */
-       in6_ifattach(&loif[0], IN6_IFT_LOOP, NULL, 0);
+       in6_ifattach(&loif[0], NULL);
 #else
        /* you MUST bring lo0 up manually, in rc script. */
 #endif
index 30f7120..ac3bfb8 100644 (file)
@@ -1,10 +1,10 @@
-/*     $OpenBSD: nd6.c,v 1.10 2000/04/13 16:27:26 itojun Exp $ */
-/*     $KAME: nd6.c,v 1.41 2000/02/24 16:34:50 itojun Exp $    */
+/*     $OpenBSD: nd6.c,v 1.11 2000/04/17 04:44:51 itojun Exp $ */
+/*     $KAME: nd6.c,v 1.55 2000/04/16 14:08:30 itojun Exp $    */
 
 /*
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
  * All rights reserved.
- * 
+ *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
@@ -16,7 +16,7 @@
  * 3. Neither the name of the project nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
- * 
+ *
  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
@@ -158,12 +158,18 @@ nd6_ifattach(ifp)
        }
 
 #define ND nd_ifinfo[ifp->if_index]
+
+       /* don't initialize if called twice */
+       if (ND.linkmtu)
+               return;
+
        ND.linkmtu = ifindex2ifnet[ifp->if_index]->if_mtu;
        ND.chlim = IPV6_DEFHLIM;
        ND.basereachable = REACHABLE_TIME;
        ND.reachable = ND_COMPUTE_RTIME(ND.basereachable);
        ND.retrans = RETRANS_TIMER;
        ND.receivedra = 0;
+       ND.flags = ND6_IFF_PERFORMNUD;
        nd6_setmtu(ifp);
 #undef ND
 }
@@ -339,7 +345,7 @@ nd6_options(ndopts)
                         * Unknown options must be silently ignored,
                         * to accomodate future extension to the protocol.
                         */
-                       log(LOG_INFO,
+                       log(LOG_DEBUG,
                            "nd6_options: unsupported option %d - "
                            "option ignored\n", nd_opt->nd_opt_type);
                }
@@ -383,6 +389,8 @@ nd6_timer(ignored_arg)
                struct ifnet *ifp;
                struct sockaddr_in6 *dst;
                struct llinfo_nd6 *next = ln->ln_next;
+               /* XXX: used for the DELAY case only: */
+               struct nd_ifinfo *ndi = NULL;
 
                if ((rt = ln->ln_rt) == NULL) {
                        ln = next;
@@ -392,6 +400,7 @@ nd6_timer(ignored_arg)
                        ln = next;
                        continue;
                }
+               ndi = &nd_ifinfo[ifp->if_index];
                dst = (struct sockaddr_in6 *)rt_key(rt);
 
                if (ln->ln_expire > time_second) {
@@ -402,6 +411,9 @@ nd6_timer(ignored_arg)
                /* sanity check */
                if (!rt)
                        panic("rt=0 in nd6_timer(ln=%p)\n", ln);
+               if (rt->rt_llinfo && (struct llinfo_nd6 *)rt->rt_llinfo != ln)
+                       panic("rt_llinfo(%p) is not equal to ln(%p)\n",
+                             rt->rt_llinfo, ln);
                if (!dst)
                        panic("dst=0 in nd6_timer(ln=%p)\n", ln);
 
@@ -437,19 +449,24 @@ nd6_timer(ignored_arg)
                        if (ln->ln_expire)
                                ln->ln_state = ND6_LLINFO_STALE;
                        break;
-               /* 
+               /*
                 * ND6_LLINFO_STALE state requires nothing for timer
                 * routine.
                 */
                case ND6_LLINFO_DELAY:
-                       ln->ln_asked = 1;
-                       ln->ln_state = ND6_LLINFO_PROBE;
-                       ln->ln_expire = time_second +
-                               nd_ifinfo[ifp->if_index].retrans / 1000;
-                       nd6_ns_output(ifp, &dst->sin6_addr, &dst->sin6_addr,
-                               ln, 0);
+                       if (ndi && (ndi->flags & ND6_IFF_PERFORMNUD) != 0) {
+                               /* We need NUD */
+                               ln->ln_asked = 1;
+                               ln->ln_state = ND6_LLINFO_PROBE;
+                               ln->ln_expire = time_second +
+                                       ndi->retrans / 1000;
+                               nd6_ns_output(ifp, &dst->sin6_addr,
+                                             &dst->sin6_addr,
+                                             ln, 0);
+                       }
+                       else
+                               ln->ln_state = ND6_LLINFO_STALE; /* XXX */
                        break;
-
                case ND6_LLINFO_PROBE:
                        if (ln->ln_asked < nd6_umaxtries) {
                                ln->ln_asked++;
@@ -639,7 +656,7 @@ nd6_lookup(addr6, create, ifp)
        if (rt && (rt->rt_flags & RTF_LLINFO) == 0) {
                /*
                 * This is the case for the default route.
-                * If we want to create a neighbor cache for the address, we 
+                * If we want to create a neighbor cache for the address, we
                 * should free the route for the destination and allocate an
                 * interface route.
                 */
@@ -650,6 +667,8 @@ nd6_lookup(addr6, create, ifp)
        }
        if (!rt) {
                if (create && ifp) {
+                       int e;
+
                        /*
                         * If no route is available and create is set,
                         * we allocate a host route for the destination
@@ -666,17 +685,19 @@ nd6_lookup(addr6, create, ifp)
                         * Create a new route. RTF_LLINFO is necessary
                         * to create a Neighbor Cache entry for the
                         * destination in nd6_rtrequest which will be
-                        * called in rtequest via ifa->ifa_rtrequest. 
+                        * called in rtequest via ifa->ifa_rtrequest.
                         */
-                       if (rtrequest(RTM_ADD, (struct sockaddr *)&sin6,
-                                     ifa->ifa_addr,
-                                     (struct sockaddr *)&all1_sa,
-                                     (ifa->ifa_flags |
-                                      RTF_HOST | RTF_LLINFO) & ~RTF_CLONING,
-                                     &rt))
+                       if ((e = rtrequest(RTM_ADD, (struct sockaddr *)&sin6,
+                                          ifa->ifa_addr,
+                                          (struct sockaddr *)&all1_sa,
+                                          (ifa->ifa_flags |
+                                           RTF_HOST | RTF_LLINFO) &
+                                          ~RTF_CLONING,
+                                          &rt)) != 0)
                                log(LOG_ERR,
                                    "nd6_lookup: failed to add route for a "
-                                   "neighbor(%s)\n", ip6_sprintf(addr6));
+                                   "neighbor(%s), errno=%d\n",
+                                   ip6_sprintf(addr6), e);
                        if (rt == NULL)
                                return(NULL);
                        if (rt->rt_llinfo) {
@@ -711,7 +732,7 @@ nd6_lookup(addr6, create, ifp)
 
 /*
  * Detect if a given IPv6 address identifies a neighbor on a given link.
- * XXX: should take care of the destination of a p2p link? 
+ * XXX: should take care of the destination of a p2p link?
  */
 int
 nd6_is_addr_neighbor(addr, ifp)
@@ -1058,14 +1079,21 @@ nd6_rtrequest(req, rt, sa)
 #endif
                /* FALLTHROUGH */
        case RTM_RESOLVE:
-               if (gate->sa_family != AF_LINK ||
-                   gate->sa_len < sizeof(null_sdl)) {
-                       log(LOG_DEBUG, "nd6_rtrequest: bad gateway value\n");
-                       break;
+               if ((ifp->if_flags & IFF_POINTOPOINT) == 0) {
+                       /*
+                        * Address resolution isn't necessary for a point to
+                        * point link, so we can skip this test for a p2p link.
+                        */
+                       if (gate->sa_family != AF_LINK ||
+                           gate->sa_len < sizeof(null_sdl)) {
+                               log(LOG_DEBUG,
+                                   "nd6_rtrequest: bad gateway value\n");
+                               break;
+                       }
+                       SDL(gate)->sdl_type = ifp->if_type;
+                       SDL(gate)->sdl_index = ifp->if_index;
                }
-               SDL(gate)->sdl_type = ifp->if_type;
-               SDL(gate)->sdl_index = ifp->if_index;
-               if (ln != 0)
+               if (ln != NULL)
                        break;  /* This happens on a route change */
                /*
                 * Case 2: This route may come from cloning, or a manual route
@@ -1090,7 +1118,7 @@ nd6_rtrequest(req, rt, sa)
                         */
                        ln->ln_state = ND6_LLINFO_REACHABLE;
                } else {
-                       /* 
+                       /*
                         * When req == RTM_RESOLVE, rt is created and
                         * initialized in rtrequest(), so rt_expire is 0.
                         */
@@ -1364,6 +1392,10 @@ nd6_ioctl(cmd, data, ifp)
        case SIOCGIFINFO_IN6:
                ndi->ndi = nd_ifinfo[ifp->if_index];
                break;
+       case SIOCSIFINFO_FLAGS:
+               /* XXX: almost all other fields of ndi->ndi is unused */
+               nd_ifinfo[ifp->if_index].flags = ndi->ndi.flags;
+               break;
        case SIOCSNDFLUSH_IN6:  /* XXX: the ioctl name is confusing... */
                /* flush default router list */
                /*
@@ -1585,7 +1617,7 @@ fail:
                                nd6_output(ifp, ln->ln_hold,
                                           (struct sockaddr_in6 *)rt_key(rt),
                                           rt);
-#endif 
+#endif
                                ln->ln_hold = 0;
                        }
                } else if (ln->ln_state == ND6_LLINFO_INCOMPLETE) {
@@ -1634,9 +1666,8 @@ fail:
        case ND_REDIRECT:
                /*
                 * If the icmp is a redirect to a better router, always set the
-                * is_router flag. Otherwise, if the entry is newly created, 
+                * is_router flag. Otherwise, if the entry is newly created,
                 * clear the flag. [RFC 2461, sec 8.3]
-                * 
                 */
                if (code == ND_REDIRECT_ROUTER)
                        ln->ln_router = 1;
@@ -1708,19 +1739,27 @@ nd6_output(ifp, m0, dst, rt0)
 
        /*
         * XXX: we currently do not make neighbor cache on any interface
-        * other than ARCnet, Ethernet and FDDI.
+        * other than ARCnet, Ethernet, FDDI and GIF.
+        *
+        * draft-ietf-ngtrans-mech-04.txt says:
+        * - unidirectional tunnels needs no ND
         */
        switch (ifp->if_type) {
        case IFT_ARCNET:
        case IFT_ETHER:
        case IFT_FDDI:
+       case IFT_GIF:           /* XXX need more cases? */
                break;
        default:
                goto sendpkt;
        }
 
+       if ((ifp->if_flags & IFF_POINTOPOINT) != 0 &&
+           (nd_ifinfo[ifp->if_index].flags & ND6_IFF_PERFORMNUD) == 0)
+               goto sendpkt;
+
        /*
-        * next hop determination. This routine is derived from ether_outpout. 
+        * next hop determination. This routine is derived from ether_outpout.
         */
        if (rt) {
                if ((rt->rt_flags & RTF_UP) == 0) {
@@ -1730,7 +1769,7 @@ nd6_output(ifp, m0, dst, rt0)
                                rt->rt_refcnt--;
                                if (rt->rt_ifp != ifp)
                                        return nd6_output(ifp, m0, dst, rt); /* XXX: loop care? */
-                       } else 
+                       } else
                                senderr(EHOSTUNREACH);
                }
                if (rt->rt_flags & RTF_GATEWAY) {
@@ -1768,6 +1807,10 @@ nd6_output(ifp, m0, dst, rt0)
                senderr(EIO);   /* XXX: good error? */
        }
 
+       /* We don't have to do link-layer address resolution on a p2p link. */
+       if ((ifp->if_flags & IFF_POINTOPOINT) != 0 &&
+           ln->ln_state < ND6_LLINFO_REACHABLE)
+               ln->ln_state = ND6_LLINFO_STALE;
 
        /*
         * The first time we send a packet to a neighbor whose entry is
@@ -1818,7 +1861,7 @@ nd6_output(ifp, m0, dst, rt0)
        
   sendpkt:
        return((*ifp->if_output)(ifp, m, (struct sockaddr *)dst, rt));
-       
+
   bad:
        if (m)
                m_freem(m);
index 1051468..71ff81a 100644 (file)
@@ -1,10 +1,10 @@
-/*     $OpenBSD: nd6.h,v 1.4 2000/02/28 11:55:22 itojun Exp $  */
-/*     $KAME: nd6.h,v 1.16 2000/02/24 16:34:51 itojun Exp $    */
+/*     $OpenBSD: nd6.h,v 1.5 2000/04/17 04:44:51 itojun Exp $  */
+/*     $KAME: nd6.h,v 1.19 2000/03/25 07:23:57 sumikawa Exp $  */
 
 /*
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
  * All rights reserved.
- * 
+ *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
@@ -16,7 +16,7 @@
  * 3. Neither the name of the project nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
- * 
+ *
  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
@@ -67,11 +67,14 @@ struct nd_ifinfo {
        u_int32_t basereachable;        /* BaseReachableTime */
        u_int32_t reachable;            /* Reachable Time */
        u_int32_t retrans;              /* Retrans Timer */
+       u_int32_t flags;                /* Flags */
        int recalctm;                   /* BaseReacable re-calculation timer */
        u_int8_t chlim;                 /* CurHopLimit */
        u_int8_t receivedra;
 };
 
+#define ND6_IFF_PERFORMNUD     0x1
+
 struct in6_nbrinfo {
        char ifname[IFNAMSIZ];  /* if name, e.g. "en0" */
        struct in6_addr addr;   /* IPv6 address of the neighbor */
index 7028219..3ffcf01 100644 (file)
@@ -1,4 +1,4 @@
-.\"    $OpenBSD: ndp.8,v 1.6 2000/04/12 21:47:55 aaron Exp $
+.\"    $OpenBSD: ndp.8,v 1.7 2000/04/17 04:44:51 itojun Exp $
 .\"
 .\" Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
 .\" All rights reserved.
@@ -27,7 +27,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\"     KAME Id: ndp.8,v 1.5 2000/02/17 05:35:56 itojun Exp
+.\"     KAME Id: ndp.8,v 1.9 2000/04/16 15:17:33 itojun Exp
 .\"
 .Dd May 17, 1998
 .Dt NDP 8
@@ -63,6 +63,7 @@
 .Nm ndp
 .Fl i
 .Ar interface
+.Op Ar flags...
 .Nm ndp
 .Fl p
 .Nm ndp
@@ -105,18 +106,33 @@ Harmonize consistency between the routing table and the default router
 list; install the top entry of the list into the kernel routing table.
 .It Fl I Op delete \(ba Ar interface
 Shows or specifies the default interface used as the default route when
-there is no default router.
-If no argument is given to the option,
-the current deafult interface will be shown.
+there is no default router. If no argument is given to the option,
+the current default interface will be shown.
 If an
 .Ar interface
 is specified, the interface will be used as the default.
 If a special keyword
 .Ic delete
 is specified, the current default interface will be deleted from the kernel.
-.It Fl i
-.Ar interface
-view ND information for specified interface.
+.It Fl i Ar interface Op Ar flags...
+View ND information for the specified interface.
+If additional arguments
+.Ar flags
+are given,
+.Nm
+sets or clears the specified flags for the interface.
+Possible flags are as follows. All of the flags can begin with the
+special character
+.Ql - ,
+which means the flag should be cleared.
+.\"
+.Bl -tag -width Ds -compact
+.It Xo
+.Ic nud
+.Xc
+turn on or off NUD (Neighbor Unreachability Detection) on the
+interface. NUD is usually turned on by default.
+.El
 .It Fl l
 Do not truncate numeric IPv6 address.
 .It Fl n
index e02383d..cdf24ff 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ndp.c,v 1.4 2000/02/28 11:56:41 itojun Exp $  */
+/*     $OpenBSD: ndp.c,v 1.5 2000/04/17 04:44:51 itojun Exp $  */
 
 /*
  * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project.
@@ -150,7 +150,7 @@ static char *ether_str __P((struct sockaddr_dl *));
 int ndp_ether_aton __P((char *, u_char *));
 void usage __P((void));
 int rtmsg __P((int));
-void ifinfo __P((char *));
+void ifinfo __P((int, char **));
 void rtrlist __P((void));
 void plist __P((void));
 void pfx_flush __P((void));
@@ -200,9 +200,11 @@ main(argc, argv)
                        /*NOTREACHED*/
 #endif
                case 'i' :
-                       if (argc != 3)
+                       argc -= optind;
+                       argv += optind;
+                       if (argc < 1)
                                usage();
-                       ifinfo(argv[2]);
+                       ifinfo(argc, argv);
                        exit(0);
                case 'n':
                        nflag = 1;
@@ -484,12 +486,16 @@ delete(host)
        if (IN6_ARE_ADDR_EQUAL(&sin->sin6_addr, &sin_m.sin6_addr)) {
                if (sdl->sdl_family == AF_LINK &&
                    (rtm->rtm_flags & RTF_LLINFO) &&
-                   !(rtm->rtm_flags & RTF_GATEWAY)) switch (sdl->sdl_type) {
-               case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023:
-               case IFT_ISO88024: case IFT_ISO88025:
-                       goto delete;
+                   !(rtm->rtm_flags & RTF_GATEWAY)) {
+                       switch (sdl->sdl_type) {
+                       case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023:
+                       case IFT_ISO88024: case IFT_ISO88025:
+                               goto delete;
+                       }
                }
        }
+       return 0;
+
 delete:
        if (sdl->sdl_family != AF_LINK) {
                printf("cannot locate %s\n", host);
@@ -692,7 +698,7 @@ getnbrinfo(addr, ifindex, warning)
        nbi.addr = *addr;
        if (ioctl(s, SIOCGNBRINFO_IN6, (caddr_t)&nbi) < 0) {
                if (warning)
-                       warn("ioctl");
+                       warn("ioctl(SIOCGNBRINFO_IN6)");
                close(s);
                return(NULL);
        }
@@ -747,7 +753,7 @@ usage()
        printf("       ndp -c[nt]\n");
        printf("       ndp -d[nt] hostname\n");
        printf("       ndp -f[nt] filename\n");
-       printf("       ndp -i interface\n");
+       printf("       ndp -i interface [flags...]\n");
 #ifdef SIOCSDEFIFACE_IN6
        printf("       ndp -I [interface|delete]\n");
 #endif
@@ -824,11 +830,14 @@ doit:
 }
 
 void
-ifinfo(ifname)
-       char *ifname;
+ifinfo(argc, argv)
+       int argc;
+       char **argv;
 {
        struct in6_ndireq nd;
-       int s;
+       int i, s;
+       char *ifname = argv[0];
+       u_int32_t newflags;
 
        if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
                perror("ndp: socket");
@@ -841,13 +850,49 @@ ifinfo(ifname)
                exit(1);
        }
 #define ND nd.ndi
+       newflags = ND.flags;
+       for (i = 1; i < argc; i++) {
+               int clear = 0;
+               char *cp = argv[i];
+
+               if (*cp == '-') {
+                       clear = 1;
+                       cp++;
+               }
+
+#define SETFLAG(s, f) \
+       do {\
+               if (strcmp(cp, (s)) == 0) {\
+                       if (clear)\
+                               newflags &= ~(f);\
+                       else\
+                               newflags |= (f);\
+               }\
+       } while (0)
+               SETFLAG("nud", ND6_IFF_PERFORMNUD);
+
+               ND.flags = newflags;
+               if (ioctl(s, SIOCSIFINFO_FLAGS, (caddr_t)&nd) < 0) {
+                       perror("ioctl(SIOCSIFINFO_FLAGS)");
+                       exit(1);
+               }
+#undef SETFLAG
+       }
+
        printf("linkmtu=%d", ND.linkmtu);
        printf(", curhlim=%d", ND.chlim);
        printf(", basereachable=%ds%dms",
               ND.basereachable / 1000, ND.basereachable % 1000);
        printf(", reachable=%ds", ND.reachable);
-       printf(", retrans=%ds%dms\n", ND.retrans / 1000, ND.retrans % 1000);
+       printf(", retrans=%ds%dms", ND.retrans / 1000, ND.retrans % 1000);
+       if (ND.flags) {
+               printf("\nFlags: ");
+               if ((ND.flags & ND6_IFF_PERFORMNUD) != 0)
+                       printf("PERFORMNUD ");
+       }
+       putc('\n', stdout);
 #undef ND
+       
        close(s);
 }