r5062 - in trunk/varnish-cache: bin/varnishd doc/sphinx/reference include lib/libvcl
kristian at varnish-cache.org
kristian at varnish-cache.org
Sun Aug 1 16:45:22 CEST 2010
Author: kristian
Date: 2010-08-01 16:45:22 +0200 (Sun, 01 Aug 2010)
New Revision: 5062
Added:
trunk/varnish-cache/bin/varnishd/cache_dir_dns.c
trunk/varnish-cache/lib/libvcl/vcc_dir_dns.c
Modified:
trunk/varnish-cache/bin/varnishd/Makefile.am
trunk/varnish-cache/bin/varnishd/cache_backend.c
trunk/varnish-cache/bin/varnishd/cache_backend_cfg.c
trunk/varnish-cache/doc/sphinx/reference/vcl.rst
trunk/varnish-cache/include/stat_field.h
trunk/varnish-cache/include/vrt.h
trunk/varnish-cache/include/vsc_fields.h
trunk/varnish-cache/lib/libvcl/Makefile.am
trunk/varnish-cache/lib/libvcl/vcc_backend.c
trunk/varnish-cache/lib/libvcl/vcc_compile.h
Log:
Add a DNS director
The DNS director allows Varnish to pick backend based on the Host header
provided by the client, and how it resolves in DNS. A suffix can be added
to make it "internal" (see vcl(7)).
There's still some quirks that I want to work out, but this seems fairly
commit-ready and non-intrusive.
Modified: trunk/varnish-cache/bin/varnishd/Makefile.am
===================================================================
--- trunk/varnish-cache/bin/varnishd/Makefile.am 2010-07-30 23:17:36 UTC (rev 5061)
+++ trunk/varnish-cache/bin/varnishd/Makefile.am 2010-08-01 14:45:22 UTC (rev 5062)
@@ -21,6 +21,7 @@
cache_center.c \
cache_cli.c \
cache_dir_random.c \
+ cache_dir_dns.c \
cache_dir_round_robin.c \
cache_esi.c \
cache_expire.c \
Modified: trunk/varnish-cache/bin/varnishd/cache_backend.c
===================================================================
--- trunk/varnish-cache/bin/varnishd/cache_backend.c 2010-07-30 23:17:36 UTC (rev 5061)
+++ trunk/varnish-cache/bin/varnishd/cache_backend.c 2010-08-01 14:45:22 UTC (rev 5062)
@@ -536,6 +536,24 @@
struct vsc_vbe *stats;
};
+/* Returns the backend if and only if the this is a simple director.
+ * XXX: Needs a better name and possibly needs a better general approach.
+ * XXX: This is mainly used by the DNS director to fetch the actual backend
+ * XXX: so it can compare DNS lookups with the actual IP.
+ */
+struct backend *
+vdi_get_backend_if_simple(const struct director *d)
+{
+ CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
+ struct vdi_simple *vs, *vs2;
+
+ vs2 = d->priv;
+ if (vs2->magic != VDI_SIMPLE_MAGIC)
+ return NULL;
+ CAST_OBJ_NOTNULL(vs, d->priv, VDI_SIMPLE_MAGIC);
+ return vs->backend;
+}
+
static struct vbe_conn *
vdi_simple_getfd(const struct director *d, struct sess *sp)
{
Modified: trunk/varnish-cache/bin/varnishd/cache_backend_cfg.c
===================================================================
--- trunk/varnish-cache/bin/varnishd/cache_backend_cfg.c 2010-07-30 23:17:36 UTC (rev 5061)
+++ trunk/varnish-cache/bin/varnishd/cache_backend_cfg.c 2010-08-01 14:45:22 UTC (rev 5062)
@@ -272,6 +272,8 @@
VRT_init_dir_hash(cli, dir, idx, priv);
else if (!strcmp(name, "random"))
VRT_init_dir_random(cli, dir, idx, priv);
+ else if (!strcmp(name, "dns"))
+ VRT_init_dir_dns(cli, dir, idx, priv);
else if (!strcmp(name, "round-robin"))
VRT_init_dir_round_robin(cli, dir, idx, priv);
else if (!strcmp(name, "client"))
Added: trunk/varnish-cache/bin/varnishd/cache_dir_dns.c
===================================================================
--- trunk/varnish-cache/bin/varnishd/cache_dir_dns.c (rev 0)
+++ trunk/varnish-cache/bin/varnishd/cache_dir_dns.c 2010-08-01 14:45:22 UTC (rev 5062)
@@ -0,0 +1,484 @@
+/*-
+ * Copyright (c) 2009 Redpill Linpro AS
+ * Copyright (c) 2010 Varnish Software AS
+ * All rights reserved.
+ *
+ * Author: Kristian Lyngstol <kristian at redpill-linpro.com>
+ *
+ * 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 "svnid.h"
+SVNID("$Id$")
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+#include <unistd.h>
+
+#include <stdio.h>
+#include "cache.h"
+#include "cache_backend.h"
+#include "vrt.h"
+
+/*--------------------------------------------------------------------*/
+
+/* FIXME: Should eventually be a configurable variable. */
+#define VDI_DNS_MAX_CACHE 1024
+#define VDI_DNS_GROUP_MAX_BACKENDS 1024
+
+/* DNS Cache entry
+ */
+struct vdi_dns_hostgroup {
+ unsigned magic;
+#define VDI_DNSDIR_MAGIC 0x1bacab21
+ char *hostname;
+ struct director *hosts[VDI_DNS_GROUP_MAX_BACKENDS];
+ unsigned nhosts;
+ unsigned next_host; /* Next to use...*/
+ double ttl;
+ VTAILQ_ENTRY(vdi_dns_hostgroup) list;
+};
+
+struct vdi_dns {
+ unsigned magic;
+#define VDI_DNS_MAGIC 0x1337a178
+ struct director dir;
+ struct director **hosts;
+ unsigned nhosts;
+ VTAILQ_HEAD(_cachelist,vdi_dns_hostgroup) cachelist;
+ unsigned ncachelist;
+ pthread_rwlock_t rwlock;
+ const char *suffix;
+ double ttl;
+ const unsigned max_cache_size;
+};
+
+
+
+/* Compare an IPv4 backend to a IPv4 addr/len */
+static int
+vdi_dns_comp_addrinfo4(struct backend *bp,
+ const struct sockaddr_in *addr,
+ const socklen_t len)
+{
+ uint32_t u, p;
+ struct sockaddr_in *bps = (struct sockaddr_in *) bp->ipv4;
+
+ if (bp->ipv4len != len || len <= 0)
+ return 0;
+
+ u = addr->sin_addr.s_addr;
+ p = bps->sin_addr.s_addr;
+
+ return u == p;
+}
+
+/* Compare an IPv6 backend to a IPv6 addr/len */
+static int
+vdi_dns_comp_addrinfo6(struct backend *bp,
+ struct sockaddr_in6 *addr,
+ const socklen_t len)
+{
+ uint8_t *u, *p;
+ int i;
+ struct sockaddr_in6 *bps = (struct sockaddr_in6 *) bp->ipv6;
+
+ if (bp->ipv6len != len || len <= 0)
+ return 0;
+
+ u = addr->sin6_addr.s6_addr;
+ p = bps->sin6_addr.s6_addr;
+
+ for (i=0; i < 16; i++) {
+ if (u[i] != p[i])
+ return 0;
+ }
+
+ return 1;
+}
+
+struct backend *
+vdi_get_backend_if_simple(const struct director *d);
+
+/* Check if a backends socket is the same as addr */
+static int
+vdi_dns_comp_addrinfo(struct director *dir,
+ struct sockaddr *addr,
+ const socklen_t len)
+{
+ struct backend *bp;
+ bp = vdi_get_backend_if_simple(dir);
+ if (addr->sa_family == PF_INET && bp->ipv4) {
+ return (vdi_dns_comp_addrinfo4(bp, (struct sockaddr_in *)
+ addr, len));
+ } else if (addr->sa_family == PF_INET6 && bp->ipv6) {
+ return (vdi_dns_comp_addrinfo6(bp, (struct sockaddr_in6 *)
+ addr, len));
+ }
+ return 0;
+}
+
+/* Pick a host from an existing hostgroup.
+ * Balance on round-robin if multiple backends are available and only pick
+ * healthy ones.
+ */
+static struct director *
+vdi_dns_pick_host(const struct sess *sp, struct vdi_dns_hostgroup *group) {
+ int initial, i, nhosts, current;
+ if (group->nhosts == 0)
+ return (NULL); // In case of error.
+ if (group->next_host >= group->nhosts)
+ group->next_host = 0;
+
+ /* Pick a healthy backend */
+ initial = group->next_host;
+ nhosts = group->nhosts;
+ for (i=0; i < nhosts; i++) {
+ if (i + initial >= nhosts)
+ current = i + initial - nhosts;
+ else
+ current = i + initial;
+ if (VBE_Healthy_sp(sp, group->hosts[current])) {
+ group->next_host = current+1;
+ return group->hosts[current];
+ }
+ }
+
+ return NULL;
+}
+
+/* Remove an item from the dns cache.
+ * If *group is NULL, the head is popped.
+ * Remember locking.
+ */
+static void
+vdi_dns_pop_cache(struct vdi_dns *vs,
+ struct vdi_dns_hostgroup *group)
+{
+ if (group == NULL)
+ group = VTAILQ_LAST( &vs->cachelist, _cachelist );
+ assert(group != NULL);
+ free(group->hostname);
+ VTAILQ_REMOVE(&vs->cachelist, group, list);
+ FREE_OBJ(group);
+ vs->ncachelist--;
+}
+
+/* Dummy in case someone feels like optimizing it? meh...
+ */
+static inline int
+vdi_dns_groupmatch(const struct vdi_dns_hostgroup *group, const char *hostname)
+{
+ return !strcmp(group->hostname, hostname);
+}
+
+/* Search the cache for 'hostname' and put a backend-pointer as necessary,
+ * return true for cache hit. This could still be a NULL backend if we did
+ * a lookup earlier and didn't find a host (ie: cache failed too)
+ *
+ * if rwlock is true, the first timed out object found (if any) is popped
+ * and freed.
+ */
+static int
+vdi_dns_cache_has(const struct sess *sp,
+ struct vdi_dns *vs,
+ const char *hostname,
+ struct director **backend,
+ int rwlock)
+{
+ struct director *ret;
+ struct vdi_dns_hostgroup *hostgr;
+ struct vdi_dns_hostgroup *hostgr2;
+ VTAILQ_FOREACH_SAFE(hostgr, &vs->cachelist, list, hostgr2) {
+ CHECK_OBJ_NOTNULL(hostgr, VDI_DNSDIR_MAGIC);
+ if (hostgr->ttl <= sp->t_req) {
+ if (rwlock)
+ vdi_dns_pop_cache(vs, hostgr);
+ return 0;
+ }
+ if (vdi_dns_groupmatch(hostgr, hostname)) {
+ ret = (vdi_dns_pick_host(sp, hostgr));
+ *backend = ret;
+ if (*backend != NULL)
+ CHECK_OBJ_NOTNULL(*backend, DIRECTOR_MAGIC);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* Add a newly cached item to the dns cache list.
+ * (Sorry for the list_add/_add confusion...)
+ */
+static void
+vdi_dns_cache_list_add(const struct sess *sp,
+ struct vdi_dns *vs,
+ struct vdi_dns_hostgroup *new)
+{
+ if (vs->ncachelist >= VDI_DNS_MAX_CACHE) {
+ VSC_main->dir_dns_cache_full++;
+ vdi_dns_pop_cache(vs, NULL);
+ }
+ CHECK_OBJ_NOTNULL(new, VDI_DNSDIR_MAGIC);
+ assert(new->hostname != 0);
+ new->ttl = sp->t_req + vs->ttl;
+ VTAILQ_INSERT_HEAD(&vs->cachelist, new, list);
+ vs->ncachelist++;
+}
+
+/* Add an item to the dns cache.
+ * XXX: Might want to factor the getaddrinfo() out of the lock and do the
+ * cache_has() afterwards to do multiple dns lookups in parallel...
+ */
+static int
+vdi_dns_cache_add(const struct sess *sp,
+ struct vdi_dns *vs,
+ const char *hostname,
+ struct director **backend)
+{
+ int error, i, host = 0;
+ struct addrinfo *res0, *res, hint;
+ struct vdi_dns_hostgroup *new;
+ /* Due to possible race while upgrading the lock, we have to
+ * recheck if the result is already looked up. The overhead for
+ * this is insignificant unless dns isn't cached properly (all
+ * unique names or something equally troublesome).
+ */
+
+ if (vdi_dns_cache_has(sp, vs, hostname, backend, 1))
+ return 1;
+
+ memset(&hint, 0, sizeof hint);
+ hint.ai_family = PF_UNSPEC;
+ hint.ai_socktype = SOCK_STREAM;
+
+ ALLOC_OBJ(new, VDI_DNSDIR_MAGIC);
+ new->hostname = calloc(sizeof(char), strlen(hostname)+1);
+ assert(new->hostname != NULL);
+ strcpy(new->hostname, hostname);
+
+ error = getaddrinfo(hostname, "80", &hint, &res0);
+ VSC_main->dir_dns_lookups++;
+ if (error) {
+ vdi_dns_cache_list_add(sp, vs, new);
+ VSC_main->dir_dns_failed++;
+ return 0;
+ }
+
+ for (res = res0; res; res = res->ai_next) {
+ if (res->ai_family != PF_INET &&
+ res->ai_family != PF_INET6)
+ continue;
+
+ for (i = 0; i < vs->nhosts; i++) {
+ if (vdi_dns_comp_addrinfo(vs->hosts[i],
+ res->ai_addr, res->ai_addrlen)) {
+ new->hosts[host] = vs->hosts[i];
+ CHECK_OBJ_NOTNULL(new->hosts[host], DIRECTOR_MAGIC);
+ host++;
+ }
+ }
+ }
+ freeaddrinfo(res0);
+
+ new->nhosts = host;
+ vdi_dns_cache_list_add(sp, vs, new);
+ *backend = vdi_dns_pick_host(sp, new);
+ return 1;
+}
+
+/* Walk through the cached lookups looking for the relevant host, add one
+ * if it isn't already cached.
+ *
+ * Returns a backend or NULL.
+ */
+static struct director *
+vdi_dns_walk_cache(const struct sess *sp,
+ struct vdi_dns *vs,
+ const char *hostname)
+{
+ struct director *backend = NULL;
+ int ret;
+ AZ(pthread_rwlock_rdlock(&vs->rwlock));
+ ret = vdi_dns_cache_has(sp, vs, hostname, &backend, 0);
+ pthread_rwlock_unlock(&vs->rwlock);
+ if (!ret) {
+ AZ(pthread_rwlock_wrlock(&vs->rwlock));
+ ret = vdi_dns_cache_add(sp, vs, hostname, &backend);
+ pthread_rwlock_unlock(&vs->rwlock);
+ } else
+ VSC_main->dir_dns_hit++;
+
+ /* Bank backend == cached a failure, so to speak */
+ if (backend != NULL)
+ CHECK_OBJ_NOTNULL(backend, DIRECTOR_MAGIC);
+ return backend;
+}
+
+/* Parses the Host:-header and heads out to find a backend.
+ */
+static struct director *
+vdi_dns_find_backend(const struct sess *sp, struct vdi_dns *vs)
+{
+ struct director *ret;
+ struct http *hp;
+ char *p;
+ char hostname[NI_MAXHOST];
+ int i;
+
+ /* bereq is only present after recv et. al, otherwise use req (ie:
+ * use req for health checks in vcl_recv and such).
+ */
+ if (sp->wrk->bereq)
+ hp = sp->wrk->bereq;
+ else
+ hp = sp->http;
+
+
+ CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
+ if (http_GetHdr(hp, H_Host, &p) == 0)
+ return (NULL);
+
+ /* We need a working copy since it's going to be modified */
+ strncpy(hostname, p, sizeof(hostname));
+
+ /* remove port-portion of the Host-header, if present. */
+ for (i = 0; i < strlen(hostname); i++) {
+ if (hostname[i] == ':') {
+ hostname[i] = '\0';
+ break;
+ }
+ }
+
+ if (vs->suffix)
+ strncat(hostname, vs->suffix, sizeof(hostname) - strlen(hostname));
+
+ ret = vdi_dns_walk_cache(sp, vs, hostname);
+ return ret;
+}
+
+static struct vbe_conn *
+vdi_dns_getfd(const struct director *director, struct sess *sp)
+{
+ int i;
+ struct vdi_dns *vs;
+ struct director *dir;
+ struct vbe_conn *vbe;
+
+ CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+ CHECK_OBJ_NOTNULL(director, DIRECTOR_MAGIC);
+ CAST_OBJ_NOTNULL(vs, director->priv, VDI_DNS_MAGIC);
+
+ dir = vdi_dns_find_backend(sp, vs);
+ if (!dir || !VBE_Healthy_sp(sp, dir))
+ return (NULL);
+
+ vbe = VBE_GetFd(dir, sp);
+ return (vbe);
+}
+
+static unsigned
+vdi_dns_healthy(double now, const struct director *dir, uintptr_t target)
+{
+ return 1;
+ /*
+ struct vdi_dns *vs;
+ struct director *dir;
+ int i;
+
+ CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+ CHECK_OBJ_NOTNULL(sp->director, DIRECTOR_MAGIC);
+ CAST_OBJ_NOTNULL(vs, sp->director->priv, VDI_DNS_MAGIC);
+
+ dir = vdi_dns_find_backend(sp, vs);
+
+ if (dir)
+ return 1;
+ return 0;
+ */
+}
+
+/*lint -e{818} not const-able */
+static void
+vdi_dns_fini(struct director *d)
+{
+ int i;
+ struct vdi_dns *vs;
+ struct director **vh;
+
+ CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
+ CAST_OBJ_NOTNULL(vs, d->priv, VDI_DNS_MAGIC);
+
+ vh = vs->hosts;
+ free(vs->hosts);
+ free(vs->dir.vcl_name);
+ vs->dir.magic = 0;
+ /* FIXME: Free the cache */
+ pthread_rwlock_destroy(&vs->rwlock);
+ FREE_OBJ(vs);
+}
+
+void
+VRT_init_dir_dns(struct cli *cli, struct director **bp, int idx,
+ const void *priv)
+{
+ const struct vrt_dir_dns *t;
+ struct vdi_dns *vs;
+ const struct vrt_dir_dns_entry *te;
+ int i;
+
+ ASSERT_CLI();
+ (void)cli;
+ t = priv;
+ ALLOC_OBJ(vs, VDI_DNS_MAGIC);
+ XXXAN(vs);
+ vs->hosts = calloc(sizeof(struct director *), t->nmember);
+ XXXAN(vs->hosts);
+
+ vs->dir.magic = DIRECTOR_MAGIC;
+ vs->dir.priv = vs;
+ vs->dir.name = "dns";
+ REPLACE(vs->dir.vcl_name, t->name);
+ vs->dir.getfd = vdi_dns_getfd;
+ vs->dir.fini = vdi_dns_fini;
+ vs->dir.healthy = vdi_dns_healthy;
+
+ vs->suffix = t->suffix;
+ vs->ttl = t->ttl;
+
+ te = t->members;
+ for (i = 0; i < t->nmember; i++, te++)
+ vs->hosts[i] = bp[te->host];
+ vs->nhosts = t->nmember;
+ vs->ttl = t->ttl;
+ VTAILQ_INIT(&vs->cachelist);
+ pthread_rwlock_init(&vs->rwlock, NULL);
+ bp[idx] = &vs->dir;
+}
Modified: trunk/varnish-cache/doc/sphinx/reference/vcl.rst
===================================================================
--- trunk/varnish-cache/doc/sphinx/reference/vcl.rst 2010-07-30 23:17:36 UTC (rev 5061)
+++ trunk/varnish-cache/doc/sphinx/reference/vcl.rst 2010-08-01 14:45:22 UTC (rev 5062)
@@ -141,6 +141,34 @@
The round-robin does not take any options.
+The DNS director
+~~~~~~~~~~~~~~~~
+
+The DNS director can use backends in three different ways. Either like the
+random or round-robin director or using .list::
+
+ director directorname dns {
+ .list = {
+ .host_header = "www.example.com";
+ .port = "80";
+ .connection_timeout = 0.4;
+ "192.168.15.0"/24;
+ "192.168.16.128"/25;
+ }
+ .ttl = 5m;
+ .suffix = "internal.example.net";
+ }
+
+This will specify 384 backends, all using port 80 and a connection timeout
+of 0.4s. Options must come before the list of IPs in the .list statement.
+
+The .ttl defines the cache duration of the DNS lookups.
+
+The above example will append "internal.example.net" to the incoming Host
+header supplied by the client, before looking it up. All settings are
+optional.
+
+
Backend probes
--------------
Modified: trunk/varnish-cache/include/stat_field.h
===================================================================
--- trunk/varnish-cache/include/stat_field.h 2010-07-30 23:17:36 UTC (rev 5061)
+++ trunk/varnish-cache/include/stat_field.h 2010-08-01 14:45:22 UTC (rev 5062)
@@ -153,6 +153,12 @@
MAC_STAT(client_drop_late, uint64_t, 0, 'a', "Connection dropped late")
MAC_STAT(uptime, uint64_t, 0, 'a', "Client uptime")
+MAC_STAT(dir_dns_lookups, uint64_t, 0, 'a', "DNS director lookups")
+MAC_STAT(dir_dns_failed, uint64_t, 0, 'a', "DNS director failed lookups")
+MAC_STAT(dir_dns_hit, uint64_t, 0, 'a', "DNS director cached lookups hit")
+MAC_STAT(dir_dns_cache_full, uint64_t, 0, 'a', "DNS director full dnscache")
+
+
MAC_STAT(critbit_cooler, uint64_t, 0, 'i', "Objhdr's on cool list")
#ifdef __MAC_STAT
Modified: trunk/varnish-cache/include/vrt.h
===================================================================
--- trunk/varnish-cache/include/vrt.h 2010-07-30 23:17:36 UTC (rev 5061)
+++ trunk/varnish-cache/include/vrt.h 2010-08-01 14:45:22 UTC (rev 5062)
@@ -107,7 +107,22 @@
const struct vrt_dir_round_robin_entry *members;
};
+/*
+ * A director with dns-based selection
+ */
+struct vrt_dir_dns_entry {
+ int host;
+};
+
+struct vrt_dir_dns {
+ const char *name;
+ const char *suffix;
+ const double ttl;
+ unsigned nmember;
+ const struct vrt_dir_dns_entry *members;
+};
+
/*
* other stuff.
* XXX: document when bored
Modified: trunk/varnish-cache/include/vsc_fields.h
===================================================================
--- trunk/varnish-cache/include/vsc_fields.h 2010-07-30 23:17:36 UTC (rev 5061)
+++ trunk/varnish-cache/include/vsc_fields.h 2010-08-01 14:45:22 UTC (rev 5062)
@@ -154,6 +154,12 @@
VSC_F_MAIN(client_drop_late, uint64_t, 0, 'a', "Connection dropped late")
VSC_F_MAIN(uptime, uint64_t, 0, 'a', "Client uptime")
+VSC_F_MAIN(dir_dns_lookups, uint64_t, 0, 'a', "DNS director lookups")
+VSC_F_MAIN(dir_dns_failed, uint64_t, 0, 'a', "DNS director failed lookups")
+VSC_F_MAIN(dir_dns_hit, uint64_t, 0, 'a', "DNS director cached lookups hit")
+VSC_F_MAIN(dir_dns_cache_full, uint64_t, 0, 'a', "DNS director full dnscache")
+
+
VSC_F_MAIN(critbit_cooler, uint64_t, 0, 'i', "Objhdr's on cool list")
#ifdef __VSC_F_MAIN
Modified: trunk/varnish-cache/lib/libvcl/Makefile.am
===================================================================
--- trunk/varnish-cache/lib/libvcl/Makefile.am 2010-07-30 23:17:36 UTC (rev 5061)
+++ trunk/varnish-cache/lib/libvcl/Makefile.am 2010-08-01 14:45:22 UTC (rev 5062)
@@ -19,6 +19,7 @@
vcc_compile.c \
vcc_dir_random.c \
vcc_dir_round_robin.c \
+ vcc_dir_dns.c \
vcc_expr.c \
vcc_parse.c \
$(builddir)/vcc_fixed_token.c \
Modified: trunk/varnish-cache/lib/libvcl/vcc_backend.c
===================================================================
--- trunk/varnish-cache/lib/libvcl/vcc_backend.c 2010-07-30 23:17:36 UTC (rev 5061)
+++ trunk/varnish-cache/lib/libvcl/vcc_backend.c 2010-08-01 14:45:22 UTC (rev 5062)
@@ -96,7 +96,7 @@
* and put it in an official sockaddr when we load the VCL.
*/
-static void
+void
Emit_Sockaddr(struct vcc *tl, const struct token *t_host,
const char *port)
{
@@ -189,7 +189,7 @@
* in that context.
*/
-static void
+void
vcc_EmitBeIdent(const struct vcc *tl, struct vsb *v,
int serial, const struct token *first, const struct token *last)
{
@@ -696,6 +696,7 @@
{ "random", vcc_ParseRandomDirector },
{ "client", vcc_ParseRandomDirector },
{ "round-robin", vcc_ParseRoundRobinDirector },
+ { "dns", vcc_ParseDnsDirector },
{ NULL, NULL }
};
Modified: trunk/varnish-cache/lib/libvcl/vcc_compile.h
===================================================================
--- trunk/varnish-cache/lib/libvcl/vcc_compile.h 2010-07-30 23:17:36 UTC (rev 5061)
+++ trunk/varnish-cache/lib/libvcl/vcc_compile.h 2010-08-01 14:45:22 UTC (rev 5062)
@@ -222,6 +222,9 @@
double vcc_DoubleVal(struct vcc *tl);
void vcc_Expr(struct vcc *tl, enum var_type fmt);
+/* vcc_dir_dns.c */
+parsedirector_f vcc_ParseDnsDirector;
+
/* vcc_obj.c */
extern const struct var vcc_vars[];
Added: trunk/varnish-cache/lib/libvcl/vcc_dir_dns.c
===================================================================
--- trunk/varnish-cache/lib/libvcl/vcc_dir_dns.c (rev 0)
+++ trunk/varnish-cache/lib/libvcl/vcc_dir_dns.c 2010-08-01 14:45:22 UTC (rev 5062)
@@ -0,0 +1,364 @@
+/*-
+ * Copyright (c) 2009 Redpill Linpro AS
+ * Copyright (c) 2010 Varnish Software AS
+ * All rights reserved.
+ *
+ * Author: Kristian Lyngstol <kristian at bohemians.org>
+ *
+ * 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 "svnid.h"
+SVNID("$Id$")
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netdb.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <limits.h>
+
+#include "vsb.h"
+
+#include "vcc_priv.h"
+#include "vcc_compile.h"
+#include "libvarnish.h"
+
+/*--------------------------------------------------------------------
+ * Parse directors
+ */
+
+
+void
+vcc_EmitBeIdent(const struct vcc *tl, struct vsb *v,
+ int serial, const struct token *first, const struct token *last);
+void
+Emit_Sockaddr(struct vcc *tl, const struct token *t_host,
+ const char *port);
+
+struct vcc_dir_backend_defaults {
+ char *port;
+ char *hostheader;
+ double connect_timeout;
+ double first_byte_timeout;
+ double between_bytes_timeout;
+ unsigned max_connections;
+ unsigned saint;
+} b_defaults;
+
+void vcc_dir_initialize_defaults(void)
+{
+ b_defaults.port = NULL;
+ b_defaults.hostheader = NULL;
+ b_defaults.connect_timeout = -1.0;
+ b_defaults.first_byte_timeout = -1.0;
+ b_defaults.between_bytes_timeout = -1.0;
+ b_defaults.max_connections = UINT_MAX;
+ b_defaults.saint = UINT_MAX;
+}
+
+struct token *dns_first;
+void
+print_backend(struct vcc *tl,
+ uint32_t serial,
+ uint8_t *ip)
+{
+ char vgcname[BUFSIZ];
+ char strip[16];
+ struct token tmptok;
+ struct vsb *vsb;
+ sprintf(strip, "%d.%d.%d.%d",ip[3],ip[2],ip[1],ip[0]);
+ tmptok.dec = strip;
+ sprintf(vgcname,"%.*s_%u",PF(tl->t_dir),serial);
+ vsb = vsb_newauto();
+ AN(vsb);
+ tl->fb = vsb;
+ Fc(tl, 0, "\t{ .host = VGC_backend_%s },\n",vgcname);
+ Fh(tl, 1, "\n#define VGC_backend_%s %u\n", vgcname, serial);
+
+ Fb(tl, 0, "\nstatic const struct vrt_backend vgc_dir_priv_%s = {\n", vgcname);
+
+ Fb(tl, 0, "\t.vcl_name = \"%.*s", PF(tl->t_dir));
+ if (serial >= 0)
+ Fb(tl, 0, "[%d]", serial);
+ Fb(tl, 0, "\",\n");
+ Emit_Sockaddr(tl, &tmptok, b_defaults.port);
+ vcc_EmitBeIdent(tl, tl->fb, serial, dns_first , tl->t);
+
+ Fb(tl, 0, "\t.hosthdr = \"");
+ if (b_defaults.hostheader != NULL)
+ Fb(tl,0, b_defaults.hostheader);
+ else
+ Fb(tl,0, strip);
+ Fb(tl, 0, "\",\n");
+
+ Fb(tl, 0, "\t.saintmode_threshold = %d,\n",b_defaults.saint);
+#define FB_TIMEOUT(type) do { \
+ if (b_defaults.type != -1.0) \
+ Fb(tl, 0, "\t.%s = %g,\n",#type,b_defaults.type); \
+ } while (0)
+ FB_TIMEOUT(connect_timeout);
+ FB_TIMEOUT(first_byte_timeout);
+ FB_TIMEOUT(between_bytes_timeout);
+
+ Fb(tl, 0, "};\n");
+ tl->fb = NULL;
+ vsb_finish(vsb);
+ Fh(tl, 0, "%s", vsb_data(vsb));
+ vsb_delete(vsb);
+ Fi(tl, 0, "\tVRT_init_dir(cli, VCL_conf.director, \"simple\",\n"
+ "\t VGC_backend_%s, &vgc_dir_priv_%s);\n", vgcname, vgcname);
+ Ff(tl, 0, "\tVRT_fini_dir(cli, VGCDIR(%s));\n", vgcname);
+ tl->ndirector++;
+}
+/*
+ * Output backends for all IPs in the range supplied by
+ * "a[0].a[1].a[2].a[3]/inmask".
+ *
+ * XXX:
+ * This assumes that a uint32_t can be safely accessed as an array of 4
+ * uint8_ts.
+ */
+void
+vcc_dir_dns_makebackend(struct vcc *tl,
+ uint32_t *serial,
+ unsigned char a[],
+ int inmask)
+{
+ uint32_t ip4=0;
+ uint32_t ip4end;
+ uint32_t mask = UINT32_MAX << (32-inmask);
+
+ ip4 |= a[0] << 24;
+ ip4 |= a[1] << 16;
+ ip4 |= a[2] << 8;
+ ip4 |= a[3] ;
+
+ ip4end = ip4 | ~mask;
+ assert (ip4 == (ip4 & mask));
+
+/* printf("uip4: \t0x%.8X\na: \t0x", ip4,ip4);
+ for (int i=0;i<4;i++) printf("%.2X",a[i]);
+ printf("\nmask:\t0x%.8X\nend:\t0x%.8X\n", mask, ip4end);
+*/
+ while (ip4 <= ip4end) {
+ uint8_t *b;
+ b=(uint8_t *)&ip4;
+ (*serial)++;
+ print_backend(tl, *serial, b);
+ ip4++;
+ }
+}
+void
+vcc_dir_dns_parse_backend_options(struct vcc *tl)
+{
+ struct fld_spec *fs;
+ struct token *t_field;
+ double t;
+ unsigned u;
+ vcc_dir_initialize_defaults();
+ fs = vcc_FldSpec(tl,
+ "?port",
+ "?host_header",
+ "?connect_timeout",
+ "?first_byte_timeout",
+ "?between_bytes_timeout",
+ "?max_connections",
+ "?saintmode_threshold",
+ NULL);
+ while (tl->t->tok != CSTR) {
+
+ vcc_IsField(tl, &t_field, fs);
+ ERRCHK(tl);
+ if (vcc_IdIs(t_field, "port")) {
+ ExpectErr(tl, CSTR);
+ assert(tl->t->dec != NULL);
+ b_defaults.port = strdup(tl->t->dec);
+ assert(b_defaults.port);
+ vcc_NextToken(tl);
+ SkipToken(tl, ';');
+ } else if (vcc_IdIs(t_field, "host_header")) {
+ ExpectErr(tl, CSTR);
+ assert(tl->t->dec != NULL);
+ b_defaults.hostheader = strdup(tl->t->dec);
+ assert(b_defaults.hostheader);
+ vcc_NextToken(tl);
+ SkipToken(tl, ';');
+ } else if (vcc_IdIs(t_field, "connect_timeout")) {
+ vcc_TimeVal(tl, &t);
+ ERRCHK(tl);
+ b_defaults.connect_timeout = t;
+ SkipToken(tl, ';');
+ } else if (vcc_IdIs(t_field, "first_byte_timeout")) {
+ vcc_TimeVal(tl, &t);
+ ERRCHK(tl);
+ b_defaults.first_byte_timeout = t;
+ SkipToken(tl, ';');
+ } else if (vcc_IdIs(t_field, "between_bytes_timeout")) {
+ vcc_TimeVal(tl, &t);
+ ERRCHK(tl);
+ b_defaults.between_bytes_timeout = t;
+ SkipToken(tl, ';');
+ } else if (vcc_IdIs(t_field, "max_connections")) {
+ u = vcc_UintVal(tl);
+ ERRCHK(tl);
+ SkipToken(tl, ';');
+ b_defaults.max_connections = u;
+ } else if (vcc_IdIs(t_field, "saintmode_threshold")) {
+ u = vcc_UintVal(tl);
+ /* UINT_MAX == magic number to mark as unset, so
+ * not allowed here.
+ */
+ if (u == UINT_MAX) {
+ vsb_printf(tl->sb,
+ "Value outside allowed range: ");
+ vcc_ErrToken(tl, tl->t);
+ vsb_printf(tl->sb, " at\n");
+ vcc_ErrWhere(tl, tl->t);
+ }
+ ERRCHK(tl);
+ b_defaults.saint = u;
+ SkipToken(tl, ';');
+ } else {
+ ErrInternal(tl);
+ return;
+ }
+
+ }
+}
+
+/* Parse a list of backends with optional /mask notation, then print out
+ * all relevant backends.
+ */
+void
+vcc_dir_dns_parse_list(struct vcc *tl, int *serial)
+{
+ unsigned char a[4],mask;
+ int ret, nitem;
+ ERRCHK(tl);
+ SkipToken(tl, '{');
+ if (tl->t->tok != CSTR)
+ vcc_dir_dns_parse_backend_options(tl);
+ while (tl->t->tok == CSTR) {
+ mask = 32;
+ ret = sscanf(tl->t->dec, "%d.%d.%d.%d",&a[0],&a[1],&a[2],&a[3],&a[4]);
+ assert(ret == 4);
+ vcc_NextToken(tl);
+ if (tl->t->tok == '/') {
+ vcc_NextToken(tl);
+ mask = vcc_UintVal(tl);
+ ERRCHK(tl);
+ }
+ vcc_dir_dns_makebackend(tl,serial,a,mask);
+ SkipToken(tl,';');
+ }
+ ExpectErr(tl, '}');
+}
+
+void
+vcc_ParseDnsDirector(struct vcc *tl)
+{
+ struct token *t_field, *t_be, *t_suffix = NULL;
+ double ttl = 60.0;
+ int nbh, nelem = 0;
+ struct fld_spec *fs;
+ const char *first;
+ char *p;
+ dns_first = tl->t;
+ tl->fb = tl->fc;
+ fs = vcc_FldSpec(tl, "!backend", "?ttl", "?suffix","?list", NULL);
+
+ Fc(tl, 0, "\nstatic const struct vrt_dir_dns_entry "
+ "vddnse_%.*s[] = {\n", PF(tl->t_dir));
+
+ for (; tl->t->tok != '}'; ) { /* List of members */
+ if (tl->t->tok == '{') {
+ nelem++;
+ first = "";
+ t_be = tl->t;
+ vcc_ResetFldSpec(fs);
+ nbh = -1;
+
+ ExpectErr(tl, '{');
+ vcc_NextToken(tl);
+ Fc(tl, 0, "\t{");
+
+ while (tl->t->tok != '}') { /* Member fields */
+ vcc_IsField(tl, &t_field, fs);
+ ERRCHK(tl);
+ if (vcc_IdIs(t_field, "backend")) {
+ vcc_ParseBackendHost(tl, nelem, &p);
+ ERRCHK(tl);
+ AN(p);
+ Fc(tl, 0, "%s .host = VGC_backend_%s", first, p);
+ } else {
+ ErrInternal(tl);
+ }
+ first = ", ";
+ }
+ vcc_FieldsOk(tl, fs);
+ if (tl->err) {
+ vsb_printf(tl->sb,
+ "\nIn member host specification starting at:\n");
+ vcc_ErrWhere(tl, t_be);
+ return;
+ }
+ Fc(tl, 0, " },\n");
+ } else {
+ vcc_IsField(tl, &t_field, fs);
+ ERRCHK(tl);
+ if (vcc_IdIs(t_field, "suffix")) {
+ ExpectErr(tl, CSTR);
+ t_suffix = tl->t;
+ vcc_NextToken(tl);
+ ExpectErr(tl, ';');
+ } else if (vcc_IdIs(t_field, "ttl")) {
+ vcc_RTimeVal(tl, &ttl);
+ ExpectErr(tl, ';');
+ } else if (vcc_IdIs(t_field, "list")) {
+ vcc_dir_dns_parse_list(tl,&nelem);
+ }
+ }
+ vcc_NextToken(tl);
+ }
+ Fc(tl, 0, "};\n");
+ Fc(tl, 0,
+ "\nstatic const struct vrt_dir_dns vgc_dir_priv_%.*s = {\n",
+ PF(tl->t_dir));
+ Fc(tl, 0, "\t.name = \"%.*s\",\n", PF(tl->t_dir));
+ Fc(tl, 0, "\t.nmember = %d,\n", nelem);
+ Fc(tl, 0, "\t.members = vddnse_%.*s,\n", PF(tl->t_dir));
+ Fc(tl, 0, "\t.suffix = ");
+ if (t_suffix)
+ Fc(tl, 0, "%.*s", PF(t_suffix));
+ else
+ Fc(tl, 0, "\"\"");
+ Fc(tl, 0, ",\n");
+ Fc(tl, 0, "\t.ttl = %f", ttl);
+ Fc(tl, 0, ",\n");
+ Fc(tl, 0, "};\n");
+ Ff(tl, 0, "\tVRT_fini_dir(cli, VGCDIR(_%.*s));\n", PF(tl->t_dir));
+}
More information about the varnish-commit
mailing list