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" #include "libpfctl.h"
const char* PFCTL_SYNCOOKIES_MODE_NAMES[] = {
"never",
"always",
"adaptive"
};
static int _pfctl_clear_states(int , const struct pfctl_kill *, static int _pfctl_clear_states(int , const struct pfctl_kill *,
unsigned int *, uint64_t); 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)); 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 int
pfctl_set_syncookies(int dev, const struct pfctl_syncookies *s) pfctl_set_syncookies(int dev, const struct pfctl_syncookies *s)
{ {
struct pfioc_nv nv; struct pfioc_nv nv;
nvlist_t *nvl; nvlist_t *nvl;
int ret; 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); nvl = nvlist_create(0);
nvlist_add_bool(nvl, "enabled", s->mode != PFCTL_SYNCOOKIES_NEVER); 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.data = nvlist_pack(nvl, &nv.len);
nv.size = nv.len; nv.size = nv.len;
@ -966,12 +995,18 @@ pfctl_get_syncookies(int dev, struct pfctl_syncookies *s)
{ {
struct pfioc_nv nv; struct pfioc_nv nv;
nvlist_t *nvl; 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)); bzero(s, sizeof(*s));
nv.data = malloc(128); nv.data = malloc(256);
nv.len = nv.size = 128; nv.len = nv.size = 256;
if (ioctl(dev, DIOCGETSYNCOOKIES, &nv)) { if (ioctl(dev, DIOCGETSYNCOOKIES, &nv)) {
free(nv.data); free(nv.data);
@ -987,7 +1022,17 @@ pfctl_get_syncookies(int dev, struct pfctl_syncookies *s)
enabled = nvlist_get_bool(nvl, "enabled"); enabled = nvlist_get_bool(nvl, "enabled");
adaptive = nvlist_get_bool(nvl, "adaptive"); 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); nvlist_destroy(nvl);

View File

@ -276,11 +276,15 @@ struct pfctl_states {
enum pfctl_syncookies_mode { enum pfctl_syncookies_mode {
PFCTL_SYNCOOKIES_NEVER, PFCTL_SYNCOOKIES_NEVER,
PFCTL_SYNCOOKIES_ALWAYS PFCTL_SYNCOOKIES_ALWAYS,
PFCTL_SYNCOOKIES_ADAPTIVE
}; };
extern const char* PFCTL_SYNCOOKIES_MODE_NAMES[];
struct pfctl_syncookies { struct pfctl_syncookies {
enum pfctl_syncookies_mode mode; enum pfctl_syncookies_mode mode;
uint8_t highwater; /* Percent */
uint8_t lowwater; /* Percent */
}; };
struct pfctl_status* pfctl_get_status(int dev); 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_hfsc_opts hfsc_opts;
static struct node_fairq_opts fairq_opts; static struct node_fairq_opts fairq_opts;
static struct node_state_opt *keep_state_defaults = NULL; 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_table(struct node_host *, const char *);
int disallow_urpf_failed(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_hfsc_opts hfsc_opts;
struct node_fairq_opts fairq_opts; struct node_fairq_opts fairq_opts;
struct codel_opts codel_opts; struct codel_opts codel_opts;
struct pfctl_watermarks *watermarks;
} v; } v;
int lineno; int lineno;
} YYSTYPE; } 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.pool_opts> pool_opts pool_opt pool_opts_l
%type <v.tagged> tagged %type <v.tagged> tagged
%type <v.rtableid> rtable %type <v.rtableid> rtable
%type <v.watermarks> syncookie_opts
%% %%
ruleset : /* empty */ ruleset : /* empty */
@ -729,14 +732,19 @@ option : SET OPTIMIZATION STRING {
| SET KEEPCOUNTERS { | SET KEEPCOUNTERS {
pf->keep_counters = true; pf->keep_counters = true;
} }
| SET SYNCOOKIES syncookie_val { | SET SYNCOOKIES syncookie_val syncookie_opts {
pf->syncookies = $3; if (pfctl_cfg_syncookies(pf, $3, $4)) {
yyerror("error setting syncookies");
YYERROR;
}
} }
; ;
syncookie_val : STRING { syncookie_val : STRING {
if (!strcmp($1, "never")) if (!strcmp($1, "never"))
$$ = PFCTL_SYNCOOKIES_NEVER; $$ = PFCTL_SYNCOOKIES_NEVER;
else if (!strcmp($1, "adaptive"))
$$ = PFCTL_SYNCOOKIES_ADAPTIVE;
else if (!strcmp($1, "always")) else if (!strcmp($1, "always"))
$$ = PFCTL_SYNCOOKIES_ALWAYS; $$ = PFCTL_SYNCOOKIES_ALWAYS;
else { 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; } stringall : STRING { $$ = $1; }
| ALL { | ALL {

View File

@ -1812,6 +1812,10 @@ pfctl_init_options(struct pfctl *pf)
pf->limit[PF_LIMIT_TABLE_ENTRIES] = PFR_KENTRY_HIWAT; pf->limit[PF_LIMIT_TABLE_ENTRIES] = PFR_KENTRY_HIWAT;
pf->debug = PF_DEBUG_URGENT; pf->debug = PF_DEBUG_URGENT;
pf->syncookies = false;
pf->syncookieswat[0] = PF_SYNCOOKIES_LOWATPCT;
pf->syncookieswat[1] = PF_SYNCOOKIES_HIWATPCT;
} }
int int
@ -2069,7 +2073,9 @@ pfctl_load_syncookies(struct pfctl *pf, u_int8_t val)
bzero(&cookies, sizeof(cookies)); 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)) { if (pfctl_set_syncookies(dev, &cookies)) {
warnx("DIOCSETSYNCOOKIES"); warnx("DIOCSETSYNCOOKIES");
@ -2078,6 +2084,49 @@ pfctl_load_syncookies(struct pfctl *pf, u_int8_t val)
return (0); 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 int
pfctl_set_debug(struct pfctl *pf, char *d) pfctl_set_debug(struct pfctl *pf, char *d)
{ {

View File

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

View File

@ -101,6 +101,8 @@ struct pfctl {
char *ifname; char *ifname;
bool keep_counters; bool keep_counters;
u_int8_t syncookies; 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 timeout_set[PFTM_MAX];
u_int8_t limit_set[PF_LIMIT_MAX]; u_int8_t limit_set[PF_LIMIT_MAX];
@ -200,6 +202,11 @@ struct pfctl_altq {
} meta; } meta;
}; };
struct pfctl_watermarks {
uint32_t hi;
uint32_t lo;
};
#ifdef __FreeBSD__ #ifdef __FreeBSD__
/* /*
* XXX * XXX
@ -270,6 +277,7 @@ int pfctl_set_logif(struct pfctl *, char *);
int pfctl_set_hostid(struct pfctl *, u_int32_t); int pfctl_set_hostid(struct pfctl *, u_int32_t);
int pfctl_set_debug(struct pfctl *, char *); int pfctl_set_debug(struct pfctl *, char *);
int pfctl_set_interface_flags(struct pfctl *, char *, int, int); 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_config(char *, struct pfctl *);
int parse_flags(char *); int parse_flags(char *);

View File

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