diff options
author | Jun-ichiro itojun Hagino <itojun@cvs.openbsd.org> | 2000-04-17 04:44:52 +0000 |
---|---|---|
committer | Jun-ichiro itojun Hagino <itojun@cvs.openbsd.org> | 2000-04-17 04:44:52 +0000 |
commit | 21c2e6c965d401f6359663687069efbf69376e00 (patch) | |
tree | 324898ea862ad4e4badb4620d5d2e6159627b0f0 | |
parent | fe45f154d1f286c742242fe504ccad68e20378f9 (diff) |
revisit in6_ifattach(). (1) make it more persistent about initializaing an
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)
-rw-r--r-- | sys/netinet6/in6.c | 84 | ||||
-rw-r--r-- | sys/netinet6/in6_ifattach.c | 942 | ||||
-rw-r--r-- | sys/netinet6/in6_ifattach.h | 19 | ||||
-rw-r--r-- | sys/netinet6/in6_var.h | 14 | ||||
-rw-r--r-- | sys/netinet6/ip6_input.c | 8 | ||||
-rw-r--r-- | sys/netinet6/nd6.c | 119 | ||||
-rw-r--r-- | sys/netinet6/nd6.h | 11 | ||||
-rw-r--r-- | usr.sbin/ndp/ndp.8 | 32 | ||||
-rw-r--r-- | usr.sbin/ndp/ndp.c | 73 |
9 files changed, 747 insertions, 555 deletions
diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c index 5e1930cb4e9..44dffbf33cf 100644 --- a/sys/netinet6/in6.c +++ b/sys/netinet6/in6.c @@ -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. @@ -82,11 +81,9 @@ #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; diff --git a/sys/netinet6/in6_ifattach.c b/sys/netinet6/in6_ifattach.c index 8a981dd412c..e2d7111ffe7 100644 --- a/sys/netinet6/in6_ifattach.c +++ b/sys/netinet6/in6_ifattach.c @@ -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 @@ -55,225 +55,536 @@ #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)); diff --git a/sys/netinet6/in6_ifattach.h b/sys/netinet6/in6_ifattach.h index d7023e7bfd4..6985bc9be33 100644 --- a/sys/netinet6/in6_ifattach.h +++ b/sys/netinet6/in6_ifattach.h @@ -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 @@ -33,18 +34,8 @@ #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_ */ diff --git a/sys/netinet6/in6_var.h b/sys/netinet6/in6_var.h index 20e3637da9b..c76b625ad4a 100644 --- a/sys/netinet6/in6_var.h +++ b/sys/netinet6/in6_var.h @@ -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 */ diff --git a/sys/netinet6/ip6_input.c b/sys/netinet6/ip6_input.c index e39d5513e0b..94370a45385 100644 --- a/sys/netinet6/ip6_input.c +++ b/sys/netinet6/ip6_input.c @@ -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 diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c index 30f7120001a..ac3bfb845d0 100644 --- a/sys/netinet6/nd6.c +++ b/sys/netinet6/nd6.c @@ -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); diff --git a/sys/netinet6/nd6.h b/sys/netinet6/nd6.h index 1051468ccff..71ff81a8ce2 100644 --- a/sys/netinet6/nd6.h +++ b/sys/netinet6/nd6.h @@ -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 */ diff --git a/usr.sbin/ndp/ndp.8 b/usr.sbin/ndp/ndp.8 index 70282196a97..3ffcf015ec4 100644 --- a/usr.sbin/ndp/ndp.8 +++ b/usr.sbin/ndp/ndp.8 @@ -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 diff --git a/usr.sbin/ndp/ndp.c b/usr.sbin/ndp/ndp.c index e02383d527a..cdf24ffc974 100644 --- a/usr.sbin/ndp/ndp.c +++ b/usr.sbin/ndp/ndp.c @@ -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); } |