summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorhenning <henning@openbsd.org>2017-11-13 11:30:11 +0000
committerhenning <henning@openbsd.org>2017-11-13 11:30:11 +0000
commit5a4ae9a9cb4b2a7d895e04d57af92fc20776c79a (patch)
treea3132982d26b8f3949158eae6d7b475a496c95cb
parentNewer Allwinner SoCs (H3/H4/A64) use an "unconfigured" default state. (diff)
downloadwireguard-openbsd-5a4ae9a9cb4b2a7d895e04d57af92fc20776c79a.tar.xz
wireguard-openbsd-5a4ae9a9cb4b2a7d895e04d57af92fc20776c79a.zip
add a generic packet rate matching filter. allows things like
pass in proto icmp max-pkt-rate 100/10 all packets matching the rule in the direction the state was created are taken into consideration (typically: requests, but not replies). Just like with the other max-*, the rule stops matching if the maximum is reached, so in typical scenarios the default block rule would kick in then. with input from Holger Mikolon ok mikeb
-rw-r--r--sbin/pfctl/parse.y27
-rw-r--r--sbin/pfctl/pfctl_parser.c5
-rw-r--r--share/man/man5/pf.conf.521
-rw-r--r--sys/net/pf.c18
-rw-r--r--sys/net/pf_ioctl.c5
-rw-r--r--sys/net/pfvar.h22
6 files changed, 79 insertions, 19 deletions
diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y
index 694e3d729b0..c170e60d2a0 100644
--- a/sbin/pfctl/parse.y
+++ b/sbin/pfctl/parse.y
@@ -1,4 +1,4 @@
-/* $OpenBSD: parse.y,v 1.663 2017/08/11 22:30:38 benno Exp $ */
+/* $OpenBSD: parse.y,v 1.664 2017/11/13 11:30:11 henning Exp $ */
/*
* Copyright (c) 2001 Markus Friedl. All rights reserved.
@@ -282,6 +282,11 @@ struct filter_opts {
sa_family_t af;
struct pf_poolhashkey *key;
} route;
+
+ struct {
+ u_int32_t limit;
+ u_int32_t seconds;
+ } pktrate;
} filter_opts;
struct antispoof_opts {
@@ -472,7 +477,7 @@ int parseport(char *, struct range *r, int);
%token QUEUE PRIORITY QLIMIT RTABLE RDOMAIN MINIMUM BURST PARENT
%token LOAD RULESET_OPTIMIZATION RTABLE RDOMAIN PRIO ONCE DEFAULT
%token STICKYADDRESS MAXSRCSTATES MAXSRCNODES SOURCETRACK GLOBAL RULE
-%token MAXSRCCONN MAXSRCCONNRATE OVERLOAD FLUSH SLOPPY PFLOW
+%token MAXSRCCONN MAXSRCCONNRATE OVERLOAD FLUSH SLOPPY PFLOW MAXPKTRATE
%token TAGGED TAG IFBOUND FLOATING STATEPOLICY STATEDEFAULTS ROUTE
%token DIVERTTO DIVERTREPLY DIVERTPACKET NATTO AFTO RDRTO RECEIVEDON NE LE GE
%token <v.string> STRING
@@ -835,6 +840,8 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto
r.af = $6;
r.prob = $9.prob;
r.rtableid = $9.rtableid;
+ r.pktrate.limit = $9.pktrate.limit;
+ r.pktrate.seconds = $9.pktrate.seconds;
if ($9.tag)
if (strlcpy(r.tagname, $9.tag,
@@ -1618,6 +1625,8 @@ pfrule : action dir logquick interface af proto fromto
}
r.tos = $8.tos;
+ r.pktrate.limit = $8.pktrate.limit;
+ r.pktrate.seconds = $8.pktrate.seconds;
r.keep_state = $8.keep.action;
o = $8.keep.options;
@@ -2184,6 +2193,19 @@ filter_opt : USER uids {
| ONCE {
filter_opts.marker |= FOM_ONCE;
}
+ | MAXPKTRATE NUMBER '/' NUMBER {
+ if ($2 < 0 || $2 > UINT_MAX ||
+ $4 < 0 || $4 > UINT_MAX) {
+ yyerror("only positive values permitted");
+ YYERROR;
+ }
+ if (filter_opts.pktrate.limit) {
+ yyerror("cannot respecify max-pkt-rate");
+ YYERROR;
+ }
+ filter_opts.pktrate.limit = $2;
+ filter_opts.pktrate.seconds = $4;
+ }
| filter_sets
;
@@ -5055,6 +5077,7 @@ lookup(char *s)
{ "matches", MATCHES},
{ "max", MAXIMUM},
{ "max-mss", MAXMSS},
+ { "max-pkt-rate", MAXPKTRATE},
{ "max-src-conn", MAXSRCCONN},
{ "max-src-conn-rate", MAXSRCCONNRATE},
{ "max-src-nodes", MAXSRCNODES},
diff --git a/sbin/pfctl/pfctl_parser.c b/sbin/pfctl/pfctl_parser.c
index f62b9ab6128..5963b6bdffd 100644
--- a/sbin/pfctl/pfctl_parser.c
+++ b/sbin/pfctl/pfctl_parser.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pfctl_parser.c,v 1.316 2017/08/14 15:53:04 henning Exp $ */
+/* $OpenBSD: pfctl_parser.c,v 1.317 2017/11/13 11:30:11 henning Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
@@ -862,6 +862,9 @@ print_rule(struct pf_rule *r, const char *anchor_call, int opts)
printf(" tos 0x%2.2x", r->tos);
if (r->prio)
printf(" prio %u", r->prio == PF_PRIO_ZERO ? 0 : r->prio);
+ if (r->pktrate.limit)
+ printf(" max-pkt-rate %u/%u", r->pktrate.limit,
+ r->pktrate.seconds);
if (r->scrub_flags & PFSTATE_SETMASK || r->qname[0]) {
char *comma = "";
diff --git a/share/man/man5/pf.conf.5 b/share/man/man5/pf.conf.5
index 117207a05b5..c0f85cc6f7f 100644
--- a/share/man/man5/pf.conf.5
+++ b/share/man/man5/pf.conf.5
@@ -1,4 +1,4 @@
-.\" $OpenBSD: pf.conf.5,v 1.569 2017/10/14 06:50:21 jmc Exp $
+.\" $OpenBSD: pf.conf.5,v 1.570 2017/11/13 11:30:11 henning Exp $
.\"
.\" Copyright (c) 2002, Daniel Hartmeier
.\" Copyright (c) 2003 - 2013 Henning Brauer <henning@openbsd.org>
@@ -28,7 +28,7 @@
.\" ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
.\" POSSIBILITY OF SUCH DAMAGE.
.\"
-.Dd $Mdocdate: October 14 2017 $
+.Dd $Mdocdate: November 13 2017 $
.Dt PF.CONF 5
.Os
.Sh NAME
@@ -626,6 +626,22 @@ The macro expansion for the
.Cm label
directive occurs only at configuration file parse time, not during runtime.
.Pp
+.It Cm max-pkt-rate Ar number Ns / Ns Ar seconds
+Measure the rate of packets matching the rule and states created by it.
+When the specified rate is exceeded, the rule stops matching.
+Only packets in the direction in which the state was created are considered,
+so that typically requests are counted and replies are not.
+For example:
+.Pp
+.Bd -literal -offset indent -compact
+block in proto icmp
+pass in proto icmp max-pkt-rate 100/10
+.Ed
+.Pp
+passes up to 100 icmp packets per 10 seconds.
+When the rate is exceeded, all icmp is blocked until the rate falls below
+100 per 10 seconds again.
+.Pp
.It Cm once
Creates a one shot rule that will remove itself from an active ruleset after
the first match.
@@ -2692,6 +2708,7 @@ filteropt = user | group | flags | icmp-type | icmp6-type |
"divert-packet" "port" port | "divert-reply" |
"divert-to" host "port" port |
"label" string | "tag" string | [ "!" ] "tagged" string |
+ "max-pkt-rate" number "/" seconds |
"set prio" ( number | "(" number [ [ "," ] number ] ")" ) |
"set queue" ( string | "(" string [ [ "," ] string ] ")" ) |
"rtable" number | "probability" number"%" | "prio" number |
diff --git a/sys/net/pf.c b/sys/net/pf.c
index cf8cdc08674..3382ee63e7c 100644
--- a/sys/net/pf.c
+++ b/sys/net/pf.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pf.c,v 1.1044 2017/11/13 01:24:09 dlg Exp $ */
+/* $OpenBSD: pf.c,v 1.1045 2017/11/13 11:30:11 henning Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
@@ -161,8 +161,6 @@ struct pool pf_src_tree_pl, pf_rule_pl, pf_queue_pl;
struct pool pf_state_pl, pf_state_key_pl, pf_state_item_pl;
struct pool pf_rule_item_pl, pf_sn_item_pl;
-void pf_init_threshold(struct pf_threshold *, u_int32_t,
- u_int32_t);
void pf_add_threshold(struct pf_threshold *);
int pf_check_threshold(struct pf_threshold *);
int pf_check_tcp_cksum(struct mbuf *, int, int,
@@ -274,6 +272,13 @@ struct pf_pool_limit pf_pool_limits[PF_LIMIT_MAX] = {
s = pf_find_state(i, k, d, m); \
if (s == NULL || (s)->timeout == PFTM_PURGE) \
return (PF_DROP); \
+ if ((s)->rule.ptr->pktrate.limit && d == (s)->direction) { \
+ pf_add_threshold(&(s)->rule.ptr->pktrate); \
+ if (pf_check_threshold(&(s)->rule.ptr->pktrate)) { \
+ s = NULL; \
+ return (PF_DROP); \
+ } \
+ } \
if (d == PF_OUT && \
(((s)->rule.ptr->rt == PF_ROUTETO && \
(s)->rule.ptr->direction == PF_OUT) || \
@@ -3596,6 +3601,13 @@ pf_match_rule(struct pf_test_ctx *ctx, struct pf_ruleset *ruleset)
ctx->pd->m->m_pkthdr.pf.prio),
TAILQ_NEXT(r, entries));
+ /* must be last! */
+ if (r->pktrate.limit) {
+ pf_add_threshold(&r->pktrate);
+ PF_TEST_ATTRIB((pf_check_threshold(&r->pktrate)),
+ TAILQ_NEXT(r, entries));
+ }
+
/* FALLTHROUGH */
if (r->tag)
ctx->tag = r->tag;
diff --git a/sys/net/pf_ioctl.c b/sys/net/pf_ioctl.c
index 99aebe1087c..e392d5364e7 100644
--- a/sys/net/pf_ioctl.c
+++ b/sys/net/pf_ioctl.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pf_ioctl.c,v 1.324 2017/10/31 22:05:12 sashan Exp $ */
+/* $OpenBSD: pf_ioctl.c,v 1.325 2017/11/13 11:30:11 henning Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
@@ -1317,6 +1317,7 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p)
pr->rule.rcv_kif = NULL;
pr->rule.anchor = NULL;
pr->rule.overload_tbl = NULL;
+ pr->rule.pktrate.limit /= PF_THRESHOLD_MULT;
bzero(&pr->rule.gcle, sizeof(pr->rule.gcle));
pr->rule.ruleset = NULL;
if (pf_anchor_copyout(ruleset, rule, pr)) {
@@ -2737,6 +2738,8 @@ pf_rule_copyin(struct pf_rule *from, struct pf_rule *to,
to->max_src_conn = from->max_src_conn;
to->max_src_conn_rate.limit = from->max_src_conn_rate.limit;
to->max_src_conn_rate.seconds = from->max_src_conn_rate.seconds;
+ pf_init_threshold(&to->pktrate, from->pktrate.limit,
+ from->pktrate.seconds);
if (to->qname[0] != 0) {
if ((to->qid = pf_qname2qid(to->qname, 0)) == 0)
diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
index 11ac9cf0ce3..c68814bc989 100644
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: pfvar.h,v 1.466 2017/09/05 22:15:32 sashan Exp $ */
+/* $OpenBSD: pfvar.h,v 1.467 2017/11/13 11:30:11 henning Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
@@ -317,6 +317,15 @@ struct pf_rule_addr {
u_int16_t weight;
};
+struct pf_threshold {
+ u_int32_t limit;
+#define PF_THRESHOLD_MULT 1000
+#define PF_THRESHOLD_MAX 0xffffffff / PF_THRESHOLD_MULT
+ u_int32_t seconds;
+ u_int32_t count;
+ u_int32_t last;
+};
+
struct pf_poolhashkey {
union {
u_int8_t key8[16];
@@ -496,6 +505,7 @@ struct pf_rule {
struct pf_pool nat;
struct pf_pool rdr;
struct pf_pool route;
+ struct pf_threshold pktrate;
u_int64_t evaluations;
u_int64_t packets[2];
@@ -610,15 +620,6 @@ struct pf_rule {
#define PFSTATE_ADAPT_END 12000 /* default adaptive timeout end */
-struct pf_threshold {
- u_int32_t limit;
-#define PF_THRESHOLD_MULT 1000
-#define PF_THRESHOLD_MAX 0xffffffff / PF_THRESHOLD_MULT
- u_int32_t seconds;
- u_int32_t count;
- u_int32_t last;
-};
-
struct pf_rule_item {
SLIST_ENTRY(pf_rule_item) entry;
struct pf_rule *r;
@@ -1749,6 +1750,7 @@ int pf_translate(struct pf_pdesc *, struct pf_addr *, u_int16_t,
int pf_translate_af(struct pf_pdesc *);
void pf_route(struct pf_pdesc *, struct pf_rule *, struct pf_state *);
void pf_route6(struct pf_pdesc *, struct pf_rule *, struct pf_state *);
+void pf_init_threshold(struct pf_threshold *, u_int32_t, u_int32_t);
void pfr_initialize(void);
int pfr_match_addr(struct pfr_ktable *, struct pf_addr *, sa_family_t);