]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
udp4 and udp6.
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Fri, 2 Feb 2007 09:48:51 +0000 (09:48 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Fri, 2 Feb 2007 09:48:51 +0000 (09:48 +0000)
git-svn-id: file:///svn/unbound/trunk@51 be551aaa-1e26-0410-a405-d3ace91eadb9

doc/Changelog
services/outside_network.c
services/outside_network.h

index 9e7a30be084286bca57817481862cfa3be38eb02..4546e1f6ca8ed1366a6f11f6051994144a769790 100644 (file)
@@ -1,3 +1,7 @@
+2 February 2007: Wouter
+       - Created udp4 and udp6 port arrays to provide service for both
+         address families.
+
 1 February 2007: Wouter
        - outside network more UDP work.
        - moved * closer to type.
index f3b6b94864678828bba817c8f0f7be9111411860..788927e37b0aa4c05f8003824d7cfce55859d4d5 100644 (file)
@@ -129,7 +129,7 @@ static int open_udp_port_range(const char* ifname, struct addrinfo* hints,
 
        if((r=getaddrinfo(ifname, ((porthint==-1)?NULL:portstr), hints, 
                &res)) != 0 || !res) {
-               log_err("node %s:%s getaddrinfo: %s %s",
+               log_err("node %s %s getaddrinfo: %s %s",
                        ifname?ifname:"default", (porthint!=-1)?portstr:"eph", 
                        gai_strerror(r), r==EAI_SYSTEM?strerror(errno):"");
                return -1;
@@ -141,12 +141,15 @@ static int open_udp_port_range(const char* ifname, struct addrinfo* hints,
 
 /**
  * Create range of UDP ports on the given interface.
+ * Returns number of ports bound.
  */
-static int 
-make_udp_range(struct outside_network* outnet, const char* ifname, 
-       size_t num_ports, size_t off, int do_ip4, int do_ip6, int porthint)
+static size_t 
+make_udp_range(struct comm_point** coms, const char* ifname, 
+       size_t num_ports, int do_ip4, int do_ip6, int porthint,
+       struct outside_network* outnet)
 {
        size_t i;
+       size_t done = 0;
        struct addrinfo hints;
        memset(&hints, 0, sizeof(hints));
        hints.ai_flags = AI_PASSIVE;
@@ -162,25 +165,58 @@ make_udp_range(struct outside_network* outnet, const char* ifname,
        hints.ai_socktype = SOCK_DGRAM;
        for(i=0; i<num_ports; i++) {
                int fd = open_udp_port_range(ifname, &hints, porthint);
-               if(fd == -1) {
-                       return 0;
-               }
                if(porthint != -1) 
                        porthint++;
-               outnet->udp_ports[i+off] = comm_point_create_udp(
-                       outnet->base, fd, outnet->udp_buff, outnet_udp_cb,
-                       outnet);
-               if(!outnet->udp_ports[i+off]) {
-                       return 0;
+               if(fd == -1)
+                       continue;
+               coms[done] = comm_point_create_udp(outnet->base, fd, 
+                       outnet->udp_buff, outnet_udp_cb, outnet);
+               if(coms[done])
+                       done++;
+       }
+       return done;
+}
+
+/** returns true is string addr is an ip6 specced address. */
+static int str_is_ip6(const char* str)
+{
+       if(strchr(str, ':'))
+               return 1;
+       else    return 0;
+}
+
+/** calculate number of ip4 and ip6 interfaces, times multiplier. */
+static void calc_num46(const char** ifs, int num_ifs, 
+       int do_ip4, int do_ip6, size_t multiplier, 
+       size_t* num_ip4, size_t* num_ip6)
+{
+       int i;
+       *num_ip4 = 0;
+       *num_ip6 = 0;
+       if(num_ifs <= 0) {
+               if(do_ip4)
+                       *num_ip4 = multiplier;
+               if(do_ip6)
+                       *num_ip6 = multiplier;
+               return;
+       }
+       for(i=0; i<num_ifs; i++)
+       {
+               if(str_is_ip6(ifs[i])) {
+                       if(do_ip6)
+                               *num_ip6 += multiplier;
+               } else {
+                       if(do_ip4)
+                               *num_ip4 += multiplier;
                }
        }
-       return 1;
+
 }
 
 /** callback for udp timeout */
 static void pending_udp_timer_cb(void *arg)
 {
-       struct pending* p = (struct pending*)arg;
+       /* struct pending* p = (struct pending*)arg; */
        /* it timed out . TODO handle it. */
 }
 
@@ -197,28 +233,56 @@ outside_network_create(struct comm_base *base, size_t bufsize,
                return NULL;
        }
        outnet->base = base;
-       outnet->num_udp = (num_ifs?num_ports * num_ifs:num_ports);
+       calc_num46(ifs, num_ifs, do_ip4, do_ip6, num_ports, 
+               &outnet->num_udp4, &outnet->num_udp6);
        if(     !(outnet->udp_buff = ldns_buffer_new(bufsize)) ||
-               !(outnet->udp_ports = (struct comm_point **)calloc(
-                       outnet->num_udp, sizeof(struct comm_point*))) ||
+               !(outnet->udp4_ports = (struct comm_point **)calloc(
+                       outnet->num_udp4+1, sizeof(struct comm_point*))) ||
+               !(outnet->udp6_ports = (struct comm_point **)calloc(
+                       outnet->num_udp6+1, sizeof(struct comm_point*))) ||
                !(outnet->pending = rbtree_create(pending_cmp)) ) {
                log_err("malloc failed");
                outside_network_delete(outnet);
                return NULL;
        }
+       /* open ip4 ports first, to get them apart from ip6 ports. */
        if(num_ifs == 0) {
-               if(!make_udp_range(outnet, NULL, num_ports, 0, do_ip4, 
-                       do_ip6, port_base)) {
+               if(do_ip4) {
+                       outnet->num_udp4 = make_udp_range(outnet->udp4_ports, 
+                               NULL, num_ports, 1, 0, port_base, outnet);
+               }
+               if(do_ip6) {
+                       outnet->num_udp6 = make_udp_range(outnet->udp6_ports, 
+                               NULL, num_ports, 0, 1, port_base, outnet);
+               }
+               if(outnet->num_udp4 != num_ports || 
+                       outnet->num_udp6 != num_ports) {
+                       log_err("Could not open all networkside ports");
                        outside_network_delete(outnet);
                        return NULL;
                }
        }
-       else for(k=0; k<num_ifs; k++) {
-               if(!make_udp_range(outnet, ifs[k], num_ports, k*num_ports, 
-                       do_ip4, do_ip6, port_base)) {
+       else {
+               size_t done_4 = 0, done_6 = 0;
+               for(k=0; k<num_ifs; k++) {
+                       if(!str_is_ip6(ifs[k]) && do_ip4) {
+                               done_4 += make_udp_range(
+                                       outnet->udp4_ports+done_4, ifs[k],
+                                       num_ports, 1, 0, port_base, outnet);
+                       }
+                       if(str_is_ip6(ifs[k]) && do_ip6) {
+                               done_6 += make_udp_range(
+                                       outnet->udp6_ports+done_6, ifs[k],
+                                       num_ports, 0, 1, port_base, outnet);
+                       }
+               }
+               if(done_6 != outnet->num_udp6 || done_4 != outnet->num_udp4) {
+                       log_err("Could not open all ports on all interfaces");
                        outside_network_delete(outnet);
                        return NULL;
                }
+               outnet->num_udp6 = done_6;
+               outnet->num_udp4 = done_4;
        }
        return outnet;
 }
@@ -240,13 +304,17 @@ void outside_network_delete(struct outside_network* outnet)
        }
        if(outnet->udp_buff)
                ldns_buffer_free(outnet->udp_buff);
-       if(outnet->udp_ports) {
+       if(outnet->udp4_ports) {
                size_t i;
-               for(i=0; i<outnet->num_udp; i++) {
-                       if(outnet->udp_ports[i])
-                               comm_point_delete(outnet->udp_ports[i]);
-               }
-               free(outnet->udp_ports);
+               for(i=0; i<outnet->num_udp4; i++)
+                       comm_point_delete(outnet->udp4_ports[i]);
+               free(outnet->udp4_ports);
+       }
+       if(outnet->udp6_ports) {
+               size_t i;
+               for(i=0; i<outnet->num_udp6; i++)
+                       comm_point_delete(outnet->udp6_ports[i]);
+               free(outnet->udp6_ports);
        }
        free(outnet);
 }
@@ -266,12 +334,10 @@ void pending_delete(struct outside_network* outnet, struct pending* p)
 /** create a new pending item with given characteristics, false on failure */
 static struct pending*
 new_pending(struct outside_network* outnet, ldns_buffer* packet, 
-       struct sockaddr_storage* addr, socklen_t addrlen, int timeout,
-       struct comm_point* c)
+       struct sockaddr_storage* addr, socklen_t addrlen)
 {
        /* alloc */
        int id_tries = 0;
-       struct timeval tv;
        struct pending* pend = (struct pending*)calloc(1, 
                sizeof(struct pending));
        if(!pend) {
@@ -288,7 +354,6 @@ new_pending(struct outside_network* outnet, ldns_buffer* packet,
        pend->id = LDNS_ID_WIRE(ldns_buffer_begin(packet));
        memcpy(&pend->addr, addr, addrlen);
        pend->addrlen = addrlen;
-       pend->c = c;
 
        /* insert in tree */
        pend->node.key = pend;
@@ -303,45 +368,81 @@ new_pending(struct outside_network* outnet, ldns_buffer* packet,
                        return NULL;
                }
        }
-       tv.tv_sec = (int)time(NULL) + timeout;
-       tv.tv_usec = 0;
-       comm_timer_set(pend->timer, &tv);
        return pend;
 }
 
+/** return true if sockaddr is ip6 */
+static int addr_is_ip6(struct sockaddr_storage* addr)
+{
+       short family = *(short*)addr;
+       if(family == AF_INET6)
+               return 1;
+       else    return 0;
+}
 
-void pending_udp_query(struct outside_network* outnet, ldns_buffer* packet, 
-       struct sockaddr_storage* addr, socklen_t addrlen, int timeout)
+/** select outgoing comm point for a query. Fills in c. */
+static void select_port(struct outside_network* outnet, struct pending* pend)
 {
-       struct pending* pend;
+       double precho;
+       int chosen, nummax;
+
+       log_assert(outnet && pend);
+       /* first select ip4 or ip6. */
+       if(addr_is_ip6(&pend->addr))
+               nummax = (int)outnet->num_udp6;
+       else    nummax = (int)outnet->num_udp4;
+
+       if(nummax == 0) {
+               /* could try ip4to6 mapping if no ip4 ports available */
+               log_err("Need to send query but have no ports of that family");
+               return;
+       }
+
        /* choose a random outgoing port and interface */
        /* uses lousy random() function. TODO: entropy source. */
-       double precho = (double)random() * (double)outnet->num_udp / 
+       precho = (double)random() * (double)nummax / 
                ((double)RAND_MAX + 1.0);
-       int chosen = (int)precho;
-       struct comm_point *c;
-       log_assert(outnet && outnet->udp_ports);
+       chosen = (int)precho;
+
        /* don't trust in perfect double rounding */
        if(chosen < 0) chosen = 0;
-       if(chosen >= (int)outnet->num_udp) chosen = (int)outnet->num_udp-1;
-       c = outnet->udp_ports[chosen];
-       log_assert(c);
-       
+       if(chosen >= nummax) chosen = nummax-1;
+
+       if(addr_is_ip6(&pend->addr))
+               pend->c = outnet->udp6_ports[chosen];
+       else    pend->c = outnet->udp4_ports[chosen];
+       log_assert(pend->c);
+
+       log_info("chose query %x outbound %d of %d", 
+               pend->id, chosen, nummax);
+}
+
+
+void pending_udp_query(struct outside_network* outnet, ldns_buffer* packet, 
+       struct sockaddr_storage* addr, socklen_t addrlen, int timeout)
+{
+       struct pending* pend;
+       struct timeval tv;
+
        /* create pending struct (and possibly change ID to be unique) */
-       if(!(pend=new_pending(outnet, packet, addr, addrlen, timeout, c))) {
-               (void)(*c->callback)(c, c->cb_arg, 1, NULL);
+       if(!(pend=new_pending(outnet, packet, addr, addrlen))) {
+               /* callback user for the error */
                return;
        }
-       log_info("chose query %x outbound %d of %d", 
-               LDNS_ID_WIRE(ldns_buffer_begin(packet)), chosen, 
-               outnet->num_udp);
+       select_port(outnet, pend);
 
        /* send it over the commlink */
-       if(!comm_point_send_udp_msg(c, packet, (struct sockaddr*)addr, 
+       if(!comm_point_send_udp_msg(pend->c, packet, (struct sockaddr*)addr, 
                addrlen)) {
                /* error, call error callback function */
                pending_delete(outnet, pend);
-               (void)(*c->callback)(c, c->cb_arg, 1, NULL);
+               /* callback user for the error */
                return;
        }
+
+       /* system calls to set timeout after sending UDP to make roundtrip
+          smaller. */
+       tv.tv_sec = (int)time(NULL) + timeout;
+       tv.tv_usec = 0;
+       comm_timer_set(pend->timer, &tv);
 }
index b20f9eaad6c79c2688f2c07294010c79df69fa04..59136072d14e2acd009c6115b85904073aa9f70b 100644 (file)
@@ -64,11 +64,18 @@ struct outside_network {
 
        /** 
         * Array of udp comm point* that are used to listen to pending events.
-        * Each is on a different port.
+        * Each is on a different port. This is for ip4 ports.
         */
-       struct comm_point **udp_ports;
-       /** number of udp ports */
-       size_t num_udp;
+       struct comm_point **udp4_ports;
+       /** number of udp4 ports */
+       size_t num_udp4;
+
+       /**
+        * The opened ip6 ports.
+        */
+       struct comm_point **udp6_ports;
+       /** number of udp6 ports */
+       size_t num_udp6;
 
        /** pending answers. sorted by id, addr */
        rbtree_t *pending;