Commit b06cdbba authored by Nils Goroll's avatar Nils Goroll

Add bans on obj.ttl, obj.age, obj.grace and obj.keep

Also add a duration argument type and the operators >, >=, < and <=
for use with it (besides the existing == and !=).

obj.ttl and obj.age are compared relative to the time the ban was
issued, as required by the fact that bans are evaluated some arbitrary
time after they are created. For this reason, the ban_dups parameter
has no effect on obj.ttl and obj.age bans, duplicates of bans using
these fields are never removed.

obj.grace and obj.keep are compared as absolute values.

In an effort to support The Proprietary Stevedore (tm) [or any other storage
engine supporting persistent bans - if any?], the existing ban serialisation
format is preserved and upgrades _should_ work seamlessly. But downgrades won't.

Using the BANS_HAS_* macros, we try to make the relationship between the ban
serialisation in cache_ban_build.c and access in cache_ban.c more obvious.

We now generate all fields and operators from table files as well as
the definition of allowed operators for fields. The user help text
about allowed operators is generated at init time.
parent 286604ce
......@@ -31,6 +31,7 @@
#include "config.h"
#include <pcre.h>
#include <stdio.h>
#include "cache_varnishd.h"
#include "cache_ban.h"
......@@ -40,6 +41,10 @@
#include "vend.h"
#include "vmb.h"
/* cache_ban_build.c */
void BAN_Build_Init(void);
void BAN_Build_Fini(void);
struct lock ban_mtx;
int ban_shutdown;
struct banhead_s ban_head = VTAILQ_HEAD_INITIALIZER(ban_head);
......@@ -55,9 +60,18 @@ struct ban_test {
uint8_t arg1;
const char *arg1_spec;
const char *arg2;
double arg2_double;
const void *arg2_spec;
};
static const char * const arg_name[BAN_ARGARRSZ + 1] = {
#define PVAR(a, b, c) [BAN_ARGIDX(c)] = (a),
#include "tbl/ban_vars.h"
[BAN_ARGARRSZ] = NULL
};
extern const char * const oper[BAN_OPERARRSZ + 1];
/*--------------------------------------------------------------------
* Storage handling of bans
*/
......@@ -148,6 +162,9 @@ ban_equal(const uint8_t *bs1, const uint8_t *bs2)
u = vbe32dec(bs1 + BANS_LENGTH);
if (u != vbe32dec(bs2 + BANS_LENGTH))
return (0);
if (bs1[BANS_FLAGS] & BANS_FLAG_NODEDUP)
return (0);
return (!memcmp(bs1 + BANS_LENGTH, bs2 + BANS_LENGTH, u - BANS_LENGTH));
}
......@@ -200,16 +217,25 @@ ban_get_lump(const uint8_t **bs)
static void
ban_iter(const uint8_t **bs, struct ban_test *bt)
{
uint64_t dtmp;
memset(bt, 0, sizeof *bt);
bt->arg2_double = nan("");
bt->arg1 = *(*bs)++;
if (bt->arg1 == BANS_ARG_REQHTTP || bt->arg1 == BANS_ARG_OBJHTTP) {
if (BANS_HAS_ARG1_SPEC(bt->arg1)) {
bt->arg1_spec = (const char *)*bs;
(*bs) += (*bs)[0] + 2;
}
if (BANS_HAS_ARG2_DOUBLE(bt->arg1)) {
dtmp = vbe64dec(ban_get_lump(bs));
bt->oper = *(*bs)++;
memcpy(&bt->arg2_double, &dtmp, sizeof dtmp);
return;
}
bt->arg2 = ban_get_lump(bs);
bt->oper = *(*bs)++;
if (bt->oper == BANS_OPER_MATCH || bt->oper == BANS_OPER_NMATCH)
if (BANS_HAS_ARG2_SPEC(bt->oper))
bt->arg2_spec = ban_get_lump(bs);
}
......@@ -456,20 +482,32 @@ BAN_Time(const struct ban *b)
*/
int
ban_evaluate(struct worker *wrk, const uint8_t *bs, struct objcore *oc,
ban_evaluate(struct worker *wrk, const uint8_t *bsarg, struct objcore *oc,
const struct http *reqhttp, unsigned *tests)
{
struct ban_test bt;
const uint8_t *be;
const uint8_t *bs, *be;
const char *p;
const char *arg1;
double darg1, darg2;
/*
* for ttl and age, fix the point in time such that banning refers to
* the same point in time when the ban is evaluated
*
* for grace/keep, we assume that the absolute values are pola and that
* users will most likely also specify a ttl criterion if they want to
* fix a point in time (such as "obj.ttl > 5h && obj.keep > 3h")
*/
bs = bsarg;
be = bs + ban_len(bs);
bs += BANS_HEAD_LEN;
while (bs < be) {
(*tests)++;
ban_iter(&bs, &bt);
arg1 = NULL;
darg1 = darg2 = nan("");
switch (bt.arg1) {
case BANS_ARG_URL:
AN(reqhttp);
......@@ -486,18 +524,42 @@ ban_evaluate(struct worker *wrk, const uint8_t *bs, struct objcore *oc,
case BANS_ARG_OBJSTATUS:
arg1 = HTTP_GetHdrPack(wrk, oc, H__Status);
break;
case BANS_ARG_OBJTTL:
darg1 = oc->ttl + oc->t_origin;
darg2 = bt.arg2_double + ban_time(bsarg);
break;
case BANS_ARG_OBJAGE:
darg1 = 0.0 - oc->t_origin;
darg2 = 0.0 - (ban_time(bsarg) - bt.arg2_double);
break;
case BANS_ARG_OBJGRACE:
darg1 = oc->grace;
darg2 = bt.arg2_double;
break;
case BANS_ARG_OBJKEEP:
darg1 = oc->keep;
darg2 = bt.arg2_double;
break;
default:
WRONG("Wrong BAN_ARG code");
}
switch (bt.oper) {
case BANS_OPER_EQ:
if (arg1 == NULL || strcmp(arg1, bt.arg2))
if (arg1 == NULL) {
if (isnan(darg1) || darg1 != darg2)
return (0);
} else if (strcmp(arg1, bt.arg2)) {
return (0);
}
break;
case BANS_OPER_NEQ:
if (arg1 != NULL && !strcmp(arg1, bt.arg2))
if (arg1 == NULL) {
if (! isnan(darg1) && darg1 == darg2)
return (0);
} else if (!strcmp(arg1, bt.arg2)) {
return (0);
}
break;
case BANS_OPER_MATCH:
if (arg1 == NULL ||
......@@ -511,6 +573,30 @@ ban_evaluate(struct worker *wrk, const uint8_t *bs, struct objcore *oc,
0, 0, NULL, 0) >= 0)
return (0);
break;
case BANS_OPER_GT:
AZ(arg1);
assert(! isnan(darg1));
if (!(darg1 > darg2))
return (0);
break;
case BANS_OPER_GTE:
AZ(arg1);
assert(! isnan(darg1));
if (!(darg1 >= darg2))
return (0);
break;
case BANS_OPER_LT:
AZ(arg1);
assert(! isnan(darg1));
if (!(darg1 < darg2))
return (0);
break;
case BANS_OPER_LTE:
AZ(arg1);
assert(! isnan(darg1));
if (!(darg1 <= darg2))
return (0);
break;
default:
WRONG("Wrong BAN_OPER code");
}
......@@ -668,46 +754,67 @@ ccf_ban(struct cli *cli, const char * const *av, void *priv)
}
}
#define Ms 60
#define Hs (Ms * 60)
#define Ds (Hs * 24)
#define Ws (Ds * 7)
#define Ys (Ds * 365)
#define Xfmt(buf, var, s, unit) \
((var) >= s && (var) % s == 0) \
bprintf((buf), "%ju" unit, (var) / s)
// XXX move to VTIM?
#define vdur_render(buf, dur) do { \
uint64_t dec = (uint64_t)floor(dur); \
uint64_t frac = (uint64_t)floor((dur) * 1e3) % UINT64_C(1000); \
if (dec == 0 && frac == 0) \
(void) strncpy(buf, "0s", sizeof(buf)); \
else if (dec == 0) \
bprintf((buf), "%jums", frac); \
else if (frac != 0) \
bprintf((buf), "%ju.%03jus", dec, frac); \
else if Xfmt(buf, dec, Ys, "y"); \
else if Xfmt(buf, dec, Ws, "w"); \
else if Xfmt(buf, dec, Ds, "d"); \
else if Xfmt(buf, dec, Hs, "h"); \
else if Xfmt(buf, dec, Ms, "m"); \
else \
bprintf((buf), "%jus", dec); \
} while (0)
static void
ban_render(struct cli *cli, const uint8_t *bs, int quote)
{
struct ban_test bt;
const uint8_t *be;
char buf[64];
be = bs + ban_len(bs);
bs += BANS_HEAD_LEN;
while (bs < be) {
ban_iter(&bs, &bt);
switch (bt.arg1) {
case BANS_ARG_URL:
VCLI_Out(cli, "req.url");
break;
case BANS_ARG_REQHTTP:
VCLI_Out(cli, "req.http.%.*s",
bt.arg1_spec[0] - 1, bt.arg1_spec + 1);
break;
case BANS_ARG_OBJHTTP:
VCLI_Out(cli, "obj.http.%.*s",
ASSERT_BAN_ARG(bt.arg1);
ASSERT_BAN_OPER(bt.oper);
if (BANS_HAS_ARG1_SPEC(bt.arg1))
VCLI_Out(cli, "%s.%.*s",
arg_name[BAN_ARGIDX(bt.arg1)],
bt.arg1_spec[0] - 1, bt.arg1_spec + 1);
break;
case BANS_ARG_OBJSTATUS:
VCLI_Out(cli, "obj.status");
break;
default:
WRONG("Wrong BANS_ARG");
}
switch (bt.oper) {
case BANS_OPER_EQ: VCLI_Out(cli, " == "); break;
case BANS_OPER_NEQ: VCLI_Out(cli, " != "); break;
case BANS_OPER_MATCH: VCLI_Out(cli, " ~ "); break;
case BANS_OPER_NMATCH: VCLI_Out(cli, " !~ "); break;
default:
WRONG("Wrong BANS_OPER");
}
if (quote)
VCLI_Quote(cli, bt.arg2);
else
VCLI_Out(cli, "%s", arg_name[BAN_ARGIDX(bt.arg1)]);
VCLI_Out(cli, " %s ", oper[BAN_OPERIDX(bt.oper)]);
if (BANS_HAS_ARG2_DOUBLE(bt.arg1)) {
vdur_render(buf, bt.arg2_double);
VCLI_Out(cli, "%s", buf);
} else if (quote) {
VCLI_Quote(cli, bt.arg2);
} else {
VCLI_Out(cli, "%s", bt.arg2);
}
if (bs < be)
VCLI_Out(cli, " && ");
}
......@@ -867,6 +974,7 @@ BAN_Init(void)
{
struct ban_proto *bp;
BAN_Build_Init();
Lck_New(&ban_mtx, lck_ban);
CLI_AddFuncs(ban_cmds);
......@@ -907,4 +1015,6 @@ BAN_Shutdown(void)
/* Export the ban list to compact it */
ban_export();
Lck_Unlock(&ban_mtx);
BAN_Build_Fini();
}
......@@ -73,16 +73,53 @@
#define BANS_FLAG_OBJ (1<<1)
#define BANS_FLAG_COMPLETED (1<<2)
#define BANS_FLAG_HTTP (1<<3)
#define BANS_FLAG_DURATION (1<<4)
#define BANS_FLAG_NODEDUP (1<<5)
#define BANS_OPER_EQ 0x10
#define _BANS_OPER_OFF BANS_OPER_EQ
#define BANS_OPER_NEQ 0x11
#define BANS_OPER_MATCH 0x12
#define BANS_OPER_NMATCH 0x13
#define BANS_OPER_GT 0x14
#define BANS_OPER_GTE 0x15
#define BANS_OPER_LT 0x16
#define BANS_OPER_LTE 0x17
#define _BANS_OPER_LIM (BANS_OPER_LTE + 1)
#define BAN_OPERIDX(x) ((x) - _BANS_OPER_OFF)
#define BAN_OPERARRSZ (_BANS_OPER_LIM - _BANS_OPER_OFF)
#define ASSERT_BAN_OPER(x) assert((x) >= _BANS_OPER_OFF && (x) < _BANS_OPER_LIM)
#define BANS_ARG_URL 0x18
#define _BANS_ARG_OFF BANS_ARG_URL
#define BANS_ARG_REQHTTP 0x19
#define BANS_ARG_OBJHTTP 0x1a
#define BANS_ARG_OBJSTATUS 0x1b
#define BANS_ARG_OBJTTL 0x1c
#define BANS_ARG_OBJAGE 0x1d
#define BANS_ARG_OBJGRACE 0x1e
#define BANS_ARG_OBJKEEP 0x1f
#define _BANS_ARG_LIM (BANS_ARG_OBJKEEP + 1)
#define BAN_ARGIDX(x) ((x) - _BANS_ARG_OFF)
#define BAN_ARGARRSZ (_BANS_ARG_LIM - _BANS_ARG_OFF)
#define ASSERT_BAN_ARG(x) assert((x) >= _BANS_ARG_OFF && (x) < _BANS_ARG_LIM)
// has an arg1_spec (BANS_FLAG_HTTP at build time)
#define BANS_HAS_ARG1_SPEC(arg) \
((arg) == BANS_ARG_REQHTTP || \
(arg) == BANS_ARG_OBJHTTP)
// has an arg2_spec (regex)
#define BANS_HAS_ARG2_SPEC(oper) \
((oper) == BANS_OPER_MATCH || \
(oper) == BANS_OPER_NMATCH)
// has an arg2_double (BANS_FLAG_DURATION at build time)
#define BANS_HAS_ARG2_DOUBLE(arg) \
((arg) >= BANS_ARG_OBJTTL && \
(arg) <= BANS_ARG_OBJKEEP)
/*--------------------------------------------------------------------*/
......
......@@ -37,6 +37,10 @@
#include "vend.h"
#include "vtim.h"
#include "vnum.h"
void BAN_Build_Init(void);
void BAN_Build_Fini(void);
struct ban_proto {
unsigned magic;
......@@ -61,6 +65,24 @@ static const struct pvar {
{ 0, 0, 0}
};
/* operators allowed per argument (pvar.tag) */
static const unsigned arg_opervalid[BAN_ARGARRSZ + 1] = {
#define ARGOPER(arg, mask) [BAN_ARGIDX(arg)] = (mask),
#include "tbl/ban_arg_oper.h"
[BAN_ARGARRSZ] = 0
};
// init'ed in _Init
static const char *arg_operhelp[BAN_ARGARRSZ + 1];
// operators
const char * const oper[BAN_OPERARRSZ + 1] = {
#define OPER(op, str) [BAN_OPERIDX(op)] = (str),
#include "tbl/ban_oper.h"
[BAN_OPERARRSZ] = NULL
};
/*--------------------------------------------------------------------
*/
......@@ -177,6 +199,18 @@ ban_parse_regexp(struct ban_proto *bp, const char *a3)
return (0);
}
static int
ban_parse_oper(const char *p)
{
int i;
for (i = 0; i < BAN_OPERARRSZ; i++) {
if (!strcmp(p, oper[i]))
return _BANS_OPER_OFF + i;
}
return -1;
}
/*--------------------------------------------------------------------
* Add a (and'ed) test-condition to a ban
*/
......@@ -186,7 +220,10 @@ BAN_AddTest(struct ban_proto *bp,
const char *a1, const char *a2, const char *a3)
{
const struct pvar *pv;
const char *err;
double darg;
uint64_t dtmp;
uint8_t denc[sizeof darg];
int op;
CHECK_OBJ_NOTNULL(bp, BAN_PROTO_MAGIC);
AN(bp->vsb);
......@@ -208,28 +245,46 @@ BAN_AddTest(struct ban_proto *bp,
bp->flags |= pv->flag;
VSB_putc(bp->vsb, pv->tag);
if (pv->flag & BANS_FLAG_HTTP)
if (pv->flag & BANS_FLAG_HTTP) {
assert(BANS_HAS_ARG1_SPEC(pv->tag));
ban_parse_http(bp, a1 + strlen(pv->name));
}
op = ban_parse_oper(a2);
if (op < 0 ||
((1<<BAN_OPERIDX(op)) & arg_opervalid[BAN_ARGIDX(pv->tag)]) == 0)
return (ban_error(bp,
"expected conditional (%s) got \"%s\"",
arg_operhelp[BAN_ARGIDX(pv->tag)],
a2));
if ((pv->flag & BANS_FLAG_DURATION) == 0) {
assert(! BANS_HAS_ARG2_DOUBLE(pv->tag));
ban_add_lump(bp, a3, strlen(a3) + 1);
if (!strcmp(a2, "~")) {
VSB_putc(bp->vsb, BANS_OPER_MATCH);
err = ban_parse_regexp(bp, a3);
if (err)
return (err);
} else if (!strcmp(a2, "!~")) {
VSB_putc(bp->vsb, BANS_OPER_NMATCH);
err = ban_parse_regexp(bp, a3);
if (err)
return (err);
} else if (!strcmp(a2, "==")) {
VSB_putc(bp->vsb, BANS_OPER_EQ);
} else if (!strcmp(a2, "!=")) {
VSB_putc(bp->vsb, BANS_OPER_NEQ);
} else {
ban_add_lump(bp, a3, strlen(a3) + 1);
VSB_putc(bp->vsb, op);
if (! BANS_HAS_ARG2_SPEC(op))
return (NULL);
return (ban_parse_regexp(bp, a3));
}
assert(pv->flag & BANS_FLAG_DURATION);
assert(BANS_HAS_ARG2_DOUBLE(pv->tag));
darg = VNUM_duration(a3);
if (isnan(darg)) {
return (ban_error(bp,
"expected conditional (~, !~, == or !=) got \"%s\"", a2));
"expected duration <n.nn>[ms|s|m|h|d|w|y] got \"%s\"", a3));
}
assert(sizeof darg == sizeof dtmp);
assert(sizeof dtmp == sizeof denc);
memcpy(&dtmp, &darg, sizeof dtmp);
vbe64enc(denc, dtmp);
ban_add_lump(bp, denc, sizeof denc);
VSB_putc(bp->vsb, op);
return (NULL);
}
......@@ -275,6 +330,7 @@ BAN_Commit(struct ban_proto *bp)
b->flags = bp->flags;
// XXX why don't we vbe*enc timestamp and flags?
memset(b->spec, 0, BANS_HEAD_LEN);
t0 = VTIM_real();
memcpy(b->spec + BANS_TIMESTAMP, &t0, sizeof t0);
......@@ -329,3 +385,66 @@ BAN_Commit(struct ban_proto *bp)
BAN_Abandon(bp);
return (NULL);
}
static void
ban_build_arg_operhelp(struct vsb *vsb, int arg)
{
unsigned mask;
const char *p = NULL, *n = NULL;
int i;
ASSERT_BAN_ARG(arg);
mask = arg_opervalid[BAN_ARGIDX(arg)];
for (i = 0; i < BAN_OPERARRSZ; i++) {
if ((mask & (1<<i)) == 0)
continue;
if (p == NULL)
p = oper[i];
else if (n == NULL)
n = oper[i];
else {
VSB_cat(vsb, p);
VSB_cat(vsb, ", ");
p = n;
n = oper[i];
}
}
if (n) {
AN(p);
VSB_cat(vsb, p);
VSB_cat(vsb, " or ");
VSB_cat(vsb, n);
return;
}
AN(p);
VSB_cat(vsb, p);
}
void
BAN_Build_Init(void) {
struct vsb *vsb = VSB_new_auto();
int i;
for (i = _BANS_ARG_OFF; i < _BANS_ARG_LIM; i ++) {
VSB_clear(vsb);
ban_build_arg_operhelp(vsb, i);
AZ(VSB_finish(vsb));
arg_operhelp[BAN_ARGIDX(i)] = strdup(VSB_data(vsb));
AN(arg_operhelp[BAN_ARGIDX(i)]);
}
arg_operhelp[BAN_ARGIDX(i)] = NULL;
VSB_destroy(&vsb);
AZ(vsb);
}
void
BAN_Build_Fini(void) {
int i;
for (i = 0; i < BAN_ARGARRSZ; i++)
free(TRUST_ME(arg_operhelp[i]));
}
varnishtest "test ban obj.status"
varnishtest "test ban obj.* except obj.http.*"
# see c00021.vtc for obj.http.* tests
server s1 {
rxreq
txresp -bodylen 5
txresp -bodylen 1
rxreq
txresp -bodylen 2
rxreq
txresp -bodylen 3
rxreq
txresp -bodylen 4
rxreq
txresp -bodylen 5
rxreq
txresp -bodylen 6
} -start
varnish v1 -vcl+backend {} -start
......@@ -12,16 +22,131 @@ varnish v1 -vcl+backend {} -start
client c1 {
txreq
rxresp
expect resp.bodylen == 5
expect resp.bodylen == 1
} -run
varnish v1 -cliok "ban obj.status == 201"
client c1 {
txreq
rxresp
expect resp.bodylen == 1
} -run
varnish v1 -cliok "ban obj.status == 200"
varnish v1 -cliok "ban.list"
varnish v1 -clijson "ban.list -j"
client c1 {
txreq
rxresp
expect resp.bodylen == 2
} -run
varnish v1 -cliok "ban obj.keep == 0s && obj.ttl > 1d"
varnish v1 -cliok "ban obj.keep == 0s && obj.ttl > 1d"
# BANS_FLAG_NODEDUP
varnish v1 -cliexpect {(?s) obj\.ttl > 1d\b.*obj\.ttl > 1d\b} "ban.list"
client c1 {
txreq
rxresp
expect resp.bodylen == 2
} -run
varnish v1 -cliok "ban obj.ttl <= 2m"
client c1 {
txreq
rxresp
expect resp.bodylen == 3
} -run
varnish v1 -cliok "ban obj.age > 1d"
varnish v1 -cliok "ban obj.age > 1d"
# BANS_FLAG_NODEDUP
varnish v1 -cliexpect {(?s) obj\.age > 1d\b.*obj\.age > 1d\b} "ban.list"
client c1 {
txreq
rxresp
expect resp.bodylen == 3
} -run
varnish v1 -cliok "ban obj.age < 1m"
client c1 {
txreq
rxresp
expect resp.bodylen == 4
} -run
varnish v1 -cliok "ban obj.grace != 10s"
varnish v1 -cliok "ban obj.grace != 10s"
# ! BANS_FLAG_NODEDUP
varnish v1 -cliexpect {(?s) obj\.grace != 10s\b.* \d C\b} "ban.list"
client c1 {
txreq
rxresp
expect resp.bodylen == 4
} -run
varnish v1 -cliok "ban obj.grace == 10s"
client c1 {
txreq
rxresp
expect resp.bodylen == 5
} -run
varnish v1 -cliok "ban obj.keep != 0s"
varnish v1 -cliok "ban obj.keep != 0s"
# ! BANS_FLAG_NODEDUP
varnish v1 -cliexpect {(?s) obj\.keep != 0s\b.* \d C\b} "ban.list"
client c1 {
txreq
rxresp
expect resp.bodylen == 5
} -run
varnish v1 -cliok "ban obj.keep == 0s"
client c1 {
txreq
rxresp
expect resp.bodylen == 6
} -run
# duration formatting - 0s is being tested above
varnish v1 -cliok "ban obj.keep == 123ms"
varnish v1 -cliexpect {(?s) obj\.keep == 123ms\b} "ban.list"
varnish v1 -cliok "ban obj.keep == 0.456s"
varnish v1 -cliexpect {(?s) obj\.keep == 456ms\b} "ban.list"
varnish v1 -cliok "ban obj.keep == 6.789s"
varnish v1 -cliexpect {(?s) obj\.keep == 6.789s\b} "ban.list"
varnish v1 -cliok "ban obj.keep == 42y"
varnish v1 -cliexpect {(?s) obj\.keep == 42y\b} "ban.list"
varnish v1 -cliok "ban obj.keep == 365d"
varnish v1 -cliexpect {(?s) obj\.keep == 1y\b} "ban.list"
varnish v1 -cliok "ban obj.keep == 9w"
varnish v1 -cliexpect {(?s) obj\.keep == 9w\b} "ban.list"
varnish v1 -cliok "ban obj.keep == 7d"
varnish v1 -cliexpect {(?s) obj\.keep == 1w\b} "ban.list"
varnish v1 -cliok "ban obj.keep == 3d"
varnish v1 -cliexpect {(?s) obj\.keep == 3d\b} "ban.list"
varnish v1 -cliok "ban obj.keep == 24h"
varnish v1 -cliexpect {(?s) obj\.keep == 1d\b} "ban.list"
varnish v1 -cliok "ban obj.keep == 18h"
varnish v1 -cliexpect {(?s) obj\.keep == 18h\b} "ban.list"
varnish v1 -cliok "ban obj.keep == 1.5h"
varnish v1 -cliexpect {(?s) obj\.keep == 90m\b} "ban.list"
varnish v1 -cliok "ban obj.keep == 10m"
varnish v1 -cliexpect {(?s) obj\.keep == 10m\b} "ban.list"
varnish v1 -cliok "ban obj.keep == 0.5m"
varnish v1 -cliexpect {(?s) obj\.keep == 30s\b} "ban.list"
varnishtest "Test vcl purging"
varnishtest "Test vcl ban()"
server s1 {
rxreq
......@@ -18,6 +18,9 @@ varnish v1 -vcl+backend {
ban("req.url // bar");
ban("req.url == bar //");
ban("foo == bar //");
ban("obj.age == 4");
ban("obj.age // 4d");
ban("obj.http.foo > 4d");
ban("req.url ~ ^/$");
return (synth(209,"foo"));
}
......@@ -36,9 +39,13 @@ logexpect l1 -v v1 -d 1 -g vxid {
expect * 1004 VCL_Error {ban[(][)]: No ban conditions found[.]}
expect * 1004 VCL_Error {ban[(][)]: Expected comparison operator[.]}
expect * 1004 VCL_Error {ban[(][)]: Expected second operand[.]}
expect * 1004 VCL_Error {ban[(][)]: expected conditional [(]~, !~, == or !=[)] got "//"}
expect * 1004 VCL_Error {ban[(][)]: Expected && between conditions, found .//.}
expect * 1004 VCL_Error {ban[(][)]: Unknown or unsupported field .foo.}
expect * 1004 VCL_Error {ban[(][)]: expected conditional [(]==, !=, ~ or !~[)] got "//"}
expect * 1004 VCL_Error {ban[(][)]: Expected && between conditions, found "//"}
expect * 1004 VCL_Error {ban[(][)]: Unknown or unsupported field "foo"}
expect * 1004 VCL_Error {ban[(][)]: expected duration <n.nn>.ms|s|m|h|d|w|y. got "4"}
expect * 1004 VCL_Error {ban[(][)]: expected conditional [(]==, !=, >, >=, < or <=[)] got "//"}
expect * 1004 VCL_Error {ban[(][)]: expected conditional [(]==, !=, ~ or !~[)] got ">"}
} -start
......
......@@ -457,23 +457,59 @@ ban(STRING)
* *<field>*:
* ``req.url``: The request url
* ``req.http.*``: Any request header
* ``obj.status``: The cache object status
* ``obj.http.*``: Any cache object header
* string fields:
* ``req.url``: The request url
* ``req.http.*``: Any request header
* ``obj.status``: The cache object status
* ``obj.http.*``: Any cache object header
``obj.status`` is treated as a string despite the fact that it
is actually an integer.
* duration fields:
* ``obj.ttl``: Remaining ttl at the time the ban is issued
* ``obj.age``: Object age at the time the ban is issued
* ``obj.grace``: The grace time of the object
* ``obj.keep``: The keep time of the object
* *<operator>*:
* ``==``: *<field>* and *<arg>* are equal strings (case sensitive)
* ``!=``: *<field>* and *<arg>* are unequal strings (case sensitive)
* ``~``: *<field>* matches the regular expression *<arg>*
* ``!~``:*<field>* does not match the regular expression *<arg>*
* for all fields:
* ``==``: *<field>* and *<arg>* are equal
* ``!=``: *<field>* and *<arg>* are unequal
strings are compared case sensitively
* for string fields:
* ``~``: *<field>* matches the regular expression *<arg>*
* ``!~``:*<field>* does not match the regular expression *<arg>*
* for duration fields:
* ``>``: *<field>* is greater than *<arg>*
* ``>=``: *<field>* is greater than or equal to *<arg>*
* ``<``: *<field>* is less than *<arg>*
* ``<=``: *<field>* is less than or equal to *<arg>*
* *<arg>*:
* for string fields:
Either a literal string or a regular expression. Note that
*<arg>* does not use any of the string delimiters like ``"`` or
``{"``\ *...*\ ``"}`` used elsewhere in varnish. To match
against strings containing whitespace, regular expressions
containing ``\s`` can be used.
* for duration fields:
* *<arg>*: Either a literal string or a regular expression. Note
that *<arg>* does not use any of the string delimiters like ``"``
or ``{"``\ *...*\ ``"}`` used elsewhere in varnish. To match
against strings containing whitespace, regular expressions
containing ``\s`` can be used.
A VCL duration like ``10s``, ``5m`` or ``1h``, see `Durations`_
Expressions can be chained using the *and* operator ``&&``. For *or*
semantics, use several bans.
......
......@@ -149,9 +149,12 @@ will produce a status of all current bans::
The ban list contains the ID of the ban, the timestamp when the ban
entered the ban list. A count of the objects that has reached this point
in the ban list, optionally postfixed with a 'G' for "Gone", if the ban
is no longer valid. Finally, the ban expression is listed. The ban can
be marked as "Gone" if it is a duplicate ban, but is still kept in the list
for optimization purposes.
is no longer valid. Finally, the ban expression is listed. Notice
that durations are not necessarily expressed in the originally given
unit, for instance ``7d`` will get turned into ``1w``.
The ban can be marked as "Gone" if it is a duplicate ban, but is still
kept in the list for optimization purposes.
Forcing a cache miss
~~~~~~~~~~~~~~~~~~~~
......
......@@ -5,6 +5,8 @@ nobase_pkginclude_HEADERS = \
tbl/acct_fields_bereq.h \
tbl/acct_fields_req.h \
tbl/backend_poll.h \
tbl/ban_arg_oper.h \
tbl/ban_oper.h \
tbl/ban_vars.h \
tbl/bo_flags.h \
tbl/boc_state.h \
......
/*-
* Copyright 2017 UPLEX - Nils Goroll Systemoptimierung
* All rights reserved.
*
* Author: Nils Goroll <nils.goroll@uplex.de>
*
* 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.
*
* Define which operators are valid for which ban arguments
*/
/*lint -save -e525 -e539 */
#define BANS_OPER_STRING \
(1<<BAN_OPERIDX(BANS_OPER_EQ) | \
1<<BAN_OPERIDX(BANS_OPER_NEQ) | \
1<<BAN_OPERIDX(BANS_OPER_MATCH) | \
1<<BAN_OPERIDX(BANS_OPER_NMATCH))
#define BANS_OPER_DURATION \
(1<<BAN_OPERIDX(BANS_OPER_EQ) | \
1<<BAN_OPERIDX(BANS_OPER_NEQ) | \
1<<BAN_OPERIDX(BANS_OPER_GT) | \
1<<BAN_OPERIDX(BANS_OPER_GTE) | \
1<<BAN_OPERIDX(BANS_OPER_LT) | \
1<<BAN_OPERIDX(BANS_OPER_LTE))
ARGOPER(BANS_ARG_URL, BANS_OPER_STRING)
ARGOPER(BANS_ARG_REQHTTP, BANS_OPER_STRING)
ARGOPER(BANS_ARG_OBJHTTP, BANS_OPER_STRING) // INT?
ARGOPER(BANS_ARG_OBJSTATUS, BANS_OPER_STRING)
ARGOPER(BANS_ARG_OBJTTL, BANS_OPER_DURATION)
ARGOPER(BANS_ARG_OBJAGE, BANS_OPER_DURATION)
ARGOPER(BANS_ARG_OBJGRACE, BANS_OPER_DURATION)
ARGOPER(BANS_ARG_OBJKEEP, BANS_OPER_DURATION)
#undef ARGOPER
#undef BANS_OPER_STRING
#undef BANS_OPER_DURATION
/*lint -restore */
/*-
* Copyright 2017 UPLEX - Nils Goroll Systemoptimierung
* All rights reserved.
*
* Author: Nils Goroll <nils.goroll@uplex.de>
*
* 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.
*
* Define ban operators
*/
/*lint -save -e525 -e539 */
OPER(BANS_OPER_EQ, "==")
OPER(BANS_OPER_NEQ, "!=")
OPER(BANS_OPER_MATCH, "~")
OPER(BANS_OPER_NMATCH, "!~")
OPER(BANS_OPER_GT, ">")
OPER(BANS_OPER_GTE, ">=")
OPER(BANS_OPER_LT, "<")
OPER(BANS_OPER_LTE, "<=")
#undef OPER
/*lint -restore */
......@@ -31,10 +31,30 @@
/*lint -save -e525 -e539 */
PVAR("req.url", BANS_FLAG_REQ, BANS_ARG_URL)
PVAR("req.http.", BANS_FLAG_REQ | BANS_FLAG_HTTP, BANS_ARG_REQHTTP)
PVAR("obj.status", BANS_FLAG_OBJ, BANS_ARG_OBJSTATUS)
PVAR("obj.http.", BANS_FLAG_OBJ | BANS_FLAG_HTTP, BANS_ARG_OBJHTTP)
PVAR("req.url",
BANS_FLAG_REQ,
BANS_ARG_URL)
PVAR("req.http.",
BANS_FLAG_REQ | BANS_FLAG_HTTP,
BANS_ARG_REQHTTP)
PVAR("obj.status",
BANS_FLAG_OBJ,
BANS_ARG_OBJSTATUS)
PVAR("obj.http.",
BANS_FLAG_OBJ | BANS_FLAG_HTTP,
BANS_ARG_OBJHTTP)
PVAR("obj.ttl",
BANS_FLAG_OBJ | BANS_FLAG_DURATION | BANS_FLAG_NODEDUP,
BANS_ARG_OBJTTL)
PVAR("obj.age",
BANS_FLAG_OBJ | BANS_FLAG_DURATION | BANS_FLAG_NODEDUP,
BANS_ARG_OBJAGE)
PVAR("obj.grace",
BANS_FLAG_OBJ | BANS_FLAG_DURATION,
BANS_ARG_OBJGRACE)
PVAR("obj.keep",
BANS_FLAG_OBJ | BANS_FLAG_DURATION,
BANS_ARG_OBJKEEP)
#undef PVAR
/*lint -restore */
......@@ -61,7 +61,9 @@ CLI_CMD(BAN_LIST,
" * ``R`` for req.* tests\n\n"
" * ``O`` for obj.* tests\n\n"
" * Pointer to ban object\n\n"
" * Ban specification",
" * Ban specification\n\n"
" Durations of ban specifications get normalized, for example \"7d\""
" gets changed into \"1w\".",
0, 0
)
......
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