Bring the 'probability' keyword into PF from NetBSD. This feature allows
authorMatthew Dillon <dillon@dragonflybsd.org>
Sun, 6 Apr 2008 21:12:42 +0000 (21:12 +0000)
committerMatthew Dillon <dillon@dragonflybsd.org>
Sun, 6 Apr 2008 21:12:42 +0000 (21:12 +0000)
a rule to have a probability associated with it which governs whether the
rule is run or not for any given packet.

Suggested-by: =?ISO-8859-1?Q?C=E9dric_Berger?= <cedric@berger.to>
Obtained-from: NetBSD-current

sys/net/pf/pf.c
sys/net/pf/pfvar.h
usr.sbin/pfctl/parse.y
usr.sbin/pfctl/pf.conf.5
usr.sbin/pfctl/pfctl_parser.c

index 1e58c46..e31decf 100644 (file)
@@ -1,7 +1,7 @@
 /*     $FreeBSD: src/sys/contrib/pf/net/pf.c,v 1.19 2004/09/11 11:18:25 mlaier Exp $   */
 /*     $OpenBSD: pf.c,v 1.433.2.2 2004/07/17 03:22:34 brad Exp $ */
 /* add $OpenBSD: pf.c,v 1.448 2004/05/11 07:34:11 dhartmei Exp $ */
-/*     $DragonFly: src/sys/net/pf/pf.c,v 1.15 2008/04/06 18:58:16 dillon Exp $ */
+/*     $DragonFly: src/sys/net/pf/pf.c,v 1.16 2008/04/06 21:12:41 dillon Exp $ */
 
 /*
  * Copyright (c) 2004 The DragonFly Project.  All rights reserved.
@@ -2561,6 +2561,8 @@ pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction,
                    !pf_match_gid(r->gid.op, r->gid.gid[0], r->gid.gid[1],
                    gid))
                        r = TAILQ_NEXT(r, entries);
+               else if (r->prob && r->prob <= karc4random())
+                       r = TAILQ_NEXT(r, entries);
                else if (r->match_tag && !pf_match_tag(m, r, nr, &tag))
                        r = TAILQ_NEXT(r, entries);
                else if (r->anchorname[0] && r->anchor == NULL)
@@ -2909,6 +2911,8 @@ pf_test_udp(struct pf_rule **rm, struct pf_state **sm, int direction,
                    !pf_match_gid(r->gid.op, r->gid.gid[0], r->gid.gid[1],
                    gid))
                        r = TAILQ_NEXT(r, entries);
+               else if (r->prob && r->prob <= karc4random())
+                       r = TAILQ_NEXT(r, entries);
                else if (r->match_tag && !pf_match_tag(m, r, nr, &tag))
                        r = TAILQ_NEXT(r, entries);
                else if (r->anchorname[0] && r->anchor == NULL)
@@ -3206,6 +3210,8 @@ pf_test_icmp(struct pf_rule **rm, struct pf_state **sm, int direction,
                        r = TAILQ_NEXT(r, entries);
                else if (r->rule_flag & PFRULE_FRAGMENT)
                        r = TAILQ_NEXT(r, entries);
+               else if (r->prob && r->prob <= karc4random())
+                       r = TAILQ_NEXT(r, entries);
                else if (r->match_tag && !pf_match_tag(m, r, nr, &tag))
                        r = TAILQ_NEXT(r, entries);
                else if (r->anchorname[0] && r->anchor == NULL)
@@ -3436,6 +3442,8 @@ pf_test_other(struct pf_rule **rm, struct pf_state **sm, int direction,
                        r = TAILQ_NEXT(r, entries);
                else if (r->rule_flag & PFRULE_FRAGMENT)
                        r = TAILQ_NEXT(r, entries);
+               else if (r->prob && r->prob <= karc4random())
+                       r = TAILQ_NEXT(r, entries);
                else if (r->match_tag && !pf_match_tag(m, r, nr, &tag))
                        r = TAILQ_NEXT(r, entries);
                else if (r->anchorname[0] && r->anchor == NULL)
@@ -3635,6 +3643,8 @@ pf_test_fragment(struct pf_rule **rm, int direction, struct pfi_kif *kif,
                    r->flagset || r->type || r->code ||
                    r->os_fingerprint != PF_OSFP_ANY)
                        r = TAILQ_NEXT(r, entries);
+               else if (r->prob && r->prob <= karc4random())
+                       r = TAILQ_NEXT(r, entries);
                else if (r->match_tag && !pf_match_tag(m, r, NULL, &tag))
                        r = TAILQ_NEXT(r, entries);
                else if (r->anchorname[0] && r->anchor == NULL)
index a0b5d54..ff5f6a6 100644 (file)
@@ -1,7 +1,7 @@
 /*     $FreeBSD: src/sys/contrib/pf/net/pfvar.h,v 1.8 2004/08/12 13:59:44 mlaier Exp $ */
 /*     $OpenBSD: pfvar.h,v 1.187 2004/03/22 04:54:18 mcbride Exp $ */
 /* add $OpenBSD: pfvar.h,v 1.194 2004/05/11 07:34:11 dhartmei Exp $ */
-/*     $DragonFly: src/sys/net/pf/pfvar.h,v 1.6 2008/04/06 18:58:16 dillon Exp $ */
+/*     $DragonFly: src/sys/net/pf/pfvar.h,v 1.7 2008/04/06 21:12:42 dillon Exp $ */
 
 /*
  * Copyright (c) 2004 The DragonFly Project.  All rights reserved.
@@ -550,6 +550,7 @@ struct pf_rule {
        u_int32_t                pqid;
        u_int32_t                rt_listid;
        u_int32_t                nr;
+       u_int32_t                prob;
 
        u_int16_t                return_icmp;
        u_int16_t                return_icmp6;
index 8dd8f95..bc45dfb 100644 (file)
@@ -1,5 +1,5 @@
 /*     $OpenBSD: parse.y,v 1.449 2004/03/20 23:20:20 david Exp $       */
-/*     $DragonFly: src/usr.sbin/pfctl/parse.y,v 1.3 2008/04/06 18:58:14 dillon Exp $ */
+/*     $DragonFly: src/usr.sbin/pfctl/parse.y,v 1.4 2008/04/06 21:12:40 dillon Exp $ */
 
 /*
  * Copyright (c) 2001 Markus Friedl.  All rights reserved.
@@ -51,6 +51,7 @@
 #include <errno.h>
 #include <string.h>
 #include <ctype.h>
+#include <math.h>
 #include <err.h>
 #include <limits.h>
 #include <pwd.h>
@@ -177,6 +178,7 @@ struct filter_opts {
        } flags;
        struct node_icmp        *icmpspec;
        u_int32_t                tos;
+       u_int32_t                prob;
        struct {
                int                      action;
                struct node_state_opt   *options;
@@ -404,7 +406,7 @@ extern char *infile;
 %token SET OPTIMIZATION TIMEOUT LIMIT LOGINTERFACE BLOCKPOLICY RANDOMID
 %token REQUIREORDER SYNPROXY FINGERPRINTS NOSYNC DEBUG HOSTID
 %token ANTISPOOF FOR
-%token BITMASK RANDOM SOURCEHASH ROUNDROBIN STATICPORT
+%token BITMASK RANDOM SOURCEHASH ROUNDROBIN STATICPORT PROBABILITY
 %token ALTQ CBQ PRIQ HFSC FAIRQ BANDWIDTH TBRSIZE LINKSHARE REALTIME UPPERLIMIT
 %token QUEUE PRIORITY QLIMIT HOGS BUCKETS
 %token LOAD
@@ -610,6 +612,7 @@ anchorrule  : ANCHOR string dir interface af proto fromto filter_opts {
                        PREPARE_ANCHOR_RULE(r, $2);
                        r.direction = $3;
                        r.af = $5;
+                       r.prob = $8.prob;
 
                        if ($8.match_tag)
                                if (strlcpy(r.match_tagname, $8.match_tag,
@@ -1546,6 +1549,7 @@ pfrule            : action dir logquick interface route af proto fromto
                        r.direction = $2;
                        r.log = $3.log;
                        r.quick = $3.quick;
+                       r.prob = $9.prob;
 
                        r.af = $6;
                        if ($9.tag)
@@ -1859,6 +1863,28 @@ filter_opt       : USER uids {
                        filter_opts.match_tag = $3;
                        filter_opts.match_tag_not = $1;
                }
+               | PROBABILITY STRING                    {
+                       char    *e;
+                       double   p = strtod($2, &e);
+
+                       if (*e == '%') {
+                               p *= 0.01;
+                               e++;
+                       }
+                       if (*e) {
+                               yyerror("invalid probability: %s", $2);
+                               free($2);
+                               YYERROR;
+                       }
+                       p = floor(p * (UINT_MAX+1.0) + 0.5);
+                       if (p < 1.0 || p >= (UINT_MAX+1.0)) {
+                               yyerror("invalid probability: %s", $2);
+                               free($2);
+                               YYERROR;
+                       }
+                       filter_opts.prob = (u_int32_t)p;
+                       free($2);
+               }
                ;
 
 action         : PASS                  { $$.b1 = PF_PASS; $$.b2 = $$.w = 0; }
@@ -4436,6 +4462,7 @@ lookup(char *s)
                { "port",               PORT},
                { "priority",           PRIORITY},
                { "priq",               PRIQ},
+               { "probability",        PROBABILITY},
                { "proto",              PROTO},
                { "qlimit",             QLIMIT},
                { "queue",              QUEUE},
index 692df16..fe657ad 100644 (file)
@@ -1,5 +1,5 @@
 .\"    $OpenBSD: pf.conf.5,v 1.291 2004/02/04 19:38:30 jmc Exp $
-.\"    $DragonFly: src/usr.sbin/pfctl/pf.conf.5,v 1.11 2008/04/06 19:29:23 swildner Exp $
+.\"    $DragonFly: src/usr.sbin/pfctl/pf.conf.5,v 1.12 2008/04/06 21:12:40 dillon Exp $
 .\"
 .\" Copyright (c) 2002, Daniel Hartmeier
 .\" All rights reserved.
@@ -1597,6 +1597,15 @@ by specifying the
 operator before the
 .Ar tagged
 keyword.
+.It Ar probability <number>
+A probability attribute can be attached to a rule, with a value set between
+0 and 1, bounds not included.
+In that case, the rule will be honoured using the given probability value
+only.
+For example, the following rule will drop 20% of incoming ICMP packets:
+.Bd -literal -offset indent
+block in proto icmp probability 20%
+.Ed
 .El
 .Sh ROUTING
 If a packet matches a rule with a route option set, the packet filter will
@@ -2551,7 +2560,8 @@ filteropt      = user | group | flags | icmp-type | icmp6-type | tos |
                  "max-mss" number | "random-id" | "reassemble tcp" |
                  fragmentation | "allow-opts" |
                  "label" string | "tag" string | [ ! ] "tagged" string
-                 "queue" ( string | "(" string [ [ "," ] string ] ")" )
+                 "queue" ( string | "(" string [ [ "," ] string ] ")" ) |
+                "probability" number"%"
 
 nat-rule       = [ "no" ] "nat" [ "pass" ] [ "on" ifspec ] [ af ]
                  [ protospec ] hosts [ "tag" string ]
index 607f48f..d2e51ce 100644 (file)
@@ -1,5 +1,5 @@
 /*     $OpenBSD: pfctl_parser.c,v 1.194.2.1 2004/05/05 04:00:50 brad Exp $ */
-/*     $DragonFly: src/usr.sbin/pfctl/pfctl_parser.c,v 1.1 2004/09/21 21:25:28 joerg Exp $ */
+/*     $DragonFly: src/usr.sbin/pfctl/pfctl_parser.c,v 1.2 2008/04/06 21:12:40 dillon Exp $ */
 
 /*
  * Copyright (c) 2001 Daniel Hartmeier
@@ -781,6 +781,21 @@ print_rule(struct pf_rule *r, int verbose)
                printf(" modulate state");
        else if (r->keep_state == PF_STATE_SYNPROXY)
                printf(" synproxy state");
+       if (r->prob) {
+               char    buf[20];
+
+               snprintf(buf, sizeof(buf), "%f", r->prob*100.0/(UINT_MAX+1.0));
+               for (i = strlen(buf)-1; i > 0; i--) {
+                       if (buf[i] == '0')
+                               buf[i] = '\0';
+                       else {
+                               if (buf[i] == '.')
+                                       buf[i] = '\0';
+                               break;
+                       }
+               }
+               printf(" probability %s%%", buf);
+       }
        opts = 0;
        if (r->max_states || r->max_src_nodes || r->max_src_states)
                opts = 1;