-/* $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:
* 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.
* 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;
}
/*
- * 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
*/
}
/*
- * 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);
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;
/* 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;
}
/*
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);
}
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));
-/* $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:
* 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 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
}
* 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);
}
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;
ln = next;
continue;
}
+ ndi = &nd_ifinfo[ifp->if_index];
dst = (struct sockaddr_in6 *)rt_key(rt);
if (ln->ln_expire > time_second) {
/* 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);
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++;
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.
*/
}
if (!rt) {
if (create && ifp) {
+ int e;
+
/*
* If no route is available and create is set,
* we allocate a host route for the destination
* 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) {
/*
* 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)
#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
*/
ln->ln_state = ND6_LLINFO_REACHABLE;
} else {
- /*
+ /*
* When req == RTM_RESOLVE, rt is created and
* initialized in rtrequest(), so rt_expire is 0.
*/
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 */
/*
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) {
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;
/*
* 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) {
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) {
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
sendpkt:
return((*ifp->if_output)(ifp, m, (struct sockaddr *)dst, rt));
-
+
bad:
if (m)
m_freem(m);