diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y index ba29321dd3d9..e0cfa3d0cad6 100644 --- a/sbin/pfctl/parse.y +++ b/sbin/pfctl/parse.y @@ -217,6 +217,8 @@ struct filter_opts { #define FOM_TOS 0x04 #define FOM_KEEP 0x08 #define FOM_SRCTRACK 0x10 +#define FOM_SETPRIO 0x0400 +#define FOM_PRIO 0x2000 struct node_uid *uid; struct node_gid *gid; struct { @@ -240,6 +242,8 @@ struct filter_opts { char *match_tag; u_int8_t match_tag_not; u_int rtableid; + u_int8_t prio; + u_int8_t set_prio[2]; struct { struct node_host *addr; u_int16_t port; @@ -453,7 +457,7 @@ int parseport(char *, struct range *r, int); %token BITMASK RANDOM SOURCEHASH ROUNDROBIN STATICPORT PROBABILITY %token ALTQ CBQ CODEL PRIQ HFSC FAIRQ BANDWIDTH TBRSIZE LINKSHARE REALTIME %token UPPERLIMIT QUEUE PRIORITY QLIMIT HOGS BUCKETS RTABLE TARGET INTERVAL -%token LOAD RULESET_OPTIMIZATION +%token LOAD RULESET_OPTIMIZATION PRIO %token STICKYADDRESS MAXSRCSTATES MAXSRCNODES SOURCETRACK GLOBAL RULE %token MAXSRCCONN MAXSRCCONNRATE OVERLOAD FLUSH SLOPPY %token TAGGED TAG IFBOUND FLOATING STATEPOLICY STATEDEFAULTS ROUTE SETTOS @@ -468,7 +472,7 @@ int parseport(char *, struct range *r, int); %type no dir af fragcache optimizer %type sourcetrack flush unaryop statelock %type action nataction natpasslog scrubaction -%type flags flag blockspec +%type flags flag blockspec prio %type portplain portstar portrange %type hashkey %type proto proto_list proto_item @@ -504,6 +508,7 @@ int parseport(char *, struct range *r, int); %type codelopts_list codelopts_item codel_opts %type bandwidth %type filter_opts filter_opt filter_opts_l +%type filter_sets filter_set filter_sets_l %type antispoof_opts antispoof_opt antispoof_opts_l %type queue_opts queue_opt queue_opts_l %type scrub_opts scrub_opt scrub_opts_l @@ -889,6 +894,17 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto YYERROR; } r.match_tag_not = $9.match_tag_not; + if ($9.marker & FOM_PRIO) { + if ($9.prio == 0) + r.prio = PF_PRIO_ZERO; + else + r.prio = $9.prio; + } + if ($9.marker & FOM_SETPRIO) { + r.set_prio[0] = $9.set_prio[0]; + r.set_prio[1] = $9.set_prio[1]; + r.scrub_flags |= PFSTATE_SETPRIO; + } decide_address_family($8.src.host, &r.af); decide_address_family($8.dst.host, &r.af); @@ -2014,6 +2030,18 @@ pfrule : action dir logquick interface route af proto fromto r.prob = $9.prob; r.rtableid = $9.rtableid; + if ($9.marker & FOM_PRIO) { + if ($9.prio == 0) + r.prio = PF_PRIO_ZERO; + else + r.prio = $9.prio; + } + if ($9.marker & FOM_SETPRIO) { + r.set_prio[0] = $9.set_prio[0]; + r.set_prio[1] = $9.set_prio[1]; + r.scrub_flags |= PFSTATE_SETPRIO; + } + r.af = $6; if ($9.tag) if (strlcpy(r.tagname, $9.tag, @@ -2434,6 +2462,18 @@ filter_opt : USER uids { filter_opts.marker |= FOM_ICMP; filter_opts.icmpspec = $1; } + | PRIO NUMBER { + if (filter_opts.marker & FOM_PRIO) { + yyerror("prio cannot be redefined"); + YYERROR; + } + if ($2 < 0 || $2 > PF_PRIO_MAX) { + yyerror("prio must be 0 - %u", PF_PRIO_MAX); + YYERROR; + } + filter_opts.marker |= FOM_PRIO; + filter_opts.prio = $2; + } | TOS tos { if (filter_opts.marker & FOM_TOS) { yyerror("tos cannot be redefined"); @@ -2532,6 +2572,42 @@ filter_opt : USER uids { filter_opts.divert.port = 1; /* some random value */ #endif } + | filter_sets + ; + +filter_sets : SET '(' filter_sets_l ')' { $$ = filter_opts; } + | SET filter_set { $$ = filter_opts; } + ; + +filter_sets_l : filter_sets_l comma filter_set + | filter_set + ; + +filter_set : prio { + if (filter_opts.marker & FOM_SETPRIO) { + yyerror("prio cannot be redefined"); + YYERROR; + } + filter_opts.marker |= FOM_SETPRIO; + filter_opts.set_prio[0] = $1.b1; + filter_opts.set_prio[1] = $1.b2; + } +prio : PRIO NUMBER { + if ($2 < 0 || $2 > PF_PRIO_MAX) { + yyerror("prio must be 0 - %u", PF_PRIO_MAX); + YYERROR; + } + $$.b1 = $$.b2 = $2; + } + | PRIO '(' NUMBER comma NUMBER ')' { + if ($3 < 0 || $3 > PF_PRIO_MAX || + $5 < 0 || $5 > PF_PRIO_MAX) { + yyerror("prio must be 0 - %u", PF_PRIO_MAX); + YYERROR; + } + $$.b1 = $3; + $$.b2 = $5; + } ; probability : STRING { @@ -5426,6 +5502,7 @@ lookup(char *s) { "overload", OVERLOAD}, { "pass", PASS}, { "port", PORT}, + { "prio", PRIO}, { "priority", PRIORITY}, { "priq", PRIQ}, { "probability", PROBABILITY}, diff --git a/sbin/pfctl/pfctl_parser.c b/sbin/pfctl/pfctl_parser.c index 22db5cabac38..4bb1477efe67 100644 --- a/sbin/pfctl/pfctl_parser.c +++ b/sbin/pfctl/pfctl_parser.c @@ -841,6 +841,21 @@ print_rule(struct pf_rule *r, const char *anchor_call, int verbose, int numeric) } if (r->tos) printf(" tos 0x%2.2x", r->tos); + if (r->prio) + printf(" prio %u", r->prio == PF_PRIO_ZERO ? 0 : r->prio); + if (r->scrub_flags & PFSTATE_SETMASK) { + char *comma = ""; + printf(" set ("); + if (r->scrub_flags & PFSTATE_SETPRIO) { + if (r->set_prio[0] == r->set_prio[1]) + printf("%s prio %u", comma, r->set_prio[0]); + else + printf("%s prio(%u, %u)", comma, r->set_prio[0], + r->set_prio[1]); + comma = ","; + } + printf(" )"); + } if (!r->keep_state && r->action == PF_PASS && !anchor_call[0]) printf(" no state"); else if (r->keep_state == PF_STATE_NORMAL) diff --git a/share/man/man5/pf.conf.5 b/share/man/man5/pf.conf.5 index 2d74a8d3f7be..3ff63bb14c54 100644 --- a/share/man/man5/pf.conf.5 +++ b/share/man/man5/pf.conf.5 @@ -28,7 +28,7 @@ .\" ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" -.Dd July 25, 2015 +.Dd June 9, 2016 .Dt PF.CONF 5 .Os .Sh NAME @@ -1785,6 +1785,25 @@ For example: pass in proto tcp to port 25 queue mail pass in proto tcp to port 22 queue(ssh_bulk, ssh_prio) .Ed +.Pp +.It Cm set prio Ar priority | Pq Ar priority , priority +Packets matching this rule will be assigned a specific queueing priority. +Priorities are assigned as integers 0 through 7. +If the packet is transmitted on a +.Xr vlan 4 +interface, the queueing priority will be written as the priority +code point in the 802.1Q VLAN header. +If two priorities are given, packets which have a TOS of +.Cm lowdelay +and TCP ACKs with no data payload will be assigned to the second one. +.Pp +For example: +.Bd -literal -offset indent +pass in proto tcp to port 25 set prio 2 +pass in proto tcp to port 22 set prio (2, 5) +.Ed +.Pp + .It Ar tag Aq Ar string Packets matching this rule will be tagged with the specified string. @@ -1845,6 +1864,9 @@ For example, the following rule will drop 20% of incoming ICMP packets: .Bd -literal -offset indent block in proto icmp probability 20% .Ed +.It Ar prio Aq Ar number +Only match packets which have the given queueing priority assigned. +.Pp .El .Sh ROUTING If a packet matches a rule with a route option set, the packet filter will @@ -2831,8 +2853,9 @@ filteropt = user | group | flags | icmp-type | icmp6-type | "tos" tos | "max-mss" number | "random-id" | "reassemble tcp" | fragmentation | "allow-opts" | "label" string | "tag" string | [ ! ] "tagged" string | + "set prio" ( number | "(" number [ [ "," ] number ] ")" ) | "queue" ( string | "(" string [ [ "," ] string ] ")" ) | - "rtable" number | "probability" number"%" + "rtable" number | "probability" number"%" | "prio" number nat-rule = [ "no" ] "nat" [ "pass" [ "log" [ "(" logopts ")" ] ] ] [ "on" ifspec ] [ af ] diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h index ed23eb5b20a7..c2fd281d509c 100644 --- a/sys/net/pfvar.h +++ b/sys/net/pfvar.h @@ -540,7 +540,7 @@ struct pf_rule { u_int16_t max_mss; u_int16_t tag; u_int16_t match_tag; - u_int16_t spare2; /* netgraph */ + u_int16_t scrub_flags; struct pf_rule_uid uid; struct pf_rule_gid gid; @@ -577,6 +577,10 @@ struct pf_rule { #define PF_FLUSH 0x01 #define PF_FLUSH_GLOBAL 0x02 u_int8_t flush; +#define PF_PRIO_ZERO 0xff /* match "prio 0" packets */ +#define PF_PRIO_MAX 7 + u_int8_t prio; + u_int8_t set_prio[2]; struct { struct pf_addr addr; @@ -739,6 +743,8 @@ struct pf_state { /* was PFSTATE_PFLOW 0x04 */ #define PFSTATE_NOSYNC 0x08 #define PFSTATE_ACK 0x10 +#define PFSTATE_SETPRIO 0x0200 +#define PFSTATE_SETMASK (PFSTATE_SETPRIO) u_int8_t timeout; u_int8_t sync_state; /* PFSYNC_S_x */ diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c index a35f9e4f5f2d..02fc7f0f8fc6 100644 --- a/sys/netpfil/pf/pf.c +++ b/sys/netpfil/pf/pf.c @@ -63,6 +63,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -2445,6 +2446,45 @@ pf_send_tcp(struct mbuf *replyto, const struct pf_rule *r, sa_family_t af, pf_send(pfse); } +static int +pf_ieee8021q_setpcp(struct mbuf *m, u_int8_t prio) +{ + struct m_tag *mtag; + + KASSERT(prio <= PF_PRIO_MAX, + ("%s with invalid pcp", __func__)); + + mtag = m_tag_locate(m, MTAG_8021Q, MTAG_8021Q_PCP_OUT, NULL); + if (mtag == NULL) { + mtag = m_tag_alloc(MTAG_8021Q, MTAG_8021Q_PCP_OUT, + sizeof(uint8_t), M_NOWAIT); + if (mtag == NULL) + return (ENOMEM); + m_tag_prepend(m, mtag); + } + + *(uint8_t *)(mtag + 1) = prio; + return (0); +} + +static int +pf_match_ieee8021q_pcp(u_int8_t prio, struct mbuf *m) +{ + struct m_tag *mtag; + u_int8_t mpcp; + + mtag = m_tag_locate(m, MTAG_8021Q, MTAG_8021Q_PCP_IN, NULL); + if (mtag == NULL) + return (0); + + if (prio == PF_PRIO_ZERO) + prio = 0; + + mpcp = *(uint8_t *)(mtag + 1); + + return (mpcp == prio); +} + static void pf_send_icmp(struct mbuf *m, u_int8_t type, u_int8_t code, sa_family_t af, struct pf_rule *r) @@ -3317,6 +3357,9 @@ pf_test_rule(struct pf_rule **rm, struct pf_state **sm, int direction, !pf_match_gid(r->gid.op, r->gid.gid[0], r->gid.gid[1], pd->lookup.gid)) r = TAILQ_NEXT(r, entries); + else if (r->prio && + !pf_match_ieee8021q_pcp(r->prio, m)) + r = TAILQ_NEXT(r, entries); else if (r->prob && r->prob <= arc4random()) r = TAILQ_NEXT(r, entries); @@ -3779,6 +3822,9 @@ pf_test_fragment(struct pf_rule **rm, int direction, struct pfi_kif *kif, pd->proto == IPPROTO_ICMPV6) && (r->type || r->code)) r = TAILQ_NEXT(r, entries); + else if (r->prio && + !pf_match_ieee8021q_pcp(r->prio, m)) + r = TAILQ_NEXT(r, entries); else if (r->prob && r->prob <= (arc4random() % (UINT_MAX - 1) + 1)) r = TAILQ_NEXT(r, entries); @@ -6003,6 +6049,18 @@ pf_test(int dir, struct ifnet *ifp, struct mbuf **m0, struct inpcb *inp) if (r->rtableid >= 0) M_SETFIB(m, r->rtableid); + if (r->scrub_flags & PFSTATE_SETPRIO) { + if (pd.tos & IPTOS_LOWDELAY) + pqid = 1; + if (pf_ieee8021q_setpcp(m, r->set_prio[pqid])) { + action = PF_DROP; + REASON_SET(&reason, PFRES_MEMORY); + log = 1; + DPFPRINTF(PF_DEBUG_MISC, + ("pf: failed to allocate 802.1q mtag\n")); + } + } + #ifdef ALTQ if (action == PF_PASS && r->qid) { if (pd.pf_mtag == NULL && @@ -6176,7 +6234,7 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0, struct inpcb *inp) struct pf_state *s = NULL; struct pf_ruleset *ruleset = NULL; struct pf_pdesc pd; - int off, terminal = 0, dirndx, rh_cnt = 0; + int off, terminal = 0, dirndx, rh_cnt = 0, pqid = 0; int fwdir = dir; M_ASSERTPKTHDR(m); @@ -6449,6 +6507,18 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0, struct inpcb *inp) if (r->rtableid >= 0) M_SETFIB(m, r->rtableid); + if (r->scrub_flags & PFSTATE_SETPRIO) { + if (pd.tos & IPTOS_LOWDELAY) + pqid = 1; + if (pf_ieee8021q_setpcp(m, r->set_prio[pqid])) { + action = PF_DROP; + REASON_SET(&reason, PFRES_MEMORY); + log = 1; + DPFPRINTF(PF_DEBUG_MISC, + ("pf: failed to allocate 802.1q mtag\n")); + } + } + #ifdef ALTQ if (action == PF_PASS && r->qid) { if (pd.pf_mtag == NULL && diff --git a/sys/netpfil/pf/pf_ioctl.c b/sys/netpfil/pf/pf_ioctl.c index 83eabcda26d2..3291c9b7cc0b 100644 --- a/sys/netpfil/pf/pf_ioctl.c +++ b/sys/netpfil/pf/pf_ioctl.c @@ -1242,6 +1242,10 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td error = ENOMEM; if (pf_anchor_setup(rule, ruleset, pr->anchor_call)) error = EINVAL; + if (rule->scrub_flags & PFSTATE_SETPRIO && + (rule->set_prio[0] > PF_PRIO_MAX || + rule->set_prio[1] > PF_PRIO_MAX)) + error = EINVAL; TAILQ_FOREACH(pa, &V_pf_pabuf, entries) if (pa->addr.type == PF_ADDR_TABLE) { pa->addr.p.tbl = pfr_attach_table(ruleset,