diff options
author | Hajimu UMEMOTO <ume@FreeBSD.org> | 2004-05-31 19:27:54 +0000 |
---|---|---|
committer | Hajimu UMEMOTO <ume@FreeBSD.org> | 2004-05-31 19:27:54 +0000 |
commit | 4d489f472bd9574d8ab26a1a24c81d44634155e2 (patch) | |
tree | 11d2a991f2fab529f38a5136ef446c0cd171e50d /lib/libc/net | |
parent | 69af1dccdc4af33964b8ae64befdfc9b66aac084 (diff) | |
download | src-4d489f472bd9574d8ab26a1a24c81d44634155e2.tar.gz src-4d489f472bd9574d8ab26a1a24c81d44634155e2.zip |
use source address as a hint to determine destination address.
Obtained from: KAME
Notes
Notes:
svn path=/head/; revision=129901
Diffstat (limited to 'lib/libc/net')
-rw-r--r-- | lib/libc/net/getaddrinfo.c | 111 |
1 files changed, 111 insertions, 0 deletions
diff --git a/lib/libc/net/getaddrinfo.c b/lib/libc/net/getaddrinfo.c index dc21cc231f04..6c4ba51ad57a 100644 --- a/lib/libc/net/getaddrinfo.c +++ b/lib/libc/net/getaddrinfo.c @@ -75,6 +75,7 @@ __FBSDID("$FreeBSD$"); #ifdef INET6 #include <net/if_var.h> #include <sys/sysctl.h> +#include <sys/ioctl.h> #include <netinet6/in6_var.h> /* XXX */ #endif #include <arpa/inet.h> @@ -247,6 +248,7 @@ static int get_portmatch(const struct addrinfo *, const char *); static int get_port(struct addrinfo *, const char *, int); static const struct afd *find_afd(int); static int addrconfig(struct addrinfo *); +static void set_source(struct ai_order *, struct policyhead *); static int comp_dst(const void *, const void *); #ifdef INET6 static int ip6_str2scopeid(char *, struct sockaddr_in6 *, u_int32_t *); @@ -261,6 +263,7 @@ static int get_addrselectpolicy(struct policyhead *); static void free_addrselectpolicy(struct policyhead *); static struct policyqueue *match_addrselectpolicy(struct sockaddr *, struct policyhead *); +static int matchlen(struct sockaddr *, struct sockaddr *); static struct addrinfo *getanswer(const querybuf *, int, const char *, int, const struct addrinfo *); @@ -677,6 +680,7 @@ reorder(sentinel) aio[i].aio_dstscope = gai_addr2scopetype(ai->ai_addr); aio[i].aio_dstpolicy = match_addrselectpolicy(ai->ai_addr, &policyhead); + set_source(&aio[i], &policyhead); } /* perform sorting. */ @@ -815,6 +819,113 @@ match_addrselectpolicy(addr, head) } +static void +set_source(aio, ph) + struct ai_order *aio; + struct policyhead *ph; +{ + struct addrinfo ai = *aio->aio_ai; + struct sockaddr_storage ss; + int s, srclen; + + /* set unspec ("no source is available"), just in case */ + aio->aio_srcsa.sa_family = AF_UNSPEC; + aio->aio_srcscope = -1; + + switch(ai.ai_family) { + case AF_INET: +#ifdef INET6 + case AF_INET6: +#endif + break; + default: /* ignore unsupported AFs explicitly */ + return; + } + + /* XXX: make a dummy addrinfo to call connect() */ + ai.ai_socktype = SOCK_DGRAM; + ai.ai_protocol = IPPROTO_UDP; /* is UDP too specific? */ + ai.ai_next = NULL; + memset(&ss, 0, sizeof(ss)); + memcpy(&ss, ai.ai_addr, ai.ai_addrlen); + ai.ai_addr = (struct sockaddr *)&ss; + get_port(&ai, "1", 0); + + /* open a socket to get the source address for the given dst */ + if ((s = _socket(ai.ai_family, ai.ai_socktype, ai.ai_protocol)) < 0) + return; /* give up */ + if (_connect(s, ai.ai_addr, ai.ai_addrlen) < 0) + goto cleanup; + srclen = ai.ai_addrlen; + if (_getsockname(s, &aio->aio_srcsa, &srclen) < 0) { + aio->aio_srcsa.sa_family = AF_UNSPEC; + goto cleanup; + } + aio->aio_srcscope = gai_addr2scopetype(&aio->aio_srcsa); + aio->aio_srcpolicy = match_addrselectpolicy(&aio->aio_srcsa, ph); + aio->aio_matchlen = matchlen(&aio->aio_srcsa, aio->aio_ai->ai_addr); +#ifdef INET6 + if (ai.ai_family == AF_INET6) { + struct in6_ifreq ifr6; + u_int32_t flags6; + + /* XXX: interface name should not be hardcoded */ + strncpy(ifr6.ifr_name, "lo0", sizeof(ifr6.ifr_name)); + memset(&ifr6, 0, sizeof(ifr6)); + memcpy(&ifr6.ifr_addr, ai.ai_addr, ai.ai_addrlen); + if (_ioctl(s, SIOCGIFAFLAG_IN6, &ifr6) == 0) { + flags6 = ifr6.ifr_ifru.ifru_flags6; + if ((flags6 & IN6_IFF_DEPRECATED)) + aio->aio_srcflag |= AIO_SRCFLAG_DEPRECATED; + } + } +#endif + + cleanup: + _close(s); + return; +} + +static int +matchlen(src, dst) + struct sockaddr *src, *dst; +{ + int match = 0; + u_char *s, *d; + u_char *lim, r; + int addrlen; + + switch (src->sa_family) { +#ifdef INET6 + case AF_INET6: + s = (u_char *)&((struct sockaddr_in6 *)src)->sin6_addr; + d = (u_char *)&((struct sockaddr_in6 *)dst)->sin6_addr; + addrlen = sizeof(struct in6_addr); + lim = s + addrlen; + break; +#endif + case AF_INET: + s = (u_char *)&((struct sockaddr_in6 *)src)->sin6_addr; + d = (u_char *)&((struct sockaddr_in6 *)dst)->sin6_addr; + addrlen = sizeof(struct in_addr); + lim = s + addrlen; + break; + default: + return(0); + } + + while (s < lim) + if ((r = (*d++ ^ *s++)) != 0) { + while (r < addrlen * 8) { + match++; + r <<= 1; + } + break; + } else + match += 8; + return(match); +} + static int comp_dst(arg1, arg2) const void *arg1, *arg2; |