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