aboutsummaryrefslogtreecommitdiff
path: root/lib/libc/net
diff options
context:
space:
mode:
authorHajimu UMEMOTO <ume@FreeBSD.org>2004-05-31 19:27:54 +0000
committerHajimu UMEMOTO <ume@FreeBSD.org>2004-05-31 19:27:54 +0000
commit4d489f472bd9574d8ab26a1a24c81d44634155e2 (patch)
tree11d2a991f2fab529f38a5136ef446c0cd171e50d /lib/libc/net
parent69af1dccdc4af33964b8ae64befdfc9b66aac084 (diff)
downloadsrc-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.c111
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;