Commit 9f32605d authored by Kristian Lyngstøl's avatar Kristian Lyngstøl

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.



git-svn-id: http://www.varnish-cache.org/svn/trunk/varnish-cache@5062 d4fa192b-c00b-0410-8231-f00ffab90ce4
parent 050abf19
......@@ -21,6 +21,7 @@ varnishd_SOURCES = \
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 \
......
......@@ -536,6 +536,24 @@ struct vdi_simple {
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)
{
......
......@@ -272,6 +272,8 @@ VRT_init_dir(struct cli *cli, struct director **dir, const char *name,
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"))
......
This diff is collapsed.
......@@ -141,6 +141,34 @@ The round-robin director
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
--------------
......
......@@ -153,6 +153,12 @@ MAC_STAT(accept_fail, uint64_t, 0, 'a', "Accept failures")
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
......
......@@ -107,6 +107,21 @@ struct vrt_dir_round_robin {
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.
......
......@@ -154,6 +154,12 @@ VSC_F_MAIN(accept_fail, uint64_t, 0, 'a', "Accept failures")
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
......
......@@ -19,6 +19,7 @@ libvcl_la_SOURCES = \
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 \
......
......@@ -96,7 +96,7 @@ CheckHostPort(const char *host, const char *port)
* 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 @@ Emit_Sockaddr(struct vcc *tl, const struct token *t_host,
* 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 @@ static const struct dirlist {
{ "random", vcc_ParseRandomDirector },
{ "client", vcc_ParseRandomDirector },
{ "round-robin", vcc_ParseRoundRobinDirector },
{ "dns", vcc_ParseDnsDirector },
{ NULL, NULL }
};
......
......@@ -222,6 +222,9 @@ unsigned vcc_UintVal(struct vcc *tl);
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[];
......
/*-
* Copyright (c) 2009 Redpill Linpro AS
* Copyright (c) 2010 Varnish Software AS
* All rights reserved.
*
* Author: Kristian Lyngstol <kristian@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));
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment