From bedda58490fce9fd0b6a091d0dbfcc01884fc9fe Mon Sep 17 00:00:00 2001 From: Wouter Wijngaards Date: Fri, 2 Feb 2007 09:48:51 +0000 Subject: [PATCH] udp4 and udp6. git-svn-id: file:///svn/unbound/trunk@51 be551aaa-1e26-0410-a405-d3ace91eadb9 --- doc/Changelog | 4 + services/outside_network.c | 207 +++++++++++++++++++++++++++---------- services/outside_network.h | 15 ++- 3 files changed, 169 insertions(+), 57 deletions(-) diff --git a/doc/Changelog b/doc/Changelog index 9e7a30be0..4546e1f6c 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -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. diff --git a/services/outside_network.c b/services/outside_network.c index f3b6b9486..788927e37 100644 --- a/services/outside_network.c +++ b/services/outside_network.c @@ -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; iudp_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; ibase = 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; kudp4_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; inum_udp; i++) { - if(outnet->udp_ports[i]) - comm_point_delete(outnet->udp_ports[i]); - } - free(outnet->udp_ports); + for(i=0; inum_udp4; i++) + comm_point_delete(outnet->udp4_ports[i]); + free(outnet->udp4_ports); + } + if(outnet->udp6_ports) { + size_t i; + for(i=0; inum_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); } diff --git a/services/outside_network.h b/services/outside_network.h index b20f9eaad..59136072d 100644 --- a/services/outside_network.h +++ b/services/outside_network.h @@ -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; -- 2.47.2