From nobody Fri Mar 28 16:08:53 2025 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4ZPQSQ3fjWz5s3lZ; Fri, 28 Mar 2025 16:08:54 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R10" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4ZPQSP4SKRz3Qky; Fri, 28 Mar 2025 16:08:53 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1743178133; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=4BxhfncOoRZkfWB1XUiSNOAMU3uHGVpIu1UK6gH7QQs=; b=jpe84+Bh+2SCU37/V4Z4aSSLyPPiT9+E0DoZgy1uHKtlzNII6mJVDVHpf+nv68vJXSloYc mUkKagcLZvAJOJJhht+6oNecQK5Ms/4oC/96c2E57vBGl+eetdEzzkoTDCAL+h30Oz5SKy KI4NS9JNZqERt60dAUQ1DPo6Xg4XgSHz+j1cgC6rnvh+F87RwVHXfS7rxSHE+0ZfOQpBnB trvwaCRl7/cYJ7NNSE9M2iqv+TzsnppDhuCf1sttawkOLlTTSvLsmycJ22CQsOo0tJGGIC xSY7dSexuuRrC6RzhsMKECTWVwbc+3n1qWxeFeWfR+tRrM2L/O75SSA2/Nnqzw== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1743178133; a=rsa-sha256; cv=none; b=SXtTINlVtYmtb55RHuxp718RiHONMCqnoYwIZfiJAOdxG2aXyNDH6EbmRL6pXUZH98ZMyM yzsQh1pWc/TuM5rLI9ixyfXhUhd+bn2zecUV0fhQr3m0RDWZbAZ+WKSBYBHg1O7ys5z/lt UlNysMlBfRBZSStzedMZZZLj0uN3aFGymiGe/GrLtGfwW4IEK8fSPrCN2gQcW6Etyq4cDH ZaRAqDu0inlTQQAHrmnKL0LaKsJpz+9qeI+e3ykkNCdvrqrWeS/Rq9TXeJjQGgShoSHo61 0WXk9g/YVq6I9udI5XxjyaOeYg2QT9+KnrC5BkQnMeCS8RlQ8/VD26r4qaSNiw== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1743178133; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=4BxhfncOoRZkfWB1XUiSNOAMU3uHGVpIu1UK6gH7QQs=; b=d5mm6Yhyv9QErdypu9wkPlU+pAs+FAGNLLVQBb4S5qKNXhXizIdIVvwHu9i9CcGZCWXiYC XmnzgGQKjhZuaqS6leF4kfgONWe3cOnZX8Dx3rk/RuaBzw7f0lAYdsP70dL4dpjcS5EfI6 6W9hs3KbdV2eW3jpXL64VHFI6Q3vRUJ9MAEjMouELwsJWMcxnOb9AbMSOWXutsU31r8tfo jq4QpgiarECxbORS2ITamWWX50RuR3N90Rh+kI5sEfR6LUhDXEoohIr1paJ6uCQ7kgsDAj 5cKHnCmRcATprPvOkTOWR2Qv0ybNI3/xAGnRLYC1wMRoFdfIevmNGsA3BCfjJQ== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4ZPQSP3WwlzqXv; Fri, 28 Mar 2025 16:08:53 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.18.1/8.18.1) with ESMTP id 52SG8rEs086509; Fri, 28 Mar 2025 16:08:53 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.18.1/8.18.1/Submit) id 52SG8rp2086506; Fri, 28 Mar 2025 16:08:53 GMT (envelope-from git) Date: Fri, 28 Mar 2025 16:08:53 GMT Message-Id: <202503281608.52SG8rp2086506@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Kajetan Staszkiewicz Subject: git: aeddee83341e - main - pfctl: Split pool parsing into separate functions List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-all@freebsd.org Sender: owner-dev-commits-src-all@FreeBSD.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: ks X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: aeddee83341eb3b847fb4862db5cd0a553bf4a36 Auto-Submitted: auto-generated The branch main has been updated by ks: URL: https://cgit.FreeBSD.org/src/commit/?id=aeddee83341eb3b847fb4862db5cd0a553bf4a36 commit aeddee83341eb3b847fb4862db5cd0a553bf4a36 Author: Kajetan Staszkiewicz AuthorDate: 2025-03-28 16:08:34 +0000 Commit: Kajetan Staszkiewicz CommitDate: 2025-03-28 16:08:34 +0000 pfctl: Split pool parsing into separate functions The pf pools are used in NAT, route-to and af-to rules. Some parts of code are duplicated between them. Create functions apply_redirspec(), apply_nat_ports() and apply_rdr_ports() to handle the common tasks. Simplify data structures used for pool parsing. Move the contents of struct redirection to struct redirspec. Map all ways of parsing pools directly onto struct redirspec. Name various forms of struct redirspect to hint where they are used. Remove struct redirspec *rroute from struct filter_opts, because filter_opts is bzero()'ed after the route part of rule is parsed, and thus can't be used. Add tests to ensure that parsing and error messages behave as expected. The tests have been written and tested with pfctl from before this patch. This is prerequisite for adding support for OpenBSD NAT syntax. Reviewed by: kp Approved by: kp (mentor) Sponsored by: InnoGames GmbH Differential Revision: https://reviews.freebsd.org/D49218 --- sbin/pfctl/parse.y | 569 ++++++++++++++++------------------- sbin/pfctl/pfctl.c | 10 + sbin/pfctl/pfctl_parser.h | 1 + sbin/pfctl/tests/files/Makefile | 2 +- sbin/pfctl/tests/files/pf1026.ok | 2 +- sbin/pfctl/tests/files/pf1027.ok | 2 +- sbin/pfctl/tests/files/pf1028.in | 1 + sbin/pfctl/tests/files/pf1028.ok | 1 + sbin/pfctl/tests/files/pf1029.in | 1 + sbin/pfctl/tests/files/pf1029.ok | 1 + sbin/pfctl/tests/files/pf1030.in | 1 + sbin/pfctl/tests/files/pf1030.ok | 1 + sbin/pfctl/tests/files/pf1031.in | 1 + sbin/pfctl/tests/files/pf1031.ok | 1 + sbin/pfctl/tests/files/pf1032.in | 1 + sbin/pfctl/tests/files/pf1032.ok | 1 + sbin/pfctl/tests/files/pf1033.fail | 1 + sbin/pfctl/tests/files/pf1033.in | 1 + sbin/pfctl/tests/files/pf1034.fail | 1 + sbin/pfctl/tests/files/pf1034.in | 1 + sbin/pfctl/tests/files/pf1035.in | 1 + sbin/pfctl/tests/files/pf1035.ok | 1 + sbin/pfctl/tests/files/pf1036.in | 1 + sbin/pfctl/tests/files/pf1036.ok | 1 + sbin/pfctl/tests/files/pf1037.in | 1 + sbin/pfctl/tests/files/pf1037.ok | 1 + sbin/pfctl/tests/files/pf1038.in | 1 + sbin/pfctl/tests/files/pf1038.ok | 1 + sbin/pfctl/tests/files/pf1039.in | 1 + sbin/pfctl/tests/files/pf1039.ok | 1 + sbin/pfctl/tests/files/pf1040.fail | 1 + sbin/pfctl/tests/files/pf1040.in | 1 + sbin/pfctl/tests/files/pf1040.ok | 1 + sbin/pfctl/tests/files/pf1041.in | 1 + sbin/pfctl/tests/files/pf1041.ok | 1 + sbin/pfctl/tests/files/pf1042.fail | 1 + sbin/pfctl/tests/files/pf1042.in | 1 + sbin/pfctl/tests/files/pf1043.fail | 1 + sbin/pfctl/tests/files/pf1043.in | 1 + sbin/pfctl/tests/files/pf1044.in | 1 + sbin/pfctl/tests/files/pf1044.ok | 1 + sbin/pfctl/tests/files/pf1045.in | 1 + sbin/pfctl/tests/files/pf1045.ok | 1 + sbin/pfctl/tests/files/pf1046.fail | 1 + sbin/pfctl/tests/files/pf1046.in | 1 + sbin/pfctl/tests/files/pf1047.fail | 1 + sbin/pfctl/tests/files/pf1047.in | 1 + sbin/pfctl/tests/files/pf1048.in | 1 + sbin/pfctl/tests/files/pf1048.ok | 1 + sbin/pfctl/tests/files/pf1049.in | 1 + sbin/pfctl/tests/files/pf1049.ok | 1 + sbin/pfctl/tests/files/pf1050.in | 1 + sbin/pfctl/tests/files/pf1050.ok | 1 + sbin/pfctl/tests/files/pf1051.in | 1 + sbin/pfctl/tests/files/pf1051.ok | 1 + sbin/pfctl/tests/files/pf1052.in | 1 + sbin/pfctl/tests/files/pf1052.ok | 1 + sbin/pfctl/tests/files/pf1053.in | 1 + sbin/pfctl/tests/files/pf1053.ok | 1 + sbin/pfctl/tests/files/pf1054.in | 3 + sbin/pfctl/tests/files/pf1054.ok | 1 + sbin/pfctl/tests/files/pf1055.in | 1 + sbin/pfctl/tests/files/pf1055.ok | 1 + sbin/pfctl/tests/files/pf1056.in | 1 + sbin/pfctl/tests/files/pf1056.ok | 1 + sbin/pfctl/tests/files/pf1057.in | 1 + sbin/pfctl/tests/files/pf1057.ok | 1 + sbin/pfctl/tests/files/pf1058.in | 1 + sbin/pfctl/tests/files/pf1058.ok | 1 + sbin/pfctl/tests/files/pf1059.in | 1 + sbin/pfctl/tests/files/pf1059.ok | 1 + sbin/pfctl/tests/files/pf1060.in | 1 + sbin/pfctl/tests/files/pf1060.ok | 1 + sbin/pfctl/tests/files/pf1061.in | 1 + sbin/pfctl/tests/files/pf1061.ok | 1 + sbin/pfctl/tests/files/pf1062.in | 1 + sbin/pfctl/tests/files/pf1062.ok | 1 + sbin/pfctl/tests/files/pf1063.in | 1 + sbin/pfctl/tests/files/pf1063.ok | 1 + sbin/pfctl/tests/files/pf1064.in | 1 + sbin/pfctl/tests/files/pf1064.ok | 1 + sbin/pfctl/tests/pfctl_test.c | 188 ++++++++++-- sbin/pfctl/tests/pfctl_test_list.inc | 37 +++ 83 files changed, 547 insertions(+), 341 deletions(-) diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y index 8f732bdf268a..ea81efd7fd94 100644 --- a/sbin/pfctl/parse.y +++ b/sbin/pfctl/parse.y @@ -227,10 +227,6 @@ struct range { int b; int t; }; -struct redirection { - struct node_host *host; - struct range rport; -}; static struct pool_opts { int marker; @@ -245,9 +241,10 @@ static struct pool_opts { } pool_opts; struct redirspec { - struct redirection *rdr; + struct node_host *host; + struct range rport; struct pool_opts pool_opts; - int af; + int af; }; static struct filter_opts { @@ -302,9 +299,8 @@ static struct filter_opts { struct node_host *addr; u_int16_t port; } divert; - struct redirspec nat; - struct redirspec rdr; - struct redirspec rroute; + struct redirspec *nat; + struct redirspec *rdr; /* new-style scrub opts */ int nodf; int minttl; @@ -382,9 +378,11 @@ void expand_eth_rule(struct pfctl_eth_rule *, struct node_mac *, struct node_mac *, struct node_host *, struct node_host *, const char *, const char *); +int apply_rdr_ports(struct pfctl_rule *r, struct pfctl_pool *, struct redirspec *); +int apply_nat_ports(struct pfctl_pool *, struct redirspec *); +int apply_redirspec(struct pfctl_pool *, struct redirspec *); void expand_rule(struct pfctl_rule *, struct node_if *, struct redirspec *, struct redirspec *, struct redirspec *, - struct node_host *, struct node_host *, struct node_host *, struct node_proto *, struct node_os *, struct node_host *, struct node_port *, struct node_host *, struct node_port *, struct node_uid *, struct node_gid *, struct node_if *, @@ -463,13 +461,10 @@ typedef struct { } etheraddr; char *bridge_to; struct { - struct node_host *host; + struct redirspec *redirspec; u_int8_t rt; - u_int8_t pool_opts; - sa_family_t af; - struct pf_poolhashkey *key; } route; - struct redirection *redirection; + struct redirspec *redirspec; struct { int action; struct node_state_opt *options; @@ -554,14 +549,15 @@ int parseport(char *, struct range *r, int); %type fromto l3fromto %type ipportspec from to %type ipspec toipspec xhost host dynaddr host_list -%type redir_host_list redirspec -%type route_host route_host_list routespec +%type redir_host redir_host_list routespec +%type route_host route_host_list %type os xos os_list %type portspec port_list port_item %type uids uid_list uid_item %type gids gid_list gid_item %type route -%type redirection redirpool +%type no_port_redirspec port_redirspec route_redirspec +%type binat_redirspec nat_redirspec %type label stringall tag anchorname %type string varstring numberstring %type keep @@ -965,7 +961,8 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto YYERROR; } - memset(&r, 0, sizeof(r)); + pfctl_init_rule(&r); + if (pf->astack[pf->asd + 1]) { if ($2 && strchr($2, '/') != NULL) { free($2); @@ -978,7 +975,7 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto pfctl_anchor_setup(&r, &pf->astack[pf->asd]->ruleset, $2 ? $2 : pf->alast->name); - + if (r.anchor == NULL) err(1, "anchorrule: unable to " "create ruleset"); @@ -1082,7 +1079,7 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto decide_address_family($8.src.host, &r.af); decide_address_family($8.dst.host, &r.af); - expand_rule(&r, $5, NULL, NULL, NULL, NULL, NULL, NULL, + expand_rule(&r, $5, NULL, NULL, NULL, $7, $8.src_os, $8.src.host, $8.src.port, $8.dst.host, $8.dst.port, $9.uid, $9.gid, $9.rcv, $9.icmpspec, pf->astack[pf->asd + 1] ? pf->alast->name : $2); @@ -1097,7 +1094,8 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto YYERROR; } - memset(&r, 0, sizeof(r)); + pfctl_init_rule(&r); + r.action = PF_NAT; r.af = $4; r.rtableid = $7; @@ -1105,7 +1103,7 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto decide_address_family($6.src.host, &r.af); decide_address_family($6.dst.host, &r.af); - expand_rule(&r, $3, NULL, NULL, NULL, NULL, NULL, NULL, + expand_rule(&r, $3, NULL, NULL, NULL, $5, $6.src_os, $6.src.host, $6.src.port, $6.dst.host, $6.dst.port, 0, 0, 0, 0, $2); free($2); @@ -1118,7 +1116,8 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto YYERROR; } - memset(&r, 0, sizeof(r)); + pfctl_init_rule(&r); + r.action = PF_RDR; r.af = $4; r.rtableid = $7; @@ -1147,7 +1146,7 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto r.dst.port_op = $6.dst.port->op; } - expand_rule(&r, $3, NULL, NULL, NULL, NULL, NULL, NULL, + expand_rule(&r, $3, NULL, NULL, NULL, $5, $6.src_os, $6.src.host, $6.src.port, $6.dst.host, $6.dst.port, 0, 0, 0, 0, $2); free($2); @@ -1160,7 +1159,8 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto YYERROR; } - memset(&r, 0, sizeof(r)); + pfctl_init_rule(&r); + r.action = PF_BINAT; r.af = $4; r.rtableid = $7; @@ -1425,7 +1425,7 @@ scrubrule : scrubaction dir logquick interface af proto fromto scrub_opts if (check_rulestate(PFCTL_STATE_SCRUB)) YYERROR; - memset(&r, 0, sizeof(r)); + pfctl_init_rule(&r); r.action = $1.b1; r.direction = $2; @@ -1470,7 +1470,7 @@ scrubrule : scrubaction dir logquick interface af proto fromto scrub_opts r.match_tag_not = $8.match_tag_not; r.rtableid = $8.rtableid; - expand_rule(&r, $4, NULL, NULL, NULL, NULL, NULL, NULL, + expand_rule(&r, $4, NULL, NULL, NULL, $6, $7.src_os, $7.src.host, $7.src.port, $7.dst.host, $7.dst.port, NULL, NULL, NULL, NULL, ""); } @@ -1587,7 +1587,7 @@ antispoof : ANTISPOOF logquick antispoof_ifspc af antispoof_opts { YYERROR; for (i = $3; i; i = i->next) { - bzero(&r, sizeof(r)); + pfctl_init_rule(&r); r.action = PF_DROP; r.direction = PF_IN; @@ -1635,9 +1635,9 @@ antispoof : ANTISPOOF logquick antispoof_ifspc af antispoof_opts { } if (h != NULL) - expand_rule(&r, j, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, h, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, ""); + expand_rule(&r, j, NULL, NULL, NULL, + NULL, NULL, h, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, ""); if ((i->ifa_flags & IFF_LOOPBACK) == 0) { bzero(&r, sizeof(r)); @@ -1657,9 +1657,10 @@ antispoof : ANTISPOOF logquick antispoof_ifspc af antispoof_opts { else h = ifa_lookup(i->ifname, 0); if (h != NULL) - expand_rule(&r, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, h, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, ""); + expand_rule(&r, NULL, NULL, + NULL, NULL, NULL, NULL, h, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, ""); } else free(hh); } @@ -2371,7 +2372,7 @@ pfrule : action dir logquick interface route af proto fromto if (check_rulestate(PFCTL_STATE_FILTER)) YYERROR; - memset(&r, 0, sizeof(r)); + pfctl_init_rule(&r); r.action = $1.b1; switch ($1.b2) { @@ -2728,44 +2729,14 @@ pfrule : action dir logquick interface route af proto fromto YYERROR; } r.rt = $5.rt; - r.route.opts = $5.pool_opts; - if ($5.key != NULL) - memcpy(&r.route.key, $5.key, - sizeof(struct pf_poolhashkey)); - } - if (r.rt) { - decide_address_family($5.host, &r.af); + decide_address_family($5.redirspec->host, &r.af); if (!(r.rule_flag & PFRULE_AFTO)) - remove_invalid_hosts(&$5.host, &r.af); - if ($5.host == NULL) { + remove_invalid_hosts(&($5.redirspec->host), &r.af); + if ($5.redirspec->host == NULL) { yyerror("no routing address with " "matching address family found."); YYERROR; } - if ((r.route.opts & PF_POOL_TYPEMASK) == - PF_POOL_NONE && ($5.host->next != NULL || - $5.host->addr.type == PF_ADDR_TABLE || - DYNIF_MULTIADDR($5.host->addr))) - r.route.opts |= PF_POOL_ROUNDROBIN; - if ($5.host->next != NULL && - !PF_POOL_DYNTYPE(r.route.opts)) { - yyerror("address pool option " - "not supported by type"); - } - if (!PF_POOL_DYNTYPE(r.route.opts) && - (disallow_table($5.host, - "tables are not supported by pool type") || - disallow_alias($5.host, - "interface (%s) is not supported by pool type"))) - YYERROR; - if ($5.host->next != NULL) { - if ((r.route.opts & PF_POOL_TYPEMASK) != - PF_POOL_ROUNDROBIN) { - yyerror("r.route.opts must " - "be PF_POOL_ROUNDROBIN"); - YYERROR; - } - } } if ($9.queues.qname != NULL) { if (strlcpy(r.qname, $9.queues.qname, @@ -2824,19 +2795,10 @@ pfrule : action dir logquick interface route af proto fromto } if ($9.marker & FOM_AFTO) { - r.naf = $9.nat.af; - - r.nat.opts = $9.nat.pool_opts.type; - r.nat.opts |= $9.nat.pool_opts.opts; - - if (!PF_POOL_DYNTYPE(r.nat.opts) && - disallow_table($9.nat.rdr->host, "tables are not " - "supported by pool type")) - YYERROR; + r.naf = $9.nat->af; } - expand_rule(&r, $4, &$9.nat, &$9.rdr, &$9.rroute, - NULL, $9.nat.rdr ? $9.nat.rdr->host : NULL, $5.host, + expand_rule(&r, $4, $9.nat, $9.rdr, $5.redirspec, $7, $8.src_os, $8.src.host, $8.src.port, $8.dst.host, $8.dst.port, $9.uid, $9.gid, $9.rcv, $9.icmpspec, ""); } @@ -3053,8 +3015,8 @@ filter_opt : USER uids { filter_opts.marker |= FOM_SCRUB_TCP; filter_opts.marker |= $3.marker; } - | AFTO af FROM redirspec pool_opts { - if (filter_opts.nat.rdr) { + | AFTO af FROM port_redirspec { + if (filter_opts.nat) { yyerror("cannot respecify af-to"); YYERROR; } @@ -3062,26 +3024,18 @@ filter_opt : USER uids { yyerror("no address family specified"); YYERROR; } + + filter_opts.nat = $4; + filter_opts.nat->af = $2; if ($4->af && $4->af != $2) { yyerror("af-to addresses must be in the " "target address family"); YYERROR; } - filter_opts.nat.af = $2; - filter_opts.nat.rdr = calloc(1, sizeof(struct redirection)); - if (filter_opts.nat.rdr == NULL) - err(1, "af-to: calloc"); - filter_opts.nat.rdr->host = $4; - memcpy(&filter_opts.nat.pool_opts, &$5, - sizeof(filter_opts.nat.pool_opts)); - filter_opts.rdr.rdr = - calloc(1, sizeof(struct redirection)); - bzero(&filter_opts.rdr.pool_opts, - sizeof(filter_opts.rdr.pool_opts)); filter_opts.marker |= FOM_AFTO; } - | AFTO af FROM redirspec pool_opts TO redirspec pool_opts { - if (filter_opts.nat.rdr) { + | AFTO af FROM port_redirspec TO port_redirspec { + if (filter_opts.nat) { yyerror("cannot respecify af-to"); YYERROR; } @@ -3089,26 +3043,16 @@ filter_opt : USER uids { yyerror("no address family specified"); YYERROR; } - if (($4->af && $4->af != $2) || - ($7->af && $7->af != $2)) { + filter_opts.nat = $4; + filter_opts.nat->af = $2; + filter_opts.rdr = $6; + filter_opts.rdr->af = $2; + if (($4->af && $4->host->af != $2) || + ($6->af && $6->host->af != $2)) { yyerror("af-to addresses must be in the " "target address family"); YYERROR; } - filter_opts.nat.af = $2; - filter_opts.nat.rdr = calloc(1, sizeof(struct redirection)); - if (filter_opts.nat.rdr == NULL) - err(1, "af-to: calloc"); - filter_opts.nat.rdr->host = $4; - memcpy(&filter_opts.nat.pool_opts, &$5, - sizeof(filter_opts.nat.pool_opts)); - filter_opts.rdr.af = $2; - filter_opts.rdr.rdr = calloc(1, sizeof(struct redirection)); - if (filter_opts.rdr.rdr == NULL) - err(1, "af-to: calloc"); - filter_opts.rdr.rdr->host = $7; - memcpy(&filter_opts.nat.pool_opts, &$8, - sizeof(filter_opts.nat.pool_opts)); filter_opts.marker |= FOM_AFTO; } | filter_sets @@ -4580,7 +4524,7 @@ portstar : numberstring { } ; -redirspec : host { $$ = $1; } +redir_host : host { $$ = $1; } | '{' optnl redir_host_list '}' { $$ = $3; } ; @@ -4592,20 +4536,41 @@ redir_host_list : host optnl { $$ = $1; } } ; -redirpool : /* empty */ { $$ = NULL; } - | ARROW redirspec { - $$ = calloc(1, sizeof(struct redirection)); +/* Redirection without port */ +no_port_redirspec: redir_host pool_opts { + $$ = calloc(1, sizeof(struct redirspec)); if ($$ == NULL) - err(1, "redirection: calloc"); - $$->host = $2; + err(1, "redirspec: calloc"); + $$->host = $1; + $$->pool_opts = $2; $$->rport.a = $$->rport.b = $$->rport.t = 0; } - | ARROW redirspec PORT portstar { - $$ = calloc(1, sizeof(struct redirection)); + ; + +/* Redirection with optional port */ +port_redirspec : no_port_redirspec; + | redir_host PORT portstar pool_opts { + $$ = calloc(1, sizeof(struct redirspec)); if ($$ == NULL) - err(1, "redirection: calloc"); - $$->host = $2; - $$->rport = $4; + err(1, "redirspec: calloc"); + $$->host = $1; + $$->rport = $3; + $$->pool_opts = $4; + } + +/* Redirection with an arrow and an optional port: FreeBSD NAT rules */ +nat_redirspec : ARROW port_redirspec { + $$ = $2; + } + ; + +/* Redirection with interfaces and without ports: route-to rules */ +route_redirspec : routespec pool_opts { + $$ = calloc(1, sizeof(struct redirspec)); + if ($$ == NULL) + err(1, "redirspec: calloc"); + $$->host = $1; + $$->pool_opts = $2; } ; @@ -4756,18 +4721,18 @@ pool_opt : BITMASK { } ; -redirection : /* empty */ { $$ = NULL; } +binat_redirspec : /* empty */ { $$ = NULL; } | ARROW host { - $$ = calloc(1, sizeof(struct redirection)); + $$ = calloc(1, sizeof(struct redirspec)); if ($$ == NULL) - err(1, "redirection: calloc"); + err(1, "redirspec: calloc"); $$->host = $2; $$->rport.a = $$->rport.b = $$->rport.t = 0; } | ARROW host PORT portstar { - $$ = calloc(1, sizeof(struct redirection)); + $$ = calloc(1, sizeof(struct redirspec)); if ($$ == NULL) - err(1, "redirection: calloc"); + err(1, "redirspec: calloc"); $$->host = $2; $$->rport = $4; } @@ -4808,7 +4773,7 @@ nataction : no NAT natpasslog { ; natrule : nataction interface af proto fromto tag tagged rtable - redirpool pool_opts + nat_redirspec { struct pfctl_rule r; struct node_state_opt *o; @@ -4816,7 +4781,7 @@ natrule : nataction interface af proto fromto tag tagged rtable if (check_rulestate(PFCTL_STATE_NAT)) YYERROR; - memset(&r, 0, sizeof(r)); + pfctl_init_rule(&r); r.action = $1.b1; r.natpass = $1.b2; @@ -4875,110 +4840,6 @@ natrule : nataction interface af proto fromto tag tagged rtable } if (check_netmask($9->host, r.af)) YYERROR; - - r.rdr.proxy_port[0] = ntohs($9->rport.a); - - switch (r.action) { - case PF_RDR: - if (!$9->rport.b && $9->rport.t && - $5.dst.port != NULL) { - r.rdr.proxy_port[1] = - ntohs($9->rport.a) + - (ntohs( - $5.dst.port->port[1]) - - ntohs( - $5.dst.port->port[0])); - } else - r.rdr.proxy_port[1] = - ntohs($9->rport.b); - break; - case PF_NAT: - r.rdr.proxy_port[1] = - ntohs($9->rport.b); - if (!r.rdr.proxy_port[0] && - !r.rdr.proxy_port[1]) { - r.rdr.proxy_port[0] = - PF_NAT_PROXY_PORT_LOW; - r.rdr.proxy_port[1] = - PF_NAT_PROXY_PORT_HIGH; - } else if (!r.rdr.proxy_port[1]) - r.rdr.proxy_port[1] = - r.rdr.proxy_port[0]; - break; - default: - break; - } - - r.rdr.opts = $10.type; - if ((r.rdr.opts & PF_POOL_TYPEMASK) == - PF_POOL_NONE && ($9->host->next != NULL || - $9->host->addr.type == PF_ADDR_TABLE || - DYNIF_MULTIADDR($9->host->addr))) - r.rdr.opts = PF_POOL_ROUNDROBIN; - if (!PF_POOL_DYNTYPE(r.rdr.opts) && - (disallow_table($9->host, - "tables are not supported by pool type") || - disallow_alias($9->host, - "interface (%s) is not supported by pool type"))) - YYERROR; - if ($9->host->next != NULL) { - if ((r.rdr.opts & PF_POOL_TYPEMASK) != - PF_POOL_ROUNDROBIN) { - yyerror("only round-robin " - "valid for multiple " - "redirection addresses"); - YYERROR; - } - } - } - - if ($10.key != NULL) - memcpy(&r.rdr.key, $10.key, - sizeof(struct pf_poolhashkey)); - - if ($10.opts) - r.rdr.opts |= $10.opts; - - if ($10.staticport) { - if (r.action != PF_NAT) { - yyerror("the 'static-port' option is " - "only valid with nat rules"); - YYERROR; - } - if (r.rdr.proxy_port[0] != - PF_NAT_PROXY_PORT_LOW && - r.rdr.proxy_port[1] != - PF_NAT_PROXY_PORT_HIGH) { - yyerror("the 'static-port' option can't" - " be used when specifying a port" - " range"); - YYERROR; - } - r.rdr.proxy_port[0] = 0; - r.rdr.proxy_port[1] = 0; - } - - if ($10.mape.offset) { - if (r.action != PF_NAT) { - yyerror("the 'map-e-portset' option is" - " only valid with nat rules"); - YYERROR; - } - if ($10.staticport) { - yyerror("the 'map-e-portset' option" - " can't be used 'static-port'"); - YYERROR; - } - if (r.rdr.proxy_port[0] != - PF_NAT_PROXY_PORT_LOW && - r.rdr.proxy_port[1] != - PF_NAT_PROXY_PORT_HIGH) { - yyerror("the 'map-e-portset' option" - " can't be used when specifying" - " a port range"); - YYERROR; - } - r.rdr.mape = $10.mape; } o = keep_state_defaults; @@ -4996,16 +4857,14 @@ natrule : nataction interface af proto fromto tag tagged rtable o = o->next; } - expand_rule(&r, $2, NULL, NULL, NULL, - $9 == NULL ? NULL : $9->host, NULL, NULL, $4, + expand_rule(&r, $2, NULL, $9, NULL, $4, $5.src_os, $5.src.host, $5.src.port, $5.dst.host, $5.dst.port, 0, 0, 0, 0, ""); - free($9); } ; binatrule : no BINAT natpasslog interface af proto FROM ipspec toipspec tag - tagged rtable redirection + tagged rtable binat_redirspec { struct pfctl_rule binat; struct pf_pooladdr *pa; @@ -5016,7 +4875,7 @@ binatrule : no BINAT natpasslog interface af proto FROM ipspec toipspec tag "permitted as a binat destination")) YYERROR; - memset(&binat, 0, sizeof(binat)); + pfctl_init_rule(&binat); if ($1 && $3.b1) { yyerror("\"pass\" not valid with \"no\""); @@ -5249,36 +5108,23 @@ routespec : route_host { $$ = $1; } ; route : /* empty */ { - $$.host = NULL; - $$.rt = 0; - $$.pool_opts = 0; + $$.rt = PF_NOPFROUTE; } | FASTROUTE { /* backwards-compat */ - $$.host = NULL; - $$.rt = 0; - $$.pool_opts = 0; + $$.rt = PF_NOPFROUTE; } - | ROUTETO routespec pool_opts { - $$.host = $2; + | ROUTETO route_redirspec { $$.rt = PF_ROUTETO; - $$.pool_opts = $3.type | $3.opts; - if ($3.key != NULL) - $$.key = $3.key; + $$.redirspec = $2; } - | REPLYTO routespec pool_opts { - $$.host = $2; + | REPLYTO route_redirspec { $$.rt = PF_REPLYTO; - $$.pool_opts = $3.type | $3.opts; - if ($3.key != NULL) - $$.key = $3.key; + $$.redirspec = $2; } - | DUPTO routespec pool_opts { - $$.host = $2; + | DUPTO route_redirspec { $$.rt = PF_DUPTO; - $$.pool_opts = $3.type | $3.opts; - if ($3.key != NULL) - $$.key = $3.key; + $$.redirspec = $2; } ; @@ -6161,12 +6007,144 @@ expand_eth_rule(struct pfctl_eth_rule *r, FREE_LIST(struct node_host, ipdsts); } +int +apply_rdr_ports(struct pfctl_rule *r, struct pfctl_pool *rpool, struct redirspec *rs) +{ + if (rs == NULL) + return 0; + + rpool->proxy_port[0] = ntohs(rs->rport.a); + + if (!rs->rport.b && rs->rport.t && r->dst.port != NULL) { + rpool->proxy_port[1] = ntohs(rs->rport.a) + + (ntohs(r->dst.port[1]) - ntohs(r->dst.port[0])); + } else + r->rdr.proxy_port[1] = ntohs(rs->rport.b); + + if (rs->pool_opts.staticport) { + yyerror("the 'static-port' option is only valid with nat rules"); + return 1; + } + + if (rs->pool_opts.mape.offset) { + yyerror("the 'map-e-portset' option is only valid with nat rules"); + return 1; + } + + return 0; +} + +int +apply_nat_ports(struct pfctl_pool *rpool, struct redirspec *rs) +{ + if (rs == NULL) + return 0; + + rpool->proxy_port[0] = ntohs(rs->rport.a); + rpool->proxy_port[1] = ntohs(rs->rport.b); + if (!rpool->proxy_port[0] && !rpool->proxy_port[1]) { + rpool->proxy_port[0] = PF_NAT_PROXY_PORT_LOW; + rpool->proxy_port[1] = PF_NAT_PROXY_PORT_HIGH; + } else if (!rpool->proxy_port[1]) + rpool->proxy_port[1] = rpool->proxy_port[0]; + + if (rs->pool_opts.staticport) { + if (rpool->proxy_port[0] != PF_NAT_PROXY_PORT_LOW && + rpool->proxy_port[1] != PF_NAT_PROXY_PORT_HIGH) { + yyerror("the 'static-port' option can't" + " be used when specifying a port" + " range"); + return 1; + } + rpool->proxy_port[0] = 0; + rpool->proxy_port[1] = 0; + } + + if (rs->pool_opts.mape.offset) { + if (rs->pool_opts.staticport) { + yyerror("the 'map-e-portset' option" + " can't be used 'static-port'"); + return 1; + } + if (rpool->proxy_port[0] != PF_NAT_PROXY_PORT_LOW && + rpool->proxy_port[1] != PF_NAT_PROXY_PORT_HIGH) { + yyerror("the 'map-e-portset' option" + " can't be used when specifying" + " a port range"); + return 1; + } + rpool->mape = rs->pool_opts.mape; + } + + return 0; +} + +int +apply_redirspec(struct pfctl_pool *rpool, struct redirspec *rs) +{ + struct node_host *h; + struct pf_pooladdr *pa; + + if (rs == NULL) + return 0; + + rpool->opts = rs->pool_opts.type; + + if ((rpool->opts & PF_POOL_TYPEMASK) == PF_POOL_NONE && + (rs->host->next != NULL || + rs->host->addr.type == PF_ADDR_TABLE || + DYNIF_MULTIADDR(rs->host->addr))) + rpool->opts = PF_POOL_ROUNDROBIN; + + if (!PF_POOL_DYNTYPE(rpool->opts) && + (disallow_table(rs->host, "tables are not supported by pool type") || + disallow_alias(rs->host, "interface (%s) is not supported by pool type"))) + return 1; + + if (rs->host->next != NULL && + ((rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_ROUNDROBIN)) { + yyerror("r.route.opts must be PF_POOL_ROUNDROBIN"); + return 1; + } + + if (rs->host->next != NULL) { + if ((rpool->opts & PF_POOL_TYPEMASK) != + PF_POOL_ROUNDROBIN) { + yyerror("only round-robin valid for multiple " + "redirection addresses"); + return 1; + } + } + + rpool->opts |= rs->pool_opts.opts; + + if (rs->pool_opts.key != NULL) + memcpy(&(rpool->key), rs->pool_opts.key, + sizeof(struct pf_poolhashkey)); + + TAILQ_INIT(&(rpool->list)); + for (h = rs->host; h != NULL; h = h->next) { + pa = calloc(1, sizeof(struct pf_pooladdr)); + if (pa == NULL) + err(1, "expand_rule: calloc"); + pa->addr = h->addr; + if (h->ifname != NULL) { + if (strlcpy(pa->ifname, h->ifname, + sizeof(pa->ifname)) >= sizeof(pa->ifname)) + errx(1, "expand_rule: strlcpy"); + } else + pa->ifname[0] = 0; + TAILQ_INSERT_TAIL(&(rpool->list), pa, entries); + } + + return 0; +} + void expand_rule(struct pfctl_rule *r, struct node_if *interfaces, struct redirspec *nat, struct redirspec *rdr, struct redirspec *route, - struct node_host *rdr_hosts, struct node_host *nat_hosts, - struct node_host *route_hosts, struct node_proto *protos, + struct node_proto *protos, struct node_os *src_oses, struct node_host *src_hosts, struct node_port *src_ports, struct node_host *dst_hosts, struct node_port *dst_ports, struct node_uid *uids, struct node_gid *gids, @@ -6178,8 +6156,7 @@ expand_rule(struct pfctl_rule *r, char label[PF_RULE_MAX_LABEL_COUNT][PF_RULE_LABEL_SIZE]; char tagname[PF_TAG_NAME_SIZE]; char match_tagname[PF_TAG_NAME_SIZE]; - struct pf_pooladdr *pa; - struct node_host *h, *osrch, *odsth; + struct node_host *osrch, *odsth; u_int8_t flags, flagset, keep_state; memcpy(label, r->label, sizeof(r->label)); @@ -6317,52 +6294,16 @@ expand_rule(struct pfctl_rule *r, r->os_fingerprint = PF_OSFP_ANY; } - TAILQ_INIT(&r->rdr.list); - for (h = rdr_hosts; h != NULL; h = h->next) { - pa = calloc(1, sizeof(struct pf_pooladdr)); - if (pa == NULL) - err(1, "expand_rule: calloc"); - pa->addr = h->addr; - if (h->ifname != NULL) { - if (strlcpy(pa->ifname, h->ifname, - sizeof(pa->ifname)) >= - sizeof(pa->ifname)) - errx(1, "expand_rule: strlcpy"); - } else - pa->ifname[0] = 0; - TAILQ_INSERT_TAIL(&r->rdr.list, pa, entries); - } - TAILQ_INIT(&r->nat.list); - for (h = nat_hosts; h != NULL; h = h->next) { - pa = calloc(1, sizeof(struct pf_pooladdr)); - if (pa == NULL) - err(1, "expand_rule: calloc"); - pa->addr = h->addr; - if (h->ifname != NULL) { - if (strlcpy(pa->ifname, h->ifname, - sizeof(pa->ifname)) >= - sizeof(pa->ifname)) - errx(1, "expand_rule: strlcpy"); - } else - pa->ifname[0] = 0; - TAILQ_INSERT_TAIL(&r->nat.list, pa, entries); - } - TAILQ_INIT(&r->route.list); - for (h = route_hosts; h != NULL; h = h->next) { - pa = calloc(1, sizeof(struct pf_pooladdr)); - if (pa == NULL) - err(1, "expand_rule: calloc"); - pa->addr = h->addr; - if (h->ifname != NULL) { - if (strlcpy(pa->ifname, h->ifname, - sizeof(pa->ifname)) >= - sizeof(pa->ifname)) - errx(1, "expand_rule: strlcpy"); - } else - pa->ifname[0] = 0; - TAILQ_INSERT_TAIL(&r->route.list, pa, entries); *** 920 LINES SKIPPED ***