diff options
author | 2017-11-13 11:30:11 +0000 | |
---|---|---|
committer | 2017-11-13 11:30:11 +0000 | |
commit | 5a4ae9a9cb4b2a7d895e04d57af92fc20776c79a (patch) | |
tree | a3132982d26b8f3949158eae6d7b475a496c95cb | |
parent | Newer Allwinner SoCs (H3/H4/A64) use an "unconfigured" default state. (diff) | |
download | wireguard-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.y | 27 | ||||
-rw-r--r-- | sbin/pfctl/pfctl_parser.c | 5 | ||||
-rw-r--r-- | share/man/man5/pf.conf.5 | 21 | ||||
-rw-r--r-- | sys/net/pf.c | 18 | ||||
-rw-r--r-- | sys/net/pf_ioctl.c | 5 | ||||
-rw-r--r-- | sys/net/pfvar.h | 22 |
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); |