From 450494c4e50e6cb3d3c450ae92eda188ba381859 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Tue, 2 Sep 2014 09:05:30 +0100 Subject: [PATCH] udp: Support IPv6 (mostly) Specifically: * struct udp now contains an array of (up to three) pairs of iaddr, fd. Code which deals with the fd and addr has been updated to use loops etc. as appropriate. * The sockets are created with the right protocol family value. For AF_INET6, we set IPV6_V6ONLY. * Specifically, when transmitting, we try all appropriate sockets and compute the persistent-failure indication as required. * And a comm_addr now contains an `int ix' for udp.c's benefit; this allows udp to note in the comm_addr which socket an incoming packet was received on (which is required for logging etc.). (NB that the socket index is ignored when sending; this is so that we can continue to construct a comm_addr in the current way; it will simply show up as notionally attached to the first of the udp's interfaces.) * We use text2iaddr to convert the string to a socket address, rather than string_item_to_ipaddr. The latter can cope only with IPv4 (and is now used only for private vpn addrs, proxies, etc.). * The default is now to create both IPv6 and IPv4 sockets. Left undone are: * The special secnet proxy protocol has a 4-byte address prepended which implies IPv4. I don't intend to fix this. * The authbind support for IPv6 will be in a future patch. Signed-off-by: Ian Jackson --- README | 2 +- resolver.c | 1 + secnet.h | 1 + udp.c | 120 ++++++++++++++++++++++++++++++++++++++++++----------- 4 files changed, 99 insertions(+), 25 deletions(-) diff --git a/README b/README index 962755e..de2e6b3 100644 --- a/README +++ b/README @@ -194,7 +194,7 @@ Defines: udp (closure => comm closure) udp: dict argument - address (string): IP address to listen and send on + address (string list): IPv6 or IPv4 addresses to listen and send on port (integer): UDP port to listen and send on buffer (buffer closure): buffer for incoming packets authbind (string): optional, path to authbind-helper program diff --git a/resolver.c b/resolver.c index f7cd373..9f71716 100644 --- a/resolver.c +++ b/resolver.c @@ -44,6 +44,7 @@ static bool_t resolve_request(void *sst, cstring_t name, struct comm_addr ca; FILLZERO(ca); ca.comm=comm; + ca.ix=-1; ca.ia.sin.sin_family=AF_INET; ca.ia.sin.sin_port=htons(port); if (inet_aton(trimmed,&ca.ia.sin.sin_addr)) diff --git a/secnet.h b/secnet.h index 64a20a7..6c9a93b 100644 --- a/secnet.h +++ b/secnet.h @@ -342,6 +342,7 @@ struct comm_addr { equivalent. */ struct comm_if *comm; union iaddr ia; + int ix; }; /* Return True if the packet was processed, and shouldn't be passed to diff --git a/udp.c b/udp.c index 899a2c8..334670f 100644 --- a/udp.c +++ b/udp.c @@ -36,12 +36,19 @@ struct notify_list { struct notify_list *next; }; +#define MAX_SOCKETS 3 /* 2 ought to do really */ + +struct udpsock { + union iaddr addr; + int fd; +}; + struct udp { closure_t cl; struct comm_if ops; struct cloc loc; - union iaddr addr; - int fd; + int n_socks; + struct udpsock socks[MAX_SOCKETS]; string_t authbind; struct buffer_if *rbuf; struct notify_list *notify; @@ -52,23 +59,30 @@ struct udp { static const char *addr_to_string(void *commst, const struct comm_addr *ca) { struct udp *st=commst; static char sbuf[100]; + int ix=ca->ix>=0 ? ca->ix : 0; - snprintf(sbuf, sizeof(sbuf), "udp:%s-%s", - iaddr_to_string(&st->addr), iaddr_to_string(&ca->ia)); + assert(ix>=0 && ixn_socks); + snprintf(sbuf, sizeof(sbuf), "udp:%d:%s-%s", + ca->ix, + iaddr_to_string(&st->socks[ix].addr), + iaddr_to_string(&ca->ia)); return sbuf; } static int udp_beforepoll(void *state, struct pollfd *fds, int *nfds_io, int *timeout_io) { + int i; struct udp *st=state; - if (*nfds_io<1) { - *nfds_io=1; + if (*nfds_ion_socks) { + *nfds_io=st->n_socks; return ERANGE; } - *nfds_io=1; - fds->fd=st->fd; - fds->events=POLLIN; + *nfds_io=st->n_socks; + for (i=0; in_socks; i++) { + fds[i].fd=st->socks[i].fd; + fds[i].events=POLLIN; + } return 0; } @@ -80,15 +94,20 @@ static void udp_afterpoll(void *state, struct pollfd *fds, int nfds) struct notify_list *n; bool_t done; int rv; + int i; - if (nfds && (fds->revents & POLLIN)) { + for (i=0; in_socks; i++) { + if (i>=nfds) continue; + if (!(fds[i].revents & POLLIN)) continue; + assert(fds[i].fd == st->socks[i].fd); + int fd=st->socks[i].fd; do { FILLZERO(from); fromlen=sizeof(from); BUF_ASSERT_FREE(st->rbuf); BUF_ALLOC(st->rbuf,"udp_afterpoll"); buffer_init(st->rbuf,calculate_max_start_pad()); - rv=recvfrom(st->fd, st->rbuf->start, + rv=recvfrom(fd, st->rbuf->start, buf_remaining_space(st->rbuf), 0, (struct sockaddr *)&from, &fromlen); if (rv>0) { @@ -114,6 +133,7 @@ static void udp_afterpoll(void *state, struct pollfd *fds, int nfds) FILLZERO(ca); ca.comm=&st->ops; ca.ia=from; + ca.ix=i; done=False; for (n=st->notify; n; n=n->next) { if (n->fn(n->state, st->rbuf, &ca)) { @@ -192,21 +212,33 @@ static bool_t udp_sendmsg(void *commst, struct buffer_if *buf, memcpy(sa,&dest->ia.sin.sin_addr,4); memset(sa+4,0,4); memcpy(sa+6,&dest->ia.sin.sin_port,2); - sendto(st->fd,sa,buf->size+8,0,(struct sockaddr *)&st->proxy, + sendto(st->socks[0].fd,sa,buf->size+8,0,(struct sockaddr *)&st->proxy, sizeof(st->proxy)); buf_unprepend(buf,8); } else { - sendto(st->fd, buf->start, buf->size, 0, - &dest->ia.sa, iaddr_socklen(&dest->ia)); + int i,r; + bool_t allunsupported=True; + for (i=0; in_socks; i++) { + if (dest->ia.sa.sa_family != st->socks[i].addr.sa.sa_family) + /* no point even trying */ + continue; + r=sendto(st->socks[i].fd, buf->start, buf->size, 0, + &dest->ia.sa, iaddr_socklen(&dest->ia)); + if (!r) return True; + if (!(errno==EAFNOSUPPORT || errno==ENETUNREACH)) + /* who knows what that error means? */ + allunsupported=False; + } + return !allunsupported; /* see doc for comm_sendmsg_fn in secnet.h */ } return True; } -static void udp_make_socket(struct udp *st, struct udp *us) +static void udp_make_socket(struct udp *st, struct udpsock *us) { const union iaddr *addr=&us->addr; - us->fd=socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); + us->fd=socket(addr->sa.sa_family, SOCK_DGRAM, IPPROTO_UDP); if (us->fd<0) { fatal_perror("udp (%s:%d): socket",st->loc.file,st->loc.line); } @@ -215,6 +247,16 @@ static void udp_make_socket(struct udp *st, struct udp *us) st->loc.file,st->loc.line); } setcloexec(us->fd); +#ifdef CONFIG_IPV6 + if (addr->sa.sa_family==AF_INET6) { + int r; + int optval=1; + socklen_t optlen=sizeof(optval); + r=setsockopt(us->fd,IPPROTO_IPV6,IPV6_V6ONLY,&optval,optlen); + if (r) fatal_perror("udp (%s:%d): setsockopt(,IPV6_V6ONLY,&1,)", + st->loc.file,st->loc.line); + } +#endif if (st->authbind) { pid_t c; @@ -267,8 +309,11 @@ static void udp_make_socket(struct udp *st, struct udp *us) static void udp_phase_hook(void *sst, uint32_t new_phase) { struct udp *st=sst; - udp_make_socket(st,st); - register_for_poll(st,udp_beforepoll,udp_afterpoll,1,"udp"); + int i; + for (i=0; in_socks; i++) + udp_make_socket(st,&st->socks[i]); + + register_for_poll(st,udp_beforepoll,udp_afterpoll,MAX_SOCKETS,"udp"); } static list_t *udp_apply(closure_t *self, struct cloc loc, dict_t *context, @@ -276,10 +321,11 @@ static list_t *udp_apply(closure_t *self, struct cloc loc, dict_t *context, { struct udp *st; item_t *item; - item_t *j; + list_t *caddrl; dict_t *d; list_t *l; uint32_t a; + int i; st=safe_malloc(sizeof(*st),"udp_apply(st)"); st->loc=loc; @@ -292,7 +338,6 @@ static list_t *udp_apply(closure_t *self, struct cloc loc, dict_t *context, st->ops.release_notify=release_notify; st->ops.sendmsg=udp_sendmsg; st->ops.addr_to_string=addr_to_string; - FILLZERO(st->addr); st->use_proxy=False; item=list_elem(args,0); @@ -301,10 +346,37 @@ static list_t *udp_apply(closure_t *self, struct cloc loc, dict_t *context, } d=item->data.dict; - st->addr.sa.sa_family=AF_INET; - j=dict_find_item(d,"address",False,"udp",st->loc); - st->addr.sin.sin_addr.s_addr=j?string_item_to_ipaddr(j, "udp"):INADDR_ANY; - st->addr.sin.sin_port=dict_read_number(d,"port",True,"udp",st->loc,0); + int port=dict_read_number(d,"port",True,"udp",st->loc,0); + + union iaddr defaultaddrs[] = { +#ifdef CONFIG_IPV6 + { .sin6 = { .sin6_family=AF_INET6, + .sin6_port=htons(port), + .sin6_addr=IN6ADDR_ANY_INIT } }, +#endif + { .sin = { .sin_family=AF_INET, + .sin_port=htons(port), + .sin_addr= { .s_addr=INADDR_ANY } } } + }; + + caddrl=dict_lookup(d,"address"); + st->n_socks=caddrl ? list_length(caddrl) : (int)ARRAY_SIZE(defaultaddrs); + if (st->n_socks<=0 || st->n_socks>MAX_SOCKETS) + cfgfatal(st->loc,"udp","`address' must be 1..%d addresses", + MAX_SOCKETS); + + for (i=0; in_socks; i++) { + struct udpsock *us=&st->socks[i]; + FILLZERO(us->addr); + if (!list_length(caddrl)) { + us->addr=defaultaddrs[i]; + } else { + text2iaddr(list_elem(caddrl,i),port, + &us->addr,"udp"); + } + us->fd=-1; + } + st->rbuf=find_cl_if(d,"buffer",CL_BUFFER,True,"udp",st->loc); st->authbind=dict_read_string(d,"authbind",False,"udp",st->loc); l=dict_lookup(d,"proxy"); -- 2.30.2