On Sun, Jul 12, 2015 at 12:09 PM, Poul-Henning Kamp <[email protected]> wrote: >>If you look at the DNS director patch, you'll see extra care in not >>removing backends needlessly. > > I havn't looked at that patch yet.
Forgot to update my DNS director draft. I also realized thanks to telnet that my ISP doesn't seem to support IPv6 (Network is unreachable), but loopback tests went well. Cheers, Dridi
From 32f7ad806267ea3ecb65c8c2cc95bc9bbd102cd6 Mon Sep 17 00:00:00 2001 From: Dridi Boukelmoune <[email protected]> Date: Tue, 7 Jul 2015 22:31:50 +0200 Subject: [PATCH 1/2] Resurrect VSA_Len Partial revert of 0848a1fe471c40a49e77df941c8e2cc133170ff7. --- include/vsa.h | 1 + lib/libvarnish/vsa.c | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/include/vsa.h b/include/vsa.h index 185be03..996afde 100644 --- a/include/vsa.h +++ b/include/vsa.h @@ -34,6 +34,7 @@ struct suckaddr; extern const int vsa_suckaddr_len; int VSA_Sane(const struct suckaddr *); +socklen_t VSA_Len(const struct suckaddr *); unsigned VSA_Port(const struct suckaddr *); int VSA_Compare(const struct suckaddr *, const struct suckaddr *); struct suckaddr *VSA_Clone(const struct suckaddr *sua); diff --git a/lib/libvarnish/vsa.c b/lib/libvarnish/vsa.c index 43d9a86..fc25cdd 100644 --- a/lib/libvarnish/vsa.c +++ b/lib/libvarnish/vsa.c @@ -306,6 +306,21 @@ VSA_Sane(const struct suckaddr *sua) } } +socklen_t +VSA_Len(const struct suckaddr *sua) +{ + CHECK_OBJ_NOTNULL(sua, SUCKADDR_MAGIC); + + switch(sua->sa.sa_family) { + case PF_INET: + return (sizeof(sua->sa4)); + case PF_INET6: + return (sizeof(sua->sa6)); + default: + return (0); + } +} + int VSA_Compare(const struct suckaddr *sua1, const struct suckaddr *sua2) { -- 2.1.0
From c489a4903e8f1ec5b7f020a7ff94d1e971812c76 Mon Sep 17 00:00:00 2001 From: Dridi Boukelmoune <[email protected]> Date: Wed, 8 Jul 2015 19:49:14 +0200 Subject: [PATCH 2/2] Resurrect the DNS director --- lib/libvmod_directors/Makefile.am | 3 +- lib/libvmod_directors/dns.c | 393 ++++++++++++++++++++++++++++++++++++++ lib/libvmod_directors/vmod.vcc | 43 +++++ 3 files changed, 438 insertions(+), 1 deletion(-) create mode 100644 lib/libvmod_directors/dns.c diff --git a/lib/libvmod_directors/Makefile.am b/lib/libvmod_directors/Makefile.am index 64b72a8..bc0261f 100644 --- a/lib/libvmod_directors/Makefile.am +++ b/lib/libvmod_directors/Makefile.am @@ -17,6 +17,7 @@ libvmod_directors_la_LDFLAGS = $(AM_LDFLAGS) -module -export-dynamic -avoid-vers libvmod_directors_la_SOURCES = \ vdir.c \ vdir.h \ + dns.c \ fall_back.c \ hash.c \ random.c \ @@ -27,7 +28,7 @@ nodist_libvmod_directors_la_SOURCES = \ vcc_if.h # BUILT_SOURCES is only a hack and dependency tracking does not help for the first build -vdir.lo fall_back.lo hash.lo random.lo round_robin.lo: vcc_if.h +vdir.lo dns.lo fall_back.lo hash.lo random.lo round_robin.lo: vcc_if.h vcc_if.c vcc_if.h vmod_directors.rst vmod_directors.man.rst: $(vmodtool) $(vmod_srcdir)/vmod.vcc @PYTHON@ $(vmodtool) $(vmodtoolargs) $(vmod_srcdir)/vmod.vcc diff --git a/lib/libvmod_directors/dns.c b/lib/libvmod_directors/dns.c new file mode 100644 index 0000000..df0cf98 --- /dev/null +++ b/lib/libvmod_directors/dns.c @@ -0,0 +1,393 @@ +/*- + * Copyright (c) 2013 Varnish Software AS + * All rights reserved. + * + * Author: Dridi Boukelmoune <[email protected]> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "config.h" + +#include <arpa/inet.h> + +#include <sys/socket.h> +#include <sys/types.h> + +#include <errno.h> +#include <netdb.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> + +#include "vrt.h" +#include "vcl.h" + +#include "cache/cache.h" +#include "cache/cache_director.h" +#include "cache/cache_backend.h" + +#include "vsa.h" +#include "vtim.h" +#include "vcc_if.h" + +struct vmod_directors_dns; + +struct dns_director { + struct vmod_directors_dns *dns; + struct director *simple_dir; + VTAILQ_ENTRY(dns_director) list; + struct suckaddr *ip_suckaddr; + char *ip_addr; + char *vcl_name; + unsigned mark; +}; + +struct vmod_directors_dns { + unsigned magic; +#define VMOD_DIRECTORS_DNS_MAGIC 0x8a3e7fd1 + pthread_t thread; + pthread_mutex_t mtx; + pthread_cond_t cond; + char *vcl_name; + char *addr; + char *port; + double ttl; + VTAILQ_HEAD(,dns_director) director_list; + struct dns_director *next; + struct director dir; + struct vcl *vcl; + int active; + unsigned mark; +}; + +static void +vmod_dns_rotate(struct vmod_directors_dns *dns) +{ + if (dns->next != NULL) + dns->next = dns->next->list.vtqe_next; + + if (dns->next == NULL) + dns->next = dns->director_list.vtqh_first; +} + +static int +vmod_dns_notfound(struct vmod_directors_dns *dns, struct suckaddr *sa) +{ + struct dns_director *d; + + AN(sa); + + VTAILQ_FOREACH(d, &dns->director_list, list) { + if (d->mark == dns->mark) + continue; + + if (VSA_Compare(d->ip_suckaddr, sa)) + continue; + + d->mark = dns->mark; + return (0); + } + + return (1); +} + +static unsigned __match_proto__(vdi_healthy_f) +vmod_dns_healthy(const struct director *d, const struct busyobj *bo, + double *changed) +{ + struct vmod_directors_dns *dns; + unsigned healthy; + + CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC); + CAST_OBJ_NOTNULL(dns, d->priv, VMOD_DIRECTORS_DNS_MAGIC); + + AZ(pthread_mutex_lock(&dns->mtx)); + (void)changed; + healthy = (dns->next != NULL); + AZ(pthread_mutex_unlock(&dns->mtx)); + + // XXX we can do better than that + return (healthy); +} + +static const struct director * __match_proto__(vdi_resolve_f) +vmod_dns_resolve(const struct director *d, struct worker *wrk, + struct busyobj *bo) +{ + struct vmod_directors_dns *dns; + struct director *dir = NULL; + + CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC); + CAST_OBJ_NOTNULL(dns, d->priv, VMOD_DIRECTORS_DNS_MAGIC); + + pthread_mutex_lock(&dns->mtx); + if (dns->next != NULL) { + dir = dns->next->simple_dir; + vmod_dns_rotate(dns); + } + AZ(pthread_mutex_unlock(&dns->mtx)); + + return (dir); +} + +static void +vmod_dns_del(VRT_CTX, struct dns_director *dir) +{ + struct dns_director *next; + struct vmod_directors_dns *dns; + + AN(dir); + AN(dir->dns); + + dns = dir->dns; + next = dns->next; + AN(next); + + if (dir == next) { + vmod_dns_rotate(dns); + if (next == dns->next) + dns->next = NULL; + } + + VTAILQ_REMOVE(&dir->dns->director_list, dir, list); + if (ctx) { + AN(ctx->vcl); + VRT_delete_backend(ctx, &dir->simple_dir); + } + free(dir->vcl_name); + free(dir->ip_addr); + free(dir->ip_suckaddr); + free(dir); +} + +static void +vmod_dns_add(VRT_CTX, struct vmod_directors_dns *dns, + struct suckaddr *sa) +{ + struct vrt_backend vrt; + struct dns_director *dir; + const unsigned char *ptr = NULL; + char buf[128]; + int af; + socklen_t len; + + dir = malloc(sizeof *dir); + AN(dir); + dir->dns = dns; + dir->mark = dns->mark; + dir->ip_suckaddr = sa; + + af = VRT_VSA_GetPtr(sa, &ptr); + AN(ptr); + len = VSA_Len(sa); + AN(inet_ntop(af, ptr, buf, len)); + dir->ip_addr = strdup(buf); + AN(dir->ip_addr); + + snprintf(buf, sizeof buf, "%s(%s)", dns->vcl_name, dir->ip_addr); + dir->vcl_name = strdup(buf); + AN(dir->vcl_name); + + INIT_OBJ(&vrt, VRT_BACKEND_MAGIC); + vrt.port = dns->port; + vrt.hosthdr = dns->addr; + vrt.vcl_name = dir->vcl_name; + + switch (af) { + case AF_INET: + vrt.ipv4_suckaddr = sa; + vrt.ipv4_addr = dir->ip_addr; + break; + case AF_INET6: + vrt.ipv6_suckaddr = sa; + vrt.ipv6_addr = dir->ip_addr; + break; + default: + WRONG("unexpected family"); + } + + + dir->simple_dir = VRT_new_backend(ctx, &vrt); + AN(dir->simple_dir); + + VTAILQ_INSERT_TAIL(&dns->director_list, dir, list); +} + +static void +vmod_dns_update(struct vmod_directors_dns *dns, struct addrinfo *addr) +{ + struct suckaddr *sa; + struct dns_director *d, *d2; + struct vrt_ctx ctx; + + AN(addr); + + INIT_OBJ(&ctx, VRT_CTX_MAGIC); + ctx.vcl = dns->vcl; + + dns->mark++; + while (addr) { + switch (addr->ai_family) { + case AF_INET: + case AF_INET6: + sa = malloc(vsa_suckaddr_len); + AN(sa); + AN(VSA_Build(sa, addr->ai_addr, addr->ai_addrlen)); + if (vmod_dns_notfound(dns, sa)) + vmod_dns_add(&ctx, dns, sa); + } + addr = addr->ai_next; + } + + VTAILQ_FOREACH_SAFE(d, &dns->director_list, list, d2) + if (d->mark != dns->mark) + vmod_dns_del(&ctx, d); + + vmod_dns_rotate(dns); +} + +static void* +vmod_dns_lookup_thread(void *obj) +{ + struct vmod_directors_dns *dns; + struct timespec ts; + struct addrinfo hints, *res; + double deadline; + int ret; + + CAST_OBJ_NOTNULL(dns, obj, VMOD_DIRECTORS_DNS_MAGIC); + + memset(&hints, 0, sizeof hints); + hints.ai_socktype = SOCK_STREAM; + hints.ai_family = AF_UNSPEC; + + while (dns->active) { + ret = getaddrinfo(dns->addr, dns->port, &hints, &res); + + AZ(pthread_mutex_lock(&dns->mtx)); + + if (ret == 0) { + vmod_dns_update(dns, res); + freeaddrinfo(res); + } + else + VSL(SLT_Error, 0, "DNS lookup failed: %d (%s)", + ret, gai_strerror(ret)); + + deadline = VTIM_real() + dns->ttl; + ts = VTIM_timespec(deadline); + ret = pthread_cond_timedwait(&dns->cond, &dns->mtx, &ts); + assert(ret == 0 || ret == ETIMEDOUT); + + AZ(pthread_mutex_unlock(&dns->mtx)); + } + + return (NULL); +} + +VCL_VOID __match_proto__() +vmod_dns__init(VRT_CTX, struct vmod_directors_dns **dnsp, const char *vcl_name, + VCL_STRING addr, VCL_STRING port) +{ + struct vmod_directors_dns *dns; + + ASSERT_CLI(); + CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); + AN(dnsp); + AZ(*dnsp); + AN(vcl_name); + AN(addr); + AN(port); + + ALLOC_OBJ(dns, VMOD_DIRECTORS_DNS_MAGIC); + AN(dns); + VTAILQ_INIT(&dns->director_list); + REPLACE(dns->vcl_name, vcl_name); + REPLACE(dns->addr, addr); + REPLACE(dns->port, port); + dns->vcl = ctx->vcl; + dns->ttl = 3600; + dns->active = 1; + + INIT_OBJ(&dns->dir, DIRECTOR_MAGIC); + dns->dir.name = "dns"; + dns->dir.vcl_name = dns->vcl_name; + dns->dir.healthy = vmod_dns_healthy; + dns->dir.resolve = vmod_dns_resolve; + dns->dir.priv = dns; + + AZ(pthread_mutex_init(&dns->mtx, NULL)); + AZ(pthread_cond_init(&dns->cond, NULL)); + AZ(pthread_create(&dns->thread, NULL, &vmod_dns_lookup_thread, dns)); + + *dnsp = dns; +} + +VCL_VOID __match_proto__() +vmod_dns__fini(struct vmod_directors_dns **dnsp) +{ + struct vmod_directors_dns *dns; + + AN(dnsp); + dns = *dnsp; + *dnsp = NULL; + + CHECK_OBJ_NOTNULL(dns, VMOD_DIRECTORS_DNS_MAGIC); + + AZ(pthread_mutex_lock(&dns->mtx)); + dns->active = 0; + AZ(pthread_mutex_unlock(&dns->mtx)); + + AZ(pthread_cond_signal(&dns->cond)); + AZ(pthread_join(dns->thread, NULL)); + + /* backends will be deleted by the VCL, pass a NULL struct ctx */ + while (dns->director_list.vtqh_first != NULL) + vmod_dns_del(NULL, dns->director_list.vtqh_first); + + AZ(pthread_cond_destroy(&dns->cond)); + AZ(pthread_mutex_destroy(&dns->mtx)); + free(dns->vcl_name); + free(dns->addr); + free(dns->port); + FREE_OBJ(dns); +} + +VCL_VOID __match_proto__() +vmod_dns_set_ttl(VRT_CTX, struct vmod_directors_dns *dns, VCL_DURATION ttl) +{ + + CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); + CHECK_OBJ_NOTNULL(dns, VMOD_DIRECTORS_DNS_MAGIC); + assert(ttl > 0); + dns->ttl = ttl; +} + +VCL_BACKEND __match_proto__() +vmod_dns_backend(VRT_CTX, struct vmod_directors_dns *dns) +{ + + CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); + CHECK_OBJ_NOTNULL(dns, VMOD_DIRECTORS_DNS_MAGIC); + return (&dns->dir); +} diff --git a/lib/libvmod_directors/vmod.vcc b/lib/libvmod_directors/vmod.vcc index cfcf2e3..9291d4d 100644 --- a/lib/libvmod_directors/vmod.vcc +++ b/lib/libvmod_directors/vmod.vcc @@ -180,3 +180,46 @@ Description Example # pick a backend based on the cookie header from the client set req.backend_hint = vdir.backend(req.http.cookie); + +$Object dns(STRING, STRING) + +Description + Create a DNS director. + + The director creates backends with DNS lookups and chooses them in a + round robin fashion. An ACL (Access Control List) can be used as a + white-list to restrict the use of resolved addresses. + +Example + :: + + acl www_backends { + "192.168.15.0"/24; + !"192.168.15.1"; + } + + sub vcl_init { + new www_dir = directors.dns("www.example.com", "80"); + www_dir.restrict_to(www_backends); + www_dir.set_ttl(5m); + } + + sub vcl_recv { + set req.backend_hint = www_dir.backend(); + } + +# TODO $Method VOID .restrict_to(ACL) +# +#Description +# Restrict usage of resolved IP addresses to a named ACL. + +$Method VOID .set_ttl(DURATION) + +Description + Set the DNS lookup TTL (defaults to one hour). + +$Method BACKEND .backend() + +Description + Pick a backend from the director. + -- 2.1.0
_______________________________________________ varnish-dev mailing list [email protected] https://www.varnish-cache.org/lists/mailman/listinfo/varnish-dev
