diff --git a/usr.sbin/npf/npfctl/npf_parse.y b/usr.sbin/npf/npfctl/npf_parse.y index d77f462cd8c58..c5c8c9c633241 100644 --- a/usr.sbin/npf/npfctl/npf_parse.y +++ b/usr.sbin/npf/npfctl/npf_parse.y @@ -3,7 +3,8 @@ * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation - * by Martin Husemann, Christos Zoulas and Mindaugas Rasiukevicius. + * by Martin Husemann, Christos Zoulas, Mindaugas Rasiukevicius and + * Emmanuel Nyarko * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -38,6 +39,11 @@ #include #endif +#include +#include +#include +#include + #include "npfctl.h" #define YYSTACKSIZE 4096 @@ -48,6 +54,9 @@ const char * yyfilename; extern int yylineno, yycolumn; extern int yylex(int); +struct node_hfsc_opts hfsc_opts; +struct queue_opts queue_opts; + void yyerror(const char *fmt, ...) { @@ -167,7 +176,21 @@ yyerror(const char *fmt, ...) %token TCP %token TO %token TREE +%token ALTQ +%token CBQ +%token PRIQ +%token HFSC +%token BANDWIDTH +%token TBRSIZE +%token LINKSHARE +%token REALTIME +%token UPPERLIMIT +%token QUEUE +%token PRIORITY +%token QLIMIT +%token RTABLE %token TYPE + %token ICMP %token ICMP6 @@ -181,8 +204,9 @@ yyerror(const char *fmt, ...) %token PARAM %token TABLE_ID %token VAR_ID +%token BW_SPEC -%type addr some_name table_store dynamic_ifaddrs +%type addr some_name table_store dynamic_ifaddrs bw_spec %type proc_param_val opt_apply ifname on_ifname ifref %type port opt_final number afamily opt_family %type block_or_pass rule_dir group_dir block_opts @@ -199,6 +223,15 @@ yyerror(const char *fmt, ...) %type filt_opts all_or_filt_opts %type rawproto %type group_opts +%type queue_opts queue_opt queue_opts_l +%type rule_queue +%type qassign qassign_list qassign_item +%type scheduler +%type cbqflags_list cbqflags_item +%type priqflags_list priqflags_item +%type hfscopts_list hfscopts_item hfsc_opts +%type bandwidth +%type queue_flags %union { char * str; @@ -209,6 +242,12 @@ yyerror(const char *fmt, ...) filt_opts_t filtopts; opt_proto_t optproto; rule_group_t rulegroup; + struct node_queue *queue; + struct node_queue_opt queue_options; + struct node_queue_bw queue_bwspec; + struct node_qassign qassign; + struct queue_opts queue_opts; + struct node_hfsc_opts hfsc_opts; } %% @@ -232,9 +271,346 @@ line | rproc | alg | set + | altq + | queuespec | ; +altq : ALTQ on_ifname queue_opts QUEUE qassign { + struct npf_altq a; + + if ($3.scheduler.qtype == ALTQT_NONE) { + yyerror("no scheduler specified!"); + YYERROR; + } + memset(&a, 0, sizeof(a)); + a.scheduler = $3.scheduler.qtype; + a.qlimit = $3.qlimit; + a.tbrsize = $3.tbrsize; + if ($5 == NULL) { + yyerror("no child queues specified"); + YYERROR; + } + if (expand_altq(&a, $2, $5, $3.queue_bwspec, + &$3.scheduler)) + YYERROR; + } + ; + +queuespec : QUEUE IDENTIFIER on_ifname queue_opts qassign { + struct npf_altq a; + + memset(&a, 0, sizeof(a)); + if (strlcpy(a.qname, $2, sizeof(a.qname)) >= + sizeof(a.qname)) { + yyerror("queue name too long (max " + "%d chars)", NPF_QNAME_SIZE-1); + free($2); + YYERROR; + } + free($2); + if ($4.tbrsize) { + yyerror("cannot specify tbrsize for queue"); + YYERROR; + } + if ($4.priority > 255) { + yyerror("priority out of range: max 255"); + YYERROR; + } + a.priority = $4.priority; + a.qlimit = $4.qlimit; + a.scheduler = $4.scheduler.qtype; + if (expand_queue(&a, $3, $5, $4.queue_bwspec, + &$4.scheduler)) { + yyerror("errors in queue definition"); + YYERROR; + } + } + ; + +queue_opts : { + memset(&queue_opts, 0, sizeof(queue_opts)); + queue_opts.priority = DEFAULT_PRIORITY; + queue_opts.qlimit = DEFAULT_QLIMIT; + queue_opts.scheduler.qtype = ALTQT_NONE; + queue_opts.queue_bwspec.bw_percent = 100; + } + queue_opts_l + { $$ = queue_opts; } + | /* empty */ { + memset(&queue_opts, 0, sizeof(queue_opts)); + queue_opts.priority = DEFAULT_PRIORITY; + queue_opts.qlimit = DEFAULT_QLIMIT; + queue_opts.scheduler.qtype = ALTQT_NONE; + queue_opts.queue_bwspec.bw_percent = 100; + $$ = queue_opts; + } + ; + +queue_opts_l : queue_opts_l queue_opt + | queue_opt + ; + +queue_opt : BANDWIDTH bandwidth { + if (queue_opts.marker & QOM_BWSPEC) { + yyerror("bandwidth cannot be respecified"); + YYERROR; + } + queue_opts.marker |= QOM_BWSPEC; + queue_opts.queue_bwspec = $2; + } + | PRIORITY number { + if (queue_opts.marker & QOM_PRIORITY) { + yyerror("priority cannot be respecified"); + YYERROR; + } + if ($2 > 255) { + yyerror("priority out of range: max 255"); + YYERROR; + } + queue_opts.marker |= QOM_PRIORITY; + queue_opts.priority = $2; + } + | QLIMIT number { + if (queue_opts.marker & QOM_QLIMIT) { + yyerror("qlimit cannot be respecified"); + YYERROR; + } + if ($2 > 65535) { + yyerror("qlimit out of range: max 65535"); + YYERROR; + } + queue_opts.marker |= QOM_QLIMIT; + queue_opts.qlimit = $2; + } + | scheduler { + if (queue_opts.marker & QOM_SCHEDULER) { + yyerror("scheduler cannot be respecified"); + YYERROR; + } + queue_opts.marker |= QOM_SCHEDULER; + queue_opts.scheduler = $1; + } + | TBRSIZE number { + if (queue_opts.marker & QOM_TBRSIZE) { + yyerror("tbrsize cannot be respecified"); + YYERROR; + } + if ($2 > 65535) { + yyerror("tbrsize too big: max 65535"); + YYERROR; + } + queue_opts.marker |= QOM_TBRSIZE; + queue_opts.tbrsize = $2; + } + ; + +bandwidth : bw_spec { + struct node_queue_bw bw; + + if (npfctl_eval_bw(&bw, $1)) { + YYERROR; + free($1); + } + $$ = bw; + } + ; + +bw_spec : BW_SPEC {$$ = $1; } + ; + +scheduler : CBQ { + $$.qtype = ALTQT_CBQ; + $$.data.cbq_opts.flags = 0; + } + | CBQ PAR_OPEN cbqflags_list PAR_CLOSE { + $$.qtype = ALTQT_CBQ; + $$.data.cbq_opts.flags = $3; + } + | PRIQ { + $$.qtype = ALTQT_PRIQ; + $$.data.priq_opts.flags = 0; + } + | PRIQ PAR_OPEN priqflags_list PAR_CLOSE { + $$.qtype = ALTQT_PRIQ; + $$.data.priq_opts.flags = $3; + } + | HFSC { + $$.qtype = ALTQT_HFSC; + bzero(&$$.data.hfsc_opts, + sizeof(struct node_hfsc_opts)); + } + | HFSC PAR_OPEN hfsc_opts PAR_CLOSE { + $$.qtype = ALTQT_HFSC; + $$.data.hfsc_opts = $3; + } + ; + +cbqflags_list : cbqflags_item { $$ |= $1; } + | cbqflags_list COMMA cbqflags_item { $$ |= $3; } + ; + +cbqflags_item : IDENTIFIER { +#ifdef CBQCLF_BORROW + if (!strcmp($1, "borrow")) + $$ = CBQCLF_BORROW; +#endif + else if (!strcmp($1, "red")) + $$ = CBQCLF_RED; + else if (!strcmp($1, "ecn")) + $$ = CBQCLF_RED|CBQCLF_ECN; + else if (!strcmp($1, "rio")) + $$ = CBQCLF_RIO; + else { + yyerror("unknown cbq flag \"%s\"", $1); + free($1); + YYERROR; + } + free($1); + } + | DEFAULT { $$ = CBQCLF_DEFCLASS; } + ; + +priqflags_list : priqflags_item { $$ |= $1; } + | priqflags_list COMMA priqflags_item { $$ |= $3; } + ; + +priqflags_item : IDENTIFIER { + if (!strcmp($1, "red")) + $$ = PRCF_RED; + else if (!strcmp($1, "ecn")) + $$ = PRCF_RED|PRCF_ECN; + else if (!strcmp($1, "rio")) + $$ = PRCF_RIO; + else { + yyerror("unknown priq flag \"%s\"", $1); + free($1); + YYERROR; + } + free($1); + } + | DEFAULT { $$ = PRCF_DEFAULTCLASS; } + ; + +hfsc_opts : { + memset(&hfsc_opts, 0, + sizeof(struct node_hfsc_opts)); + } + hfsc_opts_list { + $$ = hfsc_opts; + } + ; + +hfscopts_list : hfscopts_item + | hfscopts_list COMMA hfscopts_item + ; + +hfscopts_item : LINKSHARE bandwidth { + if (hfsc_opts.linkshare.used) { + yyerror("linkshare already specified"); + YYERROR; + } + hfsc_opts.linkshare.m2 = $2; + hfsc_opts.linkshare.used = 1; + } + | LINKSHARE PAR_OPEN bandwidth COMMA number COMMA bandwidth PAR_CLOSE + { + if (hfsc_opts.linkshare.used) { + yyerror("linkshare already specified"); + YYERROR; + } + hfsc_opts.linkshare.m1 = $3; + hfsc_opts.linkshare.d = $5; + hfsc_opts.linkshare.m2 = $7; + hfsc_opts.linkshare.used = 1; + } + | REALTIME bandwidth { + if (hfsc_opts.realtime.used) { + yyerror("realtime already specified"); + YYERROR; + } + hfsc_opts.realtime.m2 = $2; + hfsc_opts.realtime.used = 1; + } + | REALTIME PAR_OPEN bandwidth COMMA number COMMA bandwidth PAR_CLOSE + { + if (hfsc_opts.realtime.used) { + yyerror("realtime already specified"); + YYERROR; + } + hfsc_opts.realtime.m1 = $3; + hfsc_opts.realtime.d = $5; + hfsc_opts.realtime.m2 = $7; + hfsc_opts.realtime.used = 1; + } + | UPPERLIMIT bandwidth { + if (hfsc_opts.upperlimit.used) { + yyerror("upperlimit already specified"); + YYERROR; + } + hfsc_opts.upperlimit.m2 = $2; + hfsc_opts.upperlimit.used = 1; + } + | UPPERLIMIT PAR_OPEN bandwidth COMMA number COMMA bandwidth PAR_CLOSE + { + if (hfsc_opts.upperlimit.used) { + yyerror("upperlimit already specified"); + YYERROR; + } + hfsc_opts.upperlimit.m1 = $3; + hfsc_opts.upperlimit.d = $5; + hfsc_opts.upperlimit.m2 = $7; + hfsc_opts.upperlimit.used = 1; + } + | IDENTIFIER { + if (!strcmp($1, "red")) + hfsc_opts.flags |= HFCF_RED; + else if (!strcmp($1, "ecn")) + hfsc_opts.flags |= HFCF_RED|HFCF_ECN; + else if (!strcmp($1, "rio")) + hfsc_opts.flags |= HFCF_RIO; + else { + yyerror("unknown hfsc flag \"%s\"", $1); + free($1); + YYERROR; + } + free($1); + } + | DEFAULT { hfsc_opts.flags |= HFCF_DEFAULTCLASS; } + ; + +qassign : /* empty */ { $$ = NULL; } + | qassign_item { $$ = $1; } + | CURLY_OPEN qassign_list CURLY_CLOSE { $$ = $2; } + ; + +qassign_list : qassign_item { $$ = $1; } + | qassign_list COMMA qassign_item { + $1->tail->next = $3; + $1->tail = $3; + $$ = $1; + } + ; + +qassign_item : IDENTIFIER { + $$ = calloc(1, sizeof(struct node_queue)); + + if ($$ == NULL) + err(1, "qassign_item: calloc"); + if (strlcpy($$->queue, $1, sizeof($$->queue)) >= + sizeof($$->queue)) { + yyerror("queue name '%s' too long (max " + "%lu chars)", $1, sizeof($$->queue) -1); + free($1); + free($$); + YYERROR; + } + free($1); + $$->next = NULL; + $$->tail = $$; + } + ; + alg : ALG STRING { @@ -544,20 +920,21 @@ rule_group /* * Rule and misc. + * Make rule with queue optional */ rule : block_or_pass opt_stateful rule_dir opt_final on_ifname - opt_family opt_proto all_or_filt_opts opt_apply + opt_family opt_proto all_or_filt_opts opt_apply rule_queue { npfctl_build_rule($1 | $2 | $3 | $4, $5, - $6, $7, &$8, NULL, $9); + $6, $7, &$8, NULL, $9, $10); } | block_or_pass opt_stateful rule_dir opt_final on_ifname - PCAP_FILTER STRING opt_apply + PCAP_FILTER STRING opt_apply rule_queue { npfctl_build_rule($1 | $2 | $3 | $4, $5, - AF_UNSPEC, NULL, NULL, $7, $8); + AF_UNSPEC, NULL, NULL, $7, $8, $9); } ; @@ -678,6 +1055,27 @@ opt_apply | { $$ = NULL; } ; +rule_queue + : /* Empty */ + { + $$.qname = NULL; + $$.pqname = NULL; + } + | QUEUE STRING + { + $$.qname = $2; + } + | QUEUE PAR_OPEN STRING PAR_CLOSE + { + $$.qname = $3; + } + | QUEUE PAR_OPEN STRING COMMA STRING PAR_CLOSE + { + $$.qname = $3; + $$.pqname = $5; + } + ; + block_opts : RETURNRST { $$ = NPF_RULE_RETRST; } | RETURNICMP { $$ = NPF_RULE_RETICMP; } diff --git a/usr.sbin/npf/npfctl/npf_scan.l b/usr.sbin/npf/npfctl/npf_scan.l index 28bc5e9665742..7c272aea75c2c 100644 --- a/usr.sbin/npf/npfctl/npf_scan.l +++ b/usr.sbin/npf/npfctl/npf_scan.l @@ -3,7 +3,7 @@ * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation - * by Martin Husemann. + * by Martin Husemann and Emmanuel Nyarko * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -96,6 +96,7 @@ DID [a-zA-Z_][a-zA-Z_0-9-]* SPID [a-zA-Z][a-zA-Z_0-9.]* NUMBER [0-9]+ HEXDIG [0-9a-fA-F]+ +BW_UNIT [A-Za-z%]+ %% %{ @@ -174,6 +175,19 @@ icmp-type return ICMPTYPE; code return CODE; any return ANY; +altq return ALTQ; +cbq return CBQ; +priq return PRIQ; +hfsc return HFSC; +bandwidth return BANDWIDTH; +tbrsize return TBRSIZE; +linkshare return LINKSHARE; +realtime return REALTIME; +uppertime return UPPERLIMIT; +queue return QUEUE; +priority return PRIORITY; +qlimit return QLIMIT; + "/" return SLASH; "{" return CURLY_OPEN; "}" return CURLY_CLOSE; @@ -183,6 +197,11 @@ any return ANY; "=" return EQ; "!" return EXCL_MARK; +{NUMBER}[\.]*[0-9]*{BW_UNIT} { + yylval.str = estrndup(yytext, yyleng); + return BW_SPEC; +} + "0x"{HEXDIG} { char *endp, *buf = ecalloc(1, yyleng + 1); buf[yyleng] = 0; diff --git a/usr.sbin/npf/npfctl/npfctl.h b/usr.sbin/npf/npfctl/npfctl.h index 4a17517488079..a99d3bd59328a 100644 --- a/usr.sbin/npf/npfctl/npfctl.h +++ b/usr.sbin/npf/npfctl/npfctl.h @@ -48,6 +48,78 @@ #define NPF_CONF_PATH "/etc/npf.conf" #define NPF_DB_PATH "/var/db/npf.db" +#ifndef DEFAULT_QLIMIT +#define DEFAULT_QLIMIT 50 +#endif +#ifndef DEFAULT_PRIORITY +#define DEFAULT_PRIORITY 1 +#endif + +struct node_queue_bw { + uint32_t bw_absolute; + uint16_t bw_percent; +}; + +struct node_hfsc_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 */ + uint8_t used; +}; + +struct node_hfsc_opts { + struct node_hfsc_sc realtime; + struct node_hfsc_sc linkshare; + struct node_hfsc_sc upperlimit; + int flags; +}; + +struct node_queue_opt { + int qtype; + union { + struct npf_cbq_opts cbq_opts; + struct npf_priq_opts priq_opts; + struct node_hfsc_opts hfsc_opts; + } data; +}; + +struct queue_opts { + int marker; +/* use flags for which option is set*/ +#define QOM_BWSPEC 0x01 +#define QOM_SCHEDULER 0x02 +#define QOM_PRIORITY 0x04 +#define QOM_TBRSIZE 0x08 +#define QOM_QLIMIT 0x10 + struct node_queue_bw queue_bwspec; + struct node_queue_opt scheduler; + int priority; + int tbrsize; + int qlimit; +}; + +struct node_queue { + char queue[NPF_QNAME_SIZE]; + char parent[NPF_QNAME_SIZE]; + char ifname[IFNAMSIZ]; + int scheduler; + struct node_queue *next; + struct node_queue *tail; +}; + +struct node_qassign { + char *qname; + char *pqname; +}; + +/* + * generalized service curve used for admission control + */ +struct segment { + LIST_ENTRY(segment) _next; + double x, y, d, m; +}; + typedef struct fam_addr_mask { sa_family_t fam_family; npf_addr_t fam_addr;