Hi I've been asked, by net admin, to implement pf.conf simplification for divert-to rule. Reason is that divert-to is written to support only one port per line and because of that there are situations where admins must write lot of lines only because different ports. After looking at pfctl/parse.y I've found that patch (for 5.3) would be trivial and wouldn't break anything, ie. works for one port and port range at the same time.
Please let me know if there is interest for this and ofc if something needs to be fixed. Here is an example. Now: pass in quick inet proto tcp from 192.168.1.0/24 to any port 21 divert-to 127.0.0.1 port 42240 modulate state probability 20% pass in quick inet proto tcp from 192.168.1.0/24 to any port 21 divert-to 127.0.0.1 port 42241 modulate state probability 20% pass in quick inet proto tcp from 192.168.1.0/24 to any port 21 divert-to 127.0.0.1 port 42242 modulate state probability 20% pass in quick inet proto tcp from 192.168.1.0/24 to any port 21 divert-to 127.0.0.1 port 42243 modulate state probability 20% pass in quick inet proto tcp from 192.168.1.0/24 to any port 21 divert-to 127.0.0.1 port 42244 modulate state After patching: pass in quick inet proto tcp from 192.168.1.0/24 to any port 21 divert-to 127.0.0.1 port 42240:42243 modulate state probability 20% pass in quick inet proto tcp from 192.168.1.0/24 to any port 21 divert-to 127.0.0.1 port 42244 modulate state Patch: Index: parse.y =================================================================== RCS file: /cvs/src/sbin/pfctl/parse.y,v retrieving revision 1.621 diff -u -r1.621 parse.y --- parse.y 16 Jan 2013 01:49:20 -0000 1.621 +++ parse.y 17 Sep 2013 15:45:20 -0000 @@ -261,7 +261,7 @@ u_int8_t set_prio[2]; struct { struct node_host *addr; - u_int16_t port; + u_int16_t port, port_top; } divert, divert_packet; struct redirspec nat; struct redirspec rdr; @@ -475,7 +475,7 @@ %type <v.i> sourcetrack flush unaryop statelock %type <v.b> action %type <v.b> flags flag blockspec prio -%type <v.range> portplain portstar portrange +%type <v.range> portstar portrange %type <v.hashkey> hashkey %type <v.proto> proto proto_list proto_item %type <v.number> protoval @@ -2078,6 +2078,28 @@ r.divert.addr = $8.divert.addr->addr.v.a.addr; } + if ($8.divert.port_top && + $8.divert.port_top < r.divert.port) { + yyerror("invalid divert port range: " + "%u:%u", ntohs(r.divert.port), + ntohs($8.divert.port_top)); + YYERROR; + } + +#define NHS_LT(x, y) (ntohs(x) < ntohs(y)) +#define NHS_INC(x) x = htons(ntohs(x) + 1) + while(NHS_LT(r.divert.port, + $8.divert.port_top)) { + expand_rule(&r, 1, $4, &$8.nat, &$8.rdr, + &$8.rroute, $6, $7.src_os, + $7.src.host, $7.src.port, + $7.dst.host, $7.dst.port, + $8.uid, $8.gid, $8.rcv, + $8.icmpspec, ""); + NHS_INC(r.divert.port); + } +#undef NHS_INC +#undef NHS_LT } r.divert_packet.port = $8.divert_packet.port; @@ -2197,7 +2219,7 @@ } filter_opts.rtableid = $2; } - | DIVERTTO STRING PORT portplain { + | DIVERTTO STRING PORT portrange { if ((filter_opts.divert.addr = host($2)) == NULL) { yyerror("could not parse divert address: %s", $2); @@ -2210,6 +2232,7 @@ yyerror("invalid divert port: %u", ntohs($4.a)); YYERROR; } + filter_opts.divert.port_top = $4.b; } | DIVERTREPLY { filter_opts.divert.port = 1; /* some random value */ @@ -3073,15 +3096,6 @@ $$->op = $2; $$->next = NULL; $$->tail = $$; - } - ; - -portplain : numberstring { - if (parseport($1, &$$, 0) == -1) { - free($1); - YYERROR; - } - free($1); } ;