Hi, I thought it would be cool for rebound(8) to load balance on a number of DNS servers.
While I was working on this, I did not manage to convince myself as to whether this should be the default behaviour. An alternative default would be to use the master server provided. If requests started timing out, it would switch to the secondary. If the master became available again, then it would switch back to it. This diff may be useful regardless of what the default should be and given that there is no config parser at the moment that can handle options I thought I would send this out to get some feedback. Cheers, Dimitris >From 86c7ad057137181b560341779c79bca339b30e95 Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos <[email protected]> Date: Wed, 28 Oct 2015 14:45:03 +0000 Subject: [PATCH] Add a simple roundrobin load balancing feature to rebound(8) --- usr.sbin/rebound/rebound.8 | 6 ++- usr.sbin/rebound/rebound.c | 105 +++++++++++++++++++++++++++++++-------------- 2 files changed, 77 insertions(+), 34 deletions(-) diff --git a/usr.sbin/rebound/rebound.8 b/usr.sbin/rebound/rebound.8 index e25fc83..d32a5a9 100644 --- a/usr.sbin/rebound/rebound.8 +++ b/usr.sbin/rebound/rebound.8 @@ -28,8 +28,10 @@ The daemon proxies DNS requests. It listens on localhost and forwards queries to another server. .Pp -At present, the config file consists of a single line containing the next -hop DNS server. +At present, the config file consists of lines containing the next hop +DNS servers. If more than one server is specified, +.Nm +will use them in a roundrobin fashion. .Sh SEE ALSO .Xr resolv.conf 5 , .Xr unbound 8 diff --git a/usr.sbin/rebound/rebound.c b/usr.sbin/rebound/rebound.c index 80405ab..7c9c7bd 100644 --- a/usr.sbin/rebound/rebound.c +++ b/usr.sbin/rebound/rebound.c @@ -37,6 +37,8 @@ #include <getopt.h> #include <stdarg.h> +#define SA(x) ((struct sockaddr *)(x)) + uint16_t randomid(void); static struct timespec now; @@ -93,6 +95,12 @@ static TAILQ_HEAD(, request) reqfifo; static RB_HEAD(reqtree, request) reqtree; RB_PROTOTYPE_STATIC(reqtree, request, reqnode, reqcmp) +struct server { + struct sockaddr_storage addr; + int active; + TAILQ_ENTRY(server) entry; +}; +static TAILQ_HEAD(, server) servers; static void logmsg(int prio, const char *msg, ...) @@ -125,6 +133,27 @@ logerr(const char *msg, ...) exit(1); } +static void +nextserver(struct sockaddr_storage *remoteaddr) +{ + struct server *s; + + TAILQ_FOREACH(s, &servers, entry) + if (s->active) + break; + if (!s) { + s = TAILQ_FIRST(&servers); + } else { + s->active = 0; + if (TAILQ_NEXT(s, entry)) + s = TAILQ_NEXT(s, entry); + else + s = TAILQ_FIRST(&servers); + } + s->active = 1; + memcpy(remoteaddr, &s->addr, sizeof(*remoteaddr)); +} + static struct dnscache * cachelookup(struct dnspacket *dnsreq, size_t reqlen) { @@ -177,8 +206,9 @@ reqcmp(struct request *r1, struct request *r2) RB_GENERATE_STATIC(reqtree, request, reqnode, reqcmp) static struct request * -newrequest(int ud, struct sockaddr *remoteaddr) +newrequest(int ud) { + struct sockaddr_storage remoteaddr; struct sockaddr from; socklen_t fromlen; struct request *req; @@ -231,10 +261,11 @@ newrequest(int ud, struct sockaddr *remoteaddr) } req->cacheent = hit; - req->s = socket(remoteaddr->sa_family, SOCK_DGRAM, 0); + nextserver(&remoteaddr); + req->s = socket(SA(&remoteaddr)->sa_family, SOCK_DGRAM, 0); if (req->s == -1) goto fail; - if (connect(req->s, remoteaddr, remoteaddr->sa_len) == -1) { + if (connect(req->s, SA(&remoteaddr), SA(&remoteaddr)->sa_len) == -1) { logmsg(LOG_NOTICE, "failed to connect"); goto fail; } @@ -303,8 +334,9 @@ fail: } static struct request * -newtcprequest(int ld, struct sockaddr *remoteaddr) +newtcprequest(int ld) { + struct sockaddr_storage remoteaddr; struct request *req; if (!(req = calloc(1, sizeof(*req)))) @@ -321,11 +353,12 @@ newtcprequest(int ld, struct sockaddr *remoteaddr) if (req->client == -1) goto fail; - req->s = socket(remoteaddr->sa_family, SOCK_STREAM | SOCK_NONBLOCK, 0); + nextserver(&remoteaddr); + req->s = socket(SA(&remoteaddr)->sa_family, SOCK_STREAM | SOCK_NONBLOCK, 0); if (req->s == -1) goto fail; req->phase = 1; - if (connect(req->s, remoteaddr, remoteaddr->sa_len) == -1) { + if (connect(req->s, SA(&remoteaddr), SA(&remoteaddr)->sa_len) == -1) { if (errno != EINPROGRESS) goto fail; } else { @@ -340,36 +373,45 @@ fail: } static int -readconfig(FILE *conf, struct sockaddr_storage *remoteaddr) +readconfig(FILE *conf) { char buf[1024]; - struct sockaddr_in *sin = (struct sockaddr_in *)remoteaddr; - struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)remoteaddr; + struct server *s; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; - if (fgets(buf, sizeof(buf), conf) == NULL) - return -1; - buf[strcspn(buf, "\n")] = '\0'; - - memset(remoteaddr, 0, sizeof(*remoteaddr)); - if (inet_pton(AF_INET, buf, &sin->sin_addr) == 1) { - sin->sin_len = sizeof(*sin); - sin->sin_family = AF_INET; - sin->sin_port = htons(53); - return AF_INET; - } else if (inet_pton(AF_INET6, buf, &sin6->sin6_addr) == 1) { - sin6->sin6_len = sizeof(*sin6); - sin6->sin6_family = AF_INET6; - sin6->sin6_port = htons(53); - return AF_INET6; - } else { - return -1; + while ((s = TAILQ_FIRST(&servers))) { + TAILQ_REMOVE(&servers, s, entry); + free(s); + } + + while (fgets(buf, sizeof(buf), conf) != NULL) { + buf[strcspn(buf, "\n")] = '\0'; + + s = calloc(1, sizeof(*s)); + sin = (struct sockaddr_in *)&s->addr; + sin6 = (struct sockaddr_in6 *)&s->addr; + if (inet_pton(AF_INET, buf, &sin->sin_addr) == 1) { + sin->sin_len = sizeof(*sin); + sin->sin_family = AF_INET; + sin->sin_port = htons(53); + } else if (inet_pton(AF_INET6, buf, &sin6->sin6_addr) == 1) { + sin6->sin6_len = sizeof(*sin6); + sin6->sin6_family = AF_INET6; + sin6->sin6_port = htons(53); + } else { + return -1; + } + TAILQ_INSERT_TAIL(&servers, s, entry); } + if (TAILQ_EMPTY(&servers)) + return -1; + return 0; } static int launch(const char *confname, int ud, int ld, int kq) { - struct sockaddr_storage remoteaddr; struct kevent chlist[2], kev[4]; struct timespec ts, *timeout = NULL; struct request reqkey, *req; @@ -411,7 +453,7 @@ launch(const char *confname, int ud, int ld, int kq) if (pledge("stdio inet", NULL) == -1) logerr("pledge failed"); - af = readconfig(conf, &remoteaddr); + af = readconfig(conf); fclose(conf); if (af == -1) logerr("failed to read config %s", confname); @@ -437,15 +479,13 @@ launch(const char *confname, int ud, int ld, int kq) logmsg(LOG_INFO, "parent died"); exit(0); } else if (kev[i].ident == ud) { - while ((req = newrequest(ud, - (struct sockaddr *)&remoteaddr))) { + while ((req = newrequest(ud))) { EV_SET(&chlist[0], req->s, EVFILT_READ, EV_ADD, 0, 0, NULL); kevent(kq, chlist, 1, NULL, 0, NULL); } } else if (kev[i].ident == ld) { - while ((req = newtcprequest(ld, - (struct sockaddr *)&remoteaddr))) { + while ((req = newtcprequest(ld))) { EV_SET(&chlist[0], req->s, req->phase == 1 ? EVFILT_WRITE : EVFILT_READ, EV_ADD, 0, 0, NULL); @@ -555,6 +595,7 @@ main(int argc, char **argv) RB_INIT(&reqtree); TAILQ_INIT(&reqfifo); TAILQ_INIT(&cache); + TAILQ_INIT(&servers); memset(&bindaddr, 0, sizeof(bindaddr)); bindaddr.sin_len = sizeof(bindaddr); -- 2.6.2
