diff options
Diffstat (limited to 'sbin/pfctl')
-rw-r--r-- | sbin/pfctl/parse.y | 536 | ||||
-rw-r--r-- | sbin/pfctl/pfctl.c | 39 | ||||
-rw-r--r-- | sbin/pfctl/pfctl_altq.c | 284 | ||||
-rw-r--r-- | sbin/pfctl/pfctl_parser.c | 80 | ||||
-rw-r--r-- | sbin/pfctl/pfctl_parser.h | 16 | ||||
-rw-r--r-- | sbin/pfctl/pfctl_qstats.c | 68 |
6 files changed, 955 insertions, 68 deletions
diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y index 99c26c0..b5577e2 100644 --- a/sbin/pfctl/parse.y +++ b/sbin/pfctl/parse.y @@ -37,6 +37,8 @@ __FBSDID("$FreeBSD$"); #include <sys/sysctl.h> #endif #include <net/if.h> +#include <net/ethernet.h> +#include <net/if_vlan_var.h> #include <netinet/in.h> #include <netinet/in_systm.h> #include <netinet/ip.h> @@ -46,8 +48,10 @@ __FBSDID("$FreeBSD$"); #include <arpa/inet.h> #include <altq/altq.h> #include <altq/altq_cbq.h> +#include <altq/altq_codel.h> #include <altq/altq_priq.h> #include <altq/altq_hfsc.h> +#include <altq/altq_fairq.h> #include <stdio.h> #include <unistd.h> @@ -158,6 +162,7 @@ struct node_icmp { enum { PF_STATE_OPT_MAX, PF_STATE_OPT_NOSYNC, PF_STATE_OPT_SRCTRACK, PF_STATE_OPT_MAX_SRC_STATES, PF_STATE_OPT_MAX_SRC_CONN, PF_STATE_OPT_MAX_SRC_CONN_RATE, PF_STATE_OPT_MAX_SRC_NODES, + PF_STATE_OPT_MAX_PACKETS, PF_STATE_OPT_OVERLOAD, PF_STATE_OPT_STATELOCK, PF_STATE_OPT_TIMEOUT, PF_STATE_OPT_SLOPPY, }; @@ -169,6 +174,7 @@ struct node_state_opt { u_int32_t max_states; u_int32_t max_src_states; u_int32_t max_src_conn; + u_int32_t max_packets; struct { u_int32_t limit; u_int32_t seconds; @@ -215,6 +221,7 @@ struct filter_opts { #define FOM_TOS 0x04 #define FOM_KEEP 0x08 #define FOM_SRCTRACK 0x10 +#define FOM_DSCP 0x20 struct node_uid *uid; struct node_gid *gid; struct { @@ -225,7 +232,9 @@ struct filter_opts { } flags; struct node_icmp *icmpspec; u_int32_t tos; + u_int32_t dscp; u_int32_t prob; + u_int32_t tracker; struct { int action; struct node_state_opt *options; @@ -233,10 +242,19 @@ struct filter_opts { int fragment; int allowopts; char *label; + char *schedule; struct node_qassign queues; char *tag; char *match_tag; u_int8_t match_tag_not; + struct { + uint8_t pcp[2]; + uint8_t op; + uint8_t setpcp; + } ieee8021q_pcp; + u_int32_t dnpipe; + u_int32_t pdnpipe; + u_int32_t free_flags; u_int rtableid; struct { struct node_host *addr; @@ -246,6 +264,7 @@ struct filter_opts { struct antispoof_opts { char *label; + u_int32_t tracker; u_int rtableid; } antispoof_opts; @@ -298,8 +317,9 @@ struct pool_opts { } pool_opts; - +struct codel_opts codel_opts; struct node_hfsc_opts hfsc_opts; +struct node_fairq_opts fairq_opts; struct node_state_opt *keep_state_defaults = NULL; int disallow_table(struct node_host *, const char *); @@ -337,6 +357,7 @@ int expand_skip_interface(struct node_if *); int check_rulestate(int); int getservice(char *); int rule_label(struct pf_rule *, char *); +int rule_schedule(struct pf_rule *, char *); int rt_tableid_max(void); void mv_rules(struct pf_ruleset *, struct pf_ruleset *); @@ -422,6 +443,8 @@ typedef struct { struct table_opts table_opts; struct pool_opts pool_opts; struct node_hfsc_opts hfsc_opts; + struct node_fairq_opts fairq_opts; + struct codel_opts codel_opts; } v; int lineno; } YYSTYPE; @@ -436,29 +459,31 @@ int parseport(char *, struct range *r, int); %} -%token PASS BLOCK SCRUB RETURN IN OS OUT LOG QUICK ON FROM TO FLAGS +%token PASS BLOCK MATCH SCRUB RETURN IN OS OUT LOG QUICK ON FROM TO FLAGS %token RETURNRST RETURNICMP RETURNICMP6 PROTO INET INET6 ALL ANY ICMPTYPE %token ICMP6TYPE CODE KEEP MODULATE STATE PORT RDR NAT BINAT ARROW NODF -%token MINTTL ERROR ALLOWOPTS FASTROUTE FILENAME ROUTETO DUPTO REPLYTO NO LABEL -%token NOROUTE URPFFAILED FRAGMENT USER GROUP MAXMSS MAXIMUM TTL TOS DROP TABLE +%token MINTTL ERROR ALLOWOPTS FASTROUTE FILENAME ROUTETO DUPTO REPLYTO NO LABEL SCHEDULE +%token NOROUTE URPFFAILED FRAGMENT USER GROUP MAXMSS MAXIMUM TTL TOS DSCP DROP TABLE TRACKER %token REASSEMBLE FRAGDROP FRAGCROP ANCHOR NATANCHOR RDRANCHOR BINATANCHOR %token SET OPTIMIZATION TIMEOUT LIMIT LOGINTERFACE BLOCKPOLICY RANDOMID %token REQUIREORDER SYNPROXY FINGERPRINTS NOSYNC DEBUG SKIP HOSTID %token ANTISPOOF FOR INCLUDE %token BITMASK RANDOM SOURCEHASH ROUNDROBIN STATICPORT PROBABILITY -%token ALTQ CBQ PRIQ HFSC BANDWIDTH TBRSIZE LINKSHARE REALTIME UPPERLIMIT -%token QUEUE PRIORITY QLIMIT RTABLE +%token ALTQ CBQ CODEL PRIQ HFSC FAIRQ BANDWIDTH TBRSIZE LINKSHARE REALTIME +%token UPPERLIMIT QUEUE PRIORITY QLIMIT HOGS BUCKETS RTABLE TARGET INTERVAL +%token DNPIPE DNQUEUE %token LOAD RULESET_OPTIMIZATION %token STICKYADDRESS MAXSRCSTATES MAXSRCNODES SOURCETRACK GLOBAL RULE %token MAXSRCCONN MAXSRCCONNRATE OVERLOAD FLUSH SLOPPY -%token TAGGED TAG IFBOUND FLOATING STATEPOLICY STATEDEFAULTS ROUTE SETTOS +%token TAGGED TAG IFBOUND FLOATING STATEPOLICY STATEDEFAULTS ROUTE SETTOS MAXPCKT +%token IEEE8021QPCP IEEE8021QSETPCP %token DIVERTTO DIVERTREPLY %token <v.string> STRING %token <v.number> NUMBER %token <v.i> PORTBINARY %type <v.interface> interface if_list if_item_not if_item %type <v.number> number icmptype icmp6type uid gid -%type <v.number> tos not yesno +%type <v.number> tos dscp not yesno %type <v.probability> probability %type <v.i> no dir af fragcache optimizer %type <v.i> sourcetrack flush unaryop statelock @@ -473,8 +498,8 @@ int parseport(char *, struct range *r, int); %type <v.icmp> icmp6_list icmp6_item %type <v.number> reticmpspec reticmp6spec %type <v.fromto> fromto -%type <v.peer> ipportspec from to -%type <v.host> ipspec toipspec xhost host dynaddr host_list +%type <v.peer> ipportspec from to toipportspec +%type <v.host> ipspec xhost host dynaddr host_list %type <v.host> redir_host_list redirspec %type <v.host> route_host route_host_list routespec %type <v.os> os xos os_list @@ -483,7 +508,7 @@ int parseport(char *, struct range *r, int); %type <v.gid> gids gid_list gid_item %type <v.route> route %type <v.redirection> redirection redirpool -%type <v.string> label stringall tag anchorname +%type <v.string> label schedule stringall tag anchorname %type <v.string> string varstring numberstring %type <v.keep_state> keep %type <v.state_opt> state_opt_spec state_opt_list state_opt_item @@ -495,6 +520,8 @@ int parseport(char *, struct range *r, int); %type <v.number> cbqflags_list cbqflags_item %type <v.number> priqflags_list priqflags_item %type <v.hfsc_opts> hfscopts_list hfscopts_item hfsc_opts +%type <v.fairq_opts> fairqopts_list fairqopts_item fairq_opts +%type <v.codel_opts> codelopts_list codelopts_item codel_opts %type <v.queue_bwspec> bandwidth %type <v.filter_opts> filter_opts filter_opt filter_opts_l %type <v.antispoof_opts> antispoof_opts antispoof_opt antispoof_opts_l @@ -874,6 +901,11 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto YYERROR; } + r.ieee8021q_pcp.pcp[0] = $9.ieee8021q_pcp.pcp[0]; + r.ieee8021q_pcp.pcp[1] = $9.ieee8021q_pcp.pcp[1]; + r.ieee8021q_pcp.op = $9.ieee8021q_pcp.op; + r.ieee8021q_pcp.setpcp = $9.ieee8021q_pcp.setpcp; + if ($9.match_tag) if (strlcpy(r.match_tagname, $9.match_tag, PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) { @@ -1214,6 +1246,7 @@ antispoof : ANTISPOOF logquick antispoof_ifspc af antispoof_opts { if (rule_label(&r, $5.label)) YYERROR; r.rtableid = $5.rtableid; + r.cuid = $5.tracker; j = calloc(1, sizeof(struct node_if)); if (j == NULL) err(1, "antispoof: calloc"); @@ -1263,6 +1296,7 @@ antispoof : ANTISPOOF logquick antispoof_ifspc af antispoof_opts { r.logif = $2.logif; r.quick = $2.quick; r.af = $4; + r.cuid = $5.tracker; if (rule_label(&r, $5.label)) YYERROR; r.rtableid = $5.rtableid; @@ -1324,6 +1358,9 @@ antispoof_opt : label { } antispoof_opts.label = $1; } + | TRACKER number { + antispoof_opts.tracker = $2; + } | RTABLE NUMBER { if ($2 < 0 || $2 > rt_tableid_max()) { yyerror("invalid rtable id"); @@ -1466,7 +1503,7 @@ altqif : ALTQ interface queue_opts QUEUE qassign { a.scheduler = $3.scheduler.qtype; a.qlimit = $3.qlimit; a.tbrsize = $3.tbrsize; - if ($5 == NULL) { + if ($5 == NULL && $3.scheduler.qtype != ALTQT_CODEL) { yyerror("no child queues specified"); YYERROR; } @@ -1598,14 +1635,22 @@ bandwidth : STRING { bps = strtod($1, &cp); if (cp != NULL) { - if (!strcmp(cp, "b")) + if (!strcmp(cp, "b") || !strcmp(cp, "bit")) ; /* nothing */ - else if (!strcmp(cp, "Kb")) + else if (!strcmp(cp, "Kb") || !strcmp(cp, "Kbit")) bps *= 1000; - else if (!strcmp(cp, "Mb")) + else if (!strcmp(cp, "Mb") || !strcmp(cp, "Mbit")) bps *= 1000 * 1000; - else if (!strcmp(cp, "Gb")) + else if (!strcmp(cp, "Gb") || !strcmp(cp, "Gbit")) bps *= 1000 * 1000 * 1000; + else if (!strcmp(cp, "B") || !strcmp(cp, "Byte")) + ; /* nothing */ + else if (!strcmp(cp, "KB") || !strcmp(cp, "Kbyte")) + bps *= 1024; + else if (!strcmp(cp, "MB") || !strcmp(cp, "Mbyte")) + bps *= 1024 * 1024; + else if (!strcmp(cp, "GB") || !strcmp(cp, "Gbyte")) + bps *= 1024 * 1024 * 1024; else if (!strcmp(cp, "%")) { if (bps < 0 || bps > 100) { yyerror("bandwidth spec " @@ -1659,6 +1704,24 @@ scheduler : CBQ { $$.qtype = ALTQT_HFSC; $$.data.hfsc_opts = $3; } + | FAIRQ { + $$.qtype = ALTQT_FAIRQ; + bzero(&$$.data.fairq_opts, + sizeof(struct node_fairq_opts)); + } + | FAIRQ '(' fairq_opts ')' { + $$.qtype = ALTQT_FAIRQ; + $$.data.fairq_opts = $3; + } + | CODEL { + $$.qtype = ALTQT_CODEL; + bzero(&$$.data.codel_opts, + sizeof(struct codel_opts)); + } + | CODEL '(' codel_opts ')' { + $$.qtype = ALTQT_CODEL; + $$.data.codel_opts = $3; + } ; cbqflags_list : cbqflags_item { $$ |= $1; } @@ -1676,6 +1739,8 @@ cbqflags_item : STRING { $$ = CBQCLF_RED|CBQCLF_ECN; else if (!strcmp($1, "rio")) $$ = CBQCLF_RIO; + else if (!strcmp($1, "codel")) + $$ = CBQCLF_CODEL; else { yyerror("unknown cbq flag \"%s\"", $1); free($1); @@ -1698,6 +1763,8 @@ priqflags_item : STRING { $$ = PRCF_RED|PRCF_ECN; else if (!strcmp($1, "rio")) $$ = PRCF_RIO; + else if (!strcmp($1, "codel")) + $$ = PRCF_CODEL; else { yyerror("unknown priq flag \"%s\"", $1); free($1); @@ -1798,6 +1865,8 @@ hfscopts_item : LINKSHARE bandwidth { hfsc_opts.flags |= HFCF_RED|HFCF_ECN; else if (!strcmp($1, "rio")) hfsc_opts.flags |= HFCF_RIO; + else if (!strcmp($1, "codel")) + hfsc_opts.flags |= HFCF_CODEL; else { yyerror("unknown hfsc flag \"%s\"", $1); free($1); @@ -1807,6 +1876,102 @@ hfscopts_item : LINKSHARE bandwidth { } ; +fairq_opts : { + bzero(&fairq_opts, + sizeof(struct node_fairq_opts)); + } + fairqopts_list { + $$ = fairq_opts; + } + ; + +fairqopts_list : fairqopts_item + | fairqopts_list comma fairqopts_item + ; + +fairqopts_item : LINKSHARE bandwidth { + if (fairq_opts.linkshare.used) { + yyerror("linkshare already specified"); + YYERROR; + } + fairq_opts.linkshare.m2 = $2; + fairq_opts.linkshare.used = 1; + } + | LINKSHARE '(' bandwidth number bandwidth ')' { + if (fairq_opts.linkshare.used) { + yyerror("linkshare already specified"); + YYERROR; + } + fairq_opts.linkshare.m1 = $3; + fairq_opts.linkshare.d = $4; + fairq_opts.linkshare.m2 = $5; + fairq_opts.linkshare.used = 1; + } + | HOGS bandwidth { + fairq_opts.hogs_bw = $2; + } + | BUCKETS number { + fairq_opts.nbuckets = $2; + } + | STRING { + if (!strcmp($1, "default")) + fairq_opts.flags |= FARF_DEFAULTCLASS; + else if (!strcmp($1, "red")) + fairq_opts.flags |= FARF_RED; + else if (!strcmp($1, "ecn")) + fairq_opts.flags |= FARF_RED|FARF_ECN; + else if (!strcmp($1, "rio")) + fairq_opts.flags |= FARF_RIO; + else if (!strcmp($1, "codel")) + fairq_opts.flags |= FARF_CODEL; + else { + yyerror("unknown fairq flag \"%s\"", $1); + free($1); + YYERROR; + } + free($1); + } + ; + +codel_opts : { + bzero(&codel_opts, + sizeof(struct codel_opts)); + } + codelopts_list { + $$ = codel_opts; + } + ; + +codelopts_list : codelopts_item + | codelopts_list comma codelopts_item + ; + +codelopts_item : INTERVAL number { + if (codel_opts.interval) { + yyerror("interval already specified"); + YYERROR; + } + codel_opts.interval = $2; + } + | TARGET number { + if (codel_opts.target) { + yyerror("target already specified"); + YYERROR; + } + codel_opts.target = $2; + } + | STRING { + if (!strcmp($1, "ecn")) + codel_opts.ecn = 1; + else { + yyerror("unknown codel option \"%s\"", $1); + free($1); + YYERROR; + } + free($1); + } + ; + qassign : /* empty */ { $$ = NULL; } | qassign_item { $$ = $1; } | '{' optnl qassign_list '}' { $$ = $3; } @@ -1878,6 +2043,11 @@ pfrule : action dir logquick interface route af proto fromto r.prob = $9.prob; r.rtableid = $9.rtableid; + r.ieee8021q_pcp.pcp[0] = $9.ieee8021q_pcp.pcp[0]; + r.ieee8021q_pcp.pcp[1] = $9.ieee8021q_pcp.pcp[1]; + r.ieee8021q_pcp.op = $9.ieee8021q_pcp.op; + r.ieee8021q_pcp.setpcp = $9.ieee8021q_pcp.setpcp; + r.af = $6; if ($9.tag) if (strlcpy(r.tagname, $9.tag, @@ -1897,6 +2067,11 @@ pfrule : action dir logquick interface route af proto fromto if (rule_label(&r, $9.label)) YYERROR; free($9.label); + if (rule_schedule(&r, $9.schedule)) + YYERROR; + free($9.schedule); + if ($9.tracker) + r.cuid = $9.tracker; r.flags = $9.flags.b1; r.flagset = $9.flags.b2; if (($9.flags.b1 & $9.flags.b2) != $9.flags.b1) { @@ -1928,7 +2103,14 @@ pfrule : action dir logquick interface route af proto fromto #endif } - r.tos = $9.tos; + if ($9.tos) { + r.tos = $9.tos; + r.rule_flag |= PFRULE_TOS; + } + if ($9.dscp) { + r.tos = $9.dscp; + r.rule_flag |= PFRULE_DSCP; + } r.keep_state = $9.keep.action; o = $9.keep.options; @@ -1960,6 +2142,14 @@ pfrule : action dir logquick interface route af proto fromto } r.rule_flag |= PFRULE_NOSYNC; break; + case PF_STATE_OPT_MAX_PACKETS: + if (o->data.max_packets == 0) { + yyerror("max_packets must be" + "greater than 0"); + YYERROR; + } + r.spare2 = o->data.max_packets; + break; case PF_STATE_OPT_SRCTRACK: if (srctrack) { yyerror("state option " @@ -2246,6 +2436,15 @@ pfrule : action dir logquick interface route af proto fromto } #endif + if ($9.dnpipe) { + r.dnpipe = $9.dnpipe; + if ($9.free_flags & PFRULE_DN_IS_PIPE) + r.free_flags |= PFRULE_DN_IS_PIPE; + else + r.free_flags |= PFRULE_DN_IS_QUEUE; + r.pdnpipe = $9.pdnpipe; + } + expand_rule(&r, $4, $5.host, $7, $8.src_os, $8.src.host, $8.src.port, $8.dst.host, $8.dst.port, $9.uid, $9.gid, $9.icmpspec, ""); @@ -2306,6 +2505,14 @@ filter_opt : USER uids { filter_opts.marker |= FOM_TOS; filter_opts.tos = $2; } + | dscp { + if (filter_opts.marker & FOM_DSCP) { + yyerror("dscp cannot be redefined"); + YYERROR; + } + filter_opts.marker |= FOM_DSCP; + filter_opts.dscp = $1; + } | keep { if (filter_opts.marker & FOM_KEEP) { yyerror("modulate or keep cannot be redefined"); @@ -2315,6 +2522,9 @@ filter_opt : USER uids { filter_opts.keep.action = $1.action; filter_opts.keep.options = $1.options; } + | TRACKER number { + filter_opts.tracker = $2; + } | FRAGMENT { filter_opts.fragment = 1; } @@ -2328,6 +2538,13 @@ filter_opt : USER uids { } filter_opts.label = $1; } + | schedule { + if (filter_opts.schedule) { + yyerror("schedule label cannot be redefined"); + YYERROR; + } + filter_opts.schedule = $1; + } | qname { if (filter_opts.queues.qname) { yyerror("queue cannot be redefined"); @@ -2335,6 +2552,32 @@ filter_opt : USER uids { } filter_opts.queues = $1; } + | DNPIPE number { + filter_opts.dnpipe = $2; + filter_opts.free_flags |= PFRULE_DN_IS_PIPE; + } + | DNPIPE '(' number ')' { + filter_opts.dnpipe = $3; + filter_opts.free_flags |= PFRULE_DN_IS_PIPE; + } + | DNPIPE '(' number comma number ')' { + filter_opts.pdnpipe = $5; + filter_opts.dnpipe = $3; + filter_opts.free_flags |= PFRULE_DN_IS_PIPE; + } + | DNQUEUE number { + filter_opts.dnpipe = $2; + filter_opts.free_flags |= PFRULE_DN_IS_QUEUE; + } + | DNQUEUE '(' number comma number ')' { + filter_opts.pdnpipe = $5; + filter_opts.dnpipe = $3; + filter_opts.free_flags |= PFRULE_DN_IS_QUEUE; + } + | DNQUEUE '(' number ')' { + filter_opts.dnpipe = $3; + filter_opts.free_flags |= PFRULE_DN_IS_QUEUE; + } | TAG string { filter_opts.tag = $2; } @@ -2354,6 +2597,98 @@ filter_opt : USER uids { if (filter_opts.prob == 0) filter_opts.prob = 1; } + | IEEE8021QPCP STRING { + u_int pcp; + + /* + * XXXRW: More complete set of operations, similar to + * ports. + */ + if (!strcmp($2, "be")) + pcp = IEEE8021Q_PCP_BE; + else if (!strcmp($2, "bk")) + pcp = IEEE8021Q_PCP_BK; + else if (!strcmp($2, "ee")) + pcp = IEEE8021Q_PCP_EE; + else if (!strcmp($2, "ca")) + pcp = IEEE8021Q_PCP_CA; + else if (!strcmp($2, "vi")) + pcp = IEEE8021Q_PCP_VI; + else if (!strcmp($2, "vo")) + pcp = IEEE8021Q_PCP_VO; + else if (!strcmp($2, "ic")) + pcp = IEEE8021Q_PCP_IC; + else if (!strcmp($2, "nc")) + pcp = IEEE8021Q_PCP_NC; + else + pcp = 8; /* flag bad argument */ + if (pcp > 7) { + yyerror("invalid ieee8021q_pcp value %s", $2); + free($2); + YYERROR; + } + free($2); + filter_opts.ieee8021q_pcp.pcp[0] = pcp; + filter_opts.ieee8021q_pcp.pcp[1] = 0; + filter_opts.ieee8021q_pcp.op = PF_OP_EQ; + } + | IEEE8021QPCP number { + u_int pcp; + + pcp = $2; + if (pcp > 7) { + yyerror("invalid ieee8021q_pcp value %u", pcp); + YYERROR; + } + filter_opts.ieee8021q_pcp.pcp[0] = pcp; + filter_opts.ieee8021q_pcp.pcp[1] = 0; + filter_opts.ieee8021q_pcp.op = PF_OP_EQ; + } + | IEEE8021QSETPCP STRING { + u_int pcp; + + /* + * XXXRW: More complete set of operations, similar to + * ports. + */ + if (!strcmp($2, "be")) + pcp = IEEE8021Q_PCP_BE; + else if (!strcmp($2, "bk")) + pcp = IEEE8021Q_PCP_BK; + else if (!strcmp($2, "ee")) + pcp = IEEE8021Q_PCP_EE; + else if (!strcmp($2, "ca")) + pcp = IEEE8021Q_PCP_CA; + else if (!strcmp($2, "vi")) + pcp = IEEE8021Q_PCP_VI; + else if (!strcmp($2, "vo")) + pcp = IEEE8021Q_PCP_VO; + else if (!strcmp($2, "ic")) + pcp = IEEE8021Q_PCP_IC; + else if (!strcmp($2, "nc")) + pcp = IEEE8021Q_PCP_NC; + else + pcp = 8; /* flag bad argument */ + if (pcp > 7) { + yyerror("invalid ieee8021q_setpcp value %s", + $2); + free($2); + YYERROR; + } + free($2); + filter_opts.ieee8021q_pcp.setpcp = pcp | SETPCP_VALID; + } + | IEEE8021QSETPCP number { + u_int pcp; + + pcp = $2; + if (pcp > 7) { + yyerror("invalid ieee8021q_setpcp value %u", + pcp); + YYERROR; + } + filter_opts.ieee8021q_pcp.setpcp = pcp | SETPCP_VALID; + } | RTABLE NUMBER { if ($2 < 0 || $2 > rt_tableid_max()) { yyerror("invalid rtable id"); @@ -2421,6 +2756,7 @@ probability : STRING { action : PASS { $$.b1 = PF_PASS; $$.b2 = $$.w = 0; } + | MATCH { $$.b1 = PF_MATCH; $$.b2 = $$.w = 0; } | BLOCK blockspec { $$ = $2; $$.b1 = PF_DROP; } ; @@ -2749,8 +3085,8 @@ ipspec : ANY { $$ = NULL; } | '{' optnl host_list '}' { $$ = $3; } ; -toipspec : TO ipspec { $$ = $2; } - | /* empty */ { $$ = NULL; } +toipportspec : TO ipportspec { $$ = $2; } + | /* empty */ { $$.host = NULL; $$.port = NULL; } ; host_list : ipspec optnl { $$ = $1; } @@ -3398,6 +3734,48 @@ tos : STRING { } ; +dscp : DSCP STRING { + if (!strcmp($2, "EF")) + $$ = DSCP_EF; + else if (!strcmp($2, "VA")) + $$ = DSCP_VA; + else if (!strcmp($2, "af11")) + $$ = DSCP_AF11; + else if (!strcmp($2, "af12")) + $$ = DSCP_AF12; + else if (!strcmp($2, "af13")) + $$ = DSCP_AF13; + else if (!strcmp($2, "af21")) + $$ = DSCP_AF21; + else if (!strcmp($2, "af22")) + $$ = DSCP_AF22; + else if (!strcmp($2, "af23")) + $$ = DSCP_AF23; + else if (!strcmp($2, "af31")) + $$ = DSCP_AF31; + else if (!strcmp($2, "af32")) + $$ = DSCP_AF32; + else if (!strcmp($2, "af33")) + $$ = DSCP_AF33; + else if (!strcmp($2, "af41")) + $$ = DSCP_AF41; + else if (!strcmp($2, "af42")) + $$ = DSCP_AF42; + else if (!strcmp($2, "af43")) + $$ = DSCP_AF43; + else if ($2[0] == '0' && $2[1] == 'x') + $$ = strtoul($2, NULL, 16) * 4; + else + $$ = strtoul($2, NULL, 10) * 4; + if (!$$ || $$ > 255) { + yyerror("illegal dscp value %s", $2); + free($2); + YYERROR; + } + free($2); + } + ; + sourcetrack : SOURCETRACK { $$ = PF_SRCTRACK; } | SOURCETRACK GLOBAL { $$ = PF_SRCTRACK_GLOBAL; } | SOURCETRACK RULE { $$ = PF_SRCTRACK_RULE; } @@ -3482,6 +3860,15 @@ state_opt_item : MAXIMUM NUMBER { $$->next = NULL; $$->tail = $$; } + | MAXPCKT NUMBER { + $$ = calloc(1, sizeof(struct node_state_opt)); + if ($$ == NULL) + err(1, "state_opt_item: calloc"); + $$->type = PF_STATE_OPT_MAX_PACKETS; + $$->data.max_packets = $2; + $$->next = NULL; + $$->tail = $$; + } | MAXSRCCONN NUMBER { if ($2 < 0 || $2 > UINT_MAX) { yyerror("only positive values permitted"); @@ -3604,6 +3991,11 @@ label : LABEL STRING { } ; +schedule : SCHEDULE STRING { + $$ = $2; + } + ; + qname : QUEUE STRING { $$.qname = $2; $$.pqname = NULL; @@ -3979,7 +4371,7 @@ natrule : nataction interface af proto fromto tag tagged rtable } ; -binatrule : no BINAT natpasslog interface af proto FROM host toipspec tag +binatrule : no BINAT natpasslog interface af proto FROM ipportspec toipportspec tag tagged rtable redirection { struct pf_rule binat; @@ -3987,7 +4379,7 @@ binatrule : no BINAT natpasslog interface af proto FROM host toipspec tag if (check_rulestate(PFCTL_STATE_NAT)) YYERROR; - if (disallow_urpf_failed($9, "\"urpf-failed\" is not " + if (disallow_urpf_failed($9.host, "\"urpf-failed\" is not " "permitted as a binat destination")) YYERROR; @@ -4005,10 +4397,10 @@ binatrule : no BINAT natpasslog interface af proto FROM host toipspec tag binat.log = $3.b2; binat.logif = $3.w2; binat.af = $5; - if (!binat.af && $8 != NULL && $8->af) - binat.af = $8->af; - if (!binat.af && $9 != NULL && $9->af) - binat.af = $9->af; + if (!binat.af && $8.host != NULL && $8.host->af) + binat.af = $8.host->af; + if (!binat.af && $9.host != NULL && $9.host->af) + binat.af = $9.host->af; if (!binat.af && $13 != NULL && $13->host) binat.af = $13->host->af; @@ -4047,10 +4439,10 @@ binatrule : no BINAT natpasslog interface af proto FROM host toipspec tag free($6); } - if ($8 != NULL && disallow_table($8, "invalid use of " + if ($8.host != NULL && disallow_table($8.host, "invalid use of " "table <%s> as the source address of a binat rule")) YYERROR; - if ($8 != NULL && disallow_alias($8, "invalid use of " + if ($8.host != NULL && disallow_alias($8.host, "invalid use of " "interface (%s) as the source address of a binat " "rule")) YYERROR; @@ -4063,38 +4455,46 @@ binatrule : no BINAT natpasslog interface af proto FROM host toipspec tag "redirect address of a binat rule")) YYERROR; - if ($8 != NULL) { - if ($8->next) { + if ($8.host != NULL) { + if ($8.host->next) { yyerror("multiple binat ip addresses"); YYERROR; } - if ($8->addr.type == PF_ADDR_DYNIFTL) - $8->af = binat.af; - if ($8->af != binat.af) { + if ($8.host->addr.type == PF_ADDR_DYNIFTL) + $8.host->af = binat.af; + if ($8.host->af != binat.af) { yyerror("binat ip versions must match"); YYERROR; } - if (check_netmask($8, binat.af)) + if (check_netmask($8.host, binat.af)) YYERROR; - memcpy(&binat.src.addr, &$8->addr, + memcpy(&binat.src.addr, &$8.host->addr, sizeof(binat.src.addr)); - free($8); + binat.src.neg = $8.host->not; + free($8.host); } - if ($9 != NULL) { - if ($9->next) { + if ($9.host != NULL) { + if ($9.host->next) { yyerror("multiple binat ip addresses"); YYERROR; } - if ($9->af != binat.af && $9->af) { + if ($9.host->af != binat.af && $9.host->af) { yyerror("binat ip versions must match"); YYERROR; } - if (check_netmask($9, binat.af)) + if (check_netmask($9.host, binat.af)) YYERROR; - memcpy(&binat.dst.addr, &$9->addr, + memcpy(&binat.dst.addr, &$9.host->addr, sizeof(binat.dst.addr)); - binat.dst.neg = $9->not; - free($9); + binat.dst.neg = $9.host->not; + free($9.host); + } + + if ($9.port != NULL) { + binat.dst.port[0] = $9.port->port[0]; + binat.dst.port[1] = $9.port->port[1]; + binat.dst.port_op = $9.port->op; + free($9.port); } if (binat.action == PF_NOBINAT) { @@ -4440,6 +4840,15 @@ filter_consistent(struct pf_rule *r, int anchor_call) "synproxy state or modulate state"); problems++; } + if ((r->rule_flag & PFRULE_TOS) && (r->rule_flag & PFRULE_DSCP)) { + yyerror("tos and dscp cannot be used together"); + problems++; + } + if (r->dnpipe && r->pdnpipe && !r->direction) { + yyerror("dummynet cannot be specified without direction"); + problems++; + } + return (-problems); } @@ -4732,7 +5141,8 @@ expand_altq(struct pf_altq *a, struct node_if *interfaces, if ((pf->loadopt & PFCTL_FLAG_ALTQ) == 0) { FREE_LIST(struct node_if, interfaces); - FREE_LIST(struct node_queue, nqueues); + if (nqueues) + FREE_LIST(struct node_queue, nqueues); return (0); } @@ -4823,7 +5233,8 @@ expand_altq(struct pf_altq *a, struct node_if *interfaces, } ); FREE_LIST(struct node_if, interfaces); - FREE_LIST(struct node_queue, nqueues); + if (nqueues) + FREE_LIST(struct node_queue, nqueues); return (errs); } @@ -4983,6 +5394,7 @@ expand_rule(struct pf_rule *r, int added = 0, error = 0; char ifname[IF_NAMESIZE]; char label[PF_RULE_LABEL_SIZE]; + char schedule[PF_RULE_LABEL_SIZE]; char tagname[PF_TAG_NAME_SIZE]; char match_tagname[PF_TAG_NAME_SIZE]; struct pf_pooladdr *pa; @@ -4991,6 +5403,8 @@ expand_rule(struct pf_rule *r, if (strlcpy(label, r->label, sizeof(label)) >= sizeof(label)) errx(1, "expand_rule: strlcpy"); + if (strlcpy(schedule, r->schedule, sizeof(schedule)) > sizeof(schedule)) + errx(1, "expand_rule: strlcpy"); if (strlcpy(tagname, r->tagname, sizeof(tagname)) >= sizeof(tagname)) errx(1, "expand_rule: strlcpy"); if (strlcpy(match_tagname, r->match_tagname, sizeof(match_tagname)) >= @@ -5042,6 +5456,9 @@ expand_rule(struct pf_rule *r, if (strlcpy(r->label, label, sizeof(r->label)) >= sizeof(r->label)) errx(1, "expand_rule: strlcpy"); + if (strlcpy(r->schedule, schedule, sizeof(r->schedule)) >= + sizeof(r->schedule)) + errx(1, "expand_rule: strlcpy"); if (strlcpy(r->tagname, tagname, sizeof(r->tagname)) >= sizeof(r->tagname)) errx(1, "expand_rule: strlcpy"); @@ -5050,6 +5467,8 @@ expand_rule(struct pf_rule *r, errx(1, "expand_rule: strlcpy"); expand_label(r->label, PF_RULE_LABEL_SIZE, r->ifname, r->af, src_host, src_port, dst_host, dst_port, proto->proto); + expand_label(r->schedule, PF_RULE_LABEL_SIZE, r->ifname, r->af, + src_host, src_port, dst_host, dst_port, proto->proto); expand_label(r->tagname, PF_TAG_NAME_SIZE, r->ifname, r->af, src_host, src_port, dst_host, dst_port, proto->proto); expand_label(r->match_tagname, PF_TAG_NAME_SIZE, r->ifname, @@ -5226,15 +5645,21 @@ lookup(char *s) { "bitmask", BITMASK}, { "block", BLOCK}, { "block-policy", BLOCKPOLICY}, + { "buckets", BUCKETS}, { "cbq", CBQ}, { "code", CODE}, + { "codelq", CODEL}, { "crop", FRAGCROP}, { "debug", DEBUG}, { "divert-reply", DIVERTREPLY}, { "divert-to", DIVERTTO}, + { "dnpipe", DNPIPE}, + { "dnqueue", DNQUEUE}, { "drop", DROP}, { "drop-ovl", FRAGDROP}, + { "dscp", DSCP}, { "dup-to", DUPTO}, + { "fairq", FAIRQ}, { "fastroute", FASTROUTE}, { "file", FILENAME}, { "fingerprints", FINGERPRINTS}, @@ -5247,14 +5672,18 @@ lookup(char *s) { "global", GLOBAL}, { "group", GROUP}, { "hfsc", HFSC}, + { "hogs", HOGS}, { "hostid", HOSTID}, { "icmp-type", ICMPTYPE}, { "icmp6-type", ICMP6TYPE}, + { "ieee8021q-pcp", IEEE8021QPCP}, + { "ieee8021q-setpcp", IEEE8021QSETPCP}, { "if-bound", IFBOUND}, { "in", IN}, { "include", INCLUDE}, { "inet", INET}, { "inet6", INET6}, + { "interval", INTERVAL}, { "keep", KEEP}, { "label", LABEL}, { "limit", LIMIT}, @@ -5262,8 +5691,10 @@ lookup(char *s) { "load", LOAD}, { "log", LOG}, { "loginterface", LOGINTERFACE}, + { "match", MATCH}, { "max", MAXIMUM}, { "max-mss", MAXMSS}, + { "max-packets", MAXPCKT}, { "max-src-conn", MAXSRCCONN}, { "max-src-conn-rate", MAXSRCCONNRATE}, { "max-src-nodes", MAXSRCNODES}, @@ -5308,6 +5739,7 @@ lookup(char *s) { "rtable", RTABLE}, { "rule", RULE}, { "ruleset-optimization", RULESET_OPTIMIZATION}, + { "schedule", SCHEDULE}, { "scrub", SCRUB}, { "set", SET}, { "set-tos", SETTOS}, @@ -5324,10 +5756,12 @@ lookup(char *s) { "table", TABLE}, { "tag", TAG}, { "tagged", TAGGED}, + { "target", TARGET}, { "tbrsize", TBRSIZE}, { "timeout", TIMEOUT}, { "to", TO}, { "tos", TOS}, + { "tracker", TRACKER}, { "ttl", TTL}, { "upperlimit", UPPERLIMIT}, { "urpf-failed", URPFFAILED}, @@ -5939,6 +6373,20 @@ rule_label(struct pf_rule *r, char *s) return (0); } +int +rule_schedule(struct pf_rule *r, char *s) +{ + if (s) { + if (strlcpy(r->schedule, s, sizeof(r->label)) >= + sizeof(r->label)) { + yyerror("rule schedule label too long (max %d chars)", + sizeof(r->label)-1); + return (-1); + } + } + return (0); +} + u_int16_t parseicmpspec(char *w, sa_family_t af) { diff --git a/sbin/pfctl/pfctl.c b/sbin/pfctl/pfctl.c index f8802b3..d606b62b 100644 --- a/sbin/pfctl/pfctl.c +++ b/sbin/pfctl/pfctl.c @@ -78,6 +78,7 @@ void pfctl_addrprefix(char *, struct pf_addr *); int pfctl_kill_src_nodes(int, const char *, int); int pfctl_net_kill_states(int, const char *, int); int pfctl_label_kill_states(int, const char *, int); +int pfctl_kill_schedule(int, const char *, int); int pfctl_id_kill_states(int, const char *, int); void pfctl_init_options(struct pfctl *); int pfctl_load_options(struct pfctl *); @@ -117,6 +118,7 @@ const char *optiopt = NULL; char *pf_device = "/dev/pf"; char *ifaceopt; char *tableopt; +char *schedule = NULL; const char *tblcmdopt; int src_node_killers; char *src_node_kill[2]; @@ -654,6 +656,25 @@ pfctl_net_kill_states(int dev, const char *iface, int opts) } int +pfctl_kill_schedule(int dev, const char *sched, int opts) +{ + struct pfioc_schedule_kill psk; + + memset(&psk, 0, sizeof(psk)); + if (sched != NULL && strlcpy(psk.schedule, sched, + sizeof(psk.schedule)) >= sizeof(psk.schedule)) + errx(1, "invalid schedule label: %s", sched); + + if (ioctl(dev, DIOCKILLSCHEDULE, &psk)) + err(1, "DIOCKILLSCHEDULE"); + + if ((opts & PF_OPT_QUIET) == 0) + fprintf(stderr, "killed %d states from %s schedule label\n", + psk.numberkilled, sched); + return (0); +} + +int pfctl_label_kill_states(int dev, const char *iface, int opts) { struct pfioc_state_kill psk; @@ -804,10 +825,17 @@ pfctl_print_rule_counters(struct pf_rule *rule, int opts) (unsigned long long)(rule->bytes[0] + rule->bytes[1]), (uintmax_t)rule->u_states_cur); if (!(opts & PF_OPT_DEBUG)) +#ifdef PF_USER_INFO printf(" [ Inserted: uid %u pid %u " "State Creations: %-6ju]\n", (unsigned)rule->cuid, (unsigned)rule->cpid, (uintmax_t)rule->u_states_tot); +#else + printf(" [ Inserted: pid %u " + "State Creations: %-6ju]\n", + (unsigned)rule->cpid, + (uintmax_t)rule->states_tot); +#endif } } @@ -2004,7 +2032,7 @@ main(int argc, char *argv[]) usage(); while ((ch = getopt(argc, argv, - "a:AdD:eqf:F:ghi:k:K:mnNOo:Pp:rRs:t:T:vx:z")) != -1) { + "a:AdD:eqf:F:ghi:k:K:mnNOo:Pp:rRs:t:T:vx:y:z")) != -1) { switch (ch) { case 'a': anchoropt = optarg; @@ -2118,6 +2146,12 @@ main(int argc, char *argv[]) opts |= PF_OPT_VERBOSE2; opts |= PF_OPT_VERBOSE; break; + case 'y': + if (schedule != NULL && strlen(schedule) > 64) + errx(1, "Schedule label cannot be more than 64 characters\n"); + schedule = optarg; + mode = O_RDWR; + break; case 'x': debugopt = pfctl_lookup_option(optarg, debugopt_list); if (debugopt == NULL) { @@ -2326,6 +2360,9 @@ main(int argc, char *argv[]) if (src_node_killers) pfctl_kill_src_nodes(dev, ifaceopt, opts); + if (schedule) + pfctl_kill_schedule(dev, schedule, opts); + if (tblcmdopt != NULL) { error = pfctl_command_tables(argc, argv, tableopt, tblcmdopt, rulesopt, anchorname, opts); diff --git a/sbin/pfctl/pfctl_altq.c b/sbin/pfctl/pfctl_altq.c index ae566f8..716df09 100644 --- a/sbin/pfctl/pfctl_altq.c +++ b/sbin/pfctl/pfctl_altq.c @@ -33,6 +33,7 @@ __FBSDID("$FreeBSD$"); #include <errno.h> #include <limits.h> #include <math.h> +#include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -40,8 +41,10 @@ __FBSDID("$FreeBSD$"); #include <altq/altq.h> #include <altq/altq_cbq.h> +#include <altq/altq_codel.h> #include <altq/altq_priq.h> #include <altq/altq_hfsc.h> +#include <altq/altq_fairq.h> #include "pfctl_parser.h" #include "pfctl.h" @@ -59,6 +62,9 @@ static int cbq_compute_idletime(struct pfctl *, struct pf_altq *); static int check_commit_cbq(int, int, struct pf_altq *); static int print_cbq_opts(const struct pf_altq *); +static int print_codel_opts(const struct pf_altq *, + const struct node_queue_opt *); + static int eval_pfqueue_priq(struct pfctl *, struct pf_altq *); static int check_commit_priq(int, int, struct pf_altq *); static int print_priq_opts(const struct pf_altq *); @@ -68,6 +74,11 @@ static int check_commit_hfsc(int, int, struct pf_altq *); static int print_hfsc_opts(const struct pf_altq *, const struct node_queue_opt *); +static int eval_pfqueue_fairq(struct pfctl *, struct pf_altq *); +static int print_fairq_opts(const struct pf_altq *, + const struct node_queue_opt *); +static int check_commit_fairq(int, int, struct pf_altq *); + static void gsc_add_sc(struct gen_sc *, struct service_curve *); static int is_gsc_under_sc(struct gen_sc *, struct service_curve *); @@ -88,6 +99,8 @@ int eval_queue_opts(struct pf_altq *, struct node_queue_opt *, u_int32_t eval_bwspec(struct node_queue_bw *, u_int32_t); void print_hfsc_sc(const char *, u_int, u_int, u_int, const struct node_hfsc_sc *); +void print_fairq_sc(const char *, u_int, u_int, u_int, + const struct node_fairq_sc *); void pfaltq_store(struct pf_altq *a) @@ -173,6 +186,14 @@ print_altq(const struct pf_altq *a, unsigned int level, if (!print_hfsc_opts(a, qopts)) printf("hfsc "); break; + case ALTQT_FAIRQ: + if (!print_fairq_opts(a, qopts)) + printf("fairq "); + break; + case ALTQT_CODEL: + if (!print_codel_opts(a, qopts)) + printf("codel "); + break; } if (bw != NULL && bw->bw_percent > 0) { @@ -203,7 +224,8 @@ print_queue(const struct pf_altq *a, unsigned int level, printf("%s ", a->qname); if (print_interface) printf("on %s ", a->ifname); - if (a->scheduler == ALTQT_CBQ || a->scheduler == ALTQT_HFSC) { + if (a->scheduler == ALTQT_CBQ || a->scheduler == ALTQT_HFSC || + a->scheduler == ALTQT_FAIRQ) { if (bw != NULL && bw->bw_percent > 0) { if (bw->bw_percent < 100) printf("bandwidth %u%% ", bw->bw_percent); @@ -224,6 +246,9 @@ print_queue(const struct pf_altq *a, unsigned int level, case ALTQT_HFSC: print_hfsc_opts(a, qopts); break; + case ALTQT_FAIRQ: + print_fairq_opts(a, qopts); + break; } } @@ -240,15 +265,11 @@ eval_pfaltq(struct pfctl *pf, struct pf_altq *pa, struct node_queue_bw *bw, pa->ifbandwidth = bw->bw_absolute; else #ifdef __FreeBSD__ - if ((rate = getifspeed(pf->dev, pa->ifname)) == 0) { -#else - if ((rate = getifspeed(pa->ifname)) == 0) { + rate = getifspeed(pf->dev, pa->ifname); + if (rate == 0) + rate = IF_Mbps(100); #endif - fprintf(stderr, "interface %s does not know its bandwidth, " - "please specify an absolute bandwidth\n", - pa->ifname); - errors++; - } else if ((pa->ifbandwidth = eval_bwspec(bw, rate)) == 0) + if ((pa->ifbandwidth = eval_bwspec(bw, rate)) == 0) pa->ifbandwidth = rate; errors += eval_queue_opts(pa, opts, pa->ifbandwidth); @@ -294,6 +315,9 @@ check_commit_altq(int dev, int opts) case ALTQT_HFSC: error = check_commit_hfsc(dev, opts, altq); break; + case ALTQT_FAIRQ: + error = check_commit_fairq(dev, opts, altq); + break; default: break; } @@ -342,7 +366,8 @@ eval_pfqueue(struct pfctl *pf, struct pf_altq *pa, struct node_queue_bw *bw, if (pa->qlimit == 0) pa->qlimit = DEFAULT_QLIMIT; - if (pa->scheduler == ALTQT_CBQ || pa->scheduler == ALTQT_HFSC) { + if (pa->scheduler == ALTQT_CBQ || pa->scheduler == ALTQT_HFSC || + pa->scheduler == ALTQT_FAIRQ) { pa->bandwidth = eval_bwspec(bw, parent == NULL ? 0 : parent->bandwidth); @@ -388,6 +413,9 @@ eval_pfqueue(struct pfctl *pf, struct pf_altq *pa, struct node_queue_bw *bw, case ALTQT_HFSC: error = eval_pfqueue_hfsc(pf, pa); break; + case ALTQT_FAIRQ: + error = eval_pfqueue_fairq(pf, pa); + break; default: break; } @@ -568,6 +596,8 @@ print_cbq_opts(const struct pf_altq *a) printf(" ecn"); if (opts->flags & CBQCLF_RIO) printf(" rio"); + if (opts->flags & CBQCLF_CODEL) + printf(" codel"); if (opts->flags & CBQCLF_CLEARDSCP) printf(" cleardscp"); if (opts->flags & CBQCLF_FLOWVALVE) @@ -655,6 +685,8 @@ print_priq_opts(const struct pf_altq *a) printf(" ecn"); if (opts->flags & PRCF_RIO) printf(" rio"); + if (opts->flags & PRCF_CODEL) + printf(" codel"); if (opts->flags & PRCF_CLEARDSCP) printf(" cleardscp"); if (opts->flags & PRCF_DEFAULTCLASS) @@ -700,13 +732,6 @@ eval_pfqueue_hfsc(struct pfctl *pf, struct pf_altq *pa) return (-1); } - if ((opts->rtsc_m1 < opts->rtsc_m2 && opts->rtsc_m1 != 0) || - (opts->lssc_m1 < opts->lssc_m2 && opts->lssc_m1 != 0) || - (opts->ulsc_m1 < opts->ulsc_m2 && opts->ulsc_m1 != 0)) { - warnx("m1 must be zero for convex curve: %s", pa->qname); - return (-1); - } - /* * admission control: * for the real-time service curve, the sum of the service curves @@ -807,6 +832,85 @@ err_ret: return (-1); } +/* + * FAIRQ support functions + */ +static int +eval_pfqueue_fairq(struct pfctl *pf __unused, struct pf_altq *pa) +{ + struct pf_altq *altq, *parent; + struct fairq_opts *opts; + struct service_curve sc; + + opts = &pa->pq_u.fairq_opts; + + if (pa->parent[0] == 0) { + /* root queue */ + opts->lssc_m1 = pa->ifbandwidth; + opts->lssc_m2 = pa->ifbandwidth; + opts->lssc_d = 0; + return (0); + } + + LIST_INIT(&lssc); + + /* if link_share is not specified, use bandwidth */ + if (opts->lssc_m2 == 0) + opts->lssc_m2 = pa->bandwidth; + + /* + * admission control: + * for the real-time service curve, the sum of the service curves + * should not exceed 80% of the interface bandwidth. 20% is reserved + * not to over-commit the actual interface bandwidth. + * for the link-sharing service curve, the sum of the child service + * curve should not exceed the parent service curve. + * for the upper-limit service curve, the assigned bandwidth should + * be smaller than the interface bandwidth, and the upper-limit should + * be larger than the real-time service curve when both are defined. + */ + parent = qname_to_pfaltq(pa->parent, pa->ifname); + if (parent == NULL) + errx(1, "parent %s not found for %s", pa->parent, pa->qname); + + TAILQ_FOREACH(altq, &altqs, entries) { + if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0) + continue; + if (altq->qname[0] == 0) /* this is for interface */ + continue; + + if (strncmp(altq->parent, pa->parent, PF_QNAME_SIZE) != 0) + continue; + + /* if the class has a link-sharing service curve, add it. */ + if (opts->lssc_m2 != 0 && altq->pq_u.fairq_opts.lssc_m2 != 0) { + sc.m1 = altq->pq_u.fairq_opts.lssc_m1; + sc.d = altq->pq_u.fairq_opts.lssc_d; + sc.m2 = altq->pq_u.fairq_opts.lssc_m2; + gsc_add_sc(&lssc, &sc); + } + } + + /* check the link-sharing service curve. */ + if (opts->lssc_m2 != 0) { + sc.m1 = parent->pq_u.fairq_opts.lssc_m1; + sc.d = parent->pq_u.fairq_opts.lssc_d; + sc.m2 = parent->pq_u.fairq_opts.lssc_m2; + if (!is_gsc_under_sc(&lssc, &sc)) { + warnx("link-sharing sc exceeds parent's sc"); + goto err_ret; + } + } + + gsc_destroy(&lssc); + + return (0); + +err_ret: + gsc_destroy(&lssc); + return (-1); +} + static int check_commit_hfsc(int dev, int opts, struct pf_altq *pa) { @@ -847,6 +951,43 @@ check_commit_hfsc(int dev, int opts, struct pf_altq *pa) } static int +check_commit_fairq(int dev __unused, int opts __unused, struct pf_altq *pa) +{ + struct pf_altq *altq, *def = NULL; + int default_class; + int error = 0; + + /* check if fairq has one default queue for this interface */ + default_class = 0; + TAILQ_FOREACH(altq, &altqs, entries) { + if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0) + continue; + if (altq->qname[0] == 0) /* this is for interface */ + continue; + if (altq->pq_u.fairq_opts.flags & FARF_DEFAULTCLASS) { + default_class++; + def = altq; + } + } + if (default_class != 1) { + warnx("should have one default queue on %s", pa->ifname); + return (1); + } + /* make sure the default queue is a leaf */ + TAILQ_FOREACH(altq, &altqs, entries) { + if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0) + continue; + if (altq->qname[0] == 0) /* this is for interface */ + continue; + if (strncmp(altq->parent, def->qname, PF_QNAME_SIZE) == 0) { + warnx("default queue is not a leaf"); + error++; + } + } + return (error); +} + +static int print_hfsc_opts(const struct pf_altq *a, const struct node_queue_opt *qopts) { const struct hfsc_opts *opts; @@ -871,6 +1012,8 @@ print_hfsc_opts(const struct pf_altq *a, const struct node_queue_opt *qopts) printf(" ecn"); if (opts->flags & HFCF_RIO) printf(" rio"); + if (opts->flags & HFCF_CODEL) + printf(" codel"); if (opts->flags & HFCF_CLEARDSCP) printf(" cleardscp"); if (opts->flags & HFCF_DEFAULTCLASS) @@ -892,6 +1035,67 @@ print_hfsc_opts(const struct pf_altq *a, const struct node_queue_opt *qopts) return (0); } +static int +print_codel_opts(const struct pf_altq *a, const struct node_queue_opt *qopts) +{ + const struct codel_opts *opts; + + opts = &a->pq_u.codel_opts; + if (opts->target || opts->interval || opts->ecn) { + printf("codel("); + if (opts->target) + printf(" target %d", opts->target); + if (opts->interval) + printf(" interval %d", opts->interval); + if (opts->ecn) + printf("ecn"); + printf(" ) "); + + return (1); + } + + return (0); +} + +static int +print_fairq_opts(const struct pf_altq *a, const struct node_queue_opt *qopts) +{ + const struct fairq_opts *opts; + const struct node_fairq_sc *loc_lssc; + + opts = &a->pq_u.fairq_opts; + if (qopts == NULL) + loc_lssc = NULL; + else + loc_lssc = &qopts->data.fairq_opts.linkshare; + + if (opts->flags || + (opts->lssc_m2 != 0 && (opts->lssc_m2 != a->bandwidth || + opts->lssc_d != 0))) { + printf("fairq("); + if (opts->flags & FARF_RED) + printf(" red"); + if (opts->flags & FARF_ECN) + printf(" ecn"); + if (opts->flags & FARF_RIO) + printf(" rio"); + if (opts->flags & FARF_CODEL) + printf(" codel"); + if (opts->flags & FARF_CLEARDSCP) + printf(" cleardscp"); + if (opts->flags & FARF_DEFAULTCLASS) + printf(" default"); + if (opts->lssc_m2 != 0 && (opts->lssc_m2 != a->bandwidth || + opts->lssc_d != 0)) + print_fairq_sc("linkshare", opts->lssc_m1, opts->lssc_d, + opts->lssc_m2, loc_lssc); + printf(" ) "); + + return (1); + } else + return (0); +} + /* * admission control using generalized service curve */ @@ -1211,6 +1415,28 @@ eval_queue_opts(struct pf_altq *pa, struct node_queue_opt *opts, opts->data.hfsc_opts.upperlimit.d; } break; + case ALTQT_FAIRQ: + pa->pq_u.fairq_opts.flags = opts->data.fairq_opts.flags; + pa->pq_u.fairq_opts.nbuckets = opts->data.fairq_opts.nbuckets; + pa->pq_u.fairq_opts.hogs_m1 = + eval_bwspec(&opts->data.fairq_opts.hogs_bw, ref_bw); + + if (opts->data.fairq_opts.linkshare.used) { + pa->pq_u.fairq_opts.lssc_m1 = + eval_bwspec(&opts->data.fairq_opts.linkshare.m1, + ref_bw); + pa->pq_u.fairq_opts.lssc_m2 = + eval_bwspec(&opts->data.fairq_opts.linkshare.m2, + ref_bw); + pa->pq_u.fairq_opts.lssc_d = + opts->data.fairq_opts.linkshare.d; + } + break; + case ALTQT_CODEL: + pa->pq_u.codel_opts.target = opts->data.codel_opts.target; + pa->pq_u.codel_opts.interval = opts->data.codel_opts.interval; + pa->pq_u.codel_opts.ecn = opts->data.codel_opts.ecn; + break; default: warnx("eval_queue_opts: unknown scheduler type %u", opts->qtype); @@ -1256,3 +1482,27 @@ print_hfsc_sc(const char *scname, u_int m1, u_int d, u_int m2, if (d != 0) printf(")"); } + +void +print_fairq_sc(const char *scname, u_int m1, u_int d, u_int m2, + const struct node_fairq_sc *sc) +{ + printf(" %s", scname); + + if (d != 0) { + printf("("); + if (sc != NULL && sc->m1.bw_percent > 0) + printf("%u%%", sc->m1.bw_percent); + else + printf("%s", rate2str((double)m1)); + printf(" %u", d); + } + + if (sc != NULL && sc->m2.bw_percent > 0) + printf(" %u%%", sc->m2.bw_percent); + else + printf(" %s", rate2str((double)m2)); + + if (d != 0) + printf(")"); +} diff --git a/sbin/pfctl/pfctl_parser.c b/sbin/pfctl/pfctl_parser.c index 1f4375a..5b03a93 100644 --- a/sbin/pfctl/pfctl_parser.c +++ b/sbin/pfctl/pfctl_parser.c @@ -40,6 +40,8 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/proc.h> #include <net/if.h> +#include <net/ethernet.h> +#include <net/if_vlan_var.h> #include <netinet/in.h> #include <netinet/in_systm.h> #include <netinet/ip.h> @@ -65,6 +67,8 @@ __FBSDID("$FreeBSD$"); void print_op (u_int8_t, const char *, const char *); void print_port (u_int8_t, u_int16_t, u_int16_t, const char *, int); void print_ugid (u_int8_t, unsigned, unsigned, const char *, unsigned); +void print_ieee8021q_pcp (u_int8_t, uint8_t, uint8_t); +void print_ieee8021q_setpcp (u_int8_t); void print_flags (u_int8_t); void print_fromto(struct pf_rule_addr *, pf_osfp_t, struct pf_rule_addr *, u_int8_t, u_int8_t, int, int); @@ -353,6 +357,47 @@ print_ugid(u_int8_t op, unsigned u1, unsigned u2, const char *t, unsigned umax) print_op(op, a1, a2); } +static const char * +ieee8021q_pcp_name(u_int8_t pcp) +{ + const char *s; + + if (pcp == IEEE8021Q_PCP_BE) + s = "be"; + else if (pcp == IEEE8021Q_PCP_BK) + s = "bk"; + else if (pcp == IEEE8021Q_PCP_EE) + s = "ee"; + else if (pcp == IEEE8021Q_PCP_CA) + s = "ca"; + else if (pcp == IEEE8021Q_PCP_VI) + s = "vi"; + else if (pcp == IEEE8021Q_PCP_VO) + s = "vo"; + else if (pcp == IEEE8021Q_PCP_IC) + s = "ic"; + else if (pcp == IEEE8021Q_PCP_NC) + s = "nc"; + else + s = "??"; + return (s); +} + + void +print_ieee8021q_pcp(u_int8_t op, u_int8_t pcp0, u_int8_t pcp1) +{ + + printf(" ieee8021q-pcp"); + print_op(op, ieee8021q_pcp_name(pcp0), ieee8021q_pcp_name(pcp1)); +} + +void +print_ieee8021q_setpcp(u_int8_t pcp) +{ + + printf(" ieee8021q-setpcp %s", ieee8021q_pcp_name(pcp)); +} + void print_flags(u_int8_t f) { @@ -691,8 +736,14 @@ print_rule(struct pf_rule *r, const char *anchor_call, int verbose, int numeric) int i, opts; if (verbose) +#ifdef PF_USER_INFO printf("@%d ", r->nr); - if (r->action > PF_NORDR) +#else + printf("@%d(%u) ", r->nr, r->cuid); +#endif + if (r->action == PF_MATCH) + printf("match"); + else if (r->action > PF_NORDR) printf("action(%d)", r->action); else if (anchor_call[0]) { if (anchor_call[0] == '_') { @@ -847,8 +898,10 @@ print_rule(struct pf_rule *r, const char *anchor_call, int verbose, int numeric) printf(" code %u", r->code-1); } } - if (r->tos) + if (r->tos && (r->rule_flag & PFRULE_TOS)) printf(" tos 0x%2.2x", r->tos); + if (r->tos && (r->rule_flag & PFRULE_DSCP)) + printf(" dscp 0x%2.2x", r->tos & DSCP_MASK); if (!r->keep_state && r->action == PF_PASS && !anchor_call[0]) printf(" no state"); else if (r->keep_state == PF_STATE_NORMAL) @@ -873,7 +926,7 @@ print_rule(struct pf_rule *r, const char *anchor_call, int verbose, int numeric) printf(" probability %s%%", buf); } opts = 0; - if (r->max_states || r->max_src_nodes || r->max_src_states) + if (r->max_states || r->max_src_nodes || r->max_src_states || r->spare2) opts = 1; if (r->rule_flag & PFRULE_NOSYNC) opts = 1; @@ -920,6 +973,12 @@ print_rule(struct pf_rule *r, const char *anchor_call, int verbose, int numeric) printf("max-src-conn %u", r->max_src_conn); opts = 0; } + if (r->spare2) { + if (!opts) + printf(", "); + printf("max-packets %u", r->spare2); + opts = 0; + } if (r->max_src_conn_rate.limit) { if (!opts) printf(", "); @@ -999,6 +1058,14 @@ print_rule(struct pf_rule *r, const char *anchor_call, int verbose, int numeric) } if (r->label[0]) printf(" label \"%s\"", r->label); + if (r->dnpipe && r->pdnpipe) + printf(" %s(%d, %d)", + r->free_flags & PFRULE_DN_IS_PIPE ? "dnpipe" : "dnqueue", + r->dnpipe, r->pdnpipe); + else if (r->dnpipe) + printf(" %s %d", + r->free_flags & PFRULE_DN_IS_PIPE ? "dnpipe" : "dnqueue", + r->dnpipe); if (r->qname[0] && r->pqname[0]) printf(" queue(%s, %s)", r->qname, r->pqname); else if (r->qname[0]) @@ -1012,6 +1079,13 @@ print_rule(struct pf_rule *r, const char *anchor_call, int verbose, int numeric) } if (r->rtableid != -1) printf(" rtable %u", r->rtableid); + if (r->ieee8021q_pcp.op != 0) + print_ieee8021q_pcp(r->ieee8021q_pcp.op, + r->ieee8021q_pcp.pcp[0], r->ieee8021q_pcp.pcp[1]); + if (r->ieee8021q_pcp.setpcp & SETPCP_VALID) + print_ieee8021q_setpcp(r->ieee8021q_pcp.setpcp & + SETPCP_PCP_MASK); + if (r->divert.port) { #ifdef __FreeBSD__ printf(" divert-to %u", ntohs(r->divert.port)); diff --git a/sbin/pfctl/pfctl_parser.h b/sbin/pfctl/pfctl_parser.h index 5c909e9..2b7fea7 100644 --- a/sbin/pfctl/pfctl_parser.h +++ b/sbin/pfctl/pfctl_parser.h @@ -150,12 +150,28 @@ struct node_hfsc_opts { int flags; }; +struct node_fairq_sc { + struct node_queue_bw m1; /* slope of 1st segment; bps */ + u_int d; /* x-projection of m1; msec */ + struct node_queue_bw m2; /* slope of 2nd segment; bps */ + u_int8_t used; +}; + +struct node_fairq_opts { + struct node_fairq_sc linkshare; + struct node_queue_bw hogs_bw; + u_int nbuckets; + int flags; +}; + struct node_queue_opt { int qtype; union { struct cbq_opts cbq_opts; + struct codel_opts codel_opts; struct priq_opts priq_opts; struct node_hfsc_opts hfsc_opts; + struct node_fairq_opts fairq_opts; } data; }; diff --git a/sbin/pfctl/pfctl_qstats.c b/sbin/pfctl/pfctl_qstats.c index 95371e4..8ffd87d 100644 --- a/sbin/pfctl/pfctl_qstats.c +++ b/sbin/pfctl/pfctl_qstats.c @@ -36,8 +36,10 @@ __FBSDID("$FreeBSD$"); #include <altq/altq.h> #include <altq/altq_cbq.h> +#include <altq/altq_codel.h> #include <altq/altq_priq.h> #include <altq/altq_hfsc.h> +#include <altq/altq_fairq.h> #include "pfctl.h" #include "pfctl_parser.h" @@ -46,6 +48,8 @@ union class_stats { class_stats_t cbq_stats; struct priq_classstats priq_stats; struct hfsc_classstats hfsc_stats; + struct fairq_classstats fairq_stats; + struct codel_ifstats codel_stats; }; #define AVGN_MAX 8 @@ -75,8 +79,10 @@ struct pf_altq_node *pfctl_find_altq_node(struct pf_altq_node *, void pfctl_print_altq_node(int, const struct pf_altq_node *, unsigned, int); void print_cbqstats(struct queue_stats); +void print_codelstats(struct queue_stats); void print_priqstats(struct queue_stats); void print_hfscstats(struct queue_stats); +void print_fairqstats(struct queue_stats); void pfctl_free_altq_node(struct pf_altq_node *); void pfctl_print_altq_nodestat(int, const struct pf_altq_node *); @@ -162,7 +168,7 @@ pfctl_update_qstats(int dev, struct pf_altq_node **root) return (-1); } #ifdef __FreeBSD__ - if (pa.altq.qid > 0 && + if ((pa.altq.qid > 0 || pa.altq.scheduler == ALTQT_CODEL) && !(pa.altq.local_flags & PFALTQ_FLAG_IF_REMOVED)) { #else if (pa.altq.qid > 0) { @@ -300,7 +306,7 @@ pfctl_print_altq_node(int dev, const struct pf_altq_node *node, void pfctl_print_altq_nodestat(int dev, const struct pf_altq_node *a) { - if (a->altq.qid == 0) + if (a->altq.qid == 0 && a->altq.scheduler != ALTQT_CODEL) return; #ifdef __FreeBSD__ @@ -317,6 +323,12 @@ pfctl_print_altq_nodestat(int dev, const struct pf_altq_node *a) case ALTQT_HFSC: print_hfscstats(a->qstats); break; + case ALTQT_FAIRQ: + print_fairqstats(a->qstats); + break; + case ALTQT_CODEL: + print_codelstats(a->qstats); + break; } } @@ -342,6 +354,28 @@ print_cbqstats(struct queue_stats cur) } void +print_codelstats(struct queue_stats cur) +{ + printf(" [ pkts: %10llu bytes: %10llu " + "dropped pkts: %6llu bytes: %6llu ]\n", + (unsigned long long)cur.data.codel_stats.cl_xmitcnt.packets, + (unsigned long long)cur.data.codel_stats.cl_xmitcnt.bytes, + (unsigned long long)cur.data.codel_stats.cl_dropcnt.packets + + cur.data.codel_stats.stats.drop_cnt.packets, + (unsigned long long)cur.data.codel_stats.cl_dropcnt.bytes + + cur.data.codel_stats.stats.drop_cnt.bytes); + printf(" [ qlength: %3d/%3d ]\n", + cur.data.codel_stats.qlength, cur.data.codel_stats.qlimit); + + if (cur.avgn < 2) + return; + + printf(" [ measured: %7.1f packets/s, %s/s ]\n", + cur.avg_packets / STAT_INTERVAL, + rate2str((8 * cur.avg_bytes) / STAT_INTERVAL)); +} + +void print_priqstats(struct queue_stats cur) { printf(" [ pkts: %10llu bytes: %10llu " @@ -382,6 +416,26 @@ print_hfscstats(struct queue_stats cur) } void +print_fairqstats(struct queue_stats cur) +{ + printf(" [ pkts: %10llu bytes: %10llu " + "dropped pkts: %6llu bytes: %6llu ]\n", + (unsigned long long)cur.data.fairq_stats.xmit_cnt.packets, + (unsigned long long)cur.data.fairq_stats.xmit_cnt.bytes, + (unsigned long long)cur.data.fairq_stats.drop_cnt.packets, + (unsigned long long)cur.data.fairq_stats.drop_cnt.bytes); + printf(" [ qlength: %3d/%3d ]\n", + cur.data.fairq_stats.qlength, cur.data.fairq_stats.qlimit); + + if (cur.avgn < 2) + return; + + printf(" [ measured: %7.1f packets/s, %s/s ]\n", + cur.avg_packets / STAT_INTERVAL, + rate2str((8 * cur.avg_bytes) / STAT_INTERVAL)); +} + +void pfctl_free_altq_node(struct pf_altq_node *node) { while (node != NULL) { @@ -402,7 +456,7 @@ update_avg(struct pf_altq_node *a) u_int64_t b, p; int n; - if (a->altq.qid == 0) + if (a->altq.qid == 0 && a->altq.scheduler != ALTQT_CODEL) return; qs = &a->qstats; @@ -421,6 +475,14 @@ update_avg(struct pf_altq_node *a) b = qs->data.hfsc_stats.xmit_cnt.bytes; p = qs->data.hfsc_stats.xmit_cnt.packets; break; + case ALTQT_FAIRQ: + b = qs->data.fairq_stats.xmit_cnt.bytes; + p = qs->data.fairq_stats.xmit_cnt.packets; + break; + case ALTQT_CODEL: + b = qs->data.codel_stats.cl_xmitcnt.bytes; + p = qs->data.codel_stats.cl_xmitcnt.packets; + break; default: b = 0; p = 0; |