aboutsummaryrefslogtreecommitdiff
path: root/sbin/pfctl
diff options
context:
space:
mode:
Diffstat (limited to 'sbin/pfctl')
-rw-r--r--sbin/pfctl/Makefile1
-rw-r--r--sbin/pfctl/parse.y2034
-rw-r--r--sbin/pfctl/pf_print_state.c144
-rw-r--r--sbin/pfctl/pf_ruleset.c1
-rw-r--r--sbin/pfctl/pfctl.8241
-rw-r--r--sbin/pfctl/pfctl.c1579
-rw-r--r--sbin/pfctl/pfctl.h80
-rw-r--r--sbin/pfctl/pfctl_optimize.c178
-rw-r--r--sbin/pfctl/pfctl_osfp.c33
-rw-r--r--sbin/pfctl/pfctl_parser.c644
-rw-r--r--sbin/pfctl/pfctl_parser.h84
-rw-r--r--sbin/pfctl/pfctl_radix.c185
-rw-r--r--sbin/pfctl/pfctl_table.c367
-rw-r--r--sbin/pfctl/tests/Makefile3
-rw-r--r--sbin/pfctl/tests/Makefile.depend5
-rw-r--r--sbin/pfctl/tests/files/Makefile3
-rw-r--r--sbin/pfctl/tests/files/pf0004.ok24
-rw-r--r--sbin/pfctl/tests/files/pf0016.in6
-rw-r--r--sbin/pfctl/tests/files/pf0016.ok4
-rw-r--r--sbin/pfctl/tests/files/pf0018.in18
-rw-r--r--sbin/pfctl/tests/files/pf0018.ok19
-rw-r--r--sbin/pfctl/tests/files/pf0019.in4
-rw-r--r--sbin/pfctl/tests/files/pf0019.ok9
-rw-r--r--sbin/pfctl/tests/files/pf0020.in4
-rw-r--r--sbin/pfctl/tests/files/pf0020.ok12
-rw-r--r--sbin/pfctl/tests/files/pf0048.in12
-rw-r--r--sbin/pfctl/tests/files/pf0048.ok8
-rw-r--r--sbin/pfctl/tests/files/pf0069.in3
-rw-r--r--sbin/pfctl/tests/files/pf0069.ok1
-rw-r--r--sbin/pfctl/tests/files/pf0070.in3
-rw-r--r--sbin/pfctl/tests/files/pf0070.ok1
-rw-r--r--sbin/pfctl/tests/files/pf0071.in3
-rw-r--r--sbin/pfctl/tests/files/pf0071.ok1
-rw-r--r--sbin/pfctl/tests/files/pf0072.in3
-rw-r--r--sbin/pfctl/tests/files/pf0072.ok2
-rw-r--r--sbin/pfctl/tests/files/pf0084.in12
-rw-r--r--sbin/pfctl/tests/files/pf0084.ok3
-rw-r--r--sbin/pfctl/tests/files/pf0088.in2
-rw-r--r--sbin/pfctl/tests/files/pf0088.ok2
-rw-r--r--sbin/pfctl/tests/files/pf0098.in3
-rw-r--r--sbin/pfctl/tests/files/pf0098.ok1
-rw-r--r--sbin/pfctl/tests/files/pf0102.ok4
-rw-r--r--sbin/pfctl/tests/files/pf1002.in5
-rw-r--r--sbin/pfctl/tests/files/pf1002.ok5
-rw-r--r--sbin/pfctl/tests/files/pf1018.in1
-rw-r--r--sbin/pfctl/tests/files/pf1018.ok2
-rw-r--r--sbin/pfctl/tests/files/pf1019.in1
-rw-r--r--sbin/pfctl/tests/files/pf1019.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1020.in3
-rw-r--r--sbin/pfctl/tests/files/pf1020.include4
-rw-r--r--sbin/pfctl/tests/files/pf1020.ok2
-rw-r--r--sbin/pfctl/tests/files/pf1021.in1
-rw-r--r--sbin/pfctl/tests/files/pf1021.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1022.in1
-rw-r--r--sbin/pfctl/tests/files/pf1022.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1023.in3
-rw-r--r--sbin/pfctl/tests/files/pf1023.ok3
-rw-r--r--sbin/pfctl/tests/files/pf1024.in1
-rw-r--r--sbin/pfctl/tests/files/pf1024.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1025.in1
-rw-r--r--sbin/pfctl/tests/files/pf1025.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1026.in1
-rw-r--r--sbin/pfctl/tests/files/pf1026.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1027.in1
-rw-r--r--sbin/pfctl/tests/files/pf1027.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1028.in1
-rw-r--r--sbin/pfctl/tests/files/pf1028.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1029.in1
-rw-r--r--sbin/pfctl/tests/files/pf1029.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1030.in1
-rw-r--r--sbin/pfctl/tests/files/pf1030.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1031.in1
-rw-r--r--sbin/pfctl/tests/files/pf1031.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1032.in1
-rw-r--r--sbin/pfctl/tests/files/pf1032.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1033.fail1
-rw-r--r--sbin/pfctl/tests/files/pf1033.in1
-rw-r--r--sbin/pfctl/tests/files/pf1034.fail1
-rw-r--r--sbin/pfctl/tests/files/pf1034.in1
-rw-r--r--sbin/pfctl/tests/files/pf1035.in1
-rw-r--r--sbin/pfctl/tests/files/pf1035.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1036.in1
-rw-r--r--sbin/pfctl/tests/files/pf1036.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1037.in1
-rw-r--r--sbin/pfctl/tests/files/pf1037.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1038.in1
-rw-r--r--sbin/pfctl/tests/files/pf1038.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1039.in1
-rw-r--r--sbin/pfctl/tests/files/pf1039.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1040.fail1
-rw-r--r--sbin/pfctl/tests/files/pf1040.in1
-rw-r--r--sbin/pfctl/tests/files/pf1040.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1041.in1
-rw-r--r--sbin/pfctl/tests/files/pf1041.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1042.fail1
-rw-r--r--sbin/pfctl/tests/files/pf1042.in1
-rw-r--r--sbin/pfctl/tests/files/pf1043.fail1
-rw-r--r--sbin/pfctl/tests/files/pf1043.in1
-rw-r--r--sbin/pfctl/tests/files/pf1044.in1
-rw-r--r--sbin/pfctl/tests/files/pf1044.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1045.in1
-rw-r--r--sbin/pfctl/tests/files/pf1045.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1046.fail1
-rw-r--r--sbin/pfctl/tests/files/pf1046.in1
-rw-r--r--sbin/pfctl/tests/files/pf1047.fail1
-rw-r--r--sbin/pfctl/tests/files/pf1047.in1
-rw-r--r--sbin/pfctl/tests/files/pf1048.in1
-rw-r--r--sbin/pfctl/tests/files/pf1048.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1049.in1
-rw-r--r--sbin/pfctl/tests/files/pf1049.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1050.in1
-rw-r--r--sbin/pfctl/tests/files/pf1050.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1051.in1
-rw-r--r--sbin/pfctl/tests/files/pf1051.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1052.in1
-rw-r--r--sbin/pfctl/tests/files/pf1052.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1053.in1
-rw-r--r--sbin/pfctl/tests/files/pf1053.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1054.in3
-rw-r--r--sbin/pfctl/tests/files/pf1054.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1055.in1
-rw-r--r--sbin/pfctl/tests/files/pf1055.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1056.in1
-rw-r--r--sbin/pfctl/tests/files/pf1056.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1057.in1
-rw-r--r--sbin/pfctl/tests/files/pf1057.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1058.in1
-rw-r--r--sbin/pfctl/tests/files/pf1058.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1059.in1
-rw-r--r--sbin/pfctl/tests/files/pf1059.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1060.in1
-rw-r--r--sbin/pfctl/tests/files/pf1060.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1061.in1
-rw-r--r--sbin/pfctl/tests/files/pf1061.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1062.in1
-rw-r--r--sbin/pfctl/tests/files/pf1062.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1063.in1
-rw-r--r--sbin/pfctl/tests/files/pf1063.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1064.in1
-rw-r--r--sbin/pfctl/tests/files/pf1064.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1065.in1
-rw-r--r--sbin/pfctl/tests/files/pf1065.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1066.in1
-rw-r--r--sbin/pfctl/tests/files/pf1066.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1067.fail1
-rw-r--r--sbin/pfctl/tests/files/pf1067.in1
-rw-r--r--sbin/pfctl/tests/files/pf1068.in1
-rw-r--r--sbin/pfctl/tests/files/pf1068.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1069.in1
-rw-r--r--sbin/pfctl/tests/files/pf1069.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1070.fail1
-rw-r--r--sbin/pfctl/tests/files/pf1070.in2
-rw-r--r--sbin/pfctl/tests/files/pf1070.include2
-rw-r--r--sbin/pfctl/tests/files/pf1071.in1
-rw-r--r--sbin/pfctl/tests/files/pf1071.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1072.fail1
-rw-r--r--sbin/pfctl/tests/files/pf1072.in1
-rw-r--r--sbin/pfctl/tests/files/pf1073.in1
-rw-r--r--sbin/pfctl/tests/files/pf1073.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1074.fail1
-rw-r--r--sbin/pfctl/tests/files/pf1074.in1
-rw-r--r--sbin/pfctl/tests/files/pf1075.in1
-rw-r--r--sbin/pfctl/tests/files/pf1075.ok1
-rwxr-xr-xsbin/pfctl/tests/macro.sh1
-rw-r--r--sbin/pfctl/tests/pfctl_test.c209
-rw-r--r--sbin/pfctl/tests/pfctl_test_list.inc58
166 files changed, 3977 insertions, 2181 deletions
diff --git a/sbin/pfctl/Makefile b/sbin/pfctl/Makefile
index c9108f702f29..08ca9a7af81b 100644
--- a/sbin/pfctl/Makefile
+++ b/sbin/pfctl/Makefile
@@ -1,4 +1,3 @@
-
.include <src.opts.mk>
PACKAGE=pf
diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y
index 041dcb0587b3..0f7702fc4630 100644
--- a/sbin/pfctl/parse.y
+++ b/sbin/pfctl/parse.y
@@ -89,9 +89,13 @@ static struct file {
TAILQ_ENTRY(file) entry;
FILE *stream;
char *name;
+ size_t ungetpos;
+ size_t ungetsize;
+ u_char *ungetbuf;
+ int eof_reached;
int lineno;
int errors;
-} *file;
+} *file, *topfile;
struct file *pushfile(const char *, int);
int popfile(void);
int check_file_secrecy(int, const char *);
@@ -100,8 +104,9 @@ int yylex(void);
int yyerror(const char *, ...);
int kw_cmp(const void *, const void *);
int lookup(char *);
+int igetc(void);
int lgetc(int);
-int lungetc(int);
+void lungetc(int);
int findeol(void);
static TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead);
@@ -161,8 +166,8 @@ struct node_gid {
};
struct node_icmp {
- u_int8_t code;
- u_int8_t type;
+ uint16_t code;
+ uint16_t type;
u_int8_t proto;
struct node_icmp *next;
struct node_icmp *tail;
@@ -172,7 +177,8 @@ enum { PF_STATE_OPT_MAX, PF_STATE_OPT_NOSYNC, PF_STATE_OPT_SRCTRACK,
PF_STATE_OPT_MAX_SRC_STATES, PF_STATE_OPT_MAX_SRC_CONN,
PF_STATE_OPT_MAX_SRC_CONN_RATE, PF_STATE_OPT_MAX_SRC_NODES,
PF_STATE_OPT_OVERLOAD, PF_STATE_OPT_STATELOCK,
- PF_STATE_OPT_TIMEOUT, PF_STATE_OPT_SLOPPY, };
+ PF_STATE_OPT_TIMEOUT, PF_STATE_OPT_SLOPPY,
+ PF_STATE_OPT_PFLOW, PF_STATE_OPT_ALLOW_RELATED };
enum { PF_SRCTRACK_NONE, PF_SRCTRACK, PF_SRCTRACK_GLOBAL, PF_SRCTRACK_RULE };
@@ -221,6 +227,33 @@ struct node_qassign {
char *pqname;
};
+struct range {
+ int a;
+ int b;
+ int t;
+};
+
+static struct pool_opts {
+ int marker;
+#define POM_TYPE 0x01
+#define POM_STICKYADDRESS 0x02
+#define POM_ENDPI 0x04
+#define POM_IPV6NH 0x08
+ u_int8_t opts;
+ int type;
+ int staticport;
+ struct pf_poolhashkey *key;
+ struct pf_mape_portset mape;
+} pool_opts;
+
+struct redirspec {
+ struct node_host *host;
+ struct range rport;
+ struct pool_opts pool_opts;
+ sa_family_t af;
+ bool binat;
+};
+
static struct filter_opts {
int marker;
#define FOM_FLAGS 0x0001
@@ -230,16 +263,17 @@ static struct filter_opts {
#define FOM_SRCTRACK 0x0010
#define FOM_MINTTL 0x0020
#define FOM_MAXMSS 0x0040
-#define FOM_AFTO 0x0080 /* not yet implemmented */
+#define FOM_AFTO 0x0080
#define FOM_SETTOS 0x0100
#define FOM_SCRUB_TCP 0x0200
#define FOM_SETPRIO 0x0400
-#define FOM_ONCE 0x1000 /* not yet implemmented */
+#define FOM_ONCE 0x1000
#define FOM_PRIO 0x2000
#define FOM_SETDELAY 0x4000
#define FOM_FRAGCACHE 0x8000 /* does not exist in OpenBSD */
struct node_uid *uid;
struct node_gid *gid;
+ struct node_if *rcv;
struct {
u_int8_t b1;
u_int8_t b2;
@@ -272,12 +306,19 @@ static struct filter_opts {
struct node_host *addr;
u_int16_t port;
} divert;
+ struct redirspec *nat;
+ struct redirspec *rdr;
/* new-style scrub opts */
int nodf;
int minttl;
int settos;
int randomid;
int max_mss;
+ struct {
+ uint32_t limit;
+ uint32_t seconds;
+ } pktrate;
+ int max_pkt_size;
} filter_opts;
static struct antispoof_opts {
@@ -321,32 +362,21 @@ static struct table_opts {
struct node_tinithead init_nodes;
} table_opts;
-static struct pool_opts {
- int marker;
-#define POM_TYPE 0x01
-#define POM_STICKYADDRESS 0x02
- u_int8_t opts;
- int type;
- int staticport;
- struct pf_poolhashkey *key;
- struct pf_mape_portset mape;
-
-} pool_opts;
-
static struct codel_opts codel_opts;
static struct node_hfsc_opts hfsc_opts;
static struct node_fairq_opts fairq_opts;
static struct node_state_opt *keep_state_defaults = NULL;
static struct pfctl_watermarks syncookie_opts;
+int validate_range(uint8_t, uint16_t, uint16_t);
int disallow_table(struct node_host *, const char *);
int disallow_urpf_failed(struct node_host *, const char *);
int disallow_alias(struct node_host *, const char *);
-int rule_consistent(struct pfctl_rule *, int);
-int filter_consistent(struct pfctl_rule *, int);
+int rule_consistent(struct pfctl_rule *);
+int filter_consistent(struct pfctl_rule *);
int nat_consistent(struct pfctl_rule *);
int rdr_consistent(struct pfctl_rule *);
-int process_tabledef(char *, struct table_opts *);
+int process_tabledef(char *, struct table_opts *, int);
void expand_label_str(char *, size_t, const char *, const char *);
void expand_label_if(const char *, char *, size_t, const char *);
void expand_label_addr(const char *, char *, size_t, sa_family_t,
@@ -361,11 +391,19 @@ void expand_eth_rule(struct pfctl_eth_rule *,
struct node_mac *, struct node_mac *,
struct node_host *, struct node_host *, const char *,
const char *);
-void expand_rule(struct pfctl_rule *, struct node_if *,
- 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_icmp *, 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 *);
+int check_binat_redirspec(struct node_host *, struct pfctl_rule *, sa_family_t);
+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 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 *,
+ struct node_icmp *);
int expand_altq(struct pf_altq *, struct node_if *,
struct node_queue *, struct node_queue_bw bwspec,
struct node_queue_opt *);
@@ -382,15 +420,19 @@ int rt_tableid_max(void);
void mv_rules(struct pfctl_ruleset *, struct pfctl_ruleset *);
void mv_eth_rules(struct pfctl_eth_ruleset *, struct pfctl_eth_ruleset *);
+void mv_tables(struct pfctl *, struct pfr_ktablehead *,
+ struct pfctl_anchor *, struct pfctl_anchor *);
void decide_address_family(struct node_host *, sa_family_t *);
void remove_invalid_hosts(struct node_host **, sa_family_t *);
int invalid_redirect(struct node_host *, sa_family_t);
u_int16_t parseicmpspec(char *, sa_family_t);
int kw_casecmp(const void *, const void *);
int map_tos(char *string, int *);
+int filteropts_to_rule(struct pfctl_rule *, struct filter_opts *);
struct node_mac* node_mac_from_string(const char *);
struct node_mac* node_mac_from_string_masklen(const char *, int);
struct node_mac* node_mac_from_string_mask(const char *, const char *);
+static bool pfctl_setup_anchor(struct pfctl_rule *, struct pfctl *, char *);
static TAILQ_HEAD(loadanchorshead, loadanchors)
loadanchorshead = TAILQ_HEAD_INITIALIZER(loadanchorshead);
@@ -414,11 +456,7 @@ typedef struct {
u_int16_t w;
u_int16_t w2;
} b;
- struct range {
- int a;
- int b;
- int t;
- } range;
+ struct range range;
struct node_if *interface;
struct node_proto *proto;
struct node_etherproto *etherproto;
@@ -444,16 +482,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 {
- struct node_host *host;
- struct range rport;
- } *redirection;
+ struct redirspec *redirspec;
struct {
int action;
struct node_state_opt *options;
@@ -504,17 +536,18 @@ int parseport(char *, struct range *r, int);
%token REASSEMBLE ANCHOR NATANCHOR RDRANCHOR BINATANCHOR
%token SET OPTIMIZATION TIMEOUT LIMIT LOGINTERFACE BLOCKPOLICY FAILPOLICY
%token RANDOMID REQUIREORDER SYNPROXY FINGERPRINTS NOSYNC DEBUG SKIP HOSTID
-%token ANTISPOOF FOR INCLUDE KEEPCOUNTERS SYNCOOKIES L3
+%token ANTISPOOF FOR INCLUDE KEEPCOUNTERS SYNCOOKIES L3 MATCHES
%token ETHER
%token BITMASK RANDOM SOURCEHASH ROUNDROBIN STATICPORT PROBABILITY MAPEPORTSET
%token ALTQ CBQ CODEL PRIQ HFSC FAIRQ BANDWIDTH TBRSIZE LINKSHARE REALTIME
%token UPPERLIMIT QUEUE PRIORITY QLIMIT HOGS BUCKETS RTABLE TARGET INTERVAL
%token DNPIPE DNQUEUE RIDENTIFIER
-%token LOAD RULESET_OPTIMIZATION PRIO
-%token STICKYADDRESS MAXSRCSTATES MAXSRCNODES SOURCETRACK GLOBAL RULE
-%token MAXSRCCONN MAXSRCCONNRATE OVERLOAD FLUSH SLOPPY
+%token LOAD RULESET_OPTIMIZATION PRIO ONCE
+%token STICKYADDRESS ENDPI MAXSRCSTATES MAXSRCNODES SOURCETRACK GLOBAL RULE
+%token MAXSRCCONN MAXSRCCONNRATE OVERLOAD FLUSH SLOPPY PFLOW ALLOW_RELATED
%token TAGGED TAG IFBOUND FLOATING STATEPOLICY STATEDEFAULTS ROUTE SETTOS
-%token DIVERTTO DIVERTREPLY BRIDGE_TO
+%token DIVERTTO DIVERTREPLY BRIDGE_TO RECEIVEDON NE LE GE AFTO NATTO RDRTO
+%token BINATTO MAXPKTRATE MAXPKTSIZE IPV6NH
%token <v.string> STRING
%token <v.number> NUMBER
%token <v.i> PORTBINARY
@@ -538,14 +571,15 @@ int parseport(char *, struct range *r, int);
%type <v.fromto> fromto l3fromto
%type <v.peer> ipportspec from to
%type <v.host> ipspec toipspec xhost host dynaddr host_list
-%type <v.host> redir_host_list redirspec
-%type <v.host> route_host route_host_list routespec
+%type <v.host> redir_host redir_host_list routespec
+%type <v.host> route_host route_host_list
%type <v.os> os xos os_list
%type <v.port> portspec port_list port_item
%type <v.uid> uids uid_list uid_item
%type <v.gid> gids gid_list gid_item
%type <v.route> route
-%type <v.redirection> redirection redirpool
+%type <v.redirspec> no_port_redirspec port_redirspec route_redirspec
+%type <v.redirspec> binat_redirspec nat_redirspec
%type <v.string> label stringall tag anchorname
%type <v.string> string varstring numberstring
%type <v.keep_state> keep
@@ -688,10 +722,7 @@ option : SET REASSEMBLE yesno optnodf {
yyerror("hostid must be non-zero");
YYERROR;
}
- if (pfctl_set_hostid(pf, $3) != 0) {
- yyerror("error setting hostid %08x", $3);
- YYERROR;
- }
+ pfctl_set_hostid(pf, $3);
}
| SET BLOCKPOLICY DROP {
if (pf->opts & PF_OPT_VERBOSE)
@@ -762,7 +793,7 @@ option : SET REASSEMBLE yesno optnodf {
free($3);
YYERROR;
}
- if (pfctl_set_debug(pf, $3) != 0) {
+ if (pfctl_do_set_debug(pf, $3) != 0) {
yyerror("error setting debuglevel %s", $3);
free($3);
YYERROR;
@@ -883,6 +914,8 @@ varset : STRING '=' varstring {
if (isspace((unsigned char)*s)) {
yyerror("macro name cannot contain "
"whitespace");
+ free($1);
+ free($3);
YYERROR;
}
}
@@ -893,14 +926,36 @@ varset : STRING '=' varstring {
}
;
-anchorname : STRING { $$ = $1; }
+anchorname : STRING {
+ if ($1[0] == '\0') {
+ free($1);
+ yyerror("anchor name must not be empty");
+ YYERROR;
+ }
+ if (strlen(pf->anchor->path) + 1 +
+ strlen($1) >= PATH_MAX) {
+ free($1);
+ yyerror("anchor name is longer than %u",
+ PATH_MAX - 1);
+ YYERROR;
+ }
+ if ($1[0] == '_' || strstr($1, "/_") != NULL) {
+ free($1);
+ yyerror("anchor names beginning with '_' "
+ "are reserved for internal use");
+ YYERROR;
+ }
+ $$ = $1;
+ }
| /* empty */ { $$ = NULL; }
;
pfa_anchorlist : /* empty */
| pfa_anchorlist '\n'
+ | pfa_anchorlist tabledef '\n'
| pfa_anchorlist pfrule '\n'
| pfa_anchorlist anchorrule '\n'
+ | pfa_anchorlist include '\n'
;
pfa_anchor : '{'
@@ -909,6 +964,8 @@ pfa_anchor : '{'
struct pfctl_ruleset *rs;
/* stepping into a brace anchor */
+ if (pf->asd >= PFCTL_ANCHOR_STACK_DEPTH)
+ errx(1, "pfa_anchor: anchors too deep");
pf->asd++;
pf->bn++;
@@ -921,7 +978,7 @@ pfa_anchor : '{'
snprintf(ta, PF_ANCHOR_NAME_SIZE, "_%d", pf->bn);
rs = pf_find_or_create_ruleset(ta);
if (rs == NULL)
- err(1, "pfa_anchor: pf_find_or_create_ruleset");
+ err(1, "pfa_anchor: pf_find_or_create_ruleset (%s)", ta);
pf->astack[pf->asd] = rs->anchor;
pf->anchor = rs->anchor;
} '\n' pfa_anchorlist '}'
@@ -945,82 +1002,14 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto
YYERROR;
}
- if ($2 && ($2[0] == '_' || strstr($2, "/_") != NULL)) {
- free($2);
- yyerror("anchor names beginning with '_' "
- "are reserved for internal use");
+ pfctl_init_rule(&r);
+ if (! pfctl_setup_anchor(&r, pf, $2))
YYERROR;
- }
- memset(&r, 0, sizeof(r));
- if (pf->astack[pf->asd + 1]) {
- if ($2 && strchr($2, '/') != NULL) {
- free($2);
- yyerror("anchor paths containing '/' "
- "cannot be used for inline anchors.");
- YYERROR;
- }
-
- /* Move inline rules into relative location. */
- 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");
-
- if (pf->alast != r.anchor) {
- if (r.anchor->match) {
- yyerror("inline anchor '%s' "
- "already exists",
- r.anchor->name);
- YYERROR;
- }
- mv_rules(&pf->alast->ruleset,
- &r.anchor->ruleset);
- }
- pf_remove_if_empty_ruleset(&pf->alast->ruleset);
- pf->alast = r.anchor;
- } else {
- if (!$2) {
- yyerror("anchors without explicit "
- "rules must specify a name");
- YYERROR;
- }
- }
r.direction = $3;
r.quick = $4.quick;
r.af = $6;
- r.prob = $9.prob;
- r.rtableid = $9.rtableid;
- r.ridentifier = $9.ridentifier;
- if ($9.tag)
- if (strlcpy(r.tagname, $9.tag,
- PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) {
- yyerror("tag too long, max %u chars",
- PF_TAG_NAME_SIZE - 1);
- YYERROR;
- }
- if ($9.match_tag)
- if (strlcpy(r.match_tagname, $9.match_tag,
- PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) {
- yyerror("tag too long, max %u chars",
- PF_TAG_NAME_SIZE - 1);
- YYERROR;
- }
- r.match_tag_not = $9.match_tag_not;
- if (rule_label(&r, $9.label))
- YYERROR;
- for (int i = 0; i < PF_RULE_MAX_LABEL_COUNT; i++)
- free($9.label[i]);
- r.flags = $9.flags.b1;
- r.flagset = $9.flags.b2;
- if (($9.flags.b1 & $9.flags.b2) != $9.flags.b1) {
- yyerror("flags always false");
- YYERROR;
- }
if ($9.flags.b1 || $9.flags.b2 || $8.src_os) {
for (proto = $7; proto != NULL &&
proto->proto != IPPROTO_TCP;
@@ -1038,7 +1027,8 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto
}
}
- r.tos = $9.tos;
+ if (filteropts_to_rule(&r, &$9))
+ YYERROR;
if ($9.keep.action) {
yyerror("cannot specify state handling "
@@ -1046,33 +1036,12 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto
YYERROR;
}
- if ($9.match_tag)
- if (strlcpy(r.match_tagname, $9.match_tag,
- PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) {
- yyerror("tag too long, max %u chars",
- PF_TAG_NAME_SIZE - 1);
- 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);
- expand_rule(&r, $5, NULL, $7, $8.src_os,
- $8.src.host, $8.src.port, $8.dst.host, $8.dst.port,
- $9.uid, $9.gid, $9.icmpspec,
- pf->astack[pf->asd + 1] ? pf->alast->name : $2);
+ expand_rule(&r, false, $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);
free($2);
pf->astack[pf->asd + 1] = NULL;
}
@@ -1084,7 +1053,10 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto
YYERROR;
}
- memset(&r, 0, sizeof(r));
+ pfctl_init_rule(&r);
+ if (! pfctl_setup_anchor(&r, pf, $2))
+ YYERROR;
+
r.action = PF_NAT;
r.af = $4;
r.rtableid = $7;
@@ -1092,9 +1064,9 @@ 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, $5, $6.src_os,
- $6.src.host, $6.src.port, $6.dst.host, $6.dst.port,
- 0, 0, 0, $2);
+ expand_rule(&r, false, $3, NULL, NULL, NULL,
+ $5, $6.src_os, $6.src.host, $6.src.port, $6.dst.host,
+ $6.dst.port, 0, 0, 0, 0);
free($2);
}
| RDRANCHOR string interface af proto fromto rtable {
@@ -1105,7 +1077,10 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto
YYERROR;
}
- memset(&r, 0, sizeof(r));
+ pfctl_init_rule(&r);
+ if (! pfctl_setup_anchor(&r, pf, $2))
+ YYERROR;
+
r.action = PF_RDR;
r.af = $4;
r.rtableid = $7;
@@ -1134,9 +1109,9 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto
r.dst.port_op = $6.dst.port->op;
}
- expand_rule(&r, $3, NULL, $5, $6.src_os,
- $6.src.host, $6.src.port, $6.dst.host, $6.dst.port,
- 0, 0, 0, $2);
+ expand_rule(&r, false, $3, NULL, NULL, NULL,
+ $5, $6.src_os, $6.src.host, $6.src.port, $6.dst.host,
+ $6.dst.port, 0, 0, 0, 0);
free($2);
}
| BINATANCHOR string interface af proto fromto rtable {
@@ -1147,7 +1122,10 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto
YYERROR;
}
- memset(&r, 0, sizeof(r));
+ pfctl_init_rule(&r);
+ if (! pfctl_setup_anchor(&r, pf, $2))
+ YYERROR;
+
r.action = PF_BINAT;
r.af = $4;
r.rtableid = $7;
@@ -1171,19 +1149,16 @@ 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);
- pfctl_append_rule(pf, &r, $2);
+ pfctl_append_rule(pf, &r);
free($2);
}
;
-loadrule : LOAD ANCHOR string FROM string {
+loadrule : LOAD ANCHOR anchorname FROM string {
struct loadanchors *loadanchor;
- if (strlen(pf->anchor->name) + 1 +
- strlen($3) >= MAXPATHLEN) {
- yyerror("anchorname %s too long, max %u\n",
- $3, MAXPATHLEN - 1);
- free($3);
+ if ($3 == NULL) {
+ yyerror("anchor name is missing");
YYERROR;
}
loadanchor = calloc(1, sizeof(struct loadanchors));
@@ -1194,7 +1169,7 @@ loadrule : LOAD ANCHOR string FROM string {
err(1, "loadrule: malloc");
if (pf->anchor->name[0])
snprintf(loadanchor->anchorname, MAXPATHLEN,
- "%s/%s", pf->anchor->name, $3);
+ "%s/%s", pf->anchor->path, $3);
else
strlcpy(loadanchor->anchorname, $3, MAXPATHLEN);
if ((loadanchor->filename = strdup($5)) == NULL)
@@ -1229,7 +1204,7 @@ etherrule : ETHER action dir quick interface bridge etherproto etherfromto l3fro
r.direction = $3;
r.quick = $4.quick;
if ($10.tag != NULL)
- memcpy(&r.tagname, $10.tag, sizeof(r.tagname));
+ strlcpy(r.tagname, $10.tag, sizeof(r.tagname));
if ($10.match_tag)
if (strlcpy(r.match_tagname, $10.match_tag,
PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) {
@@ -1239,7 +1214,7 @@ etherrule : ETHER action dir quick interface bridge etherproto etherfromto l3fro
}
r.match_tag_not = $10.match_tag_not;
if ($10.queues.qname != NULL)
- memcpy(&r.qname, $10.queues.qname, sizeof(r.qname));
+ strlcpy(r.qname, $10.queues.qname, sizeof(r.qname));
r.dnpipe = $10.dnpipe;
r.dnflags = $10.free_flags;
if (eth_rule_label(&r, $10.label))
@@ -1265,6 +1240,8 @@ etherpfa_anchor : '{'
struct pfctl_eth_ruleset *rs;
/* steping into a brace anchor */
+ if (pf->asd >= PFCTL_ANCHOR_STACK_DEPTH)
+ errx(1, "pfa_anchor: anchors too deep");
pf->asd++;
pf->bn++;
@@ -1412,7 +1389,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;
@@ -1457,9 +1434,9 @@ 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, $6, $7.src_os,
- $7.src.host, $7.src.port, $7.dst.host, $7.dst.port,
- NULL, NULL, NULL, "");
+ expand_rule(&r, false, $4, NULL, NULL, NULL,
+ $6, $7.src_os, $7.src.host, $7.src.port, $7.dst.host,
+ $7.dst.port, NULL, NULL, NULL, NULL);
}
;
@@ -1574,7 +1551,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;
@@ -1622,9 +1599,9 @@ antispoof : ANTISPOOF logquick antispoof_ifspc af antispoof_opts {
}
if (h != NULL)
- expand_rule(&r, j, NULL, NULL, NULL, h,
- NULL, NULL, NULL, NULL, NULL,
- NULL, "");
+ expand_rule(&r, false, 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));
@@ -1644,9 +1621,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, h, NULL, NULL,
- NULL, NULL, NULL, NULL, "");
+ expand_rule(&r, false, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, h, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL);
} else
free(hh);
}
@@ -1725,7 +1703,7 @@ tabledef : TABLE '<' STRING '>' table_opts {
YYERROR;
}
if (pf->loadopt & PFCTL_FLAG_TABLE)
- if (process_tabledef($3, &$5)) {
+ if (process_tabledef($3, &$5, pf->opts)) {
free($3);
YYERROR;
}
@@ -2358,7 +2336,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) {
@@ -2381,63 +2359,11 @@ pfrule : action dir logquick interface route af proto fromto
r.log = $3.log;
r.logif = $3.logif;
r.quick = $3.quick;
- r.prob = $9.prob;
- r.rtableid = $9.rtableid;
-
- if ($9.nodf)
- r.scrub_flags |= PFSTATE_NODF;
- if ($9.randomid)
- r.scrub_flags |= PFSTATE_RANDOMID;
- if ($9.minttl)
- r.min_ttl = $9.minttl;
- if ($9.max_mss)
- r.max_mss = $9.max_mss;
- if ($9.marker & FOM_SETTOS) {
- r.scrub_flags |= PFSTATE_SETTOS;
- r.set_tos = $9.settos;
- }
- if ($9.marker & FOM_SCRUB_TCP)
- r.scrub_flags |= PFSTATE_SCRUB_TCP;
-
- 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,
- PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) {
- yyerror("tag too long, max %u chars",
- PF_TAG_NAME_SIZE - 1);
- YYERROR;
- }
- if ($9.match_tag)
- if (strlcpy(r.match_tagname, $9.match_tag,
- PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) {
- yyerror("tag too long, max %u chars",
- PF_TAG_NAME_SIZE - 1);
- YYERROR;
- }
- r.match_tag_not = $9.match_tag_not;
- if (rule_label(&r, $9.label))
- YYERROR;
- for (int i = 0; i < PF_RULE_MAX_LABEL_COUNT; i++)
- free($9.label[i]);
- r.ridentifier = $9.ridentifier;
- r.flags = $9.flags.b1;
- r.flagset = $9.flags.b2;
- if (($9.flags.b1 & $9.flags.b2) != $9.flags.b1) {
- yyerror("flags always false");
+
+ if (filteropts_to_rule(&r, &$9))
YYERROR;
- }
+
if ($9.flags.b1 || $9.flags.b2 || $8.src_os) {
for (proto = $7; proto != NULL &&
proto->proto != IPPROTO_TCP;
@@ -2453,18 +2379,8 @@ pfrule : action dir logquick interface route af proto fromto
"apply to tcp");
YYERROR;
}
-#if 0
- if (($9.flags.b1 & parse_flags("S")) == 0 &&
- $8.src_os) {
- yyerror("OS fingerprinting requires "
- "the SYN TCP flag (flags S/SA)");
- YYERROR;
- }
-#endif
}
- r.tos = $9.tos;
- r.keep_state = $9.keep.action;
o = $9.keep.options;
/* 'keep state' by default on pass rules. */
@@ -2615,6 +2531,22 @@ pfrule : action dir logquick interface route af proto fromto
}
r.rule_flag |= PFRULE_STATESLOPPY;
break;
+ case PF_STATE_OPT_PFLOW:
+ if (r.rule_flag & PFRULE_PFLOW) {
+ yyerror("state pflow option: "
+ "multiple definitions");
+ YYERROR;
+ }
+ r.rule_flag |= PFRULE_PFLOW;
+ break;
+ case PF_STATE_OPT_ALLOW_RELATED:
+ if (r.rule_flag & PFRULE_ALLOW_RELATED) {
+ yyerror("state allow-related option: "
+ "multiple definitions");
+ YYERROR;
+ }
+ r.rule_flag |= PFRULE_ALLOW_RELATED;
+ break;
case PF_STATE_OPT_TIMEOUT:
if (o->data.timeout.number ==
PFTM_ADAPTIVE_START ||
@@ -2682,10 +2614,6 @@ pfrule : action dir logquick interface route af proto fromto
if (r.keep_state && !statelock)
r.rule_flag |= default_statelock;
- if ($9.fragment)
- r.rule_flag |= PFRULE_FRAGMENT;
- r.allow_opts = $9.allowopts;
-
decide_address_family($8.src.host, &r.af);
decide_address_family($8.dst.host, &r.af);
@@ -2696,62 +2624,18 @@ pfrule : action dir logquick interface route af proto fromto
YYERROR;
}
r.rt = $5.rt;
- r.rpool.opts = $5.pool_opts;
- if ($5.key != NULL)
- memcpy(&r.rpool.key, $5.key,
- sizeof(struct pf_poolhashkey));
- }
- if (r.rt) {
- decide_address_family($5.host, &r.af);
- remove_invalid_hosts(&$5.host, &r.af);
- if ($5.host == NULL) {
- yyerror("no routing address with "
- "matching address family found.");
- YYERROR;
- }
- if ((r.rpool.opts & PF_POOL_TYPEMASK) ==
- PF_POOL_NONE && ($5.host->next != NULL ||
- $5.host->addr.type == PF_ADDR_TABLE ||
- DYNIF_MULTIADDR($5.host->addr)))
- r.rpool.opts |= PF_POOL_ROUNDROBIN;
- if ((r.rpool.opts & PF_POOL_TYPEMASK) !=
- PF_POOL_ROUNDROBIN &&
- disallow_table($5.host, "tables are only "
- "supported in round-robin routing pools"))
- YYERROR;
- if ((r.rpool.opts & PF_POOL_TYPEMASK) !=
- PF_POOL_ROUNDROBIN &&
- disallow_alias($5.host, "interface (%s) "
- "is only supported in round-robin "
- "routing pools"))
- YYERROR;
- if ($5.host->next != NULL) {
- if ((r.rpool.opts & PF_POOL_TYPEMASK) !=
- PF_POOL_ROUNDROBIN) {
- yyerror("r.rpool.opts must "
- "be PF_POOL_ROUNDROBIN");
+
+ if (!($5.redirspec->pool_opts.opts & PF_POOL_IPV6NH)) {
+ decide_address_family($5.redirspec->host, &r.af);
+ if (!(r.rule_flag & PFRULE_AFTO))
+ remove_invalid_hosts(&($5.redirspec->host), &r.af);
+ if ($5.redirspec->host == NULL) {
+ yyerror("no routing address with "
+ "matching address family found.");
YYERROR;
}
}
}
- if ($9.queues.qname != NULL) {
- if (strlcpy(r.qname, $9.queues.qname,
- sizeof(r.qname)) >= sizeof(r.qname)) {
- yyerror("rule qname too long (max "
- "%d chars)", sizeof(r.qname)-1);
- YYERROR;
- }
- free($9.queues.qname);
- }
- if ($9.queues.pqname != NULL) {
- if (strlcpy(r.pqname, $9.queues.pqname,
- sizeof(r.pqname)) >= sizeof(r.pqname)) {
- yyerror("rule pqname too long (max "
- "%d chars)", sizeof(r.pqname)-1);
- YYERROR;
- }
- free($9.queues.pqname);
- }
#ifdef __FreeBSD__
r.divert.port = $9.divert.port;
#else
@@ -2790,9 +2674,40 @@ pfrule : action dir logquick interface route af proto fromto
r.free_flags |= PFRULE_DN_IS_QUEUE;
}
- expand_rule(&r, $4, $5.host, $7, $8.src_os,
- $8.src.host, $8.src.port, $8.dst.host, $8.dst.port,
- $9.uid, $9.gid, $9.icmpspec, "");
+ if ($9.marker & FOM_AFTO) {
+ 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, false, $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);
}
;
@@ -2927,6 +2842,14 @@ filter_opt : USER uids {
filter_opts.match_tag = $3;
filter_opts.match_tag_not = $1;
}
+ | not RECEIVEDON if_item {
+ if (filter_opts.rcv) {
+ yyerror("cannot respecify received-on");
+ YYERROR;
+ }
+ filter_opts.rcv = $3;
+ filter_opts.rcv->not = $1;
+ }
| PROBABILITY probability {
double p;
@@ -2957,7 +2880,7 @@ filter_opt : USER uids {
}
| DIVERTTO STRING PORT portplain {
#ifndef __FreeBSD__
- if ((filter_opts.divert.addr = host($2)) == NULL) {
+ if ((filter_opts.divert.addr = host($2, pf->opts)) == NULL) {
yyerror("could not parse divert address: %s",
$2);
free($2);
@@ -2999,6 +2922,94 @@ filter_opt : USER uids {
filter_opts.marker |= FOM_SCRUB_TCP;
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 {
+ if (filter_opts.nat) {
+ yyerror("cannot respecify af-to");
+ YYERROR;
+ }
+ if ($2 == 0) {
+ yyerror("no address family specified");
+ YYERROR;
+ }
+
+ filter_opts.nat = $4;
+ filter_opts.nat->af = $2;
+ remove_invalid_hosts(&($4->host), &(filter_opts.nat->af));
+ if ($4->host == NULL) {
+ yyerror("af-to addresses must be in the "
+ "target address family");
+ YYERROR;
+ }
+ filter_opts.marker |= FOM_AFTO;
+ }
+ | AFTO af FROM port_redirspec TO port_redirspec {
+ if (filter_opts.nat) {
+ yyerror("cannot respecify af-to");
+ YYERROR;
+ }
+ if ($2 == 0) {
+ yyerror("no address family specified");
+ YYERROR;
+ }
+ filter_opts.nat = $4;
+ filter_opts.nat->af = $2;
+ filter_opts.rdr = $6;
+ filter_opts.rdr->af = $2;
+ remove_invalid_hosts(&($4->host), &(filter_opts.nat->af));
+ remove_invalid_hosts(&($6->host), &(filter_opts.rdr->af));
+ if ($4->host == NULL || $6->host == NULL) {
+ yyerror("af-to addresses must be in the "
+ "target address family");
+ YYERROR;
+ }
+ filter_opts.marker |= FOM_AFTO;
+ }
+ | MAXPKTRATE NUMBER '/' NUMBER {
+ if ($2 < 0 || $2 > UINT_MAX ||
+ $4 < 0 || $4 > UINT_MAX) {
+ yyerror("only positive values permitted");
+ YYERROR;
+ }
+ if (filter_opts.pktrate.limit) {
+ yyerror("cannot respecify max-pkt-rate");
+ YYERROR;
+ }
+ filter_opts.pktrate.limit = $2;
+ filter_opts.pktrate.seconds = $4;
+ }
+ | MAXPKTSIZE NUMBER {
+ if ($2 < 0 || $2 > UINT16_MAX) {
+ yyerror("only positive values permitted");
+ YYERROR;
+ }
+ filter_opts.max_pkt_size = $2;
+ }
+ | ONCE {
+ filter_opts.marker |= FOM_ONCE;
+ }
| filter_sets
;
@@ -3204,8 +3215,8 @@ logopts : logopt { $$ = $1; }
;
logopt : ALL { $$.log = PF_LOG_ALL; $$.logif = 0; }
- | USER { $$.log = PF_LOG_SOCKET_LOOKUP; $$.logif = 0; }
- | GROUP { $$.log = PF_LOG_SOCKET_LOOKUP; $$.logif = 0; }
+ | MATCHES { $$.log = PF_LOG_MATCHES; $$.logif = 0; }
+ | USER { $$.log = PF_LOG_USER; $$.logif = 0; }
| TO string {
const char *errstr;
u_int i;
@@ -3265,6 +3276,15 @@ if_item : STRING {
$$->next = NULL;
$$->tail = $$;
}
+ | ANY {
+ $$ = calloc(1, sizeof(struct node_if));
+ if ($$ == NULL)
+ err(1, "if_item: calloc");
+ strlcpy($$->ifname, "any", sizeof($$->ifname));
+ $$->not = 0;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
;
af : /* empty */ { $$ = 0; }
@@ -3554,11 +3574,13 @@ toipspec : TO ipspec { $$ = $2; }
host_list : ipspec optnl { $$ = $1; }
| host_list comma ipspec optnl {
- if ($3 == NULL)
+ if ($1 == NULL) {
+ freehostlist($3);
$$ = $1;
- else if ($1 == NULL)
+ } else if ($3 == NULL) {
+ freehostlist($1);
$$ = $3;
- else {
+ } else {
$1->tail->next = $3;
$1->tail = $3->tail;
$$ = $1;
@@ -3594,7 +3616,7 @@ xhost : not host {
;
host : STRING {
- if (($$ = host($1)) == NULL) {
+ if (($$ = host($1, pf->opts)) == NULL) {
/* error. "any" is handled elsewhere */
free($1);
yyerror("could not parse host specification");
@@ -3606,7 +3628,8 @@ host : STRING {
| STRING '-' STRING {
struct node_host *b, *e;
- if ((b = host($1)) == NULL || (e = host($3)) == NULL) {
+ if ((b = host($1, pf->opts)) == NULL ||
+ (e = host($3, pf->opts)) == NULL) {
free($1);
free($3);
yyerror("could not parse host specification");
@@ -3615,9 +3638,9 @@ host : STRING {
if (b->af != e->af ||
b->addr.type != PF_ADDR_ADDRMASK ||
e->addr.type != PF_ADDR_ADDRMASK ||
- unmask(&b->addr.v.a.mask, b->af) !=
+ unmask(&b->addr.v.a.mask) !=
(b->af == AF_INET ? 32 : 128) ||
- unmask(&e->addr.v.a.mask, e->af) !=
+ unmask(&e->addr.v.a.mask) !=
(e->af == AF_INET ? 32 : 128) ||
b->next != NULL || b->not ||
e->next != NULL || e->not) {
@@ -3642,7 +3665,7 @@ host : STRING {
if (asprintf(&buf, "%s/%lld", $1, (long long)$3) == -1)
err(1, "host: asprintf");
free($1);
- if (($$ = host(buf)) == NULL) {
+ if (($$ = host(buf, pf->opts)) == NULL) {
/* error. "any" is handled elsewhere */
free(buf);
yyerror("could not parse host specification");
@@ -3660,7 +3683,7 @@ host : STRING {
if (asprintf(&buf, "%lld/%lld", $1, $3) == -1)
#endif
err(1, "host: asprintf");
- if (($$ = host(buf)) == NULL) {
+ if (($$ = host(buf, pf->opts)) == NULL) {
/* error. "any" is handled elsewhere */
free(buf);
yyerror("could not parse host specification");
@@ -3786,9 +3809,14 @@ port_item : portrange {
err(1, "port_item: calloc");
$$->port[0] = $1.a;
$$->port[1] = $1.b;
- if ($1.t)
+ if ($1.t) {
$$->op = PF_OP_RRG;
- else
+ if (validate_range($$->op, $$->port[0],
+ $$->port[1])) {
+ yyerror("invalid port range");
+ YYERROR;
+ }
+ } else
$$->op = PF_OP_EQ;
$$->next = NULL;
$$->tail = $$;
@@ -3805,6 +3833,10 @@ port_item : portrange {
$$->port[0] = $2.a;
$$->port[1] = $2.b;
$$->op = $1;
+ if (validate_range($$->op, $$->port[0], $$->port[1])) {
+ yyerror("invalid port range");
+ YYERROR;
+ }
$$->next = NULL;
$$->tail = $$;
}
@@ -3820,6 +3852,10 @@ port_item : portrange {
$$->port[0] = $1.a;
$$->port[1] = $3.a;
$$->op = $2;
+ if (validate_range($$->op, $$->port[0], $$->port[1])) {
+ yyerror("invalid port range");
+ YYERROR;
+ }
$$->next = NULL;
$$->tail = $$;
}
@@ -3866,7 +3902,7 @@ uid_item : uid {
$$->tail = $$;
}
| unaryop uid {
- if ($2 == UID_MAX && $1 != PF_OP_EQ && $1 != PF_OP_NE) {
+ if ($2 == -1 && $1 != PF_OP_EQ && $1 != PF_OP_NE) {
yyerror("user unknown requires operator = or "
"!=");
YYERROR;
@@ -3881,7 +3917,7 @@ uid_item : uid {
$$->tail = $$;
}
| uid PORTBINARY uid {
- if ($1 == UID_MAX || $3 == UID_MAX) {
+ if ($1 == -1 || $3 == -1) {
yyerror("user unknown requires operator = or "
"!=");
YYERROR;
@@ -3899,16 +3935,16 @@ uid_item : uid {
uid : STRING {
if (!strcmp($1, "unknown"))
- $$ = UID_MAX;
+ $$ = -1;
else {
- struct passwd *pw;
+ uid_t uid;
- if ((pw = getpwnam($1)) == NULL) {
+ if (uid_from_user($1, &uid) == -1) {
yyerror("unknown user %s", $1);
free($1);
YYERROR;
}
- $$ = pw->pw_uid;
+ $$ = uid;
}
free($1);
}
@@ -3944,7 +3980,7 @@ gid_item : gid {
$$->tail = $$;
}
| unaryop gid {
- if ($2 == GID_MAX && $1 != PF_OP_EQ && $1 != PF_OP_NE) {
+ if ($2 == -1 && $1 != PF_OP_EQ && $1 != PF_OP_NE) {
yyerror("group unknown requires operator = or "
"!=");
YYERROR;
@@ -3959,7 +3995,7 @@ gid_item : gid {
$$->tail = $$;
}
| gid PORTBINARY gid {
- if ($1 == GID_MAX || $3 == GID_MAX) {
+ if ($1 == -1 || $3 == -1) {
yyerror("group unknown requires operator = or "
"!=");
YYERROR;
@@ -3977,16 +4013,16 @@ gid_item : gid {
gid : STRING {
if (!strcmp($1, "unknown"))
- $$ = GID_MAX;
+ $$ = -1;
else {
- struct group *grp;
+ gid_t gid;
- if ((grp = getgrnam($1)) == NULL) {
+ if (gid_from_group($1, &gid) == -1) {
yyerror("unknown group %s", $1);
free($1);
YYERROR;
}
- $$ = grp->gr_gid;
+ $$ = gid;
}
free($1);
}
@@ -4193,7 +4229,7 @@ tos : STRING {
| NUMBER {
$$ = $1;
if ($$ < 0 || $$ > 255) {
- yyerror("illegal tos value %s", $1);
+ yyerror("illegal tos value %lu", $1);
YYERROR;
}
}
@@ -4368,6 +4404,22 @@ state_opt_item : MAXIMUM NUMBER {
$$->next = NULL;
$$->tail = $$;
}
+ | PFLOW {
+ $$ = calloc(1, sizeof(struct node_state_opt));
+ if ($$ == NULL)
+ err(1, "state_opt_item: calloc");
+ $$->type = PF_STATE_OPT_PFLOW;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ | ALLOW_RELATED {
+ $$ = calloc(1, sizeof(struct node_state_opt));
+ if ($$ == NULL)
+ err(1, "state_opt_item: calloc");
+ $$->type = PF_STATE_OPT_ALLOW_RELATED;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
| STRING NUMBER {
int i;
@@ -4440,7 +4492,7 @@ portstar : numberstring {
}
;
-redirspec : host { $$ = $1; }
+redir_host : host { $$ = $1; }
| '{' optnl redir_host_list '}' { $$ = $3; }
;
@@ -4452,20 +4504,42 @@ 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 : /* empty */ { $$ = NULL; }
+ | 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;
}
;
@@ -4576,6 +4650,22 @@ pool_opt : BITMASK {
pool_opts.marker |= POM_STICKYADDRESS;
pool_opts.opts |= PF_POOL_STICKYADDR;
}
+ | ENDPI {
+ if (pool_opts.marker & POM_ENDPI) {
+ yyerror("endpoint-independent cannot be redefined");
+ YYERROR;
+ }
+ pool_opts.marker |= POM_ENDPI;
+ pool_opts.opts |= PF_POOL_ENDPI;
+ }
+ | IPV6NH {
+ if (pool_opts.marker & POM_IPV6NH) {
+ yyerror("prefer-ipv6-nexthop cannot be redefined");
+ YYERROR;
+ }
+ pool_opts.marker |= POM_IPV6NH;
+ pool_opts.opts |= PF_POOL_IPV6NH;
+ }
| MAPEPORTSET number '/' number '/' number {
if (pool_opts.mape.offset) {
yyerror("map-e-portset cannot be redefined");
@@ -4608,18 +4698,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;
}
@@ -4660,14 +4750,15 @@ 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;
if (check_rulestate(PFCTL_STATE_NAT))
YYERROR;
- memset(&r, 0, sizeof(r));
+ pfctl_init_rule(&r);
r.action = $1.b1;
r.natpass = $1.b2;
@@ -4714,138 +4805,52 @@ natrule : nataction interface af proto fromto tag tagged rtable
"address'");
YYERROR;
}
+ if ($9->pool_opts.opts & PF_POOL_IPV6NH) {
+ yyerror("The prefer-ipv6-nexthop option "
+ "can't be used for nat/rdr/binat pools"
+ );
+ YYERROR;
+ }
if (!r.af && ! $9->host->ifindex)
r.af = $9->host->af;
remove_invalid_hosts(&$9->host, &r.af);
if (invalid_redirect($9->host, r.af))
YYERROR;
+ if ($9->host->addr.type == PF_ADDR_DYNIFTL) {
+ if (($9->host = gen_dynnode($9->host, r.af)) == NULL)
+ err(1, "calloc");
+ }
if (check_netmask($9->host, r.af))
YYERROR;
+ }
- r.rpool.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.rpool.proxy_port[1] =
- ntohs($9->rport.a) +
- (ntohs(
- $5.dst.port->port[1]) -
- ntohs(
- $5.dst.port->port[0]));
- } else
- r.rpool.proxy_port[1] =
- ntohs($9->rport.b);
- break;
- case PF_NAT:
- r.rpool.proxy_port[1] =
- ntohs($9->rport.b);
- if (!r.rpool.proxy_port[0] &&
- !r.rpool.proxy_port[1]) {
- r.rpool.proxy_port[0] =
- PF_NAT_PROXY_PORT_LOW;
- r.rpool.proxy_port[1] =
- PF_NAT_PROXY_PORT_HIGH;
- } else if (!r.rpool.proxy_port[1])
- r.rpool.proxy_port[1] =
- r.rpool.proxy_port[0];
- break;
- default:
- break;
- }
-
- r.rpool.opts = $10.type;
- if ((r.rpool.opts & PF_POOL_TYPEMASK) ==
- PF_POOL_NONE && ($9->host->next != NULL ||
- $9->host->addr.type == PF_ADDR_TABLE ||
- DYNIF_MULTIADDR($9->host->addr)))
- r.rpool.opts = PF_POOL_ROUNDROBIN;
- if ((r.rpool.opts & PF_POOL_TYPEMASK) !=
- PF_POOL_ROUNDROBIN &&
- disallow_table($9->host, "tables are only "
- "supported in round-robin redirection "
- "pools"))
- YYERROR;
- if ((r.rpool.opts & PF_POOL_TYPEMASK) !=
- PF_POOL_ROUNDROBIN &&
- disallow_alias($9->host, "interface (%s) "
- "is only supported in round-robin "
- "redirection pools"))
- YYERROR;
- if ($9->host->next != NULL) {
- if ((r.rpool.opts & PF_POOL_TYPEMASK) !=
- PF_POOL_ROUNDROBIN) {
- yyerror("only round-robin "
- "valid for multiple "
- "redirection addresses");
+ o = keep_state_defaults;
+ while (o) {
+ switch (o->type) {
+ case PF_STATE_OPT_PFLOW:
+ if (r.rule_flag & PFRULE_PFLOW) {
+ yyerror("state pflow option: "
+ "multiple definitions");
YYERROR;
}
+ r.rule_flag |= PFRULE_PFLOW;
+ break;
}
+ o = o->next;
}
- if ($10.key != NULL)
- memcpy(&r.rpool.key, $10.key,
- sizeof(struct pf_poolhashkey));
-
- if ($10.opts)
- r.rpool.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.rpool.proxy_port[0] !=
- PF_NAT_PROXY_PORT_LOW &&
- r.rpool.proxy_port[1] !=
- PF_NAT_PROXY_PORT_HIGH) {
- yyerror("the 'static-port' option can't"
- " be used when specifying a port"
- " range");
- YYERROR;
- }
- r.rpool.proxy_port[0] = 0;
- r.rpool.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.rpool.proxy_port[0] !=
- PF_NAT_PROXY_PORT_LOW &&
- r.rpool.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.rpool.mape = $10.mape;
- }
-
- expand_rule(&r, $2, $9 == NULL ? NULL : $9->host, $4,
+ expand_rule(&r, false, $2, NULL, $9, NULL, $4,
$5.src_os, $5.src.host, $5.src.port, $5.dst.host,
- $5.dst.port, 0, 0, 0, "");
- free($9);
+ $5.dst.port, 0, 0, 0, 0);
}
;
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;
+ struct pfctl_pooladdr *pa;
if (check_rulestate(PFCTL_STATE_NAT))
YYERROR;
@@ -4853,7 +4858,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\"");
@@ -4936,6 +4941,10 @@ binatrule : no BINAT natpasslog interface af proto FROM ipspec toipspec tag
yyerror("binat ip versions must match");
YYERROR;
}
+ if ($8->addr.type == PF_ADDR_DYNIFTL) {
+ if (($8 = gen_dynnode($8, binat.af)) == NULL)
+ err(1, "calloc");
+ }
if (check_netmask($8, binat.af))
YYERROR;
memcpy(&binat.src.addr, &$8->addr,
@@ -4951,6 +4960,10 @@ binatrule : no BINAT natpasslog interface af proto FROM ipspec toipspec tag
yyerror("binat ip versions must match");
YYERROR;
}
+ if ($9->addr.type == PF_ADDR_DYNIFTL) {
+ if (($9 = gen_dynnode($9, binat.af)) == NULL)
+ err(1, "calloc");
+ }
if (check_netmask($9, binat.af))
YYERROR;
memcpy(&binat.dst.addr, &$9->addr,
@@ -4980,6 +4993,10 @@ binatrule : no BINAT natpasslog interface af proto FROM ipspec toipspec tag
"a single address");
YYERROR;
}
+ if ($13->host->addr.type == PF_ADDR_DYNIFTL) {
+ if (($13->host = gen_dynnode($13->host, binat.af)) == NULL)
+ err(1, "calloc");
+ }
if (check_netmask($13->host, binat.af))
YYERROR;
@@ -4992,19 +5009,19 @@ binatrule : no BINAT natpasslog interface af proto FROM ipspec toipspec tag
YYERROR;
}
- TAILQ_INIT(&binat.rpool.list);
- pa = calloc(1, sizeof(struct pf_pooladdr));
+ pa = calloc(1, sizeof(struct pfctl_pooladdr));
if (pa == NULL)
err(1, "binat: calloc");
pa->addr = $13->host->addr;
pa->ifname[0] = 0;
- TAILQ_INSERT_TAIL(&binat.rpool.list,
+ pa->af = $13->host->af;
+ TAILQ_INSERT_TAIL(&binat.rdr.list,
pa, entries);
free($13);
}
- pfctl_append_rule(pf, &binat, "");
+ pfctl_append_rule(pf, &binat);
}
;
@@ -5055,13 +5072,6 @@ route_host : STRING {
route_host_list : route_host optnl { $$ = $1; }
| route_host_list comma route_host optnl {
- if ($1->af == 0)
- $1->af = $3->af;
- if ($1->af != $3->af) {
- yyerror("all pool addresses must be in the "
- "same address family");
- YYERROR;
- }
$1->tail->next = $3;
$1->tail = $3->tail;
$$ = $1;
@@ -5073,36 +5083,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;
}
;
@@ -5116,7 +5113,7 @@ timeout_spec : STRING NUMBER
yyerror("only positive values permitted");
YYERROR;
}
- if (pfctl_set_timeout(pf, $1, $2, 0) != 0) {
+ if (pfctl_apply_timeout(pf, $1, $2, 0) != 0) {
yyerror("unknown timeout %s", $1);
free($1);
YYERROR;
@@ -5130,7 +5127,7 @@ timeout_spec : STRING NUMBER
yyerror("only positive values permitted");
YYERROR;
}
- if (pfctl_set_timeout(pf, "interval", $2, 0) != 0)
+ if (pfctl_apply_timeout(pf, "interval", $2, 0) != 0)
YYERROR;
}
;
@@ -5149,7 +5146,7 @@ limit_spec : STRING NUMBER
yyerror("only positive values permitted");
YYERROR;
}
- if (pfctl_set_limit(pf, $1, $2) != 0) {
+ if (pfctl_apply_limit(pf, $1, $2) != 0) {
yyerror("unable to set limit %s %u", $1, $2);
free($1);
YYERROR;
@@ -5181,10 +5178,10 @@ yesno : NO { $$ = 0; }
;
unaryop : '=' { $$ = PF_OP_EQ; }
- | '!' '=' { $$ = PF_OP_NE; }
- | '<' '=' { $$ = PF_OP_LE; }
+ | NE { $$ = PF_OP_NE; }
+ | LE { $$ = PF_OP_LE; }
| '<' { $$ = PF_OP_LT; }
- | '>' '=' { $$ = PF_OP_GE; }
+ | GE { $$ = PF_OP_GE; }
| '>' { $$ = PF_OP_GT; }
;
@@ -5205,6 +5202,19 @@ yyerror(const char *fmt, ...)
}
int
+validate_range(uint8_t op, uint16_t p1, uint16_t p2)
+{
+ uint16_t a = ntohs(p1);
+ uint16_t b = ntohs(p2);
+
+ if ((op == PF_OP_RRG && a > b) || /* 34:12, i.e. none */
+ (op == PF_OP_IRG && a >= b) || /* 34><12, i.e. none */
+ (op == PF_OP_XRG && a > b)) /* 34<>22, i.e. all */
+ return 1;
+ return 0;
+}
+
+int
disallow_table(struct node_host *h, const char *fmt)
{
for (; h != NULL; h = h->next)
@@ -5238,7 +5248,7 @@ disallow_alias(struct node_host *h, const char *fmt)
}
int
-rule_consistent(struct pfctl_rule *r, int anchor_call)
+rule_consistent(struct pfctl_rule *r)
{
int problems = 0;
@@ -5248,7 +5258,7 @@ rule_consistent(struct pfctl_rule *r, int anchor_call)
case PF_DROP:
case PF_SCRUB:
case PF_NOSCRUB:
- problems = filter_consistent(r, anchor_call);
+ problems = filter_consistent(r);
break;
case PF_NAT:
case PF_NONAT:
@@ -5267,7 +5277,7 @@ rule_consistent(struct pfctl_rule *r, int anchor_call)
}
int
-filter_consistent(struct pfctl_rule *r, int anchor_call)
+filter_consistent(struct pfctl_rule *r)
{
int problems = 0;
@@ -5286,6 +5296,10 @@ filter_consistent(struct pfctl_rule *r, int anchor_call)
yyerror("must indicate address family with icmp-type/code");
problems++;
}
+ if (r->rule_flag & PFRULE_AFTO && r->af == r->naf) {
+ yyerror("must indicate different address family with af-to");
+ problems++;
+ }
if (r->overload_tblname[0] &&
r->max_src_conn == 0 && r->max_src_conn_rate.seconds == 0) {
yyerror("'overload' requires 'max-src-conn' "
@@ -5299,8 +5313,9 @@ filter_consistent(struct pfctl_rule *r, int anchor_call)
r->af == AF_INET ? "inet" : "inet6");
problems++;
}
- if (r->allow_opts && r->action != PF_PASS) {
- yyerror("allow-opts can only be specified for pass rules");
+ if (r->allow_opts && r->action != PF_PASS && r->action != PF_MATCH) {
+ yyerror("allow-opts can only be specified for pass or "
+ "match rules");
problems++;
}
if (r->rule_flag & PFRULE_FRAGMENT && (r->src.port_op ||
@@ -5327,8 +5342,20 @@ filter_consistent(struct pfctl_rule *r, int anchor_call)
"synproxy state or modulate state");
problems++;
}
- /* match rules rules */
- if (r->action == PF_MATCH) {
+ if ((r->keep_state == PF_STATE_SYNPROXY) && (r->direction != PF_IN))
+ fprintf(stderr, "%s:%d: warning: "
+ "synproxy used for inbound rules only, "
+ "ignored for outbound\n", file->name, yylval.lineno);
+ if (r->rule_flag & PFRULE_AFTO && r->rt) {
+ if (r->rt != PF_ROUTETO && r->rt != PF_REPLYTO) {
+ yyerror("dup-to "
+ "must not be used on af-to rules");
+ problems++;
+ }
+ }
+ /* Basic rule sanity check. */
+ switch (r->action) {
+ case PF_MATCH:
if (r->divert.port) {
yyerror("divert is not supported on match rules");
problems++;
@@ -5338,6 +5365,32 @@ filter_consistent(struct pfctl_rule *r, int anchor_call)
"must not be used on match rules");
problems++;
}
+ if (r->rule_flag & PFRULE_AFTO) {
+ yyerror("af-to is not supported on match rules");
+ problems++;
+ }
+ break;
+ case PF_DROP:
+ if (r->rt) {
+ yyerror("route-to, reply-to and dup-to "
+ "are not supported on block rules");
+ problems++;
+ }
+ break;
+ default:;
+ }
+ if (!TAILQ_EMPTY(&(r->nat.list)) || !TAILQ_EMPTY(&(r->rdr.list))) {
+ if (r->action != PF_MATCH && !r->keep_state) {
+ yyerror("nat-to and rdr-to require keep state");
+ 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);
}
@@ -5363,8 +5416,8 @@ rdr_consistent(struct pfctl_rule *r)
yyerror("dst port only applies to tcp/udp/sctp");
problems++;
}
- if (r->rpool.proxy_port[0]) {
- yyerror("rpool port only applies to tcp/udp/sctp");
+ if (r->rdr.proxy_port[0]) {
+ yyerror("rdr port only applies to tcp/udp/sctp");
problems++;
}
}
@@ -5377,10 +5430,11 @@ rdr_consistent(struct pfctl_rule *r)
}
int
-process_tabledef(char *name, struct table_opts *opts)
+process_tabledef(char *name, struct table_opts *opts, int popts)
{
struct pfr_buffer ab;
struct node_tinit *ti;
+ struct pfr_uktable *ukt;
unsigned long maxcount;
size_t s = sizeof(maxcount);
@@ -5388,7 +5442,7 @@ process_tabledef(char *name, struct table_opts *opts)
ab.pfrb_type = PFRB_ADDRS;
SIMPLEQ_FOREACH(ti, &opts->init_nodes, entries) {
if (ti->file)
- if (pfr_buf_load(&ab, ti->file, 0, append_addr)) {
+ if (pfr_buf_load(&ab, ti->file, 0, append_addr, popts)) {
if (errno)
yyerror("cannot load \"%s\": %s",
ti->file, strerror(errno));
@@ -5407,9 +5461,29 @@ process_tabledef(char *name, struct table_opts *opts)
if (pf->opts & PF_OPT_VERBOSE)
print_tabledef(name, opts->flags, opts->init_addr,
&opts->init_nodes);
+ if (!(pf->opts & PF_OPT_NOACTION) ||
+ (pf->opts & PF_OPT_DUMMYACTION))
+ warn_duplicate_tables(name, pf->anchor->path);
+ else if (pf->opts & PF_OPT_VERBOSE)
+ fprintf(stderr, "%s:%d: skipping duplicate table checks"
+ " for <%s>\n", file->name, yylval.lineno, name);
+ /*
+ * postpone definition of non-root tables to moment
+ * when path is fully resolved.
+ */
+ if (pf->asd > 0) {
+ ukt = calloc(1, sizeof(struct pfr_uktable));
+ if (ukt == NULL) {
+ DBGPRINT(
+ "%s:%d: not enough memory for <%s>\n", file->name,
+ yylval.lineno, name);
+ goto _error;
+ }
+ } else
+ ukt = NULL;
if (!(pf->opts & PF_OPT_NOACTION) &&
pfctl_define_table(name, opts->flags, opts->init_addr,
- pf->anchor->name, &ab, pf->anchor->ruleset.tticket)) {
+ pf->anchor->path, &ab, pf->anchor->ruleset.tticket, ukt)) {
if (sysctlbyname("net.pf.request_maxcount", &maxcount, &s,
NULL, 0) == -1)
@@ -5421,10 +5495,32 @@ process_tabledef(char *name, struct table_opts *opts)
name);
else
yyerror("cannot define table %s: %s", name,
- pfr_strerror(errno));
+ pf_strerror(errno));
goto _error;
}
+
+ if (ukt != NULL) {
+ ukt->pfrukt_init_addr = opts->init_addr;
+ if (RB_INSERT(pfr_ktablehead, &pfr_ktables,
+ &ukt->pfrukt_kt) != NULL) {
+ /*
+ * I think this should not happen, because
+ * pfctl_define_table() above does the same check
+ * effectively.
+ */
+ DBGPRINT(
+ "%s:%d table %s already exists in %s\n",
+ file->name, yylval.lineno,
+ ukt->pfrukt_name, pf->anchor->path);
+ free(ukt);
+ goto _error;
+ }
+ DBGPRINT("%s %s@%s inserted to tree\n",
+ __func__, ukt->pfrukt_name, pf->anchor->path);
+ } else
+ DBGPRINT("%s ukt is null\n", __func__);
+
pf->tdirty = 1;
pfr_buf_clear(&ab);
return (0);
@@ -5475,18 +5571,18 @@ expand_label_str(char *label, size_t len, const char *srch, const char *repl)
char *p, *q;
if ((tmp = calloc(1, len)) == NULL)
- err(1, "expand_label_str: calloc");
+ err(1, "%s: calloc", __func__);
p = q = label;
while ((q = strstr(p, srch)) != NULL) {
*q = '\0';
if ((strlcat(tmp, p, len) >= len) ||
(strlcat(tmp, repl, len) >= len))
- errx(1, "expand_label: label too long");
+ errx(1, "%s: label too long", __func__);
q += strlen(srch);
p = q;
}
if (strlcat(tmp, p, len) >= len)
- errx(1, "expand_label: label too long");
+ errx(1, "%s: label too long", __func__);
strlcpy(label, tmp, len); /* always fits */
free(tmp);
}
@@ -5534,7 +5630,7 @@ expand_label_addr(const char *name, char *label, size_t len, sa_family_t af,
sizeof(a)) == NULL)
snprintf(tmp, sizeof(tmp), "?");
else {
- bits = unmask(&addr->addr.v.a.mask, af);
+ bits = unmask(&addr->addr.v.a.mask);
if ((af == AF_INET && bits < 32) ||
(af == AF_INET6 && bits < 128))
snprintf(tmp, sizeof(tmp),
@@ -5652,7 +5748,7 @@ expand_altq(struct pf_altq *a, struct node_if *interfaces,
memcpy(&pa, a, sizeof(struct pf_altq));
if (strlcpy(pa.ifname, interface->ifname,
sizeof(pa.ifname)) >= sizeof(pa.ifname))
- errx(1, "expand_altq: strlcpy");
+ errx(1, "%s: strlcpy", __func__);
if (interface->not) {
yyerror("altq on ! <interface> is not supported");
@@ -5686,16 +5782,16 @@ expand_altq(struct pf_altq *a, struct node_if *interfaces,
memset(&pb, 0, sizeof(struct pf_altq));
if (strlcpy(qname, "root_", sizeof(qname)) >=
sizeof(qname))
- errx(1, "expand_altq: strlcpy");
+ errx(1, "%s: strlcpy", __func__);
if (strlcat(qname, interface->ifname,
sizeof(qname)) >= sizeof(qname))
- errx(1, "expand_altq: strlcat");
+ errx(1, "%s: strlcat", __func__);
if (strlcpy(pb.qname, qname,
sizeof(pb.qname)) >= sizeof(pb.qname))
- errx(1, "expand_altq: strlcpy");
+ errx(1, "%s: strlcpy", __func__);
if (strlcpy(pb.ifname, interface->ifname,
sizeof(pb.ifname)) >= sizeof(pb.ifname))
- errx(1, "expand_altq: strlcpy");
+ errx(1, "%s: strlcpy", __func__);
pb.qlimit = pa.qlimit;
pb.scheduler = pa.scheduler;
bw.bw_absolute = pa.ifbandwidth;
@@ -5710,20 +5806,20 @@ expand_altq(struct pf_altq *a, struct node_if *interfaces,
LOOP_THROUGH(struct node_queue, queue, nqueues,
n = calloc(1, sizeof(struct node_queue));
if (n == NULL)
- err(1, "expand_altq: calloc");
+ err(1, "%s: calloc", __func__);
if (pa.scheduler == ALTQT_CBQ ||
pa.scheduler == ALTQT_HFSC ||
pa.scheduler == ALTQT_FAIRQ)
if (strlcpy(n->parent, qname,
sizeof(n->parent)) >=
sizeof(n->parent))
- errx(1, "expand_altq: strlcpy");
+ errx(1, "%s: strlcpy", __func__);
if (strlcpy(n->queue, queue->queue,
sizeof(n->queue)) >= sizeof(n->queue))
- errx(1, "expand_altq: strlcpy");
+ errx(1, "%s: strlcpy", __func__);
if (strlcpy(n->ifname, interface->ifname,
sizeof(n->ifname)) >= sizeof(n->ifname))
- errx(1, "expand_altq: strlcpy");
+ errx(1, "%s: strlcpy", __func__);
n->scheduler = pa.scheduler;
n->next = NULL;
n->tail = n;
@@ -5806,10 +5902,10 @@ expand_queue(struct pf_altq *a, struct node_if *interfaces,
if (strlcpy(pa.ifname, tqueue->ifname,
sizeof(pa.ifname)) >= sizeof(pa.ifname))
- errx(1, "expand_queue: strlcpy");
+ errx(1, "%s: strlcpy", __func__);
if (strlcpy(pa.parent, tqueue->parent,
sizeof(pa.parent)) >= sizeof(pa.parent))
- errx(1, "expand_queue: strlcpy");
+ errx(1, "%s: strlcpy", __func__);
if (eval_pfqueue(pf, &pa, &bwspec, opts))
errs++;
@@ -5827,19 +5923,19 @@ expand_queue(struct pf_altq *a, struct node_if *interfaces,
n = calloc(1,
sizeof(struct node_queue));
if (n == NULL)
- err(1, "expand_queue: calloc");
+ err(1, "%s: calloc", __func__);
if (strlcpy(n->parent, a->qname,
sizeof(n->parent)) >=
sizeof(n->parent))
- errx(1, "expand_queue strlcpy");
+ errx(1, "%s strlcpy", __func__);
if (strlcpy(n->queue, nq->queue,
sizeof(n->queue)) >=
sizeof(n->queue))
- errx(1, "expand_queue strlcpy");
+ errx(1, "%s strlcpy", __func__);
if (strlcpy(n->ifname, tqueue->ifname,
sizeof(n->ifname)) >=
sizeof(n->ifname))
- errx(1, "expand_queue strlcpy");
+ errx(1, "%s strlcpy", __func__);
n->scheduler = tqueue->scheduler;
n->next = NULL;
n->tail = n;
@@ -5908,12 +6004,12 @@ expand_eth_rule(struct pfctl_eth_rule *r,
char qname[PF_QNAME_SIZE];
if (strlcpy(tagname, r->tagname, sizeof(tagname)) >= sizeof(tagname))
- errx(1, "expand_eth_rule: tagname");
+ errx(1, "%s: tagname", __func__);
if (strlcpy(match_tagname, r->match_tagname, sizeof(match_tagname)) >=
sizeof(match_tagname))
- errx(1, "expand_eth_rule: match_tagname");
+ errx(1, "%s: match_tagname", __func__);
if (strlcpy(qname, r->qname, sizeof(qname)) >= sizeof(qname))
- errx(1, "expand_eth_rule: qname");
+ errx(1, "%s: qname", __func__);
LOOP_THROUGH(struct node_if, interface, interfaces,
LOOP_THROUGH(struct node_etherproto, proto, protos,
@@ -5945,12 +6041,12 @@ expand_eth_rule(struct pfctl_eth_rule *r,
if (strlcpy(r->tagname, tagname, sizeof(r->tagname)) >=
sizeof(r->tagname))
- errx(1, "expand_eth_rule: r->tagname");
+ errx(1, "%s: r->tagname", __func__);
if (strlcpy(r->match_tagname, match_tagname,
sizeof(r->match_tagname)) >= sizeof(r->match_tagname))
- errx(1, "expand_eth_rule: r->match_tagname");
+ errx(1, "%s: r->match_tagname", __func__);
if (strlcpy(r->qname, qname, sizeof(r->qname)) >= sizeof(r->qname))
- errx(1, "expand_eth_rule: r->qname");
+ errx(1, "%s: r->qname", __func__);
if (bridge_to)
strlcpy(r->bridge_to, bridge_to, sizeof(r->bridge_to));
@@ -5966,14 +6062,262 @@ 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) {
+ rpool->proxy_port[1] = ntohs(rs->rport.a) +
+ (ntohs(r->dst.port[1]) - ntohs(r->dst.port[0]));
+ } else {
+ if (validate_range(rs->rport.t, rs->rport.a,
+ rs->rport.b)) {
+ yyerror("invalid rdr-to port range");
+ return (1);
+ }
+ 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 pfctl_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));
+
+ for (h = rs->host; h != NULL; h = h->next) {
+ pa = calloc(1, sizeof(struct pfctl_pooladdr));
+ if (pa == NULL)
+ err(1, "%s: calloc", __func__);
+ pa->addr = h->addr;
+ pa->af = h->af;
+ if (h->ifname != NULL) {
+ if (strlcpy(pa->ifname, h->ifname,
+ sizeof(pa->ifname)) >= sizeof(pa->ifname))
+ errx(1, "%s: strlcpy", __func__);
+ } else
+ pa->ifname[0] = 0;
+ TAILQ_INSERT_TAIL(&(rpool->list), pa, entries);
+ }
+
+ return 0;
+}
+
+int
+check_binat_redirspec(struct node_host *src_host, struct pfctl_rule *r,
+ sa_family_t af)
+{
+ struct pfctl_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
-expand_rule(struct pfctl_rule *r,
- struct node_if *interfaces, struct node_host *rpool_hosts,
- 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, struct node_icmp *icmp_types,
- const char *anchor_call)
+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 pfctl_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 redirspec *rdr, struct redirspec *route,
+ 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,
+ struct node_if *rcv, struct node_icmp *icmp_types)
{
sa_family_t af = r->af;
int added = 0, error = 0;
@@ -5981,17 +6325,16 @@ 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;
+ struct node_host *osrch, *odsth;
u_int8_t flags, flagset, keep_state;
memcpy(label, r->label, sizeof(r->label));
assert(sizeof(r->label) == sizeof(label));
if (strlcpy(tagname, r->tagname, sizeof(tagname)) >= sizeof(tagname))
- errx(1, "expand_rule: strlcpy");
+ errx(1, "%s: strlcpy", __func__);
if (strlcpy(match_tagname, r->match_tagname, sizeof(match_tagname)) >=
sizeof(match_tagname))
- errx(1, "expand_rule: strlcpy");
+ errx(1, "%s: strlcpy", __func__);
flags = r->flags;
flagset = r->flagset;
keep_state = r->keep_state;
@@ -6000,14 +6343,20 @@ expand_rule(struct pfctl_rule *r,
LOOP_THROUGH(struct node_proto, proto, protos,
LOOP_THROUGH(struct node_icmp, icmp_type, icmp_types,
LOOP_THROUGH(struct node_host, src_host, src_hosts,
- LOOP_THROUGH(struct node_port, src_port, src_ports,
- LOOP_THROUGH(struct node_os, src_os, src_oses,
LOOP_THROUGH(struct node_host, dst_host, dst_hosts,
+ LOOP_THROUGH(struct node_port, src_port, src_ports,
LOOP_THROUGH(struct node_port, dst_port, dst_ports,
+ LOOP_THROUGH(struct node_os, src_os, src_oses,
LOOP_THROUGH(struct node_uid, uid, uids,
LOOP_THROUGH(struct node_gid, gid, gids,
r->af = af;
+
+ if (r->rule_flag & PFRULE_AFTO) {
+ assert(nat != NULL);
+ r->naf = nat->af;
+ }
+
/* for link-local IPv6 address, interface must match up */
if ((r->af && src_host->af && r->af != src_host->af) ||
(r->af && dst_host->af && r->af != dst_host->af) ||
@@ -6016,9 +6365,9 @@ expand_rule(struct pfctl_rule *r,
(src_host->ifindex && dst_host->ifindex &&
src_host->ifindex != dst_host->ifindex) ||
(src_host->ifindex && *interface->ifname &&
- src_host->ifindex != if_nametoindex(interface->ifname)) ||
+ src_host->ifindex != ifa_nametoindex(interface->ifname)) ||
(dst_host->ifindex && *interface->ifname &&
- dst_host->ifindex != if_nametoindex(interface->ifname)))
+ dst_host->ifindex != ifa_nametoindex(interface->ifname)))
continue;
if (!r->af && src_host->af)
r->af = src_host->af;
@@ -6028,9 +6377,9 @@ expand_rule(struct pfctl_rule *r,
if (*interface->ifname)
strlcpy(r->ifname, interface->ifname,
sizeof(r->ifname));
- else if (if_indextoname(src_host->ifindex, ifname))
+ else if (ifa_indextoname(src_host->ifindex, ifname))
strlcpy(r->ifname, ifname, sizeof(r->ifname));
- else if (if_indextoname(dst_host->ifindex, ifname))
+ else if (ifa_indextoname(dst_host->ifindex, ifname))
strlcpy(r->ifname, ifname, sizeof(r->ifname));
else
memset(r->ifname, '\0', sizeof(r->ifname));
@@ -6038,10 +6387,22 @@ expand_rule(struct pfctl_rule *r,
memcpy(r->label, label, sizeof(r->label));
if (strlcpy(r->tagname, tagname, sizeof(r->tagname)) >=
sizeof(r->tagname))
- errx(1, "expand_rule: strlcpy");
+ errx(1, "%s: strlcpy", __func__);
if (strlcpy(r->match_tagname, match_tagname,
sizeof(r->match_tagname)) >= sizeof(r->match_tagname))
- errx(1, "expand_rule: strlcpy");
+ errx(1, "%s: strlcpy", __func__);
+
+ osrch = odsth = NULL;
+ if (src_host->addr.type == PF_ADDR_DYNIFTL) {
+ osrch = src_host;
+ if ((src_host = gen_dynnode(src_host, r->af)) == NULL)
+ err(1, "%s: calloc", __func__);
+ }
+ if (dst_host->addr.type == PF_ADDR_DYNIFTL) {
+ odsth = dst_host;
+ if ((dst_host = gen_dynnode(dst_host, r->af)) == NULL)
+ err(1, "%s: calloc", __func__);
+ }
error += check_netmask(src_host, r->af);
error += check_netmask(dst_host, r->af);
@@ -6064,6 +6425,11 @@ expand_rule(struct pfctl_rule *r,
r->gid.op = gid->op;
r->gid.gid[0] = gid->gid[0];
r->gid.gid[1] = gid->gid[1];
+ if (rcv) {
+ strlcpy(r->rcv_ifname, rcv->ifname,
+ sizeof(r->rcv_ifname));
+ r->rcvifnot = rcv->not;
+ }
r->type = icmp_type->type;
r->code = icmp_type->code;
@@ -6097,43 +6463,85 @@ expand_rule(struct pfctl_rule *r,
r->os_fingerprint = PF_OSFP_ANY;
}
- TAILQ_INIT(&r->rpool.list);
- for (h = rpool_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->rpool.list, pa, entries);
+ if (r->action == PF_RDR) {
+ /* Pre-FreeBSD 15 "rdr" rule */
+ error += apply_rdr_ports(r, &(r->rdr), rdr);
+ error += apply_redirspec(&(r->rdr), rdr);
+ } else if (r->action == PF_NAT) {
+ /* Pre-FreeBSD 15 "nat" rule */
+ 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_rdr_ports(r, &(r->rdr), rdr);
+ error += apply_redirspec(&(r->rdr), rdr);
+
+ if (nat && nat->binat)
+ error += check_binat_redirspec(src_host, r, af);
}
- if (rule_consistent(r, anchor_call[0]) < 0 || error)
+ if (rule_consistent(r) < 0 || error)
yyerror("skipping rule due to errors");
else {
r->nr = pf->astack[pf->asd]->match++;
- pfctl_append_rule(pf, r, anchor_call);
+ pfctl_append_rule(pf, r);
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) {
+ free(src_host);
+ src_host = osrch;
+ }
+ if (odsth && dst_host->addr.type == PF_ADDR_DYNIFTL) {
+ free(dst_host);
+ dst_host = odsth;
+ }
+
))))))))));
- FREE_LIST(struct node_if, interfaces);
- FREE_LIST(struct node_proto, protos);
- FREE_LIST(struct node_host, src_hosts);
- FREE_LIST(struct node_port, src_ports);
- FREE_LIST(struct node_os, src_oses);
- FREE_LIST(struct node_host, dst_hosts);
- FREE_LIST(struct node_port, dst_ports);
- FREE_LIST(struct node_uid, uids);
- FREE_LIST(struct node_gid, gids);
- FREE_LIST(struct node_icmp, icmp_types);
- FREE_LIST(struct node_host, rpool_hosts);
+ if (!keeprule) {
+ FREE_LIST(struct node_if, interfaces);
+ FREE_LIST(struct node_proto, protos);
+ FREE_LIST(struct node_host, src_hosts);
+ FREE_LIST(struct node_port, src_ports);
+ FREE_LIST(struct node_os, src_oses);
+ FREE_LIST(struct node_host, dst_hosts);
+ FREE_LIST(struct node_port, dst_ports);
+ FREE_LIST(struct node_uid, uids);
+ FREE_LIST(struct node_gid, gids);
+ FREE_LIST(struct node_icmp, icmp_types);
+ if (nat) {
+ FREE_LIST(struct node_host, nat->host);
+ free(nat);
+ }
+ if (rdr) {
+ FREE_LIST(struct node_host, rdr->host);
+ free(rdr);
+ }
+ if (route) {
+ FREE_LIST(struct node_host, route->host);
+ free(route);
+ }
+ }
if (!added)
yyerror("rule expands to no valid combination");
@@ -6175,6 +6583,12 @@ expand_skip_interface(struct node_if *interfaces)
return (0);
}
+void
+freehostlist(struct node_host *h)
+{
+ FREE_LIST(struct node_host, h);
+}
+
#undef FREE_LIST
#undef LOOP_THROUGH
@@ -6201,8 +6615,10 @@ lookup(char *s)
{
/* this has to be sorted always */
static const struct keywords keywords[] = {
+ { "af-to", AFTO},
{ "all", ALL},
{ "allow-opts", ALLOWOPTS},
+ { "allow-related", ALLOW_RELATED},
{ "altq", ALTQ},
{ "anchor", ANCHOR},
{ "antispoof", ANTISPOOF},
@@ -6210,6 +6626,7 @@ lookup(char *s)
{ "bandwidth", BANDWIDTH},
{ "binat", BINAT},
{ "binat-anchor", BINATANCHOR},
+ { "binat-to", BINATTO},
{ "bitmask", BITMASK},
{ "block", BLOCK},
{ "block-policy", BLOCKPOLICY},
@@ -6225,6 +6642,7 @@ lookup(char *s)
{ "dnqueue", DNQUEUE},
{ "drop", DROP},
{ "dup-to", DUPTO},
+ { "endpoint-independent", ENDPI},
{ "ether", ETHER},
{ "fail-policy", FAILPOLICY},
{ "fairq", FAIRQ},
@@ -6261,8 +6679,11 @@ lookup(char *s)
{ "loginterface", LOGINTERFACE},
{ "map-e-portset", MAPEPORTSET},
{ "match", MATCH},
+ { "matches", MATCHES},
{ "max", MAXIMUM},
{ "max-mss", MAXMSS},
+ { "max-pkt-rate", MAXPKTRATE},
+ { "max-pkt-size", MAXPKTSIZE},
{ "max-src-conn", MAXSRCCONN},
{ "max-src-conn-rate", MAXSRCCONNRATE},
{ "max-src-nodes", MAXSRCNODES},
@@ -6271,17 +6692,21 @@ lookup(char *s)
{ "modulate", MODULATE},
{ "nat", NAT},
{ "nat-anchor", NATANCHOR},
+ { "nat-to", NATTO},
{ "no", NO},
{ "no-df", NODF},
{ "no-route", NOROUTE},
{ "no-sync", NOSYNC},
{ "on", ON},
+ { "once", ONCE},
{ "optimization", OPTIMIZATION},
{ "os", OS},
{ "out", OUT},
{ "overload", OVERLOAD},
{ "pass", PASS},
+ { "pflow", PFLOW},
{ "port", PORT},
+ { "prefer-ipv6-nexthop", IPV6NH},
{ "prio", PRIO},
{ "priority", PRIORITY},
{ "priq", PRIQ},
@@ -6294,8 +6719,10 @@ lookup(char *s)
{ "random-id", RANDOMID},
{ "rdr", RDR},
{ "rdr-anchor", RDRANCHOR},
+ { "rdr-to", RDRTO},
{ "realtime", REALTIME},
{ "reassemble", REASSEMBLE},
+ { "received-on", RECEIVEDON},
{ "reply-to", REPLYTO},
{ "require-order", REQUIREORDER},
{ "return", RETURN},
@@ -6352,44 +6779,47 @@ lookup(char *s)
}
}
-#define MAXPUSHBACK 128
+#define START_EXPAND 1
+#define DONE_EXPAND 2
-static char *parsebuf;
-static int parseindex;
-static char pushback_buffer[MAXPUSHBACK];
-static int pushback_index = 0;
+static int expanding;
int
-lgetc(int quotec)
+igetc(void)
{
- int c, next;
-
- if (parsebuf) {
- /* Read character from the parsebuffer instead of input. */
- if (parseindex >= 0) {
- c = parsebuf[parseindex++];
- if (c != '\0')
- return (c);
- parsebuf = NULL;
- } else
- parseindex++;
+ int c;
+ while (1) {
+ if (file->ungetpos > 0)
+ c = file->ungetbuf[--file->ungetpos];
+ else
+ c = getc(file->stream);
+ if (c == START_EXPAND)
+ expanding = 1;
+ else if (c == DONE_EXPAND)
+ expanding = 0;
+ else
+ break;
}
+ return (c);
+}
- if (pushback_index)
- return (pushback_buffer[--pushback_index]);
+int
+lgetc(int quotec)
+{
+ int c, next;
if (quotec) {
- if ((c = getc(file->stream)) == EOF) {
+ if ((c = igetc()) == EOF) {
yyerror("reached end of file while parsing quoted string");
- if (popfile() == EOF)
+ if (file == topfile || popfile() == EOF)
return (EOF);
return (quotec);
}
return (c);
}
- while ((c = getc(file->stream)) == '\\') {
- next = getc(file->stream);
+ while ((c = igetc()) == '\\') {
+ next = igetc();
if (next != '\n') {
c = next;
break;
@@ -6398,28 +6828,38 @@ lgetc(int quotec)
file->lineno++;
}
- while (c == EOF) {
- if (popfile() == EOF)
- return (EOF);
- c = getc(file->stream);
+ if (c == EOF) {
+ /*
+ * Fake EOL when hit EOF for the first time. This gets line
+ * count right if last line in included file is syntactically
+ * invalid and has no newline.
+ */
+ if (file->eof_reached == 0) {
+ file->eof_reached = 1;
+ return ('\n');
+ }
+ while (c == EOF) {
+ if (file == topfile || popfile() == EOF)
+ return (EOF);
+ c = igetc();
+ }
}
return (c);
}
-int
+void
lungetc(int c)
{
if (c == EOF)
- return (EOF);
- if (parsebuf) {
- parseindex--;
- if (parseindex >= 0)
- return (c);
+ return;
+ if (file->ungetpos >= file->ungetsize) {
+ void *p = reallocarray(file->ungetbuf, file->ungetsize, 2);
+ if (p == NULL)
+ err(1, "%s", __func__);
+ file->ungetbuf = p;
+ file->ungetsize *= 2;
}
- if (pushback_index < MAXPUSHBACK-1)
- return (pushback_buffer[pushback_index++] = c);
- else
- return (EOF);
+ file->ungetbuf[file->ungetpos++] = c;
}
int
@@ -6427,14 +6867,9 @@ findeol(void)
{
int c;
- parsebuf = NULL;
-
/* skip to either EOF or the first real EOL */
while (1) {
- if (pushback_index)
- c = pushback_buffer[--pushback_index];
- else
- c = lgetc(0);
+ c = lgetc(0);
if (c == '\n') {
file->lineno++;
break;
@@ -6462,7 +6897,7 @@ top:
if (c == '#')
while ((c = lgetc(0)) != '\n' && c != EOF)
; /* nothing */
- if (c == '$' && parsebuf == NULL) {
+ if (c == '$' && !expanding) {
while (1) {
if ((c = lgetc(0)) == EOF)
return (0);
@@ -6484,8 +6919,13 @@ top:
yyerror("macro '%s' not defined", buf);
return (findeol());
}
- parsebuf = val;
- parseindex = 0;
+ p = val + strlen(val) - 1;
+ lungetc(DONE_EXPAND);
+ while (p >= val) {
+ lungetc(*p);
+ p--;
+ }
+ lungetc(START_EXPAND);
goto top;
}
@@ -6502,7 +6942,8 @@ top:
} else if (c == '\\') {
if ((next = lgetc(quotec)) == EOF)
return (0);
- if (next == quotec || c == ' ' || c == '\t')
+ if (next == quotec || next == ' ' ||
+ next == '\t')
c = next;
else if (next == '\n') {
file->lineno++;
@@ -6513,6 +6954,9 @@ top:
} else if (c == quotec) {
*p = '\0';
break;
+ } else if (c == '\0') {
+ yyerror("syntax error");
+ return (findeol());
}
if (p + 1 >= buf + sizeof(buf) - 1) {
yyerror("string too long");
@@ -6522,14 +6966,21 @@ top:
}
yylval.v.string = strdup(buf);
if (yylval.v.string == NULL)
- err(1, "yylex: strdup");
+ err(1, "%s: strdup", __func__);
return (STRING);
+ case '!':
+ next = lgetc(0);
+ if (next == '=')
+ return (NE);
+ lungetc(next);
+ break;
case '<':
next = lgetc(0);
if (next == '>') {
yylval.v.i = PF_OP_XRG;
return (PORTBINARY);
- }
+ } else if (next == '=')
+ return (LE);
lungetc(next);
break;
case '>':
@@ -6537,7 +6988,8 @@ top:
if (next == '<') {
yylval.v.i = PF_OP_IRG;
return (PORTBINARY);
- }
+ } else if (next == '=')
+ return (GE);
lungetc(next);
break;
case '-':
@@ -6554,7 +7006,7 @@ top:
if (c == '-' || isdigit(c)) {
do {
*p++ = c;
- if ((unsigned)(p-buf) >= sizeof(buf)) {
+ if ((size_t)(p-buf) >= sizeof(buf)) {
yyerror("string too long");
return (findeol());
}
@@ -6593,7 +7045,7 @@ nodigits:
if (isalnum(c) || c == ':' || c == '_') {
do {
*p++ = c;
- if ((unsigned)(p-buf) >= sizeof(buf)) {
+ if ((size_t)(p-buf) >= sizeof(buf)) {
yyerror("string too long");
return (findeol());
}
@@ -6602,7 +7054,7 @@ nodigits:
*p = '\0';
if ((token = lookup(buf)) == STRING)
if ((yylval.v.string = strdup(buf)) == NULL)
- err(1, "yylex: strdup");
+ err(1, "%s: strdup", __func__);
return (token);
}
if (c == '\n') {
@@ -6627,8 +7079,8 @@ check_file_secrecy(int fd, const char *fname)
warnx("%s: owner not root or current user", fname);
return (-1);
}
- if (st.st_mode & (S_IRWXG | S_IRWXO)) {
- warnx("%s: group/world readable/writeable", fname);
+ if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) {
+ warnx("%s: group writable or world read/writable", fname);
return (-1);
}
return (0);
@@ -6641,19 +7093,21 @@ pushfile(const char *name, int secret)
if ((nfile = calloc(1, sizeof(struct file))) == NULL ||
(nfile->name = strdup(name)) == NULL) {
- warn("malloc");
+ warn("%s", __func__);
+ if (nfile)
+ free(nfile);
return (NULL);
}
if (TAILQ_FIRST(&files) == NULL && strcmp(nfile->name, "-") == 0) {
nfile->stream = stdin;
free(nfile->name);
if ((nfile->name = strdup("stdin")) == NULL) {
- warn("strdup");
+ warn("%s", __func__);
free(nfile);
return (NULL);
}
- } else if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
- warn("%s", nfile->name);
+ } else if ((nfile->stream = pfctl_fopen(nfile->name, "r")) == NULL) {
+ warn("%s: %s", __func__, nfile->name);
free(nfile->name);
free(nfile);
return (NULL);
@@ -6664,7 +7118,16 @@ pushfile(const char *name, int secret)
free(nfile);
return (NULL);
}
- nfile->lineno = 1;
+ nfile->lineno = TAILQ_EMPTY(&files) ? 1 : 0;
+ nfile->ungetsize = 16;
+ nfile->ungetbuf = malloc(nfile->ungetsize);
+ if (nfile->ungetbuf == NULL) {
+ warn("%s", __func__);
+ fclose(nfile->stream);
+ free(nfile->name);
+ free(nfile);
+ return (NULL);
+ }
TAILQ_INSERT_TAIL(&files, nfile, entry);
return (nfile);
}
@@ -6674,16 +7137,17 @@ popfile(void)
{
struct file *prev;
- if ((prev = TAILQ_PREV(file, files, entry)) != NULL) {
+ if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
prev->errors += file->errors;
- TAILQ_REMOVE(&files, file, entry);
- fclose(file->stream);
- free(file->name);
- free(file);
- file = prev;
- return (0);
- }
- return (EOF);
+
+ TAILQ_REMOVE(&files, file, entry);
+ fclose(file->stream);
+ free(file->name);
+ free(file->ungetbuf);
+ free(file);
+ file = prev;
+
+ return (file ? 0 : EOF);
}
int
@@ -6706,6 +7170,7 @@ parse_config(char *filename, struct pfctl *xpf)
warn("cannot open the main config file!");
return (-1);
}
+ topfile = file;
yyparse();
errors = file->errors;
@@ -6730,9 +7195,10 @@ symset(const char *nam, const char *val, int persist)
{
struct sym *sym;
- for (sym = TAILQ_FIRST(&symhead); sym && strcmp(nam, sym->nam);
- sym = TAILQ_NEXT(sym, entry))
- ; /* nothing */
+ TAILQ_FOREACH(sym, &symhead, entry) {
+ if (strcmp(nam, sym->nam) == 0)
+ break;
+ }
if (sym != NULL) {
if (sym->persist == 1)
@@ -6773,10 +7239,9 @@ pfctl_cmdline_symset(char *s)
if ((val = strrchr(s, '=')) == NULL)
return (-1);
- if ((sym = malloc(strlen(s) - strlen(val) + 1)) == NULL)
- err(1, "pfctl_cmdline_symset: malloc");
-
- strlcpy(sym, s, strlen(s) - strlen(val) + 1);
+ sym = strndup(s, val - s);
+ if (sym == NULL)
+ err(1, "%s: malloc", __func__);
ret = symset(sym, val + 1, 1);
free(sym);
@@ -6789,11 +7254,12 @@ symget(const char *nam)
{
struct sym *sym;
- TAILQ_FOREACH(sym, &symhead, entry)
+ TAILQ_FOREACH(sym, &symhead, entry) {
if (strcmp(nam, sym->nam) == 0) {
sym->used = 1;
return (sym->val);
}
+ }
return (NULL);
}
@@ -6804,19 +7270,11 @@ mv_rules(struct pfctl_ruleset *src, struct pfctl_ruleset *dst)
struct pfctl_rule *r;
for (i = 0; i < PF_RULESET_MAX; ++i) {
- while ((r = TAILQ_FIRST(src->rules[i].active.ptr))
- != NULL) {
- TAILQ_REMOVE(src->rules[i].active.ptr, r, entries);
- TAILQ_INSERT_TAIL(dst->rules[i].active.ptr, r, entries);
+ TAILQ_FOREACH(r, src->rules[i].active.ptr, entries)
dst->anchor->match++;
- }
+ TAILQ_CONCAT(dst->rules[i].active.ptr, src->rules[i].active.ptr, entries);
src->anchor->match = 0;
- while ((r = TAILQ_FIRST(src->rules[i].inactive.ptr))
- != NULL) {
- TAILQ_REMOVE(src->rules[i].inactive.ptr, r, entries);
- TAILQ_INSERT_TAIL(dst->rules[i].inactive.ptr,
- r, entries);
- }
+ TAILQ_CONCAT(dst->rules[i].inactive.ptr, src->rules[i].inactive.ptr, entries);
}
}
@@ -6834,6 +7292,61 @@ mv_eth_rules(struct pfctl_eth_ruleset *src, struct pfctl_eth_ruleset *dst)
}
void
+mv_tables(struct pfctl *pf, struct pfr_ktablehead *ktables,
+ struct pfctl_anchor *a, struct pfctl_anchor *alast)
+{
+ struct pfr_ktable *kt, *kt_safe;
+ char new_path[PF_ANCHOR_MAXPATH];
+ char *path_cut;
+ int sz;
+ struct pfr_uktable *ukt;
+ SLIST_HEAD(, pfr_uktable) ukt_list;
+
+ /*
+ * Here we need to rename anchor path from temporal names such as
+ * _1/_2/foo to _1/bar/foo etc.
+ *
+ * This also means we need to remove and insert table to ktables
+ * tree as anchor path is being updated.
+ */
+ SLIST_INIT(&ukt_list);
+ DBGPRINT("%s [ %s ] (%s)\n", __func__, a->path, alast->path);
+ RB_FOREACH_SAFE(kt, pfr_ktablehead, ktables, kt_safe) {
+ path_cut = strstr(kt->pfrkt_anchor, alast->path);
+ if (path_cut != NULL) {
+ path_cut += strlen(alast->path);
+ if (*path_cut)
+ sz = snprintf(new_path, sizeof (new_path),
+ "%s%s", a->path, path_cut);
+ else
+ sz = snprintf(new_path, sizeof (new_path),
+ "%s", a->path);
+ if (sz >= sizeof (new_path))
+ errx(1, "new path is too long for %s@%s\n",
+ kt->pfrkt_name, kt->pfrkt_anchor);
+
+ DBGPRINT("%s %s@%s -> %s@%s\n", __func__,
+ kt->pfrkt_name, kt->pfrkt_anchor,
+ kt->pfrkt_name, new_path);
+ RB_REMOVE(pfr_ktablehead, ktables, kt);
+ strlcpy(kt->pfrkt_anchor, new_path,
+ sizeof(kt->pfrkt_anchor));
+ SLIST_INSERT_HEAD(&ukt_list, (struct pfr_uktable *)kt,
+ pfrukt_entry);
+ }
+ }
+
+ while ((ukt = SLIST_FIRST(&ukt_list)) != NULL) {
+ SLIST_REMOVE_HEAD(&ukt_list, pfrukt_entry);
+ if (RB_INSERT(pfr_ktablehead, ktables,
+ (struct pfr_ktable *)ukt) != NULL)
+ errx(1, "%s@%s exists already\n",
+ ukt->pfrukt_name,
+ ukt->pfrukt_anchor);
+ }
+}
+
+void
decide_address_family(struct node_host *n, sa_family_t *af)
{
if (*af != 0 || n == NULL)
@@ -7042,7 +7555,7 @@ parseport(char *port, struct range *r, int extensions)
}
int
-pfctl_load_anchors(int dev, struct pfctl *pf, struct pfr_buffer *trans)
+pfctl_load_anchors(int dev, struct pfctl *pf)
{
struct loadanchors *la;
@@ -7051,7 +7564,7 @@ pfctl_load_anchors(int dev, struct pfctl *pf, struct pfr_buffer *trans)
fprintf(stderr, "\nLoading anchor %s from %s\n",
la->anchorname, la->filename);
if (pfctl_rules(dev, la->filename, pf->opts, pf->optimize,
- la->anchorname, trans) == -1)
+ la->anchorname, pf->trans) == -1)
return (-1);
}
@@ -7137,7 +7650,7 @@ node_mac_from_string(const char *str)
m = calloc(1, sizeof(struct node_mac));
if (m == NULL)
- err(1, "mac: calloc");
+ err(1, "%s: calloc", __func__);
if (sscanf(str, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
&m->mac[0], &m->mac[1], &m->mac[2], &m->mac[3], &m->mac[4],
@@ -7195,3 +7708,172 @@ node_mac_from_string_mask(const char *str, const char *mask)
return (m);
}
+
+int
+filteropts_to_rule(struct pfctl_rule *r, struct filter_opts *opts)
+{
+ if (opts->marker & FOM_ONCE) {
+ if ((r->action != PF_PASS && r->action != PF_DROP) || r->anchor) {
+ yyerror("'once' only applies to pass/block rules");
+ return (1);
+ }
+ r->rule_flag |= PFRULE_ONCE;
+ }
+
+ r->keep_state = opts->keep.action;
+ r->pktrate.limit = opts->pktrate.limit;
+ r->pktrate.seconds = opts->pktrate.seconds;
+ r->prob = opts->prob;
+ r->rtableid = opts->rtableid;
+ r->ridentifier = opts->ridentifier;
+ r->max_pkt_size = opts->max_pkt_size;
+ r->tos = opts->tos;
+
+ if (opts->nodf)
+ r->scrub_flags |= PFSTATE_NODF;
+ if (opts->randomid)
+ r->scrub_flags |= PFSTATE_RANDOMID;
+ if (opts->minttl)
+ r->min_ttl = opts->minttl;
+ if (opts->max_mss)
+ r->max_mss = opts->max_mss;
+
+ if (opts->tag)
+ if (strlcpy(r->tagname, opts->tag,
+ PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) {
+ yyerror("tag too long, max %u chars",
+ PF_TAG_NAME_SIZE - 1);
+ return (1);
+ }
+ if (opts->match_tag)
+ if (strlcpy(r->match_tagname, opts->match_tag,
+ PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) {
+ yyerror("tag too long, max %u chars",
+ PF_TAG_NAME_SIZE - 1);
+ return (1);
+ }
+ r->match_tag_not = opts->match_tag_not;
+
+ if (rule_label(r, opts->label))
+ return (1);
+ for (int i = 0; i < PF_RULE_MAX_LABEL_COUNT; i++)
+ free(opts->label[i]);
+
+ if (opts->marker & FOM_AFTO)
+ r->rule_flag |= PFRULE_AFTO;
+ if (opts->marker & FOM_SCRUB_TCP)
+ r->scrub_flags |= PFSTATE_SCRUB_TCP;
+ if (opts->marker & FOM_PRIO)
+ r->prio = opts->prio ? opts->prio : PF_PRIO_ZERO;
+ if (opts->marker & FOM_SETPRIO) {
+ r->set_prio[0] = opts->set_prio[0];
+ r->set_prio[1] = opts->set_prio[1];
+ r->scrub_flags |= PFSTATE_SETPRIO;
+ }
+ if (opts->marker & FOM_SETTOS) {
+ r->scrub_flags |= PFSTATE_SETTOS;
+ r->set_tos = opts->settos;
+ }
+
+ r->flags = opts->flags.b1;
+ r->flagset = opts->flags.b2;
+ if ((opts->flags.b1 & opts->flags.b2) != opts->flags.b1) {
+ yyerror("flags always false");
+ return (1);
+ }
+
+ if (opts->queues.qname != NULL) {
+ if (strlcpy(r->qname, opts->queues.qname,
+ sizeof(r->qname)) >= sizeof(r->qname)) {
+ yyerror("rule qname too long (max "
+ "%d chars)", sizeof(r->qname)-1);
+ return (1);
+ }
+ free(opts->queues.qname);
+ }
+ if (opts->queues.pqname != NULL) {
+ if (strlcpy(r->pqname, opts->queues.pqname,
+ sizeof(r->pqname)) >= sizeof(r->pqname)) {
+ yyerror("rule pqname too long (max "
+ "%d chars)", sizeof(r->pqname)-1);
+ return (1);
+ }
+ free(opts->queues.pqname);
+ }
+
+ if (opts->fragment)
+ r->rule_flag |= PFRULE_FRAGMENT;
+ r->allow_opts = opts->allowopts;
+
+ return (0);
+}
+
+static bool
+pfctl_setup_anchor(struct pfctl_rule *r, struct pfctl *pf, char *anchorname)
+{
+ char *p;
+
+ if (pf->astack[pf->asd + 1]) {
+ if (anchorname && strchr(anchorname, '/') != NULL) {
+ free(anchorname);
+ yyerror("anchor paths containing '/' "
+ "cannot be used for inline anchors.");
+ return (false);
+ }
+
+ /* Move inline rules into relative location. */
+ pfctl_anchor_setup(r,
+ &pf->astack[pf->asd]->ruleset,
+ anchorname ? anchorname : pf->alast->name);
+
+ if (r->anchor == NULL)
+ err(1, "anchorrule: unable to "
+ "create ruleset");
+
+ if (pf->alast != r->anchor) {
+ if (r->anchor->match) {
+ yyerror("inline anchor '%s' "
+ "already exists",
+ r->anchor->name);
+ return (false);
+ }
+ mv_rules(&pf->alast->ruleset,
+ &r->anchor->ruleset);
+ mv_tables(pf, &pfr_ktables, r->anchor, pf->alast);
+ }
+ pf_remove_if_empty_ruleset(&pf->alast->ruleset);
+ pf->alast = r->anchor;
+ } else {
+ if (! anchorname) {
+ yyerror("anchors without explicit "
+ "rules must specify a name");
+ return (false);
+ }
+ /*
+ * Don't make non-brace anchors part of the main anchor pool.
+ */
+ if ((r->anchor = calloc(1, sizeof(*r->anchor))) == NULL) {
+ err(1, "anchorrule: calloc");
+ }
+ pf_init_ruleset(&r->anchor->ruleset);
+ r->anchor->ruleset.anchor = r->anchor;
+ if (strlcpy(r->anchor->path, anchorname,
+ sizeof(r->anchor->path)) >= sizeof(r->anchor->path)) {
+ errx(1, "anchorrule: strlcpy");
+ }
+ if ((p = strrchr(anchorname, '/')) != NULL) {
+ if (strlen(p) == 1) {
+ yyerror("anchorrule: bad anchor name %s",
+ anchorname);
+ return (false);
+ }
+ } else
+ p = anchorname;
+ if (strlcpy(r->anchor->name, p,
+ sizeof(r->anchor->name)) >= sizeof(r->anchor->name)) {
+ errx(1, "anchorrule: strlcpy");
+ }
+ }
+
+ return (true);
+}
diff --git a/sbin/pfctl/pf_print_state.c b/sbin/pfctl/pf_print_state.c
index 20594e050c9e..417ff70de975 100644
--- a/sbin/pfctl/pf_print_state.c
+++ b/sbin/pfctl/pf_print_state.c
@@ -32,7 +32,6 @@
*
*/
-#include <sys/cdefs.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/endian.h>
@@ -86,31 +85,18 @@ print_addr(struct pf_addr_wrap *addr, sa_family_t af, int verbose)
printf("<%s>", addr->v.tblname);
return;
case PF_ADDR_RANGE: {
- char buf[48];
+ print_addr_str(af, &addr->v.a.addr);
+ printf(" - ");
+ print_addr_str(af, &addr->v.a.mask);
- if (inet_ntop(af, &addr->v.a.addr, buf, sizeof(buf)) == NULL)
- printf("?");
- else
- printf("%s", buf);
- if (inet_ntop(af, &addr->v.a.mask, buf, sizeof(buf)) == NULL)
- printf(" - ?");
- else
- printf(" - %s", buf);
break;
}
case PF_ADDR_ADDRMASK:
if (PF_AZERO(&addr->v.a.addr, AF_INET6) &&
PF_AZERO(&addr->v.a.mask, AF_INET6))
printf("any");
- else {
- char buf[48];
-
- if (inet_ntop(af, &addr->v.a.addr, buf,
- sizeof(buf)) == NULL)
- printf("?");
- else
- printf("%s", buf);
- }
+ else
+ print_addr_str(af, &addr->v.a.addr);
break;
case PF_ADDR_NOROUTE:
printf("no-route");
@@ -127,62 +113,63 @@ print_addr(struct pf_addr_wrap *addr, sa_family_t af, int verbose)
if (addr->type != PF_ADDR_RANGE &&
!(PF_AZERO(&addr->v.a.addr, AF_INET6) &&
PF_AZERO(&addr->v.a.mask, AF_INET6))) {
- int bits = unmask(&addr->v.a.mask, af);
-
- if (bits != (af == AF_INET ? 32 : 128))
- printf("/%d", bits);
+ if (af == AF_INET || af == AF_INET6) {
+ int bits = unmask(&addr->v.a.mask);
+ if (bits < (af == AF_INET ? 32 : 128))
+ printf("/%d", bits);
+ }
}
}
void
+print_addr_str(sa_family_t af, struct pf_addr *addr)
+{
+ static char buf[48];
+
+ if (inet_ntop(af, addr, buf, sizeof(buf)) == NULL)
+ printf("?");
+ else
+ printf("%s", buf);
+}
+
+void
print_name(struct pf_addr *addr, sa_family_t af)
{
- char host[NI_MAXHOST];
-
- strlcpy(host, "?", sizeof(host));
- switch (af) {
- case AF_INET: {
- struct sockaddr_in sin;
-
- memset(&sin, 0, sizeof(sin));
- sin.sin_len = sizeof(sin);
- sin.sin_family = AF_INET;
- sin.sin_addr = addr->v4;
- getnameinfo((struct sockaddr *)&sin, sin.sin_len,
- host, sizeof(host), NULL, 0, NI_NOFQDN);
- break;
- }
- case AF_INET6: {
- struct sockaddr_in6 sin6;
-
- memset(&sin6, 0, sizeof(sin6));
- sin6.sin6_len = sizeof(sin6);
- sin6.sin6_family = AF_INET6;
- sin6.sin6_addr = addr->v6;
- getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len,
- host, sizeof(host), NULL, 0, NI_NOFQDN);
- break;
- }
+ struct sockaddr_storage ss;
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+ char host[NI_MAXHOST];
+
+ memset(&ss, 0, sizeof(ss));
+ ss.ss_family = af;
+ if (ss.ss_family == AF_INET) {
+ sin = (struct sockaddr_in *)&ss;
+ sin->sin_len = sizeof(*sin);
+ sin->sin_addr = addr->v4;
+ } else {
+ sin6 = (struct sockaddr_in6 *)&ss;
+ sin6->sin6_len = sizeof(*sin6);
+ sin6->sin6_addr = addr->v6;
}
- printf("%s", host);
+
+ if (getnameinfo((struct sockaddr *)&ss, ss.ss_len, host, sizeof(host),
+ NULL, 0, NI_NOFQDN) != 0)
+ printf("?");
+ else
+ printf("%s", host);
}
void
print_host(struct pf_addr *addr, u_int16_t port, sa_family_t af, int opts)
{
+ struct pf_addr_wrap aw;
+
if (opts & PF_OPT_USEDNS)
print_name(addr, af);
else {
- struct pf_addr_wrap aw;
-
memset(&aw, 0, sizeof(aw));
aw.v.a.addr = *addr;
- if (af == AF_INET)
- aw.v.a.mask.addr32[0] = 0xffffffff;
- else {
- memset(&aw.v.a.mask, 0xff, sizeof(aw.v.a.mask));
- af = AF_INET6;
- }
+ memset(&aw.v.a.mask, 0xff, sizeof(aw.v.a.mask));
print_addr(&aw, af, opts & PF_OPT_VERBOSE2);
}
@@ -242,8 +229,10 @@ print_state(struct pfctl_state *s, int opts)
struct pfctl_state_key *key, *sk, *nk;
const char *protoname;
int min, sec;
- sa_family_t af;
uint8_t proto;
+ int afto = (s->key[PF_SK_STACK].af != s->key[PF_SK_WIRE].af);
+ int idx;
+ const char *sn_type_names[] = PF_SN_TYPE_NAMES;
#ifndef __NO_STRICT_ALIGNMENT
struct pfctl_state_key aligned_key[2];
@@ -253,7 +242,6 @@ print_state(struct pfctl_state *s, int opts)
key = s->key;
#endif
- af = s->key[PF_SK_WIRE].af;
proto = s->key[PF_SK_WIRE].proto;
if (s->direction == PF_OUT) {
@@ -277,22 +265,26 @@ print_state(struct pfctl_state *s, int opts)
else
printf("%u ", proto);
- print_host(&nk->addr[1], nk->port[1], af, opts);
- if (PF_ANEQ(&nk->addr[1], &sk->addr[1], af) ||
+ print_host(&nk->addr[1], nk->port[1], nk->af, opts);
+ if (nk->af != sk->af || PF_ANEQ(&nk->addr[1], &sk->addr[1], nk->af) ||
nk->port[1] != sk->port[1]) {
+ idx = afto ? 0 : 1;
printf(" (");
- print_host(&sk->addr[1], sk->port[1], af, opts);
+ print_host(&sk->addr[idx], sk->port[idx], sk->af,
+ opts);
printf(")");
}
- if (s->direction == PF_OUT)
+ if (s->direction == PF_OUT || (afto && s->direction == PF_IN))
printf(" -> ");
else
printf(" <- ");
- print_host(&nk->addr[0], nk->port[0], af, opts);
- if (PF_ANEQ(&nk->addr[0], &sk->addr[0], af) ||
+ print_host(&nk->addr[0], nk->port[0], nk->af, opts);
+ if (nk->af != sk->af || PF_ANEQ(&nk->addr[0], &sk->addr[0], nk->af) ||
nk->port[0] != sk->port[0]) {
+ idx = afto ? 1 : 0;
printf(" (");
- print_host(&sk->addr[0], sk->port[0], af, opts);
+ print_host(&sk->addr[idx], sk->port[idx], sk->af,
+ opts);
printf(")");
}
@@ -377,6 +369,8 @@ print_state(struct pfctl_state *s, int opts)
printf(", sloppy");
if (s->state_flags & PFSTATE_NOSYNC)
printf(", no-sync");
+ if (s->state_flags & PFSTATE_PFLOW)
+ printf(", pflow");
if (s->state_flags & PFSTATE_ACK)
printf(", psync-ack");
if (s->state_flags & PFSTATE_NODF)
@@ -398,10 +392,14 @@ print_state(struct pfctl_state *s, int opts)
printf(", dummynet queue (%d %d)",
s->dnpipe, s->dnrpipe);
}
- if (s->sync_flags & PFSYNC_FLAG_SRCNODE)
- printf(", source-track");
- if (s->sync_flags & PFSYNC_FLAG_NATSRCNODE)
- printf(", sticky-address");
+ if (s->src_node_flags & PFSTATE_SRC_NODE_LIMIT)
+ printf(", %s", sn_type_names[PF_SN_LIMIT]);
+ if (s->src_node_flags & PFSTATE_SRC_NODE_LIMIT_GLOBAL)
+ printf(" global");
+ if (s->src_node_flags & PFSTATE_SRC_NODE_NAT)
+ printf(", %s", sn_type_names[PF_SN_NAT]);
+ if (s->src_node_flags & PFSTATE_SRC_NODE_ROUTE)
+ printf(", %s", sn_type_names[PF_SN_ROUTE]);
if (s->log)
printf(", log");
if (s->log & PF_LOG_ALL)
@@ -431,7 +429,7 @@ print_state(struct pfctl_state *s, int opts)
default:
printf(" gateway: ");
}
- print_host(&s->rt_addr, 0, af, opts);
+ print_host(&s->rt_addr, 0, s->rt_af, opts);
if (s->rt_ifname[0])
printf("@%s", s->rt_ifname);
}
@@ -445,7 +443,7 @@ print_state(struct pfctl_state *s, int opts)
}
int
-unmask(struct pf_addr *m, sa_family_t af)
+unmask(struct pf_addr *m)
{
int i = 31, j = 0, b = 0;
u_int32_t tmp;
diff --git a/sbin/pfctl/pf_ruleset.c b/sbin/pfctl/pf_ruleset.c
index 09ee0fdb916c..2b7ec09f28aa 100644
--- a/sbin/pfctl/pf_ruleset.c
+++ b/sbin/pfctl/pf_ruleset.c
@@ -36,7 +36,6 @@
* $OpenBSD: pf_ruleset.c,v 1.2 2008/12/18 15:31:37 dhill Exp $
*/
-#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/mbuf.h>
diff --git a/sbin/pfctl/pfctl.8 b/sbin/pfctl/pfctl.8
index 41eb2bea9f94..58de54cdf923 100644
--- a/sbin/pfctl/pfctl.8
+++ b/sbin/pfctl/pfctl.8
@@ -24,7 +24,7 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
-.Dd February 22, 2021
+.Dd August 28, 2025
.Dt PFCTL 8
.Os
.Sh NAME
@@ -43,7 +43,7 @@
.Op Fl K Ar host | network
.Xo
.Oo Fl k
-.Ar host | network | label | id | gateway
+.Ar host | network | label | id | gateway | nat
.Oc Xc
.Op Fl o Ar level
.Op Fl p Ar device
@@ -82,7 +82,7 @@ Translation rules are described in
.Xr pf.conf 5 .
.Pp
When the variable
-.Va pf
+.Va pf_enable
is set to
.Dv YES
in
@@ -104,9 +104,7 @@ to 1.
Set them permanently in
.Xr sysctl.conf 5 .
.Pp
-The
-.Nm
-utility provides several commands.
+At least one option must be specified.
The options are as follows:
.Bl -tag -width Ds
.It Fl A
@@ -116,8 +114,10 @@ Other rules and options are ignored.
Apply flags
.Fl f ,
.Fl F ,
+.Fl s ,
+.Fl T ,
and
-.Fl s
+.Fl z
only to the rules in the specified
.Ar anchor .
In addition to the main ruleset,
@@ -187,6 +187,13 @@ as the anchor name:
.Bd -literal -offset indent
# pfctl -a '*' -sr
.Ed
+.Pp
+To flush all rulesets and tables recursively, specify only
+.Sq *
+as the anchor name:
+.Bd -literal -offset indent
+# pfctl -a '*' -Fa
+.Ed
.It Fl D Ar macro Ns = Ns Ar value
Define
.Ar macro
@@ -205,28 +212,46 @@ Flush the filter parameters specified by
.Ar modifier
(may be abbreviated):
.Pp
-.Bl -tag -width xxxxxxxxxxxx -compact
-.It Fl F Cm nat
+.Bl -tag -width xxxxxxxxx -compact
+.It Cm nat
Flush the NAT rules.
-.It Fl F Cm queue
+.It Cm queue
Flush the queue rules.
-.It Fl F Cm ethernet
+.It Cm ethernet
Flush the Ethernet filter rules.
-.It Fl F Cm rules
+.It Cm rules
Flush the filter rules.
-.It Fl F Cm states
+.It Cm states
Flush the state table (NAT and filter).
-.It Fl F Cm Sources
+.It Cm Sources
Flush the source tracking table.
-.It Fl F Cm info
+.It Cm info
Flush the filter information (statistics that are not bound to rules).
-.It Fl F Cm Tables
+.It Cm Tables
Flush the tables.
-.It Fl F Cm osfp
+.It Cm osfp
Flush the passive operating system fingerprints.
-.It Fl F Cm all
+.It Cm Reset
+Reset limits, timeouts and other options back to default settings.
+See the OPTIONS section in
+.Xr pf.conf 5
+for details.
+.It Cm all
Flush all of the above.
.El
+.Pp
+If
+.Fl a
+is specified as well and
+.Ar anchor
+is terminated with a
+.Sq *
+character,
+.Cm rules ,
+.Cm Tables
+and
+.Cm all
+flush the given anchor recursively.
.It Fl f Ar file
Load the rules contained in
.Ar file .
@@ -256,15 +281,17 @@ option may be specified, which will kill all the source tracking
entries from the first host/network to the second.
.It Xo
.Fl k
-.Ar host | network | label | id | gateway
+.Ar host | network | label | id | key | gateway | nat
.Xc
Kill all of the state entries matching the specified
.Ar host ,
.Ar network ,
.Ar label ,
.Ar id ,
+.Ar key ,
+.Ar gateway,
or
-.Ar gateway.
+.Ar nat.
.Pp
For example, to kill all of the state entries originating from
.Dq host :
@@ -294,7 +321,7 @@ To kill all states with the target
.Pp
.Dl # pfctl -k 0.0.0.0/0 -k host2
.Pp
-It is also possible to kill states by rule label or state ID.
+It is also possible to kill states by rule label, state key or state ID.
In this mode the first
.Fl k
argument is used to specify the type
@@ -305,6 +332,17 @@ from rules carrying the label
.Pp
.Dl # pfctl -k label -k foobar
.Pp
+To kill one specific state by its key
+(protocol, host1, port1, direction, host2 and port2 in the same format
+of pfctl -s state),
+use the
+.Ar key
+modifier and as a second argument the state key.
+To kill a state whose protocol is TCP and originating from
+10.0.0.101:32123 to 10.0.0.1:80 use:
+.Pp
+.Dl # pfctl -k key -k 'tcp 10.0.0.1:80 <- 10.0.0.101:32123'
+.Pp
To kill one specific state by its unique state ID
(as shown by pfctl -s state -vv),
use the
@@ -332,6 +370,10 @@ To kill all states using a gateway in 192.168.0.0/24:
.Pp
.Dl # pfctl -k gateway -k 192.168.0.0/24
.Pp
+States can also be killed based on their pre-NAT address:
+.Pp
+.Dl # pfctl -k nat -k 192.168.0.1
+.Pp
.It Fl M
Kill matching states in the opposite direction (on other interfaces) when
killing states.
@@ -360,15 +402,16 @@ Other rules and options are ignored.
.It Fl o Ar level
Control the ruleset optimizer, overriding any rule file settings.
.Pp
-.Bl -tag -width xxxxxxxxxxxx -compact
-.It Fl o Cm none
+.Bl -tag -width xxxxxxxxx -compact
+.It Cm none
Disable the ruleset optimizer.
-.It Fl o Cm basic
+.It Cm basic
Enable basic ruleset optimizations.
This is the default behaviour.
-.It Fl o Cm profile
+.It Cm profile
Enable basic ruleset optimizations with profiling.
.El
+.Pp
For further information on the ruleset optimizer, see
.Xr pf.conf 5 .
.It Fl P
@@ -385,16 +428,20 @@ Only print errors and warnings.
Load only the filter rules present in the rule file.
Other rules and options are ignored.
.It Fl r
-Perform reverse DNS lookups on states when displaying them.
-.It Fl s Ar modifier
+Perform reverse DNS lookups on states and tables when displaying them.
+.Fl N
+and
+.Fl r
+are mutually exclusive.
+.It Fl s Ar modifier Op Fl R Ar id
Show the filter parameters specified by
.Ar modifier
(may be abbreviated):
.Pp
-.Bl -tag -width xxxxxxxxxxxxx -compact
-.It Fl s Cm nat
+.Bl -tag -width xxxxxxxxxxx -compact
+.It Cm nat
Show the currently loaded NAT rules.
-.It Fl s Cm queue
+.It Cm queue
Show the currently loaded queue rules.
When used together with
.Fl v ,
@@ -404,18 +451,25 @@ When used together with
.Nm
will loop and show updated queue statistics every five seconds, including
measured bandwidth and packets per second.
-.It Fl s Cm ether
+.It Cm ether
Show the currently loaded Ethernet rules.
When used together with
.Fl v ,
the per-rule statistics (number of evaluations,
packets, and bytes) are also shown.
-.It Fl s Cm rules
+.It Cm rules
Show the currently loaded filter rules.
When used together with
.Fl v ,
the per-rule statistics (number of evaluations,
packets, and bytes) are also shown.
+When used together with
+.Fl g
+or
+.Fl vv ,
+expired rules
+.Pq marked as Dq # expired
+are also shown.
Note that the
.Dq skip step
optimization done automatically by the kernel
@@ -423,7 +477,7 @@ will skip evaluation of rules where possible.
Packets passed statefully are counted in the rule that created the state
(even though the rule is not evaluated more than once for the entire
connection).
-.It Fl s Cm Anchors
+.It Cm Anchors
Show the currently loaded anchors directly attached to the main ruleset.
If
.Fl a Ar anchor
@@ -434,31 +488,34 @@ If
.Fl v
is specified, all anchors attached under the target anchor will be
displayed recursively.
-.It Fl s Cm states
+.It Cm states
Show the contents of the state table.
-.It Fl s Cm Sources
+.It Cm Sources
Show the contents of the source tracking table.
-.It Fl s Cm info
+.It Cm info
Show filter information (statistics and counters).
When used together with
.Fl v ,
-source tracking statistics are also shown.
-.It Fl s Cm Running
+source tracking statistics, the firewall's 32-bit hostid number and the
+main ruleset's MD5 checksum for use with
+.Xr pfsync 4
+are also shown.
+.It Cm Running
Show the running status and provide a non-zero exit status when disabled.
-.It Fl s Cm labels
+.It Cm labels
Show per-rule statistics (label, evaluations, packets total, bytes total,
packets in, bytes in, packets out, bytes out, state creations) of
filter rules with labels, useful for accounting.
-.It Fl s Cm timeouts
+.It Cm timeouts
Show the current global timeouts.
-.It Fl s Cm memory
+.It Cm memory
Show the current pool memory hard limits.
-.It Fl s Cm Tables
+.It Cm Tables
Show the list of tables.
-.It Fl s Cm osfp
+.It Cm osfp
Show the list of operating system fingerprints.
-.It Fl s Cm Interfaces
-Show the list of interfaces and interface drivers available to PF.
+.It Cm Interfaces
+Show the list of interfaces and interface groups available to PF.
When used together with
.Fl v ,
it additionally lists which interfaces have skip rules activated.
@@ -467,43 +524,93 @@ When used together with
interface statistics are also shown.
.Fl i
can be used to select an interface or a group of interfaces.
-.It Fl s Cm all
+.It Cm all
Show all of the above, except for the lists of interfaces and operating
system fingerprints.
.El
-.It Fl T Ar command Op Ar address ...
+.Pp
+Counters shown with
+.Fl s Cm info
+are:
+.Pp
+.Bl -tag -width xxxxxxxxxxxxxx -compact
+.It match
+explicit rule match
+.It bad-offset
+currently unused
+.It fragment
+invalid fragments dropped
+.It short
+short packets dropped
+.It normalize
+dropped by normalizer: illegal packets
+.It memory
+memory could not be allocated
+.It bad-timestamp
+bad TCP timestamp; RFC 1323
+.It congestion
+network interface queue congested
+.It ip-option
+bad IP/IPv6 options
+.It proto-cksum
+invalid protocol checksum
+.It state-mismatch
+packet was associated with a state entry, but sequence numbers did not match
+.It state-insert
+state insertion failure
+.It state-limit
+configured state limit was reached
+.It src-limit
+source node/connection limit
+.It synproxy
+dropped by synproxy
+.It map-failed
+address mapping failed
+.It translate
+no free ports in translation port range
+.El
+.It Fl S
+Do not perform domain name resolution.
+If a name cannot be resolved without DNS, an error will be reported.
+.It Fl t Ar table Fl T Ar command Op Ar address ...
Specify the
.Ar command
-(may be abbreviated) to apply to the table.
+(may be abbreviated) to apply to
+.Ar table .
Commands include:
.Pp
-.Bl -tag -width xxxxxxxxxxxx -compact
-.It Fl T Cm kill
-Kill a table.
-.It Fl T Cm flush
-Flush all addresses of a table.
-.It Fl T Cm add
-Add one or more addresses in a table.
-Automatically create a nonexisting table.
-.It Fl T Cm delete
+.Bl -tag -width "expire number" -compact
+.It Cm add
+Add one or more addresses to a table.
+Automatically create a persistent table if it does not exist.
+.It Cm delete
Delete one or more addresses from a table.
-.It Fl T Cm expire Ar number
+.It Cm expire Ar number
Delete addresses which had their statistics cleared more than
.Ar number
seconds ago.
For entries which have never had their statistics cleared,
.Ar number
refers to the time they were added to the table.
-.It Fl T Cm replace
+.It Cm flush
+Flush all addresses in a table.
+.It Cm kill
+Kill a table.
+.It Cm replace
Replace the addresses of the table.
-Automatically create a nonexisting table.
-.It Fl T Cm show
+Automatically create a persistent table if it does not exist.
+.It Cm show
Show the content (addresses) of a table.
-.It Fl T Cm test
+.It Cm test
Test if the given addresses match a table.
-.It Fl T Cm zero
-Clear all the statistics of a table.
-.It Fl T Cm load
+.It Cm zero Op Ar address ...
+Clear all the statistics of a table, or only for specified addresses.
+.It Cm reset
+Clear statistics only for addresses with non-zero statistics. Addresses
+with counter values at zero and their
+.Dq Cleared
+timestamp are left untouched.
+.It Cm load
Load only the table definitions from
.Xr pf.conf 5 .
This is used in conjunction with the
@@ -526,6 +633,8 @@ line and/or in an unformatted text file, using the
flag.
Comments starting with a
.Sq #
+or
+.Sq \;
are allowed in the text file.
With these commands, the
.Fl v
@@ -666,8 +775,6 @@ tables of the same name from anchors attached below it.
.It C
This flag is set when per-address counters are enabled on the table.
.El
-.It Fl t Ar table
-Specify the name of the table.
.It Fl v
Produce more verbose output.
A second use of
diff --git a/sbin/pfctl/pfctl.c b/sbin/pfctl/pfctl.c
index bfa76b299a02..21562fa03e0d 100644
--- a/sbin/pfctl/pfctl.c
+++ b/sbin/pfctl/pfctl.c
@@ -59,6 +59,8 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <stdarg.h>
+#include <libgen.h>
#include "pfctl_parser.h"
#include "pfctl.h"
@@ -66,23 +68,26 @@
void usage(void);
int pfctl_enable(int, int);
int pfctl_disable(int, int);
-int pfctl_clear_stats(int, int);
-int pfctl_get_skip_ifaces(void);
-int pfctl_check_skip_ifaces(char *);
-int pfctl_adjust_skip_ifaces(struct pfctl *);
-int pfctl_clear_interface_flags(int, int);
-int pfctl_flush_eth_rules(int, int, char *);
+void pfctl_clear_stats(struct pfctl_handle *, int);
+void pfctl_get_skip_ifaces(void);
+void pfctl_check_skip_ifaces(char *);
+void pfctl_adjust_skip_ifaces(struct pfctl *);
+void pfctl_clear_interface_flags(int, int);
+void pfctl_flush_eth_rules(int, int, char *);
int pfctl_flush_rules(int, int, char *);
-int pfctl_flush_nat(int, int, char *);
+void pfctl_flush_nat(int, int, char *);
int pfctl_clear_altq(int, int);
-int pfctl_clear_src_nodes(int, int);
-int pfctl_clear_iface_states(int, const char *, int);
-void pfctl_addrprefix(char *, struct pf_addr *);
-int pfctl_kill_src_nodes(int, const char *, int);
-int pfctl_net_kill_states(int, const char *, int);
-int pfctl_gateway_kill_states(int, const char *, int);
-int pfctl_label_kill_states(int, const char *, int);
-int pfctl_id_kill_states(int, const char *, int);
+void pfctl_clear_src_nodes(int, int);
+void pfctl_clear_iface_states(int, const char *, int);
+struct addrinfo *
+ pfctl_addrprefix(char *, struct pf_addr *, int);
+void pfctl_kill_src_nodes(int, int);
+void pfctl_net_kill_states(int, const char *, int);
+void pfctl_gateway_kill_states(int, const char *, int);
+void pfctl_label_kill_states(int, const char *, int);
+void pfctl_id_kill_states(int, const char *, int);
+void pfctl_key_kill_states(int, const char *, int);
+int pfctl_parse_host(char *, struct pf_rule_addr *);
void pfctl_init_options(struct pfctl *);
int pfctl_load_options(struct pfctl *);
int pfctl_load_limit(struct pfctl *, unsigned int, unsigned int);
@@ -93,18 +98,20 @@ int pfctl_load_hostid(struct pfctl *, u_int32_t);
int pfctl_load_reassembly(struct pfctl *, u_int32_t);
int pfctl_load_syncookies(struct pfctl *, u_int8_t);
int pfctl_get_pool(int, struct pfctl_pool *, u_int32_t, u_int32_t, int,
- char *);
+ const char *, int);
void pfctl_print_eth_rule_counters(struct pfctl_eth_rule *, int);
void pfctl_print_rule_counters(struct pfctl_rule *, int);
int pfctl_show_eth_rules(int, char *, int, enum pfctl_show, char *, int, int);
int pfctl_show_rules(int, char *, int, enum pfctl_show, char *, int, int);
-int pfctl_show_nat(int, char *, int, char *, int);
+int pfctl_show_nat(int, const char *, int, char *, int, int);
int pfctl_show_src_nodes(int, int);
int pfctl_show_states(int, const char *, int);
int pfctl_show_status(int, int);
int pfctl_show_running(int);
int pfctl_show_timeouts(int, int);
int pfctl_show_limits(int, int);
+void pfctl_read_limits(struct pfctl_handle *);
+void pfctl_restore_limits(void);
void pfctl_debug(int, u_int32_t, int);
int pfctl_test_altqsupport(int, int);
int pfctl_show_anchors(int, int, char *);
@@ -120,6 +127,19 @@ int pfctl_load_ruleset(struct pfctl *, char *,
struct pfctl_ruleset *, int, int);
int pfctl_load_rule(struct pfctl *, char *, struct pfctl_rule *, int);
const char *pfctl_lookup_option(char *, const char * const *);
+void pfctl_reset(int, int);
+int pfctl_walk_show(int, struct pfioc_ruleset *, void *);
+int pfctl_walk_get(int, struct pfioc_ruleset *, void *);
+int pfctl_walk_anchors(int, int, const char *,
+ int(*)(int, struct pfioc_ruleset *, void *), void *);
+struct pfr_anchors *
+ pfctl_get_anchors(int, const char *, int);
+int pfctl_recurse(int, int, const char *,
+ int(*)(int, int, struct pfr_anchoritem *));
+int pfctl_call_clearrules(int, int, struct pfr_anchoritem *);
+int pfctl_call_cleartables(int, int, struct pfr_anchoritem *);
+int pfctl_call_clearanchors(int, int, struct pfr_anchoritem *);
+int pfctl_call_showtables(int, int, struct pfr_anchoritem *);
static struct pfctl_anchor_global pf_anchors;
struct pfctl_anchor pf_main_anchor;
@@ -132,7 +152,7 @@ static const char *showopt;
static const char *debugopt;
static char *anchoropt;
static const char *optiopt = NULL;
-static const char *pf_device = "/dev/pf";
+static const char *pf_device = PF_DEVICE;
static char *ifaceopt;
static char *tableopt;
static const char *tblcmdopt;
@@ -144,8 +164,10 @@ int loadopt;
int altqsupport;
int dev = -1;
+struct pfctl_handle *pfh = NULL;
static int first_title = 1;
static int labels = 0;
+static int exit_val = 0;
#define INDENT(d, o) do { \
if (o) { \
@@ -164,9 +186,13 @@ static const struct {
{ "src-nodes", PF_LIMIT_SRC_NODES },
{ "frags", PF_LIMIT_FRAGS },
{ "table-entries", PF_LIMIT_TABLE_ENTRIES },
+ { "anchors", PF_LIMIT_ANCHORS },
+ { "eth-anchors", PF_LIMIT_ETH_ANCHORS },
{ NULL, 0 }
};
+static unsigned int limit_curr[PF_LIMIT_MAX];
+
struct pf_hint {
const char *name;
int timeout;
@@ -227,18 +253,18 @@ static const struct {
static const char * const clearopt_list[] = {
"nat", "queue", "rules", "Sources",
"states", "info", "Tables", "osfp", "all",
- "ethernet", NULL
+ "ethernet", "Reset", NULL
};
static const char * const showopt_list[] = {
"ether", "nat", "queue", "rules", "Anchors", "Sources", "states",
"info", "Interfaces", "labels", "timeouts", "memory", "Tables",
- "osfp", "Running", "all", NULL
+ "osfp", "Running", "all", "creatorids", NULL
};
static const char * const tblcmdopt_list[] = {
"kill", "flush", "add", "delete", "load", "replace", "show",
- "test", "zero", "expire", NULL
+ "test", "zero", "expire", "reset", NULL
};
static const char * const debugopt_list[] = {
@@ -255,7 +281,7 @@ usage(void)
extern char *__progname;
fprintf(stderr,
-"usage: %s [-AdeghMmNnOPqRrvz] [-a anchor] [-D macro=value] [-F modifier]\n"
+"usage: %s [-AdeghMmNnOPqRSrvz] [-a anchor] [-D macro=value] [-F modifier]\n"
"\t[-f file] [-i interface] [-K host | network]\n"
"\t[-k host | network | gateway | label | id] [-o level] [-p device]\n"
"\t[-s modifier] [-t table -T command [address ...]] [-x level]\n",
@@ -264,6 +290,40 @@ usage(void)
exit(1);
}
+void
+pfctl_err(int opts, int eval, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ if ((opts & PF_OPT_IGNFAIL) == 0)
+ verr(eval, fmt, ap);
+ else
+ vwarn(fmt, ap);
+
+ va_end(ap);
+
+ exit_val = eval;
+}
+
+void
+pfctl_errx(int opts, int eval, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ if ((opts & PF_OPT_IGNFAIL) == 0)
+ verrx(eval, fmt, ap);
+ else
+ vwarnx(fmt, ap);
+
+ va_end(ap);
+
+ exit_val = eval;
+}
+
/*
* Cache protocol number to name translations.
*
@@ -310,13 +370,15 @@ pfctl_proto2name(int proto)
int
pfctl_enable(int dev, int opts)
{
- if (ioctl(dev, DIOCSTART)) {
- if (errno == EEXIST)
+ int ret;
+
+ if ((ret = pfctl_startstop(pfh, 1)) != 0) {
+ if (ret == EEXIST)
errx(1, "pf already enabled");
- else if (errno == ESRCH)
+ else if (ret == ESRCH)
errx(1, "pfil registeration failed");
else
- err(1, "DIOCSTART");
+ errc(1, ret, "DIOCSTART");
}
if ((opts & PF_OPT_QUIET) == 0)
fprintf(stderr, "pf enabled\n");
@@ -331,11 +393,13 @@ pfctl_enable(int dev, int opts)
int
pfctl_disable(int dev, int opts)
{
- if (ioctl(dev, DIOCSTOP)) {
- if (errno == ENOENT)
+ int ret;
+
+ if ((ret = pfctl_startstop(pfh, 0)) != 0) {
+ if (ret == ENOENT)
errx(1, "pf not enabled");
else
- err(1, "DIOCSTOP");
+ errc(1, ret, "DIOCSTOP");
}
if ((opts & PF_OPT_QUIET) == 0)
fprintf(stderr, "pf disabled\n");
@@ -347,17 +411,17 @@ pfctl_disable(int dev, int opts)
return (0);
}
-int
-pfctl_clear_stats(int dev, int opts)
+void
+pfctl_clear_stats(struct pfctl_handle *h, int opts)
{
- if (ioctl(dev, DIOCCLRSTATUS))
- err(1, "DIOCCLRSTATUS");
+ int ret;
+ if ((ret = pfctl_clear_status(h)) != 0)
+ pfctl_err(opts, 1, "DIOCCLRSTATUS");
if ((opts & PF_OPT_QUIET) == 0)
fprintf(stderr, "pf: statistics cleared\n");
- return (0);
}
-int
+void
pfctl_get_skip_ifaces(void)
{
bzero(&skip_b, sizeof(skip_b));
@@ -370,10 +434,9 @@ pfctl_get_skip_ifaces(void)
if (skip_b.pfrb_size <= skip_b.pfrb_msize)
break;
}
- return (0);
}
-int
+void
pfctl_check_skip_ifaces(char *ifname)
{
struct pfi_kif *p;
@@ -388,8 +451,6 @@ pfctl_check_skip_ifaces(char *ifname)
continue;
for (n = h; n != NULL; n = n->next) {
- if (p->pfik_ifp == NULL)
- continue;
if (strncmp(p->pfik_name, ifname, IFNAMSIZ))
continue;
@@ -397,10 +458,9 @@ pfctl_check_skip_ifaces(char *ifname)
}
}
}
- return (0);
}
-int
+void
pfctl_adjust_skip_ifaces(struct pfctl *pf)
{
struct pfi_kif *p, *pp;
@@ -416,9 +476,6 @@ pfctl_adjust_skip_ifaces(struct pfctl *pf)
for (n = h; n != NULL; n = n->next)
PFRB_FOREACH(pp, &skip_b) {
- if (pp->pfik_ifp == NULL)
- continue;
-
if (strncmp(pp->pfik_name, n->ifname, IFNAMSIZ))
continue;
@@ -431,16 +488,14 @@ pfctl_adjust_skip_ifaces(struct pfctl *pf)
}
PFRB_FOREACH(p, &skip_b) {
- if (p->pfik_ifp == NULL || ! (p->pfik_flags & PFI_IFLAG_SKIP))
+ if (! (p->pfik_flags & PFI_IFLAG_SKIP))
continue;
pfctl_set_interface_flags(pf, p->pfik_name, PFI_IFLAG_SKIP, 0);
}
-
- return (0);
}
-int
+void
pfctl_clear_interface_flags(int dev, int opts)
{
struct pfioc_iface pi;
@@ -454,10 +509,9 @@ pfctl_clear_interface_flags(int dev, int opts)
if ((opts & PF_OPT_QUIET) == 0)
fprintf(stderr, "pf: interface flags reset\n");
}
- return (0);
}
-int
+void
pfctl_flush_eth_rules(int dev, int opts, char *anchorname)
{
int ret;
@@ -468,8 +522,6 @@ pfctl_flush_eth_rules(int dev, int opts, char *anchorname)
if ((opts & PF_OPT_QUIET) == 0)
fprintf(stderr, "Ethernet rules cleared\n");
-
- return (ret);
}
int
@@ -478,14 +530,16 @@ pfctl_flush_rules(int dev, int opts, char *anchorname)
int ret;
ret = pfctl_clear_rules(dev, anchorname);
- if (ret != 0)
- err(1, "pfctl_clear_rules");
- if ((opts & PF_OPT_QUIET) == 0)
+ if (ret != 0) {
+ pfctl_err(opts, 1, "%s", __func__);
+ return (1);
+ } else if ((opts & PF_OPT_QUIET) == 0)
fprintf(stderr, "rules cleared\n");
+
return (0);
}
-int
+void
pfctl_flush_nat(int dev, int opts, char *anchorname)
{
int ret;
@@ -495,7 +549,6 @@ pfctl_flush_nat(int dev, int opts, char *anchorname)
err(1, "pfctl_clear_nat");
if ((opts & PF_OPT_QUIET) == 0)
fprintf(stderr, "nat cleared\n");
- return (0);
}
int
@@ -516,66 +569,66 @@ pfctl_clear_altq(int dev, int opts)
return (0);
}
-int
+void
pfctl_clear_src_nodes(int dev, int opts)
{
if (ioctl(dev, DIOCCLRSRCNODES))
- err(1, "DIOCCLRSRCNODES");
+ pfctl_err(opts, 1, "DIOCCLRSRCNODES");
if ((opts & PF_OPT_QUIET) == 0)
fprintf(stderr, "source tracking entries cleared\n");
- return (0);
}
-int
+void
pfctl_clear_iface_states(int dev, const char *iface, int opts)
{
struct pfctl_kill kill;
unsigned int killed;
+ int ret;
memset(&kill, 0, sizeof(kill));
if (iface != NULL && strlcpy(kill.ifname, iface,
sizeof(kill.ifname)) >= sizeof(kill.ifname))
- errx(1, "invalid interface: %s", iface);
+ pfctl_errx(opts, 1, "invalid interface: %s", iface);
if (opts & PF_OPT_KILLMATCH)
kill.kill_match = true;
- if (pfctl_clear_states(dev, &kill, &killed))
- err(1, "DIOCCLRSTATES");
+ if ((ret = pfctl_clear_states_h(pfh, &kill, &killed)) != 0)
+ pfctl_err(opts, 1, "DIOCCLRSTATUS");
if ((opts & PF_OPT_QUIET) == 0)
fprintf(stderr, "%d states cleared\n", killed);
- return (0);
}
-void
-pfctl_addrprefix(char *addr, struct pf_addr *mask)
+struct addrinfo *
+pfctl_addrprefix(char *addr, struct pf_addr *mask, int numeric)
{
char *p;
const char *errstr;
int prefix, ret_ga, q, r;
struct addrinfo hints, *res;
- if ((p = strchr(addr, '/')) == NULL)
- return;
-
- *p++ = '\0';
- prefix = strtonum(p, 0, 128, &errstr);
- if (errstr)
- errx(1, "prefix is %s: %s", errstr, p);
-
bzero(&hints, sizeof(hints));
- /* prefix only with numeric addresses */
- hints.ai_flags |= AI_NUMERICHOST;
+ hints.ai_socktype = SOCK_DGRAM; /* dummy */
+ if (numeric)
+ hints.ai_flags = AI_NUMERICHOST;
+
+ if ((p = strchr(addr, '/')) != NULL) {
+ *p++ = '\0';
+ /* prefix only with numeric addresses */
+ hints.ai_flags |= AI_NUMERICHOST;
+ }
if ((ret_ga = getaddrinfo(addr, NULL, &hints, &res))) {
errx(1, "getaddrinfo: %s", gai_strerror(ret_ga));
/* NOTREACHED */
}
- if (res->ai_family == AF_INET && prefix > 32)
- errx(1, "prefix too long for AF_INET");
- else if (res->ai_family == AF_INET6 && prefix > 128)
- errx(1, "prefix too long for AF_INET6");
+ if (p == NULL)
+ return (res);
+
+ prefix = strtonum(p, 0, res->ai_family == AF_INET6 ? 128 : 32, &errstr);
+ if (errstr)
+ errx(1, "prefix is %s: %s", errstr, p);
q = prefix >> 3;
r = prefix & 7;
@@ -594,17 +647,17 @@ pfctl_addrprefix(char *addr, struct pf_addr *mask)
(0xff00 >> r) & 0xff;
break;
}
- freeaddrinfo(res);
+
+ return (res);
}
-int
-pfctl_kill_src_nodes(int dev, const char *iface, int opts)
+void
+pfctl_kill_src_nodes(int dev, int opts)
{
struct pfioc_src_node_kill psnk;
struct addrinfo *res[2], *resp[2];
struct sockaddr last_src, last_dst;
int killed, sources, dests;
- int ret_ga;
killed = sources = dests = 0;
@@ -614,12 +667,9 @@ pfctl_kill_src_nodes(int dev, const char *iface, int opts)
memset(&last_src, 0xff, sizeof(last_src));
memset(&last_dst, 0xff, sizeof(last_dst));
- pfctl_addrprefix(src_node_kill[0], &psnk.psnk_src.addr.v.a.mask);
+ res[0] = pfctl_addrprefix(src_node_kill[0],
+ &psnk.psnk_src.addr.v.a.mask, (opts & PF_OPT_NODNS));
- if ((ret_ga = getaddrinfo(src_node_kill[0], NULL, NULL, &res[0]))) {
- errx(1, "getaddrinfo: %s", gai_strerror(ret_ga));
- /* NOTREACHED */
- }
for (resp[0] = res[0]; resp[0]; resp[0] = resp[0]->ai_next) {
if (resp[0]->ai_addr == NULL)
continue;
@@ -631,29 +681,16 @@ pfctl_kill_src_nodes(int dev, const char *iface, int opts)
psnk.psnk_af = resp[0]->ai_family;
sources++;
- if (psnk.psnk_af == AF_INET)
- psnk.psnk_src.addr.v.a.addr.v4 =
- ((struct sockaddr_in *)resp[0]->ai_addr)->sin_addr;
- else if (psnk.psnk_af == AF_INET6)
- psnk.psnk_src.addr.v.a.addr.v6 =
- ((struct sockaddr_in6 *)resp[0]->ai_addr)->
- sin6_addr;
- else
- errx(1, "Unknown address family %d", psnk.psnk_af);
+ copy_satopfaddr(&psnk.psnk_src.addr.v.a.addr, resp[0]->ai_addr);
if (src_node_killers > 1) {
dests = 0;
memset(&psnk.psnk_dst.addr.v.a.mask, 0xff,
sizeof(psnk.psnk_dst.addr.v.a.mask));
memset(&last_dst, 0xff, sizeof(last_dst));
- pfctl_addrprefix(src_node_kill[1],
- &psnk.psnk_dst.addr.v.a.mask);
- if ((ret_ga = getaddrinfo(src_node_kill[1], NULL, NULL,
- &res[1]))) {
- errx(1, "getaddrinfo: %s",
- gai_strerror(ret_ga));
- /* NOTREACHED */
- }
+ res[1] = pfctl_addrprefix(src_node_kill[1],
+ &psnk.psnk_dst.addr.v.a.mask,
+ (opts & PF_OPT_NODNS));
for (resp[1] = res[1]; resp[1];
resp[1] = resp[1]->ai_next) {
if (resp[1]->ai_addr == NULL)
@@ -668,18 +705,8 @@ pfctl_kill_src_nodes(int dev, const char *iface, int opts)
dests++;
- if (psnk.psnk_af == AF_INET)
- psnk.psnk_dst.addr.v.a.addr.v4 =
- ((struct sockaddr_in *)resp[1]->
- ai_addr)->sin_addr;
- else if (psnk.psnk_af == AF_INET6)
- psnk.psnk_dst.addr.v.a.addr.v6 =
- ((struct sockaddr_in6 *)resp[1]->
- ai_addr)->sin6_addr;
- else
- errx(1, "Unknown address family %d",
- psnk.psnk_af);
-
+ copy_satopfaddr(&psnk.psnk_dst.addr.v.a.addr,
+ resp[1]->ai_addr);
if (ioctl(dev, DIOCKILLSRCNODES, &psnk))
err(1, "DIOCKILLSRCNODES");
killed += psnk.psnk_killed;
@@ -697,10 +724,9 @@ pfctl_kill_src_nodes(int dev, const char *iface, int opts)
if ((opts & PF_OPT_QUIET) == 0)
fprintf(stderr, "killed %d src nodes from %d sources and %d "
"destinations\n", killed, sources, dests);
- return (0);
}
-int
+void
pfctl_net_kill_states(int dev, const char *iface, int opts)
{
struct pfctl_kill kill;
@@ -708,7 +734,7 @@ pfctl_net_kill_states(int dev, const char *iface, int opts)
struct sockaddr last_src, last_dst;
unsigned int newkilled;
int killed, sources, dests;
- int ret_ga;
+ int ret;
killed = sources = dests = 0;
@@ -719,17 +745,20 @@ pfctl_net_kill_states(int dev, const char *iface, int opts)
memset(&last_dst, 0xff, sizeof(last_dst));
if (iface != NULL && strlcpy(kill.ifname, iface,
sizeof(kill.ifname)) >= sizeof(kill.ifname))
- errx(1, "invalid interface: %s", iface);
+ pfctl_errx(opts, 1, "invalid interface: %s", iface);
+
+ if (state_killers == 2 && (strcmp(state_kill[0], "nat") == 0)) {
+ kill.nat = true;
+ state_kill[0] = state_kill[1];
+ state_killers = 1;
+ }
- pfctl_addrprefix(state_kill[0], &kill.src.addr.v.a.mask);
+ res[0] = pfctl_addrprefix(state_kill[0],
+ &kill.src.addr.v.a.mask, (opts & PF_OPT_NODNS));
if (opts & PF_OPT_KILLMATCH)
kill.kill_match = true;
- if ((ret_ga = getaddrinfo(state_kill[0], NULL, NULL, &res[0]))) {
- errx(1, "getaddrinfo: %s", gai_strerror(ret_ga));
- /* NOTREACHED */
- }
for (resp[0] = res[0]; resp[0]; resp[0] = resp[0]->ai_next) {
if (resp[0]->ai_addr == NULL)
continue;
@@ -741,29 +770,16 @@ pfctl_net_kill_states(int dev, const char *iface, int opts)
kill.af = resp[0]->ai_family;
sources++;
- if (kill.af == AF_INET)
- kill.src.addr.v.a.addr.v4 =
- ((struct sockaddr_in *)resp[0]->ai_addr)->sin_addr;
- else if (kill.af == AF_INET6)
- kill.src.addr.v.a.addr.v6 =
- ((struct sockaddr_in6 *)resp[0]->ai_addr)->
- sin6_addr;
- else
- errx(1, "Unknown address family %d", kill.af);
+ copy_satopfaddr(&kill.src.addr.v.a.addr, resp[0]->ai_addr);
if (state_killers > 1) {
dests = 0;
memset(&kill.dst.addr.v.a.mask, 0xff,
sizeof(kill.dst.addr.v.a.mask));
memset(&last_dst, 0xff, sizeof(last_dst));
- pfctl_addrprefix(state_kill[1],
- &kill.dst.addr.v.a.mask);
- if ((ret_ga = getaddrinfo(state_kill[1], NULL, NULL,
- &res[1]))) {
- errx(1, "getaddrinfo: %s",
- gai_strerror(ret_ga));
- /* NOTREACHED */
- }
+ res[1] = pfctl_addrprefix(state_kill[1],
+ &kill.dst.addr.v.a.mask,
+ (opts & PF_OPT_NODNS));
for (resp[1] = res[1]; resp[1];
resp[1] = resp[1]->ai_next) {
if (resp[1]->ai_addr == NULL)
@@ -778,26 +794,17 @@ pfctl_net_kill_states(int dev, const char *iface, int opts)
dests++;
- if (kill.af == AF_INET)
- kill.dst.addr.v.a.addr.v4 =
- ((struct sockaddr_in *)resp[1]->
- ai_addr)->sin_addr;
- else if (kill.af == AF_INET6)
- kill.dst.addr.v.a.addr.v6 =
- ((struct sockaddr_in6 *)resp[1]->
- ai_addr)->sin6_addr;
- else
- errx(1, "Unknown address family %d",
- kill.af);
+ copy_satopfaddr(&kill.dst.addr.v.a.addr,
+ resp[1]->ai_addr);
- if (pfctl_kill_states(dev, &kill, &newkilled))
- err(1, "DIOCKILLSTATES");
+ if ((ret = pfctl_kill_states_h(pfh, &kill, &newkilled)) != 0)
+ pfctl_errx(opts, 1, "DIOCKILLSTATES");
killed += newkilled;
}
freeaddrinfo(res[1]);
} else {
- if (pfctl_kill_states(dev, &kill, &newkilled))
- err(1, "DIOCKILLSTATES");
+ if ((ret = pfctl_kill_states_h(pfh, &kill, &newkilled)) != 0)
+ pfctl_errx(opts, 1, "DIOCKILLSTATES");
killed += newkilled;
}
}
@@ -807,10 +814,9 @@ pfctl_net_kill_states(int dev, const char *iface, int opts)
if ((opts & PF_OPT_QUIET) == 0)
fprintf(stderr, "killed %d states from %d sources and %d "
"destinations\n", killed, sources, dests);
- return (0);
}
-int
+void
pfctl_gateway_kill_states(int dev, const char *iface, int opts)
{
struct pfctl_kill kill;
@@ -818,7 +824,6 @@ pfctl_gateway_kill_states(int dev, const char *iface, int opts)
struct sockaddr last_src;
unsigned int newkilled;
int killed = 0;
- int ret_ga;
if (state_killers != 2 || (strlen(state_kill[1]) == 0)) {
warnx("no gateway specified");
@@ -831,17 +836,14 @@ pfctl_gateway_kill_states(int dev, const char *iface, int opts)
memset(&last_src, 0xff, sizeof(last_src));
if (iface != NULL && strlcpy(kill.ifname, iface,
sizeof(kill.ifname)) >= sizeof(kill.ifname))
- errx(1, "invalid interface: %s", iface);
+ pfctl_errx(opts, 1, "invalid interface: %s", iface);
if (opts & PF_OPT_KILLMATCH)
kill.kill_match = true;
- pfctl_addrprefix(state_kill[1], &kill.rt_addr.addr.v.a.mask);
+ res = pfctl_addrprefix(state_kill[1], &kill.rt_addr.addr.v.a.mask,
+ (opts & PF_OPT_NODNS));
- if ((ret_ga = getaddrinfo(state_kill[1], NULL, NULL, &res))) {
- errx(1, "getaddrinfo: %s", gai_strerror(ret_ga));
- /* NOTREACHED */
- }
for (resp = res; resp; resp = resp->ai_next) {
if (resp->ai_addr == NULL)
continue;
@@ -852,18 +854,10 @@ pfctl_gateway_kill_states(int dev, const char *iface, int opts)
kill.af = resp->ai_family;
- if (kill.af == AF_INET)
- kill.rt_addr.addr.v.a.addr.v4 =
- ((struct sockaddr_in *)resp->ai_addr)->sin_addr;
- else if (kill.af == AF_INET6)
- kill.rt_addr.addr.v.a.addr.v6 =
- ((struct sockaddr_in6 *)resp->ai_addr)->
- sin6_addr;
- else
- errx(1, "Unknown address family %d", kill.af);
-
- if (pfctl_kill_states(dev, &kill, &newkilled))
- err(1, "DIOCKILLSTATES");
+ copy_satopfaddr(&kill.rt_addr.addr.v.a.addr,
+ resp->ai_addr);
+ if (pfctl_kill_states_h(pfh, &kill, &newkilled))
+ pfctl_errx(opts, 1, "DIOCKILLSTATES");
killed += newkilled;
}
@@ -871,14 +865,14 @@ pfctl_gateway_kill_states(int dev, const char *iface, int opts)
if ((opts & PF_OPT_QUIET) == 0)
fprintf(stderr, "killed %d states\n", killed);
- return (0);
}
-int
+void
pfctl_label_kill_states(int dev, const char *iface, int opts)
{
struct pfctl_kill kill;
unsigned int killed;
+ int ret;
if (state_killers != 2 || (strlen(state_kill[1]) == 0)) {
warnx("no label specified");
@@ -887,7 +881,7 @@ pfctl_label_kill_states(int dev, const char *iface, int opts)
memset(&kill, 0, sizeof(kill));
if (iface != NULL && strlcpy(kill.ifname, iface,
sizeof(kill.ifname)) >= sizeof(kill.ifname))
- errx(1, "invalid interface: %s", iface);
+ pfctl_errx(opts, 1, "invalid interface: %s", iface);
if (opts & PF_OPT_KILLMATCH)
kill.kill_match = true;
@@ -896,20 +890,19 @@ pfctl_label_kill_states(int dev, const char *iface, int opts)
sizeof(kill.label))
errx(1, "label too long: %s", state_kill[1]);
- if (pfctl_kill_states(dev, &kill, &killed))
- err(1, "DIOCKILLSTATES");
+ if ((ret = pfctl_kill_states_h(pfh, &kill, &killed)) != 0)
+ pfctl_errx(opts, 1, "DIOCKILLSTATES");
if ((opts & PF_OPT_QUIET) == 0)
fprintf(stderr, "killed %d states\n", killed);
-
- return (0);
}
-int
+void
pfctl_id_kill_states(int dev, const char *iface, int opts)
{
struct pfctl_kill kill;
unsigned int killed;
+ int ret;
if (state_killers != 2 || (strlen(state_kill[1]) == 0)) {
warnx("no id specified");
@@ -935,44 +928,144 @@ pfctl_id_kill_states(int dev, const char *iface, int opts)
usage();
}
- if (pfctl_kill_states(dev, &kill, &killed))
- err(1, "DIOCKILLSTATES");
+ if ((ret = pfctl_kill_states_h(pfh, &kill, &killed)) != 0)
+ pfctl_errx(opts, 1, "DIOCKILLSTATES");
+
+ if ((opts & PF_OPT_QUIET) == 0)
+ fprintf(stderr, "killed %d states\n", killed);
+}
+
+void
+pfctl_key_kill_states(int dev, const char *iface, int opts)
+{
+ struct pfctl_kill kill;
+ char *s, *token, *tokens[4];
+ struct protoent *p;
+ u_int i, sidx, didx;
+ int ret, killed;
+
+ if (state_killers != 2 || (strlen(state_kill[1]) == 0)) {
+ warnx("no key specified");
+ usage();
+ }
+ memset(&kill, 0, sizeof(kill));
+
+ if (iface != NULL &&
+ strlcpy(kill.ifname, iface, sizeof(kill.ifname)) >=
+ sizeof(kill.ifname))
+ pfctl_errx(opts, 1, "invalid interface: %s", iface);
+
+ s = strdup(state_kill[1]);
+ if (!s)
+ errx(1, "%s: strdup", __func__);
+ i = 0;
+ while ((token = strsep(&s, " \t")) != NULL)
+ if (*token != '\0') {
+ if (i < 4)
+ tokens[i] = token;
+ i++;
+ }
+ if (i != 4)
+ errx(1, "%s: key must be "
+ "\"protocol host1:port1 direction host2:port2\" format",
+ __func__);
+
+ if ((p = getprotobyname(tokens[0])) == NULL)
+ errx(1, "invalid protocol: %s", tokens[0]);
+ kill.proto = p->p_proto;
+
+ if (strcmp(tokens[2], "->") == 0) {
+ sidx = 1;
+ didx = 3;
+ } else if (strcmp(tokens[2], "<-") == 0) {
+ sidx = 3;
+ didx = 1;
+ } else
+ errx(1, "invalid direction: %s", tokens[2]);
+
+ if (pfctl_parse_host(tokens[sidx], &kill.src) == -1)
+ errx(1, "invalid host: %s", tokens[sidx]);
+ if (pfctl_parse_host(tokens[didx], &kill.dst) == -1)
+ errx(1, "invalid host: %s", tokens[didx]);
+
+ if ((ret = pfctl_kill_states_h(pfh, &kill, &killed)) != 0)
+ pfctl_errx(opts, 1, "DIOCKILLSTATES");
if ((opts & PF_OPT_QUIET) == 0)
fprintf(stderr, "killed %d states\n", killed);
+}
+
+int
+pfctl_parse_host(char *str, struct pf_rule_addr *addr)
+{
+ char *s = NULL, *sbs, *sbe;
+ struct addrinfo hints, *ai;
+
+ s = strdup(str);
+ if (!s)
+ errx(1, "pfctl_parse_host: strdup");
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_socktype = SOCK_DGRAM; /* dummy */
+ hints.ai_flags = AI_NUMERICHOST;
+
+ if ((sbs = strchr(s, '[')) != NULL && (sbe = strrchr(s, ']')) != NULL) {
+ hints.ai_family = AF_INET6;
+ *(sbs++) = *sbe = '\0';
+ } else if ((sbs = strchr(s, ':')) != NULL) {
+ hints.ai_family = AF_INET;
+ *(sbs++) = '\0';
+ } else {
+ /* Assume that no ':<number>' means port 0 */
+ }
+
+ if (getaddrinfo(s, sbs, &hints, &ai) != 0)
+ goto error;
+
+ copy_satopfaddr(&addr->addr.v.a.addr, ai->ai_addr);
+ addr->port[0] = ai->ai_family == AF_INET6 ?
+ ((struct sockaddr_in6 *)ai->ai_addr)->sin6_port :
+ ((struct sockaddr_in *)ai->ai_addr)->sin_port;
+ freeaddrinfo(ai);
+ free(s);
+
+ memset(&addr->addr.v.a.mask, 0xff, sizeof(struct pf_addr));
+ addr->port_op = PF_OP_EQ;
+ addr->addr.type = PF_ADDR_ADDRMASK;
return (0);
+
+error:
+ free(s);
+ return (-1);
}
int
pfctl_get_pool(int dev, struct pfctl_pool *pool, u_int32_t nr,
- u_int32_t ticket, int r_action, char *anchorname)
+ u_int32_t ticket, int r_action, const char *anchorname, int which)
{
struct pfioc_pooladdr pp;
- struct pf_pooladdr *pa;
+ struct pfctl_pooladdr *pa;
u_int32_t pnr, mpnr;
+ int ret;
memset(&pp, 0, sizeof(pp));
- memcpy(pp.anchor, anchorname, sizeof(pp.anchor));
- pp.r_action = r_action;
- pp.r_num = nr;
- pp.ticket = ticket;
- if (ioctl(dev, DIOCGETADDRS, &pp)) {
- warn("DIOCGETADDRS");
+ if ((ret = pfctl_get_addrs(pfh, ticket, nr, r_action, anchorname, &mpnr, which)) != 0) {
+ warnc(ret, "DIOCGETADDRS");
return (-1);
}
- mpnr = pp.nr;
+
TAILQ_INIT(&pool->list);
for (pnr = 0; pnr < mpnr; ++pnr) {
- pp.nr = pnr;
- if (ioctl(dev, DIOCGETADDR, &pp)) {
- warn("DIOCGETADDR");
+ if ((ret = pfctl_get_addr(pfh, ticket, nr, r_action, anchorname, pnr, &pp, which)) != 0) {
+ warnc(ret, "DIOCGETADDR");
return (-1);
}
- pa = calloc(1, sizeof(struct pf_pooladdr));
+ pa = calloc(1, sizeof(struct pfctl_pooladdr));
if (pa == NULL)
err(1, "calloc");
- bcopy(&pp.addr, pa, sizeof(struct pf_pooladdr));
+ bcopy(&pp.addr, pa, sizeof(struct pfctl_pooladdr));
+ pa->af = pp.af;
TAILQ_INSERT_TAIL(&pool->list, pa, entries);
}
@@ -982,7 +1075,7 @@ pfctl_get_pool(int dev, struct pfctl_pool *pool, u_int32_t nr,
void
pfctl_move_pool(struct pfctl_pool *src, struct pfctl_pool *dst)
{
- struct pf_pooladdr *pa;
+ struct pfctl_pooladdr *pa;
while ((pa = TAILQ_FIRST(&src->list)) != NULL) {
TAILQ_REMOVE(&src->list, pa, entries);
@@ -993,7 +1086,7 @@ pfctl_move_pool(struct pfctl_pool *src, struct pfctl_pool *dst)
void
pfctl_clear_pool(struct pfctl_pool *pool)
{
- struct pf_pooladdr *pa;
+ struct pfctl_pooladdr *pa;
while ((pa = TAILQ_FIRST(&pool->list)) != NULL) {
TAILQ_REMOVE(&pool->list, pa, entries);
@@ -1032,7 +1125,7 @@ pfctl_print_rule_counters(struct pfctl_rule *rule, int opts)
{
if (opts & PF_OPT_DEBUG) {
const char *t[PF_SKIP_COUNT] = { "i", "d", "f",
- "p", "sa", "sp", "da", "dp" };
+ "p", "sa", "da", "sp", "dp" };
int i;
printf(" [ Skip steps: ");
@@ -1049,6 +1142,9 @@ pfctl_print_rule_counters(struct pfctl_rule *rule, int opts)
printf(" [ queue: qname=%s qid=%u pqname=%s pqid=%u ]\n",
rule->qname, rule->qid, rule->pqname, rule->pqid);
+ if (rule->rule_flag & PFRULE_EXPIRED)
+ printf(" [ Expired: %lld secs ago ]\n",
+ (long long)(time(NULL) - rule->exptime));
}
if (opts & PF_OPT_VERBOSE) {
printf(" [ Evaluations: %-8llu Packets: %-8llu "
@@ -1058,6 +1154,15 @@ pfctl_print_rule_counters(struct pfctl_rule *rule, int opts)
rule->packets[1]),
(unsigned long long)(rule->bytes[0] +
rule->bytes[1]), (uintmax_t)rule->states_cur);
+ printf(" [ Source Nodes: %-6ju "
+ "Limit: %-6ju "
+ "NAT/RDR: %-6ju "
+ "Route: %-6ju "
+ "]\n",
+ (uintmax_t)rule->src_nodes,
+ (uintmax_t)rule->src_nodes_type[PF_SN_LIMIT],
+ (uintmax_t)rule->src_nodes_type[PF_SN_NAT],
+ (uintmax_t)rule->src_nodes_type[PF_SN_ROUTE]);
if (!(opts & PF_OPT_DEBUG))
printf(" [ Inserted: uid %u pid %u "
"State Creations: %-6ju]\n",
@@ -1096,6 +1201,7 @@ pfctl_show_eth_rules(int dev, char *path, int opts, enum pfctl_show format,
int brace;
int dotitle = opts & PF_OPT_SHOWALL;
int len = strlen(path);
+ int ret;
char *npath, *p;
/*
@@ -1110,7 +1216,7 @@ pfctl_show_eth_rules(int dev, char *path, int opts, enum pfctl_show format,
if (anchorname[0] == '/') {
if ((npath = calloc(1, MAXPATHLEN)) == NULL)
- errx(1, "pfctl_rules: calloc");
+ errx(1, "calloc");
snprintf(npath, MAXPATHLEN, "%s", anchorname);
} else {
if (path[0])
@@ -1128,12 +1234,12 @@ pfctl_show_eth_rules(int dev, char *path, int opts, enum pfctl_show format,
struct pfctl_eth_rulesets_info ri;
u_int32_t mnr, nr;
- if (pfctl_get_eth_rulesets_info(dev, &ri, npath)) {
- if (errno == EINVAL) {
+ if ((ret = pfctl_get_eth_rulesets_info(dev, &ri, npath)) != 0) {
+ if (ret == EINVAL) {
fprintf(stderr, "Anchor '%s' "
"not found.\n", anchorname);
} else {
- warn("DIOCGETETHRULESETS");
+ warnc(ret, "DIOCGETETHRULESETS");
return (-1);
}
}
@@ -1143,8 +1249,8 @@ pfctl_show_eth_rules(int dev, char *path, int opts, enum pfctl_show format,
for (nr = 0; nr < mnr; ++nr) {
struct pfctl_eth_ruleset_info rs;
- if (pfctl_get_eth_ruleset(dev, npath, nr, &rs))
- err(1, "DIOCGETETHRULESET");
+ if ((ret = pfctl_get_eth_ruleset(dev, npath, nr, &rs)) != 0)
+ errc(1, ret, "DIOCGETETHRULESET");
INDENT(depth, !(opts & PF_OPT_VERBOSE));
printf("anchor \"%s\" all {\n", rs.name);
pfctl_show_eth_rules(dev, npath, opts,
@@ -1156,16 +1262,16 @@ pfctl_show_eth_rules(int dev, char *path, int opts, enum pfctl_show format,
return (0);
}
- if (pfctl_get_eth_rules_info(dev, &info, path)) {
- warn("DIOCGETETHRULES");
+ if ((ret = pfctl_get_eth_rules_info(dev, &info, path)) != 0) {
+ warnc(ret, "DIOCGETETHRULES");
return (-1);
}
for (int nr = 0; nr < info.nr; nr++) {
brace = 0;
INDENT(depth, !(opts & PF_OPT_VERBOSE));
- if (pfctl_get_eth_rule(dev, nr, info.ticket, path, &rule,
- opts & PF_OPT_CLRRULECTRS, anchor_call) != 0) {
- warn("DIOCGETETHRULE");
+ if ((ret = pfctl_get_eth_rule(dev, nr, info.ticket, path, &rule,
+ opts & PF_OPT_CLRRULECTRS, anchor_call)) != 0) {
+ warnc(ret, "DIOCGETETHRULE");
return (-1);
}
if (anchor_call[0] &&
@@ -1209,7 +1315,6 @@ pfctl_show_rules(int dev, char *path, int opts, enum pfctl_show format,
struct pfctl_rule rule;
char anchor_call[MAXPATHLEN];
u_int32_t nr, header = 0;
- int rule_numbers = opts & (PF_OPT_VERBOSE2 | PF_OPT_DEBUG);
int numeric = opts & PF_OPT_NUMERIC;
int len = strlen(path), ret = 0;
char *npath, *p;
@@ -1226,8 +1331,8 @@ pfctl_show_rules(int dev, char *path, int opts, enum pfctl_show format,
if (anchorname[0] == '/') {
if ((npath = calloc(1, MAXPATHLEN)) == NULL)
- errx(1, "pfctl_rules: calloc");
- snprintf(npath, MAXPATHLEN, "%s", anchorname);
+ errx(1, "calloc");
+ strlcpy(npath, anchorname, MAXPATHLEN);
} else {
if (path[0])
snprintf(&path[len], MAXPATHLEN - len, "/%s", anchorname);
@@ -1245,21 +1350,12 @@ pfctl_show_rules(int dev, char *path, int opts, enum pfctl_show format,
u_int32_t mnr, nr;
memset(&prs, 0, sizeof(prs));
- memcpy(prs.path, npath, sizeof(prs.path));
- if (ioctl(dev, DIOCGETRULESETS, &prs)) {
- if (errno == EINVAL)
- fprintf(stderr, "Anchor '%s' "
- "not found.\n", anchorname);
- else
- err(1, "DIOCGETRULESETS");
- }
- mnr = prs.nr;
+ if ((ret = pfctl_get_rulesets(pfh, npath, &mnr)) != 0)
+ errx(1, "%s", pf_strerror(ret));
- pfctl_print_rule_counters(&rule, opts);
for (nr = 0; nr < mnr; ++nr) {
- prs.nr = nr;
- if (ioctl(dev, DIOCGETRULESET, &prs))
- err(1, "DIOCGETRULESET");
+ if ((ret = pfctl_get_ruleset(pfh, npath, nr, &prs)) != 0)
+ errx(1, "%s", pf_strerror(ret));
INDENT(depth, !(opts & PF_OPT_VERBOSE));
printf("anchor \"%s\" all {\n", prs.name);
pfctl_show_rules(dev, npath, opts,
@@ -1272,16 +1368,16 @@ pfctl_show_rules(int dev, char *path, int opts, enum pfctl_show format,
}
if (opts & PF_OPT_SHOWALL) {
- ret = pfctl_get_rules_info(dev, &ri, PF_PASS, path);
+ ret = pfctl_get_rules_info_h(pfh, &ri, PF_PASS, path);
if (ret != 0) {
- warn("DIOCGETRULES");
+ warnx("%s", pf_strerror(ret));
goto error;
}
header++;
}
- ret = pfctl_get_rules_info(dev, &ri, PF_SCRUB, path);
+ ret = pfctl_get_rules_info_h(pfh, &ri, PF_SCRUB, path);
if (ret != 0) {
- warn("DIOCGETRULES");
+ warnx("%s", pf_strerror(ret));
goto error;
}
if (opts & PF_OPT_SHOWALL) {
@@ -1292,14 +1388,22 @@ pfctl_show_rules(int dev, char *path, int opts, enum pfctl_show format,
}
for (nr = 0; nr < ri.nr; ++nr) {
- if (pfctl_get_clear_rule(dev, nr, ri.ticket, path, PF_SCRUB,
- &rule, anchor_call, opts & PF_OPT_CLRRULECTRS)) {
- warn("DIOCGETRULENV");
+ if ((ret = pfctl_get_clear_rule_h(pfh, nr, ri.ticket, path, PF_SCRUB,
+ &rule, anchor_call, opts & PF_OPT_CLRRULECTRS)) != 0) {
+ warnc(ret, "DIOCGETRULENV");
goto error;
}
- if (pfctl_get_pool(dev, &rule.rpool,
- nr, ri.ticket, PF_SCRUB, path) != 0)
+ if (pfctl_get_pool(dev, &rule.rdr,
+ nr, ri.ticket, PF_SCRUB, path, PF_RDR) != 0)
+ goto error;
+
+ if (pfctl_get_pool(dev, &rule.nat,
+ nr, ri.ticket, PF_SCRUB, path, PF_NAT) != 0)
+ goto error;
+
+ if (pfctl_get_pool(dev, &rule.route,
+ nr, ri.ticket, PF_SCRUB, path, PF_RT) != 0)
goto error;
switch (format) {
@@ -1308,29 +1412,45 @@ pfctl_show_rules(int dev, char *path, int opts, enum pfctl_show format,
case PFCTL_SHOW_RULES:
if (rule.label[0][0] && (opts & PF_OPT_SHOWALL))
labels = 1;
- print_rule(&rule, anchor_call, rule_numbers, numeric);
- printf("\n");
+ print_rule(&rule, anchor_call, opts, numeric);
+ /*
+ * Do not print newline, when we have not
+ * printed expired rule.
+ */
+ if (!(rule.rule_flag & PFRULE_EXPIRED) ||
+ (opts & (PF_OPT_VERBOSE2|PF_OPT_DEBUG)))
+ printf("\n");
pfctl_print_rule_counters(&rule, opts);
break;
case PFCTL_SHOW_NOTHING:
break;
}
- pfctl_clear_pool(&rule.rpool);
+ pfctl_clear_pool(&rule.rdr);
+ pfctl_clear_pool(&rule.nat);
+ pfctl_clear_pool(&rule.route);
}
- ret = pfctl_get_rules_info(dev, &ri, PF_PASS, path);
+ ret = pfctl_get_rules_info_h(pfh, &ri, PF_PASS, path);
if (ret != 0) {
- warn("DIOCGETRULES");
+ warnc(ret, "DIOCGETRULES");
goto error;
}
for (nr = 0; nr < ri.nr; ++nr) {
- if (pfctl_get_clear_rule(dev, nr, ri.ticket, path, PF_PASS,
- &rule, anchor_call, opts & PF_OPT_CLRRULECTRS)) {
- warn("DIOCGETRULE");
+ if ((ret = pfctl_get_clear_rule_h(pfh, nr, ri.ticket, path, PF_PASS,
+ &rule, anchor_call, opts & PF_OPT_CLRRULECTRS)) != 0) {
+ warnc(ret, "DIOCGETRULE");
goto error;
}
- if (pfctl_get_pool(dev, &rule.rpool,
- nr, ri.ticket, PF_PASS, path) != 0)
+ if (pfctl_get_pool(dev, &rule.rdr,
+ nr, ri.ticket, PF_PASS, path, PF_RDR) != 0)
+ goto error;
+
+ if (pfctl_get_pool(dev, &rule.nat,
+ nr, ri.ticket, PF_PASS, path, PF_NAT) != 0)
+ goto error;
+
+ if (pfctl_get_pool(dev, &rule.route,
+ nr, ri.ticket, PF_PASS, path, PF_RT) != 0)
goto error;
switch (format) {
@@ -1357,13 +1477,21 @@ pfctl_show_rules(int dev, char *path, int opts, enum pfctl_show format,
(unsigned long long)rule.bytes[1],
(uintmax_t)rule.states_tot);
}
+
+ if (anchor_call[0] &&
+ (((p = strrchr(anchor_call, '/')) ?
+ p[1] == '_' : anchor_call[0] == '_') ||
+ opts & PF_OPT_RECURSE)) {
+ pfctl_show_rules(dev, npath, opts, format,
+ anchor_call, depth, rule.anchor_wildcard);
+ }
break;
}
case PFCTL_SHOW_RULES:
if (rule.label[0][0] && (opts & PF_OPT_SHOWALL))
labels = 1;
INDENT(depth, !(opts & PF_OPT_VERBOSE));
- print_rule(&rule, anchor_call, rule_numbers, numeric);
+ print_rule(&rule, anchor_call, opts, numeric);
/*
* If this is a 'unnamed' brace notation
@@ -1389,7 +1517,8 @@ pfctl_show_rules(int dev, char *path, int opts, enum pfctl_show format,
case PFCTL_SHOW_NOTHING:
break;
}
- pfctl_clear_pool(&rule.rpool);
+ pfctl_clear_pool(&rule.rdr);
+ pfctl_clear_pool(&rule.nat);
}
error:
@@ -1398,7 +1527,8 @@ pfctl_show_rules(int dev, char *path, int opts, enum pfctl_show format,
}
int
-pfctl_show_nat(int dev, char *path, int opts, char *anchorname, int depth)
+pfctl_show_nat(int dev, const char *path, int opts, char *anchorname, int depth,
+ int wildcard)
{
struct pfctl_rules_info ri;
struct pfctl_rule rule;
@@ -1406,46 +1536,86 @@ pfctl_show_nat(int dev, char *path, int opts, char *anchorname, int depth)
u_int32_t nr;
static int nattype[3] = { PF_NAT, PF_RDR, PF_BINAT };
int i, dotitle = opts & PF_OPT_SHOWALL;
- int brace, ret;
+ int ret;
int len = strlen(path);
- char *p;
+ char *npath, *p;
- if (path[0])
- snprintf(&path[len], MAXPATHLEN - len, "/%s", anchorname);
- else
- snprintf(&path[len], MAXPATHLEN - len, "%s", anchorname);
+ /*
+ * Truncate a trailing / and * on an anchorname before searching for
+ * the ruleset, this is syntactic sugar that doesn't actually make it
+ * to the kernel.
+ */
+ if ((p = strrchr(anchorname, '/')) != NULL &&
+ p[1] == '*' && p[2] == '\0') {
+ p[0] = '\0';
+ }
+
+ if ((npath = calloc(1, MAXPATHLEN)) == NULL)
+ errx(1, "calloc");
+
+ if (anchorname[0] == '/') {
+ snprintf(npath, MAXPATHLEN, "%s", anchorname);
+ } else {
+ snprintf(npath, MAXPATHLEN, "%s", path);
+ if (npath[0])
+ snprintf(&npath[len], MAXPATHLEN - len, "/%s", anchorname);
+ else
+ snprintf(&npath[len], MAXPATHLEN - len, "%s", anchorname);
+ }
+
+ /*
+ * If this anchor was called with a wildcard path, go through
+ * the rulesets in the anchor rather than the rules.
+ */
+ if (wildcard && (opts & PF_OPT_RECURSE)) {
+ struct pfioc_ruleset prs;
+ u_int32_t mnr, nr;
+ memset(&prs, 0, sizeof(prs));
+ if ((ret = pfctl_get_rulesets(pfh, npath, &mnr)) != 0) {
+ if (ret == EINVAL)
+ fprintf(stderr, "NAT anchor '%s' "
+ "not found.\n", anchorname);
+ else
+ errx(1, "%s", pf_strerror(ret));
+ }
+
+ for (nr = 0; nr < mnr; ++nr) {
+ if ((ret = pfctl_get_ruleset(pfh, npath, nr, &prs)) != 0)
+ errx(1, "%s", pf_strerror(ret));
+ INDENT(depth, !(opts & PF_OPT_VERBOSE));
+ printf("nat-anchor \"%s\" all {\n", prs.name);
+ pfctl_show_nat(dev, npath, opts,
+ prs.name, depth + 1, 0);
+ INDENT(depth, !(opts & PF_OPT_VERBOSE));
+ printf("}\n");
+ }
+ npath[len] = '\0';
+ return (0);
+ }
for (i = 0; i < 3; i++) {
- ret = pfctl_get_rules_info(dev, &ri, nattype[i], path);
+ ret = pfctl_get_rules_info_h(pfh, &ri, nattype[i], npath);
if (ret != 0) {
- warn("DIOCGETRULES");
+ warnc(ret, "DIOCGETRULES");
return (-1);
}
for (nr = 0; nr < ri.nr; ++nr) {
- brace = 0;
INDENT(depth, !(opts & PF_OPT_VERBOSE));
- if (pfctl_get_rule(dev, nr, ri.ticket, path,
- nattype[i], &rule, anchor_call)) {
- warn("DIOCGETRULE");
+ if ((ret = pfctl_get_rule_h(pfh, nr, ri.ticket, npath,
+ nattype[i], &rule, anchor_call)) != 0) {
+ warnc(ret, "DIOCGETRULE");
return (-1);
}
- if (pfctl_get_pool(dev, &rule.rpool, nr,
- ri.ticket, nattype[i], path) != 0)
+ if (pfctl_get_pool(dev, &rule.rdr, nr,
+ ri.ticket, nattype[i], npath, PF_RDR) != 0)
+ return (-1);
+ if (pfctl_get_pool(dev, &rule.nat, nr,
+ ri.ticket, nattype[i], npath, PF_NAT) != 0)
+ return (-1);
+ if (pfctl_get_pool(dev, &rule.route, nr,
+ ri.ticket, nattype[i], npath, PF_RT) != 0)
return (-1);
-
- if (anchor_call[0] &&
- ((((p = strrchr(anchor_call, '_')) != NULL) &&
- (p == anchor_call ||
- *(--p) == '/')) || (opts & PF_OPT_RECURSE))) {
- brace++;
- if ((p = strrchr(anchor_call, '/')) !=
- NULL)
- p++;
- else
- p = &anchor_call[0];
- } else
- p = &anchor_call[0];
if (dotitle) {
pfctl_print_title("TRANSLATION RULES:");
@@ -1453,90 +1623,85 @@ pfctl_show_nat(int dev, char *path, int opts, char *anchorname, int depth)
}
print_rule(&rule, anchor_call,
opts & PF_OPT_VERBOSE2, opts & PF_OPT_NUMERIC);
- if (brace)
+ if (anchor_call[0] &&
+ (((p = strrchr(anchor_call, '/')) ?
+ p[1] == '_' : anchor_call[0] == '_') ||
+ opts & PF_OPT_RECURSE)) {
printf(" {\n");
- else
- printf("\n");
- pfctl_print_rule_counters(&rule, opts);
- pfctl_clear_pool(&rule.rpool);
- if (brace) {
- pfctl_show_nat(dev, path, opts, p, depth + 1);
+ pfctl_print_rule_counters(&rule, opts);
+ pfctl_show_nat(dev, npath, opts, anchor_call,
+ depth + 1, rule.anchor_wildcard);
INDENT(depth, !(opts & PF_OPT_VERBOSE));
printf("}\n");
+ } else {
+ printf("\n");
+ pfctl_print_rule_counters(&rule, opts);
}
}
}
return (0);
}
-int
-pfctl_show_src_nodes(int dev, int opts)
+static int
+pfctl_print_src_node(struct pfctl_src_node *sn, void *arg)
{
- struct pfioc_src_nodes psn;
- struct pf_src_node *p;
- char *inbuf = NULL, *newinbuf = NULL;
- unsigned int len = 0;
- int i;
+ int *opts = (int *)arg;
- memset(&psn, 0, sizeof(psn));
- for (;;) {
- psn.psn_len = len;
- if (len) {
- newinbuf = realloc(inbuf, len);
- if (newinbuf == NULL)
- err(1, "realloc");
- psn.psn_buf = inbuf = newinbuf;
- }
- if (ioctl(dev, DIOCGETSRCNODES, &psn) < 0) {
- warn("DIOCGETSRCNODES");
- free(inbuf);
- return (-1);
- }
- if (psn.psn_len + sizeof(struct pfioc_src_nodes) < len)
- break;
- if (len == 0 && psn.psn_len == 0)
- goto done;
- if (len == 0 && psn.psn_len != 0)
- len = psn.psn_len;
- if (psn.psn_len == 0)
- goto done; /* no src_nodes */
- len *= 2;
- }
- p = psn.psn_src_nodes;
- if (psn.psn_len > 0 && (opts & PF_OPT_SHOWALL))
+ if (*opts & PF_OPT_SHOWALL) {
pfctl_print_title("SOURCE TRACKING NODES:");
- for (i = 0; i < psn.psn_len; i += sizeof(*p)) {
- print_src_node(p, opts);
- p++;
+ *opts &= ~PF_OPT_SHOWALL;
}
-done:
- free(inbuf);
+
+ print_src_node(sn, *opts);
+
return (0);
}
int
-pfctl_show_states(int dev, const char *iface, int opts)
+pfctl_show_src_nodes(int dev, int opts)
{
- struct pfctl_states states;
- struct pfctl_state *s;
- int dotitle = (opts & PF_OPT_SHOWALL);
+ int error;
- memset(&states, 0, sizeof(states));
+ error = pfctl_get_srcnodes(pfh, pfctl_print_src_node, &opts);
- if (pfctl_get_states(dev, &states))
- return (-1);
+ return (error);
+}
- TAILQ_FOREACH(s, &states.states, entry) {
- if (iface != NULL && strcmp(s->ifname, iface))
- continue;
- if (dotitle) {
- pfctl_print_title("STATES:");
- dotitle = 0;
- }
- print_state(s, opts);
+struct pfctl_show_state_arg {
+ int opts;
+ int dotitle;
+ const char *iface;
+};
+
+static int
+pfctl_show_state(struct pfctl_state *s, void *arg)
+{
+ struct pfctl_show_state_arg *a = (struct pfctl_show_state_arg *)arg;
+
+ if (a->dotitle) {
+ pfctl_print_title("STATES:");
+ a->dotitle = 0;
}
+ print_state(s, a->opts);
+
+ return (0);
+}
- pfctl_free_states(&states);
+int
+pfctl_show_states(int dev, const char *iface, int opts)
+{
+ struct pfctl_show_state_arg arg;
+ struct pfctl_state_filter filter = {};
+
+ if (iface != NULL)
+ strlcpy(filter.ifname, iface, IFNAMSIZ);
+
+ arg.opts = opts;
+ arg.dotitle = opts & PF_OPT_SHOWALL;
+ arg.iface = iface;
+
+ if (pfctl_get_filtered_states_iter(&filter, pfctl_show_state, &arg))
+ return (-1);
return (0);
}
@@ -1546,14 +1711,15 @@ pfctl_show_status(int dev, int opts)
{
struct pfctl_status *status;
struct pfctl_syncookies cookies;
+ int ret;
- if ((status = pfctl_get_status(dev)) == NULL) {
+ if ((status = pfctl_get_status_h(pfh)) == NULL) {
warn("DIOCGETSTATUS");
return (-1);
}
- if (pfctl_get_syncookies(dev, &cookies)) {
+ if ((ret = pfctl_get_syncookies(dev, &cookies)) != 0) {
pfctl_free_status(status);
- warn("DIOCGETSYNCOOKIES");
+ warnc(ret, "DIOCGETSYNCOOKIES");
return (-1);
}
if (opts & PF_OPT_SHOWALL)
@@ -1569,7 +1735,7 @@ pfctl_show_running(int dev)
struct pfctl_status *status;
int running;
- if ((status = pfctl_get_status(dev)) == NULL) {
+ if ((status = pfctl_get_status_h(pfh)) == NULL) {
warn("DIOCGETSTATUS");
return (-1);
}
@@ -1584,17 +1750,16 @@ pfctl_show_running(int dev)
int
pfctl_show_timeouts(int dev, int opts)
{
- struct pfioc_tm pt;
+ uint32_t seconds;
int i;
+ int ret;
if (opts & PF_OPT_SHOWALL)
pfctl_print_title("TIMEOUTS:");
- memset(&pt, 0, sizeof(pt));
for (i = 0; pf_timeouts[i].name; i++) {
- pt.timeout = pf_timeouts[i].timeout;
- if (ioctl(dev, DIOCGETTIMEOUT, &pt))
- err(1, "DIOCGETTIMEOUT");
- printf("%-20s %10d", pf_timeouts[i].name, pt.seconds);
+ if ((ret = pfctl_get_timeout(pfh, pf_timeouts[i].timeout, &seconds)) != 0)
+ errc(1, ret, "DIOCGETTIMEOUT");
+ printf("%-20s %10d", pf_timeouts[i].name, seconds);
if (pf_timeouts[i].timeout >= PFTM_ADAPTIVE_START &&
pf_timeouts[i].timeout <= PFTM_ADAPTIVE_END)
printf(" states");
@@ -1609,55 +1774,98 @@ pfctl_show_timeouts(int dev, int opts)
int
pfctl_show_limits(int dev, int opts)
{
- struct pfioc_limit pl;
+ unsigned int limit;
int i;
+ int ret;
if (opts & PF_OPT_SHOWALL)
pfctl_print_title("LIMITS:");
- memset(&pl, 0, sizeof(pl));
for (i = 0; pf_limits[i].name; i++) {
- pl.index = pf_limits[i].index;
- if (ioctl(dev, DIOCGETLIMIT, &pl))
- err(1, "DIOCGETLIMIT");
+ if ((ret = pfctl_get_limit(pfh, pf_limits[i].index, &limit)) != 0)
+ errc(1, ret, "DIOCGETLIMIT");
printf("%-13s ", pf_limits[i].name);
- if (pl.limit == UINT_MAX)
+ if (limit == UINT_MAX)
printf("unlimited\n");
else
- printf("hard limit %8u\n", pl.limit);
+ printf("hard limit %8u\n", limit);
}
return (0);
}
-/* callbacks for rule/nat/rdr/addr */
-int
-pfctl_add_pool(struct pfctl *pf, struct pfctl_pool *p, sa_family_t af)
+void
+pfctl_read_limits(struct pfctl_handle *h)
{
- struct pf_pooladdr *pa;
+ int i;
- if ((pf->opts & PF_OPT_NOACTION) == 0) {
- if (ioctl(pf->dev, DIOCBEGINADDRS, &pf->paddr))
- err(1, "DIOCBEGINADDRS");
+ for (i = 0; pf_limits[i].name; i++) {
+ if (pfctl_get_limit(h, i, &limit_curr[i]))
+ err(1, "DIOCGETLIMIT");
+ }
+}
+
+void
+pfctl_restore_limits(void)
+{
+ int i;
+
+ if (pfh == NULL)
+ return;
+
+ for (i = 0; pf_limits[i].name; i++) {
+ if (pfctl_set_limit(pfh, i, limit_curr[i]))
+ warn("DIOCSETLIMIT (%s)", pf_limits[i].name);
}
+}
+
+void
+pfctl_show_creators(int opts)
+{
+ int ret;
+ uint32_t creators[16];
+ size_t count = nitems(creators);
+
+ ret = pfctl_get_creatorids(pfh, creators, &count);
+ if (ret != 0)
+ errx(ret, "Failed to retrieve creators");
+
+ printf("Creator IDs:\n");
+ for (size_t i = 0; i < count; i++)
+ printf("%08x\n", creators[i]);
+}
+
+/* callbacks for rule/nat/rdr/addr */
+int
+pfctl_add_pool(struct pfctl *pf, struct pfctl_pool *p, int which)
+{
+ struct pfctl_pooladdr *pa;
+ int ret;
- pf->paddr.af = af;
TAILQ_FOREACH(pa, &p->list, entries) {
- memcpy(&pf->paddr.addr, pa, sizeof(struct pf_pooladdr));
+ memcpy(&pf->paddr.addr, pa, sizeof(struct pfctl_pooladdr));
+ pf->paddr.af = pa->af;
if ((pf->opts & PF_OPT_NOACTION) == 0) {
- if (ioctl(pf->dev, DIOCADDADDR, &pf->paddr))
- err(1, "DIOCADDADDR");
+ if ((ret = pfctl_add_addr(pf->h, &pf->paddr, which)) != 0)
+ errc(1, ret, "DIOCADDADDR");
}
}
return (0);
}
-int
-pfctl_append_rule(struct pfctl *pf, struct pfctl_rule *r,
- const char *anchor_call)
+void
+pfctl_init_rule(struct pfctl_rule *r)
+{
+ memset(r, 0, sizeof(struct pfctl_rule));
+ TAILQ_INIT(&(r->rdr.list));
+ TAILQ_INIT(&(r->nat.list));
+ TAILQ_INIT(&(r->route.list));
+}
+
+void
+pfctl_append_rule(struct pfctl *pf, struct pfctl_rule *r)
{
u_int8_t rs_num;
struct pfctl_rule *rule;
struct pfctl_ruleset *rs;
- char *p;
rs_num = pf_get_ruleset_number(r->action);
if (rs_num == PF_RULESET_MAX)
@@ -1665,37 +1873,17 @@ pfctl_append_rule(struct pfctl *pf, struct pfctl_rule *r,
rs = &pf->anchor->ruleset;
- if (anchor_call[0] && r->anchor == NULL) {
- /*
- * Don't make non-brace anchors part of the main anchor pool.
- */
- if ((r->anchor = calloc(1, sizeof(*r->anchor))) == NULL)
- err(1, "pfctl_append_rule: calloc");
-
- pf_init_ruleset(&r->anchor->ruleset);
- r->anchor->ruleset.anchor = r->anchor;
- if (strlcpy(r->anchor->path, anchor_call,
- sizeof(rule->anchor->path)) >= sizeof(rule->anchor->path))
- errx(1, "pfctl_append_rule: strlcpy");
- if ((p = strrchr(anchor_call, '/')) != NULL) {
- if (!strlen(p))
- err(1, "pfctl_append_rule: bad anchor name %s",
- anchor_call);
- } else
- p = (char *)anchor_call;
- if (strlcpy(r->anchor->name, p,
- sizeof(rule->anchor->name)) >= sizeof(rule->anchor->name))
- errx(1, "pfctl_append_rule: strlcpy");
- }
-
if ((rule = calloc(1, sizeof(*rule))) == NULL)
err(1, "calloc");
bcopy(r, rule, sizeof(*rule));
- TAILQ_INIT(&rule->rpool.list);
- pfctl_move_pool(&r->rpool, &rule->rpool);
+ TAILQ_INIT(&rule->rdr.list);
+ pfctl_move_pool(&r->rdr, &rule->rdr);
+ TAILQ_INIT(&rule->nat.list);
+ pfctl_move_pool(&r->nat, &rule->nat);
+ TAILQ_INIT(&rule->route.list);
+ pfctl_move_pool(&r->route, &rule->route);
TAILQ_INSERT_TAIL(rs->rules[rs_num].active.ptr, rule, entries);
- return (0);
}
int
@@ -1853,6 +2041,7 @@ pfctl_load_eth_rule(struct pfctl *pf, char *path, struct pfctl_eth_rule *r,
char *name;
char anchor[PF_ANCHOR_NAME_SIZE];
int len = strlen(path);
+ int ret;
if (strlcpy(anchor, path, sizeof(anchor)) >= sizeof(anchor))
errx(1, "pfctl_load_eth_rule: strlcpy");
@@ -1872,9 +2061,9 @@ pfctl_load_eth_rule(struct pfctl *pf, char *path, struct pfctl_eth_rule *r,
name = "";
if ((pf->opts & PF_OPT_NOACTION) == 0)
- if (pfctl_add_eth_rule(pf->dev, r, anchor, name,
- pf->eth_ticket))
- err(1, "DIOCADDETHRULENV");
+ if ((ret = pfctl_add_eth_rule(pf->dev, r, anchor, name,
+ pf->eth_ticket)) != 0)
+ errc(1, ret, "DIOCADDETHRULENV");
if (pf->opts & PF_OPT_VERBOSE) {
INDENT(depth, !(pf->opts & PF_OPT_VERBOSE2));
@@ -1887,6 +2076,41 @@ pfctl_load_eth_rule(struct pfctl *pf, char *path, struct pfctl_eth_rule *r,
return (0);
}
+static int
+pfctl_load_tables(struct pfctl *pf, char *path, struct pfctl_anchor *a,
+ int rs_num)
+{
+ struct pfr_ktable *kt, *ktw;
+ struct pfr_uktable *ukt;
+ char anchor_path[PF_ANCHOR_MAXPATH];
+ int e;
+
+ RB_FOREACH_SAFE(kt, pfr_ktablehead, &pfr_ktables, ktw) {
+ if (strcmp(kt->pfrkt_anchor, a->path) != 0)
+ continue;
+
+ if (path != NULL && *path) {
+ strlcpy(anchor_path, kt->pfrkt_anchor,
+ sizeof(anchor_path));
+ snprintf(kt->pfrkt_anchor, PF_ANCHOR_MAXPATH, "%s/%s",
+ path, anchor_path);
+ }
+ ukt = (struct pfr_uktable *)kt;
+ e = pfr_ina_define(&ukt->pfrukt_t, ukt->pfrukt_addrs.pfrb_caddr,
+ ukt->pfrukt_addrs.pfrb_size, NULL, NULL,
+ pf->anchor->ruleset.tticket,
+ ukt->pfrukt_init_addr ? PFR_FLAG_ADDRSTOO : 0);
+ if (e != 0)
+ err(1, "%s pfr_ina_define() %s@%s", __func__,
+ kt->pfrkt_name, kt->pfrkt_anchor);
+ RB_REMOVE(pfr_ktablehead, &pfr_ktables, kt);
+ pfr_buf_clear(&ukt->pfrukt_addrs);
+ free(ukt);
+ }
+
+ return (0);
+}
+
int
pfctl_load_ruleset(struct pfctl *pf, char *path, struct pfctl_ruleset *rs,
int rs_num, int depth)
@@ -1910,13 +2134,12 @@ pfctl_load_ruleset(struct pfctl *pf, char *path, struct pfctl_ruleset *rs,
if ((pf->opts & PF_OPT_NOACTION) == 0 &&
(error = pfctl_ruleset_trans(pf,
path, rs->anchor, false))) {
- printf("pfctl_load_rulesets: "
- "pfctl_ruleset_trans %d\n", error);
+ printf("%s: "
+ "pfctl_ruleset_trans %d\n", __func__, error);
goto error;
}
} else if (pf->opts & PF_OPT_VERBOSE)
printf("\n");
-
}
if (pf->optimize && rs_num == PF_RULESET_FILTER)
@@ -1936,6 +2159,8 @@ pfctl_load_ruleset(struct pfctl *pf, char *path, struct pfctl_ruleset *rs,
if ((error = pfctl_load_ruleset(pf, path,
&r->anchor->ruleset, rs_num, depth + 1)))
goto error;
+ if ((error = pfctl_load_tables(pf, path, r->anchor, rs_num)))
+ goto error;
} else if (pf->opts & PF_OPT_VERBOSE)
printf("\n");
free(r);
@@ -1958,15 +2183,17 @@ pfctl_load_rule(struct pfctl *pf, char *path, struct pfctl_rule *r, int depth)
{
u_int8_t rs_num = pf_get_ruleset_number(r->action);
char *name;
- u_int32_t ticket;
char anchor[PF_ANCHOR_NAME_SIZE];
int len = strlen(path);
int error;
bool was_present;
/* set up anchor before adding to path for anchor_call */
- if ((pf->opts & PF_OPT_NOACTION) == 0)
- ticket = pfctl_get_ticket(pf->trans, rs_num, path);
+ if ((pf->opts & PF_OPT_NOACTION) == 0) {
+ if (pf->trans == NULL)
+ errx(1, "pfctl_load_rule: no transaction");
+ pf->anchor->ruleset.tticket = pfctl_get_ticket(pf->trans, rs_num, path);
+ }
if (strlcpy(anchor, path, sizeof(anchor)) >= sizeof(anchor))
errx(1, "pfctl_load_rule: strlcpy");
@@ -1986,9 +2213,19 @@ pfctl_load_rule(struct pfctl *pf, char *path, struct pfctl_rule *r, int depth)
was_present = false;
if ((pf->opts & PF_OPT_NOACTION) == 0) {
- if (pfctl_add_pool(pf, &r->rpool, r->af))
+ if ((pf->opts & PF_OPT_NOACTION) == 0) {
+ if ((error = pfctl_begin_addrs(pf->h,
+ &pf->paddr.ticket)) != 0)
+ errc(1, error, "DIOCBEGINADDRS");
+ }
+
+ if (pfctl_add_pool(pf, &r->rdr, PF_RDR))
return (1);
- error = pfctl_add_rule(pf->dev, r, anchor, name, ticket,
+ if (pfctl_add_pool(pf, &r->nat, PF_NAT))
+ return (1);
+ if (pfctl_add_pool(pf, &r->route, PF_RT))
+ return (1);
+ error = pfctl_add_rule_h(pf->h, r, anchor, name, pf->anchor->ruleset.tticket,
pf->paddr.ticket);
switch (error) {
case 0:
@@ -1999,7 +2236,7 @@ pfctl_load_rule(struct pfctl *pf, char *path, struct pfctl_rule *r, int depth)
was_present = true;
break;
default:
- err(1, "DIOCADDRULENV");
+ errc(1, error, "DIOCADDRULE");
}
}
@@ -2012,7 +2249,8 @@ pfctl_load_rule(struct pfctl *pf, char *path, struct pfctl_rule *r, int depth)
printf(" -- rule was already present");
}
path[len] = '\0';
- pfctl_clear_pool(&r->rpool);
+ pfctl_clear_pool(&r->rdr);
+ pfctl_clear_pool(&r->nat);
return (0);
}
@@ -2042,8 +2280,8 @@ int
pfctl_rules(int dev, char *filename, int opts, int optimize,
char *anchorname, struct pfr_buffer *trans)
{
-#define ERR(x) do { warn(x); goto _error; } while(0)
-#define ERRX(x) do { warnx(x); goto _error; } while(0)
+#define ERR(...) do { warn(__VA_ARGS__); goto _error; } while(0)
+#define ERRX(...) do { warnx(__VA_ARGS__); goto _error; } while(0)
struct pfr_buffer *t, buf;
struct pfioc_altq pa;
@@ -2051,12 +2289,14 @@ pfctl_rules(int dev, char *filename, int opts, int optimize,
struct pfctl_ruleset *rs;
struct pfctl_eth_ruleset *ethrs;
struct pfr_table trs;
- char *path;
+ char *path = NULL;
int osize;
RB_INIT(&pf_anchors);
memset(&pf_main_anchor, 0, sizeof(pf_main_anchor));
pf_init_ruleset(&pf_main_anchor.ruleset);
+ memset(&pf, 0, sizeof(pf));
+ memset(&trs, 0, sizeof(trs));
pf_main_anchor.ruleset.anchor = &pf_main_anchor;
memset(&pf_eth_main_anchor, 0, sizeof(pf_eth_main_anchor));
@@ -2066,6 +2306,7 @@ pfctl_rules(int dev, char *filename, int opts, int optimize,
if (trans == NULL) {
bzero(&buf, sizeof(buf));
buf.pfrb_type = PFRB_TRANS;
+ pf.trans = &buf;
t = &buf;
osize = 0;
} else {
@@ -2078,27 +2319,28 @@ pfctl_rules(int dev, char *filename, int opts, int optimize,
memset(&pf, 0, sizeof(pf));
memset(&trs, 0, sizeof(trs));
if ((path = calloc(1, MAXPATHLEN)) == NULL)
- ERRX("pfctl_rules: calloc");
+ ERRX("%s: calloc", __func__);
if (strlcpy(trs.pfrt_anchor, anchorname,
sizeof(trs.pfrt_anchor)) >= sizeof(trs.pfrt_anchor))
- ERRX("pfctl_rules: strlcpy");
+ ERRX("%s: strlcpy", __func__);
pf.dev = dev;
+ pf.h = pfh;
pf.opts = opts;
pf.optimize = optimize;
pf.loadopt = loadopt;
/* non-brace anchor, create without resolving the path */
if ((pf.anchor = calloc(1, sizeof(*pf.anchor))) == NULL)
- ERRX("pfctl_rules: calloc");
+ ERRX("%s: calloc", __func__);
rs = &pf.anchor->ruleset;
pf_init_ruleset(rs);
rs->anchor = pf.anchor;
if (strlcpy(pf.anchor->path, anchorname,
sizeof(pf.anchor->path)) >= sizeof(pf.anchor->path))
- errx(1, "pfctl_rules: strlcpy");
+ errx(1, "%s: strlcpy", __func__);
if (strlcpy(pf.anchor->name, anchorname,
sizeof(pf.anchor->name)) >= sizeof(pf.anchor->name))
- errx(1, "pfctl_rules: strlcpy");
+ errx(1, "%s: strlcpy", __func__);
pf.astack[0] = pf.anchor;
@@ -2111,14 +2353,14 @@ pfctl_rules(int dev, char *filename, int opts, int optimize,
/* Set up ethernet anchor */
if ((pf.eanchor = calloc(1, sizeof(*pf.eanchor))) == NULL)
- ERRX("pfctl_rules: calloc");
+ ERRX("%s: calloc", __func__);
if (strlcpy(pf.eanchor->path, anchorname,
sizeof(pf.eanchor->path)) >= sizeof(pf.eanchor->path))
- errx(1, "pfctl_rules: strlcpy");
+ errx(1, "%s: strlcpy", __func__);
if (strlcpy(pf.eanchor->name, anchorname,
sizeof(pf.eanchor->name)) >= sizeof(pf.eanchor->name))
- errx(1, "pfctl_rules: strlcpy");
+ errx(1, "%s: strlcpy", __func__);
ethrs = &pf.eanchor->ruleset;
pf_init_eth_ruleset(ethrs);
@@ -2132,7 +2374,7 @@ pfctl_rules(int dev, char *filename, int opts, int optimize,
* loaded at parse time.
*/
if (pfctl_ruleset_trans(&pf, anchorname, pf.anchor, true))
- ERRX("pfctl_rules");
+ ERRX("%s", __func__);
if (pf.loadopt & PFCTL_FLAG_ETH)
pf.eth_ticket = pfctl_get_ticket(t, PF_RULESET_ETH, anchorname);
if (altqsupport && (pf.loadopt & PFCTL_FLAG_ALTQ))
@@ -2173,17 +2415,17 @@ pfctl_rules(int dev, char *filename, int opts, int optimize,
if (check_commit_altq(dev, opts) != 0)
ERRX("errors in altq config");
- /* process "load anchor" directives */
- if (!anchorname[0])
- if (pfctl_load_anchors(dev, &pf, t) == -1)
+ if (trans == NULL) {
+ /* process "load anchor" directives */
+ if (pfctl_load_anchors(dev, &pf) == -1)
ERRX("load anchors");
- if (trans == NULL && (opts & PF_OPT_NOACTION) == 0) {
- if (!anchorname[0])
- if (pfctl_load_options(&pf))
+ if ((opts & PF_OPT_NOACTION) == 0) {
+ if (!anchorname[0] && pfctl_load_options(&pf))
goto _error;
- if (pfctl_trans(dev, t, DIOCXCOMMIT, osize))
- ERR("DIOCXCOMMIT");
+ if (pfctl_trans(dev, t, DIOCXCOMMIT, osize))
+ ERR("DIOCXCOMMIT");
+ }
}
free(path);
return (0);
@@ -2234,6 +2476,11 @@ pfctl_init_options(struct pfctl *pf)
pf->timeout[PFTM_TCP_CLOSING] = PFTM_TCP_CLOSING_VAL;
pf->timeout[PFTM_TCP_FIN_WAIT] = PFTM_TCP_FIN_WAIT_VAL;
pf->timeout[PFTM_TCP_CLOSED] = PFTM_TCP_CLOSED_VAL;
+ pf->timeout[PFTM_SCTP_FIRST_PACKET] = PFTM_TCP_FIRST_PACKET_VAL;
+ pf->timeout[PFTM_SCTP_OPENING] = PFTM_TCP_OPENING_VAL;
+ pf->timeout[PFTM_SCTP_ESTABLISHED] = PFTM_TCP_ESTABLISHED_VAL;
+ pf->timeout[PFTM_SCTP_CLOSING] = PFTM_TCP_CLOSING_VAL;
+ pf->timeout[PFTM_SCTP_CLOSED] = PFTM_TCP_CLOSED_VAL;
pf->timeout[PFTM_UDP_FIRST_PACKET] = PFTM_UDP_FIRST_PACKET_VAL;
pf->timeout[PFTM_UDP_SINGLE] = PFTM_UDP_SINGLE_VAL;
pf->timeout[PFTM_UDP_MULTIPLE] = PFTM_UDP_MULTIPLE_VAL;
@@ -2251,8 +2498,14 @@ pfctl_init_options(struct pfctl *pf)
pf->limit[PF_LIMIT_STATES] = PFSTATE_HIWAT;
pf->limit[PF_LIMIT_FRAGS] = PFFRAG_FRENT_HIWAT;
- pf->limit[PF_LIMIT_SRC_NODES] = PFSNODE_HIWAT;
- pf->limit[PF_LIMIT_TABLE_ENTRIES] = PFR_KENTRY_HIWAT;
+
+ pf->limit[PF_LIMIT_SRC_NODES] = (limit_curr[PF_LIMIT_SRC_NODES] == 0) ?
+ PFSNODE_HIWAT : limit_curr[PF_LIMIT_SRC_NODES];
+ pf->limit[PF_LIMIT_TABLE_ENTRIES] =
+ (limit_curr[PF_LIMIT_TABLE_ENTRIES] == 0) ?
+ PFR_KENTRY_HIWAT : limit_curr[PF_LIMIT_TABLE_ENTRIES];
+ pf->limit[PF_LIMIT_ANCHORS] = (limit_curr[PF_LIMIT_ANCHORS] == 0) ?
+ PF_ANCHOR_HIWAT : limit_curr[PF_LIMIT_ANCHORS];
pf->debug = PF_DEBUG_URGENT;
pf->reassemble = 0;
@@ -2279,7 +2532,7 @@ pfctl_load_options(struct pfctl *pf)
}
/*
- * If we've set the limit, but haven't explicitly set adaptive
+ * If we've set the states limit, but haven't explicitly set adaptive
* timeouts, do it now with a start of 60% and end of 120%.
*/
if (pf->limit_set[PF_LIMIT_STATES] &&
@@ -2333,7 +2586,7 @@ pfctl_load_options(struct pfctl *pf)
}
int
-pfctl_set_limit(struct pfctl *pf, const char *opt, unsigned int limit)
+pfctl_apply_limit(struct pfctl *pf, const char *opt, unsigned int limit)
{
int i;
@@ -2353,29 +2606,29 @@ pfctl_set_limit(struct pfctl *pf, const char *opt, unsigned int limit)
if (pf->opts & PF_OPT_VERBOSE)
printf("set limit %s %d\n", opt, limit);
+ if ((pf->opts & PF_OPT_NOACTION) == 0)
+ pfctl_load_options(pf);
+
return (0);
}
int
pfctl_load_limit(struct pfctl *pf, unsigned int index, unsigned int limit)
{
- struct pfioc_limit pl;
-
- memset(&pl, 0, sizeof(pl));
- pl.index = index;
- pl.limit = limit;
- if (ioctl(pf->dev, DIOCSETLIMIT, &pl)) {
+ if (pfctl_set_limit(pf->h, index, limit)) {
if (errno == EBUSY)
- warnx("Current pool size exceeds requested hard limit");
+ warnx("Current pool size exceeds requested %s limit %u",
+ pf_limits[index].name, limit);
else
- warnx("DIOCSETLIMIT");
+ warnx("Cannot set %s limit to %u",
+ pf_limits[index].name, limit);
return (1);
}
return (0);
}
int
-pfctl_set_timeout(struct pfctl *pf, const char *opt, int seconds, int quiet)
+pfctl_apply_timeout(struct pfctl *pf, const char *opt, int seconds, int quiet)
{
int i;
@@ -2405,12 +2658,7 @@ pfctl_set_timeout(struct pfctl *pf, const char *opt, int seconds, int quiet)
int
pfctl_load_timeout(struct pfctl *pf, unsigned int timeout, unsigned int seconds)
{
- struct pfioc_tm pt;
-
- memset(&pt, 0, sizeof(pt));
- pt.timeout = timeout;
- pt.seconds = seconds;
- if (ioctl(pf->dev, DIOCSETTIMEOUT, &pt)) {
+ if (pfctl_set_timeout(pf->h, timeout, seconds)) {
warnx("DIOCSETTIMEOUT");
return (1);
}
@@ -2459,7 +2707,7 @@ pfctl_set_optimization(struct pfctl *pf, const char *opt)
}
for (i = 0; hint[i].name; i++)
- if ((r = pfctl_set_timeout(pf, hint[i].name,
+ if ((r = pfctl_apply_timeout(pf, hint[i].name,
hint[i].timeout, 1)))
return (r);
@@ -2495,26 +2743,18 @@ pfctl_set_logif(struct pfctl *pf, char *ifname)
int
pfctl_load_logif(struct pfctl *pf, char *ifname)
{
- struct pfioc_if pi;
-
- memset(&pi, 0, sizeof(pi));
- if (ifname && strlcpy(pi.ifname, ifname,
- sizeof(pi.ifname)) >= sizeof(pi.ifname)) {
+ if (ifname != NULL && strlen(ifname) >= IFNAMSIZ) {
warnx("pfctl_load_logif: strlcpy");
return (1);
}
- if (ioctl(pf->dev, DIOCSETSTATUSIF, &pi)) {
- warnx("DIOCSETSTATUSIF");
- return (1);
- }
- return (0);
+ return (pfctl_set_statusif(pfh, ifname ? ifname : ""));
}
-int
+void
pfctl_set_hostid(struct pfctl *pf, u_int32_t hostid)
{
if ((loadopt & PFCTL_FLAG_OPTION) == 0)
- return (0);
+ return;
HTONL(hostid);
@@ -2523,8 +2763,6 @@ pfctl_set_hostid(struct pfctl *pf, u_int32_t hostid)
if (pf->opts & PF_OPT_VERBOSE)
printf("set hostid 0x%08x\n", ntohl(hostid));
-
- return (0);
}
int
@@ -2609,9 +2847,10 @@ pfctl_cfg_syncookies(struct pfctl *pf, uint8_t val, struct pfctl_watermarks *w)
}
int
-pfctl_set_debug(struct pfctl *pf, char *d)
+pfctl_do_set_debug(struct pfctl *pf, char *d)
{
u_int32_t level;
+ int ret;
if ((loadopt & PFCTL_FLAG_OPTION) == 0)
return (0);
@@ -2633,8 +2872,8 @@ pfctl_set_debug(struct pfctl *pf, char *d)
level = pf->debug;
if ((pf->opts & PF_OPT_NOACTION) == 0)
- if (ioctl(dev, DIOCSETDEBUG, &level))
- err(1, "DIOCSETDEBUG");
+ if ((ret = pfctl_set_debug(pfh, level)) != 0)
+ errc(1, ret, "DIOCSETDEBUG");
if (pf->opts & PF_OPT_VERBOSE)
printf("set debug %s\n", d);
@@ -2645,7 +2884,7 @@ pfctl_set_debug(struct pfctl *pf, char *d)
int
pfctl_load_debug(struct pfctl *pf, unsigned int level)
{
- if (ioctl(pf->dev, DIOCSETDEBUG, &level)) {
+ if (pfctl_set_debug(pf->h, level)) {
warnx("DIOCSETDEBUG");
return (1);
}
@@ -2678,7 +2917,7 @@ pfctl_set_interface_flags(struct pfctl *pf, char *ifname, int flags, int how)
if ((pf->opts & PF_OPT_NOACTION) == 0) {
if (how == 0) {
if (ioctl(pf->dev, DIOCCLRIFFLAG, &pi))
- err(1, "DIOCCLRIFFLAG");
+ pfctl_err(pf->opts, 1, "DIOCCLRIFFLAG");
} else {
if (ioctl(pf->dev, DIOCSETIFFLAG, &pi))
err(1, "DIOCSETIFFLAG");
@@ -2691,8 +2930,10 @@ pfctl_set_interface_flags(struct pfctl *pf, char *ifname, int flags, int how)
void
pfctl_debug(int dev, u_int32_t level, int opts)
{
- if (ioctl(dev, DIOCSETDEBUG, &level))
- err(1, "DIOCSETDEBUG");
+ int ret;
+
+ if ((ret = pfctl_set_debug(pfh, level)) != 0)
+ errc(1, ret, "DIOCSETDEBUG");
if ((opts & PF_OPT_QUIET) == 0) {
fprintf(stderr, "debug level set to '");
switch (level) {
@@ -2735,45 +2976,187 @@ pfctl_test_altqsupport(int dev, int opts)
}
int
-pfctl_show_anchors(int dev, int opts, char *anchorname)
+pfctl_walk_show(int opts, struct pfioc_ruleset *pr, void *warg)
+{
+ if (pr->path[0]) {
+ if (pr->path[0] != '_' || (opts & PF_OPT_VERBOSE))
+ printf(" %s/%s\n", pr->path, pr->name);
+ } else if (pr->name[0] != '_' || (opts & PF_OPT_VERBOSE))
+ printf(" %s\n", pr->name);
+
+ return (0);
+}
+
+int
+pfctl_walk_get(int opts, struct pfioc_ruleset *pr, void *warg)
+{
+ struct pfr_anchoritem *pfra;
+ struct pfr_anchors *anchors;
+ int e;
+
+ anchors = (struct pfr_anchors *)warg;
+
+ pfra = malloc(sizeof(*pfra));
+ if (pfra == NULL)
+ err(1, "%s", __func__);
+
+ if (pr->path[0])
+ e = asprintf(&pfra->pfra_anchorname, "%s/%s", pr->path,
+ pr->name);
+ else
+ e = asprintf(&pfra->pfra_anchorname, "%s", pr->name);
+
+ if (e == -1)
+ err(1, "%s", __func__);
+
+ SLIST_INSERT_HEAD(anchors, pfra, pfra_sle);
+
+ return (0);
+}
+
+int
+pfctl_walk_anchors(int dev, int opts, const char *anchor,
+ int(walkf)(int, struct pfioc_ruleset *, void *), void *warg)
{
struct pfioc_ruleset pr;
u_int32_t mnr, nr;
+ int ret;
memset(&pr, 0, sizeof(pr));
- memcpy(pr.path, anchorname, sizeof(pr.path));
- if (ioctl(dev, DIOCGETRULESETS, &pr)) {
- if (errno == EINVAL)
- fprintf(stderr, "Anchor '%s' not found.\n",
- anchorname);
- else
- err(1, "DIOCGETRULESETS");
- return (-1);
- }
- mnr = pr.nr;
+ if ((ret = pfctl_get_rulesets(pfh, anchor, &mnr)) != 0)
+ errx(1, "%s", pf_strerror(ret));
for (nr = 0; nr < mnr; ++nr) {
char sub[MAXPATHLEN];
- pr.nr = nr;
- if (ioctl(dev, DIOCGETRULESET, &pr))
- err(1, "DIOCGETRULESET");
+ if ((ret = pfctl_get_ruleset(pfh, anchor, nr, &pr)) != 0)
+ errc(1, ret, "DIOCGETRULESET");
if (!strcmp(pr.name, PF_RESERVED_ANCHOR))
continue;
- sub[0] = 0;
- if (pr.path[0]) {
- strlcat(sub, pr.path, sizeof(sub));
- strlcat(sub, "/", sizeof(sub));
- }
- strlcat(sub, pr.name, sizeof(sub));
- if (sub[0] != '_' || (opts & PF_OPT_VERBOSE))
- printf(" %s\n", sub);
- if ((opts & PF_OPT_VERBOSE) && pfctl_show_anchors(dev, opts, sub))
+ sub[0] = '\0';
+ if (walkf(opts, &pr, warg))
+ return (-1);
+
+ if (pr.path[0])
+ snprintf(sub, sizeof(sub), "%s/%s", pr.path, pr.name);
+ else
+ snprintf(sub, sizeof(sub), "%s", pr.name);
+ if (pfctl_walk_anchors(dev, opts, sub, walkf, warg))
return (-1);
}
return (0);
}
int
+pfctl_show_anchors(int dev, int opts, char *anchor)
+{
+ return (
+ pfctl_walk_anchors(dev, opts, anchor, pfctl_walk_show, NULL));
+}
+
+struct pfr_anchors *
+pfctl_get_anchors(int dev, const char *anchor, int opts)
+{
+ struct pfioc_ruleset pr;
+ static struct pfr_anchors anchors;
+ char anchorbuf[PATH_MAX];
+ char *n;
+
+ SLIST_INIT(&anchors);
+
+ memset(&pr, 0, sizeof(pr));
+ if (*anchor != '\0') {
+ strlcpy(anchorbuf, anchor, sizeof(anchorbuf));
+ n = dirname(anchorbuf);
+ if (n[0] != '.' && n[1] != '\0')
+ strlcpy(pr.path, n, sizeof(pr.path));
+ strlcpy(anchorbuf, anchor, sizeof(anchorbuf));
+ n = basename(anchorbuf);
+ if (n != NULL)
+ strlcpy(pr.name, n, sizeof(pr.name));
+ }
+
+ /* insert a root anchor first. */
+ pfctl_walk_get(opts, &pr, &anchors);
+
+ if (pfctl_walk_anchors(dev, opts, anchor, pfctl_walk_get, &anchors))
+ errx(1, "%s failed to retrieve list of anchors, can't continue",
+ __func__);
+
+ return (&anchors);
+}
+
+int
+pfctl_call_cleartables(int dev, int opts, struct pfr_anchoritem *pfra)
+{
+ /*
+ * PF_OPT_QUIET makes pfctl_clear_tables() to stop printing number of
+ * tables cleared for given anchor.
+ */
+ opts |= PF_OPT_QUIET;
+ return ((pfctl_do_clear_tables(pfra->pfra_anchorname, opts) == -1) ?
+ 1 : 0);
+}
+
+int
+pfctl_call_clearrules(int dev, int opts, struct pfr_anchoritem *pfra)
+{
+ /*
+ * PF_OPT_QUIET makes pfctl_clear_rules() to stop printing a 'rules
+ * cleared' message for every anchor it deletes.
+ */
+ opts |= PF_OPT_QUIET;
+ return (pfctl_flush_rules(dev, opts, pfra->pfra_anchorname));
+}
+
+int
+pfctl_call_clearanchors(int dev, int opts, struct pfr_anchoritem *pfra)
+{
+ int rv = 0;
+
+ rv |= pfctl_call_cleartables(dev, opts, pfra);
+ rv |= pfctl_call_clearrules(dev, opts, pfra);
+
+ return (rv);
+}
+
+int
+pfctl_call_showtables(int dev, int opts, struct pfr_anchoritem *pfra)
+{
+ pfctl_show_tables(pfra->pfra_anchorname, opts);
+ return (0);
+}
+
+int
+pfctl_recurse(int dev, int opts, const char *anchorname,
+ int(*walkf)(int, int, struct pfr_anchoritem *))
+{
+ int rv = 0;
+ struct pfr_anchors *anchors;
+ struct pfr_anchoritem *pfra, *pfra_save;
+
+ anchors = pfctl_get_anchors(dev, anchorname, opts);
+ /*
+ * While traversing the list, pfctl_clear_*() must always return
+ * so that failures on one anchor do not prevent clearing others.
+ */
+ opts |= PF_OPT_IGNFAIL;
+ if ((opts & PF_OPT_CALLSHOW) == 0)
+ printf("Removing:\n");
+ SLIST_FOREACH_SAFE(pfra, anchors, pfra_sle, pfra_save) {
+ if ((opts & PF_OPT_CALLSHOW) == 0)
+ printf(" %s\n",
+ (*pfra->pfra_anchorname == '\0') ? "/" :
+ pfra->pfra_anchorname);
+ rv |= walkf(dev, opts, pfra);
+ SLIST_REMOVE(anchors, pfra, pfr_anchoritem, pfra_sle);
+ free(pfra->pfra_anchorname);
+ free(pfra);
+ }
+
+ return (rv);
+}
+
+int
pfctl_show_eth_anchors(int dev, int opts, char *anchorname)
{
struct pfctl_eth_rulesets_info ri;
@@ -2785,15 +3168,15 @@ pfctl_show_eth_anchors(int dev, int opts, char *anchorname)
fprintf(stderr, "Anchor '%s' not found.\n",
anchorname);
else
- err(1, "DIOCGETETHRULESETS");
+ errc(1, ret, "DIOCGETETHRULESETS");
return (-1);
}
for (int nr = 0; nr < ri.nr; nr++) {
char sub[MAXPATHLEN];
- if (pfctl_get_eth_ruleset(dev, anchorname, nr, &rs) != 0)
- err(1, "DIOCGETETHRULESET");
+ if ((ret = pfctl_get_eth_ruleset(dev, anchorname, nr, &rs)) != 0)
+ errc(1, ret, "DIOCGETETHRULESET");
if (!strcmp(rs.name, PF_RESERVED_ANCHOR))
continue;
@@ -2821,10 +3204,49 @@ pfctl_lookup_option(char *cmd, const char * const *list)
return (NULL);
}
+void
+pfctl_reset(int dev, int opts)
+{
+ struct pfctl pf;
+ struct pfr_buffer t;
+ int i;
+
+ memset(&pf, 0, sizeof(pf));
+ pf.dev = dev;
+ pf.h = pfh;
+ pfctl_init_options(&pf);
+
+ /* Force reset upon pfctl_load_options() */
+ pf.debug_set = 1;
+ pf.reass_set = 1;
+ pf.syncookieswat_set = 1;
+ pf.ifname = strdup("none");
+ if (pf.ifname == NULL)
+ err(1, "%s: strdup", __func__);
+ pf.ifname_set = 1;
+
+ memset(&t, 0, sizeof(t));
+ t.pfrb_type = PFRB_TRANS;
+ if (pfctl_trans(dev, &t, DIOCXBEGIN, 0))
+ err(1, "%s: DIOCXBEGIN", __func__);
+
+ for (i = 0; pf_limits[i].name; i++)
+ pf.limit_set[pf_limits[i].index] = 1;
+
+ for (i = 0; pf_timeouts[i].name; i++)
+ pf.timeout_set[pf_timeouts[i].timeout] = 1;
+
+ pfctl_load_options(&pf);
+
+ if (pfctl_trans(dev, &t, DIOCXCOMMIT, 0))
+ err(1, "%s: DIOCXCOMMIT", __func__);
+
+ pfctl_clear_interface_flags(dev, opts);
+}
+
int
main(int argc, char *argv[])
{
- int error = 0;
int ch;
int mode = O_RDONLY;
int opts = 0;
@@ -2836,7 +3258,7 @@ main(int argc, char *argv[])
usage();
while ((ch = getopt(argc, argv,
- "a:AdD:eqf:F:ghi:k:K:mMnNOo:Pp:rRs:t:T:vx:z")) != -1) {
+ "a:AdD:eqf:F:ghi:k:K:mMnNOo:Pp:rRs:St:T:vx:z")) != -1) {
switch (ch) {
case 'a':
anchoropt = optarg;
@@ -2938,6 +3360,9 @@ main(int argc, char *argv[])
usage();
}
break;
+ case 'S':
+ opts |= PF_OPT_NODNS;
+ break;
case 't':
tableopt = optarg;
break;
@@ -2973,6 +3398,12 @@ main(int argc, char *argv[])
}
}
+ if ((opts & PF_OPT_NODNS) && (opts & PF_OPT_USEDNS))
+ errx(1, "-N and -r are mutually exclusive");
+
+ if ((tblcmdopt == NULL) ^ (tableopt == NULL))
+ usage();
+
if (tblcmdopt != NULL) {
argc -= optind;
argv += optind;
@@ -2981,7 +3412,7 @@ main(int argc, char *argv[])
loadopt |= PFCTL_FLAG_TABLE;
tblcmdopt = NULL;
} else
- mode = strchr("acdefkrz", ch) ? O_RDWR : O_RDONLY;
+ mode = strchr("st", ch) ? O_RDONLY : O_RDWR;
} else if (argc != optind) {
warnx("unknown command line argument: %s ...", argv[optind]);
usage();
@@ -2990,12 +3421,21 @@ main(int argc, char *argv[])
if (loadopt == 0)
loadopt = ~0;
- if ((path = calloc(1, MAXPATHLEN)) == NULL)
- errx(1, "pfctl: calloc");
memset(anchorname, 0, sizeof(anchorname));
if (anchoropt != NULL) {
int len = strlen(anchoropt);
+ if (anchoropt[0] == '\0')
+ errx(1, "anchor name must not be empty");
+ if (mode == O_RDONLY && showopt == NULL && tblcmdopt == NULL) {
+ warnx("anchors apply to -f, -F, -s, and -T only");
+ usage();
+ }
+ if (mode == O_RDWR && tblcmdopt == NULL &&
+ (anchoropt[0] == '_' || strstr(anchoropt, "/_") != NULL))
+ errx(1, "anchor names beginning with '_' cannot "
+ "be modified from the command line");
+
if (len >= 1 && anchoropt[len - 1] == '*') {
if (len >= 2 && anchoropt[len - 2] == '/')
anchoropt[len - 2] = '\0';
@@ -3028,10 +3468,21 @@ main(int argc, char *argv[])
altqsupport = 1;
#endif
}
+ pfh = pfctl_open(pf_device);
+ if (pfh == NULL)
+ err(1, "Failed to open netlink");
+
+ if ((opts & PF_OPT_NOACTION) == 0) {
+ pfctl_read_limits(pfh);
+ atexit(pfctl_restore_limits);
+ }
if (opts & PF_OPT_DISABLE)
if (pfctl_disable(dev, opts))
- error = 1;
+ exit_val = 1;
+
+ if ((path = calloc(1, MAXPATHLEN)) == NULL)
+ errx(1, "%s: calloc", __func__);
if (showopt != NULL) {
switch (*showopt) {
@@ -3053,7 +3504,7 @@ main(int argc, char *argv[])
break;
case 'n':
pfctl_load_fingerprints(dev, opts);
- pfctl_show_nat(dev, path, opts, anchorname, 0);
+ pfctl_show_nat(dev, path, opts, anchorname, 0, 0);
break;
case 'q':
pfctl_show_altq(dev, ifaceopt, opts,
@@ -3069,7 +3520,7 @@ main(int argc, char *argv[])
pfctl_show_status(dev, opts);
break;
case 'R':
- error = pfctl_show_running(dev);
+ exit_val = pfctl_show_running(dev);
break;
case 't':
pfctl_show_timeouts(dev, opts);
@@ -3088,20 +3539,27 @@ main(int argc, char *argv[])
pfctl_show_eth_rules(dev, path, opts, 0, anchorname, 0,
0);
- pfctl_show_nat(dev, path, opts, anchorname, 0);
- pfctl_show_rules(dev, path, opts, 0, anchorname, 0, 0);
+ pfctl_show_nat(dev, path, opts, anchorname, 0, 0);
+ pfctl_show_rules(dev, path, opts, PFCTL_SHOW_RULES,
+ anchorname, 0, 0);
pfctl_show_altq(dev, ifaceopt, opts, 0);
pfctl_show_states(dev, ifaceopt, opts);
pfctl_show_src_nodes(dev, opts);
pfctl_show_status(dev, opts);
- pfctl_show_rules(dev, path, opts, 1, anchorname, 0, 0);
+ pfctl_show_rules(dev, path, opts, PFCTL_SHOW_LABELS,
+ anchorname, 0, 0);
pfctl_show_timeouts(dev, opts);
pfctl_show_limits(dev, opts);
pfctl_show_tables(anchorname, opts);
pfctl_show_fingerprints(opts);
break;
case 'T':
- pfctl_show_tables(anchorname, opts);
+ if (opts & PF_OPT_RECURSE) {
+ opts |= PF_OPT_CALLSHOW;
+ pfctl_recurse(dev, opts, anchorname,
+ pfctl_call_showtables);
+ } else
+ pfctl_show_tables(anchorname, opts);
break;
case 'o':
pfctl_load_fingerprints(dev, opts);
@@ -3110,6 +3568,9 @@ main(int argc, char *argv[])
case 'I':
pfctl_show_ifaces(ifaceopt, opts);
break;
+ case 'c':
+ pfctl_show_creators(opts);
+ break;
}
}
@@ -3121,16 +3582,16 @@ main(int argc, char *argv[])
}
if (clearopt != NULL) {
- if (anchorname[0] == '_' || strstr(anchorname, "/_") != NULL)
- errx(1, "anchor names beginning with '_' cannot "
- "be modified from the command line");
-
switch (*clearopt) {
case 'e':
pfctl_flush_eth_rules(dev, opts, anchorname);
break;
case 'r':
- pfctl_flush_rules(dev, opts, anchorname);
+ if (opts & PF_OPT_RECURSE)
+ pfctl_recurse(dev, opts, anchorname,
+ pfctl_call_clearrules);
+ else
+ pfctl_flush_rules(dev, opts, anchorname);
break;
case 'n':
pfctl_flush_nat(dev, opts, anchorname);
@@ -3145,27 +3606,45 @@ main(int argc, char *argv[])
pfctl_clear_src_nodes(dev, opts);
break;
case 'i':
- pfctl_clear_stats(dev, opts);
+ pfctl_clear_stats(pfh, opts);
break;
case 'a':
+ if (ifaceopt) {
+ warnx("don't specify an interface with -Fall");
+ usage();
+ /* NOTREACHED */
+ }
pfctl_flush_eth_rules(dev, opts, anchorname);
pfctl_flush_rules(dev, opts, anchorname);
pfctl_flush_nat(dev, opts, anchorname);
- pfctl_clear_tables(anchorname, opts);
+ if (opts & PF_OPT_RECURSE)
+ pfctl_recurse(dev, opts, anchorname,
+ pfctl_call_clearanchors);
+ else {
+ pfctl_do_clear_tables(anchorname, opts);
+ pfctl_flush_rules(dev, opts, anchorname);
+ }
if (!*anchorname) {
pfctl_clear_altq(dev, opts);
pfctl_clear_iface_states(dev, ifaceopt, opts);
pfctl_clear_src_nodes(dev, opts);
- pfctl_clear_stats(dev, opts);
+ pfctl_clear_stats(pfh, opts);
pfctl_clear_fingerprints(dev, opts);
- pfctl_clear_interface_flags(dev, opts);
+ pfctl_reset(dev, opts);
}
break;
case 'o':
pfctl_clear_fingerprints(dev, opts);
break;
case 'T':
- pfctl_clear_tables(anchorname, opts);
+ if ((opts & PF_OPT_RECURSE) == 0)
+ pfctl_do_clear_tables(anchorname, opts);
+ else
+ pfctl_recurse(dev, opts, anchorname,
+ pfctl_call_cleartables);
+ break;
+ case 'R':
+ pfctl_reset(dev, opts);
break;
}
}
@@ -3176,15 +3655,17 @@ main(int argc, char *argv[])
pfctl_id_kill_states(dev, ifaceopt, opts);
else if (!strcmp(state_kill[0], "gateway"))
pfctl_gateway_kill_states(dev, ifaceopt, opts);
+ else if (!strcmp(state_kill[0], "key"))
+ pfctl_key_kill_states(dev, ifaceopt, opts);
else
pfctl_net_kill_states(dev, ifaceopt, opts);
}
if (src_node_killers)
- pfctl_kill_src_nodes(dev, ifaceopt, opts);
+ pfctl_kill_src_nodes(dev, opts);
if (tblcmdopt != NULL) {
- error = pfctl_command_tables(argc, argv, tableopt,
+ exit_val = pfctl_table(argc, argv, tableopt,
tblcmdopt, rulesopt, anchorname, opts);
rulesopt = NULL;
}
@@ -3205,29 +3686,22 @@ main(int argc, char *argv[])
if ((rulesopt != NULL) && (loadopt & PFCTL_FLAG_OPTION) &&
!anchorname[0] && !(opts & PF_OPT_NOACTION))
- if (pfctl_get_skip_ifaces())
- error = 1;
+ pfctl_get_skip_ifaces();
- if (rulesopt != NULL && !(opts & (PF_OPT_MERGE|PF_OPT_NOACTION)) &&
+ if (rulesopt != NULL && !(opts & PF_OPT_MERGE) &&
!anchorname[0] && (loadopt & PFCTL_FLAG_OPTION))
if (pfctl_file_fingerprints(dev, opts, PF_OSFP_FILE))
- error = 1;
+ exit_val = 1;
if (rulesopt != NULL) {
- if (anchorname[0] == '_' || strstr(anchorname, "/_") != NULL)
- errx(1, "anchor names beginning with '_' cannot "
- "be modified from the command line");
if (pfctl_rules(dev, rulesopt, opts, optimize,
anchorname, NULL))
- error = 1;
- else if (!(opts & PF_OPT_NOACTION) &&
- (loadopt & PFCTL_FLAG_TABLE))
- warn_namespace_collision(NULL);
+ exit_val = 1;
}
if (opts & PF_OPT_ENABLE)
if (pfctl_enable(dev, opts))
- error = 1;
+ exit_val = 1;
if (debugopt != NULL) {
switch (*debugopt) {
@@ -3246,5 +3720,30 @@ main(int argc, char *argv[])
}
}
- exit(error);
+ /*
+ * prevent pfctl_restore_limits() exit handler from restoring
+ * pf(4) options settings on successful exit.
+ */
+ if (exit_val == 0) {
+ close(dev);
+ dev = -1;
+ pfctl_close(pfh);
+ pfh = NULL;
+ }
+
+ return (exit_val);
+}
+
+char *
+pf_strerror(int errnum)
+{
+ switch (errnum) {
+ case ESRCH:
+ return "Table does not exist.";
+ case EINVAL:
+ case ENOENT:
+ return "Anchor does not exist.";
+ default:
+ return strerror(errnum);
+ }
}
diff --git a/sbin/pfctl/pfctl.h b/sbin/pfctl/pfctl.h
index b9da5e96a90e..136f51ea08f9 100644
--- a/sbin/pfctl/pfctl.h
+++ b/sbin/pfctl/pfctl.h
@@ -36,6 +36,14 @@
#include <libpfctl.h>
+#ifdef PFCTL_DEBUG
+#define DBGPRINT(...) fprintf(stderr, __VA_ARGS__)
+#else
+#define DBGPRINT(...) (void)(0)
+#endif
+
+extern struct pfctl_handle *pfh;
+
struct pfctl;
enum pfctl_show { PFCTL_SHOW_RULES, PFCTL_SHOW_LABELS, PFCTL_SHOW_NOTHING };
@@ -53,13 +61,52 @@ struct pfr_buffer {
(var) != NULL; \
(var) = pfr_buf_next((buf), (var)))
-int pfr_get_fd(void);
-int pfr_clr_tables(struct pfr_table *, int *, int);
-int pfr_add_tables(struct pfr_table *, int, int *, int);
-int pfr_del_tables(struct pfr_table *, int, int *, int);
+RB_HEAD(pfr_ktablehead, pfr_ktable);
+struct pfr_ktable {
+ struct pfr_tstats pfrkt_ts;
+ RB_ENTRY(pfr_ktable) pfrkt_tree;
+ SLIST_ENTRY(pfr_ktable) pfrkt_workq;
+ struct radix_node_head *pfrkt_ip4;
+ struct radix_node_head *pfrkt_ip6;
+ struct pfr_ktable *pfrkt_shadow;
+ struct pfr_ktable *pfrkt_root;
+ struct pf_kruleset *pfrkt_rs;
+ long pfrkt_larg;
+ int pfrkt_nflags;
+};
+#define pfrkt_t pfrkt_ts.pfrts_t
+#define pfrkt_name pfrkt_t.pfrt_name
+#define pfrkt_anchor pfrkt_t.pfrt_anchor
+#define pfrkt_ruleset pfrkt_t.pfrt_ruleset
+#define pfrkt_flags pfrkt_t.pfrt_flags
+#define pfrkt_cnt pfrkt_kts.pfrkts_cnt
+#define pfrkt_refcnt pfrkt_kts.pfrkts_refcnt
+#define pfrkt_tzero pfrkt_kts.pfrkts_tzero
+
+struct pfr_uktable {
+ struct pfr_ktable pfrukt_kt;
+ struct pfr_buffer pfrukt_addrs;
+ int pfrukt_init_addr;
+ SLIST_ENTRY(pfr_uktable) pfrukt_entry;
+};
+
+#define pfrukt_t pfrukt_kt.pfrkt_ts.pfrts_t
+#define pfrukt_name pfrukt_kt.pfrkt_t.pfrt_name
+#define pfrukt_anchor pfrukt_kt.pfrkt_t.pfrt_anchor
+
+extern struct pfr_ktablehead pfr_ktables;
+
+struct pfr_anchoritem {
+ SLIST_ENTRY(pfr_anchoritem) pfra_sle;
+ char *pfra_anchorname;
+};
+
+SLIST_HEAD(pfr_anchors, pfr_anchoritem);
+
+int pfr_add_table(struct pfr_table *, int *, int);
+int pfr_del_table(struct pfr_table *, int *, int);
int pfr_get_tables(struct pfr_table *, struct pfr_table *, int *, int);
-int pfr_get_tstats(struct pfr_table *, struct pfr_tstats *, int *, int);
-int pfr_clr_tstats(struct pfr_table *, int, int *, int);
+int pfr_clr_astats(struct pfr_table *, struct pfr_addr *, int, int *, int);
int pfr_clr_addrs(struct pfr_table *, int *, int);
int pfr_add_addrs(struct pfr_table *, struct pfr_addr *, int, int *, int);
int pfr_del_addrs(struct pfr_table *, struct pfr_addr *, int, int *, int);
@@ -75,19 +122,19 @@ int pfr_buf_add(struct pfr_buffer *, const void *);
void *pfr_buf_next(struct pfr_buffer *, const void *);
int pfr_buf_grow(struct pfr_buffer *, int);
int pfr_buf_load(struct pfr_buffer *, char *, int,
- int (*)(struct pfr_buffer *, char *, int));
-char *pfr_strerror(int);
+ int (*)(struct pfr_buffer *, char *, int, int), int);
+char *pf_strerror(int);
int pfi_get_ifaces(const char *, struct pfi_kif *, int *);
-int pfi_clr_istats(const char *, int *, int);
void pfctl_print_title(char *);
-int pfctl_clear_tables(const char *, int);
-int pfctl_show_tables(const char *, int);
-int pfctl_command_tables(int, char *[], char *, const char *, char *,
+int pfctl_do_clear_tables(const char *, int);
+void pfctl_show_tables(const char *, int);
+int pfctl_table(int, char *[], char *, const char *, char *,
const char *, int);
int pfctl_show_altq(int, const char *, int, int);
-void warn_namespace_collision(const char *);
-int pfctl_show_ifaces(const char *, int);
+void warn_duplicate_tables(const char *, const char *);
+void pfctl_show_ifaces(const char *, int);
+void pfctl_show_creators(int);
FILE *pfctl_fopen(const char *, const char *);
#ifdef __FreeBSD__
@@ -119,10 +166,10 @@ void pfaltq_store(struct pf_altq *);
char *rate2str(double);
void print_addr(struct pf_addr_wrap *, sa_family_t, int);
+void print_addr_str(sa_family_t, struct pf_addr *);
void print_host(struct pf_addr *, u_int16_t p, sa_family_t, int);
void print_seq(struct pfctl_state_peer *);
void print_state(struct pfctl_state *, int);
-int unmask(struct pf_addr *, sa_family_t);
int pfctl_cmdline_symset(char *);
int pfctl_add_trans(struct pfr_buffer *, int, const char *);
@@ -149,4 +196,7 @@ void expand_label(char *, size_t, struct pfctl_rule *);
const char *pfctl_proto2name(int);
+void pfctl_err(int, int, const char *, ...);
+void pfctl_errx(int, int, const char *, ...);
+
#endif /* _PFCTL_H_ */
diff --git a/sbin/pfctl/pfctl_optimize.c b/sbin/pfctl/pfctl_optimize.c
index a377f9eb04dc..2d16bbd22b39 100644
--- a/sbin/pfctl/pfctl_optimize.c
+++ b/sbin/pfctl/pfctl_optimize.c
@@ -16,7 +16,6 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#include <sys/cdefs.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
@@ -136,7 +135,9 @@ static struct pf_rule_field {
PF_RULE_FIELD(return_ttl, BREAK),
PF_RULE_FIELD(overload_tblname, BREAK),
PF_RULE_FIELD(flush, BREAK),
- PF_RULE_FIELD(rpool, BREAK),
+ PF_RULE_FIELD(rdr, BREAK),
+ PF_RULE_FIELD(nat, BREAK),
+ PF_RULE_FIELD(route, BREAK),
PF_RULE_FIELD(logif, BREAK),
/*
@@ -173,6 +174,7 @@ static struct pf_rule_field {
PF_RULE_FIELD(dst.port_op, NOMERGE),
PF_RULE_FIELD(src.neg, NOMERGE),
PF_RULE_FIELD(dst.neg, NOMERGE),
+ PF_RULE_FIELD(af, NOMERGE),
/* These fields can be merged */
PF_RULE_FIELD(src.addr, COMBINED),
@@ -239,6 +241,8 @@ int skip_cmp_src_addr(struct pfctl_rule *, struct pfctl_rule *);
int skip_cmp_src_port(struct pfctl_rule *, struct pfctl_rule *);
int superblock_inclusive(struct superblock *, struct pf_opt_rule *);
void superblock_free(struct pfctl *, struct superblock *);
+struct pf_opt_tbl *pf_opt_table_ref(struct pf_opt_tbl *);
+void pf_opt_table_unref(struct pf_opt_tbl *);
static int (*skip_comparitors[PF_SKIP_COUNT])(struct pfctl_rule *,
@@ -250,8 +254,8 @@ static const char *skip_comparitors_names[PF_SKIP_COUNT];
{ "af", PF_SKIP_AF, skip_cmp_af }, \
{ "proto", PF_SKIP_PROTO, skip_cmp_proto }, \
{ "saddr", PF_SKIP_SRC_ADDR, skip_cmp_src_addr }, \
- { "sport", PF_SKIP_SRC_PORT, skip_cmp_src_port }, \
{ "daddr", PF_SKIP_DST_ADDR, skip_cmp_dst_addr }, \
+ { "sport", PF_SKIP_SRC_PORT, skip_cmp_src_port }, \
{ "dport", PF_SKIP_DST_PORT, skip_cmp_dst_port } \
}
@@ -269,7 +273,10 @@ pfctl_optimize_ruleset(struct pfctl *pf, struct pfctl_ruleset *rs)
struct pfctl_rule *r;
struct pfctl_rulequeue *old_rules;
- DEBUG("optimizing ruleset");
+ if (TAILQ_EMPTY(rs->rules[PF_RULESET_FILTER].active.ptr))
+ return (0);
+
+ DEBUG("optimizing ruleset \"%s\"", rs->anchor->path);
memset(&table_buffer, 0, sizeof(table_buffer));
skip_init();
TAILQ_INIT(&opt_queue);
@@ -290,13 +297,24 @@ pfctl_optimize_ruleset(struct pfctl *pf, struct pfctl_ruleset *rs)
if ((por = calloc(1, sizeof(*por))) == NULL)
err(1, "calloc");
memcpy(&por->por_rule, r, sizeof(*r));
- if (TAILQ_FIRST(&r->rpool.list) != NULL) {
- TAILQ_INIT(&por->por_rule.rpool.list);
- pfctl_move_pool(&r->rpool, &por->por_rule.rpool);
+ if (TAILQ_FIRST(&r->rdr.list) != NULL) {
+ TAILQ_INIT(&por->por_rule.rdr.list);
+ pfctl_move_pool(&r->rdr, &por->por_rule.rdr);
} else
- bzero(&por->por_rule.rpool,
- sizeof(por->por_rule.rpool));
-
+ bzero(&por->por_rule.rdr,
+ sizeof(por->por_rule.rdr));
+ if (TAILQ_FIRST(&r->nat.list) != NULL) {
+ TAILQ_INIT(&por->por_rule.nat.list);
+ pfctl_move_pool(&r->nat, &por->por_rule.nat);
+ } else
+ bzero(&por->por_rule.nat,
+ sizeof(por->por_rule.nat));
+ if (TAILQ_FIRST(&r->route.list) != NULL) {
+ TAILQ_INIT(&por->por_rule.route.list);
+ pfctl_move_pool(&r->route, &por->por_rule.route);
+ } else
+ bzero(&por->por_rule.route,
+ sizeof(por->por_rule.route));
TAILQ_INSERT_TAIL(&opt_queue, por, por_entry);
}
@@ -325,14 +343,18 @@ pfctl_optimize_ruleset(struct pfctl *pf, struct pfctl_ruleset *rs)
if ((r = calloc(1, sizeof(*r))) == NULL)
err(1, "calloc");
memcpy(r, &por->por_rule, sizeof(*r));
- TAILQ_INIT(&r->rpool.list);
- pfctl_move_pool(&por->por_rule.rpool, &r->rpool);
+ TAILQ_INIT(&r->rdr.list);
+ pfctl_move_pool(&por->por_rule.rdr, &r->rdr);
+ TAILQ_INIT(&r->nat.list);
+ pfctl_move_pool(&por->por_rule.nat, &r->nat);
TAILQ_INSERT_TAIL(
rs->rules[PF_RULESET_FILTER].active.ptr,
r, entries);
+ pf_opt_table_unref(por->por_src_tbl);
+ pf_opt_table_unref(por->por_dst_tbl);
free(por);
}
- free(block);
+ superblock_free(pf, block);
}
return (0);
@@ -340,16 +362,8 @@ pfctl_optimize_ruleset(struct pfctl *pf, struct pfctl_ruleset *rs)
error:
while ((por = TAILQ_FIRST(&opt_queue))) {
TAILQ_REMOVE(&opt_queue, por, por_entry);
- if (por->por_src_tbl) {
- pfr_buf_clear(por->por_src_tbl->pt_buf);
- free(por->por_src_tbl->pt_buf);
- free(por->por_src_tbl);
- }
- if (por->por_dst_tbl) {
- pfr_buf_clear(por->por_dst_tbl->pt_buf);
- free(por->por_dst_tbl->pt_buf);
- free(por->por_dst_tbl);
- }
+ pf_opt_table_unref(por->por_src_tbl);
+ pf_opt_table_unref(por->por_dst_tbl);
free(por);
}
while ((block = TAILQ_FIRST(&superblocks))) {
@@ -522,12 +536,14 @@ combine_rules(struct pfctl *pf, struct superblock *block)
if (add_opt_table(pf, &p1->por_dst_tbl,
p1->por_rule.af, &p2->por_rule.dst))
return (1);
- p2->por_dst_tbl = p1->por_dst_tbl;
if (p1->por_dst_tbl->pt_rulecount >=
TABLE_THRESHOLD) {
TAILQ_REMOVE(&block->sb_rules, p2,
por_entry);
free(p2);
+ } else {
+ p2->por_dst_tbl =
+ pf_opt_table_ref(p1->por_dst_tbl);
}
} else if (!src_eq && dst_eq && p1->por_dst_tbl == NULL
&& p2->por_src_tbl == NULL &&
@@ -544,12 +560,14 @@ combine_rules(struct pfctl *pf, struct superblock *block)
if (add_opt_table(pf, &p1->por_src_tbl,
p1->por_rule.af, &p2->por_rule.src))
return (1);
- p2->por_src_tbl = p1->por_src_tbl;
if (p1->por_src_tbl->pt_rulecount >=
TABLE_THRESHOLD) {
TAILQ_REMOVE(&block->sb_rules, p2,
por_entry);
free(p2);
+ } else {
+ p2->por_src_tbl =
+ pf_opt_table_ref(p1->por_src_tbl);
}
}
}
@@ -705,11 +723,7 @@ reorder_rules(struct pfctl *pf, struct superblock *block, int depth)
* it based on a more optimal skipstep order.
*/
TAILQ_INIT(&head);
- while ((por = TAILQ_FIRST(&block->sb_rules))) {
- TAILQ_REMOVE(&block->sb_rules, por, por_entry);
- TAILQ_INSERT_TAIL(&head, por, por_entry);
- }
-
+ TAILQ_CONCAT(&head, &block->sb_rules, por_entry);
while (!TAILQ_EMPTY(&head)) {
largest = 1;
@@ -730,11 +744,7 @@ reorder_rules(struct pfctl *pf, struct superblock *block, int depth)
* Nothing useful left. Leave remaining rules in order.
*/
DEBUG("(%d) no more commonality for skip steps", depth);
- while ((por = TAILQ_FIRST(&head))) {
- TAILQ_REMOVE(&head, por, por_entry);
- TAILQ_INSERT_TAIL(&block->sb_rules, por,
- por_entry);
- }
+ TAILQ_CONCAT(&block->sb_rules, &head, por_entry);
} else {
/*
* There is commonality. Extract those common rules
@@ -845,10 +855,7 @@ block_feedback(struct pfctl *pf, struct superblock *block)
*/
TAILQ_INIT(&queue);
- while ((por1 = TAILQ_FIRST(&block->sb_rules)) != NULL) {
- TAILQ_REMOVE(&block->sb_rules, por1, por_entry);
- TAILQ_INSERT_TAIL(&queue, por1, por_entry);
- }
+ TAILQ_CONCAT(&queue, &block->sb_rules, por_entry);
while ((por1 = TAILQ_FIRST(&queue)) != NULL) {
TAILQ_REMOVE(&queue, por1, por_entry);
@@ -878,24 +885,23 @@ block_feedback(struct pfctl *pf, struct superblock *block)
int
load_feedback_profile(struct pfctl *pf, struct superblocks *superblocks)
{
+ char anchor_call[MAXPATHLEN] = "";
struct superblock *block, *blockcur;
struct superblocks prof_superblocks;
struct pf_opt_rule *por;
struct pf_opt_queue queue;
- struct pfioc_rule pr;
+ struct pfctl_rules_info rules;
struct pfctl_rule a, b, rule;
- int nr, mnr;
+ int nr, mnr, ret;
TAILQ_INIT(&queue);
TAILQ_INIT(&prof_superblocks);
- memset(&pr, 0, sizeof(pr));
- pr.rule.action = PF_PASS;
- if (ioctl(pf->dev, DIOCGETRULES, &pr)) {
- warn("DIOCGETRULES");
+ if ((ret = pfctl_get_rules_info_h(pf->h, &rules, PF_PASS, "")) != 0) {
+ warnx("%s", pf_strerror(ret));
return (1);
}
- mnr = pr.nr;
+ mnr = rules.nr;
DEBUG("Loading %d active rules for a feedback profile", mnr);
for (nr = 0; nr < mnr; ++nr) {
@@ -904,24 +910,27 @@ load_feedback_profile(struct pfctl *pf, struct superblocks *superblocks)
warn("calloc");
return (1);
}
- pr.nr = nr;
- if (pfctl_get_rule(pf->dev, nr, pr.ticket, "", PF_PASS,
- &rule, pr.anchor_call)) {
- warn("DIOCGETRULENV");
+ if (pfctl_get_rule_h(pf->h, nr, rules.ticket, "", PF_PASS,
+ &rule, anchor_call)) {
+ warnx("%s", pf_strerror(ret));
+ free(por);
return (1);
}
memcpy(&por->por_rule, &rule, sizeof(por->por_rule));
- rs = pf_find_or_create_ruleset(pr.anchor_call);
+ rs = pf_find_or_create_ruleset(anchor_call);
por->por_rule.anchor = rs->anchor;
- if (TAILQ_EMPTY(&por->por_rule.rpool.list))
- memset(&por->por_rule.rpool, 0,
- sizeof(por->por_rule.rpool));
+ if (TAILQ_EMPTY(&por->por_rule.rdr.list))
+ memset(&por->por_rule.rdr, 0,
+ sizeof(por->por_rule.rdr));
+ if (TAILQ_EMPTY(&por->por_rule.nat.list))
+ memset(&por->por_rule.nat, 0,
+ sizeof(por->por_rule.nat));
TAILQ_INSERT_TAIL(&queue, por, por_entry);
- /* XXX pfctl_get_pool(pf->dev, &rule.rpool, nr, pr.ticket,
+ /* XXX pfctl_get_pool(pf->dev, &rule.rdr, nr, pr.ticket,
* PF_PASS, pf->anchor) ???
- * ... pfctl_clear_pool(&rule.rpool)
+ * ... pfctl_clear_pool(&rule.rdr)
*/
}
@@ -1233,12 +1242,13 @@ add_opt_table(struct pfctl *pf, struct pf_opt_tbl **tbl, sa_family_t af,
((*tbl)->pt_buf = calloc(1, sizeof(*(*tbl)->pt_buf))) ==
NULL)
err(1, "calloc");
+ (*tbl)->pt_refcnt = 1;
(*tbl)->pt_buf->pfrb_type = PFRB_ADDRS;
SIMPLEQ_INIT(&(*tbl)->pt_nodes);
/* This is just a temporary table name */
snprintf((*tbl)->pt_name, sizeof((*tbl)->pt_name), "%s%d",
- PF_OPT_TABLE_PREFIX, tablenum++);
+ PF_OPTIMIZER_TABLE_PFX, tablenum++);
DEBUG("creating table <%s>", (*tbl)->pt_name);
}
@@ -1249,7 +1259,7 @@ add_opt_table(struct pfctl *pf, struct pf_opt_tbl **tbl, sa_family_t af,
#ifdef OPT_DEBUG
DEBUG("<%s> adding %s/%d", (*tbl)->pt_name, inet_ntop(af,
&node_host.addr.v.a.addr, buf, sizeof(buf)),
- unmask(&node_host.addr.v.a.mask, af));
+ unmask(&node_host.addr.v.a.mask));
#endif /* OPT_DEBUG */
if (append_addr_host((*tbl)->pt_buf, &node_host, 0, 0)) {
@@ -1305,9 +1315,9 @@ pf_opt_create_table(struct pfctl *pf, struct pf_opt_tbl *tbl)
/* Now we have to pick a table name that isn't used */
again:
DEBUG("translating temporary table <%s> to <%s%x_%d>", tbl->pt_name,
- PF_OPT_TABLE_PREFIX, table_identifier, tablenum);
+ PF_OPTIMIZER_TABLE_PFX, table_identifier, tablenum);
snprintf(tbl->pt_name, sizeof(tbl->pt_name), "%s%x_%d",
- PF_OPT_TABLE_PREFIX, table_identifier, tablenum);
+ PF_OPTIMIZER_TABLE_PFX, table_identifier, tablenum);
PFRB_FOREACH(t, &table_buffer) {
if (strcasecmp(t->pfrt_name, tbl->pt_name) == 0) {
/* Collision. Try again */
@@ -1321,7 +1331,8 @@ again:
if (pfctl_define_table(tbl->pt_name, PFR_TFLAG_CONST, 1,
- pf->astack[0]->name, tbl->pt_buf, pf->astack[0]->ruleset.tticket)) {
+ pf->astack[0]->path, tbl->pt_buf, pf->astack[0]->ruleset.tticket,
+ NULL)) {
warn("failed to create table %s in %s",
tbl->pt_name, pf->astack[0]->name);
return (1);
@@ -1584,8 +1595,8 @@ exclude_supersets(struct pfctl_rule *super, struct pfctl_rule *sub)
sub->src.addr.type == PF_ADDR_ADDRMASK &&
super->src.neg == sub->src.neg &&
super->af == sub->af &&
- unmask(&super->src.addr.v.a.mask, super->af) <
- unmask(&sub->src.addr.v.a.mask, sub->af) &&
+ unmask(&super->src.addr.v.a.mask) <
+ unmask(&sub->src.addr.v.a.mask) &&
super->src.addr.v.a.addr.addr32[0] ==
(sub->src.addr.v.a.addr.addr32[0] &
super->src.addr.v.a.mask.addr32[0]) &&
@@ -1612,8 +1623,8 @@ exclude_supersets(struct pfctl_rule *super, struct pfctl_rule *sub)
sub->dst.addr.type == PF_ADDR_ADDRMASK &&
super->dst.neg == sub->dst.neg &&
super->af == sub->af &&
- unmask(&super->dst.addr.v.a.mask, super->af) <
- unmask(&sub->dst.addr.v.a.mask, sub->af) &&
+ unmask(&super->dst.addr.v.a.mask) <
+ unmask(&sub->dst.addr.v.a.mask) &&
super->dst.addr.v.a.addr.addr32[0] ==
(sub->dst.addr.v.a.addr.addr32[0] &
super->dst.addr.v.a.mask.addr32[0]) &&
@@ -1641,20 +1652,8 @@ superblock_free(struct pfctl *pf, struct superblock *block)
struct pf_opt_rule *por;
while ((por = TAILQ_FIRST(&block->sb_rules))) {
TAILQ_REMOVE(&block->sb_rules, por, por_entry);
- if (por->por_src_tbl) {
- if (por->por_src_tbl->pt_buf) {
- pfr_buf_clear(por->por_src_tbl->pt_buf);
- free(por->por_src_tbl->pt_buf);
- }
- free(por->por_src_tbl);
- }
- if (por->por_dst_tbl) {
- if (por->por_dst_tbl->pt_buf) {
- pfr_buf_clear(por->por_dst_tbl->pt_buf);
- free(por->por_dst_tbl->pt_buf);
- }
- free(por->por_dst_tbl);
- }
+ pf_opt_table_unref(por->por_src_tbl);
+ pf_opt_table_unref(por->por_dst_tbl);
free(por);
}
if (block->sb_profiled_block)
@@ -1662,3 +1661,24 @@ superblock_free(struct pfctl *pf, struct superblock *block)
free(block);
}
+struct pf_opt_tbl *
+pf_opt_table_ref(struct pf_opt_tbl *pt)
+{
+ /* parser does not run concurrently, we don't need atomic ops. */
+ if (pt != NULL)
+ pt->pt_refcnt++;
+
+ return (pt);
+}
+
+void
+pf_opt_table_unref(struct pf_opt_tbl *pt)
+{
+ if ((pt != NULL) && ((--pt->pt_refcnt) == 0)) {
+ if (pt->pt_buf != NULL) {
+ pfr_buf_clear(pt->pt_buf);
+ free(pt->pt_buf);
+ }
+ free(pt);
+ }
+}
diff --git a/sbin/pfctl/pfctl_osfp.c b/sbin/pfctl/pfctl_osfp.c
index 7fcdcf96228d..5770c8343a46 100644
--- a/sbin/pfctl/pfctl_osfp.c
+++ b/sbin/pfctl/pfctl_osfp.c
@@ -16,7 +16,6 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#include <sys/cdefs.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
@@ -113,16 +112,11 @@ pfctl_file_fingerprints(int dev, int opts, const char *fp_filename)
while ((line = fgetln(in, &len)) != NULL) {
lineno++;
- if (class)
- free(class);
- if (version)
- free(version);
- if (subtype)
- free(subtype);
- if (desc)
- free(desc);
- if (tcpopts)
- free(tcpopts);
+ free(class);
+ free(version);
+ free(subtype);
+ free(desc);
+ free(tcpopts);
class = version = subtype = desc = tcpopts = NULL;
memset(&fp, 0, sizeof(fp));
@@ -251,16 +245,11 @@ pfctl_file_fingerprints(int dev, int opts, const char *fp_filename)
add_fingerprint(dev, opts, &fp);
}
- if (class)
- free(class);
- if (version)
- free(version);
- if (subtype)
- free(subtype);
- if (desc)
- free(desc);
- if (tcpopts)
- free(tcpopts);
+ free(class);
+ free(version);
+ free(subtype);
+ free(desc);
+ free(tcpopts);
fclose(in);
@@ -275,7 +264,7 @@ void
pfctl_clear_fingerprints(int dev, int opts)
{
if (ioctl(dev, DIOCOSFPFLUSH))
- err(1, "DIOCOSFPFLUSH");
+ pfctl_err(opts, 1, "DIOCOSFPFLUSH");
}
/* flush pfctl's view of the fingerprints */
diff --git a/sbin/pfctl/pfctl_parser.c b/sbin/pfctl/pfctl_parser.c
index 936c5ec53759..b8531067d3f6 100644
--- a/sbin/pfctl/pfctl_parser.c
+++ b/sbin/pfctl/pfctl_parser.c
@@ -33,18 +33,19 @@
*
*/
-#include <sys/cdefs.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/param.h>
#include <sys/proc.h>
+#include <net/if_dl.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/icmp6.h>
+#include <netinet/tcp.h>
#include <net/pfvar.h>
#include <arpa/inet.h>
@@ -59,6 +60,7 @@
#include <errno.h>
#include <err.h>
#include <ifaddrs.h>
+#include <inttypes.h>
#include <unistd.h>
#include "pfctl_parser.h"
@@ -66,18 +68,17 @@
void print_op (u_int8_t, const char *, const char *);
void print_port (u_int8_t, u_int16_t, u_int16_t, const char *, int);
-void print_ugid (u_int8_t, unsigned, unsigned, const char *, unsigned);
-void print_flags (u_int8_t);
+void print_ugid (u_int8_t, id_t, id_t, const char *);
+void print_flags (uint16_t);
void print_fromto(struct pf_rule_addr *, pf_osfp_t,
struct pf_rule_addr *, sa_family_t, u_int8_t, int, int);
int ifa_skip_if(const char *filter, struct node_host *p);
-struct node_host *host_if(const char *, int, int *);
-struct node_host *host_v4(const char *, int);
-struct node_host *host_v6(const char *, int);
+struct node_host *host_if(const char *, int);
+struct node_host *host_ip(const char *, int);
struct node_host *host_dns(const char *, int, int);
-const char * const tcpflags = "FSRPAUEW";
+const char * const tcpflags = "FSRPAUEWe";
static const struct icmptypeent icmp_type[] = {
{ "echoreq", ICMP_ECHO },
@@ -133,7 +134,8 @@ static const struct icmptypeent icmp6_type[] = {
{ "niqry", ICMP6_NI_QUERY },
{ "nirep", ICMP6_NI_REPLY },
{ "mtraceresp", MLD_MTRACE_RESP },
- { "mtrace", MLD_MTRACE }
+ { "mtrace", MLD_MTRACE },
+ { "listenrepv2", MLDV2_LISTENER_REPORT },
};
static const struct icmpcodeent icmp_code[] = {
@@ -192,6 +194,11 @@ const struct pf_timeout pf_timeouts[] = {
{ "tcp.finwait", PFTM_TCP_FIN_WAIT },
{ "tcp.closed", PFTM_TCP_CLOSED },
{ "tcp.tsdiff", PFTM_TS_DIFF },
+ { "sctp.first", PFTM_SCTP_FIRST_PACKET },
+ { "sctp.opening", PFTM_SCTP_OPENING },
+ { "sctp.established", PFTM_SCTP_ESTABLISHED },
+ { "sctp.closing", PFTM_SCTP_CLOSING },
+ { "sctp.closed", PFTM_SCTP_CLOSED },
{ "udp.first", PFTM_UDP_FIRST_PACKET },
{ "udp.single", PFTM_UDP_SINGLE },
{ "udp.multiple", PFTM_UDP_MULTIPLE },
@@ -221,10 +228,21 @@ pfctl_parser_init(void)
err(1, "Failed to create interface group query response map");
}
+void
+copy_satopfaddr(struct pf_addr *pfa, struct sockaddr *sa)
+{
+ if (sa->sa_family == AF_INET6)
+ pfa->v6 = ((struct sockaddr_in6 *)sa)->sin6_addr;
+ else if (sa->sa_family == AF_INET)
+ pfa->v4 = ((struct sockaddr_in *)sa)->sin_addr;
+ else
+ warnx("unhandled af %d", sa->sa_family);
+}
+
const struct icmptypeent *
geticmptypebynumber(u_int8_t type, sa_family_t af)
{
- unsigned int i;
+ size_t i;
if (af != AF_INET6) {
for (i=0; i < nitems(icmp_type); i++) {
@@ -243,7 +261,7 @@ geticmptypebynumber(u_int8_t type, sa_family_t af)
const struct icmptypeent *
geticmptypebyname(char *w, sa_family_t af)
{
- unsigned int i;
+ size_t i;
if (af != AF_INET6) {
for (i=0; i < nitems(icmp_type); i++) {
@@ -262,7 +280,7 @@ geticmptypebyname(char *w, sa_family_t af)
const struct icmpcodeent *
geticmpcodebynumber(u_int8_t type, u_int8_t code, sa_family_t af)
{
- unsigned int i;
+ size_t i;
if (af != AF_INET6) {
for (i=0; i < nitems(icmp_code); i++) {
@@ -283,7 +301,7 @@ geticmpcodebynumber(u_int8_t type, u_int8_t code, sa_family_t af)
const struct icmpcodeent *
geticmpcodebyname(u_long type, char *w, sa_family_t af)
{
- unsigned int i;
+ size_t i;
if (af != AF_INET6) {
for (i=0; i < nitems(icmp_code); i++) {
@@ -346,21 +364,21 @@ print_port(u_int8_t op, u_int16_t p1, u_int16_t p2, const char *proto, int numer
}
void
-print_ugid(u_int8_t op, unsigned u1, unsigned u2, const char *t, unsigned umax)
+print_ugid(u_int8_t op, id_t i1, id_t i2, const char *t)
{
char a1[11], a2[11];
- snprintf(a1, sizeof(a1), "%u", u1);
- snprintf(a2, sizeof(a2), "%u", u2);
+ snprintf(a1, sizeof(a1), "%ju", (uintmax_t)i1);
+ snprintf(a2, sizeof(a2), "%ju", (uintmax_t)i2);
printf(" %s", t);
- if (u1 == umax && (op == PF_OP_EQ || op == PF_OP_NE))
+ if (i1 == -1 && (op == PF_OP_EQ || op == PF_OP_NE))
print_op(op, "unknown", a2);
else
print_op(op, a1, a2);
}
void
-print_flags(u_int8_t f)
+print_flags(uint16_t f)
{
int i;
@@ -371,9 +389,11 @@ print_flags(u_int8_t f)
void
print_fromto(struct pf_rule_addr *src, pf_osfp_t osfp, struct pf_rule_addr *dst,
- sa_family_t af, u_int8_t proto, int verbose, int numeric)
+ sa_family_t af, u_int8_t proto, int opts, int numeric)
{
char buf[PF_OSFP_LEN*3];
+ int verbose = opts & (PF_OPT_VERBOSE2 | PF_OPT_DEBUG);
+
if (src->addr.type == PF_ADDR_ADDRMASK &&
dst->addr.type == PF_ADDR_ADDRMASK &&
PF_AZERO(&src->addr.v.a.addr, AF_INET6) &&
@@ -411,10 +431,9 @@ print_fromto(struct pf_rule_addr *src, pf_osfp_t osfp, struct pf_rule_addr *dst,
}
void
-print_pool(struct pfctl_pool *pool, u_int16_t p1, u_int16_t p2,
- sa_family_t af, int id)
+print_pool(struct pfctl_pool *pool, u_int16_t p1, u_int16_t p2, int id)
{
- struct pf_pooladdr *pooladdr;
+ struct pfctl_pooladdr *pooladdr;
if ((TAILQ_FIRST(&pool->list) != NULL) &&
TAILQ_NEXT(TAILQ_FIRST(&pool->list), entries) != NULL)
@@ -424,15 +443,15 @@ print_pool(struct pfctl_pool *pool, u_int16_t p1, u_int16_t p2,
case PF_NAT:
case PF_RDR:
case PF_BINAT:
- print_addr(&pooladdr->addr, af, 0);
+ print_addr(&pooladdr->addr, pooladdr->af, 0);
break;
case PF_PASS:
case PF_MATCH:
- if (PF_AZERO(&pooladdr->addr.v.a.addr, af))
+ if (PF_AZERO(&pooladdr->addr.v.a.addr, pooladdr->af))
printf("%s", pooladdr->ifname);
else {
printf("(%s ", pooladdr->ifname);
- print_addr(&pooladdr->addr, af, 0);
+ print_addr(&pooladdr->addr, pooladdr->af, 0);
printf(")");
}
break;
@@ -484,18 +503,17 @@ print_pool(struct pfctl_pool *pool, u_int16_t p1, u_int16_t p2,
}
if (pool->opts & PF_POOL_STICKYADDR)
printf(" sticky-address");
+ if (pool->opts & PF_POOL_ENDPI)
+ printf(" endpoint-independent");
if (id == PF_NAT && p1 == 0 && p2 == 0)
printf(" static-port");
if (pool->mape.offset > 0)
printf(" map-e-portset %u/%u/%u",
pool->mape.offset, pool->mape.psidlen, pool->mape.psid);
+ if (pool->opts & PF_POOL_IPV6NH)
+ printf(" prefer-ipv6-nexthop");
}
-const char * const pf_reasons[PFRES_MAX+1] = PFRES_NAMES;
-const char * const pf_lcounters[LCNT_MAX+1] = LCNT_NAMES;
-const char * const pf_fcounters[FCNT_MAX+1] = FCNT_NAMES;
-const char * const pf_scounters[FCNT_MAX+1] = FCNT_NAMES;
-
void
print_status(struct pfctl_status *s, struct pfctl_syncookies *cookies, int opts)
{
@@ -510,7 +528,8 @@ print_status(struct pfctl_status *s, struct pfctl_syncookies *cookies, int opts)
running = s->running ? "Enabled" : "Disabled";
if (s->since) {
- unsigned int sec, min, hrs, day = runtime;
+ unsigned int sec, min, hrs;
+ time_t day = runtime;
sec = day % 60;
day /= 60;
@@ -519,8 +538,8 @@ print_status(struct pfctl_status *s, struct pfctl_syncookies *cookies, int opts)
hrs = day % 24;
day /= 24;
snprintf(statline, sizeof(statline),
- "Status: %s for %u days %.2u:%.2u:%.2u",
- running, day, hrs, min, sec);
+ "Status: %s for %lld days %.2u:%.2u:%.2u",
+ running, (long long)day, hrs, min, sec);
} else
snprintf(statline, sizeof(statline), "Status: %s", running);
printf("%-44s", statline);
@@ -597,6 +616,20 @@ print_status(struct pfctl_status *s, struct pfctl_syncookies *cookies, int opts)
printf("%14s\n", "");
}
}
+ if (opts & PF_OPT_VERBOSE) {
+ printf("Fragments\n");
+ printf(" %-25s %14ju %14s\n", "current entries",
+ s->fragments, "");
+ TAILQ_FOREACH(c, &s->ncounters, entry) {
+ printf(" %-25s %14ju ", c->name,
+ c->counter);
+ if (runtime > 0)
+ printf("%14.1f/s\n",
+ (double)c->counter / (double)runtime);
+ else
+ printf("%14s\n", "");
+ }
+ }
printf("Counters\n");
TAILQ_FOREACH(c, &s->counters, entry) {
printf(" %-25s %14ju ", c->name, c->counter);
@@ -623,6 +656,11 @@ print_status(struct pfctl_status *s, struct pfctl_syncookies *cookies, int opts)
PFCTL_SYNCOOKIES_MODE_NAMES[cookies->mode]);
printf(" %-25s %s\n", "active",
s->syncookies_active ? "active" : "inactive");
+ if (opts & PF_OPT_VERBOSE2) {
+ printf(" %-25s %d %%\n", "highwater", cookies->highwater);
+ printf(" %-25s %d %%\n", "lowwater", cookies->lowwater);
+ printf(" %-25s %d\n", "halfopen states", cookies->halfopen_states);
+ }
printf("Reassemble %24s %s\n",
s->reass & PF_REASS_ENABLED ? "yes" : "no",
s->reass & PF_REASS_NODF ? "no-df" : ""
@@ -637,10 +675,11 @@ print_running(struct pfctl_status *status)
}
void
-print_src_node(struct pf_src_node *sn, int opts)
+print_src_node(struct pfctl_src_node *sn, int opts)
{
struct pf_addr_wrap aw;
- int min, sec;
+ uint64_t min, sec;
+ const char *sn_type_names[] = PF_SN_TYPE_NAMES;
memset(&aw, 0, sizeof(aw));
if (sn->af == AF_INET)
@@ -652,7 +691,7 @@ print_src_node(struct pf_src_node *sn, int opts)
print_addr(&aw, sn->af, opts & PF_OPT_VERBOSE2);
printf(" -> ");
aw.v.a.addr = sn->raddr;
- print_addr(&aw, sn->af, opts & PF_OPT_VERBOSE2);
+ print_addr(&aw, sn->raf, opts & PF_OPT_VERBOSE2);
printf(" ( states %u, connections %u, rate %u.%u/%us )\n", sn->states,
sn->conn, sn->conn_rate.count / 1000,
(sn->conn_rate.count % 1000) / 100, sn->conn_rate.seconds);
@@ -661,38 +700,35 @@ print_src_node(struct pf_src_node *sn, int opts)
sn->creation /= 60;
min = sn->creation % 60;
sn->creation /= 60;
- printf(" age %.2u:%.2u:%.2u", sn->creation, min, sec);
+ printf(" age %.2" PRIu64 ":%.2" PRIu64 ":%.2" PRIu64,
+ sn->creation, min, sec);
if (sn->states == 0) {
sec = sn->expire % 60;
sn->expire /= 60;
min = sn->expire % 60;
sn->expire /= 60;
- printf(", expires in %.2u:%.2u:%.2u",
+ printf(", expires in %.2" PRIu64 ":%.2" PRIu64 ":%.2" PRIu64,
sn->expire, min, sec);
}
- printf(", %llu pkts, %llu bytes",
-#ifdef __FreeBSD__
- (unsigned long long)(sn->packets[0] + sn->packets[1]),
- (unsigned long long)(sn->bytes[0] + sn->bytes[1]));
-#else
+ printf(", %" PRIu64 " pkts, %" PRIu64 " bytes",
sn->packets[0] + sn->packets[1],
sn->bytes[0] + sn->bytes[1]);
-#endif
switch (sn->ruletype) {
case PF_NAT:
- if (sn->rule.nr != -1)
- printf(", nat rule %u", sn->rule.nr);
+ if (sn->rule != -1)
+ printf(", nat rule %u", sn->rule);
break;
case PF_RDR:
- if (sn->rule.nr != -1)
- printf(", rdr rule %u", sn->rule.nr);
+ if (sn->rule != -1)
+ printf(", rdr rule %u", sn->rule);
break;
case PF_PASS:
case PF_MATCH:
- if (sn->rule.nr != -1)
- printf(", filter rule %u", sn->rule.nr);
+ if (sn->rule != -1)
+ printf(", filter rule %u", sn->rule);
break;
}
+ printf(", %s", sn_type_names[sn->type]);
printf("\n");
}
}
@@ -820,34 +856,39 @@ print_eth_rule(struct pfctl_eth_rule *r, const char *anchor_call,
}
void
-print_rule(struct pfctl_rule *r, const char *anchor_call, int verbose, int numeric)
+print_rule(struct pfctl_rule *r, const char *anchor_call, int opts, int numeric)
{
static const char *actiontypes[] = { "pass", "block", "scrub",
"no scrub", "nat", "no nat", "binat", "no binat", "rdr", "no rdr",
- "", "", "match"};
+ "synproxy drop", "defer", "match", "af-rt", "route-to" };
static const char *anchortypes[] = { "anchor", "anchor", "anchor",
"anchor", "nat-anchor", "nat-anchor", "binat-anchor",
"binat-anchor", "rdr-anchor", "rdr-anchor" };
- int i, opts;
+ int i, ropts;
+ int verbose = opts & (PF_OPT_VERBOSE2 | PF_OPT_DEBUG);
char *p;
+ if ((r->rule_flag & PFRULE_EXPIRED) && (!verbose))
+ return;
+
if (verbose)
printf("@%d ", r->nr);
- if (r->action == PF_MATCH)
- printf("match");
- else if (r->action > PF_NORDR)
- printf("action(%d)", r->action);
- else if (anchor_call[0]) {
- p = strrchr(anchor_call, '/');
- if (p ? p[1] == '_' : anchor_call[0] == '_')
- printf("%s", anchortypes[r->action]);
- else
- printf("%s \"%s\"", anchortypes[r->action],
- anchor_call);
+ if (anchor_call[0]) {
+ if (r->action >= nitems(anchortypes)) {
+ printf("anchor(%d)", r->action);
+ } else {
+ p = strrchr(anchor_call, '/');
+ if (p ? p[1] == '_' : anchor_call[0] == '_')
+ printf("%s", anchortypes[r->action]);
+ else
+ printf("%s \"%s\"", anchortypes[r->action],
+ anchor_call);
+ }
} else {
- printf("%s", actiontypes[r->action]);
- if (r->natpass)
- printf(" pass");
+ if (r->action >= nitems(actiontypes))
+ printf("action(%d)", r->action);
+ else
+ printf("%s", actiontypes[r->action]);
}
if (r->action == PF_DROP) {
if (r->rule_flag & PFRULE_RETURN)
@@ -907,7 +948,9 @@ print_rule(struct pfctl_rule *r, const char *anchor_call, int verbose, int numer
printf(" (");
if (r->log & PF_LOG_ALL)
printf("%sall", count++ ? ", " : "");
- if (r->log & PF_LOG_SOCKET_LOOKUP)
+ if (r->log & PF_LOG_MATCHES)
+ printf("%smatches", count++ ? ", " : "");
+ if (r->log & PF_LOG_USER)
printf("%suser", count++ ? ", " : "");
if (r->logif)
printf("%sto pflog%u", count++ ? ", " : "",
@@ -931,7 +974,7 @@ print_rule(struct pfctl_rule *r, const char *anchor_call, int verbose, int numer
else if (r->rt == PF_DUPTO)
printf(" dup-to");
printf(" ");
- print_pool(&r->rpool, 0, 0, r->af, PF_PASS);
+ print_pool(&r->route, 0, 0, PF_PASS);
}
if (r->af) {
if (r->af == AF_INET)
@@ -948,13 +991,14 @@ print_rule(struct pfctl_rule *r, const char *anchor_call, int verbose, int numer
printf(" proto %u", r->proto);
}
print_fromto(&r->src, r->os_fingerprint, &r->dst, r->af, r->proto,
- verbose, numeric);
+ opts, numeric);
+ if (r->rcv_ifname[0])
+ printf(" %sreceived-on %s", r->rcvifnot ? "!" : "",
+ r->rcv_ifname);
if (r->uid.op)
- print_ugid(r->uid.op, r->uid.uid[0], r->uid.uid[1], "user",
- UID_MAX);
+ print_ugid(r->uid.op, r->uid.uid[0], r->uid.uid[1], "user");
if (r->gid.op)
- print_ugid(r->gid.op, r->gid.gid[0], r->gid.gid[1], "group",
- GID_MAX);
+ print_ugid(r->gid.op, r->gid.gid[0], r->gid.gid[1], "group");
if (r->flags || r->flagset) {
printf(" flags ");
print_flags(r->flags);
@@ -991,6 +1035,11 @@ print_rule(struct pfctl_rule *r, const char *anchor_call, int verbose, int numer
printf(" tos 0x%2.2x", r->tos);
if (r->prio)
printf(" prio %u", r->prio == PF_PRIO_ZERO ? 0 : r->prio);
+ if (r->pktrate.limit)
+ printf(" max-pkt-rate %u/%u", r->pktrate.limit,
+ r->pktrate.seconds);
+ if (r->max_pkt_size)
+ printf( " max-pkt-size %u", r->max_pkt_size);
if (r->scrub_flags & PFSTATE_SETMASK) {
char *comma = "";
printf(" set (");
@@ -1031,70 +1080,72 @@ print_rule(struct pfctl_rule *r, const char *anchor_call, int verbose, int numer
}
printf(" probability %s%%", buf);
}
- opts = 0;
+ ropts = 0;
if (r->max_states || r->max_src_nodes || r->max_src_states)
- opts = 1;
+ ropts = 1;
if (r->rule_flag & PFRULE_NOSYNC)
- opts = 1;
+ ropts = 1;
if (r->rule_flag & PFRULE_SRCTRACK)
- opts = 1;
+ ropts = 1;
if (r->rule_flag & PFRULE_IFBOUND)
- opts = 1;
+ ropts = 1;
if (r->rule_flag & PFRULE_STATESLOPPY)
- opts = 1;
- for (i = 0; !opts && i < PFTM_MAX; ++i)
+ ropts = 1;
+ if (r->rule_flag & PFRULE_PFLOW)
+ ropts = 1;
+ for (i = 0; !ropts && i < PFTM_MAX; ++i)
if (r->timeout[i])
- opts = 1;
- if (opts) {
+ ropts = 1;
+ if (ropts) {
printf(" (");
if (r->max_states) {
printf("max %u", r->max_states);
- opts = 0;
+ ropts = 0;
}
if (r->rule_flag & PFRULE_NOSYNC) {
- if (!opts)
+ if (!ropts)
printf(", ");
printf("no-sync");
- opts = 0;
+ ropts = 0;
}
if (r->rule_flag & PFRULE_SRCTRACK) {
- if (!opts)
+ if (!ropts)
printf(", ");
printf("source-track");
if (r->rule_flag & PFRULE_RULESRCTRACK)
printf(" rule");
else
printf(" global");
- opts = 0;
+ ropts = 0;
}
if (r->max_src_states) {
- if (!opts)
+ if (!ropts)
printf(", ");
printf("max-src-states %u", r->max_src_states);
- opts = 0;
+ ropts = 0;
}
if (r->max_src_conn) {
- if (!opts)
+ if (!ropts)
printf(", ");
printf("max-src-conn %u", r->max_src_conn);
- opts = 0;
+ ropts = 0;
}
if (r->max_src_conn_rate.limit) {
- if (!opts)
+ if (!ropts)
printf(", ");
printf("max-src-conn-rate %u/%u",
r->max_src_conn_rate.limit,
r->max_src_conn_rate.seconds);
- opts = 0;
+ ropts = 0;
}
if (r->max_src_nodes) {
- if (!opts)
+ if (!ropts)
printf(", ");
printf("max-src-nodes %u", r->max_src_nodes);
- opts = 0;
+ ropts = 0;
}
if (r->overload_tblname[0]) {
- if (!opts)
+ if (!ropts)
printf(", ");
printf("overload <%s>", r->overload_tblname);
if (r->flush)
@@ -1103,24 +1154,30 @@ print_rule(struct pfctl_rule *r, const char *anchor_call, int verbose, int numer
printf(" global");
}
if (r->rule_flag & PFRULE_IFBOUND) {
- if (!opts)
+ if (!ropts)
printf(", ");
printf("if-bound");
- opts = 0;
+ ropts = 0;
}
if (r->rule_flag & PFRULE_STATESLOPPY) {
- if (!opts)
+ if (!ropts)
printf(", ");
printf("sloppy");
- opts = 0;
+ ropts = 0;
+ }
+ if (r->rule_flag & PFRULE_PFLOW) {
+ if (!ropts)
+ printf(", ");
+ printf("pflow");
+ ropts = 0;
}
for (i = 0; i < PFTM_MAX; ++i)
if (r->timeout[i]) {
int j;
- if (!opts)
+ if (!ropts)
printf(", ");
- opts = 0;
+ ropts = 0;
for (j = 0; pf_timeouts[j].name != NULL;
++j)
if (pf_timeouts[j].timeout == i)
@@ -1183,6 +1240,8 @@ print_rule(struct pfctl_rule *r, const char *anchor_call, int verbose, int numer
printf(" %s %d",
r->free_flags & PFRULE_DN_IS_PIPE ? "dnpipe" : "dnqueue",
r->dnpipe);
+ if (r->rule_flag & PFRULE_ONCE)
+ printf(" once");
if (r->qname[0] && r->pqname[0])
printf(" queue(%s, %s)", r->qname, r->pqname);
else if (r->qname[0])
@@ -1203,24 +1262,44 @@ print_rule(struct pfctl_rule *r, const char *anchor_call, int verbose, int numer
if (PF_AZERO(&r->divert.addr, r->af)) {
printf(" divert-reply");
} else {
- /* XXX cut&paste from print_addr */
- char buf[48];
-
printf(" divert-to ");
- if (inet_ntop(r->af, &r->divert.addr, buf,
- sizeof(buf)) == NULL)
- printf("?");
- else
- printf("%s", buf);
+ print_addr_str(r->af, &r->divert.addr);
printf(" port %u", ntohs(r->divert.port));
}
#endif
}
- if (!anchor_call[0] && (r->action == PF_NAT ||
- r->action == PF_BINAT || r->action == PF_RDR)) {
+ if (anchor_call[0])
+ return;
+ if (r->action == PF_NAT || r->action == PF_BINAT || r->action == PF_RDR) {
printf(" -> ");
- print_pool(&r->rpool, r->rpool.proxy_port[0],
- r->rpool.proxy_port[1], r->af, r->action);
+ print_pool(&r->rdr, r->rdr.proxy_port[0],
+ r->rdr.proxy_port[1], r->action);
+ } else {
+ if (!TAILQ_EMPTY(&r->nat.list)) {
+ if (r->rule_flag & PFRULE_AFTO) {
+ printf(" af-to %s from ", r->naf == AF_INET ? "inet" : (r->naf == AF_INET6 ? "inet6" : "? "));
+ } else {
+ printf(" nat-to ");
+ }
+ print_pool(&r->nat, r->nat.proxy_port[0],
+ r->nat.proxy_port[1], PF_NAT);
+ }
+ if (!TAILQ_EMPTY(&r->rdr.list)) {
+ if (r->rule_flag & PFRULE_AFTO) {
+ printf(" to ");
+ } else {
+ printf(" rdr-to ");
+ }
+ print_pool(&r->rdr, r->rdr.proxy_port[0],
+ r->rdr.proxy_port[1], PF_RDR);
+ }
+ }
+
+ if (r->rule_flag & PFRULE_EXPIRED) {
+ printf(" # expired");
+
+ if (r->exptime != 0)
+ printf(" %s", ctime(&r->exptime));
}
}
@@ -1266,7 +1345,7 @@ int
parse_flags(char *s)
{
char *p, *q;
- u_int8_t f = 0;
+ uint16_t f = 0;
for (p = s; *p; p++) {
if ((q = strchr(tcpflags, *p)) == NULL)
@@ -1274,18 +1353,24 @@ parse_flags(char *s)
else
f |= 1 << (q - tcpflags);
}
- return (f ? f : PF_TH_ALL);
+ return (f ? f : TH_FLAGS);
}
void
-set_ipmask(struct node_host *h, u_int8_t b)
+set_ipmask(struct node_host *h, int bb)
{
struct pf_addr *m, *n;
int i, j = 0;
+ uint8_t b;
m = &h->addr.v.a.mask;
memset(m, 0, sizeof(*m));
+ if (bb == -1)
+ b = h->af == AF_INET ? 32 : 128;
+ else
+ b = bb;
+
while (b >= 32) {
m->addr32[j++] = 0xffffffff;
b -= 32;
@@ -1306,27 +1391,45 @@ int
check_netmask(struct node_host *h, sa_family_t af)
{
struct node_host *n = NULL;
- struct pf_addr *m;
+ struct pf_addr *m;
for (n = h; n != NULL; n = n->next) {
if (h->addr.type == PF_ADDR_TABLE)
continue;
m = &h->addr.v.a.mask;
- /* fix up netmask for dynaddr */
- if (af == AF_INET && h->addr.type == PF_ADDR_DYNIFTL &&
- unmask(m, AF_INET6) > 32)
- set_ipmask(n, 32);
/* netmasks > 32 bit are invalid on v4 */
if (af == AF_INET &&
(m->addr32[1] || m->addr32[2] || m->addr32[3])) {
fprintf(stderr, "netmask %u invalid for IPv4 address\n",
- unmask(m, AF_INET6));
+ unmask(m));
return (1);
}
}
return (0);
}
+struct node_host *
+gen_dynnode(struct node_host *h, sa_family_t af)
+{
+ struct node_host *n;
+
+ if (h->addr.type != PF_ADDR_DYNIFTL)
+ return (NULL);
+
+ if ((n = calloc(1, sizeof(*n))) == NULL)
+ return (NULL);
+ bcopy(h, n, sizeof(*n));
+ n->ifname = NULL;
+ n->next = NULL;
+ n->tail = NULL;
+
+ /* fix up netmask */
+ if (af == AF_INET && unmask(&n->addr.v.a.mask) > 32)
+ set_ipmask(n, 32);
+
+ return (n);
+}
+
/* interface lookup routines */
static struct node_host *iftab;
@@ -1367,7 +1470,7 @@ ifa_add_groups_to_map(char *ifa_name)
ENTRY item;
ENTRY *ret_item;
int *answer;
-
+
item.key = ifg->ifgrq_group;
if (hsearch_r(item, FIND, &ret_item, &isgroup_map) == 0) {
struct ifgroupreq ifgr2;
@@ -1406,13 +1509,14 @@ ifa_load(void)
err(1, "getifaddrs");
for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
- if (!(ifa->ifa_addr->sa_family == AF_INET ||
+ if (ifa->ifa_addr == NULL ||
+ !(ifa->ifa_addr->sa_family == AF_INET ||
ifa->ifa_addr->sa_family == AF_INET6 ||
ifa->ifa_addr->sa_family == AF_LINK))
continue;
n = calloc(1, sizeof(struct node_host));
if (n == NULL)
- err(1, "address: calloc");
+ err(1, "%s: calloc", __func__);
n->af = ifa->ifa_addr->sa_family;
n->ifa_flags = ifa->ifa_flags;
#ifdef __KAME__
@@ -1431,43 +1535,28 @@ ifa_load(void)
}
#endif
n->ifindex = 0;
- if (n->af == AF_INET) {
- memcpy(&n->addr.v.a.addr, &((struct sockaddr_in *)
- ifa->ifa_addr)->sin_addr.s_addr,
- sizeof(struct in_addr));
- memcpy(&n->addr.v.a.mask, &((struct sockaddr_in *)
- ifa->ifa_netmask)->sin_addr.s_addr,
- sizeof(struct in_addr));
- if (ifa->ifa_broadaddr != NULL)
- memcpy(&n->bcast, &((struct sockaddr_in *)
- ifa->ifa_broadaddr)->sin_addr.s_addr,
- sizeof(struct in_addr));
- if (ifa->ifa_dstaddr != NULL)
- memcpy(&n->peer, &((struct sockaddr_in *)
- ifa->ifa_dstaddr)->sin_addr.s_addr,
- sizeof(struct in_addr));
- } else if (n->af == AF_INET6) {
- memcpy(&n->addr.v.a.addr, &((struct sockaddr_in6 *)
- ifa->ifa_addr)->sin6_addr.s6_addr,
- sizeof(struct in6_addr));
- memcpy(&n->addr.v.a.mask, &((struct sockaddr_in6 *)
- ifa->ifa_netmask)->sin6_addr.s6_addr,
- sizeof(struct in6_addr));
- if (ifa->ifa_broadaddr != NULL)
- memcpy(&n->bcast, &((struct sockaddr_in6 *)
- ifa->ifa_broadaddr)->sin6_addr.s6_addr,
- sizeof(struct in6_addr));
- if (ifa->ifa_dstaddr != NULL)
- memcpy(&n->peer, &((struct sockaddr_in6 *)
- ifa->ifa_dstaddr)->sin6_addr.s6_addr,
- sizeof(struct in6_addr));
- n->ifindex = ((struct sockaddr_in6 *)
- ifa->ifa_addr)->sin6_scope_id;
- } else if (n->af == AF_LINK) {
+ if (n->af == AF_LINK) {
+ n->ifindex = ((struct sockaddr_dl *)
+ ifa->ifa_addr)->sdl_index;
ifa_add_groups_to_map(ifa->ifa_name);
+ } else {
+ copy_satopfaddr(&n->addr.v.a.addr, ifa->ifa_addr);
+ ifa->ifa_netmask->sa_family = ifa->ifa_addr->sa_family;
+ copy_satopfaddr(&n->addr.v.a.mask, ifa->ifa_netmask);
+ if (ifa->ifa_broadaddr != NULL) {
+ ifa->ifa_broadaddr->sa_family = ifa->ifa_addr->sa_family;
+ copy_satopfaddr(&n->bcast, ifa->ifa_broadaddr);
+ }
+ if (ifa->ifa_dstaddr != NULL) {
+ ifa->ifa_dstaddr->sa_family = ifa->ifa_addr->sa_family;
+ copy_satopfaddr(&n->peer, ifa->ifa_dstaddr);
+ }
+ if (n->af == AF_INET6)
+ n->ifindex = ((struct sockaddr_in6 *)
+ ifa->ifa_addr) ->sin6_scope_id;
}
if ((n->ifname = strdup(ifa->ifa_name)) == NULL)
- err(1, "ifa_load: strdup");
+ err(1, "%s: strdup", __func__);
n->next = NULL;
n->tail = n;
if (h == NULL)
@@ -1523,7 +1612,7 @@ is_a_group(char *name)
{
ENTRY item;
ENTRY *ret_item;
-
+
item.key = name;
if (hsearch_r(item, FIND, &ret_item, &isgroup_map) == 0)
return (0);
@@ -1531,6 +1620,34 @@ is_a_group(char *name)
return (*(int *)ret_item->data);
}
+unsigned int
+ifa_nametoindex(const char *ifa_name)
+{
+ struct node_host *p;
+
+ for (p = iftab; p; p = p->next) {
+ if (p->af == AF_LINK && strcmp(p->ifname, ifa_name) == 0)
+ return (p->ifindex);
+ }
+ errno = ENXIO;
+ return (0);
+}
+
+char *
+ifa_indextoname(unsigned int ifindex, char *ifa_name)
+{
+ struct node_host *p;
+
+ for (p = iftab; p; p = p->next) {
+ if (p->af == AF_LINK && ifindex == p->ifindex) {
+ strlcpy(ifa_name, p->ifname, IFNAMSIZ);
+ return (ifa_name);
+ }
+ }
+ errno = ENXIO;
+ return (NULL);
+}
+
struct node_host *
ifa_exists(char *ifa_name)
{
@@ -1619,6 +1736,8 @@ ifa_lookup(char *ifa_name, int flags)
if ((flags & PFI_AFLAG_BROADCAST) &&
!(p->ifa_flags & IFF_BROADCAST))
continue;
+ if ((flags & PFI_AFLAG_BROADCAST) && p->bcast.v4.s_addr == 0)
+ continue;
if ((flags & PFI_AFLAG_PEER) &&
!(p->ifa_flags & IFF_POINTOPOINT))
continue;
@@ -1640,7 +1759,7 @@ ifa_lookup(char *ifa_name, int flags)
got6 = 1;
n = calloc(1, sizeof(struct node_host));
if (n == NULL)
- err(1, "address: calloc");
+ err(1, "%s: calloc", __func__);
n->af = p->af;
if (flags & PFI_AFLAG_BROADCAST)
memcpy(&n->addr.v.a.addr, &p->bcast,
@@ -1652,19 +1771,9 @@ ifa_lookup(char *ifa_name, int flags)
memcpy(&n->addr.v.a.addr, &p->addr.v.a.addr,
sizeof(struct pf_addr));
if (flags & PFI_AFLAG_NETWORK)
- set_ipmask(n, unmask(&p->addr.v.a.mask, n->af));
- else {
- if (n->af == AF_INET) {
- if (p->ifa_flags & IFF_LOOPBACK &&
- p->ifa_flags & IFF_LINK1)
- memcpy(&n->addr.v.a.mask,
- &p->addr.v.a.mask,
- sizeof(struct pf_addr));
- else
- set_ipmask(n, 32);
- } else
- set_ipmask(n, 128);
- }
+ set_ipmask(n, unmask(&p->addr.v.a.mask));
+ else
+ set_ipmask(n, -1);
n->ifindex = p->ifindex;
n->ifname = strdup(p->ifname);
@@ -1703,57 +1812,41 @@ ifa_skip_if(const char *filter, struct node_host *p)
struct node_host *
-host(const char *s)
+host(const char *s, int opts)
{
struct node_host *h = NULL;
- int mask, v4mask, v6mask, cont = 1;
- char *p, *q, *ps;
-
- if ((p = strrchr(s, '/')) != NULL) {
- mask = strtol(p+1, &q, 0);
- if (!q || *q || mask > 128 || q == (p+1)) {
- fprintf(stderr, "invalid netmask '%s'\n", p);
- return (NULL);
+ int mask = -1;
+ char *p, *ps;
+ const char *errstr;
+
+ if ((p = strchr(s, '/')) != NULL) {
+ mask = strtonum(p+1, 0, 128, &errstr);
+ if (errstr) {
+ fprintf(stderr, "netmask is %s: %s\n", errstr, p);
+ goto error;
}
if ((ps = malloc(strlen(s) - strlen(p) + 1)) == NULL)
- err(1, "host: malloc");
+ err(1, "%s: malloc", __func__);
strlcpy(ps, s, strlen(s) - strlen(p) + 1);
- v4mask = v6mask = mask;
} else {
if ((ps = strdup(s)) == NULL)
- err(1, "host: strdup");
- v4mask = 32;
- v6mask = 128;
- mask = -1;
+ err(1, "%s: strdup", __func__);
}
- /* IPv4 address? */
- if (cont && (h = host_v4(s, mask)) != NULL)
- cont = 0;
-
- /* IPv6 address? */
- if (cont && (h = host_v6(ps, v6mask)) != NULL)
- cont = 0;
-
- /* interface with this name exists? */
- /* expensive with thousands of interfaces - prioritze IPv4/6 check */
- if (cont && (h = host_if(ps, mask, &cont)) != NULL)
- cont = 0;
-
- /* dns lookup */
- if (cont && (h = host_dns(ps, v4mask, v6mask)) != NULL)
- cont = 0;
- free(ps);
-
- if (h == NULL || cont == 1) {
+ if ((h = host_ip(ps, mask)) == NULL &&
+ (h = host_if(ps, mask)) == NULL &&
+ (h = host_dns(ps, mask, (opts & PF_OPT_NODNS))) == NULL) {
fprintf(stderr, "no IP address found for %s\n", s);
- return (NULL);
+ goto error;
}
+
+error:
+ free(ps);
return (h);
}
struct node_host *
-host_if(const char *s, int mask, int *cont)
+host_if(const char *s, int mask)
{
struct node_host *n, *h = NULL;
char *p, *ps;
@@ -1770,101 +1863,79 @@ host_if(const char *s, int mask, int *cont)
flags |= PFI_AFLAG_PEER;
else if (!strcmp(p+1, "0"))
flags |= PFI_AFLAG_NOALIAS;
- else {
- free(ps);
- return (NULL);
- }
+ else
+ goto error;
*p = '\0';
- *cont = 0;
}
if (flags & (flags - 1) & PFI_AFLAG_MODEMASK) { /* Yep! */
fprintf(stderr, "illegal combination of interface modifiers\n");
- free(ps);
- return (NULL);
+ goto error;
}
if ((flags & (PFI_AFLAG_NETWORK|PFI_AFLAG_BROADCAST)) && mask > -1) {
fprintf(stderr, "network or broadcast lookup, but "
"extra netmask given\n");
- free(ps);
- return (NULL);
+ goto error;
}
if (ifa_exists(ps) || !strncmp(ps, "self", IFNAMSIZ)) {
/* interface with this name exists */
h = ifa_lookup(ps, flags);
- for (n = h; n != NULL && mask > -1; n = n->next)
- set_ipmask(n, mask);
+ if (mask > -1)
+ for (n = h; n != NULL; n = n->next)
+ set_ipmask(n, mask);
}
+error:
free(ps);
return (h);
}
struct node_host *
-host_v4(const char *s, int mask)
+host_ip(const char *s, int mask)
{
+ struct addrinfo hints, *res;
struct node_host *h = NULL;
- struct in_addr ina;
- int bits = 32;
- memset(&ina, 0, sizeof(struct in_addr));
- if (strrchr(s, '/') != NULL) {
- if ((bits = inet_net_pton(AF_INET, s, &ina, sizeof(ina))) == -1)
- return (NULL);
- } else {
- if (inet_pton(AF_INET, s, &ina) != 1)
- return (NULL);
- }
-
- h = calloc(1, sizeof(struct node_host));
+ h = calloc(1, sizeof(*h));
if (h == NULL)
- err(1, "address: calloc");
- h->ifname = NULL;
- h->af = AF_INET;
- h->addr.v.a.addr.addr32[0] = ina.s_addr;
- set_ipmask(h, bits);
- h->next = NULL;
- h->tail = h;
-
- return (h);
-}
-
-struct node_host *
-host_v6(const char *s, int mask)
-{
- struct addrinfo hints, *res;
- struct node_host *h = NULL;
+ err(1, "%s: calloc", __func__);
+ if (mask != -1) {
+ /* Try to parse 10/8 */
+ h->af = AF_INET;
+ if (inet_net_pton(AF_INET, s, &h->addr.v.a.addr.v4,
+ sizeof(h->addr.v.a.addr.v4)) != -1)
+ goto out;
+ }
memset(&hints, 0, sizeof(hints));
- hints.ai_family = AF_INET6;
+ hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM; /*dummy*/
hints.ai_flags = AI_NUMERICHOST;
- if (getaddrinfo(s, "0", &hints, &res) == 0) {
- h = calloc(1, sizeof(struct node_host));
- if (h == NULL)
- err(1, "address: calloc");
- h->ifname = NULL;
- h->af = AF_INET6;
- memcpy(&h->addr.v.a.addr,
- &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr,
- sizeof(h->addr.v.a.addr));
- h->ifindex =
- ((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id;
- set_ipmask(h, mask);
+ if (getaddrinfo(s, NULL, &hints, &res) == 0) {
+ h->af = res->ai_family;
+ copy_satopfaddr(&h->addr.v.a.addr, res->ai_addr);
+ if (h->af == AF_INET6)
+ h->ifindex =
+ ((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id;
freeaddrinfo(res);
- h->next = NULL;
- h->tail = h;
+ } else {
+ free(h);
+ return (NULL);
}
+out:
+ set_ipmask(h, mask);
+ h->ifname = NULL;
+ h->next = NULL;
+ h->tail = h;
return (h);
}
struct node_host *
-host_dns(const char *s, int v4mask, int v6mask)
+host_dns(const char *s, int mask, int numeric)
{
struct addrinfo hints, *res0, *res;
struct node_host *n, *h = NULL;
- int error, noalias = 0;
- int got4 = 0, got6 = 0;
+ int noalias = 0, got4 = 0, got6 = 0;
char *p, *ps;
if ((ps = strdup(s)) == NULL)
@@ -1876,11 +1947,10 @@ host_dns(const char *s, int v4mask, int v6mask)
memset(&hints, 0, sizeof(hints));
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM; /* DUMMY */
- error = getaddrinfo(ps, NULL, &hints, &res0);
- if (error) {
- free(ps);
- return (h);
- }
+ if (numeric)
+ hints.ai_flags = AI_NUMERICHOST;
+ if (getaddrinfo(ps, NULL, &hints, &res0) != 0)
+ goto error;
for (res = res0; res; res = res->ai_next) {
if (res->ai_family != AF_INET &&
@@ -1902,22 +1972,11 @@ host_dns(const char *s, int v4mask, int v6mask)
err(1, "host_dns: calloc");
n->ifname = NULL;
n->af = res->ai_family;
- if (res->ai_family == AF_INET) {
- memcpy(&n->addr.v.a.addr,
- &((struct sockaddr_in *)
- res->ai_addr)->sin_addr.s_addr,
- sizeof(struct in_addr));
- set_ipmask(n, v4mask);
- } else {
- memcpy(&n->addr.v.a.addr,
- &((struct sockaddr_in6 *)
- res->ai_addr)->sin6_addr.s6_addr,
- sizeof(struct in6_addr));
+ copy_satopfaddr(&n->addr.v.a.addr, res->ai_addr);
+ if (res->ai_family == AF_INET6)
n->ifindex =
- ((struct sockaddr_in6 *)
- res->ai_addr)->sin6_scope_id;
- set_ipmask(n, v6mask);
- }
+ ((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id;
+ set_ipmask(n, mask);
n->next = NULL;
n->tail = n;
if (h == NULL)
@@ -1928,6 +1987,7 @@ host_dns(const char *s, int v4mask, int v6mask)
}
}
freeaddrinfo(res0);
+error:
free(ps);
return (h);
@@ -1939,7 +1999,7 @@ host_dns(const char *s, int v4mask, int v6mask)
* if set to 1, only simple addresses are accepted (no netblock, no "!").
*/
int
-append_addr(struct pfr_buffer *b, char *s, int test)
+append_addr(struct pfr_buffer *b, char *s, int test, int opts)
{
char *r;
struct node_host *h, *n;
@@ -1947,7 +2007,7 @@ append_addr(struct pfr_buffer *b, char *s, int test)
for (r = s; *r == '!'; r++)
not = !not;
- if ((n = host(r)) == NULL) {
+ if ((n = host(r, opts)) == NULL) {
errno = 0;
return (-1);
}
@@ -1976,7 +2036,7 @@ append_addr_host(struct pfr_buffer *b, struct node_host *n, int test, int not)
bzero(&addr, sizeof(addr));
addr.pfra_not = n->not ^ not;
addr.pfra_af = n->af;
- addr.pfra_net = unmask(&n->addr.v.a.mask, n->af);
+ addr.pfra_net = unmask(&n->addr.v.a.mask);
switch (n->af) {
case AF_INET:
addr.pfra_ip4addr.s_addr = n->addr.v.a.addr.addr32[0];
diff --git a/sbin/pfctl/pfctl_parser.h b/sbin/pfctl/pfctl_parser.h
index 83a4880106a8..44ddfb45fbe1 100644
--- a/sbin/pfctl/pfctl_parser.h
+++ b/sbin/pfctl/pfctl_parser.h
@@ -36,26 +36,29 @@
#include <libpfctl.h>
+#include <pfctl.h>
+
#define PF_OSFP_FILE "/etc/pf.os"
-#define PF_OPT_DISABLE 0x0001
-#define PF_OPT_ENABLE 0x0002
-#define PF_OPT_VERBOSE 0x0004
-#define PF_OPT_NOACTION 0x0008
-#define PF_OPT_QUIET 0x0010
-#define PF_OPT_CLRRULECTRS 0x0020
-#define PF_OPT_USEDNS 0x0040
-#define PF_OPT_VERBOSE2 0x0080
-#define PF_OPT_DUMMYACTION 0x0100
-#define PF_OPT_DEBUG 0x0200
-#define PF_OPT_SHOWALL 0x0400
-#define PF_OPT_OPTIMIZE 0x0800
-#define PF_OPT_NUMERIC 0x1000
-#define PF_OPT_MERGE 0x2000
-#define PF_OPT_RECURSE 0x4000
-#define PF_OPT_KILLMATCH 0x8000
-
-#define PF_TH_ALL 0xFF
+#define PF_OPT_DISABLE 0x00001
+#define PF_OPT_ENABLE 0x00002
+#define PF_OPT_VERBOSE 0x00004
+#define PF_OPT_NOACTION 0x00008
+#define PF_OPT_QUIET 0x00010
+#define PF_OPT_CLRRULECTRS 0x00020
+#define PF_OPT_USEDNS 0x00040
+#define PF_OPT_VERBOSE2 0x00080
+#define PF_OPT_DUMMYACTION 0x00100
+#define PF_OPT_DEBUG 0x00200
+#define PF_OPT_SHOWALL 0x00400
+#define PF_OPT_OPTIMIZE 0x00800
+#define PF_OPT_NUMERIC 0x01000
+#define PF_OPT_MERGE 0x02000
+#define PF_OPT_RECURSE 0x04000
+#define PF_OPT_KILLMATCH 0x08000
+#define PF_OPT_NODNS 0x10000
+#define PF_OPT_IGNFAIL 0x20000
+#define PF_OPT_CALLSHOW 0x40000
#define PF_NAT_PROXY_PORT_LOW 50001
#define PF_NAT_PROXY_PORT_HIGH 65535
@@ -75,6 +78,7 @@ struct pfr_buffer; /* forward definition */
struct pfctl {
int dev;
+ struct pfctl_handle *h;
int opts;
int optimize;
int loadopt;
@@ -88,6 +92,7 @@ struct pfctl {
struct pfioc_queue *pqueue;
struct pfr_buffer *trans;
struct pfctl_anchor *anchor, *alast;
+ struct pfr_ktablehead pfr_ktlast;
int eth_nr;
struct pfctl_eth_anchor *eanchor, *ealast;
struct pfctl_eth_anchor *eastack[PFCTL_ANCHOR_STACK_DEPTH];
@@ -136,6 +141,8 @@ struct node_host {
struct node_host *tail;
};
+void freehostlist(struct node_host *);
+
struct node_mac {
u_int8_t mac[ETHER_ADDR_LEN];
u_int8_t mask[ETHER_ADDR_LEN];
@@ -256,10 +263,10 @@ struct pf_opt_tbl {
char pt_name[PF_TABLE_NAME_SIZE];
int pt_rulecount;
int pt_generated;
+ uint32_t pt_refcnt;
struct node_tinithead pt_nodes;
struct pfr_buffer *pt_buf;
};
-#define PF_OPT_TABLE_PREFIX "__automatic_"
/* optimizer pf_rule container */
struct pf_opt_rule {
@@ -273,33 +280,38 @@ struct pf_opt_rule {
TAILQ_HEAD(pf_opt_queue, pf_opt_rule);
+struct pfr_uktable;
+
+void copy_satopfaddr(struct pf_addr *, struct sockaddr *);
+
int pfctl_rules(int, char *, int, int, char *, struct pfr_buffer *);
int pfctl_optimize_ruleset(struct pfctl *, struct pfctl_ruleset *);
-int pfctl_append_rule(struct pfctl *, struct pfctl_rule *, const char *);
+void pfctl_init_rule(struct pfctl_rule *r);
+void pfctl_append_rule(struct pfctl *, struct pfctl_rule *);
int pfctl_append_eth_rule(struct pfctl *, struct pfctl_eth_rule *,
const char *);
int pfctl_add_altq(struct pfctl *, struct pf_altq *);
-int pfctl_add_pool(struct pfctl *, struct pfctl_pool *, sa_family_t);
+int pfctl_add_pool(struct pfctl *, struct pfctl_pool *, int);
void pfctl_move_pool(struct pfctl_pool *, struct pfctl_pool *);
void pfctl_clear_pool(struct pfctl_pool *);
-int pfctl_set_timeout(struct pfctl *, const char *, int, int);
+int pfctl_apply_timeout(struct pfctl *, const char *, int, int);
int pfctl_set_reassembly(struct pfctl *, int, int);
int pfctl_set_optimization(struct pfctl *, const char *);
-int pfctl_set_limit(struct pfctl *, const char *, unsigned int);
+int pfctl_apply_limit(struct pfctl *, const char *, unsigned int);
int pfctl_set_logif(struct pfctl *, char *);
-int pfctl_set_hostid(struct pfctl *, u_int32_t);
-int pfctl_set_debug(struct pfctl *, char *);
+void pfctl_set_hostid(struct pfctl *, u_int32_t);
+int pfctl_do_set_debug(struct pfctl *, char *);
int pfctl_set_interface_flags(struct pfctl *, char *, int, int);
int pfctl_cfg_syncookies(struct pfctl *, uint8_t, struct pfctl_watermarks *);
int parse_config(char *, struct pfctl *);
int parse_flags(char *);
-int pfctl_load_anchors(int, struct pfctl *, struct pfr_buffer *);
+int pfctl_load_anchors(int, struct pfctl *);
-void print_pool(struct pfctl_pool *, u_int16_t, u_int16_t, sa_family_t, int);
-void print_src_node(struct pf_src_node *, int);
+void print_pool(struct pfctl_pool *, u_int16_t, u_int16_t, int);
+void print_src_node(struct pfctl_src_node *, int);
void print_eth_rule(struct pfctl_eth_rule *, const char *, int);
void print_rule(struct pfctl_rule *, const char *, int, int);
void print_tabledef(const char *, int, int, struct node_tinithead *);
@@ -317,7 +329,7 @@ void print_queue(const struct pf_altq *, unsigned, struct node_queue_bw *,
int, struct node_queue_opt *);
int pfctl_define_table(char *, int, int, const char *, struct pfr_buffer *,
- u_int32_t);
+ u_int32_t, struct pfr_uktable *);
void pfctl_clear_fingerprints(int, int);
int pfctl_file_fingerprints(int, int, const char *);
@@ -357,18 +369,24 @@ struct pf_timeout {
extern const struct pf_timeout pf_timeouts[];
-void set_ipmask(struct node_host *, u_int8_t);
+void set_ipmask(struct node_host *, int);
int check_netmask(struct node_host *, sa_family_t);
-int unmask(struct pf_addr *, sa_family_t);
+int unmask(struct pf_addr *);
+struct node_host *gen_dynnode(struct node_host *, sa_family_t);
void ifa_load(void);
+unsigned int ifa_nametoindex(const char *);
+char *ifa_indextoname(unsigned int, char *);
int get_query_socket(void);
struct node_host *ifa_exists(char *);
struct node_host *ifa_grouplookup(char *ifa_name, int flags);
struct node_host *ifa_lookup(char *, int);
-struct node_host *host(const char *);
+struct node_host *host(const char *, int);
-int append_addr(struct pfr_buffer *, char *, int);
+int append_addr(struct pfr_buffer *, char *, int, int);
int append_addr_host(struct pfr_buffer *,
struct node_host *, int, int);
+int pfr_ktable_compare(struct pfr_ktable *,
+ struct pfr_ktable *);
+RB_PROTOTYPE(pfr_ktablehead, pfr_ktable, pfrkt_tree, pfr_ktable_compare);
#endif /* _PFCTL_PARSER_H_ */
diff --git a/sbin/pfctl/pfctl_radix.c b/sbin/pfctl/pfctl_radix.c
index d33f091d8b69..98f907738d95 100644
--- a/sbin/pfctl/pfctl_radix.c
+++ b/sbin/pfctl/pfctl_radix.c
@@ -32,7 +32,6 @@
*
*/
-#include <sys/cdefs.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
@@ -49,6 +48,7 @@
#include <err.h>
#include "pfctl.h"
+#include "pfctl_parser.h"
#define BUF_SIZE 256
@@ -56,6 +56,19 @@ extern int dev;
static int pfr_next_token(char buf[BUF_SIZE], FILE *);
+struct pfr_ktablehead pfr_ktables = { 0 };
+RB_GENERATE(pfr_ktablehead, pfr_ktable, pfrkt_tree, pfr_ktable_compare);
+
+int
+pfr_ktable_compare(struct pfr_ktable *p, struct pfr_ktable *q)
+{
+ int d;
+
+ if ((d = strncmp(p->pfrkt_name, q->pfrkt_name, PF_TABLE_NAME_SIZE)))
+ return (d);
+ return (strcmp(p->pfrkt_anchor, q->pfrkt_anchor));
+}
+
static void
pfr_report_error(struct pfr_table *tbl, struct pfioc_table *io,
const char *err)
@@ -75,65 +88,15 @@ pfr_report_error(struct pfr_table *tbl, struct pfioc_table *io,
}
int
-pfr_clr_tables(struct pfr_table *filter, int *ndel, int flags)
+pfr_add_table(struct pfr_table *tbl, int *nadd, int flags)
{
- struct pfioc_table io;
-
- bzero(&io, sizeof io);
- io.pfrio_flags = flags;
- if (filter != NULL)
- io.pfrio_table = *filter;
- if (ioctl(dev, DIOCRCLRTABLES, &io))
- return (-1);
- if (ndel != NULL)
- *ndel = io.pfrio_ndel;
- return (0);
+ return (pfctl_add_table(pfh, tbl, nadd, flags));
}
int
-pfr_add_tables(struct pfr_table *tbl, int size, int *nadd, int flags)
+pfr_del_table(struct pfr_table *tbl, int *ndel, int flags)
{
- struct pfioc_table io;
-
- if (size < 0 || (size && tbl == NULL)) {
- errno = EINVAL;
- return (-1);
- }
- bzero(&io, sizeof io);
- io.pfrio_flags = flags;
- io.pfrio_buffer = tbl;
- io.pfrio_esize = sizeof(*tbl);
- io.pfrio_size = size;
- if (ioctl(dev, DIOCRADDTABLES, &io)) {
- pfr_report_error(tbl, &io, "add table");
- return (-1);
- }
- if (nadd != NULL)
- *nadd = io.pfrio_nadd;
- return (0);
-}
-
-int
-pfr_del_tables(struct pfr_table *tbl, int size, int *ndel, int flags)
-{
- struct pfioc_table io;
-
- if (size < 0 || (size && tbl == NULL)) {
- errno = EINVAL;
- return (-1);
- }
- bzero(&io, sizeof io);
- io.pfrio_flags = flags;
- io.pfrio_buffer = tbl;
- io.pfrio_esize = sizeof(*tbl);
- io.pfrio_size = size;
- if (ioctl(dev, DIOCRDELTABLES, &io)) {
- pfr_report_error(tbl, &io, "delete table");
- return (-1);
- }
- if (ndel != NULL)
- *ndel = io.pfrio_ndel;
- return (0);
+ return (pfctl_del_table(pfh, tbl, ndel, flags));
}
int
@@ -162,47 +125,9 @@ pfr_get_tables(struct pfr_table *filter, struct pfr_table *tbl, int *size,
}
int
-pfr_get_tstats(struct pfr_table *filter, struct pfr_tstats *tbl, int *size,
- int flags)
-{
- struct pfioc_table io;
-
- if (size == NULL || *size < 0 || (*size && tbl == NULL)) {
- errno = EINVAL;
- return (-1);
- }
- bzero(&io, sizeof io);
- io.pfrio_flags = flags;
- if (filter != NULL)
- io.pfrio_table = *filter;
- io.pfrio_buffer = tbl;
- io.pfrio_esize = sizeof(*tbl);
- io.pfrio_size = *size;
- if (ioctl(dev, DIOCRGETTSTATS, &io)) {
- pfr_report_error(filter, &io, "get tstats for");
- return (-1);
- }
- *size = io.pfrio_size;
- return (0);
-}
-
-int
pfr_clr_addrs(struct pfr_table *tbl, int *ndel, int flags)
{
- struct pfioc_table io;
-
- if (tbl == NULL) {
- errno = EINVAL;
- return (-1);
- }
- bzero(&io, sizeof io);
- io.pfrio_flags = flags;
- io.pfrio_table = *tbl;
- if (ioctl(dev, DIOCRCLRADDRS, &io))
- return (-1);
- if (ndel != NULL)
- *ndel = io.pfrio_ndel;
- return (0);
+ return (pfctl_clear_addrs(pfh, tbl, ndel, flags));
}
int
@@ -211,7 +136,10 @@ pfr_add_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size,
{
int ret;
- ret = pfctl_table_add_addrs(dev, tbl, addr, size, nadd, flags);
+ if (*nadd)
+ *nadd = 0;
+
+ ret = pfctl_table_add_addrs_h(pfh, tbl, addr, size, nadd, flags);
if (ret) {
errno = ret;
return (-1);
@@ -225,7 +153,7 @@ pfr_del_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size,
{
int ret;
- ret = pfctl_table_del_addrs(dev, tbl, addr, size, ndel, flags);
+ ret = pfctl_table_del_addrs_h(pfh, tbl, addr, size, ndel, flags);
if (ret) {
errno = ret;
return (-1);
@@ -288,23 +216,23 @@ pfr_get_astats(struct pfr_table *tbl, struct pfr_astats *addr, int *size,
}
int
-pfr_clr_tstats(struct pfr_table *tbl, int size, int *nzero, int flags)
+pfr_clr_astats(struct pfr_table *tbl, struct pfr_addr *addr, int size,
+ int *nzero, int flags)
{
struct pfioc_table io;
- if (size < 0 || (size && !tbl)) {
+ if (size < 0 || !tbl || (size && !addr)) {
errno = EINVAL;
return (-1);
}
bzero(&io, sizeof io);
io.pfrio_flags = flags;
- io.pfrio_buffer = tbl;
- io.pfrio_esize = sizeof(*tbl);
+ io.pfrio_table = *tbl;
+ io.pfrio_buffer = addr;
+ io.pfrio_esize = sizeof(*addr);
io.pfrio_size = size;
- if (ioctl(dev, DIOCRCLRTSTATS, &io)) {
- pfr_report_error(tbl, &io, "clear tstats from");
+ if (ioctl(dev, DIOCRCLRASTATS, &io) == -1)
return (-1);
- }
if (nzero)
*nzero = io.pfrio_nzero;
return (0);
@@ -342,6 +270,7 @@ pfr_ina_define(struct pfr_table *tbl, struct pfr_addr *addr, int size,
struct pfioc_table io;
if (tbl == NULL || size < 0 || (size && addr == NULL)) {
+ DBGPRINT("%s %p %d %p\n", __func__, tbl, size, addr);
errno = EINVAL;
return (-1);
}
@@ -462,25 +391,15 @@ pfr_buf_grow(struct pfr_buffer *b, int minsize)
if (!b->pfrb_msize) {
if (minsize < 64)
minsize = 64;
- b->pfrb_caddr = calloc(bs, minsize);
- if (b->pfrb_caddr == NULL)
- return (-1);
- b->pfrb_msize = minsize;
- } else {
- if (minsize == 0)
- minsize = b->pfrb_msize * 2;
- if (minsize < 0 || minsize >= SIZE_T_MAX / bs) {
- /* msize overflow */
- errno = ENOMEM;
- return (-1);
- }
- p = realloc(b->pfrb_caddr, minsize * bs);
- if (p == NULL)
- return (-1);
- bzero(p + b->pfrb_msize * bs, (minsize - b->pfrb_msize) * bs);
- b->pfrb_caddr = p;
- b->pfrb_msize = minsize;
}
+ if (minsize == 0)
+ minsize = b->pfrb_msize * 2;
+ p = reallocarray(b->pfrb_caddr, minsize, bs);
+ if (p == NULL)
+ return (-1);
+ bzero(p + b->pfrb_msize * bs, (minsize - b->pfrb_msize) * bs);
+ b->pfrb_caddr = p;
+ b->pfrb_msize = minsize;
return (0);
}
@@ -492,15 +411,14 @@ pfr_buf_clear(struct pfr_buffer *b)
{
if (b == NULL)
return;
- if (b->pfrb_caddr != NULL)
- free(b->pfrb_caddr);
+ free(b->pfrb_caddr);
b->pfrb_caddr = NULL;
b->pfrb_size = b->pfrb_msize = 0;
}
int
pfr_buf_load(struct pfr_buffer *b, char *file, int nonetwork,
- int (*append_addr)(struct pfr_buffer *, char *, int))
+ int (*append_addr)(struct pfr_buffer *, char *, int, int), int opts)
{
FILE *fp;
char buf[BUF_SIZE];
@@ -516,7 +434,7 @@ pfr_buf_load(struct pfr_buffer *b, char *file, int nonetwork,
return (-1);
}
while ((rv = pfr_next_token(buf, fp)) == 1)
- if (append_addr(b, buf, nonetwork)) {
+ if (append_addr(b, buf, nonetwork, opts)) {
rv = -1;
break;
}
@@ -535,8 +453,8 @@ pfr_next_token(char buf[BUF_SIZE], FILE *fp)
/* skip spaces */
while (isspace(next_ch) && !feof(fp))
next_ch = fgetc(fp);
- /* remove from '#' until end of line */
- if (next_ch == '#')
+ /* remove from '#' or ';' until end of line */
+ if (next_ch == '#' || next_ch == ';')
while (!feof(fp)) {
next_ch = fgetc(fp);
if (next_ch == '\n')
@@ -561,16 +479,3 @@ pfr_next_token(char buf[BUF_SIZE], FILE *fp)
buf[i] = '\0';
return (1);
}
-
-char *
-pfr_strerror(int errnum)
-{
- switch (errnum) {
- case ESRCH:
- return "Table does not exist";
- case ENOENT:
- return "Anchor or Ruleset does not exist";
- default:
- return strerror(errnum);
- }
-}
diff --git a/sbin/pfctl/pfctl_table.c b/sbin/pfctl/pfctl_table.c
index fe934a8d2ea2..4955e1791fd7 100644
--- a/sbin/pfctl/pfctl_table.c
+++ b/sbin/pfctl/pfctl_table.c
@@ -32,14 +32,14 @@
*
*/
-#include <sys/cdefs.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
#include <net/if.h>
#include <net/pfvar.h>
-#include <arpa/inet.h>
#include <ctype.h>
#include <err.h>
@@ -55,14 +55,12 @@
#include "pfctl.h"
extern void usage(void);
-static int pfctl_table(int, char *[], char *, const char *, char *,
- const char *, int);
-static void print_table(struct pfr_table *, int, int);
-static void print_tstats(struct pfr_tstats *, int);
-static int load_addr(struct pfr_buffer *, int, char *[], char *, int);
+static void print_table(const struct pfr_table *, int, int);
+static int print_tstats(const struct pfr_tstats *, int);
+static int load_addr(struct pfr_buffer *, int, char *[], char *, int, int);
static void print_addrx(struct pfr_addr *, struct pfr_addr *, int);
+static int nonzero_astats(struct pfr_astats *);
static void print_astats(struct pfr_astats *, int);
-static void radix_perror(void);
static void xprintf(int, const char *, ...);
static void print_iface(struct pfi_kif *, int);
@@ -76,26 +74,28 @@ static const char *istats_text[2][2][2] = {
{ { "In6/Pass:", "In6/Block:" }, { "Out6/Pass:", "Out6/Block:" } }
};
-#define RVTEST(fct) do { \
- if ((!(opts & PF_OPT_NOACTION) || \
- (opts & PF_OPT_DUMMYACTION)) && \
- (fct)) { \
- radix_perror(); \
- goto _error; \
- } \
+#define RVTEST(fct) do { \
+ if ((!(opts & PF_OPT_NOACTION) || \
+ (opts & PF_OPT_DUMMYACTION)) && \
+ (fct)) { \
+ if ((opts & PF_OPT_RECURSE) == 0) \
+ warnx("%s", pf_strerror(errno)); \
+ goto _error; \
+ } \
} while (0)
#define CREATE_TABLE do { \
+ warn_duplicate_tables(table.pfrt_name, \
+ table.pfrt_anchor); \
table.pfrt_flags |= PFR_TFLAG_PERSIST; \
if ((!(opts & PF_OPT_NOACTION) || \
(opts & PF_OPT_DUMMYACTION)) && \
- (pfr_add_tables(&table, 1, &nadd, flags)) && \
+ (pfr_add_table(&table, &nadd, flags)) && \
(errno != EPERM)) { \
- radix_perror(); \
+ warnx("%s", pf_strerror(errno)); \
goto _error; \
} \
if (nadd) { \
- warn_namespace_collision(table.pfrt_name); \
xprintf(opts, "%d table created", nadd); \
if (opts & PF_OPT_NOACTION) \
return (0); \
@@ -104,24 +104,23 @@ static const char *istats_text[2][2][2] = {
} while(0)
int
-pfctl_clear_tables(const char *anchor, int opts)
+pfctl_do_clear_tables(const char *anchor, int opts)
{
- return pfctl_table(0, NULL, NULL, "-F", NULL, anchor, opts);
-}
+ int rv;
-int
-pfctl_show_tables(const char *anchor, int opts)
-{
- return pfctl_table(0, NULL, NULL, "-s", NULL, anchor, opts);
+ if ((rv = pfctl_table(0, NULL, NULL, "-F", NULL, anchor, opts)) == -1) {
+ if ((opts & PF_OPT_IGNFAIL) == 0)
+ exit(1);
+ }
+
+ return (rv);
}
-int
-pfctl_command_tables(int argc, char *argv[], char *tname,
- const char *command, char *file, const char *anchor, int opts)
+void
+pfctl_show_tables(const char *anchor, int opts)
{
- if (tname == NULL || command == NULL)
- usage();
- return pfctl_table(argc, argv, tname, command, file, anchor, opts);
+ if (pfctl_table(0, NULL, NULL, "-s", NULL, anchor, opts))
+ exit(1);
}
int
@@ -157,39 +156,42 @@ pfctl_table(int argc, char *argv[], char *tname, const char *command,
if (!strcmp(command, "-F")) {
if (argc || file != NULL)
usage();
- RVTEST(pfr_clr_tables(&table, &ndel, flags));
+ RVTEST(pfctl_clear_tables(pfh, &table, &ndel, flags));
xprintf(opts, "%d tables deleted", ndel);
} else if (!strcmp(command, "-s")) {
b.pfrb_type = (opts & PF_OPT_VERBOSE2) ?
PFRB_TSTATS : PFRB_TABLES;
if (argc || file != NULL)
usage();
- for (;;) {
- pfr_buf_grow(&b, b.pfrb_size);
- b.pfrb_size = b.pfrb_msize;
- if (opts & PF_OPT_VERBOSE2)
- RVTEST(pfr_get_tstats(&table,
- b.pfrb_caddr, &b.pfrb_size, flags));
- else
- RVTEST(pfr_get_tables(&table,
- b.pfrb_caddr, &b.pfrb_size, flags));
- if (b.pfrb_size <= b.pfrb_msize)
- break;
- }
if ((opts & PF_OPT_SHOWALL) && b.pfrb_size > 0)
pfctl_print_title("TABLES:");
- PFRB_FOREACH(p, &b)
- if (opts & PF_OPT_VERBOSE2)
- print_tstats(p, opts & PF_OPT_DEBUG);
- else
+ if (opts & PF_OPT_VERBOSE2) {
+ uintptr_t arg = opts & PF_OPT_DEBUG;
+ pfctl_get_tstats(pfh, &table,
+ (pfctl_get_tstats_fn)print_tstats, (void *)arg);
+ } else {
+ for (;;) {
+ pfr_buf_grow(&b, b.pfrb_size);
+ b.pfrb_size = b.pfrb_msize;
+ RVTEST(pfr_get_tables(&table,
+ b.pfrb_caddr, &b.pfrb_size, flags));
+ if (b.pfrb_size <= b.pfrb_msize)
+ break;
+ }
+
+ if ((opts & PF_OPT_SHOWALL) && b.pfrb_size > 0)
+ pfctl_print_title("TABLES:");
+
+ PFRB_FOREACH(p, &b)
print_table(p, opts & PF_OPT_VERBOSE,
opts & PF_OPT_DEBUG);
+ }
} else if (!strcmp(command, "kill")) {
if (argc || file != NULL)
usage();
- RVTEST(pfr_del_tables(&table, 1, &ndel, flags));
+ RVTEST(pfr_del_table(&table, &ndel, flags));
xprintf(opts, "%d table deleted", ndel);
} else if (!strcmp(command, "flush")) {
if (argc || file != NULL)
@@ -198,7 +200,7 @@ pfctl_table(int argc, char *argv[], char *tname, const char *command,
xprintf(opts, "%d addresses deleted", ndel);
} else if (!strcmp(command, "add")) {
b.pfrb_type = PFRB_ADDRS;
- if (load_addr(&b, argc, argv, file, 0))
+ if (load_addr(&b, argc, argv, file, 0, opts))
goto _error;
CREATE_TABLE;
if (opts & PF_OPT_VERBOSE)
@@ -208,12 +210,13 @@ pfctl_table(int argc, char *argv[], char *tname, const char *command,
xprintf(opts, "%d/%d addresses added", nadd, b.pfrb_size);
if (opts & PF_OPT_VERBOSE)
PFRB_FOREACH(a, &b)
- if ((opts & PF_OPT_VERBOSE2) || a->pfra_fback)
+ if ((opts & PF_OPT_VERBOSE2) ||
+ a->pfra_fback != PFR_FB_NONE)
print_addrx(a, NULL,
opts & PF_OPT_USEDNS);
} else if (!strcmp(command, "delete")) {
b.pfrb_type = PFRB_ADDRS;
- if (load_addr(&b, argc, argv, file, 0))
+ if (load_addr(&b, argc, argv, file, 0, opts))
goto _error;
if (opts & PF_OPT_VERBOSE)
flags |= PFR_FLAG_FEEDBACK;
@@ -222,12 +225,13 @@ pfctl_table(int argc, char *argv[], char *tname, const char *command,
xprintf(opts, "%d/%d addresses deleted", ndel, b.pfrb_size);
if (opts & PF_OPT_VERBOSE)
PFRB_FOREACH(a, &b)
- if ((opts & PF_OPT_VERBOSE2) || a->pfra_fback)
+ if ((opts & PF_OPT_VERBOSE2) ||
+ a->pfra_fback != PFR_FB_NONE)
print_addrx(a, NULL,
opts & PF_OPT_USEDNS);
} else if (!strcmp(command, "replace")) {
b.pfrb_type = PFRB_ADDRS;
- if (load_addr(&b, argc, argv, file, 0))
+ if (load_addr(&b, argc, argv, file, 0, opts))
goto _error;
CREATE_TABLE;
if (opts & PF_OPT_VERBOSE)
@@ -253,7 +257,8 @@ pfctl_table(int argc, char *argv[], char *tname, const char *command,
xprintf(opts, "no changes");
if (opts & PF_OPT_VERBOSE)
PFRB_FOREACH(a, &b)
- if ((opts & PF_OPT_VERBOSE2) || a->pfra_fback)
+ if ((opts & PF_OPT_VERBOSE2) ||
+ a->pfra_fback != PFR_FB_NONE)
print_addrx(a, NULL,
opts & PF_OPT_USEDNS);
} else if (!strcmp(command, "expire")) {
@@ -276,7 +281,7 @@ pfctl_table(int argc, char *argv[], char *tname, const char *command,
break;
}
PFRB_FOREACH(p, &b) {
- ((struct pfr_astats *)p)->pfras_a.pfra_fback = 0;
+ ((struct pfr_astats *)p)->pfras_a.pfra_fback = PFR_FB_NONE;
if (time(NULL) - ((struct pfr_astats *)p)->pfras_tzero >
lifetime)
if (pfr_buf_add(&b2,
@@ -291,6 +296,37 @@ pfctl_table(int argc, char *argv[], char *tname, const char *command,
xprintf(opts, "%d/%d addresses expired", ndel, b2.pfrb_size);
if (opts & PF_OPT_VERBOSE)
PFRB_FOREACH(a, &b2)
+ if ((opts & PF_OPT_VERBOSE2) ||
+ a->pfra_fback != PFR_FB_NONE)
+ print_addrx(a, NULL,
+ opts & PF_OPT_USEDNS);
+ } else if (!strcmp(command, "reset")) {
+ struct pfr_astats *as;
+
+ b.pfrb_type = PFRB_ASTATS;
+ b2.pfrb_type = PFRB_ADDRS;
+ if (argc || file != NULL)
+ usage();
+ do {
+ pfr_buf_grow(&b, b.pfrb_size);
+ b.pfrb_size = b.pfrb_msize;
+ RVTEST(pfr_get_astats(&table, b.pfrb_caddr,
+ &b.pfrb_size, flags));
+ } while (b.pfrb_size > b.pfrb_msize);
+ PFRB_FOREACH(as, &b) {
+ as->pfras_a.pfra_fback = 0;
+ if (nonzero_astats(as))
+ if (pfr_buf_add(&b2, &as->pfras_a))
+ err(1, "duplicate buffer");
+ }
+
+ if (opts & PF_OPT_VERBOSE)
+ flags |= PFR_FLAG_FEEDBACK;
+ RVTEST(pfr_clr_astats(&table, b2.pfrb_caddr, b2.pfrb_size,
+ &nzero, flags));
+ xprintf(opts, "%d/%d stats cleared", nzero, b.pfrb_size);
+ if (opts & PF_OPT_VERBOSE)
+ PFRB_FOREACH(a, &b2)
if ((opts & PF_OPT_VERBOSE2) || a->pfra_fback)
print_addrx(a, NULL,
opts & PF_OPT_USEDNS);
@@ -320,7 +356,7 @@ pfctl_table(int argc, char *argv[], char *tname, const char *command,
b.pfrb_type = PFRB_ADDRS;
b2.pfrb_type = PFRB_ADDRS;
- if (load_addr(&b, argc, argv, file, 1))
+ if (load_addr(&b, argc, argv, file, 1, opts))
goto _error;
if (opts & PF_OPT_VERBOSE2) {
flags |= PFR_FLAG_REPLACE;
@@ -345,11 +381,24 @@ pfctl_table(int argc, char *argv[], char *tname, const char *command,
}
if (nmatch < b.pfrb_size)
rv = 2;
+ } else if (!strcmp(command, "zero") && (argc || file != NULL)) {
+ b.pfrb_type = PFRB_ADDRS;
+ if (load_addr(&b, argc, argv, file, 0, opts))
+ goto _error;
+ if (opts & PF_OPT_VERBOSE)
+ flags |= PFR_FLAG_FEEDBACK;
+ RVTEST(pfr_clr_astats(&table, b.pfrb_caddr, b.pfrb_size,
+ &nzero, flags));
+ xprintf(opts, "%d/%d addresses cleared", nzero, b.pfrb_size);
+ if (opts & PF_OPT_VERBOSE)
+ PFRB_FOREACH(a, &b)
+ if (opts & PF_OPT_VERBOSE2 ||
+ a->pfra_fback != PFR_FB_NONE)
+ print_addrx(a, NULL,
+ opts & PF_OPT_USEDNS);
} else if (!strcmp(command, "zero")) {
- if (argc || file != NULL)
- usage();
flags |= PFR_FLAG_ADDRSTOO;
- RVTEST(pfr_clr_tstats(&table, 1, &nzero, flags));
+ RVTEST(pfctl_clear_tstats(pfh, &table, &nzero, flags));
xprintf(opts, "%d table/stats cleared", nzero);
} else
warnx("pfctl_table: unknown command '%s'", command);
@@ -364,38 +413,43 @@ _cleanup:
}
void
-print_table(struct pfr_table *ta, int verbose, int debug)
+print_table(const struct pfr_table *ta, int verbose, int debug)
{
if (!debug && !(ta->pfrt_flags & PFR_TFLAG_ACTIVE))
return;
- if (verbose) {
- printf("%c%c%c%c%c%c%c\t%s",
+ if (verbose)
+ printf("%c%c%c%c%c%c%c\t",
(ta->pfrt_flags & PFR_TFLAG_CONST) ? 'c' : '-',
(ta->pfrt_flags & PFR_TFLAG_PERSIST) ? 'p' : '-',
(ta->pfrt_flags & PFR_TFLAG_ACTIVE) ? 'a' : '-',
(ta->pfrt_flags & PFR_TFLAG_INACTIVE) ? 'i' : '-',
(ta->pfrt_flags & PFR_TFLAG_REFERENCED) ? 'r' : '-',
(ta->pfrt_flags & PFR_TFLAG_REFDANCHOR) ? 'h' : '-',
- (ta->pfrt_flags & PFR_TFLAG_COUNTERS) ? 'C' : '-',
- ta->pfrt_name);
- if (ta->pfrt_anchor[0])
- printf("\t%s", ta->pfrt_anchor);
- puts("");
- } else
- puts(ta->pfrt_name);
+ (ta->pfrt_flags & PFR_TFLAG_COUNTERS) ? 'C' : '-');
+
+ printf("%s", ta->pfrt_name);
+ if (ta->pfrt_anchor[0] != '\0')
+ printf("@%s", ta->pfrt_anchor);
+
+ printf("\n");
}
-void
-print_tstats(struct pfr_tstats *ts, int debug)
+int
+print_tstats(const struct pfr_tstats *ts, int debug)
{
- time_t time = ts->pfrts_tzero;
- int dir, op;
+ time_t time = ts->pfrts_tzero;
+ int dir, op;
+ char *ct;
if (!debug && !(ts->pfrts_flags & PFR_TFLAG_ACTIVE))
- return;
+ return (0);
+ ct = ctime(&time);
print_table(&ts->pfrts_t, 1, debug);
printf("\tAddresses: %d\n", ts->pfrts_cnt);
- printf("\tCleared: %s", ctime(&time));
+ if (ct)
+ printf("\tCleared: %s", ct);
+ else
+ printf("\tCleared: %lld\n", (long long)time);
printf("\tReferences: [ Anchors: %-18d Rules: %-18d ]\n",
ts->pfrts_refcnt[PFR_REFCNT_ANCHOR],
ts->pfrts_refcnt[PFR_REFCNT_RULE]);
@@ -408,19 +462,21 @@ print_tstats(struct pfr_tstats *ts, int debug)
stats_text[dir][op],
(unsigned long long)ts->pfrts_packets[dir][op],
(unsigned long long)ts->pfrts_bytes[dir][op]);
+
+ return (0);
}
int
load_addr(struct pfr_buffer *b, int argc, char *argv[], char *file,
- int nonetwork)
+ int nonetwork, int opts)
{
while (argc--)
- if (append_addr(b, *argv++, nonetwork)) {
+ if (append_addr(b, *argv++, nonetwork, opts)) {
if (errno)
warn("cannot decode %s", argv[-1]);
return (-1);
}
- if (pfr_buf_load(b, file, nonetwork, append_addr)) {
+ if (pfr_buf_load(b, file, nonetwork, append_addr, opts)) {
warn("cannot load %s", file);
return (-1);
}
@@ -453,34 +509,56 @@ print_addrx(struct pfr_addr *ad, struct pfr_addr *rad, int dns)
printf("\t nomatch");
if (dns && ad->pfra_net == hostnet) {
char host[NI_MAXHOST];
- union sockaddr_union sa;
+ struct sockaddr_storage ss;
strlcpy(host, "?", sizeof(host));
- bzero(&sa, sizeof(sa));
- sa.sa.sa_family = ad->pfra_af;
- if (sa.sa.sa_family == AF_INET) {
- sa.sa.sa_len = sizeof(sa.sin);
- sa.sin.sin_addr = ad->pfra_ip4addr;
+ bzero(&ss, sizeof(ss));
+ ss.ss_family = ad->pfra_af;
+ if (ss.ss_family == AF_INET) {
+ struct sockaddr_in *sin = (struct sockaddr_in *)&ss;
+
+ sin->sin_len = sizeof(*sin);
+ sin->sin_addr = ad->pfra_ip4addr;
} else {
- sa.sa.sa_len = sizeof(sa.sin6);
- sa.sin6.sin6_addr = ad->pfra_ip6addr;
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&ss;
+
+ sin6->sin6_len = sizeof(*sin6);
+ sin6->sin6_addr = ad->pfra_ip6addr;
}
- if (getnameinfo(&sa.sa, sa.sa.sa_len, host, sizeof(host),
- NULL, 0, NI_NAMEREQD) == 0)
+ if (getnameinfo((struct sockaddr *)&ss, ss.ss_len, host,
+ sizeof(host), NULL, 0, NI_NAMEREQD) == 0)
printf("\t(%s)", host);
}
printf("\n");
}
+int
+nonzero_astats(struct pfr_astats *as)
+{
+ uint64_t s = 0;
+
+ for (int dir = 0; dir < PFR_DIR_MAX; dir++)
+ for (int op = 0; op < PFR_OP_ADDR_MAX; op++)
+ s |= as->pfras_packets[dir][op] |
+ as->pfras_bytes[dir][op];
+
+ return (!!s);
+}
+
void
print_astats(struct pfr_astats *as, int dns)
{
- time_t time = as->pfras_tzero;
- int dir, op;
+ time_t time = as->pfras_tzero;
+ int dir, op;
+ char *ct;
+ ct = ctime(&time);
print_addrx(&as->pfras_a, NULL, dns);
- printf("\tCleared: %s", ctime(&time));
- if (as->pfras_a.pfra_fback == PFR_FB_NOCOUNT)
+ if (ct)
+ printf("\tCleared: %s", ct);
+ else
+ printf("\tCleared: %lld\n", (long long)time);
+ if (as->pfras_a.pfra_fback == PFR_FB_NOCOUNT)
return;
for (dir = 0; dir < PFR_DIR_MAX; dir++)
for (op = 0; op < PFR_OP_ADDR_MAX; op++)
@@ -490,37 +568,59 @@ print_astats(struct pfr_astats *as, int dns)
(unsigned long long)as->pfras_bytes[dir][op]);
}
-void
-radix_perror(void)
-{
- extern char *__progname;
- fprintf(stderr, "%s: %s.\n", __progname, pfr_strerror(errno));
-}
-
int
pfctl_define_table(char *name, int flags, int addrs, const char *anchor,
- struct pfr_buffer *ab, u_int32_t ticket)
+ struct pfr_buffer *ab, u_int32_t ticket, struct pfr_uktable *ukt)
{
- struct pfr_table tbl;
-
- bzero(&tbl, sizeof(tbl));
- if (strlcpy(tbl.pfrt_name, name, sizeof(tbl.pfrt_name)) >=
- sizeof(tbl.pfrt_name) || strlcpy(tbl.pfrt_anchor, anchor,
- sizeof(tbl.pfrt_anchor)) >= sizeof(tbl.pfrt_anchor))
- errx(1, "pfctl_define_table: strlcpy");
- tbl.pfrt_flags = flags;
+ struct pfr_table tbl_buf;
+ struct pfr_table *tbl;
+
+ if (ukt == NULL) {
+ bzero(&tbl_buf, sizeof(tbl_buf));
+ tbl = &tbl_buf;
+ } else {
+ if (ab->pfrb_size != 0) {
+ /*
+ * copy IP addresses which come with table from
+ * temporal buffer to buffer attached to table.
+ */
+ ukt->pfrukt_addrs = *ab;
+ ab->pfrb_size = 0;
+ ab->pfrb_msize = 0;
+ ab->pfrb_caddr = NULL;
+ } else
+ memset(&ukt->pfrukt_addrs, 0,
+ sizeof(struct pfr_buffer));
+
+ tbl = &ukt->pfrukt_t;
+ }
- return pfr_ina_define(&tbl, ab->pfrb_caddr, ab->pfrb_size, NULL,
- NULL, ticket, addrs ? PFR_FLAG_ADDRSTOO : 0);
+ if (strlcpy(tbl->pfrt_name, name, sizeof(tbl->pfrt_name)) >=
+ sizeof(tbl->pfrt_name) ||
+ strlcpy(tbl->pfrt_anchor, anchor, sizeof(tbl->pfrt_anchor)) >=
+ sizeof(tbl->pfrt_anchor))
+ errx(1, "%s: strlcpy", __func__);
+ tbl->pfrt_flags = flags;
+ DBGPRINT("%s %s@%s [%x]\n", __func__, tbl->pfrt_name, tbl->pfrt_anchor,
+ tbl->pfrt_flags);
+
+ /*
+ * non-root anchors processed by parse.y are loaded to kernel later.
+ * Here we load tables, which are either created for root anchor
+ * or by 'pfctl -t ... -T ...' command.
+ */
+ if (ukt != NULL)
+ return (0);
+
+ return (pfr_ina_define(tbl, ab->pfrb_caddr, ab->pfrb_size, NULL, NULL,
+ ticket, addrs ? PFR_FLAG_ADDRSTOO : 0));
}
void
-warn_namespace_collision(const char *filter)
+warn_duplicate_tables(const char *tablename, const char *anchorname)
{
struct pfr_buffer b;
struct pfr_table *t;
- const char *name = NULL, *lastcoll;
- int coll = 0;
bzero(&b, sizeof(b));
b.pfrb_type = PFRB_TABLES;
@@ -536,22 +636,13 @@ warn_namespace_collision(const char *filter)
PFRB_FOREACH(t, &b) {
if (!(t->pfrt_flags & PFR_TFLAG_ACTIVE))
continue;
- if (filter != NULL && strcmp(filter, t->pfrt_name))
+ if (!strcmp(anchorname, t->pfrt_anchor))
continue;
- if (!t->pfrt_anchor[0])
- name = t->pfrt_name;
- else if (name != NULL && !strcmp(name, t->pfrt_name)) {
- coll++;
- lastcoll = name;
- name = NULL;
- }
+ if (!strcmp(tablename, t->pfrt_name))
+ warnx("warning: table <%s> already defined"
+ " in anchor \"%s\"", tablename,
+ t->pfrt_anchor[0] ? t->pfrt_anchor : "/");
}
- if (coll == 1)
- warnx("warning: namespace collision with <%s> global table.",
- lastcoll);
- else if (coll > 1)
- warnx("warning: namespace collisions with %d global tables.",
- coll);
pfr_buf_clear(&b);
}
@@ -578,7 +669,7 @@ xprintf(int opts, const char *fmt, ...)
/* interface stuff */
-int
+void
pfctl_show_ifaces(const char *filter, int opts)
{
struct pfr_buffer b;
@@ -589,10 +680,8 @@ pfctl_show_ifaces(const char *filter, int opts)
for (;;) {
pfr_buf_grow(&b, b.pfrb_size);
b.pfrb_size = b.pfrb_msize;
- if (pfi_get_ifaces(filter, b.pfrb_caddr, &b.pfrb_size)) {
- radix_perror();
- return (1);
- }
+ if (pfi_get_ifaces(filter, b.pfrb_caddr, &b.pfrb_size))
+ errx(1, "%s", pf_strerror(errno));
if (b.pfrb_size <= b.pfrb_msize)
break;
}
@@ -600,14 +689,14 @@ pfctl_show_ifaces(const char *filter, int opts)
pfctl_print_title("INTERFACES:");
PFRB_FOREACH(p, &b)
print_iface(p, opts);
- return (0);
}
void
print_iface(struct pfi_kif *p, int opts)
{
- time_t tzero = p->pfik_tzero;
- int i, af, dir, act;
+ time_t tzero = p->pfik_tzero;
+ int i, af, dir, act;
+ char *ct;
printf("%s", p->pfik_name);
if (opts & PF_OPT_VERBOSE) {
@@ -618,7 +707,11 @@ print_iface(struct pfi_kif *p, int opts)
if (!(opts & PF_OPT_VERBOSE2))
return;
- printf("\tCleared: %s", ctime(&tzero));
+ ct = ctime(&tzero);
+ if (ct)
+ printf("\tCleared: %s", ct);
+ else
+ printf("\tCleared: %lld\n", (long long)tzero);
printf("\tReferences: %-18d\n", p->pfik_rulerefs);
for (i = 0; i < 8; i++) {
af = (i>>2) & 1;
diff --git a/sbin/pfctl/tests/Makefile b/sbin/pfctl/tests/Makefile
index db41a445903f..281cd97dee78 100644
--- a/sbin/pfctl/tests/Makefile
+++ b/sbin/pfctl/tests/Makefile
@@ -1,4 +1,3 @@
-
PACKAGE= tests
ATF_TESTS_C= pfctl_test
@@ -8,4 +7,6 @@ LIBADD+= sbuf
SUBDIR+= files
WARNS=6
+pfctl_test.o: pfctl_test_list.inc
+
.include <bsd.test.mk>
diff --git a/sbin/pfctl/tests/Makefile.depend b/sbin/pfctl/tests/Makefile.depend
index 11aba52f82cf..a59dba5bf1aa 100644
--- a/sbin/pfctl/tests/Makefile.depend
+++ b/sbin/pfctl/tests/Makefile.depend
@@ -1,6 +1,11 @@
# Autogenerated - do NOT edit!
DIRDEPS = \
+ lib/${CSU_DIR} \
+ lib/atf/libatf-c \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/libsbuf \
.include <dirdeps.mk>
diff --git a/sbin/pfctl/tests/files/Makefile b/sbin/pfctl/tests/files/Makefile
index c676a42f62b8..fc52b1db3c30 100644
--- a/sbin/pfctl/tests/files/Makefile
+++ b/sbin/pfctl/tests/files/Makefile
@@ -1,10 +1,9 @@
-
PACKAGE= tests
TESTSDIR= ${TESTSBASE}/sbin/pfctl/files
BINDIR= ${TESTSDIR}
# We use ${.CURDIR} as workaround so that the glob patterns work.
-FILES!= echo ${.CURDIR}/pf????.in ${.CURDIR}/pf????.include ${.CURDIR}/pf????.ok
+FILES!= echo ${.CURDIR}/pf????.in ${.CURDIR}/pf????.include ${.CURDIR}/pf????.ok ${.CURDIR}/pf????.fail
.include <bsd.progs.mk>
diff --git a/sbin/pfctl/tests/files/pf0004.ok b/sbin/pfctl/tests/files/pf0004.ok
index 5fca4a50f7b1..87b71cdeff3d 100644
--- a/sbin/pfctl/tests/files/pf0004.ok
+++ b/sbin/pfctl/tests/files/pf0004.ok
@@ -15,48 +15,48 @@ block drop in proto tcp from any port >= 80 to any port 1024:2048
block drop in inet proto tcp from 10.0.0.0/8 port = ssh to 192.168.0.0/16 port = ircd
block drop in inet proto tcp from 10.0.0.0/8 port = ssh to 192.168.0.0/16 port = 6668
block drop in inet proto tcp from 10.0.0.0/8 port = ssh to 192.168.0.0/16 port 6669:65535
-block drop in inet proto tcp from 10.0.0.0/8 port = ssh to 12.34.56.78 port = ircd
-block drop in inet proto tcp from 10.0.0.0/8 port = ssh to 12.34.56.78 port = 6668
-block drop in inet proto tcp from 10.0.0.0/8 port = ssh to 12.34.56.78 port 6669:65535
block drop in inet proto tcp from 10.0.0.0/8 port = ftp to 192.168.0.0/16 port = ircd
block drop in inet proto tcp from 10.0.0.0/8 port = ftp to 192.168.0.0/16 port = 6668
block drop in inet proto tcp from 10.0.0.0/8 port = ftp to 192.168.0.0/16 port 6669:65535
+block drop in inet proto tcp from 10.0.0.0/8 port = ssh to 12.34.56.78 port = ircd
+block drop in inet proto tcp from 10.0.0.0/8 port = ssh to 12.34.56.78 port = 6668
+block drop in inet proto tcp from 10.0.0.0/8 port = ssh to 12.34.56.78 port 6669:65535
block drop in inet proto tcp from 10.0.0.0/8 port = ftp to 12.34.56.78 port = ircd
block drop in inet proto tcp from 10.0.0.0/8 port = ftp to 12.34.56.78 port = 6668
block drop in inet proto tcp from 10.0.0.0/8 port = ftp to 12.34.56.78 port 6669:65535
block drop in inet proto tcp from 172.16.0.0/12 port = ssh to 192.168.0.0/16 port = ircd
block drop in inet proto tcp from 172.16.0.0/12 port = ssh to 192.168.0.0/16 port = 6668
block drop in inet proto tcp from 172.16.0.0/12 port = ssh to 192.168.0.0/16 port 6669:65535
-block drop in inet proto tcp from 172.16.0.0/12 port = ssh to 12.34.56.78 port = ircd
-block drop in inet proto tcp from 172.16.0.0/12 port = ssh to 12.34.56.78 port = 6668
-block drop in inet proto tcp from 172.16.0.0/12 port = ssh to 12.34.56.78 port 6669:65535
block drop in inet proto tcp from 172.16.0.0/12 port = ftp to 192.168.0.0/16 port = ircd
block drop in inet proto tcp from 172.16.0.0/12 port = ftp to 192.168.0.0/16 port = 6668
block drop in inet proto tcp from 172.16.0.0/12 port = ftp to 192.168.0.0/16 port 6669:65535
+block drop in inet proto tcp from 172.16.0.0/12 port = ssh to 12.34.56.78 port = ircd
+block drop in inet proto tcp from 172.16.0.0/12 port = ssh to 12.34.56.78 port = 6668
+block drop in inet proto tcp from 172.16.0.0/12 port = ssh to 12.34.56.78 port 6669:65535
block drop in inet proto tcp from 172.16.0.0/12 port = ftp to 12.34.56.78 port = ircd
block drop in inet proto tcp from 172.16.0.0/12 port = ftp to 12.34.56.78 port = 6668
block drop in inet proto tcp from 172.16.0.0/12 port = ftp to 12.34.56.78 port 6669:65535
block drop in inet proto udp from 10.0.0.0/8 port = ssh to 192.168.0.0/16 port = 6667
block drop in inet proto udp from 10.0.0.0/8 port = ssh to 192.168.0.0/16 port = 6668
block drop in inet proto udp from 10.0.0.0/8 port = ssh to 192.168.0.0/16 port 6669:65535
-block drop in inet proto udp from 10.0.0.0/8 port = ssh to 12.34.56.78 port = 6667
-block drop in inet proto udp from 10.0.0.0/8 port = ssh to 12.34.56.78 port = 6668
-block drop in inet proto udp from 10.0.0.0/8 port = ssh to 12.34.56.78 port 6669:65535
block drop in inet proto udp from 10.0.0.0/8 port = ftp to 192.168.0.0/16 port = 6667
block drop in inet proto udp from 10.0.0.0/8 port = ftp to 192.168.0.0/16 port = 6668
block drop in inet proto udp from 10.0.0.0/8 port = ftp to 192.168.0.0/16 port 6669:65535
+block drop in inet proto udp from 10.0.0.0/8 port = ssh to 12.34.56.78 port = 6667
+block drop in inet proto udp from 10.0.0.0/8 port = ssh to 12.34.56.78 port = 6668
+block drop in inet proto udp from 10.0.0.0/8 port = ssh to 12.34.56.78 port 6669:65535
block drop in inet proto udp from 10.0.0.0/8 port = ftp to 12.34.56.78 port = 6667
block drop in inet proto udp from 10.0.0.0/8 port = ftp to 12.34.56.78 port = 6668
block drop in inet proto udp from 10.0.0.0/8 port = ftp to 12.34.56.78 port 6669:65535
block drop in inet proto udp from 172.16.0.0/12 port = ssh to 192.168.0.0/16 port = 6667
block drop in inet proto udp from 172.16.0.0/12 port = ssh to 192.168.0.0/16 port = 6668
block drop in inet proto udp from 172.16.0.0/12 port = ssh to 192.168.0.0/16 port 6669:65535
-block drop in inet proto udp from 172.16.0.0/12 port = ssh to 12.34.56.78 port = 6667
-block drop in inet proto udp from 172.16.0.0/12 port = ssh to 12.34.56.78 port = 6668
-block drop in inet proto udp from 172.16.0.0/12 port = ssh to 12.34.56.78 port 6669:65535
block drop in inet proto udp from 172.16.0.0/12 port = ftp to 192.168.0.0/16 port = 6667
block drop in inet proto udp from 172.16.0.0/12 port = ftp to 192.168.0.0/16 port = 6668
block drop in inet proto udp from 172.16.0.0/12 port = ftp to 192.168.0.0/16 port 6669:65535
+block drop in inet proto udp from 172.16.0.0/12 port = ssh to 12.34.56.78 port = 6667
+block drop in inet proto udp from 172.16.0.0/12 port = ssh to 12.34.56.78 port = 6668
+block drop in inet proto udp from 172.16.0.0/12 port = ssh to 12.34.56.78 port 6669:65535
block drop in inet proto udp from 172.16.0.0/12 port = ftp to 12.34.56.78 port = 6667
block drop in inet proto udp from 172.16.0.0/12 port = ftp to 12.34.56.78 port = 6668
block drop in inet proto udp from 172.16.0.0/12 port = ftp to 12.34.56.78 port 6669:65535
diff --git a/sbin/pfctl/tests/files/pf0016.in b/sbin/pfctl/tests/files/pf0016.in
index 738bfb664395..7dbc53aa6a21 100644
--- a/sbin/pfctl/tests/files/pf0016.in
+++ b/sbin/pfctl/tests/files/pf0016.in
@@ -1,5 +1,5 @@
# Test rule order processing: should fail unless nat -> filter
-#match out on lo0 from 192.168.1.1 to any nat-to 10.0.0.1
-#match in on lo0 proto tcp from any to 1.2.3.4/32 port 2222 rdr-to 10.0.0.10 port 22
-#match on lo0 from 192.168.1.1 to any binat-to 10.0.0.1
+match out on lo0 from 192.168.1.1 to any nat-to 10.0.0.1
+match in on lo0 proto tcp from any to 1.2.3.4/32 port 2222 rdr-to 10.0.0.10 port 22
+match on lo0 from 192.168.1.1 to any binat-to 10.0.0.1
pass in on lo1000000 from any to any no state
diff --git a/sbin/pfctl/tests/files/pf0016.ok b/sbin/pfctl/tests/files/pf0016.ok
index 6f0c211a5b8a..d65374a16475 100644
--- a/sbin/pfctl/tests/files/pf0016.ok
+++ b/sbin/pfctl/tests/files/pf0016.ok
@@ -1 +1,5 @@
+match out on lo0 inet from 192.168.1.1 to any nat-to 10.0.0.1
+match in on lo0 inet proto tcp from any to 1.2.3.4 port = 2222 rdr-to 10.0.0.10 port 22
+match out on lo0 inet from 192.168.1.1 to any nat-to 10.0.0.1 static-port
+match in on lo0 inet from any to 10.0.0.1 rdr-to 192.168.1.1
pass in on lo1000000 all no state
diff --git a/sbin/pfctl/tests/files/pf0018.in b/sbin/pfctl/tests/files/pf0018.in
index 46606b476d79..ab3c81f86c5f 100644
--- a/sbin/pfctl/tests/files/pf0018.in
+++ b/sbin/pfctl/tests/files/pf0018.in
@@ -3,17 +3,17 @@
TEST_LIST1 = "{ 192.168.1.5, 192.168.1.6, 192.168.1.7 }"
TEST_LIST2 = "{ 172.6.1.1, 172.14.1.2/32, 172.16.2.0/24 }"
-#match out on lo0 from 192.168.1.1 to any nat-to 10.0.0.1
-#match out on lo0 proto tcp from 192.168.1.2 to any nat-to 10.0.0.2
-#match out on lo0 proto udp from 192.168.1.3 to any nat-to 10.0.0.3
-#match out on lo0 proto icmp from 192.168.1.4 to any nat-to 10.0.0.4
+match out on lo0 from 192.168.1.1 to any nat-to 10.0.0.1
+match out on lo0 proto tcp from 192.168.1.2 to any nat-to 10.0.0.2
+match out on lo0 proto udp from 192.168.1.3 to any nat-to 10.0.0.3
+match out on lo0 proto icmp from 192.168.1.4 to any nat-to 10.0.0.4
-#match out on lo0 inet from $TEST_LIST1 to $TEST_LIST2 nat-to lo0
+match out on lo0 inet from $TEST_LIST1 to $TEST_LIST2 nat-to lo0
-#match out on lo0 inet from 192.168.0.1/24 to any nat-to (lo0)
+match out on lo0 inet from 192.168.0.1/24 to any nat-to (lo0)
-#match out on lo0 from 192.168.1.8 to ! 172.17.0.0/16 nat-to 10.0.0.8
+match out on lo0 from 192.168.1.8 to ! 172.17.0.0/16 nat-to 10.0.0.8
-#match out on ! lo0 proto { udp, tcp } from any to any nat-to 10.0.0.8 static-port
+match out on ! lo0 proto { udp, tcp } from any to any nat-to 10.0.0.8 static-port
-#match out on { lo0, tun1000000 } from any to any nat-to 10.0.0.8
+match out on { lo0, tun1000000 } from any to any nat-to 10.0.0.8
diff --git a/sbin/pfctl/tests/files/pf0018.ok b/sbin/pfctl/tests/files/pf0018.ok
index c19ead6da1f0..6ba137ae84f8 100644
--- a/sbin/pfctl/tests/files/pf0018.ok
+++ b/sbin/pfctl/tests/files/pf0018.ok
@@ -1,2 +1,21 @@
TEST_LIST1 = "{ 192.168.1.5, 192.168.1.6, 192.168.1.7 }"
TEST_LIST2 = "{ 172.6.1.1, 172.14.1.2/32, 172.16.2.0/24 }"
+match out on lo0 inet from 192.168.1.1 to any nat-to 10.0.0.1
+match out on lo0 inet proto tcp from 192.168.1.2 to any nat-to 10.0.0.2
+match out on lo0 inet proto udp from 192.168.1.3 to any nat-to 10.0.0.3
+match out on lo0 inet proto icmp from 192.168.1.4 to any nat-to 10.0.0.4
+match out on lo0 inet from 192.168.1.5 to 172.6.1.1 nat-to 127.0.0.1
+match out on lo0 inet from 192.168.1.5 to 172.14.1.2 nat-to 127.0.0.1
+match out on lo0 inet from 192.168.1.5 to 172.16.2.0/24 nat-to 127.0.0.1
+match out on lo0 inet from 192.168.1.6 to 172.6.1.1 nat-to 127.0.0.1
+match out on lo0 inet from 192.168.1.6 to 172.14.1.2 nat-to 127.0.0.1
+match out on lo0 inet from 192.168.1.6 to 172.16.2.0/24 nat-to 127.0.0.1
+match out on lo0 inet from 192.168.1.7 to 172.6.1.1 nat-to 127.0.0.1
+match out on lo0 inet from 192.168.1.7 to 172.14.1.2 nat-to 127.0.0.1
+match out on lo0 inet from 192.168.1.7 to 172.16.2.0/24 nat-to 127.0.0.1
+match out on lo0 inet from 192.168.0.0/24 to any nat-to (lo0) round-robin
+match out on lo0 inet from 192.168.1.8 to ! 172.17.0.0/16 nat-to 10.0.0.8
+match out on ! lo0 inet proto udp all nat-to 10.0.0.8 static-port
+match out on ! lo0 inet proto tcp all nat-to 10.0.0.8 static-port
+match out on lo0 inet all nat-to 10.0.0.8
+match out on tun1000000 inet all nat-to 10.0.0.8
diff --git a/sbin/pfctl/tests/files/pf0019.in b/sbin/pfctl/tests/files/pf0019.in
index 0b1456e6fd03..e2bedbb64bd0 100644
--- a/sbin/pfctl/tests/files/pf0019.in
+++ b/sbin/pfctl/tests/files/pf0019.in
@@ -3,7 +3,7 @@ GOOD = "{ lo0, lo1000000 }"
GOOD_NET = "{ 127.0.0.0/24, 10.0.1.0/24 }"
DEST_NET = "{ 1.2.3.4/25, 2.4.6.8/30 }"
-#match in on lo0 proto tcp from any to 1.2.3.4/32 port 2222 rdr-to 10.0.0.10 port 22
+match in on lo0 proto tcp from any to 1.2.3.4/32 port 2222 rdr-to 10.0.0.10 port 22
# Test list processing
-#match in on $GOOD proto tcp from $GOOD_NET to $DEST_NET port 21 rdr-to 127.0.0.1 port 8021
+match in on $GOOD proto tcp from $GOOD_NET to $DEST_NET port 21 rdr-to 127.0.0.1 port 8021
diff --git a/sbin/pfctl/tests/files/pf0019.ok b/sbin/pfctl/tests/files/pf0019.ok
index 16c845aa2cd6..a5afc374d19f 100644
--- a/sbin/pfctl/tests/files/pf0019.ok
+++ b/sbin/pfctl/tests/files/pf0019.ok
@@ -2,3 +2,12 @@ EVIL = "lo0"
GOOD = "{ lo0, lo1000000 }"
GOOD_NET = "{ 127.0.0.0/24, 10.0.1.0/24 }"
DEST_NET = "{ 1.2.3.4/25, 2.4.6.8/30 }"
+match in on lo0 inet proto tcp from any to 1.2.3.4 port = 2222 rdr-to 10.0.0.10 port 22
+match in on lo0 inet proto tcp from 127.0.0.0/24 to 1.2.3.0/25 port = ftp rdr-to 127.0.0.1 port 8021
+match in on lo0 inet proto tcp from 127.0.0.0/24 to 2.4.6.8/30 port = ftp rdr-to 127.0.0.1 port 8021
+match in on lo0 inet proto tcp from 10.0.1.0/24 to 1.2.3.0/25 port = ftp rdr-to 127.0.0.1 port 8021
+match in on lo0 inet proto tcp from 10.0.1.0/24 to 2.4.6.8/30 port = ftp rdr-to 127.0.0.1 port 8021
+match in on lo1000000 inet proto tcp from 127.0.0.0/24 to 1.2.3.0/25 port = ftp rdr-to 127.0.0.1 port 8021
+match in on lo1000000 inet proto tcp from 127.0.0.0/24 to 2.4.6.8/30 port = ftp rdr-to 127.0.0.1 port 8021
+match in on lo1000000 inet proto tcp from 10.0.1.0/24 to 1.2.3.0/25 port = ftp rdr-to 127.0.0.1 port 8021
+match in on lo1000000 inet proto tcp from 10.0.1.0/24 to 2.4.6.8/30 port = ftp rdr-to 127.0.0.1 port 8021
diff --git a/sbin/pfctl/tests/files/pf0020.in b/sbin/pfctl/tests/files/pf0020.in
index b00125bbcdb8..c973785bc9c5 100644
--- a/sbin/pfctl/tests/files/pf0020.in
+++ b/sbin/pfctl/tests/files/pf0020.in
@@ -5,5 +5,5 @@ GOOD = "{ lo0, lo1000000 }"
GOOD_NET = "{ 127.0.0.0/24, 10.0.1.0/24 }"
DEST_NET = "{ 1.2.3.4/25, 2.4.6.8/30 }"
-#match out on $EVIL inet from $GOOD_NET to $DEST_NET nat-to $EVIL
-#match in on $GOOD proto tcp from $GOOD_NET to $DEST_NET port 21 rdr-to 127.0.0.1 port 8021
+match out on $EVIL inet from $GOOD_NET to $DEST_NET nat-to $EVIL
+match in on $GOOD proto tcp from $GOOD_NET to $DEST_NET port 21 rdr-to 127.0.0.1 port 8021
diff --git a/sbin/pfctl/tests/files/pf0020.ok b/sbin/pfctl/tests/files/pf0020.ok
index 16c845aa2cd6..bd2c6cf2055d 100644
--- a/sbin/pfctl/tests/files/pf0020.ok
+++ b/sbin/pfctl/tests/files/pf0020.ok
@@ -2,3 +2,15 @@ EVIL = "lo0"
GOOD = "{ lo0, lo1000000 }"
GOOD_NET = "{ 127.0.0.0/24, 10.0.1.0/24 }"
DEST_NET = "{ 1.2.3.4/25, 2.4.6.8/30 }"
+match out on lo0 inet from 127.0.0.0/24 to 1.2.3.0/25 nat-to 127.0.0.1
+match out on lo0 inet from 127.0.0.0/24 to 2.4.6.8/30 nat-to 127.0.0.1
+match out on lo0 inet from 10.0.1.0/24 to 1.2.3.0/25 nat-to 127.0.0.1
+match out on lo0 inet from 10.0.1.0/24 to 2.4.6.8/30 nat-to 127.0.0.1
+match in on lo0 inet proto tcp from 127.0.0.0/24 to 1.2.3.0/25 port = ftp rdr-to 127.0.0.1 port 8021
+match in on lo0 inet proto tcp from 127.0.0.0/24 to 2.4.6.8/30 port = ftp rdr-to 127.0.0.1 port 8021
+match in on lo0 inet proto tcp from 10.0.1.0/24 to 1.2.3.0/25 port = ftp rdr-to 127.0.0.1 port 8021
+match in on lo0 inet proto tcp from 10.0.1.0/24 to 2.4.6.8/30 port = ftp rdr-to 127.0.0.1 port 8021
+match in on lo1000000 inet proto tcp from 127.0.0.0/24 to 1.2.3.0/25 port = ftp rdr-to 127.0.0.1 port 8021
+match in on lo1000000 inet proto tcp from 127.0.0.0/24 to 2.4.6.8/30 port = ftp rdr-to 127.0.0.1 port 8021
+match in on lo1000000 inet proto tcp from 10.0.1.0/24 to 1.2.3.0/25 port = ftp rdr-to 127.0.0.1 port 8021
+match in on lo1000000 inet proto tcp from 10.0.1.0/24 to 2.4.6.8/30 port = ftp rdr-to 127.0.0.1 port 8021
diff --git a/sbin/pfctl/tests/files/pf0048.in b/sbin/pfctl/tests/files/pf0048.in
index e97a819de945..a0dd143c8dd2 100644
--- a/sbin/pfctl/tests/files/pf0048.in
+++ b/sbin/pfctl/tests/files/pf0048.in
@@ -1,12 +1,12 @@
table < regress > { 1.2.3.4 !5.6.7.8 10/8 lo0 }
table <regress.1> const { ::1 fe80::/64 }
table <regress.a> { 1.2.3.4 !5.6.7.8 } { ::1 ::2 ::3 } file "/dev/null" const { 4.3.2.1 }
-#match out on lo0 inet from < regress.1> to <regress.2> nat-to lo0:0
-#match out on !lo0 inet from !<regress.1 > to <regress.2> nat-to lo0:0
-#match in on lo0 inet6 from <regress.1> to <regress.2> rdr-to lo0:0
-#match in on !lo0 inet6 from !< regress.1 > to <regress.2> rdr-to lo0:0
-#match in from { <regress.1> !<regress.2> } to any
-#match out from any to { !<regress.1>, <regress.2> }
+match out on lo0 inet from < regress.1> to <regress.2> nat-to lo0:0
+match out on !lo0 inet from !<regress.1 > to <regress.2> nat-to lo0:0
+match in on lo0 inet6 from <regress.1> to <regress.2> rdr-to lo0:0
+match in on !lo0 inet6 from !< regress.1 > to <regress.2> rdr-to lo0:0
+match in from { <regress.1> !<regress.2> } to any
+match out from any to { !<regress.1>, <regress.2> }
pass in from <regress> to any
pass out from any to <regress >
pass in from { <regress.1> <regress.2> } to any
diff --git a/sbin/pfctl/tests/files/pf0048.ok b/sbin/pfctl/tests/files/pf0048.ok
index f3536f566d35..89569fb4f8ba 100644
--- a/sbin/pfctl/tests/files/pf0048.ok
+++ b/sbin/pfctl/tests/files/pf0048.ok
@@ -1,6 +1,14 @@
table <regress> { 1.2.3.4 !5.6.7.8 10.0.0.0/8 ::1 fe80::1 127.0.0.1 }
table <regress.1> const { ::1 fe80::/64 }
table <regress.a> const { 1.2.3.4 !5.6.7.8 ::1 ::2 ::3 } file "/dev/null" { 4.3.2.1 }
+match out on lo0 inet from <regress.1> to <regress.2> nat-to 127.0.0.1
+match out on ! lo0 inet from ! <regress.1> to <regress.2> nat-to 127.0.0.1
+match in on lo0 inet6 from <regress.1> to <regress.2> rdr-to ::1
+match in on ! lo0 inet6 from ! <regress.1> to <regress.2> rdr-to ::1
+match in from <regress.1> to any
+match in from ! <regress.2> to any
+match out from any to ! <regress.1>
+match out from any to <regress.2>
pass in from <regress> to any flags S/SA keep state
pass out from any to <regress> flags S/SA keep state
pass in from <regress.1> to any flags S/SA keep state
diff --git a/sbin/pfctl/tests/files/pf0069.in b/sbin/pfctl/tests/files/pf0069.in
index 1298954bbeda..85847b9bd6b2 100644
--- a/sbin/pfctl/tests/files/pf0069.in
+++ b/sbin/pfctl/tests/files/pf0069.in
@@ -1,3 +1,2 @@
-#match out on lo0 inet all tag regress nat-to lo0
+match out on lo0 inet all tag regress nat-to lo0
pass out quick on lo0 keep state tagged regress
-
diff --git a/sbin/pfctl/tests/files/pf0069.ok b/sbin/pfctl/tests/files/pf0069.ok
index 33e0519645fc..2bf34c04baa7 100644
--- a/sbin/pfctl/tests/files/pf0069.ok
+++ b/sbin/pfctl/tests/files/pf0069.ok
@@ -1 +1,2 @@
+match out on lo0 inet all tag regress nat-to 127.0.0.1
pass out quick on lo0 all flags S/SA keep state tagged regress
diff --git a/sbin/pfctl/tests/files/pf0070.in b/sbin/pfctl/tests/files/pf0070.in
index 8d5e34a13ff8..1ccec9302436 100644
--- a/sbin/pfctl/tests/files/pf0070.in
+++ b/sbin/pfctl/tests/files/pf0070.in
@@ -1,3 +1,2 @@
-#match out on lo0 from 10.0.0.0/8 to any nat-to lo0
+match out on lo0 from 10.0.0.0/8 to any nat-to lo0
block out on lo0 tagged regress
-
diff --git a/sbin/pfctl/tests/files/pf0070.ok b/sbin/pfctl/tests/files/pf0070.ok
index d30b70ff3e5a..cf79485b40c1 100644
--- a/sbin/pfctl/tests/files/pf0070.ok
+++ b/sbin/pfctl/tests/files/pf0070.ok
@@ -1 +1,2 @@
+match out on lo0 inet from 10.0.0.0/8 to any nat-to 127.0.0.1
block drop out on lo0 all tagged regress
diff --git a/sbin/pfctl/tests/files/pf0071.in b/sbin/pfctl/tests/files/pf0071.in
index 48976b61ed3d..8975a8ebc943 100644
--- a/sbin/pfctl/tests/files/pf0071.in
+++ b/sbin/pfctl/tests/files/pf0071.in
@@ -1,3 +1,2 @@
-#match in on lo0 proto tcp from 10.0.0.0/8 to port 80 rdr-to lo0
+match in on lo0 proto tcp from 10.0.0.0/8 to port 80 rdr-to lo0
block out on lo0 tagged regress
-
diff --git a/sbin/pfctl/tests/files/pf0071.ok b/sbin/pfctl/tests/files/pf0071.ok
index d30b70ff3e5a..2bae94fc8fac 100644
--- a/sbin/pfctl/tests/files/pf0071.ok
+++ b/sbin/pfctl/tests/files/pf0071.ok
@@ -1 +1,2 @@
+match in on lo0 inet proto tcp from 10.0.0.0/8 to any port = http rdr-to 127.0.0.1
block drop out on lo0 all tagged regress
diff --git a/sbin/pfctl/tests/files/pf0072.in b/sbin/pfctl/tests/files/pf0072.in
index fd037f31ef27..d23843b799d5 100644
--- a/sbin/pfctl/tests/files/pf0072.in
+++ b/sbin/pfctl/tests/files/pf0072.in
@@ -1,4 +1,3 @@
# test binat tagging
-#match on lo0 from 192.168.1.1 to any tag regress binat-to 10.0.0.1
+match on lo0 from 192.168.1.1 to any tag regress binat-to 10.0.0.1
block out on lo0 tagged regress
-
diff --git a/sbin/pfctl/tests/files/pf0072.ok b/sbin/pfctl/tests/files/pf0072.ok
index d30b70ff3e5a..02e676dadc06 100644
--- a/sbin/pfctl/tests/files/pf0072.ok
+++ b/sbin/pfctl/tests/files/pf0072.ok
@@ -1 +1,3 @@
+match out on lo0 inet from 192.168.1.1 to any tag regress nat-to 10.0.0.1 static-port
+match in on lo0 inet from any to 10.0.0.1 tag regress rdr-to 192.168.1.1
block drop out on lo0 all tagged regress
diff --git a/sbin/pfctl/tests/files/pf0084.in b/sbin/pfctl/tests/files/pf0084.in
index c0390df889e3..17140a786d73 100644
--- a/sbin/pfctl/tests/files/pf0084.in
+++ b/sbin/pfctl/tests/files/pf0084.in
@@ -1,9 +1,9 @@
-#match out on tun1000000 from 10.0.0.0/24 to any \
-# nat-to { 10.0.1.1, 10.0.1.2 } round-robin sticky-address
-#match in on tun1000000 from any to 10.0.1.1 \
-# rdr-to { 10.0.0.0/24 } sticky-address random
-#match in on tun1000000 from any to 10.0.1.2 \
-# rdr-to { 10.0.0.1, 10.0.0.2 } sticky-address
+match out on tun1000000 from 10.0.0.0/24 to any \
+ nat-to { 10.0.1.1, 10.0.1.2 } round-robin sticky-address
+match in on tun1000000 from any to 10.0.1.1 \
+ rdr-to { 10.0.0.0/24 } sticky-address random
+match in on tun1000000 from any to 10.0.1.2 \
+ rdr-to { 10.0.0.1, 10.0.0.2 } sticky-address
pass in proto tcp from any to any port 22 \
keep state (source-track)
diff --git a/sbin/pfctl/tests/files/pf0084.ok b/sbin/pfctl/tests/files/pf0084.ok
index 272fd6052023..1ca89e515a3d 100644
--- a/sbin/pfctl/tests/files/pf0084.ok
+++ b/sbin/pfctl/tests/files/pf0084.ok
@@ -1,3 +1,6 @@
+match out on tun1000000 inet from 10.0.0.0/24 to any nat-to { 10.0.1.1, 10.0.1.2 } round-robin sticky-address
+match in on tun1000000 inet from any to 10.0.1.1 rdr-to 10.0.0.0/24 random sticky-address
+match in on tun1000000 inet from any to 10.0.1.2 rdr-to { 10.0.0.1, 10.0.0.2 } round-robin sticky-address
pass in proto tcp from any to any port = ssh flags S/SA keep state (source-track global)
pass in proto tcp from any to any port = smtp flags S/SA keep state (source-track global)
pass in proto tcp from any to any port = http flags S/SA keep state (source-track rule, max-src-states 3, max-src-nodes 1000)
diff --git a/sbin/pfctl/tests/files/pf0088.in b/sbin/pfctl/tests/files/pf0088.in
index 4700b6916b7e..a85aa84a30bb 100644
--- a/sbin/pfctl/tests/files/pf0088.in
+++ b/sbin/pfctl/tests/files/pf0088.in
@@ -16,7 +16,7 @@ pass to 10.0.0.2 keep state
block from 10.0.0.3 to 10.0.0.2
pass to 10.0.0.2 modulate state
block from 10.0.0.3 to 10.0.0.2
-pass to 10.0.0.2 synproxy state
+pass in to 10.0.0.2 synproxy state
pass out proto tcp from 10.0.0.4 to 10.0.0.5 keep state
diff --git a/sbin/pfctl/tests/files/pf0088.ok b/sbin/pfctl/tests/files/pf0088.ok
index 47251a4503dd..801056a4ab46 100644
--- a/sbin/pfctl/tests/files/pf0088.ok
+++ b/sbin/pfctl/tests/files/pf0088.ok
@@ -11,7 +11,7 @@ pass inet from any to 10.0.0.2 flags S/SA keep state
block drop inet from 10.0.0.3 to 10.0.0.2
pass inet from any to 10.0.0.2 flags S/SA modulate state
block drop inet from 10.0.0.3 to 10.0.0.2
-pass inet from any to 10.0.0.2 flags S/SA synproxy state
+pass in inet from any to 10.0.0.2 flags S/SA synproxy state
pass out inet proto tcp from 10.0.0.4 to 10.0.0.5 flags S/SA keep state
pass out inet proto tcp from 10.0.0.4 to 10.0.0.5 port = http flags S/SA keep state
pass out all flags S/SA keep state
diff --git a/sbin/pfctl/tests/files/pf0098.in b/sbin/pfctl/tests/files/pf0098.in
index b2b642be2026..c26f0fcfe4d3 100644
--- a/sbin/pfctl/tests/files/pf0098.in
+++ b/sbin/pfctl/tests/files/pf0098.in
@@ -1,4 +1,3 @@
# Test rule order processing should pass (require-order no longer required)
pass in on lo1000000 all
-#match out on lo0 inet6 all nat-to lo0
-
+match out on lo0 inet6 all nat-to lo0
diff --git a/sbin/pfctl/tests/files/pf0098.ok b/sbin/pfctl/tests/files/pf0098.ok
index 62016c91d60b..105bb46b4ae5 100644
--- a/sbin/pfctl/tests/files/pf0098.ok
+++ b/sbin/pfctl/tests/files/pf0098.ok
@@ -1 +1,2 @@
pass in on lo1000000 all flags S/SA keep state
+match out on lo0 inet6 all nat-to { ::1, fe80::1 } round-robin
diff --git a/sbin/pfctl/tests/files/pf0102.ok b/sbin/pfctl/tests/files/pf0102.ok
index 3233ca5a2643..1c76ec2725ba 100644
--- a/sbin/pfctl/tests/files/pf0102.ok
+++ b/sbin/pfctl/tests/files/pf0102.ok
@@ -1,8 +1,8 @@
pass inet from 1.1.1.1 to (self) flags S/SA keep state
-pass inet6 from 2002:: to (self)/32 flags S/SA keep state
+pass inet6 from 2002:: to (self) flags S/SA keep state
pass inet6 from 2002:: to (self) flags S/SA keep state
pass inet from 1.1.1.1 to (self) flags S/SA keep state
pass inet from 1.1.1.1 to (self) flags S/SA keep state
-pass inet6 from 2002:: to (self)/32 flags S/SA keep state
+pass inet6 from 2002:: to (self)/40 flags S/SA keep state
pass inet6 from 2002:: to (self)/40 flags S/SA keep state
pass inet from 1.1.1.1 to (self) flags S/SA keep state
diff --git a/sbin/pfctl/tests/files/pf1002.in b/sbin/pfctl/tests/files/pf1002.in
index 5180e8395f9c..3fdde81be7de 100644
--- a/sbin/pfctl/tests/files/pf1002.in
+++ b/sbin/pfctl/tests/files/pf1002.in
@@ -1 +1,6 @@
set timeout interval 10
+set timeout sctp.first 11
+set timeout sctp.opening 12
+set timeout sctp.established 13
+set timeout sctp.closing 14
+set timeout sctp.closed 15
diff --git a/sbin/pfctl/tests/files/pf1002.ok b/sbin/pfctl/tests/files/pf1002.ok
index 5180e8395f9c..3fdde81be7de 100644
--- a/sbin/pfctl/tests/files/pf1002.ok
+++ b/sbin/pfctl/tests/files/pf1002.ok
@@ -1 +1,6 @@
set timeout interval 10
+set timeout sctp.first 11
+set timeout sctp.opening 12
+set timeout sctp.established 13
+set timeout sctp.closing 14
+set timeout sctp.closed 15
diff --git a/sbin/pfctl/tests/files/pf1018.in b/sbin/pfctl/tests/files/pf1018.in
new file mode 100644
index 000000000000..90f0a3a0bab7
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1018.in
@@ -0,0 +1 @@
+pass from { 192.0.2.1 2001:db8::1 } to (pppoe0)
diff --git a/sbin/pfctl/tests/files/pf1018.ok b/sbin/pfctl/tests/files/pf1018.ok
new file mode 100644
index 000000000000..04950f0035b8
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1018.ok
@@ -0,0 +1,2 @@
+pass inet from 192.0.2.1 to (pppoe0) flags S/SA keep state
+pass inet6 from 2001:db8::1 to (pppoe0) flags S/SA keep state
diff --git a/sbin/pfctl/tests/files/pf1019.in b/sbin/pfctl/tests/files/pf1019.in
new file mode 100644
index 000000000000..04a770768714
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1019.in
@@ -0,0 +1 @@
+pass in keep state (pflow)
diff --git a/sbin/pfctl/tests/files/pf1019.ok b/sbin/pfctl/tests/files/pf1019.ok
new file mode 100644
index 000000000000..e865d57da16c
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1019.ok
@@ -0,0 +1 @@
+pass in all flags S/SA keep state (pflow)
diff --git a/sbin/pfctl/tests/files/pf1020.in b/sbin/pfctl/tests/files/pf1020.in
new file mode 100644
index 000000000000..7f98df69bd04
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1020.in
@@ -0,0 +1,3 @@
+table <tabl1> file "./pf1020.include"
+
+block from <tabl1>
diff --git a/sbin/pfctl/tests/files/pf1020.include b/sbin/pfctl/tests/files/pf1020.include
new file mode 100644
index 000000000000..3fca07f64bfa
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1020.include
@@ -0,0 +1,4 @@
+; comment1
+# comment2
+1.0.0.1/32 ; comment1
+2.0.0.2/32 # comment2
diff --git a/sbin/pfctl/tests/files/pf1020.ok b/sbin/pfctl/tests/files/pf1020.ok
new file mode 100644
index 000000000000..16073b3d6987
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1020.ok
@@ -0,0 +1,2 @@
+table <tabl1> file "./pf1020.include"
+block drop from <tabl1> to any
diff --git a/sbin/pfctl/tests/files/pf1021.in b/sbin/pfctl/tests/files/pf1021.in
new file mode 100644
index 000000000000..841b024157c6
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1021.in
@@ -0,0 +1 @@
+nat on vtnet1 inet from ! (vtnet1) to any -> (vtnet1) endpoint-independent
diff --git a/sbin/pfctl/tests/files/pf1021.ok b/sbin/pfctl/tests/files/pf1021.ok
new file mode 100644
index 000000000000..3b5b84e2e11b
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1021.ok
@@ -0,0 +1 @@
+nat on vtnet1 inet from ! (vtnet1) to any -> (vtnet1) round-robin endpoint-independent
diff --git a/sbin/pfctl/tests/files/pf1022.in b/sbin/pfctl/tests/files/pf1022.in
new file mode 100644
index 000000000000..640eb1334100
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1022.in
@@ -0,0 +1 @@
+pass out on em0 from 192.0.2.1 to 198.51.100.1 received-on fxp0
diff --git a/sbin/pfctl/tests/files/pf1022.ok b/sbin/pfctl/tests/files/pf1022.ok
new file mode 100644
index 000000000000..2f7b4a5bd616
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1022.ok
@@ -0,0 +1 @@
+pass out on em0 inet from 192.0.2.1 to 198.51.100.1 received-on fxp0 flags S/SA keep state
diff --git a/sbin/pfctl/tests/files/pf1023.in b/sbin/pfctl/tests/files/pf1023.in
new file mode 100644
index 000000000000..4855ae0f339e
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1023.in
@@ -0,0 +1,3 @@
+match log(matches) inet proto tcp
+match log(matches) inet from 192.0.2.0/24
+pass
diff --git a/sbin/pfctl/tests/files/pf1023.ok b/sbin/pfctl/tests/files/pf1023.ok
new file mode 100644
index 000000000000..63fa40113ecf
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1023.ok
@@ -0,0 +1,3 @@
+match log (matches) inet proto tcp all
+match log (matches) inet from 192.0.2.0/24 to any
+pass all flags S/SA keep state
diff --git a/sbin/pfctl/tests/files/pf1024.in b/sbin/pfctl/tests/files/pf1024.in
new file mode 100644
index 000000000000..be518bb3bd53
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1024.in
@@ -0,0 +1 @@
+pass in inet af-to inet6 from 2001:db8::1
diff --git a/sbin/pfctl/tests/files/pf1024.ok b/sbin/pfctl/tests/files/pf1024.ok
new file mode 100644
index 000000000000..2d4ddb9d0ce7
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1024.ok
@@ -0,0 +1 @@
+pass in inet all flags S/SA keep state af-to inet6 from 2001:db8::1
diff --git a/sbin/pfctl/tests/files/pf1025.in b/sbin/pfctl/tests/files/pf1025.in
new file mode 100644
index 000000000000..d4ad821a6899
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1025.in
@@ -0,0 +1 @@
+pass in from 10.0.0.0/8 af-to inet6 from 2001:db8::1
diff --git a/sbin/pfctl/tests/files/pf1025.ok b/sbin/pfctl/tests/files/pf1025.ok
new file mode 100644
index 000000000000..8f48c987c6a0
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1025.ok
@@ -0,0 +1 @@
+pass in inet from 10.0.0.0/8 to any flags S/SA keep state af-to inet6 from 2001:db8::1
diff --git a/sbin/pfctl/tests/files/pf1026.in b/sbin/pfctl/tests/files/pf1026.in
new file mode 100644
index 000000000000..3691d0947b39
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1026.in
@@ -0,0 +1 @@
+pass in on epair2b route-to (epair0a 192.0.2.2) inet6 from any to 64:ff9b::/96 af-to inet from (epair0a)
diff --git a/sbin/pfctl/tests/files/pf1026.ok b/sbin/pfctl/tests/files/pf1026.ok
new file mode 100644
index 000000000000..323036f2b800
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1026.ok
@@ -0,0 +1 @@
+pass in on epair2b route-to (epair0a 192.0.2.2) inet6 from any to 64:ff9b::/96 flags S/SA keep state af-to inet from (epair0a) round-robin
diff --git a/sbin/pfctl/tests/files/pf1027.in b/sbin/pfctl/tests/files/pf1027.in
new file mode 100644
index 000000000000..3c5c24025e0a
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1027.in
@@ -0,0 +1 @@
+pass in on epair2b reply-to (epair0a 2001:db8::1) inet6 from any to 64:ff9b::/96 af-to inet from (epair0a)
diff --git a/sbin/pfctl/tests/files/pf1027.ok b/sbin/pfctl/tests/files/pf1027.ok
new file mode 100644
index 000000000000..b50f1e216837
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1027.ok
@@ -0,0 +1 @@
+pass in on epair2b reply-to (epair0a 2001:db8::1) inet6 from any to 64:ff9b::/96 flags S/SA keep state af-to inet from (epair0a) round-robin
diff --git a/sbin/pfctl/tests/files/pf1028.in b/sbin/pfctl/tests/files/pf1028.in
new file mode 100644
index 000000000000..2386fcb52249
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1028.in
@@ -0,0 +1 @@
+rdr on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3
diff --git a/sbin/pfctl/tests/files/pf1028.ok b/sbin/pfctl/tests/files/pf1028.ok
new file mode 100644
index 000000000000..07be890f4e05
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1028.ok
@@ -0,0 +1 @@
+rdr on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3
diff --git a/sbin/pfctl/tests/files/pf1029.in b/sbin/pfctl/tests/files/pf1029.in
new file mode 100644
index 000000000000..73815839aadd
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1029.in
@@ -0,0 +1 @@
+rdr on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 port 1002
diff --git a/sbin/pfctl/tests/files/pf1029.ok b/sbin/pfctl/tests/files/pf1029.ok
new file mode 100644
index 000000000000..6e9083bf856a
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1029.ok
@@ -0,0 +1 @@
+rdr on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 port 1002
diff --git a/sbin/pfctl/tests/files/pf1030.in b/sbin/pfctl/tests/files/pf1030.in
new file mode 100644
index 000000000000..b6f891998a71
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1030.in
@@ -0,0 +1 @@
+rdr on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 port 50001:65535
diff --git a/sbin/pfctl/tests/files/pf1030.ok b/sbin/pfctl/tests/files/pf1030.ok
new file mode 100644
index 000000000000..4f6b2eba2f39
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1030.ok
@@ -0,0 +1 @@
+rdr on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 port 50001:65535
diff --git a/sbin/pfctl/tests/files/pf1031.in b/sbin/pfctl/tests/files/pf1031.in
new file mode 100644
index 000000000000..7cad4ae64000
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1031.in
@@ -0,0 +1 @@
+rdr on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 port 1004:2004 -> 192.0.2.3 port 1004
diff --git a/sbin/pfctl/tests/files/pf1031.ok b/sbin/pfctl/tests/files/pf1031.ok
new file mode 100644
index 000000000000..8dd7fe027716
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1031.ok
@@ -0,0 +1 @@
+rdr on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 port 1004:2004 -> 192.0.2.3 port 1004
diff --git a/sbin/pfctl/tests/files/pf1032.in b/sbin/pfctl/tests/files/pf1032.in
new file mode 100644
index 000000000000..a2eec78da045
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1032.in
@@ -0,0 +1 @@
+rdr on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 port 1005:2005 -> 192.0.2.3 port 3004:*
diff --git a/sbin/pfctl/tests/files/pf1032.ok b/sbin/pfctl/tests/files/pf1032.ok
new file mode 100644
index 000000000000..3b3f124efc33
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1032.ok
@@ -0,0 +1 @@
+rdr on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 port 1005:2005 -> 192.0.2.3 port 3004:4004
diff --git a/sbin/pfctl/tests/files/pf1033.fail b/sbin/pfctl/tests/files/pf1033.fail
new file mode 100644
index 000000000000..d9fbfe4296e3
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1033.fail
@@ -0,0 +1 @@
+the 'static-port' option is only valid with nat rules
diff --git a/sbin/pfctl/tests/files/pf1033.in b/sbin/pfctl/tests/files/pf1033.in
new file mode 100644
index 000000000000..76f33e7e8f0e
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1033.in
@@ -0,0 +1 @@
+rdr on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 static-port
diff --git a/sbin/pfctl/tests/files/pf1034.fail b/sbin/pfctl/tests/files/pf1034.fail
new file mode 100644
index 000000000000..e407996a8fa3
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1034.fail
@@ -0,0 +1 @@
+the 'map-e-portset' option is only valid with nat rules
diff --git a/sbin/pfctl/tests/files/pf1034.in b/sbin/pfctl/tests/files/pf1034.in
new file mode 100644
index 000000000000..be847a8af241
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1034.in
@@ -0,0 +1 @@
+rdr on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 map-e-portset 6/8/0x34
diff --git a/sbin/pfctl/tests/files/pf1035.in b/sbin/pfctl/tests/files/pf1035.in
new file mode 100644
index 000000000000..9382ffedc8c9
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1035.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3
diff --git a/sbin/pfctl/tests/files/pf1035.ok b/sbin/pfctl/tests/files/pf1035.ok
new file mode 100644
index 000000000000..be573ef460f5
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1035.ok
@@ -0,0 +1 @@
+nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3
diff --git a/sbin/pfctl/tests/files/pf1036.in b/sbin/pfctl/tests/files/pf1036.in
new file mode 100644
index 000000000000..81718c908303
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1036.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 port 50001:65535
diff --git a/sbin/pfctl/tests/files/pf1036.ok b/sbin/pfctl/tests/files/pf1036.ok
new file mode 100644
index 000000000000..be573ef460f5
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1036.ok
@@ -0,0 +1 @@
+nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3
diff --git a/sbin/pfctl/tests/files/pf1037.in b/sbin/pfctl/tests/files/pf1037.in
new file mode 100644
index 000000000000..a30f6c0e7bbe
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1037.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 port 1003
diff --git a/sbin/pfctl/tests/files/pf1037.ok b/sbin/pfctl/tests/files/pf1037.ok
new file mode 100644
index 000000000000..020e2de28dec
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1037.ok
@@ -0,0 +1 @@
+nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 port 1003
diff --git a/sbin/pfctl/tests/files/pf1038.in b/sbin/pfctl/tests/files/pf1038.in
new file mode 100644
index 000000000000..532060e56494
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1038.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 port 1004:2004
diff --git a/sbin/pfctl/tests/files/pf1038.ok b/sbin/pfctl/tests/files/pf1038.ok
new file mode 100644
index 000000000000..a4021db7b1b2
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1038.ok
@@ -0,0 +1 @@
+nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 port 1004:2004
diff --git a/sbin/pfctl/tests/files/pf1039.in b/sbin/pfctl/tests/files/pf1039.in
new file mode 100644
index 000000000000..dba14b0625de
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1039.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 static-port
diff --git a/sbin/pfctl/tests/files/pf1039.ok b/sbin/pfctl/tests/files/pf1039.ok
new file mode 100644
index 000000000000..80cfbe742865
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1039.ok
@@ -0,0 +1 @@
+nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 static-port
diff --git a/sbin/pfctl/tests/files/pf1040.fail b/sbin/pfctl/tests/files/pf1040.fail
new file mode 100644
index 000000000000..5b9afc22b441
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1040.fail
@@ -0,0 +1 @@
+the 'static-port' option can't be used when specifying a port range
diff --git a/sbin/pfctl/tests/files/pf1040.in b/sbin/pfctl/tests/files/pf1040.in
new file mode 100644
index 000000000000..38d7292a560a
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1040.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 port 1006 static-port
diff --git a/sbin/pfctl/tests/files/pf1040.ok b/sbin/pfctl/tests/files/pf1040.ok
new file mode 100644
index 000000000000..ffe2e023f77c
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1040.ok
@@ -0,0 +1 @@
+nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 map-e-portset 6/8/52
diff --git a/sbin/pfctl/tests/files/pf1041.in b/sbin/pfctl/tests/files/pf1041.in
new file mode 100644
index 000000000000..4c384ac70e05
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1041.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 map-e-portset 6/8/0x34
diff --git a/sbin/pfctl/tests/files/pf1041.ok b/sbin/pfctl/tests/files/pf1041.ok
new file mode 100644
index 000000000000..ffe2e023f77c
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1041.ok
@@ -0,0 +1 @@
+nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 map-e-portset 6/8/52
diff --git a/sbin/pfctl/tests/files/pf1042.fail b/sbin/pfctl/tests/files/pf1042.fail
new file mode 100644
index 000000000000..56e174a5ece5
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1042.fail
@@ -0,0 +1 @@
+the 'map-e-portset' option can't be used 'static-port'
diff --git a/sbin/pfctl/tests/files/pf1042.in b/sbin/pfctl/tests/files/pf1042.in
new file mode 100644
index 000000000000..906f637b6a0a
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1042.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 static-port map-e-portset 6/8/0x34
diff --git a/sbin/pfctl/tests/files/pf1043.fail b/sbin/pfctl/tests/files/pf1043.fail
new file mode 100644
index 000000000000..cdfab00916a2
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1043.fail
@@ -0,0 +1 @@
+the 'map-e-portset' option can't be used when specifying a port range
diff --git a/sbin/pfctl/tests/files/pf1043.in b/sbin/pfctl/tests/files/pf1043.in
new file mode 100644
index 000000000000..15428a9e54bc
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1043.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 port 1007 map-e-portset 6/8/0x34
diff --git a/sbin/pfctl/tests/files/pf1044.in b/sbin/pfctl/tests/files/pf1044.in
new file mode 100644
index 000000000000..6a927b66b83f
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1044.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> <targets> sticky-address
diff --git a/sbin/pfctl/tests/files/pf1044.ok b/sbin/pfctl/tests/files/pf1044.ok
new file mode 100644
index 000000000000..a68b1daaa73a
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1044.ok
@@ -0,0 +1 @@
+nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> <targets> round-robin sticky-address
diff --git a/sbin/pfctl/tests/files/pf1045.in b/sbin/pfctl/tests/files/pf1045.in
new file mode 100644
index 000000000000..38f708ce19b8
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1045.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 203.0.113.0/24 bitmask
diff --git a/sbin/pfctl/tests/files/pf1045.ok b/sbin/pfctl/tests/files/pf1045.ok
new file mode 100644
index 000000000000..5388db7e58a4
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1045.ok
@@ -0,0 +1 @@
+nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 203.0.113.0/24 bitmask
diff --git a/sbin/pfctl/tests/files/pf1046.fail b/sbin/pfctl/tests/files/pf1046.fail
new file mode 100644
index 000000000000..b152f9063241
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1046.fail
@@ -0,0 +1 @@
+tables are not supported by pool type
diff --git a/sbin/pfctl/tests/files/pf1046.in b/sbin/pfctl/tests/files/pf1046.in
new file mode 100644
index 000000000000..e4a9f79efd6f
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1046.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> <targets> bitmask
diff --git a/sbin/pfctl/tests/files/pf1047.fail b/sbin/pfctl/tests/files/pf1047.fail
new file mode 100644
index 000000000000..239b96b2fed4
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1047.fail
@@ -0,0 +1 @@
+interface \(vtnet1\) is not supported by pool type
diff --git a/sbin/pfctl/tests/files/pf1047.in b/sbin/pfctl/tests/files/pf1047.in
new file mode 100644
index 000000000000..369bfcb0fb26
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1047.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> (vtnet1) bitmask
diff --git a/sbin/pfctl/tests/files/pf1048.in b/sbin/pfctl/tests/files/pf1048.in
new file mode 100644
index 000000000000..01232a33b5d8
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1048.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 203.0.113.0/24 random
diff --git a/sbin/pfctl/tests/files/pf1048.ok b/sbin/pfctl/tests/files/pf1048.ok
new file mode 100644
index 000000000000..35e86fc676fc
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1048.ok
@@ -0,0 +1 @@
+nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 203.0.113.0/24 random
diff --git a/sbin/pfctl/tests/files/pf1049.in b/sbin/pfctl/tests/files/pf1049.in
new file mode 100644
index 000000000000..3f2e5acf8265
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1049.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> { 192.0.2.3 }
diff --git a/sbin/pfctl/tests/files/pf1049.ok b/sbin/pfctl/tests/files/pf1049.ok
new file mode 100644
index 000000000000..be573ef460f5
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1049.ok
@@ -0,0 +1 @@
+nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3
diff --git a/sbin/pfctl/tests/files/pf1050.in b/sbin/pfctl/tests/files/pf1050.in
new file mode 100644
index 000000000000..69ccaf445c3b
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1050.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> <targets>
diff --git a/sbin/pfctl/tests/files/pf1050.ok b/sbin/pfctl/tests/files/pf1050.ok
new file mode 100644
index 000000000000..24ca9b459bb7
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1050.ok
@@ -0,0 +1 @@
+nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> <targets> round-robin
diff --git a/sbin/pfctl/tests/files/pf1051.in b/sbin/pfctl/tests/files/pf1051.in
new file mode 100644
index 000000000000..734da64a372c
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1051.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> { 203.0.113.1 203.0.113.2 }
diff --git a/sbin/pfctl/tests/files/pf1051.ok b/sbin/pfctl/tests/files/pf1051.ok
new file mode 100644
index 000000000000..86f23488be41
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1051.ok
@@ -0,0 +1 @@
+nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> { 203.0.113.1, 203.0.113.2 } round-robin
diff --git a/sbin/pfctl/tests/files/pf1052.in b/sbin/pfctl/tests/files/pf1052.in
new file mode 100644
index 000000000000..2ea770f3c06e
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1052.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> { 203.0.113.1 <targets> }
diff --git a/sbin/pfctl/tests/files/pf1052.ok b/sbin/pfctl/tests/files/pf1052.ok
new file mode 100644
index 000000000000..b71d105eb77a
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1052.ok
@@ -0,0 +1 @@
+nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> { 203.0.113.1, <targets> } round-robin
diff --git a/sbin/pfctl/tests/files/pf1053.in b/sbin/pfctl/tests/files/pf1053.in
new file mode 100644
index 000000000000..f0cced0b64a2
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1053.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 203.0.113.0/24
diff --git a/sbin/pfctl/tests/files/pf1053.ok b/sbin/pfctl/tests/files/pf1053.ok
new file mode 100644
index 000000000000..de321b8c738f
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1053.ok
@@ -0,0 +1 @@
+nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 203.0.113.0/24
diff --git a/sbin/pfctl/tests/files/pf1054.in b/sbin/pfctl/tests/files/pf1054.in
new file mode 100644
index 000000000000..9e66bb2a81d6
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1054.in
@@ -0,0 +1,3 @@
+# XXX: it causes just the 0th address to be used without cycling
+# Probably a bug
+nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 203.0.113.0/24 round-robin
diff --git a/sbin/pfctl/tests/files/pf1054.ok b/sbin/pfctl/tests/files/pf1054.ok
new file mode 100644
index 000000000000..3d7ab7974d87
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1054.ok
@@ -0,0 +1 @@
+nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 203.0.113.0/24 round-robin
diff --git a/sbin/pfctl/tests/files/pf1055.in b/sbin/pfctl/tests/files/pf1055.in
new file mode 100644
index 000000000000..c116ef5fd43e
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1055.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 203.0.113.0/24 source-hash 0x42424242424242424242424242424242
diff --git a/sbin/pfctl/tests/files/pf1055.ok b/sbin/pfctl/tests/files/pf1055.ok
new file mode 100644
index 000000000000..468e47012169
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1055.ok
@@ -0,0 +1 @@
+nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 203.0.113.0/24 source-hash 0x42424242424242424242424242424242
diff --git a/sbin/pfctl/tests/files/pf1056.in b/sbin/pfctl/tests/files/pf1056.in
new file mode 100644
index 000000000000..bd2af077fc3f
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1056.in
@@ -0,0 +1 @@
+pass in on vtnet0 inet6 from any to 64:ff9b::/96 af-to inet from 203.0.113.1 to 203.0.113.2
diff --git a/sbin/pfctl/tests/files/pf1056.ok b/sbin/pfctl/tests/files/pf1056.ok
new file mode 100644
index 000000000000..0397570dbce0
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1056.ok
@@ -0,0 +1 @@
+pass in on vtnet0 inet6 from any to 64:ff9b::/96 flags S/SA keep state af-to inet from 203.0.113.1 to 203.0.113.2
diff --git a/sbin/pfctl/tests/files/pf1057.in b/sbin/pfctl/tests/files/pf1057.in
new file mode 100644
index 000000000000..0e26976e5a0d
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1057.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> vlan1057
diff --git a/sbin/pfctl/tests/files/pf1057.ok b/sbin/pfctl/tests/files/pf1057.ok
new file mode 100644
index 000000000000..7626951e138c
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1057.ok
@@ -0,0 +1 @@
+nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 203.0.113.5
diff --git a/sbin/pfctl/tests/files/pf1058.in b/sbin/pfctl/tests/files/pf1058.in
new file mode 100644
index 000000000000..27c0ef1d69b3
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1058.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> { 203.0.113.1 vlan1058 }
diff --git a/sbin/pfctl/tests/files/pf1058.ok b/sbin/pfctl/tests/files/pf1058.ok
new file mode 100644
index 000000000000..b1d2b07a58b4
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1058.ok
@@ -0,0 +1 @@
+nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> { 203.0.113.1, 203.0.113.5 } round-robin
diff --git a/sbin/pfctl/tests/files/pf1059.in b/sbin/pfctl/tests/files/pf1059.in
new file mode 100644
index 000000000000..92ed5c50656b
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1059.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> (vlan1059)
diff --git a/sbin/pfctl/tests/files/pf1059.ok b/sbin/pfctl/tests/files/pf1059.ok
new file mode 100644
index 000000000000..6b028f18ee60
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1059.ok
@@ -0,0 +1 @@
+nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> (vlan1059) round-robin
diff --git a/sbin/pfctl/tests/files/pf1060.in b/sbin/pfctl/tests/files/pf1060.in
new file mode 100644
index 000000000000..85cdd19f2897
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1060.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> { 203.0.113.0 (vlan1060) }
diff --git a/sbin/pfctl/tests/files/pf1060.ok b/sbin/pfctl/tests/files/pf1060.ok
new file mode 100644
index 000000000000..3364b3cbdcc5
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1060.ok
@@ -0,0 +1 @@
+nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> { 203.0.113.0, (vlan1060) } round-robin
diff --git a/sbin/pfctl/tests/files/pf1061.in b/sbin/pfctl/tests/files/pf1061.in
new file mode 100644
index 000000000000..32eb8272db8b
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1061.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 2001:db8::1 to 2001:db8::2 -> vlan1061:0
diff --git a/sbin/pfctl/tests/files/pf1061.ok b/sbin/pfctl/tests/files/pf1061.ok
new file mode 100644
index 000000000000..d2e6d969cb11
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1061.ok
@@ -0,0 +1 @@
+nat on vtnet0 inet6 proto tcp from 2001:db8::1 to 2001:db8::2 -> 2001:db8::cb00:7105
diff --git a/sbin/pfctl/tests/files/pf1062.in b/sbin/pfctl/tests/files/pf1062.in
new file mode 100644
index 000000000000..4d6a0ecc2e92
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1062.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 2001:db8::1 to 2001:db8::2 -> { 2001:db8::3 vlan1062:0 }
diff --git a/sbin/pfctl/tests/files/pf1062.ok b/sbin/pfctl/tests/files/pf1062.ok
new file mode 100644
index 000000000000..cb5db62ded1d
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1062.ok
@@ -0,0 +1 @@
+nat on vtnet0 inet6 proto tcp from 2001:db8::1 to 2001:db8::2 -> { 2001:db8::3, 2001:db8::cb00:7105 } round-robin
diff --git a/sbin/pfctl/tests/files/pf1063.in b/sbin/pfctl/tests/files/pf1063.in
new file mode 100644
index 000000000000..3d164538640d
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1063.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 2001:db8::1 to 2001:db8::2 -> (vlan1063)
diff --git a/sbin/pfctl/tests/files/pf1063.ok b/sbin/pfctl/tests/files/pf1063.ok
new file mode 100644
index 000000000000..13189e00cc8a
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1063.ok
@@ -0,0 +1 @@
+nat on vtnet0 inet6 proto tcp from 2001:db8::1 to 2001:db8::2 -> (vlan1063) round-robin
diff --git a/sbin/pfctl/tests/files/pf1064.in b/sbin/pfctl/tests/files/pf1064.in
new file mode 100644
index 000000000000..78d04135154f
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1064.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 2001:db8::1 to 2001:db8::2 -> { fe80::2 (vlan1064) }
diff --git a/sbin/pfctl/tests/files/pf1064.ok b/sbin/pfctl/tests/files/pf1064.ok
new file mode 100644
index 000000000000..ed15d054ab34
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1064.ok
@@ -0,0 +1 @@
+nat on vtnet0 inet6 proto tcp from 2001:db8::1 to 2001:db8::2 -> { fe80::2, (vlan1064) } round-robin
diff --git a/sbin/pfctl/tests/files/pf1065.in b/sbin/pfctl/tests/files/pf1065.in
new file mode 100644
index 000000000000..690045befee6
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1065.in
@@ -0,0 +1 @@
+no nat on vtnet0 proto tcp from 2001:db8::1 to 2001:db8::2
diff --git a/sbin/pfctl/tests/files/pf1065.ok b/sbin/pfctl/tests/files/pf1065.ok
new file mode 100644
index 000000000000..651a2fa0ae09
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1065.ok
@@ -0,0 +1 @@
+no nat on vtnet0 inet6 proto tcp from 2001:db8::1 to 2001:db8::2
diff --git a/sbin/pfctl/tests/files/pf1066.in b/sbin/pfctl/tests/files/pf1066.in
new file mode 100644
index 000000000000..e81461c470ab
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1066.in
@@ -0,0 +1 @@
+no rdr on vtnet0 proto tcp from 2001:db8::1 to 2001:db8::2
diff --git a/sbin/pfctl/tests/files/pf1066.ok b/sbin/pfctl/tests/files/pf1066.ok
new file mode 100644
index 000000000000..5ff596fa0158
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1066.ok
@@ -0,0 +1 @@
+no rdr on vtnet0 inet6 proto tcp from 2001:db8::1 to 2001:db8::2
diff --git a/sbin/pfctl/tests/files/pf1067.fail b/sbin/pfctl/tests/files/pf1067.fail
new file mode 100644
index 000000000000..23ac1daad64f
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1067.fail
@@ -0,0 +1 @@
+route-to, reply-to and dup-to are not supported on block rules
diff --git a/sbin/pfctl/tests/files/pf1067.in b/sbin/pfctl/tests/files/pf1067.in
new file mode 100644
index 000000000000..47f3bf6285dd
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1067.in
@@ -0,0 +1 @@
+block in route-to (if0 127.0.0.1/8)
diff --git a/sbin/pfctl/tests/files/pf1068.in b/sbin/pfctl/tests/files/pf1068.in
new file mode 100644
index 000000000000..993cfa37f8f9
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1068.in
@@ -0,0 +1 @@
+pass in proto icmp max-pkt-rate 100/10
diff --git a/sbin/pfctl/tests/files/pf1068.ok b/sbin/pfctl/tests/files/pf1068.ok
new file mode 100644
index 000000000000..bd36043207f9
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1068.ok
@@ -0,0 +1 @@
+pass in proto icmp all max-pkt-rate 100/10 keep state
diff --git a/sbin/pfctl/tests/files/pf1069.in b/sbin/pfctl/tests/files/pf1069.in
new file mode 100644
index 000000000000..3a69158fff7e
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1069.in
@@ -0,0 +1 @@
+pass in proto icmp max-pkt-size 128
diff --git a/sbin/pfctl/tests/files/pf1069.ok b/sbin/pfctl/tests/files/pf1069.ok
new file mode 100644
index 000000000000..b79228266156
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1069.ok
@@ -0,0 +1 @@
+pass in proto icmp all max-pkt-size 128 keep state
diff --git a/sbin/pfctl/tests/files/pf1070.fail b/sbin/pfctl/tests/files/pf1070.fail
new file mode 100644
index 000000000000..60b56d9da2b9
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1070.fail
@@ -0,0 +1 @@
+pf1070.include:2: syntax error
diff --git a/sbin/pfctl/tests/files/pf1070.in b/sbin/pfctl/tests/files/pf1070.in
new file mode 100644
index 000000000000..42b874d4d6f4
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1070.in
@@ -0,0 +1,2 @@
+pass in
+include pf1070.include
diff --git a/sbin/pfctl/tests/files/pf1070.include b/sbin/pfctl/tests/files/pf1070.include
new file mode 100644
index 000000000000..09c3755dbe28
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1070.include
@@ -0,0 +1,2 @@
+block out
+invalidline
diff --git a/sbin/pfctl/tests/files/pf1071.in b/sbin/pfctl/tests/files/pf1071.in
new file mode 100644
index 000000000000..9e6c2abc0621
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1071.in
@@ -0,0 +1 @@
+pass inet from (lo0)/24
diff --git a/sbin/pfctl/tests/files/pf1071.ok b/sbin/pfctl/tests/files/pf1071.ok
new file mode 100644
index 000000000000..409b5dc4b068
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1071.ok
@@ -0,0 +1 @@
+pass inet from (lo0)/24 to any flags S/SA keep state
diff --git a/sbin/pfctl/tests/files/pf1072.fail b/sbin/pfctl/tests/files/pf1072.fail
new file mode 100644
index 000000000000..06ef5ae457e5
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1072.fail
@@ -0,0 +1 @@
+invalid port range
diff --git a/sbin/pfctl/tests/files/pf1072.in b/sbin/pfctl/tests/files/pf1072.in
new file mode 100644
index 000000000000..e09e92388ce1
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1072.in
@@ -0,0 +1 @@
+pass in proto tcp from any port 500:100 to any
diff --git a/sbin/pfctl/tests/files/pf1073.in b/sbin/pfctl/tests/files/pf1073.in
new file mode 100644
index 000000000000..477995893ac3
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1073.in
@@ -0,0 +1 @@
+pass in on vtnet0 route-to ( vtnet1 2001:db8::1 ) prefer-ipv6-nexthop inet
diff --git a/sbin/pfctl/tests/files/pf1073.ok b/sbin/pfctl/tests/files/pf1073.ok
new file mode 100644
index 000000000000..f34867508c75
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1073.ok
@@ -0,0 +1 @@
+pass in on vtnet0 route-to (vtnet1 2001:db8::1) prefer-ipv6-nexthop inet all flags S/SA keep state
diff --git a/sbin/pfctl/tests/files/pf1074.fail b/sbin/pfctl/tests/files/pf1074.fail
new file mode 100644
index 000000000000..afe8ee3c458f
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1074.fail
@@ -0,0 +1 @@
+no routing address with matching address family found.
diff --git a/sbin/pfctl/tests/files/pf1074.in b/sbin/pfctl/tests/files/pf1074.in
new file mode 100644
index 000000000000..5d285bc5d6e8
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1074.in
@@ -0,0 +1 @@
+pass in on vtnet0 route-to ( vtnet1 2001:db8::1 ) inet
diff --git a/sbin/pfctl/tests/files/pf1075.in b/sbin/pfctl/tests/files/pf1075.in
new file mode 100644
index 000000000000..835a31a25c6a
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1075.in
@@ -0,0 +1 @@
+pass inet from (lo0)/24 once
diff --git a/sbin/pfctl/tests/files/pf1075.ok b/sbin/pfctl/tests/files/pf1075.ok
new file mode 100644
index 000000000000..2369c9410cda
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1075.ok
@@ -0,0 +1 @@
+pass inet from (lo0)/24 to any flags S/SA keep state once
diff --git a/sbin/pfctl/tests/macro.sh b/sbin/pfctl/tests/macro.sh
index 9c48dbbc69f0..071c6cb4f426 100755
--- a/sbin/pfctl/tests/macro.sh
+++ b/sbin/pfctl/tests/macro.sh
@@ -3,6 +3,7 @@ atf_test_case "space" cleanup
space_head()
{
atf_set descr "Test macros with spaces"
+ atf_set require.kmods "pf"
}
space_body()
diff --git a/sbin/pfctl/tests/pfctl_test.c b/sbin/pfctl/tests/pfctl_test.c
index 5a98ad9f0d5f..5f0aa7826bb4 100644
--- a/sbin/pfctl/tests/pfctl_test.c
+++ b/sbin/pfctl/tests/pfctl_test.c
@@ -30,7 +30,7 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#include <sys/cdefs.h>
+
#include <sys/types.h>
#include <sys/param.h>
#include <err.h>
@@ -65,24 +65,6 @@
* Copied from OpenBSD.
*/
-static bool
-check_pf_module_available(void)
-{
- int modid;
- struct module_stat stat;
-
- if ((modid = modfind("pf")) < 0) {
- warn("pf module not found");
- return false;
- }
- stat.version = sizeof(struct module_stat);
- if (modstat(modid, &stat) < 0) {
- warn("can't stat pf module id %d", modid);
- return false;
- }
- return (true);
-}
-
extern char **environ;
static struct sbuf *
@@ -119,27 +101,14 @@ read_file(const char *filename)
}
static void
-run_pfctl_test(const char *input_path, const char *expected_path,
- const atf_tc_t *tc)
+run_command_pipe(const char *argv[], struct sbuf **output)
{
- int status;
+ posix_spawn_file_actions_t action;
pid_t pid;
int pipefds[2];
- char input_files_path[PATH_MAX];
- struct sbuf *expected_output;
- struct sbuf *real_output;
- posix_spawn_file_actions_t action;
-
- if (!check_pf_module_available())
- atf_tc_skip("pf(4) is not loaded");
-
- /* The test inputs need to be able to use relative includes. */
- snprintf(input_files_path, sizeof(input_files_path), "%s/files",
- atf_tc_get_config_var(tc, "srcdir"));
- ATF_REQUIRE_ERRNO(0, chdir(input_files_path) == 0);
+ int status;
ATF_REQUIRE_ERRNO(0, pipe(pipefds) == 0);
- expected_output = read_file(expected_path);
posix_spawn_file_actions_init(&action);
posix_spawn_file_actions_addclose(&action, STDIN_FILENO);
@@ -147,27 +116,115 @@ run_pfctl_test(const char *input_path, const char *expected_path,
posix_spawn_file_actions_adddup2(&action, pipefds[0], STDOUT_FILENO);
posix_spawn_file_actions_adddup2(&action, pipefds[0], STDERR_FILENO);
- const char *argv[] = { "pfctl", "-o", "none", "-nvf", input_path,
- NULL };
- printf("Running %s %s %s %s %s\n", argv[0], argv[1], argv[2], argv[3],
- argv[4]);
+ printf("Running ");
+ for (int i=0; argv[i] != NULL; i++)
+ printf("%s ", argv[i]);
+ printf("\n");
+
status = posix_spawnp(
- &pid, "pfctl", &action, NULL, __DECONST(char **, argv), environ);
+ &pid, argv[0], &action, NULL, __DECONST(char **, argv), environ);
ATF_REQUIRE_EQ_MSG(
status, 0, "posix_spawn failed: %s", strerror(errno));
posix_spawn_file_actions_destroy(&action);
close(pipefds[0]);
- real_output = read_fd(pipefds[1], 0);
- printf("---\n%s---\n", sbuf_data(real_output));
+ (*output) = read_fd(pipefds[1], 0);
+ printf("---\n%s---\n", sbuf_data(*output));
ATF_REQUIRE_EQ(waitpid(pid, &status, 0), pid);
ATF_REQUIRE_MSG(WIFEXITED(status),
- "pfctl returned non-zero! Output:\n %s", sbuf_data(real_output));
+ "%s returned non-zero! Output:\n %s", argv[0], sbuf_data(*output));
+ close(pipefds[1]);
+}
+
+static void
+run_command(const char *argv[])
+{
+ posix_spawn_file_actions_t action;
+ pid_t pid;
+ int status;
+
+ posix_spawn_file_actions_init(&action);
+ posix_spawn_file_actions_addopen(&action, STDOUT_FILENO, "/dev/null", O_WRONLY, 0);
+ posix_spawn_file_actions_addopen(&action, STDERR_FILENO, "/dev/null", O_WRONLY, 0);
+ posix_spawn_file_actions_addopen(&action, STDIN_FILENO, "/dev/zero", O_RDONLY, 0);
+
+ printf("Running ");
+ for (int i=0; argv[i] != NULL; i++)
+ printf("%s ", argv[i]);
+ printf("\n");
+
+ status = posix_spawnp(
+ &pid, argv[0], &action, NULL, __DECONST(char **, argv), environ);
+ posix_spawn_file_actions_destroy(&action);
+ waitpid(pid, &status, 0);
+}
+
+static void
+run_pfctl_test(const char *input_path, const char *output_path,
+ const atf_tc_t *tc, bool test_failure)
+{
+ char input_files_path[PATH_MAX];
+ struct sbuf *expected_output;
+ struct sbuf *real_output;
+
+ /* The test inputs need to be able to use relative includes. */
+ snprintf(input_files_path, sizeof(input_files_path), "%s/files",
+ atf_tc_get_config_var(tc, "srcdir"));
+ ATF_REQUIRE_ERRNO(0, chdir(input_files_path) == 0);
+ expected_output = read_file(output_path);
+
+ const char *argv[] = { "pfctl", "-o", "none", "-nvf", input_path,
+ NULL };
+ run_command_pipe(argv, &real_output);
+
+ if (test_failure) {
+ /*
+ * Error output contains additional strings like line number
+ * or "skipping rule due to errors", so use regexp to see
+ * if the expected error message is there somewhere.
+ */
+ ATF_CHECK_MATCH(sbuf_data(expected_output), sbuf_data(real_output));
+ sbuf_delete(expected_output);
+ } else {
+ ATF_CHECK_STREQ(sbuf_data(expected_output), sbuf_data(real_output));
+ sbuf_delete(expected_output);
+ }
- ATF_CHECK_STREQ(sbuf_data(expected_output), sbuf_data(real_output));
- sbuf_delete(expected_output);
sbuf_delete(real_output);
- close(pipefds[1]);
+}
+
+static void
+do_pf_test_iface_create(const char *number)
+{
+ struct sbuf *ifconfig_output;
+ char ifname[16] = {0};
+
+ snprintf(ifname, sizeof(ifname), "vlan%s", number);
+ const char *argv[] = { "ifconfig", ifname, "create", NULL};
+ run_command_pipe(argv, &ifconfig_output);
+ sbuf_delete(ifconfig_output);
+
+ const char *argv_inet[] = { "ifconfig", ifname, "inet", "203.0.113.5/30", NULL};
+ run_command_pipe(argv_inet, &ifconfig_output);
+ sbuf_delete(ifconfig_output);
+
+ const char *argv_inet6[] = { "ifconfig", ifname, "inet6", "2001:db8::203.0.113.5/126", NULL};
+ run_command_pipe(argv_inet6, &ifconfig_output);
+ sbuf_delete(ifconfig_output);
+
+ const char *argv_show[] = { "ifconfig", ifname, NULL};
+ run_command_pipe(argv_show, &ifconfig_output);
+ sbuf_delete(ifconfig_output);
+}
+
+static void
+do_pf_test_iface_remove(const char *number)
+{
+ char ifname[16] = {0};
+
+ snprintf(ifname, sizeof(ifname), "vlan%s", number);
+ const char *argv[] = { "ifconfig", ifname, "destroy", NULL};
+ run_command(argv);
}
static void
@@ -179,7 +236,21 @@ do_pf_test(const char *number, const atf_tc_t *tc)
atf_tc_get_config_var(tc, "srcdir"), number);
asprintf(&expected_path, "%s/files/pf%s.ok",
atf_tc_get_config_var(tc, "srcdir"), number);
- run_pfctl_test(input_path, expected_path, tc);
+ run_pfctl_test(input_path, expected_path, tc, false);
+ free(input_path);
+ free(expected_path);
+}
+
+static void
+do_pf_test_fail(const char *number, const atf_tc_t *tc)
+{
+ char *input_path;
+ char *expected_path;
+ asprintf(&input_path, "%s/files/pf%s.in",
+ atf_tc_get_config_var(tc, "srcdir"), number);
+ asprintf(&expected_path, "%s/files/pf%s.fail",
+ atf_tc_get_config_var(tc, "srcdir"), number);
+ run_pfctl_test(input_path, expected_path, tc, true);
free(input_path);
free(expected_path);
}
@@ -190,15 +261,17 @@ do_selfpf_test(const char *number, const atf_tc_t *tc)
char *expected_path;
asprintf(&expected_path, "%s/files/pf%s.ok",
atf_tc_get_config_var(tc, "srcdir"), number);
- run_pfctl_test(expected_path, expected_path, tc);
+ run_pfctl_test(expected_path, expected_path, tc, false);
free(expected_path);
}
+/* Standard tests perform the normal test and then the selfpf test */
#define PFCTL_TEST(number, descr) \
ATF_TC(pf##number); \
ATF_TC_HEAD(pf##number, tc) \
{ \
atf_tc_set_md_var(tc, "descr", descr); \
+ atf_tc_set_md_var(tc, "require.kmods", "pf"); \
} \
ATF_TC_BODY(pf##number, tc) \
{ \
@@ -208,21 +281,61 @@ do_selfpf_test(const char *number, const atf_tc_t *tc)
ATF_TC_HEAD(selfpf##number, tc) \
{ \
atf_tc_set_md_var(tc, "descr", "Self " descr); \
+ atf_tc_set_md_var(tc, "require.kmods", "pf"); \
} \
ATF_TC_BODY(selfpf##number, tc) \
{ \
do_selfpf_test(#number, tc); \
}
+/* Tests for failure perform only the normal test */
+#define PFCTL_TEST_FAIL(number, descr) \
+ ATF_TC(pf##number); \
+ ATF_TC_HEAD(pf##number, tc) \
+ { \
+ atf_tc_set_md_var(tc, "descr", descr); \
+ atf_tc_set_md_var(tc, "require.kmods", "pf"); \
+ } \
+ ATF_TC_BODY(pf##number, tc) \
+ { \
+ do_pf_test_fail(#number, tc); \
+ }
+/* Tests with interface perform only the normal test */
+#define PFCTL_TEST_IFACE(number, descr) \
+ ATF_TC_WITH_CLEANUP(pf##number); \
+ ATF_TC_HEAD(pf##number, tc) \
+ { \
+ atf_tc_set_md_var(tc, "descr", descr); \
+ atf_tc_set_md_var(tc, "execenv", "jail"); \
+ atf_tc_set_md_var(tc, "execenv.jail.params", "vnet"); \
+ atf_tc_set_md_var(tc, "require.kmods", "pf"); \
+ } \
+ ATF_TC_BODY(pf##number, tc) \
+ { \
+ do_pf_test_iface_create(#number); \
+ do_pf_test(#number, tc); \
+ } \
+ ATF_TC_CLEANUP(pf##number, tc) \
+ { \
+ do_pf_test_iface_remove(#number); \
+ }
#include "pfctl_test_list.inc"
#undef PFCTL_TEST
+#undef PFCTL_TEST_FAIL
+#undef PFCTL_TEST_IFACE
ATF_TP_ADD_TCS(tp)
{
#define PFCTL_TEST(number, descr) \
ATF_TP_ADD_TC(tp, pf##number); \
ATF_TP_ADD_TC(tp, selfpf##number);
+#define PFCTL_TEST_FAIL(number, descr) \
+ ATF_TP_ADD_TC(tp, pf##number);
+#define PFCTL_TEST_IFACE(number, descr) \
+ ATF_TP_ADD_TC(tp, pf##number);
#include "pfctl_test_list.inc"
#undef PFCTL_TEST
+#undef PFCTL_TEST_FAIL
+#undef PFCTL_TEST_IFACE
return atf_no_error();
}
diff --git a/sbin/pfctl/tests/pfctl_test_list.inc b/sbin/pfctl/tests/pfctl_test_list.inc
index 98ea3257d492..9dd4a590ad8f 100644
--- a/sbin/pfctl/tests/pfctl_test_list.inc
+++ b/sbin/pfctl/tests/pfctl_test_list.inc
@@ -126,3 +126,61 @@ PFCTL_TEST(1014, "Ethernet rule with one label")
PFCTL_TEST(1015, "Ethernet rule with several labels")
PFCTL_TEST(1016, "Ethernet rule with ridentifier and one label")
PFCTL_TEST(1017, "Ethernet rule with ridentifier and several labels")
+PFCTL_TEST(1018, "Test dynamic address mask")
+PFCTL_TEST(1019, "Test pflow option")
+PFCTL_TEST(1020, "Test hashmark and semicolon comment")
+PFCTL_TEST(1021, "Endpoint-independent")
+PFCTL_TEST(1022, "Test received-on")
+PFCTL_TEST(1023, "Test match log(matches)")
+PFCTL_TEST(1024, "nat64")
+PFCTL_TEST(1025, "nat64 with implicit address family")
+PFCTL_TEST(1026, "nat64 with route-to")
+PFCTL_TEST(1027, "nat64 with reply-to")
+PFCTL_TEST(1028, "RDR pool: For RDR rules no port specified means keep port")
+PFCTL_TEST(1029, "RDR pool: A single port is shown")
+PFCTL_TEST(1030, "RDR pool: The default values are shown for RDR rules")
+PFCTL_TEST(1031, "RDR pool: Multiple ports redirected to a single port")
+PFCTL_TEST(1032, "RDR pool: Multiple ports redirected to a port range")
+PFCTL_TEST_FAIL(1033, "RDR pool: static-port can't be used with RDR rules")
+PFCTL_TEST_FAIL(1034, "RDR pool: MAP-E port can't be used with RDR rules")
+PFCTL_TEST(1035, "NAT pool: For NAT rules no port specified means default values")
+PFCTL_TEST(1036, "NAT pool: Default port numbers are not shown, even if explicitly applied")
+PFCTL_TEST(1037, "NAT pool: Single port")
+PFCTL_TEST(1038, "NAT pool: Two ports")
+PFCTL_TEST(1039, "NAT pool: Static port")
+PFCTL_TEST_FAIL(1040, "NAT pool: Static port can't be used with port numbers")
+PFCTL_TEST(1041, "NAT pool: MAP-E is displayed using decimal system")
+PFCTL_TEST_FAIL(1042, "NAT pool: MAP-E port can't be used with static port")
+PFCTL_TEST_FAIL(1043, "NAT pool: MAP-E port can't be used with port numbers")
+PFCTL_TEST(1044, "pool: sticky-address is applied on top of round-robin")
+PFCTL_TEST(1045, "pool: bitmask is allowed for prefixes")
+PFCTL_TEST_FAIL(1046, "pool: bitmask is not allowed for tables")
+PFCTL_TEST_FAIL(1047, "pool: bitmask is not allowed for interfaces in brackets")
+PFCTL_TEST(1048, "pool: random is allowed for prefixes")
+PFCTL_TEST(1049, "pool: round-robin is not set for a single host, even if it looks like a table")
+PFCTL_TEST(1050, "pool: round-robin is set automatically for tables")
+PFCTL_TEST(1051, "pool: round-robin is set automatically for multiple targets")
+PFCTL_TEST(1052, "pool: hosts and table are allowed, round-robin is automatically set")
+PFCTL_TEST(1053, "pool: round-robin is not set automatically for prefixes")
+PFCTL_TEST(1054, "pool: round-robin is allowed for prefixes")
+PFCTL_TEST(1055, "pool: source hash")
+PFCTL_TEST(1056, "af-to: from and to")
+PFCTL_TEST_IFACE(1057, "Interface translation: IPv4 rule, interface without brackets is translated")
+PFCTL_TEST_IFACE(1058, "Interface translation: IPv4 rule, interface without brackets is translated, extra host, round-robin is applied")
+PFCTL_TEST_IFACE(1059, "Interface translation: IPv4 rule, interface with brackets is not translated, round-robin is applied")
+PFCTL_TEST_IFACE(1060, "Interface translation: IPv4 rule, interface with brackets is not translated, extra host, round-robin is applied")
+PFCTL_TEST_IFACE(1061, "Interface translation: IPv6 rule, interface without brackets is translated")
+PFCTL_TEST_IFACE(1062, "Interface translation: IPv6 rule, interface without brackets is translated, extra host, round-robin is applied")
+PFCTL_TEST_IFACE(1063, "Interface translation: IPv6 rule, interface with brackets is not translated, round-robin is applied")
+PFCTL_TEST_IFACE(1064, "Interface translation: IPv6 rule, interface with brackets is not translated, extra host, round robin is applied")
+PFCTL_TEST(1065, "no nat")
+PFCTL_TEST(1066, "no rdr")
+PFCTL_TEST_FAIL(1067, "route-to can't be used on block rules")
+PFCTL_TEST(1068, "max-pkt-rate")
+PFCTL_TEST(1069, "max-pkt-size")
+PFCTL_TEST_FAIL(1070, "include line number")
+PFCTL_TEST(1071, "mask length on (lo0)")
+PFCTL_TEST_FAIL(1072, "Invalid port range")
+PFCTL_TEST(1073, "Filter AF different than route-to AF, with prefer-ipv6-nexthop")
+PFCTL_TEST_FAIL(1074, "Filter AF different than route-to AF, without prefer-ipv6-nexthop")
+PFCTL_TEST(1075, "One shot rule")