pfctl: userspace adaptive syncookies configration

Hook up the userspace bits to configure syncookies in adaptive mode.

MFC after:	1 week
Sponsored by:	Modirum MDPay
Differential Revision:	https://reviews.freebsd.org/D32136
This commit is contained in:
Kristof Provost 2021-08-13 13:42:59 +02:00
parent 955460d41e
commit 5062afff9d
7 changed files with 160 additions and 11 deletions

View File

@ -50,6 +50,12 @@
#include "libpfctl.h"
const char* PFCTL_SYNCOOKIES_MODE_NAMES[] = {
"never",
"always",
"adaptive"
};
static int _pfctl_clear_states(int , const struct pfctl_kill *,
unsigned int *, uint64_t);
@ -938,17 +944,40 @@ pfctl_kill_states(int dev, const struct pfctl_kill *kill, unsigned int *killed)
return (_pfctl_clear_states(dev, kill, killed, DIOCKILLSTATESNV));
}
static int
pfctl_get_limit(int dev, const int index, u_int *limit)
{
struct pfioc_limit pl;
bzero(&pl, sizeof(pl));
pl.index = index;
if (ioctl(dev, DIOCGETLIMIT, &pl) == -1)
return (errno);
*limit = pl.limit;
return (0);
}
int
pfctl_set_syncookies(int dev, const struct pfctl_syncookies *s)
{
struct pfioc_nv nv;
nvlist_t *nvl;
int ret;
u_int state_limit;
ret = pfctl_get_limit(dev, PF_LIMIT_STATES, &state_limit);
if (ret != 0)
return (ret);
nvl = nvlist_create(0);
nvlist_add_bool(nvl, "enabled", s->mode != PFCTL_SYNCOOKIES_NEVER);
nvlist_add_bool(nvl, "adaptive", false); /* XXX TODO */
nvlist_add_bool(nvl, "adaptive", s->mode == PFCTL_SYNCOOKIES_ADAPTIVE);
nvlist_add_number(nvl, "highwater", state_limit * s->highwater / 100);
nvlist_add_number(nvl, "lowwater", state_limit * s->lowwater / 100);
nv.data = nvlist_pack(nvl, &nv.len);
nv.size = nv.len;
@ -966,12 +995,18 @@ pfctl_get_syncookies(int dev, struct pfctl_syncookies *s)
{
struct pfioc_nv nv;
nvlist_t *nvl;
bool enabled, adaptive;
int ret;
u_int state_limit;
bool enabled, adaptive;
ret = pfctl_get_limit(dev, PF_LIMIT_STATES, &state_limit);
if (ret != 0)
return (ret);
bzero(s, sizeof(*s));
nv.data = malloc(128);
nv.len = nv.size = 128;
nv.data = malloc(256);
nv.len = nv.size = 256;
if (ioctl(dev, DIOCGETSYNCOOKIES, &nv)) {
free(nv.data);
@ -987,7 +1022,17 @@ pfctl_get_syncookies(int dev, struct pfctl_syncookies *s)
enabled = nvlist_get_bool(nvl, "enabled");
adaptive = nvlist_get_bool(nvl, "adaptive");
s->mode = enabled ? PFCTL_SYNCOOKIES_ALWAYS : PFCTL_SYNCOOKIES_NEVER;
if (enabled) {
if (adaptive)
s->mode = PFCTL_SYNCOOKIES_ADAPTIVE;
else
s->mode = PFCTL_SYNCOOKIES_ALWAYS;
} else {
s->mode = PFCTL_SYNCOOKIES_NEVER;
}
s->highwater = nvlist_get_number(nvl, "highwater") * 100 / state_limit;
s->lowwater = nvlist_get_number(nvl, "lowwater") * 100 / state_limit;
nvlist_destroy(nvl);

View File

@ -276,11 +276,15 @@ struct pfctl_states {
enum pfctl_syncookies_mode {
PFCTL_SYNCOOKIES_NEVER,
PFCTL_SYNCOOKIES_ALWAYS
PFCTL_SYNCOOKIES_ALWAYS,
PFCTL_SYNCOOKIES_ADAPTIVE
};
extern const char* PFCTL_SYNCOOKIES_MODE_NAMES[];
struct pfctl_syncookies {
enum pfctl_syncookies_mode mode;
uint8_t highwater; /* Percent */
uint8_t lowwater; /* Percent */
};
struct pfctl_status* pfctl_get_status(int dev);

View File

@ -320,6 +320,7 @@ static struct codel_opts codel_opts;
static struct node_hfsc_opts hfsc_opts;
static struct node_fairq_opts fairq_opts;
static struct node_state_opt *keep_state_defaults = NULL;
static struct pfctl_watermarks syncookie_opts;
int disallow_table(struct node_host *, const char *);
int disallow_urpf_failed(struct node_host *, const char *);
@ -445,6 +446,7 @@ typedef struct {
struct node_hfsc_opts hfsc_opts;
struct node_fairq_opts fairq_opts;
struct codel_opts codel_opts;
struct pfctl_watermarks *watermarks;
} v;
int lineno;
} YYSTYPE;
@ -531,6 +533,7 @@ int parseport(char *, struct range *r, int);
%type <v.pool_opts> pool_opts pool_opt pool_opts_l
%type <v.tagged> tagged
%type <v.rtableid> rtable
%type <v.watermarks> syncookie_opts
%%
ruleset : /* empty */
@ -729,14 +732,19 @@ option : SET OPTIMIZATION STRING {
| SET KEEPCOUNTERS {
pf->keep_counters = true;
}
| SET SYNCOOKIES syncookie_val {
pf->syncookies = $3;
| SET SYNCOOKIES syncookie_val syncookie_opts {
if (pfctl_cfg_syncookies(pf, $3, $4)) {
yyerror("error setting syncookies");
YYERROR;
}
}
;
syncookie_val : STRING {
if (!strcmp($1, "never"))
$$ = PFCTL_SYNCOOKIES_NEVER;
else if (!strcmp($1, "adaptive"))
$$ = PFCTL_SYNCOOKIES_ADAPTIVE;
else if (!strcmp($1, "always"))
$$ = PFCTL_SYNCOOKIES_ALWAYS;
else {
@ -745,6 +753,37 @@ syncookie_val : STRING {
}
}
;
syncookie_opts : /* empty */ { $$ = NULL; }
| {
memset(&syncookie_opts, 0, sizeof(syncookie_opts));
} '(' syncookie_opt_l ')' { $$ = &syncookie_opts; }
;
syncookie_opt_l : syncookie_opt_l comma syncookie_opt
| syncookie_opt
;
syncookie_opt : STRING STRING {
double val;
char *cp;
val = strtod($2, &cp);
if (cp == NULL || strcmp(cp, "%"))
YYERROR;
if (val <= 0 || val > 100) {
yyerror("illegal percentage value");
YYERROR;
}
if (!strcmp($1, "start")) {
syncookie_opts.hi = val;
} else if (!strcmp($1, "end")) {
syncookie_opts.lo = val;
} else {
yyerror("illegal syncookie option");
YYERROR;
}
}
;
stringall : STRING { $$ = $1; }
| ALL {

View File

@ -1812,6 +1812,10 @@ pfctl_init_options(struct pfctl *pf)
pf->limit[PF_LIMIT_TABLE_ENTRIES] = PFR_KENTRY_HIWAT;
pf->debug = PF_DEBUG_URGENT;
pf->syncookies = false;
pf->syncookieswat[0] = PF_SYNCOOKIES_LOWATPCT;
pf->syncookieswat[1] = PF_SYNCOOKIES_HIWATPCT;
}
int
@ -2069,7 +2073,9 @@ pfctl_load_syncookies(struct pfctl *pf, u_int8_t val)
bzero(&cookies, sizeof(cookies));
cookies.mode = val ? PFCTL_SYNCOOKIES_ALWAYS : PFCTL_SYNCOOKIES_NEVER;
cookies.mode = val;
cookies.lowwater = pf->syncookieswat[0];
cookies.highwater = pf->syncookieswat[1];
if (pfctl_set_syncookies(dev, &cookies)) {
warnx("DIOCSETSYNCOOKIES");
@ -2078,6 +2084,49 @@ pfctl_load_syncookies(struct pfctl *pf, u_int8_t val)
return (0);
}
int
pfctl_cfg_syncookies(struct pfctl *pf, uint8_t val, struct pfctl_watermarks *w)
{
if (val != PF_SYNCOOKIES_ADAPTIVE && w != NULL) {
warnx("syncookies start/end only apply to adaptive");
return (1);
}
if (val == PF_SYNCOOKIES_ADAPTIVE && w != NULL) {
if (!w->hi)
w->hi = PF_SYNCOOKIES_HIWATPCT;
if (!w->lo)
w->lo = w->hi / 2;
if (w->lo >= w->hi) {
warnx("start must be higher than end");
return (1);
}
pf->syncookieswat[0] = w->lo;
pf->syncookieswat[1] = w->hi;
pf->syncookieswat_set = 1;
}
if (pf->opts & PF_OPT_VERBOSE) {
if (val == PF_SYNCOOKIES_NEVER)
printf("set syncookies never\n");
else if (val == PF_SYNCOOKIES_ALWAYS)
printf("set syncookies always\n");
else if (val == PF_SYNCOOKIES_ADAPTIVE) {
if (pf->syncookieswat_set)
printf("set syncookies adaptive (start %u%%, "
"end %u%%)\n", pf->syncookieswat[1],
pf->syncookieswat[0]);
else
printf("set syncookies adaptive\n");
} else { /* cannot happen */
warnx("king bula ate all syncookies");
return (1);
}
}
pf->syncookies = val;
return (0);
}
int
pfctl_set_debug(struct pfctl *pf, char *d)
{

View File

@ -50,6 +50,7 @@ __FBSDID("$FreeBSD$");
#include <net/pfvar.h>
#include <arpa/inet.h>
#include <assert.h>
#include <search.h>
#include <stdio.h>
#include <stdlib.h>
@ -618,9 +619,9 @@ print_status(struct pfctl_status *s, struct pfctl_syncookies *cookies, int opts)
}
printf("Syncookies\n");
assert(cookies->mode <= PFCTL_SYNCOOKIES_ADAPTIVE);
printf(" %-25s %s\n", "mode",
cookies->mode == PFCTL_SYNCOOKIES_NEVER ?
"never" : "always");
PFCTL_SYNCOOKIES_MODE_NAMES[cookies->mode]);
}
}

View File

@ -101,6 +101,8 @@ struct pfctl {
char *ifname;
bool keep_counters;
u_int8_t syncookies;
u_int8_t syncookieswat[2]; /* lowat, highwat, in % */
u_int8_t syncookieswat_set;
u_int8_t timeout_set[PFTM_MAX];
u_int8_t limit_set[PF_LIMIT_MAX];
@ -200,6 +202,11 @@ struct pfctl_altq {
} meta;
};
struct pfctl_watermarks {
uint32_t hi;
uint32_t lo;
};
#ifdef __FreeBSD__
/*
* XXX
@ -270,6 +277,7 @@ int pfctl_set_logif(struct pfctl *, char *);
int pfctl_set_hostid(struct pfctl *, u_int32_t);
int pfctl_set_debug(struct pfctl *, char *);
int pfctl_set_interface_flags(struct pfctl *, char *, int, int);
int pfctl_cfg_syncookies(struct pfctl *, uint8_t, struct pfctl_watermarks *);
int parse_config(char *, struct pfctl *);
int parse_flags(char *);

View File

@ -1386,6 +1386,9 @@ enum pf_syncookies_mode {
PF_SYNCOOKIES_MODE_MAX = PF_SYNCOOKIES_ADAPTIVE
};
#define PF_SYNCOOKIES_HIWATPCT 25
#define PF_SYNCOOKIES_LOWATPCT (PF_SYNCOOKIES_HIWATPCT / 2)
#ifdef _KERNEL
struct pf_kstatus {
counter_u64_t counters[PFRES_MAX]; /* reason for passing/dropping */