Source-Changes-HG archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
[src/trunk]: src/lib/libc/net Implement the address selection policy; from Fr...
details: https://anonhg.NetBSD.org/src/rev/9134df736e84
branches: trunk
changeset: 342213:9134df736e84
user: christos <christos%NetBSD.org@localhost>
date: Sun Dec 13 02:02:59 2015 +0000
description:
Implement the address selection policy; from FreeBSD
diffstat:
lib/libc/net/getaddrinfo.c | 525 ++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 522 insertions(+), 3 deletions(-)
diffs (truncated from 601 to 300 lines):
diff -r 83efe801b433 -r 9134df736e84 lib/libc/net/getaddrinfo.c
--- a/lib/libc/net/getaddrinfo.c Sat Dec 12 23:55:42 2015 +0000
+++ b/lib/libc/net/getaddrinfo.c Sun Dec 13 02:02:59 2015 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: getaddrinfo.c,v 1.111 2015/12/02 18:09:53 christos Exp $ */
+/* $NetBSD: getaddrinfo.c,v 1.112 2015/12/13 02:02:59 christos Exp $ */
/* $KAME: getaddrinfo.c,v 1.29 2000/08/31 17:26:57 itojun Exp $ */
/*
@@ -55,7 +55,7 @@
#include <sys/cdefs.h>
#if defined(LIBC_SCCS) && !defined(lint)
-__RCSID("$NetBSD: getaddrinfo.c,v 1.111 2015/12/02 18:09:53 christos Exp $");
+__RCSID("$NetBSD: getaddrinfo.c,v 1.112 2015/12/13 02:02:59 christos Exp $");
#endif /* LIBC_SCCS and not lint */
#ifndef RUMP_ACTION
@@ -64,8 +64,11 @@
#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/sysctl.h>
#include <net/if.h>
#include <netinet/in.h>
+#include <netinet6/in6_var.h>
#include <arpa/inet.h>
#include <arpa/nameser.h>
#include <assert.h>
@@ -117,6 +120,14 @@
};
#endif
+struct policyqueue {
+ TAILQ_ENTRY(policyqueue) pc_entry;
+#ifdef INET6
+ struct in6_addrpolicy pc_policy;
+#endif
+};
+TAILQ_HEAD(policyhead, policyqueue);
+
static const struct afd {
int a_af;
int a_addrlen;
@@ -174,6 +185,23 @@
#define PTON_MAX 4
#endif
+#define AIO_SRCFLAG_DEPRECATED 0x1
+
+struct ai_order {
+ union {
+ struct sockaddr_storage aiou_ss;
+ struct sockaddr aiou_sa;
+ } aio_src_un;
+#define aio_srcsa aio_src_un.aiou_sa
+ u_int32_t aio_srcflag;
+ int aio_srcscope;
+ int aio_dstscope;
+ struct policyqueue *aio_srcpolicy;
+ struct policyqueue *aio_dstpolicy;
+ struct addrinfo *aio_ai;
+ int aio_matchlen;
+};
+
static const ns_src default_dns_files[] = {
{ NSSRC_FILES, NS_SUCCESS },
{ NSSRC_DNS, NS_SUCCESS },
@@ -222,9 +250,20 @@
struct servent_data *);
static const struct afd *find_afd(int);
static int addrconfig(uint64_t *);
+static void set_source(struct ai_order *, struct policyhead *,
+ struct servent_data *);
+static int comp_dst(const void *, const void *);
#ifdef INET6
static int ip6_str2scopeid(char *, struct sockaddr_in6 *, u_int32_t *);
#endif
+static int gai_addr2scopetype(struct sockaddr *);
+
+static int reorder(struct addrinfo *, struct servent_data *);
+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(res_state, const querybuf *, int,
const char *, int, const struct addrinfo *);
@@ -421,6 +460,7 @@
const struct explore *ex;
struct servent_data svd;
uint64_t mask = (uint64_t)~0ULL;
+ int numeric = 0;
/* hostname is allowed to be NULL */
/* servname is allowed to be NULL */
@@ -557,8 +597,10 @@
* If numeric representation of AF1 can be interpreted as FQDN
* representation of AF2, we need to think again about the code below.
*/
- if (sentinel.ai_next)
+ if (sentinel.ai_next) {
+ numeric = 1;
goto good;
+ }
if (hostname == NULL)
ERR(EAI_NODATA);
@@ -614,6 +656,31 @@
if (sentinel.ai_next) {
good:
+ /*
+ * If the returned entry is for an active connection,
+ * and the given name is not numeric, reorder the
+ * list, so that the application would try the list
+ * in the most efficient order. Since the head entry
+ * of the original list may contain ai_canonname and
+ * that entry may be moved elsewhere in the new list,
+ * we keep the pointer and will restore it in the new
+ * head entry. (Note that RFC3493 requires the head
+ * entry store it when requested by the caller).
+ */
+ if (hints == NULL || !(hints->ai_flags & AI_PASSIVE)) {
+ if (!numeric) {
+ char *canonname;
+
+ canonname = sentinel.ai_next->ai_canonname;
+ sentinel.ai_next->ai_canonname = NULL;
+ (void)reorder(&sentinel, &svd);
+ if (sentinel.ai_next->ai_canonname == NULL) {
+ sentinel.ai_next->ai_canonname
+ = canonname;
+ } else if (canonname != NULL)
+ free(canonname);
+ }
+ }
endservent_r(&svd);
*res = sentinel.ai_next;
return SUCCESS;
@@ -628,6 +695,458 @@
return error;
}
+static int
+reorder(struct addrinfo *sentinel, struct servent_data *svd)
+{
+ struct addrinfo *ai, **aip;
+ struct ai_order *aio;
+ int i, n;
+ struct policyhead policyhead;
+
+ /* count the number of addrinfo elements for sorting. */
+ for (n = 0, ai = sentinel->ai_next; ai != NULL; ai = ai->ai_next, n++)
+ ;
+
+ /*
+ * If the number is small enough, we can skip the reordering process.
+ */
+ if (n <= 1)
+ return(n);
+
+ /* allocate a temporary array for sort and initialization of it. */
+ if ((aio = malloc(sizeof(*aio) * n)) == NULL)
+ return(n); /* give up reordering */
+ memset(aio, 0, sizeof(*aio) * n);
+
+ /* retrieve address selection policy from the kernel */
+ TAILQ_INIT(&policyhead);
+ if (!get_addrselectpolicy(&policyhead)) {
+ /* no policy is installed into kernel, we don't sort. */
+ free(aio);
+ return (n);
+ }
+
+ for (i = 0, ai = sentinel->ai_next; i < n; ai = ai->ai_next, i++) {
+ aio[i].aio_ai = ai;
+ aio[i].aio_dstscope = gai_addr2scopetype(ai->ai_addr);
+ aio[i].aio_dstpolicy = match_addrselectpolicy(ai->ai_addr,
+ &policyhead);
+ set_source(&aio[i], &policyhead, svd);
+ }
+
+ /* perform sorting. */
+ qsort(aio, n, sizeof(*aio), comp_dst);
+
+ /* reorder the addrinfo chain. */
+ for (i = 0, aip = &sentinel->ai_next; i < n; i++) {
+ *aip = aio[i].aio_ai;
+ aip = &aio[i].aio_ai->ai_next;
+ }
+ *aip = NULL;
+
+ /* cleanup and return */
+ free(aio);
+ free_addrselectpolicy(&policyhead);
+ return(n);
+}
+
+static int
+get_addrselectpolicy(struct policyhead *head)
+{
+#ifdef INET6
+ int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, IPV6CTL_ADDRCTLPOLICY };
+ size_t l;
+ char *buf;
+ struct in6_addrpolicy *pol, *ep;
+
+ if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &l, NULL, 0) < 0)
+ return (0);
+ if (l == 0)
+ return (0);
+ if ((buf = malloc(l)) == NULL)
+ return (0);
+ if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), buf, &l, NULL, 0) < 0) {
+ free(buf);
+ return (0);
+ }
+
+ ep = (struct in6_addrpolicy *)(buf + l);
+ for (pol = (struct in6_addrpolicy *)buf; pol + 1 <= ep; pol++) {
+ struct policyqueue *new;
+
+ if ((new = malloc(sizeof(*new))) == NULL) {
+ free_addrselectpolicy(head); /* make the list empty */
+ break;
+ }
+ new->pc_policy = *pol;
+ TAILQ_INSERT_TAIL(head, new, pc_entry);
+ }
+
+ free(buf);
+ return (1);
+#else
+ return (0);
+#endif
+}
+
+static void
+free_addrselectpolicy(struct policyhead *head)
+{
+ struct policyqueue *ent, *nent;
+
+ for (ent = TAILQ_FIRST(head); ent; ent = nent) {
+ nent = TAILQ_NEXT(ent, pc_entry);
+ TAILQ_REMOVE(head, ent, pc_entry);
+ free(ent);
+ }
+}
+
+static struct policyqueue *
+match_addrselectpolicy(struct sockaddr *addr, struct policyhead *head)
+{
+#ifdef INET6
+ struct policyqueue *ent, *bestent = NULL;
+ struct in6_addrpolicy *pol;
+ int matchlen, bestmatchlen = -1;
+ u_char *mp, *ep, *k, *p, m;
+ struct sockaddr_in6 key;
+
+ switch(addr->sa_family) {
+ case AF_INET6:
+ key = *(struct sockaddr_in6 *)addr;
+ break;
+ case AF_INET:
+ /* convert the address into IPv4-mapped IPv6 address. */
+ memset(&key, 0, sizeof(key));
+ key.sin6_family = AF_INET6;
+ key.sin6_len = sizeof(key);
+ key.sin6_addr.s6_addr[10] = 0xff;
+ key.sin6_addr.s6_addr[11] = 0xff;
+ memcpy(&key.sin6_addr.s6_addr[12],
+ &((struct sockaddr_in *)addr)->sin_addr, 4);
+ break;
+ default:
+ return(NULL);
+ }
+
+ for (ent = TAILQ_FIRST(head); ent; ent = TAILQ_NEXT(ent, pc_entry)) {
+ pol = &ent->pc_policy;
+ matchlen = 0;
+
+ mp = (u_char *)&pol->addrmask.sin6_addr;
+ ep = mp + 16; /* XXX: scope field? */
+ k = (u_char *)&key.sin6_addr;
+ p = (u_char *)&pol->addr.sin6_addr;
+ for (; mp < ep && *mp; mp++, k++, p++) {
+ m = *mp;
+ if ((*k & m) != *p)
+ goto next; /* not match */
+ if (m == 0xff) /* short cut for a typical case */
+ matchlen += 8;
+ else {
+ while (m >= 0x80) {
+ matchlen++;
+ m <<= 1;
+ }
+ }
Home |
Main Index |
Thread Index |
Old Index