📄 in6_ifattach.c
字号:
//==========================================================================
//
// src/sys/netinet6/in6_ifattach.c
//
//==========================================================================
//####BSDCOPYRIGHTBEGIN####
//
// -------------------------------------------
//
// Portions of this software may have been derived from OpenBSD,
// FreeBSD or other sources, and are covered by the appropriate
// copyright disclaimers included herein.
//
// Portions created by Red Hat are
// Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
//
// -------------------------------------------
//
//####BSDCOPYRIGHTEND####
//==========================================================================
/* $KAME: in6_ifattach.c,v 1.149 2001/12/07 07:07:09 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:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 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
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/malloc.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#ifdef __bsdi__
#include <crypto/md5.h>
#elif defined(__OpenBSD__)
#include <sys/md5k.h>
#else
#include <sys/md5.h>
#endif
#ifdef __OpenBSD__
#include <dev/rndvar.h>
#endif
#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/in_var.h>
#ifndef __NetBSD__
#include <netinet/if_ether.h>
#endif
#if (defined(__FreeBSD__) && __FreeBSD__ >= 4)
#include <netinet/in_pcb.h>
#endif
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
#include <netinet6/in6_var.h>
#if (defined(__FreeBSD__) && __FreeBSD__ >= 4)
#include <netinet6/in6_pcb.h>
#endif
#include <netinet6/in6_ifattach.h>
#include <netinet6/ip6_var.h>
#include <netinet6/nd6.h>
#include <netinet6/scope6_var.h>
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;
#ifdef IP6_AUTO_LINKLOCAL
int ip6_auto_linklocal = IP6_AUTO_LINKLOCAL;
#else
int ip6_auto_linklocal = 1; /* enable by default */
#endif
#ifdef __NetBSD__
struct callout in6_tmpaddrtimer_ch = CALLOUT_INITIALIZER;
#elif (defined(__FreeBSD__) && __FreeBSD__ >= 3)
struct callout in6_tmpaddrtimer_ch;
#elif defined(__OpenBSD__)
struct timeout in6_tmpaddrtimer_ch;
#endif
#if (defined(__FreeBSD__) && __FreeBSD__ >= 4)
extern struct inpcbinfo udbinfo;
extern struct inpcbinfo ripcbinfo;
#endif
#if defined(__NetBSD__) || defined(__OpenBSD__)
static int get_hostid_ifid __P((struct ifnet *, struct in6_addr *));
#endif
static int get_rand_ifid __P((struct ifnet *, struct in6_addr *));
static int generate_tmp_ifid __P((u_int8_t *, const u_int8_t *, u_int8_t *));
static int get_hw_ifid __P((struct ifnet *, struct in6_addr *));
#ifndef MIP6
static int get_ifid __P((struct ifnet *, struct ifnet *, struct in6_addr *));
#endif /* !MIP6 */
static int in6_ifattach_linklocal __P((struct ifnet *, struct ifnet *));
static int in6_ifattach_loopback __P((struct ifnet *));
#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))
#define IFID_LOCAL(in6) (!EUI64_LOCAL(in6))
#define IFID_UNIVERSAL(in6) (!EUI64_UNIVERSAL(in6))
#define GEN_TEMPID_RETRY_MAX 5
#if defined(__NetBSD__) || defined(__OpenBSD__)
/*
* Generate a last-resort interface identifier from hostid.
* works only for certain architectures (like sparc).
* also, using hostid itself may constitute a privacy threat, much worse
* than MAC addresses (hostids are used for software licensing).
* maybe we should use MD5(hostid) instead.
*/
static int
get_hostid_ifid(ifp, in6)
struct ifnet *ifp;
struct in6_addr *in6; /* upper 64bits are preserved */
{
int off, len;
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 };
if (!hostid)
return -1;
/* get up to 8 bytes from the hostid field - should we get */
len = (sizeof(hostid) > 8) ? 8 : sizeof(hostid);
off = sizeof(*in6) - len;
bcopy(&hostid, &in6->s6_addr[off], len);
/* make sure we do not return anything bogus */
if (bcmp(&in6->s6_addr[8], allzero, sizeof(allzero)))
return -1;
if (bcmp(&in6->s6_addr[8], allone, sizeof(allone)))
return -1;
/* make sure to set "u" bit to local, and "g" bit to individual. */
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;
}
#endif
/*
* Generate a last-resort interface identifier, when the machine has no
* IEEE802/EUI64 address sources.
* 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
get_rand_ifid(ifp, in6)
struct ifnet *ifp;
struct in6_addr *in6; /* upper 64bits are preserved */
{
MD5_CTX ctxt;
u_int8_t digest[16];
#ifdef __FreeBSD__
int hostnamelen = strlen(hostname);
#endif
#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(ifid) */
bcopy(digest, &in6->s6_addr[8], 8);
/* make sure to set "u" bit to local, and "g" bit to individual. */
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;
}
static int
generate_tmp_ifid(seed0, seed1, ret)
u_int8_t *seed0, *ret;
const u_int8_t *seed1;
{
MD5_CTX ctxt;
u_int8_t seed[16], digest[16], nullbuf[8];
u_int32_t val32;
#ifndef __OpenBSD__
struct timeval tv;
#endif
/*
* interface ID for subnet anycast addresses.
* XXX: we assume the unicast address range that requires IDs
* in EUI-64 format.
*/
const u_int8_t anycast_id[8] = {0xfd, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0x80};
const u_int8_t isatap_id[4] = {0x00, 0x00, 0x5e, 0xfe};
int badid, retry = 0;
/* If there's no hisotry, start with a random seed. */
bzero(nullbuf, sizeof(nullbuf));
if (bcmp(nullbuf, seed0, sizeof(nullbuf)) == 0) {
int i;
for (i = 0; i < 2; i++) {
#ifndef __OpenBSD__
microtime(&tv);
val32 = random() ^ tv.tv_usec;
#else
val32 = arc4random();
#endif
bcopy(&val32, seed + sizeof(val32) * i, sizeof(val32));
}
} else {
bcopy(seed0, seed, 8);
}
/* copy the right-most 64-bits of the given address */
/* XXX assumption on the size of IFID */
bcopy(seed1, &seed[8], 8);
again:
if (0) { /* for debugging purposes only */
int i;
printf("generate_tmp_ifid: new randomized ID from: ");
for (i = 0; i < 16; i++)
printf("%02x", seed[i]);
printf(" ");
}
/* generate 16 bytes of pseudo-random value. */
bzero(&ctxt, sizeof(ctxt));
MD5Init(&ctxt);
MD5Update(&ctxt, seed, sizeof(seed));
MD5Final(digest, &ctxt);
/*
* draft-ietf-ipngwg-temp-addresses-v2-00.txt 3.2.1. (3)
* Take the left-most 64-bits of the MD5 digest and set bit 6 (the
* left-most bit is numbered 0) to zero.
*/
bcopy(digest, ret, 8);
ret[0] &= ~EUI64_UBIT;
/*
* Reject inappropriate identifiers according to
* draft-ietf-ipngwg-temp-addresses-v2-00.txt 3.2.1. (4)
* At this moment, we reject following cases:
* - all 0 identifier
* - identifiers that conflict with reserved subnet anycast addresses,
* which are defined in RFC 2526.
* - identifiers that conflict with ISATAP addresses
* - identifiers used in our own addresses
*/
badid = 0;
if (bcmp(nullbuf, ret, sizeof(nullbuf)) == 0)
badid = 1;
else if (bcmp(anycast_id, ret, 7) == 0 &&
(anycast_id[7] & ret[7]) == anycast_id[7]) {
badid = 1;
} else if (bcmp(isatap_id, ret, sizeof(isatap_id)) == 0)
badid = 1;
else {
struct in6_ifaddr *ia;
for (ia = in6_ifaddr; ia; ia = ia->ia_next) {
if (bcmp(&ia->ia_addr.sin6_addr.s6_addr[8], ret, 8)
== 0) {
badid = 1;
break;
}
}
}
/*
* In the event that an unacceptable identifier has been generated,
* restart the process, using the right-most 64 bits of the MD5 digest
* obtained in place of the history value.
*/
if (badid) {
if (0) { /* for debugging purposes only */
int i;
printf("unacceptable random ID: ");
for (i = 0; i < 16; i++)
printf("%02x", digest[i]);
printf("\n");
}
if (++retry < GEN_TEMPID_RETRY_MAX) {
bcopy(&digest[8], seed, 8);
goto again;
} else {
/*
* We're so unlucky. Give up for now, and return
* all 0 IDs to tell the caller not to make a
* temporary address.
*/
nd6log((LOG_NOTICE,
"generate_tmp_ifid: never found a good ID\n"));
bzero(ret, 8);
}
}
/*
* draft-ietf-ipngwg-temp-addresses-v2-00.txt 3.2.1. (6)
* Take the rightmost 64-bits of the MD5 digest and save them in
* stable storage as the history value to be used in the next
* iteration of the algorithm.
*/
bcopy(&digest[8], seed0, 8);
if (0) { /* for debugging purposes only */
int i;
printf("to: ");
for (i = 0; i < 16; i++)
printf("%02x", digest[i]);
printf("\n");
}
return 0;
}
/*
* Get interface identifier for the specified interface.
* XXX assumes single sockaddr_dl (AF_LINK address) per an interface
*/
static int
get_hw_ifid(ifp, in6)
struct ifnet *ifp;
struct in6_addr *in6; /* upper 64bits are preserved */
{
struct ifaddr *ifa;
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 };
#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
#else
for (ifa = ifp->if_addrlist.tqh_first;
ifa;
ifa = ifa->ifa_list.tqe_next)
#endif
{
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;
switch (ifp->if_type) {
case IFT_IEEE1394:
#ifdef IFT_IEEE80211
case IFT_IEEE80211:
#endif
/* IEEE1394 uses 16byte length address starting with EUI64 */
if (addrlen > 8)
addrlen = 8;
break;
default:
break;
}
/* get EUI64 */
switch (ifp->if_type) {
/* IEEE802/EUI64 cases - what others? */
case IFT_ETHER:
case IFT_FDDI:
case IFT_ATM:
case IFT_IEEE1394:
#ifdef IFT_IEEE80211
case IFT_IEEE80211:
#endif
/* 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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -