Changeset View
Changeset View
Standalone View
Standalone View
sbin/pfctl/parse.y
Show First 20 Lines • Show All 239 Lines • ▼ Show 20 Lines | #define POM_ENDPI 0x04 | ||||
struct pf_mape_portset mape; | struct pf_mape_portset mape; | ||||
} pool_opts; | } pool_opts; | ||||
struct redirspec { | struct redirspec { | ||||
struct node_host *host; | struct node_host *host; | ||||
struct range rport; | struct range rport; | ||||
struct pool_opts pool_opts; | struct pool_opts pool_opts; | ||||
int af; | int af; | ||||
bool binat; | |||||
}; | }; | ||||
static struct filter_opts { | static struct filter_opts { | ||||
int marker; | int marker; | ||||
#define FOM_FLAGS 0x0001 | #define FOM_FLAGS 0x0001 | ||||
#define FOM_ICMP 0x0002 | #define FOM_ICMP 0x0002 | ||||
#define FOM_TOS 0x0004 | #define FOM_TOS 0x0004 | ||||
#define FOM_KEEP 0x0008 | #define FOM_KEEP 0x0008 | ||||
▲ Show 20 Lines • Show All 120 Lines • ▼ Show 20 Lines | |||||
void expand_eth_rule(struct pfctl_eth_rule *, | void expand_eth_rule(struct pfctl_eth_rule *, | ||||
struct node_if *, struct node_etherproto *, | struct node_if *, struct node_etherproto *, | ||||
struct node_mac *, struct node_mac *, | struct node_mac *, struct node_mac *, | ||||
struct node_host *, struct node_host *, const char *, | struct node_host *, struct node_host *, const char *, | ||||
const char *); | const char *); | ||||
int apply_rdr_ports(struct pfctl_rule *r, struct pfctl_pool *, struct redirspec *); | int apply_rdr_ports(struct pfctl_rule *r, struct pfctl_pool *, struct redirspec *); | ||||
int apply_nat_ports(struct pfctl_pool *, struct redirspec *); | int apply_nat_ports(struct pfctl_pool *, struct redirspec *); | ||||
int apply_redirspec(struct pfctl_pool *, struct redirspec *); | int apply_redirspec(struct pfctl_pool *, struct redirspec *); | ||||
void expand_rule(struct pfctl_rule *, struct node_if *, | int check_binat_redirspec(struct node_host *, struct pfctl_rule *, int); | ||||
void add_binat_rdr_rule(struct pfctl_rule *, struct redirspec *, | |||||
struct node_host *, struct pfctl_rule *, struct redirspec **, | |||||
struct node_host **); | |||||
void expand_rule(struct pfctl_rule *, bool, struct node_if *, | |||||
struct redirspec *, struct redirspec *, struct redirspec *, | struct redirspec *, struct redirspec *, struct redirspec *, | ||||
struct node_proto *, struct node_os *, struct node_host *, | struct node_proto *, struct node_os *, struct node_host *, | ||||
struct node_port *, struct node_host *, struct node_port *, | struct node_port *, struct node_host *, struct node_port *, | ||||
struct node_uid *, struct node_gid *, struct node_if *, | struct node_uid *, struct node_gid *, struct node_if *, | ||||
struct node_icmp *, const char *); | struct node_icmp *, const char *); | ||||
int expand_altq(struct pf_altq *, struct node_if *, | int expand_altq(struct pf_altq *, struct node_if *, | ||||
struct node_queue *, struct node_queue_bw bwspec, | struct node_queue *, struct node_queue_bw bwspec, | ||||
struct node_queue_opt *); | struct node_queue_opt *); | ||||
▲ Show 20 Lines • Show All 127 Lines • ▼ Show 20 Lines | |||||
%token BITMASK RANDOM SOURCEHASH ROUNDROBIN STATICPORT PROBABILITY MAPEPORTSET | %token BITMASK RANDOM SOURCEHASH ROUNDROBIN STATICPORT PROBABILITY MAPEPORTSET | ||||
%token ALTQ CBQ CODEL PRIQ HFSC FAIRQ BANDWIDTH TBRSIZE LINKSHARE REALTIME | %token ALTQ CBQ CODEL PRIQ HFSC FAIRQ BANDWIDTH TBRSIZE LINKSHARE REALTIME | ||||
%token UPPERLIMIT QUEUE PRIORITY QLIMIT HOGS BUCKETS RTABLE TARGET INTERVAL | %token UPPERLIMIT QUEUE PRIORITY QLIMIT HOGS BUCKETS RTABLE TARGET INTERVAL | ||||
%token DNPIPE DNQUEUE RIDENTIFIER | %token DNPIPE DNQUEUE RIDENTIFIER | ||||
%token LOAD RULESET_OPTIMIZATION PRIO | %token LOAD RULESET_OPTIMIZATION PRIO | ||||
%token STICKYADDRESS ENDPI MAXSRCSTATES MAXSRCNODES SOURCETRACK GLOBAL RULE | %token STICKYADDRESS ENDPI MAXSRCSTATES MAXSRCNODES SOURCETRACK GLOBAL RULE | ||||
%token MAXSRCCONN MAXSRCCONNRATE OVERLOAD FLUSH SLOPPY PFLOW ALLOW_RELATED | %token MAXSRCCONN MAXSRCCONNRATE OVERLOAD FLUSH SLOPPY PFLOW ALLOW_RELATED | ||||
%token TAGGED TAG IFBOUND FLOATING STATEPOLICY STATEDEFAULTS ROUTE SETTOS | %token TAGGED TAG IFBOUND FLOATING STATEPOLICY STATEDEFAULTS ROUTE SETTOS | ||||
%token DIVERTTO DIVERTREPLY BRIDGE_TO RECEIVEDON NE LE GE AFTO | %token DIVERTTO DIVERTREPLY BRIDGE_TO RECEIVEDON NE LE GE AFTO NATTO RDRTO | ||||
%token BINATTO | |||||
%token <v.string> STRING | %token <v.string> STRING | ||||
%token <v.number> NUMBER | %token <v.number> NUMBER | ||||
%token <v.i> PORTBINARY | %token <v.i> PORTBINARY | ||||
%type <v.interface> interface if_list if_item_not if_item | %type <v.interface> interface if_list if_item_not if_item | ||||
%type <v.number> number icmptype icmp6type uid gid | %type <v.number> number icmptype icmp6type uid gid | ||||
%type <v.number> tos not yesno optnodf | %type <v.number> tos not yesno optnodf | ||||
%type <v.probability> probability | %type <v.probability> probability | ||||
%type <v.i> no dir af fragcache optimizer syncookie_val | %type <v.i> no dir af fragcache optimizer syncookie_val | ||||
▲ Show 20 Lines • Show All 420 Lines • ▼ Show 20 Lines | anchorrule : ANCHOR anchorname dir quick interface af proto fromto | ||||
if ($2 && ($2[0] == '_' || strstr($2, "/_") != NULL)) { | if ($2 && ($2[0] == '_' || strstr($2, "/_") != NULL)) { | ||||
free($2); | free($2); | ||||
yyerror("anchor names beginning with '_' " | yyerror("anchor names beginning with '_' " | ||||
"are reserved for internal use"); | "are reserved for internal use"); | ||||
YYERROR; | YYERROR; | ||||
} | } | ||||
pfctl_init_rule(&r); | pfctl_init_rule(&r); | ||||
kp: Is it worth factoring this out into a pfctl_init_rule() or something? | |||||
Done Inline ActionsYeah, why not. vegeta_tuxpowered.net: Yeah, why not. | |||||
if (pf->astack[pf->asd + 1]) { | if (pf->astack[pf->asd + 1]) { | ||||
if ($2 && strchr($2, '/') != NULL) { | if ($2 && strchr($2, '/') != NULL) { | ||||
free($2); | free($2); | ||||
yyerror("anchor paths containing '/' " | yyerror("anchor paths containing '/' " | ||||
"cannot be used for inline anchors."); | "cannot be used for inline anchors."); | ||||
YYERROR; | YYERROR; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 100 Lines • ▼ Show 20 Lines | anchorrule : ANCHOR anchorname dir quick interface af proto fromto | ||||
r.set_prio[0] = $9.set_prio[0]; | r.set_prio[0] = $9.set_prio[0]; | ||||
r.set_prio[1] = $9.set_prio[1]; | r.set_prio[1] = $9.set_prio[1]; | ||||
r.scrub_flags |= PFSTATE_SETPRIO; | r.scrub_flags |= PFSTATE_SETPRIO; | ||||
} | } | ||||
decide_address_family($8.src.host, &r.af); | decide_address_family($8.src.host, &r.af); | ||||
decide_address_family($8.dst.host, &r.af); | decide_address_family($8.dst.host, &r.af); | ||||
expand_rule(&r, $5, NULL, NULL, NULL, | expand_rule(&r, false, $5, NULL, NULL, NULL, | ||||
$7, $8.src_os, $8.src.host, $8.src.port, $8.dst.host, | $7, $8.src_os, $8.src.host, $8.src.port, $8.dst.host, | ||||
$8.dst.port, $9.uid, $9.gid, $9.rcv, $9.icmpspec, | $8.dst.port, $9.uid, $9.gid, $9.rcv, $9.icmpspec, | ||||
pf->astack[pf->asd + 1] ? pf->alast->name : $2); | pf->astack[pf->asd + 1] ? pf->alast->name : $2); | ||||
free($2); | free($2); | ||||
pf->astack[pf->asd + 1] = NULL; | pf->astack[pf->asd + 1] = NULL; | ||||
} | } | ||||
| NATANCHOR string interface af proto fromto rtable { | | NATANCHOR string interface af proto fromto rtable { | ||||
struct pfctl_rule r; | struct pfctl_rule r; | ||||
if (check_rulestate(PFCTL_STATE_NAT)) { | if (check_rulestate(PFCTL_STATE_NAT)) { | ||||
free($2); | free($2); | ||||
YYERROR; | YYERROR; | ||||
} | } | ||||
pfctl_init_rule(&r); | pfctl_init_rule(&r); | ||||
r.action = PF_NAT; | r.action = PF_NAT; | ||||
r.af = $4; | r.af = $4; | ||||
r.rtableid = $7; | r.rtableid = $7; | ||||
decide_address_family($6.src.host, &r.af); | decide_address_family($6.src.host, &r.af); | ||||
decide_address_family($6.dst.host, &r.af); | decide_address_family($6.dst.host, &r.af); | ||||
expand_rule(&r, $3, NULL, NULL, NULL, | expand_rule(&r, false, $3, NULL, NULL, NULL, | ||||
$5, $6.src_os, $6.src.host, $6.src.port, $6.dst.host, | $5, $6.src_os, $6.src.host, $6.src.port, $6.dst.host, | ||||
$6.dst.port, 0, 0, 0, 0, $2); | $6.dst.port, 0, 0, 0, 0, $2); | ||||
free($2); | free($2); | ||||
} | } | ||||
| RDRANCHOR string interface af proto fromto rtable { | | RDRANCHOR string interface af proto fromto rtable { | ||||
struct pfctl_rule r; | struct pfctl_rule r; | ||||
if (check_rulestate(PFCTL_STATE_NAT)) { | if (check_rulestate(PFCTL_STATE_NAT)) { | ||||
Show All 26 Lines | | RDRANCHOR string interface af proto fromto rtable { | ||||
" not supported in rdr-anchor"); | " not supported in rdr-anchor"); | ||||
YYERROR; | YYERROR; | ||||
} | } | ||||
r.dst.port[0] = $6.dst.port->port[0]; | r.dst.port[0] = $6.dst.port->port[0]; | ||||
r.dst.port[1] = $6.dst.port->port[1]; | r.dst.port[1] = $6.dst.port->port[1]; | ||||
r.dst.port_op = $6.dst.port->op; | r.dst.port_op = $6.dst.port->op; | ||||
} | } | ||||
expand_rule(&r, $3, NULL, NULL, NULL, | expand_rule(&r, false, $3, NULL, NULL, NULL, | ||||
$5, $6.src_os, $6.src.host, $6.src.port, $6.dst.host, | $5, $6.src_os, $6.src.host, $6.src.port, $6.dst.host, | ||||
$6.dst.port, 0, 0, 0, 0, $2); | $6.dst.port, 0, 0, 0, 0, $2); | ||||
free($2); | free($2); | ||||
} | } | ||||
| BINATANCHOR string interface af proto fromto rtable { | | BINATANCHOR string interface af proto fromto rtable { | ||||
struct pfctl_rule r; | struct pfctl_rule r; | ||||
if (check_rulestate(PFCTL_STATE_NAT)) { | if (check_rulestate(PFCTL_STATE_NAT)) { | ||||
▲ Show 20 Lines • Show All 307 Lines • ▼ Show 20 Lines | scrubrule : scrubaction dir logquick interface af proto fromto scrub_opts | ||||
PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) { | PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) { | ||||
yyerror("tag too long, max %u chars", | yyerror("tag too long, max %u chars", | ||||
PF_TAG_NAME_SIZE - 1); | PF_TAG_NAME_SIZE - 1); | ||||
YYERROR; | YYERROR; | ||||
} | } | ||||
r.match_tag_not = $8.match_tag_not; | r.match_tag_not = $8.match_tag_not; | ||||
r.rtableid = $8.rtableid; | r.rtableid = $8.rtableid; | ||||
expand_rule(&r, $4, NULL, NULL, NULL, | expand_rule(&r, false, $4, NULL, NULL, NULL, | ||||
$6, $7.src_os, $7.src.host, $7.src.port, $7.dst.host, | $6, $7.src_os, $7.src.host, $7.src.port, $7.dst.host, | ||||
$7.dst.port, NULL, NULL, NULL, NULL, ""); | $7.dst.port, NULL, NULL, NULL, NULL, ""); | ||||
} | } | ||||
; | ; | ||||
scrub_opts : { | scrub_opts : { | ||||
bzero(&scrub_opts, sizeof scrub_opts); | bzero(&scrub_opts, sizeof scrub_opts); | ||||
scrub_opts.rtableid = -1; | scrub_opts.rtableid = -1; | ||||
▲ Show 20 Lines • Show All 148 Lines • ▼ Show 20 Lines | antispoof : ANTISPOOF logquick antispoof_ifspc af antispoof_opts { | ||||
h->addr.iflags = PFI_AFLAG_NETWORK; | h->addr.iflags = PFI_AFLAG_NETWORK; | ||||
} else { | } else { | ||||
h = ifa_lookup(j->ifname, | h = ifa_lookup(j->ifname, | ||||
PFI_AFLAG_NETWORK); | PFI_AFLAG_NETWORK); | ||||
hh = NULL; | hh = NULL; | ||||
} | } | ||||
if (h != NULL) | if (h != NULL) | ||||
expand_rule(&r, j, NULL, NULL, NULL, | expand_rule(&r, false, j, NULL, NULL, | ||||
NULL, NULL, h, NULL, NULL, NULL, | NULL, NULL, NULL, h, NULL, NULL, | ||||
NULL, NULL, NULL, NULL, ""); | NULL, NULL, NULL, NULL, NULL, ""); | ||||
if ((i->ifa_flags & IFF_LOOPBACK) == 0) { | if ((i->ifa_flags & IFF_LOOPBACK) == 0) { | ||||
bzero(&r, sizeof(r)); | bzero(&r, sizeof(r)); | ||||
r.action = PF_DROP; | r.action = PF_DROP; | ||||
r.direction = PF_IN; | r.direction = PF_IN; | ||||
r.log = $2.log; | r.log = $2.log; | ||||
r.logif = $2.logif; | r.logif = $2.logif; | ||||
r.quick = $2.quick; | r.quick = $2.quick; | ||||
r.af = $4; | r.af = $4; | ||||
r.ridentifier = $5.ridentifier; | r.ridentifier = $5.ridentifier; | ||||
if (rule_label(&r, $5.label)) | if (rule_label(&r, $5.label)) | ||||
YYERROR; | YYERROR; | ||||
r.rtableid = $5.rtableid; | r.rtableid = $5.rtableid; | ||||
if (hh != NULL) | if (hh != NULL) | ||||
h = hh; | h = hh; | ||||
else | else | ||||
h = ifa_lookup(i->ifname, 0); | h = ifa_lookup(i->ifname, 0); | ||||
if (h != NULL) | if (h != NULL) | ||||
expand_rule(&r, NULL, NULL, | expand_rule(&r, false, NULL, | ||||
NULL, NULL, NULL, NULL, h, | |||||
NULL, NULL, NULL, NULL, | NULL, NULL, NULL, NULL, | ||||
NULL, NULL, NULL, ""); | NULL, h, NULL, NULL, NULL, | ||||
NULL, NULL, NULL, NULL, ""); | |||||
} else | } else | ||||
free(hh); | free(hh); | ||||
} | } | ||||
for (int i = 0; i < PF_RULE_MAX_LABEL_COUNT; i++) | for (int i = 0; i < PF_RULE_MAX_LABEL_COUNT; i++) | ||||
free($5.label[i]); | free($5.label[i]); | ||||
} | } | ||||
; | ; | ||||
▲ Show 20 Lines • Show All 1,119 Lines • ▼ Show 20 Lines | #endif | ||||
if ($9.free_flags & PFRULE_DN_IS_PIPE) | if ($9.free_flags & PFRULE_DN_IS_PIPE) | ||||
r.free_flags |= PFRULE_DN_IS_PIPE; | r.free_flags |= PFRULE_DN_IS_PIPE; | ||||
else | else | ||||
r.free_flags |= PFRULE_DN_IS_QUEUE; | r.free_flags |= PFRULE_DN_IS_QUEUE; | ||||
} | } | ||||
if ($9.marker & FOM_AFTO) { | if ($9.marker & FOM_AFTO) { | ||||
r.naf = $9.nat->af; | r.naf = $9.nat->af; | ||||
} else { | |||||
if ($9.nat) { | |||||
if (!r.af && ! $9.nat->host->ifindex) | |||||
r.af = $9.nat->host->af; | |||||
remove_invalid_hosts(&($9.nat->host), &r.af); | |||||
if (invalid_redirect($9.nat->host, r.af)) | |||||
YYERROR; | |||||
if ($9.nat->host->addr.type == PF_ADDR_DYNIFTL) { | |||||
if (($9.nat->host = gen_dynnode($9.nat->host, r.af)) == NULL) | |||||
err(1, "calloc"); | |||||
} | } | ||||
if (check_netmask($9.nat->host, r.af)) | |||||
YYERROR; | |||||
} | |||||
if ($9.rdr) { | |||||
if (!r.af && ! $9.rdr->host->ifindex) | |||||
r.af = $9.rdr->host->af; | |||||
remove_invalid_hosts(&($9.rdr->host), &r.af); | |||||
if (invalid_redirect($9.rdr->host, r.af)) | |||||
YYERROR; | |||||
if ($9.rdr->host->addr.type == PF_ADDR_DYNIFTL) { | |||||
if (($9.rdr->host = gen_dynnode($9.rdr->host, r.af)) == NULL) | |||||
err(1, "calloc"); | |||||
} | |||||
if (check_netmask($9.rdr->host, r.af)) | |||||
YYERROR; | |||||
} | |||||
} | |||||
expand_rule(&r, $4, $9.nat, $9.rdr, $5.redirspec, | expand_rule(&r, false, $4, $9.nat, $9.rdr, $5.redirspec, | ||||
$7, $8.src_os, $8.src.host, $8.src.port, $8.dst.host, | $7, $8.src_os, $8.src.host, $8.src.port, $8.dst.host, | ||||
$8.dst.port, $9.uid, $9.gid, $9.rcv, $9.icmpspec, ""); | $8.dst.port, $9.uid, $9.gid, $9.rcv, $9.icmpspec, ""); | ||||
} | } | ||||
; | ; | ||||
filter_opts : { | filter_opts : { | ||||
bzero(&filter_opts, sizeof filter_opts); | bzero(&filter_opts, sizeof filter_opts); | ||||
filter_opts.rtableid = -1; | filter_opts.rtableid = -1; | ||||
▲ Show 20 Lines • Show All 200 Lines • ▼ Show 20 Lines | | SCRUB '(' scrub_opts ')' { | ||||
filter_opts.settos = $3.settos; | filter_opts.settos = $3.settos; | ||||
} | } | ||||
filter_opts.randomid = $3.randomid; | filter_opts.randomid = $3.randomid; | ||||
filter_opts.max_mss = $3.maxmss; | filter_opts.max_mss = $3.maxmss; | ||||
if ($3.reassemble_tcp) | if ($3.reassemble_tcp) | ||||
filter_opts.marker |= FOM_SCRUB_TCP; | filter_opts.marker |= FOM_SCRUB_TCP; | ||||
filter_opts.marker |= $3.marker; | filter_opts.marker |= $3.marker; | ||||
} | } | ||||
| NATTO port_redirspec { | |||||
if (filter_opts.nat) { | |||||
yyerror("cannot respecify nat-to/binat-to"); | |||||
YYERROR; | |||||
} | |||||
filter_opts.nat = $2; | |||||
} | |||||
| RDRTO port_redirspec { | |||||
if (filter_opts.rdr) { | |||||
yyerror("cannot respecify rdr-to"); | |||||
YYERROR; | |||||
} | |||||
filter_opts.rdr = $2; | |||||
} | |||||
| BINATTO port_redirspec { | |||||
if (filter_opts.nat) { | |||||
yyerror("cannot respecify nat-to/binat-to"); | |||||
YYERROR; | |||||
} | |||||
filter_opts.nat = $2; | |||||
filter_opts.nat->binat = 1; | |||||
filter_opts.nat->pool_opts.staticport = 1; | |||||
} | |||||
| AFTO af FROM port_redirspec { | | AFTO af FROM port_redirspec { | ||||
if (filter_opts.nat) { | if (filter_opts.nat) { | ||||
yyerror("cannot respecify af-to"); | yyerror("cannot respecify af-to"); | ||||
YYERROR; | YYERROR; | ||||
} | } | ||||
if ($2 == 0) { | if ($2 == 0) { | ||||
yyerror("no address family specified"); | yyerror("no address family specified"); | ||||
YYERROR; | YYERROR; | ||||
▲ Show 20 Lines • Show All 1,827 Lines • ▼ Show 20 Lines | natrule : nataction interface af proto fromto tag tagged rtable | ||||
YYERROR; | YYERROR; | ||||
} | } | ||||
r.rule_flag |= PFRULE_PFLOW; | r.rule_flag |= PFRULE_PFLOW; | ||||
break; | break; | ||||
} | } | ||||
o = o->next; | o = o->next; | ||||
} | } | ||||
expand_rule(&r, $2, NULL, $9, NULL, $4, | expand_rule(&r, false, $2, NULL, $9, NULL, $4, | ||||
$5.src_os, $5.src.host, $5.src.port, $5.dst.host, | $5.src_os, $5.src.host, $5.src.port, $5.dst.host, | ||||
$5.dst.port, 0, 0, 0, 0, ""); | $5.dst.port, 0, 0, 0, 0, ""); | ||||
} | } | ||||
; | ; | ||||
binatrule : no BINAT natpasslog interface af proto FROM ipspec toipspec tag | binatrule : no BINAT natpasslog interface af proto FROM ipspec toipspec tag | ||||
tagged rtable binat_redirspec | tagged rtable binat_redirspec | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 152 Lines • ▼ Show 20 Lines | binatrule : no BINAT natpasslog interface af proto FROM ipspec toipspec tag | ||||
binat.af) && | binat.af) && | ||||
!PF_AEQ(&binat.src.addr.v.a.mask, | !PF_AEQ(&binat.src.addr.v.a.mask, | ||||
&$13->host->addr.v.a.mask, binat.af)) { | &$13->host->addr.v.a.mask, binat.af)) { | ||||
yyerror("'binat' source mask and " | yyerror("'binat' source mask and " | ||||
"redirect mask must be the same"); | "redirect mask must be the same"); | ||||
YYERROR; | YYERROR; | ||||
} | } | ||||
TAILQ_INIT(&binat.rdr.list); | |||||
TAILQ_INIT(&binat.nat.list); | |||||
pa = calloc(1, sizeof(struct pf_pooladdr)); | pa = calloc(1, sizeof(struct pf_pooladdr)); | ||||
if (pa == NULL) | if (pa == NULL) | ||||
err(1, "binat: calloc"); | err(1, "binat: calloc"); | ||||
pa->addr = $13->host->addr; | pa->addr = $13->host->addr; | ||||
pa->ifname[0] = 0; | pa->ifname[0] = 0; | ||||
TAILQ_INSERT_TAIL(&binat.rdr.list, | TAILQ_INSERT_TAIL(&binat.rdr.list, | ||||
pa, entries); | pa, entries); | ||||
▲ Show 20 Lines • Show All 343 Lines • ▼ Show 20 Lines | case PF_DROP: | ||||
if (r->rt) { | if (r->rt) { | ||||
yyerror("route-to, reply-to and dup-to " | yyerror("route-to, reply-to and dup-to " | ||||
"are not supported on block rules"); | "are not supported on block rules"); | ||||
problems++; | problems++; | ||||
} | } | ||||
break; | break; | ||||
default:; | default:; | ||||
} | } | ||||
if (r->rdr.opts & PF_POOL_STICKYADDR && !r->keep_state) { | if (!TAILQ_EMPTY(&(r->nat.list)) || !TAILQ_EMPTY(&(r->rdr.list))) { | ||||
yyerror("'sticky-address' requires 'keep state'"); | if (r->action != PF_MATCH && !r->keep_state) { | ||||
yyerror("nat-to and rdr-to require keep state"); | |||||
problems++; | problems++; | ||||
} | } | ||||
if (r->direction == PF_INOUT) { | |||||
yyerror("nat-to and rdr-to require a direction"); | |||||
problems++; | |||||
} | |||||
} | |||||
if (r->route.opts & PF_POOL_STICKYADDR && !r->keep_state) { | |||||
yyerror("'sticky-address' requires 'keep state'"); | |||||
} | |||||
return (-problems); | return (-problems); | ||||
} | } | ||||
int | int | ||||
nat_consistent(struct pfctl_rule *r) | nat_consistent(struct pfctl_rule *r) | ||||
{ | { | ||||
return (0); /* yeah! */ | return (0); /* yeah! */ | ||||
} | } | ||||
▲ Show 20 Lines • Show All 726 Lines • ▼ Show 20 Lines | apply_redirspec(struct pfctl_pool *rpool, struct redirspec *rs) | ||||
} | } | ||||
rpool->opts |= rs->pool_opts.opts; | rpool->opts |= rs->pool_opts.opts; | ||||
if (rs->pool_opts.key != NULL) | if (rs->pool_opts.key != NULL) | ||||
memcpy(&(rpool->key), rs->pool_opts.key, | memcpy(&(rpool->key), rs->pool_opts.key, | ||||
sizeof(struct pf_poolhashkey)); | sizeof(struct pf_poolhashkey)); | ||||
TAILQ_INIT(&(rpool->list)); | |||||
for (h = rs->host; h != NULL; h = h->next) { | for (h = rs->host; h != NULL; h = h->next) { | ||||
pa = calloc(1, sizeof(struct pf_pooladdr)); | pa = calloc(1, sizeof(struct pf_pooladdr)); | ||||
if (pa == NULL) | if (pa == NULL) | ||||
err(1, "expand_rule: calloc"); | err(1, "expand_rule: calloc"); | ||||
pa->addr = h->addr; | pa->addr = h->addr; | ||||
if (h->ifname != NULL) { | if (h->ifname != NULL) { | ||||
if (strlcpy(pa->ifname, h->ifname, | if (strlcpy(pa->ifname, h->ifname, | ||||
sizeof(pa->ifname)) >= sizeof(pa->ifname)) | sizeof(pa->ifname)) >= sizeof(pa->ifname)) | ||||
errx(1, "expand_rule: strlcpy"); | errx(1, "expand_rule: strlcpy"); | ||||
} else | } else | ||||
pa->ifname[0] = 0; | pa->ifname[0] = 0; | ||||
TAILQ_INSERT_TAIL(&(rpool->list), pa, entries); | TAILQ_INSERT_TAIL(&(rpool->list), pa, entries); | ||||
} | } | ||||
return 0; | return 0; | ||||
} | } | ||||
int | |||||
check_binat_redirspec(struct node_host *src_host, struct pfctl_rule *r, int af) | |||||
{ | |||||
struct pf_pooladdr *nat_pool = TAILQ_FIRST(&(r->nat.list)); | |||||
int error = 0; | |||||
/* XXX: FreeBSD allows syntax like "{ host1 host2 }" for redirection | |||||
* pools but does not covert them to tables automatically, because | |||||
* syntax "{ (iface1 host1), (iface2 iface2) }" is allowed for route-to | |||||
* redirection. Add a FreeBSD-specific guard against using multiple | |||||
* hosts for source and redirection. | |||||
*/ | |||||
if (src_host->next) { | |||||
yyerror("invalid use of table as the source address " | |||||
"of a binat-to rule"); | |||||
error++; | |||||
} | |||||
if (TAILQ_NEXT(nat_pool, entries)) { | |||||
yyerror ("tables cannot be used as the redirect " | |||||
"address of a binat-to rule"); | |||||
error++; | |||||
} | |||||
if (disallow_table(src_host, "invalid use of table " | |||||
"<%s> as the source address of a binat-to rule") || | |||||
disallow_alias(src_host, "invalid use of interface " | |||||
"(%s) as the source address of a binat-to rule")) { | |||||
error++; | |||||
} else if ((r->src.addr.type != PF_ADDR_ADDRMASK && | |||||
r->src.addr.type != PF_ADDR_DYNIFTL) || | |||||
(nat_pool->addr.type != PF_ADDR_ADDRMASK && | |||||
nat_pool->addr.type != PF_ADDR_DYNIFTL)) { | |||||
yyerror("binat-to requires a specified " | |||||
"source and redirect address"); | |||||
error++; | |||||
} | |||||
if (DYNIF_MULTIADDR(r->src.addr) || | |||||
DYNIF_MULTIADDR(nat_pool->addr)) { | |||||
yyerror ("dynamic interfaces must be " | |||||
"used with:0 in a binat-to rule"); | |||||
error++; | |||||
} | |||||
if (PF_AZERO(&r->src.addr.v.a.mask, af) || | |||||
PF_AZERO(&(nat_pool->addr.v.a.mask), af)) { | |||||
yyerror ("source and redir addresess must have " | |||||
"a matching network mask in binat-rule"); | |||||
error++; | |||||
} | |||||
if (nat_pool->addr.type == PF_ADDR_TABLE) { | |||||
yyerror ("tables cannot be used as the redirect " | |||||
"address of a binat-to rule"); | |||||
error++; | |||||
} | |||||
if (r->direction != PF_INOUT) { | |||||
yyerror("binat-to cannot be specified " | |||||
"with a direction"); | |||||
error++; | |||||
} | |||||
/* first specify outbound NAT rule */ | |||||
r->direction = PF_OUT; | |||||
return (error); | |||||
} | |||||
void | void | ||||
expand_rule(struct pfctl_rule *r, | add_binat_rdr_rule( | ||||
struct pfctl_rule *binat_rule, | |||||
struct redirspec *binat_nat_redirspec, struct node_host *binat_src_host, | |||||
struct pfctl_rule *rdr_rule, struct redirspec **rdr_redirspec, | |||||
struct node_host **rdr_dst_host) | |||||
{ | |||||
struct node_host *rdr_src_host; | |||||
/* | |||||
* We're copying the whole rule, but we must re-init redir pools. | |||||
* FreeBSD uses lists of pf_pooladdr, we can't just overwrite them. | |||||
*/ | |||||
bcopy(binat_rule, rdr_rule, sizeof(struct pfctl_rule)); | |||||
TAILQ_INIT(&(rdr_rule->rdr.list)); | |||||
TAILQ_INIT(&(rdr_rule->nat.list)); | |||||
/* now specify inbound rdr rule */ | |||||
rdr_rule->direction = PF_IN; | |||||
if ((rdr_src_host = calloc(1, sizeof(*rdr_src_host))) == NULL) | |||||
err(1, "%s", __func__); | |||||
bcopy(binat_src_host, rdr_src_host, sizeof(*rdr_src_host)); | |||||
rdr_src_host->ifname = NULL; | |||||
rdr_src_host->next = NULL; | |||||
rdr_src_host->tail = NULL; | |||||
if (((*rdr_dst_host) = calloc(1, sizeof(**rdr_dst_host))) == NULL) | |||||
err(1, "%s", __func__); | |||||
bcopy(&(binat_nat_redirspec->host->addr), &((*rdr_dst_host)->addr), | |||||
sizeof((*rdr_dst_host)->addr)); | |||||
(*rdr_dst_host)->ifname = NULL; | |||||
(*rdr_dst_host)->next = NULL; | |||||
(*rdr_dst_host)->tail = NULL; | |||||
if (((*rdr_redirspec) = calloc(1, sizeof(**rdr_redirspec))) == NULL) | |||||
err(1, "%s", __func__); | |||||
bcopy(binat_nat_redirspec, (*rdr_redirspec), sizeof(**rdr_redirspec)); | |||||
(*rdr_redirspec)->pool_opts.staticport = 0; | |||||
(*rdr_redirspec)->host = rdr_src_host; | |||||
} | |||||
void | |||||
expand_rule(struct pfctl_rule *r, bool keeprule, | |||||
struct node_if *interfaces, struct redirspec *nat, | struct node_if *interfaces, struct redirspec *nat, | ||||
struct redirspec *rdr, struct redirspec *route, | struct redirspec *rdr, struct redirspec *route, | ||||
struct node_proto *protos, | struct node_proto *protos, | ||||
struct node_os *src_oses, struct node_host *src_hosts, | struct node_os *src_oses, struct node_host *src_hosts, | ||||
struct node_port *src_ports, struct node_host *dst_hosts, | struct node_port *src_ports, struct node_host *dst_hosts, | ||||
struct node_port *dst_ports, struct node_uid *uids, struct node_gid *gids, | struct node_port *dst_ports, struct node_uid *uids, struct node_gid *gids, | ||||
struct node_if *rcv, struct node_icmp *icmp_types, const char *anchor_call) | struct node_if *rcv, struct node_icmp *icmp_types, const char *anchor_call) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 137 Lines • ▼ Show 20 Lines | if (src_os && src_os->os) { | ||||
fprintf(stderr, | fprintf(stderr, | ||||
"warning: unknown '%s' OS fingerprint\n", | "warning: unknown '%s' OS fingerprint\n", | ||||
src_os->os); | src_os->os); | ||||
} else { | } else { | ||||
r->os_fingerprint = PF_OSFP_ANY; | r->os_fingerprint = PF_OSFP_ANY; | ||||
} | } | ||||
if (r->action == PF_RDR) { | if (r->action == PF_RDR) { | ||||
/* Pre-FreeBSD 15 "rdr" rule */ | |||||
error += apply_rdr_ports(r, &(r->rdr), rdr); | error += apply_rdr_ports(r, &(r->rdr), rdr); | ||||
error += apply_redirspec(&(r->rdr), rdr); | |||||
} else if (r->action == PF_NAT) { | } else if (r->action == PF_NAT) { | ||||
/* Pre-FreeBSD 15 "nat" rule */ | |||||
error += apply_nat_ports(&(r->rdr), rdr); | error += apply_nat_ports(&(r->rdr), rdr); | ||||
} | error += apply_redirspec(&(r->rdr), rdr); | ||||
} else { | |||||
/* Modern rule with optional NAT, BINAT, RDR or ROUTE*/ | |||||
error += apply_redirspec(&(r->route), route); | |||||
error += apply_nat_ports(&(r->nat), nat); | |||||
error += apply_redirspec(&(r->nat), nat); | error += apply_redirspec(&(r->nat), nat); | ||||
error += apply_rdr_ports(r, &(r->rdr), rdr); | |||||
error += apply_redirspec(&(r->rdr), rdr); | error += apply_redirspec(&(r->rdr), rdr); | ||||
error += apply_redirspec(&(r->route), route); | |||||
r->nat.proxy_port[0] = PF_NAT_PROXY_PORT_LOW; | if (nat && nat->binat) | ||||
r->nat.proxy_port[1] = PF_NAT_PROXY_PORT_HIGH; | error += check_binat_redirspec(src_host, r, af); | ||||
} | |||||
if (rule_consistent(r, anchor_call[0]) < 0 || error) | if (rule_consistent(r, anchor_call[0]) < 0 || error) | ||||
yyerror("skipping rule due to errors"); | yyerror("skipping rule due to errors"); | ||||
else { | else { | ||||
r->nr = pf->astack[pf->asd]->match++; | r->nr = pf->astack[pf->asd]->match++; | ||||
pfctl_append_rule(pf, r, anchor_call); | pfctl_append_rule(pf, r, anchor_call); | ||||
added++; | added++; | ||||
} | } | ||||
/* Generate binat's matching inbound rule */ | |||||
if (!error && nat && nat->binat) { | |||||
struct pfctl_rule rdr_rule; | |||||
struct redirspec *rdr_redirspec; | |||||
struct node_host *rdr_dst_host; | |||||
add_binat_rdr_rule( | |||||
r, nat, src_hosts, | |||||
&rdr_rule, &rdr_redirspec, &rdr_dst_host); | |||||
expand_rule(&rdr_rule, true, interface, NULL, rdr_redirspec, | |||||
NULL, proto, src_os, dst_host, dst_port, | |||||
rdr_dst_host, src_port, uid, gid, rcv, icmp_type, | |||||
""); | |||||
} | |||||
if (osrch && src_host->addr.type == PF_ADDR_DYNIFTL) { | if (osrch && src_host->addr.type == PF_ADDR_DYNIFTL) { | ||||
free(src_host); | free(src_host); | ||||
src_host = osrch; | src_host = osrch; | ||||
} | } | ||||
if (odsth && dst_host->addr.type == PF_ADDR_DYNIFTL) { | if (odsth && dst_host->addr.type == PF_ADDR_DYNIFTL) { | ||||
free(dst_host); | free(dst_host); | ||||
dst_host = odsth; | dst_host = odsth; | ||||
} | } | ||||
)))))))))); | )))))))))); | ||||
if (!keeprule) { | |||||
FREE_LIST(struct node_if, interfaces); | FREE_LIST(struct node_if, interfaces); | ||||
FREE_LIST(struct node_proto, protos); | FREE_LIST(struct node_proto, protos); | ||||
FREE_LIST(struct node_host, src_hosts); | FREE_LIST(struct node_host, src_hosts); | ||||
FREE_LIST(struct node_port, src_ports); | FREE_LIST(struct node_port, src_ports); | ||||
FREE_LIST(struct node_os, src_oses); | FREE_LIST(struct node_os, src_oses); | ||||
FREE_LIST(struct node_host, dst_hosts); | FREE_LIST(struct node_host, dst_hosts); | ||||
FREE_LIST(struct node_port, dst_ports); | FREE_LIST(struct node_port, dst_ports); | ||||
FREE_LIST(struct node_uid, uids); | FREE_LIST(struct node_uid, uids); | ||||
FREE_LIST(struct node_gid, gids); | FREE_LIST(struct node_gid, gids); | ||||
FREE_LIST(struct node_icmp, icmp_types); | FREE_LIST(struct node_icmp, icmp_types); | ||||
if (nat) { | if (nat) { | ||||
FREE_LIST(struct node_host, nat->host); | FREE_LIST(struct node_host, nat->host); | ||||
free(nat); | free(nat); | ||||
} | } | ||||
if (rdr) { | if (rdr) { | ||||
FREE_LIST(struct node_host, rdr->host); | FREE_LIST(struct node_host, rdr->host); | ||||
free(rdr); | free(rdr); | ||||
} | } | ||||
if (route) { | if (route) { | ||||
FREE_LIST(struct node_host, route->host); | FREE_LIST(struct node_host, route->host); | ||||
free(route); | free(route); | ||||
} | } | ||||
} | |||||
if (!added) | if (!added) | ||||
yyerror("rule expands to no valid combination"); | yyerror("rule expands to no valid combination"); | ||||
} | } | ||||
int | int | ||||
expand_skip_interface(struct node_if *interfaces) | expand_skip_interface(struct node_if *interfaces) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 68 Lines • ▼ Show 20 Lines | static const struct keywords keywords[] = { | ||||
{ "allow-related", ALLOW_RELATED}, | { "allow-related", ALLOW_RELATED}, | ||||
{ "altq", ALTQ}, | { "altq", ALTQ}, | ||||
{ "anchor", ANCHOR}, | { "anchor", ANCHOR}, | ||||
{ "antispoof", ANTISPOOF}, | { "antispoof", ANTISPOOF}, | ||||
{ "any", ANY}, | { "any", ANY}, | ||||
{ "bandwidth", BANDWIDTH}, | { "bandwidth", BANDWIDTH}, | ||||
{ "binat", BINAT}, | { "binat", BINAT}, | ||||
{ "binat-anchor", BINATANCHOR}, | { "binat-anchor", BINATANCHOR}, | ||||
{ "binat-to", BINATTO}, | |||||
{ "bitmask", BITMASK}, | { "bitmask", BITMASK}, | ||||
{ "block", BLOCK}, | { "block", BLOCK}, | ||||
{ "block-policy", BLOCKPOLICY}, | { "block-policy", BLOCKPOLICY}, | ||||
{ "bridge-to", BRIDGE_TO}, | { "bridge-to", BRIDGE_TO}, | ||||
{ "buckets", BUCKETS}, | { "buckets", BUCKETS}, | ||||
{ "cbq", CBQ}, | { "cbq", CBQ}, | ||||
{ "code", CODE}, | { "code", CODE}, | ||||
{ "codelq", CODEL}, | { "codelq", CODEL}, | ||||
▲ Show 20 Lines • Show All 47 Lines • ▼ Show 20 Lines | static const struct keywords keywords[] = { | ||||
{ "max-src-conn", MAXSRCCONN}, | { "max-src-conn", MAXSRCCONN}, | ||||
{ "max-src-conn-rate", MAXSRCCONNRATE}, | { "max-src-conn-rate", MAXSRCCONNRATE}, | ||||
{ "max-src-nodes", MAXSRCNODES}, | { "max-src-nodes", MAXSRCNODES}, | ||||
{ "max-src-states", MAXSRCSTATES}, | { "max-src-states", MAXSRCSTATES}, | ||||
{ "min-ttl", MINTTL}, | { "min-ttl", MINTTL}, | ||||
{ "modulate", MODULATE}, | { "modulate", MODULATE}, | ||||
{ "nat", NAT}, | { "nat", NAT}, | ||||
{ "nat-anchor", NATANCHOR}, | { "nat-anchor", NATANCHOR}, | ||||
{ "nat-to", NATTO}, | |||||
{ "no", NO}, | { "no", NO}, | ||||
{ "no-df", NODF}, | { "no-df", NODF}, | ||||
{ "no-route", NOROUTE}, | { "no-route", NOROUTE}, | ||||
{ "no-sync", NOSYNC}, | { "no-sync", NOSYNC}, | ||||
{ "on", ON}, | { "on", ON}, | ||||
{ "optimization", OPTIMIZATION}, | { "optimization", OPTIMIZATION}, | ||||
{ "os", OS}, | { "os", OS}, | ||||
{ "out", OUT}, | { "out", OUT}, | ||||
{ "overload", OVERLOAD}, | { "overload", OVERLOAD}, | ||||
{ "pass", PASS}, | { "pass", PASS}, | ||||
{ "pflow", PFLOW}, | { "pflow", PFLOW}, | ||||
{ "port", PORT}, | { "port", PORT}, | ||||
{ "prio", PRIO}, | { "prio", PRIO}, | ||||
{ "priority", PRIORITY}, | { "priority", PRIORITY}, | ||||
{ "priq", PRIQ}, | { "priq", PRIQ}, | ||||
{ "probability", PROBABILITY}, | { "probability", PROBABILITY}, | ||||
{ "proto", PROTO}, | { "proto", PROTO}, | ||||
{ "qlimit", QLIMIT}, | { "qlimit", QLIMIT}, | ||||
{ "queue", QUEUE}, | { "queue", QUEUE}, | ||||
{ "quick", QUICK}, | { "quick", QUICK}, | ||||
{ "random", RANDOM}, | { "random", RANDOM}, | ||||
{ "random-id", RANDOMID}, | { "random-id", RANDOMID}, | ||||
{ "rdr", RDR}, | { "rdr", RDR}, | ||||
{ "rdr-anchor", RDRANCHOR}, | { "rdr-anchor", RDRANCHOR}, | ||||
{ "rdr-to", RDRTO}, | |||||
{ "realtime", REALTIME}, | { "realtime", REALTIME}, | ||||
{ "reassemble", REASSEMBLE}, | { "reassemble", REASSEMBLE}, | ||||
{ "received-on", RECEIVEDON}, | { "received-on", RECEIVEDON}, | ||||
{ "reply-to", REPLYTO}, | { "reply-to", REPLYTO}, | ||||
{ "require-order", REQUIREORDER}, | { "require-order", REQUIREORDER}, | ||||
{ "return", RETURN}, | { "return", RETURN}, | ||||
{ "return-icmp", RETURNICMP}, | { "return-icmp", RETURNICMP}, | ||||
{ "return-icmp6", RETURNICMP6}, | { "return-icmp6", RETURNICMP6}, | ||||
▲ Show 20 Lines • Show All 905 Lines • Show Last 20 Lines |
Is it worth factoring this out into a pfctl_init_rule() or something?