summaryrefslogtreecommitdiff
path: root/sbin/pfctl
diff options
context:
space:
mode:
Diffstat (limited to 'sbin/pfctl')
-rw-r--r--sbin/pfctl/parse.y6203
-rw-r--r--sbin/pfctl/pfctl_parser.c6
2 files changed, 4840 insertions, 1369 deletions
diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y
index 9e0da745497..545f8185633 100644
--- a/sbin/pfctl/parse.y
+++ b/sbin/pfctl/parse.y
@@ -1,41 +1,34 @@
-/* $OpenBSD: parse.y,v 1.464 2004/12/07 09:36:16 deraadt Exp $ */
+/* $OpenBSD: parse.y,v 1.465 2004/12/07 09:38:04 deraadt Exp $ */
/*
- * Copyright (c) 2001 Daniel Hartmeier
- * Copyright (c) 2002,2003 Henning Brauer
- * All rights reserved.
+ * Copyright (c) 2001 Markus Friedl. All rights reserved.
+ * Copyright (c) 2001 Daniel Hartmeier. All rights reserved.
+ * Copyright (c) 2001 Theo de Raadt. All rights reserved.
+ * Copyright (c) 2002,2003 Henning Brauer. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
*
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
- * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (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/types.h>
-#include <sys/ioctl.h>
#include <sys/socket.h>
-#include <sys/param.h>
-#include <sys/proc.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
@@ -44,1552 +37,5032 @@
#include <netinet/icmp6.h>
#include <net/pfvar.h>
#include <arpa/inet.h>
+#include <altq/altq.h>
+#include <altq/altq_cbq.h>
+#include <altq/altq_priq.h>
+#include <altq/altq_hfsc.h>
#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
#include <netdb.h>
#include <stdarg.h>
#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
#include <err.h>
-#include <ifaddrs.h>
+#include <limits.h>
+#include <pwd.h>
+#include <grp.h>
+#include <md5.h>
#include "pfctl_parser.h"
#include "pfctl.h"
-void print_op (u_int8_t, const char *, const char *);
-void print_port (u_int8_t, u_int16_t, u_int16_t, const char *);
-void print_ugid (u_int8_t, unsigned, unsigned, const char *, unsigned);
-void print_flags (u_int8_t);
-void print_fromto(struct pf_rule_addr *, pf_osfp_t,
- struct pf_rule_addr *, u_int8_t, u_int8_t, int);
-int ifa_skip_if(const char *filter, struct node_host *p);
-
-struct node_host *host_if(const char *, int);
-struct node_host *host_v4(const char *, int);
-struct node_host *host_v6(const char *, int);
-struct node_host *host_dns(const char *, int, int);
-
-const char *tcpflags = "FSRPAUEW";
-
-static const struct icmptypeent icmp_type[] = {
- { "echoreq", ICMP_ECHO },
- { "echorep", ICMP_ECHOREPLY },
- { "unreach", ICMP_UNREACH },
- { "squench", ICMP_SOURCEQUENCH },
- { "redir", ICMP_REDIRECT },
- { "althost", ICMP_ALTHOSTADDR },
- { "routeradv", ICMP_ROUTERADVERT },
- { "routersol", ICMP_ROUTERSOLICIT },
- { "timex", ICMP_TIMXCEED },
- { "paramprob", ICMP_PARAMPROB },
- { "timereq", ICMP_TSTAMP },
- { "timerep", ICMP_TSTAMPREPLY },
- { "inforeq", ICMP_IREQ },
- { "inforep", ICMP_IREQREPLY },
- { "maskreq", ICMP_MASKREQ },
- { "maskrep", ICMP_MASKREPLY },
- { "trace", ICMP_TRACEROUTE },
- { "dataconv", ICMP_DATACONVERR },
- { "mobredir", ICMP_MOBILE_REDIRECT },
- { "ipv6-where", ICMP_IPV6_WHEREAREYOU },
- { "ipv6-here", ICMP_IPV6_IAMHERE },
- { "mobregreq", ICMP_MOBILE_REGREQUEST },
- { "mobregrep", ICMP_MOBILE_REGREPLY },
- { "skip", ICMP_SKIP },
- { "photuris", ICMP_PHOTURIS }
+static struct pfctl *pf = NULL;
+static FILE *fin = NULL;
+static int debug = 0;
+static int lineno = 1;
+static int errors = 0;
+static int rulestate = 0;
+static u_int16_t returnicmpdefault =
+ (ICMP_UNREACH << 8) | ICMP_UNREACH_PORT;
+static u_int16_t returnicmp6default =
+ (ICMP6_DST_UNREACH << 8) | ICMP6_DST_UNREACH_NOPORT;
+static int blockpolicy = PFRULE_DROP;
+static int require_order = 1;
+static int default_statelock;
+
+enum {
+ PFCTL_STATE_NONE,
+ PFCTL_STATE_OPTION,
+ PFCTL_STATE_SCRUB,
+ PFCTL_STATE_QUEUE,
+ PFCTL_STATE_NAT,
+ PFCTL_STATE_FILTER
};
-static const struct icmptypeent icmp6_type[] = {
- { "unreach", ICMP6_DST_UNREACH },
- { "toobig", ICMP6_PACKET_TOO_BIG },
- { "timex", ICMP6_TIME_EXCEEDED },
- { "paramprob", ICMP6_PARAM_PROB },
- { "echoreq", ICMP6_ECHO_REQUEST },
- { "echorep", ICMP6_ECHO_REPLY },
- { "groupqry", ICMP6_MEMBERSHIP_QUERY },
- { "listqry", MLD_LISTENER_QUERY },
- { "grouprep", ICMP6_MEMBERSHIP_REPORT },
- { "listenrep", MLD_LISTENER_REPORT },
- { "groupterm", ICMP6_MEMBERSHIP_REDUCTION },
- { "listendone", MLD_LISTENER_DONE },
- { "routersol", ND_ROUTER_SOLICIT },
- { "routeradv", ND_ROUTER_ADVERT },
- { "neighbrsol", ND_NEIGHBOR_SOLICIT },
- { "neighbradv", ND_NEIGHBOR_ADVERT },
- { "redir", ND_REDIRECT },
- { "routrrenum", ICMP6_ROUTER_RENUMBERING },
- { "wrureq", ICMP6_WRUREQUEST },
- { "wrurep", ICMP6_WRUREPLY },
- { "fqdnreq", ICMP6_FQDN_QUERY },
- { "fqdnrep", ICMP6_FQDN_REPLY },
- { "niqry", ICMP6_NI_QUERY },
- { "nirep", ICMP6_NI_REPLY },
- { "mtraceresp", MLD_MTRACE_RESP },
- { "mtrace", MLD_MTRACE }
+struct node_proto {
+ u_int8_t proto;
+ struct node_proto *next;
+ struct node_proto *tail;
};
-static const struct icmpcodeent icmp_code[] = {
- { "net-unr", ICMP_UNREACH, ICMP_UNREACH_NET },
- { "host-unr", ICMP_UNREACH, ICMP_UNREACH_HOST },
- { "proto-unr", ICMP_UNREACH, ICMP_UNREACH_PROTOCOL },
- { "port-unr", ICMP_UNREACH, ICMP_UNREACH_PORT },
- { "needfrag", ICMP_UNREACH, ICMP_UNREACH_NEEDFRAG },
- { "srcfail", ICMP_UNREACH, ICMP_UNREACH_SRCFAIL },
- { "net-unk", ICMP_UNREACH, ICMP_UNREACH_NET_UNKNOWN },
- { "host-unk", ICMP_UNREACH, ICMP_UNREACH_HOST_UNKNOWN },
- { "isolate", ICMP_UNREACH, ICMP_UNREACH_ISOLATED },
- { "net-prohib", ICMP_UNREACH, ICMP_UNREACH_NET_PROHIB },
- { "host-prohib", ICMP_UNREACH, ICMP_UNREACH_HOST_PROHIB },
- { "net-tos", ICMP_UNREACH, ICMP_UNREACH_TOSNET },
- { "host-tos", ICMP_UNREACH, ICMP_UNREACH_TOSHOST },
- { "filter-prohib", ICMP_UNREACH, ICMP_UNREACH_FILTER_PROHIB },
- { "host-preced", ICMP_UNREACH, ICMP_UNREACH_HOST_PRECEDENCE },
- { "cutoff-preced", ICMP_UNREACH, ICMP_UNREACH_PRECEDENCE_CUTOFF },
- { "redir-net", ICMP_REDIRECT, ICMP_REDIRECT_NET },
- { "redir-host", ICMP_REDIRECT, ICMP_REDIRECT_HOST },
- { "redir-tos-net", ICMP_REDIRECT, ICMP_REDIRECT_TOSNET },
- { "redir-tos-host", ICMP_REDIRECT, ICMP_REDIRECT_TOSHOST },
- { "normal-adv", ICMP_ROUTERADVERT, ICMP_ROUTERADVERT_NORMAL },
- { "common-adv", ICMP_ROUTERADVERT, ICMP_ROUTERADVERT_NOROUTE_COMMON },
- { "transit", ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS },
- { "reassemb", ICMP_TIMXCEED, ICMP_TIMXCEED_REASS },
- { "badhead", ICMP_PARAMPROB, ICMP_PARAMPROB_ERRATPTR },
- { "optmiss", ICMP_PARAMPROB, ICMP_PARAMPROB_OPTABSENT },
- { "badlen", ICMP_PARAMPROB, ICMP_PARAMPROB_LENGTH },
- { "unknown-ind", ICMP_PHOTURIS, ICMP_PHOTURIS_UNKNOWN_INDEX },
- { "auth-fail", ICMP_PHOTURIS, ICMP_PHOTURIS_AUTH_FAILED },
- { "decrypt-fail", ICMP_PHOTURIS, ICMP_PHOTURIS_DECRYPT_FAILED }
+struct node_port {
+ u_int16_t port[2];
+ u_int8_t op;
+ struct node_port *next;
+ struct node_port *tail;
};
-static const struct icmpcodeent icmp6_code[] = {
- { "admin-unr", ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADMIN },
- { "noroute-unr", ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOROUTE },
- { "notnbr-unr", ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOTNEIGHBOR },
- { "beyond-unr", ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_BEYONDSCOPE },
- { "addr-unr", ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADDR },
- { "port-unr", ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOPORT },
- { "transit", ICMP6_TIME_EXCEEDED, ICMP6_TIME_EXCEED_TRANSIT },
- { "reassemb", ICMP6_TIME_EXCEEDED, ICMP6_TIME_EXCEED_REASSEMBLY },
- { "badhead", ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER },
- { "nxthdr", ICMP6_PARAM_PROB, ICMP6_PARAMPROB_NEXTHEADER },
- { "redironlink", ND_REDIRECT, ND_REDIRECT_ONLINK },
- { "redirrouter", ND_REDIRECT, ND_REDIRECT_ROUTER }
+struct node_uid {
+ uid_t uid[2];
+ u_int8_t op;
+ struct node_uid *next;
+ struct node_uid *tail;
};
-const struct pf_timeout pf_timeouts[] = {
- { "tcp.first", PFTM_TCP_FIRST_PACKET },
- { "tcp.opening", PFTM_TCP_OPENING },
- { "tcp.established", PFTM_TCP_ESTABLISHED },
- { "tcp.closing", PFTM_TCP_CLOSING },
- { "tcp.finwait", PFTM_TCP_FIN_WAIT },
- { "tcp.closed", PFTM_TCP_CLOSED },
- { "tcp.tsdiff", PFTM_TS_DIFF },
- { "udp.first", PFTM_UDP_FIRST_PACKET },
- { "udp.single", PFTM_UDP_SINGLE },
- { "udp.multiple", PFTM_UDP_MULTIPLE },
- { "icmp.first", PFTM_ICMP_FIRST_PACKET },
- { "icmp.error", PFTM_ICMP_ERROR_REPLY },
- { "other.first", PFTM_OTHER_FIRST_PACKET },
- { "other.single", PFTM_OTHER_SINGLE },
- { "other.multiple", PFTM_OTHER_MULTIPLE },
- { "frag", PFTM_FRAG },
- { "interval", PFTM_INTERVAL },
- { "adaptive.start", PFTM_ADAPTIVE_START },
- { "adaptive.end", PFTM_ADAPTIVE_END },
- { "src.track", PFTM_SRC_NODE },
- { NULL, 0 }
+struct node_gid {
+ gid_t gid[2];
+ u_int8_t op;
+ struct node_gid *next;
+ struct node_gid *tail;
};
-const struct icmptypeent *
-geticmptypebynumber(u_int8_t type, sa_family_t af)
-{
- unsigned int i;
+struct node_icmp {
+ u_int8_t code;
+ u_int8_t type;
+ u_int8_t proto;
+ struct node_icmp *next;
+ struct node_icmp *tail;
+};
+
+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 };
+
+enum { PF_SRCTRACK_NONE, PF_SRCTRACK, PF_SRCTRACK_GLOBAL, PF_SRCTRACK_RULE };
+
+struct node_state_opt {
+ int type;
+ union {
+ u_int32_t max_states;
+ u_int32_t max_src_states;
+ u_int32_t max_src_conn;
+ struct {
+ u_int32_t limit;
+ u_int32_t seconds;
+
+ } max_src_conn_rate;
+ struct {
+ u_int8_t flush;
+ char tblname[PF_TABLE_NAME_SIZE];
+ } overload;
+ u_int32_t max_src_nodes;
+ u_int8_t src_track;
+ u_int32_t statelock;
+ struct {
+ int number;
+ u_int32_t seconds;
+ } timeout;
+ } data;
+ struct node_state_opt *next;
+ struct node_state_opt *tail;
+};
+
+struct peer {
+ struct node_host *host;
+ struct node_port *port;
+};
+
+struct node_queue {
+ char queue[PF_QNAME_SIZE];
+ char parent[PF_QNAME_SIZE];
+ char ifname[IFNAMSIZ];
+ int scheduler;
+ struct node_queue *next;
+ struct node_queue *tail;
+} *queues = NULL;
+
+struct node_qassign {
+ char *qname;
+ char *pqname;
+};
+
+struct filter_opts {
+ int marker;
+#define FOM_FLAGS 0x01
+#define FOM_ICMP 0x02
+#define FOM_TOS 0x04
+#define FOM_KEEP 0x08
+#define FOM_SRCTRACK 0x10
+ struct node_uid *uid;
+ struct node_gid *gid;
+ struct {
+ u_int8_t b1;
+ u_int8_t b2;
+ u_int16_t w;
+ u_int16_t w2;
+ } flags;
+ struct node_icmp *icmpspec;
+ u_int32_t tos;
+ u_int32_t prob;
+ struct {
+ int action;
+ struct node_state_opt *options;
+ } keep;
+ int fragment;
+ int allowopts;
+ char *label;
+ struct node_qassign queues;
+ char *tag;
+ char *match_tag;
+ u_int8_t match_tag_not;
+} filter_opts;
+
+struct antispoof_opts {
+ char *label;
+} antispoof_opts;
+
+struct scrub_opts {
+ int marker;
+#define SOM_MINTTL 0x01
+#define SOM_MAXMSS 0x02
+#define SOM_FRAGCACHE 0x04
+ int nodf;
+ int minttl;
+ int maxmss;
+ int fragcache;
+ int randomid;
+ int reassemble_tcp;
+} scrub_opts;
+
+struct queue_opts {
+ int marker;
+#define QOM_BWSPEC 0x01
+#define QOM_SCHEDULER 0x02
+#define QOM_PRIORITY 0x04
+#define QOM_TBRSIZE 0x08
+#define QOM_QLIMIT 0x10
+ struct node_queue_bw queue_bwspec;
+ struct node_queue_opt scheduler;
+ int priority;
+ int tbrsize;
+ int qlimit;
+} queue_opts;
+
+struct table_opts {
+ int flags;
+ int init_addr;
+ struct node_tinithead init_nodes;
+} table_opts;
+
+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;
+
+} pool_opts;
+
+
+struct node_hfsc_opts hfsc_opts;
+
+int yyerror(const char *, ...);
+int disallow_table(struct node_host *, const char *);
+int disallow_alias(struct node_host *, const char *);
+int rule_consistent(struct pf_rule *);
+int filter_consistent(struct pf_rule *);
+int nat_consistent(struct pf_rule *);
+int rdr_consistent(struct pf_rule *);
+int process_tabledef(char *, struct table_opts *);
+int yyparse(void);
+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, u_int8_t,
+ struct node_host *);
+void expand_label_port(const char *, char *, size_t, struct node_port *);
+void expand_label_proto(const char *, char *, size_t, u_int8_t);
+void expand_label_nr(const char *, char *, size_t);
+void expand_label(char *, size_t, const char *, u_int8_t, struct node_host *,
+ struct node_port *, struct node_host *, struct node_port *,
+ u_int8_t);
+void expand_rule(struct pf_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 expand_altq(struct pf_altq *, struct node_if *, struct node_queue *,
+ struct node_queue_bw bwspec, struct node_queue_opt *);
+int expand_queue(struct pf_altq *, struct node_if *, struct node_queue *,
+ struct node_queue_bw, struct node_queue_opt *);
+
+int check_rulestate(int);
+int kw_cmp(const void *, const void *);
+int lookup(char *);
+int lgetc(FILE *);
+int lungetc(int);
+int findeol(void);
+int yylex(void);
+int atoul(char *, u_long *);
+int getservice(char *);
+int rule_label(struct pf_rule *, char *);
+
+TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead);
+struct sym {
+ TAILQ_ENTRY(sym) entries;
+ int used;
+ int persist;
+ char *nam;
+ char *val;
+};
- if (af != AF_INET6) {
- for (i=0; i < (sizeof (icmp_type) / sizeof(icmp_type[0]));
- i++) {
- if (type == icmp_type[i].type)
- return (&icmp_type[i]);
+
+int symset(const char *, const char *, int);
+char *symget(const char *);
+
+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);
+
+TAILQ_HEAD(loadanchorshead, loadanchors)
+ loadanchorshead = TAILQ_HEAD_INITIALIZER(loadanchorshead);
+
+struct loadanchors {
+ TAILQ_ENTRY(loadanchors) entries;
+ char *anchorname;
+ char *filename;
+};
+
+typedef struct {
+ union {
+ u_int32_t number;
+ int i;
+ char *string;
+ struct {
+ u_int8_t b1;
+ u_int8_t b2;
+ u_int16_t w;
+ u_int16_t w2;
+ } b;
+ struct range {
+ int a;
+ int b;
+ int t;
+ } range;
+ struct node_if *interface;
+ struct node_proto *proto;
+ struct node_icmp *icmp;
+ struct node_host *host;
+ struct node_os *os;
+ struct node_port *port;
+ struct node_uid *uid;
+ struct node_gid *gid;
+ struct node_state_opt *state_opt;
+ struct peer peer;
+ struct {
+ struct peer src, dst;
+ struct node_os *src_os;
+ } fromto;
+ struct {
+ struct node_host *host;
+ 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 {
+ int action;
+ struct node_state_opt *options;
+ } keep_state;
+ struct {
+ u_int8_t log;
+ u_int8_t quick;
+ } logquick;
+ struct pf_poolhashkey *hashkey;
+ struct node_queue *queue;
+ struct node_queue_opt queue_options;
+ struct node_queue_bw queue_bwspec;
+ struct node_qassign qassign;
+ struct filter_opts filter_opts;
+ struct antispoof_opts antispoof_opts;
+ struct queue_opts queue_opts;
+ struct scrub_opts scrub_opts;
+ struct table_opts table_opts;
+ struct pool_opts pool_opts;
+ struct node_hfsc_opts hfsc_opts;
+ } v;
+ int lineno;
+} YYSTYPE;
+
+#define DYNIF_MULTIADDR(addr) ((addr).type == PF_ADDR_DYNIFTL && \
+ (!((addr).iflags & PFI_AFLAG_NOALIAS) || \
+ !isdigit((addr).v.ifname[strlen((addr).v.ifname)-1])))
+
+%}
+
+%token PASS BLOCK SCRUB RETURN IN OS OUT LOG LOGALL QUICK ON FROM TO FLAGS
+%token RETURNRST RETURNICMP RETURNICMP6 PROTO INET INET6 ALL ANY ICMPTYPE
+%token ICMP6TYPE CODE KEEP MODULATE STATE PORT RDR NAT BINAT ARROW NODF
+%token MINTTL ERROR ALLOWOPTS FASTROUTE FILENAME ROUTETO DUPTO REPLYTO NO LABEL
+%token NOROUTE FRAGMENT USER GROUP MAXMSS MAXIMUM TTL TOS DROP TABLE
+%token REASSEMBLE FRAGDROP FRAGCROP ANCHOR NATANCHOR RDRANCHOR BINATANCHOR
+%token SET OPTIMIZATION TIMEOUT LIMIT LOGINTERFACE BLOCKPOLICY RANDOMID
+%token REQUIREORDER SYNPROXY FINGERPRINTS NOSYNC DEBUG HOSTID
+%token ANTISPOOF FOR
+%token BITMASK RANDOM SOURCEHASH ROUNDROBIN STATICPORT PROBABILITY
+%token ALTQ CBQ PRIQ HFSC BANDWIDTH TBRSIZE LINKSHARE REALTIME UPPERLIMIT
+%token QUEUE PRIORITY QLIMIT
+%token LOAD
+%token STICKYADDRESS MAXSRCSTATES MAXSRCNODES SOURCETRACK GLOBAL RULE
+%token MAXSRCCONN MAXSRCCONNRATE OVERLOAD FLUSH
+%token TAGGED TAG IFBOUND GRBOUND FLOATING STATEPOLICY
+%token <v.string> STRING
+%token <v.i> PORTBINARY
+%type <v.interface> interface if_list if_item_not if_item
+%type <v.number> number icmptype icmp6type uid gid
+%type <v.number> tos not yesno natpass
+%type <v.i> no dir log af fragcache sourcetrack flush
+%type <v.i> unaryop statelock
+%type <v.b> action nataction scrubaction
+%type <v.b> flags flag blockspec
+%type <v.range> port rport
+%type <v.hashkey> hashkey
+%type <v.proto> proto proto_list proto_item
+%type <v.icmp> icmpspec
+%type <v.icmp> icmp_list icmp_item
+%type <v.icmp> icmp6_list icmp6_item
+%type <v.fromto> fromto
+%type <v.peer> ipportspec from to
+%type <v.host> ipspec xhost host dynaddr host_list
+%type <v.host> redir_host_list redirspec
+%type <v.host> route_host route_host_list routespec
+%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.string> label string tag
+%type <v.keep_state> keep
+%type <v.state_opt> state_opt_spec state_opt_list state_opt_item
+%type <v.logquick> logquick
+%type <v.interface> antispoof_ifspc antispoof_iflst antispoof_if
+%type <v.qassign> qname
+%type <v.queue> qassign qassign_list qassign_item
+%type <v.queue_options> scheduler
+%type <v.number> cbqflags_list cbqflags_item
+%type <v.number> priqflags_list priqflags_item
+%type <v.hfsc_opts> hfscopts_list hfscopts_item hfsc_opts
+%type <v.queue_bwspec> bandwidth
+%type <v.filter_opts> filter_opts filter_opt filter_opts_l
+%type <v.antispoof_opts> antispoof_opts antispoof_opt antispoof_opts_l
+%type <v.queue_opts> queue_opts queue_opt queue_opts_l
+%type <v.scrub_opts> scrub_opts scrub_opt scrub_opts_l
+%type <v.table_opts> table_opts table_opt table_opts_l
+%type <v.pool_opts> pool_opts pool_opt pool_opts_l
+%%
+
+ruleset : /* empty */
+ | ruleset '\n'
+ | ruleset option '\n'
+ | ruleset scrubrule '\n'
+ | ruleset natrule '\n'
+ | ruleset binatrule '\n'
+ | ruleset pfrule '\n'
+ | ruleset anchorrule '\n'
+ | ruleset loadrule '\n'
+ | ruleset altqif '\n'
+ | ruleset queuespec '\n'
+ | ruleset varset '\n'
+ | ruleset antispoof '\n'
+ | ruleset tabledef '\n'
+ | ruleset error '\n' { errors++; }
+ ;
+
+option : SET OPTIMIZATION STRING {
+ if (check_rulestate(PFCTL_STATE_OPTION)) {
+ free($3);
+ YYERROR;
+ }
+ if (pfctl_set_optimization(pf, $3) != 0) {
+ yyerror("unknown optimization %s", $3);
+ free($3);
+ YYERROR;
+ }
+ free ($3);
}
- } else {
- for (i=0; i < (sizeof (icmp6_type) /
- sizeof(icmp6_type[0])); i++) {
- if (type == icmp6_type[i].type)
- return (&icmp6_type[i]);
+ | SET TIMEOUT timeout_spec
+ | SET TIMEOUT '{' timeout_list '}'
+ | SET LIMIT limit_spec
+ | SET LIMIT '{' limit_list '}'
+ | SET LOGINTERFACE STRING {
+ if (check_rulestate(PFCTL_STATE_OPTION)) {
+ free($3);
+ YYERROR;
+ }
+ if (pfctl_set_logif(pf, $3) != 0) {
+ yyerror("error setting loginterface %s", $3);
+ free($3);
+ YYERROR;
+ }
+ free($3);
}
- }
- return (NULL);
-}
+ | SET HOSTID number {
+ if ($3 == 0) {
+ yyerror("hostid must be non-zero");
+ YYERROR;
+ }
+ if (pfctl_set_hostid(pf, $3) != 0) {
+ yyerror("error setting hostid %08x", $3);
+ YYERROR;
+ }
+ }
+ | SET BLOCKPOLICY DROP {
+ if (pf->opts & PF_OPT_VERBOSE)
+ printf("set block-policy drop\n");
+ if (check_rulestate(PFCTL_STATE_OPTION))
+ YYERROR;
+ blockpolicy = PFRULE_DROP;
+ }
+ | SET BLOCKPOLICY RETURN {
+ if (pf->opts & PF_OPT_VERBOSE)
+ printf("set block-policy return\n");
+ if (check_rulestate(PFCTL_STATE_OPTION))
+ YYERROR;
+ blockpolicy = PFRULE_RETURN;
+ }
+ | SET REQUIREORDER yesno {
+ if (pf->opts & PF_OPT_VERBOSE)
+ printf("set require-order %s\n",
+ $3 == 1 ? "yes" : "no");
+ require_order = $3;
+ }
+ | SET FINGERPRINTS STRING {
+ if (pf->opts & PF_OPT_VERBOSE)
+ printf("fingerprints %s\n", $3);
+ if (check_rulestate(PFCTL_STATE_OPTION)) {
+ free($3);
+ YYERROR;
+ }
+ if (pfctl_file_fingerprints(pf->dev, pf->opts, $3)) {
+ yyerror("error loading fingerprints %s", $3);
+ free($3);
+ YYERROR;
+ }
+ free($3);
+ }
+ | SET STATEPOLICY statelock {
+ if (pf->opts & PF_OPT_VERBOSE)
+ switch ($3) {
+ case 0:
+ printf("set state-policy floating\n");
+ break;
+ case PFRULE_IFBOUND:
+ printf("set state-policy if-bound\n");
+ break;
+ case PFRULE_GRBOUND:
+ printf("set state-policy "
+ "group-bound\n");
+ break;
+ }
+ default_statelock = $3;
+ }
+ | SET DEBUG STRING {
+ if (check_rulestate(PFCTL_STATE_OPTION)) {
+ free($3);
+ YYERROR;
+ }
+ if (pfctl_set_debug(pf, $3) != 0) {
+ yyerror("error setting debuglevel %s", $3);
+ free($3);
+ YYERROR;
+ }
+ free($3);
+ }
+ ;
-const struct icmptypeent *
-geticmptypebyname(char *w, sa_family_t af)
-{
- unsigned int i;
+string : string STRING {
+ if (asprintf(&$$, "%s %s", $1, $2) == -1)
+ err(1, "string: asprintf");
+ free($1);
+ free($2);
+ }
+ | STRING
+ ;
- if (af != AF_INET6) {
- for (i=0; i < (sizeof (icmp_type) / sizeof(icmp_type[0]));
- i++) {
- if (!strcmp(w, icmp_type[i].name))
- return (&icmp_type[i]);
+varset : STRING '=' string {
+ if (pf->opts & PF_OPT_VERBOSE)
+ printf("%s = \"%s\"\n", $1, $3);
+ if (symset($1, $3, 0) == -1)
+ err(1, "cannot store variable %s", $1);
+ free($1);
+ free($3);
}
- } else {
- for (i=0; i < (sizeof (icmp6_type) /
- sizeof(icmp6_type[0])); i++) {
- if (!strcmp(w, icmp6_type[i].name))
- return (&icmp6_type[i]);
+ ;
+
+anchorrule : ANCHOR string dir interface af proto fromto filter_opts {
+ struct pf_rule r;
+
+ if (check_rulestate(PFCTL_STATE_FILTER)) {
+ free($2);
+ YYERROR;
+ }
+
+ memset(&r, 0, sizeof(r));
+ r.direction = $3;
+ r.af = $5;
+ r.prob = $8.prob;
+
+ if ($8.match_tag)
+ if (strlcpy(r.match_tagname, $8.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 = $8.match_tag_not;
+
+ decide_address_family($7.src.host, &r.af);
+ decide_address_family($7.dst.host, &r.af);
+
+ expand_rule(&r, $4, NULL, $6, $7.src_os,
+ $7.src.host, $7.src.port, $7.dst.host, $7.dst.port,
+ 0, 0, 0, $2);
+ free($2);
}
- }
- return (NULL);
-}
+ | NATANCHOR string interface af proto fromto {
+ struct pf_rule r;
-const struct icmpcodeent *
-geticmpcodebynumber(u_int8_t type, u_int8_t code, sa_family_t af)
-{
- unsigned int i;
+ if (check_rulestate(PFCTL_STATE_NAT)) {
+ free($2);
+ YYERROR;
+ }
- if (af != AF_INET6) {
- for (i=0; i < (sizeof (icmp_code) / sizeof(icmp_code[0]));
- i++) {
- if (type == icmp_code[i].type &&
- code == icmp_code[i].code)
- return (&icmp_code[i]);
+ memset(&r, 0, sizeof(r));
+ r.action = PF_NAT;
+ r.af = $4;
+
+ 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);
+ free($2);
}
- } else {
- for (i=0; i < (sizeof (icmp6_code) /
- sizeof(icmp6_code[0])); i++) {
- if (type == icmp6_code[i].type &&
- code == icmp6_code[i].code)
- return (&icmp6_code[i]);
+ | RDRANCHOR string interface af proto fromto {
+ struct pf_rule r;
+
+ if (check_rulestate(PFCTL_STATE_NAT)) {
+ free($2);
+ YYERROR;
+ }
+
+ memset(&r, 0, sizeof(r));
+ r.action = PF_RDR;
+ r.af = $4;
+
+ decide_address_family($6.src.host, &r.af);
+ decide_address_family($6.dst.host, &r.af);
+
+ if ($6.src.port != NULL) {
+ yyerror("source port parameter not supported"
+ " in rdr-anchor");
+ YYERROR;
+ }
+ if ($6.dst.port != NULL) {
+ if ($6.dst.port->next != NULL) {
+ yyerror("destination port list "
+ "expansion not supported in "
+ "rdr-anchor");
+ YYERROR;
+ } else if ($6.dst.port->op != PF_OP_EQ) {
+ yyerror("destination port operators"
+ " not supported in rdr-anchor");
+ YYERROR;
+ }
+ r.dst.port[0] = $6.dst.port->port[0];
+ r.dst.port[1] = $6.dst.port->port[1];
+ 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);
+ free($2);
}
- }
- return (NULL);
-}
+ | BINATANCHOR string interface af proto fromto {
+ struct pf_rule r;
-const struct icmpcodeent *
-geticmpcodebyname(u_long type, char *w, sa_family_t af)
-{
- unsigned int i;
+ if (check_rulestate(PFCTL_STATE_NAT)) {
+ free($2);
+ YYERROR;
+ }
- if (af != AF_INET6) {
- for (i=0; i < (sizeof (icmp_code) / sizeof(icmp_code[0]));
- i++) {
- if (type == icmp_code[i].type &&
- !strcmp(w, icmp_code[i].name))
- return (&icmp_code[i]);
+ memset(&r, 0, sizeof(r));
+ r.action = PF_BINAT;
+ r.af = $4;
+ if ($5 != NULL) {
+ if ($5->next != NULL) {
+ yyerror("proto list expansion"
+ " not supported in binat-anchor");
+ YYERROR;
+ }
+ r.proto = $5->proto;
+ free($5);
+ }
+
+ if ($6.src.host != NULL || $6.src.port != NULL ||
+ $6.dst.host != NULL || $6.dst.port != NULL) {
+ yyerror("fromto parameter not supported"
+ " in binat-anchor");
+ YYERROR;
+ }
+
+ decide_address_family($6.src.host, &r.af);
+ decide_address_family($6.dst.host, &r.af);
+
+ pfctl_add_rule(pf, &r, $2);
+ free($2);
}
- } else {
- for (i=0; i < (sizeof (icmp6_code) /
- sizeof(icmp6_code[0])); i++) {
- if (type == icmp6_code[i].type &&
- !strcmp(w, icmp6_code[i].name))
- return (&icmp6_code[i]);
+ ;
+
+loadrule : LOAD ANCHOR string FROM string {
+ struct loadanchors *loadanchor;
+
+ if (strlen($3) >= MAXPATHLEN) {
+ yyerror("anchorname %s too long, max %u\n",
+ $3, MAXPATHLEN - 1);
+ free($3);
+ YYERROR;
+ }
+ loadanchor = calloc(1, sizeof(struct loadanchors));
+ if (loadanchor == NULL)
+ err(1, "loadrule: calloc");
+ if ((loadanchor->anchorname = strdup($3)) == NULL)
+ err(1, "loadrule: strdup");
+ if ((loadanchor->filename = strdup($5)) == NULL)
+ err(1, "loadrule: strdup");
+
+ TAILQ_INSERT_TAIL(&loadanchorshead, loadanchor,
+ entries);
+
+ free($3);
+ free($5);
+ };
+
+scrubaction : no SCRUB {
+ $$.b2 = $$.w = 0;
+ if ($1)
+ $$.b1 = PF_NOSCRUB;
+ else
+ $$.b1 = PF_SCRUB;
}
- }
- return (NULL);
-}
+ ;
-void
-print_op(u_int8_t op, const char *a1, const char *a2)
-{
- if (op == PF_OP_IRG)
- printf(" %s >< %s", a1, a2);
- else if (op == PF_OP_XRG)
- printf(" %s <> %s", a1, a2);
- else if (op == PF_OP_EQ)
- printf(" = %s", a1);
- else if (op == PF_OP_NE)
- printf(" != %s", a1);
- else if (op == PF_OP_LT)
- printf(" < %s", a1);
- else if (op == PF_OP_LE)
- printf(" <= %s", a1);
- else if (op == PF_OP_GT)
- printf(" > %s", a1);
- else if (op == PF_OP_GE)
- printf(" >= %s", a1);
- else if (op == PF_OP_RRG)
- printf(" %s:%s", a1, a2);
-}
+scrubrule : scrubaction dir logquick interface af proto fromto scrub_opts
+ {
+ struct pf_rule r;
-void
-print_port(u_int8_t op, u_int16_t p1, u_int16_t p2, const char *proto)
-{
- char a1[6], a2[6];
- struct servent *s;
+ if (check_rulestate(PFCTL_STATE_SCRUB))
+ YYERROR;
- s = getservbyport(p1, proto);
- p1 = ntohs(p1);
- p2 = ntohs(p2);
- snprintf(a1, sizeof(a1), "%u", p1);
- snprintf(a2, sizeof(a2), "%u", p2);
- printf(" port");
- if (s != NULL && (op == PF_OP_EQ || op == PF_OP_NE))
- print_op(op, s->s_name, a2);
- else
- print_op(op, a1, a2);
-}
+ memset(&r, 0, sizeof(r));
-void
-print_ugid(u_int8_t op, unsigned u1, unsigned u2, const char *t, unsigned umax)
-{
- char a1[11], a2[11];
+ r.action = $1.b1;
+ r.direction = $2;
- snprintf(a1, sizeof(a1), "%u", u1);
- snprintf(a2, sizeof(a2), "%u", u2);
- printf(" %s", t);
- if (u1 == umax && (op == PF_OP_EQ || op == PF_OP_NE))
- print_op(op, "unknown", a2);
- else
- print_op(op, a1, a2);
-}
+ r.log = $3.log;
+ if ($3.quick) {
+ yyerror("scrub rules do not support 'quick'");
+ YYERROR;
+ }
-void
-print_flags(u_int8_t f)
+ r.af = $5;
+ if ($8.nodf)
+ r.rule_flag |= PFRULE_NODF;
+ if ($8.randomid)
+ r.rule_flag |= PFRULE_RANDOMID;
+ if ($8.reassemble_tcp) {
+ if (r.direction != PF_INOUT) {
+ yyerror("reassemble tcp rules can not "
+ "specify direction");
+ YYERROR;
+ }
+ r.rule_flag |= PFRULE_REASSEMBLE_TCP;
+ }
+ if ($8.minttl)
+ r.min_ttl = $8.minttl;
+ if ($8.maxmss)
+ r.max_mss = $8.maxmss;
+ if ($8.fragcache)
+ r.rule_flag |= $8.fragcache;
+
+ expand_rule(&r, $4, NULL, $6, $7.src_os,
+ $7.src.host, $7.src.port, $7.dst.host, $7.dst.port,
+ NULL, NULL, NULL, "");
+ }
+ ;
+
+scrub_opts : {
+ bzero(&scrub_opts, sizeof scrub_opts);
+ }
+ scrub_opts_l
+ { $$ = scrub_opts; }
+ | /* empty */ {
+ bzero(&scrub_opts, sizeof scrub_opts);
+ $$ = scrub_opts;
+ }
+ ;
+
+scrub_opts_l : scrub_opts_l scrub_opt
+ | scrub_opt
+ ;
+
+scrub_opt : NODF {
+ if (scrub_opts.nodf) {
+ yyerror("no-df cannot be respecified");
+ YYERROR;
+ }
+ scrub_opts.nodf = 1;
+ }
+ | MINTTL number {
+ if (scrub_opts.marker & SOM_MINTTL) {
+ yyerror("min-ttl cannot be respecified");
+ YYERROR;
+ }
+ if ($2 > 255) {
+ yyerror("illegal min-ttl value %d", $2);
+ YYERROR;
+ }
+ scrub_opts.marker |= SOM_MINTTL;
+ scrub_opts.minttl = $2;
+ }
+ | MAXMSS number {
+ if (scrub_opts.marker & SOM_MAXMSS) {
+ yyerror("max-mss cannot be respecified");
+ YYERROR;
+ }
+ if ($2 > 65535) {
+ yyerror("illegal max-mss value %d", $2);
+ YYERROR;
+ }
+ scrub_opts.marker |= SOM_MAXMSS;
+ scrub_opts.maxmss = $2;
+ }
+ | fragcache {
+ if (scrub_opts.marker & SOM_FRAGCACHE) {
+ yyerror("fragcache cannot be respecified");
+ YYERROR;
+ }
+ scrub_opts.marker |= SOM_FRAGCACHE;
+ scrub_opts.fragcache = $1;
+ }
+ | REASSEMBLE STRING {
+ if (strcasecmp($2, "tcp") != 0) {
+ free($2);
+ YYERROR;
+ }
+ free($2);
+ if (scrub_opts.reassemble_tcp) {
+ yyerror("reassemble tcp cannot be respecified");
+ YYERROR;
+ }
+ scrub_opts.reassemble_tcp = 1;
+ }
+ | RANDOMID {
+ if (scrub_opts.randomid) {
+ yyerror("random-id cannot be respecified");
+ YYERROR;
+ }
+ scrub_opts.randomid = 1;
+ }
+ ;
+
+fragcache : FRAGMENT REASSEMBLE { $$ = 0; /* default */ }
+ | FRAGMENT FRAGCROP { $$ = PFRULE_FRAGCROP; }
+ | FRAGMENT FRAGDROP { $$ = PFRULE_FRAGDROP; }
+ ;
+
+antispoof : ANTISPOOF logquick antispoof_ifspc af antispoof_opts {
+ struct pf_rule r;
+ struct node_host *h = NULL, *hh;
+ struct node_if *i, *j;
+
+ if (check_rulestate(PFCTL_STATE_FILTER))
+ YYERROR;
+
+ for (i = $3; i; i = i->next) {
+ bzero(&r, sizeof(r));
+
+ r.action = PF_DROP;
+ r.direction = PF_IN;
+ r.log = $2.log;
+ r.quick = $2.quick;
+ r.af = $4;
+ if (rule_label(&r, $5.label))
+ YYERROR;
+ j = calloc(1, sizeof(struct node_if));
+ if (j == NULL)
+ err(1, "antispoof: calloc");
+ if (strlcpy(j->ifname, i->ifname,
+ sizeof(j->ifname)) >= sizeof(j->ifname)) {
+ free(j);
+ yyerror("interface name too long");
+ YYERROR;
+ }
+ j->not = 1;
+ if (i->dynamic) {
+ h = calloc(1, sizeof(*h));
+ if (h == NULL)
+ err(1, "address: calloc");
+ h->addr.type = PF_ADDR_DYNIFTL;
+ set_ipmask(h, 128);
+ if (strlcpy(h->addr.v.ifname, i->ifname,
+ sizeof(h->addr.v.ifname)) >=
+ sizeof(h->addr.v.ifname)) {
+ yyerror(
+ "interface name too long");
+ YYERROR;
+ }
+ hh = malloc(sizeof(*hh));
+ if (hh == NULL)
+ err(1, "address: malloc");
+ bcopy(h, hh, sizeof(*hh));
+ h->addr.iflags = PFI_AFLAG_NETWORK;
+ } else {
+ h = ifa_lookup(j->ifname,
+ PFI_AFLAG_NETWORK);
+ hh = NULL;
+ }
+
+ if (h != NULL)
+ expand_rule(&r, j, NULL, NULL, NULL, h,
+ NULL, NULL, NULL, NULL, NULL,
+ NULL, "");
+
+ if ((i->ifa_flags & IFF_LOOPBACK) == 0) {
+ bzero(&r, sizeof(r));
+
+ r.action = PF_DROP;
+ r.direction = PF_IN;
+ r.log = $2.log;
+ r.quick = $2.quick;
+ r.af = $4;
+ if (rule_label(&r, $5.label))
+ YYERROR;
+ if (hh != NULL)
+ h = hh;
+ else
+ h = ifa_lookup(i->ifname, 0);
+ if (h != NULL)
+ expand_rule(&r, NULL, NULL,
+ NULL, NULL, h, NULL, NULL,
+ NULL, NULL, NULL, NULL, "");
+ } else
+ free(hh);
+ }
+ free($5.label);
+ }
+ ;
+
+antispoof_ifspc : FOR antispoof_if { $$ = $2; }
+ | FOR '{' antispoof_iflst '}' { $$ = $3; }
+ ;
+
+antispoof_iflst : antispoof_if { $$ = $1; }
+ | antispoof_iflst comma antispoof_if {
+ $1->tail->next = $3;
+ $1->tail = $3;
+ $$ = $1;
+ }
+ ;
+
+antispoof_if : if_item { $$ = $1; }
+ | '(' if_item ')' {
+ $2->dynamic = 1;
+ $$ = $2;
+ }
+ ;
+
+antispoof_opts : { bzero(&antispoof_opts, sizeof antispoof_opts); }
+ antispoof_opts_l
+ { $$ = antispoof_opts; }
+ | /* empty */ {
+ bzero(&antispoof_opts, sizeof antispoof_opts);
+ $$ = antispoof_opts;
+ }
+ ;
+
+antispoof_opts_l : antispoof_opts_l antispoof_opt
+ | antispoof_opt
+ ;
+
+antispoof_opt : label {
+ if (antispoof_opts.label) {
+ yyerror("label cannot be redefined");
+ YYERROR;
+ }
+ antispoof_opts.label = $1;
+ }
+ ;
+
+not : '!' { $$ = 1; }
+ | /* empty */ { $$ = 0; }
+ ;
+
+tabledef : TABLE '<' STRING '>' table_opts {
+ struct node_host *h, *nh;
+ struct node_tinit *ti, *nti;
+
+ if (strlen($3) >= PF_TABLE_NAME_SIZE) {
+ yyerror("table name too long, max %d chars",
+ PF_TABLE_NAME_SIZE - 1);
+ free($3);
+ YYERROR;
+ }
+ if (pf->loadopt & PFCTL_FLAG_TABLE)
+ if (process_tabledef($3, &$5)) {
+ free($3);
+ YYERROR;
+ }
+ free($3);
+ for (ti = SIMPLEQ_FIRST(&$5.init_nodes);
+ ti != SIMPLEQ_END(&$5.init_nodes); ti = nti) {
+ if (ti->file)
+ free(ti->file);
+ for (h = ti->host; h != NULL; h = nh) {
+ nh = h->next;
+ free(h);
+ }
+ nti = SIMPLEQ_NEXT(ti, entries);
+ free(ti);
+ }
+ }
+ ;
+
+table_opts : {
+ bzero(&table_opts, sizeof table_opts);
+ SIMPLEQ_INIT(&table_opts.init_nodes);
+ }
+ table_opts_l
+ { $$ = table_opts; }
+ | /* empty */
+ {
+ bzero(&table_opts, sizeof table_opts);
+ SIMPLEQ_INIT(&table_opts.init_nodes);
+ $$ = table_opts;
+ }
+ ;
+
+table_opts_l : table_opts_l table_opt
+ | table_opt
+ ;
+
+table_opt : STRING {
+ if (!strcmp($1, "const"))
+ table_opts.flags |= PFR_TFLAG_CONST;
+ else if (!strcmp($1, "persist"))
+ table_opts.flags |= PFR_TFLAG_PERSIST;
+ else {
+ free($1);
+ YYERROR;
+ }
+ free($1);
+ }
+ | '{' '}' { table_opts.init_addr = 1; }
+ | '{' host_list '}' {
+ struct node_host *n;
+ struct node_tinit *ti;
+
+ for (n = $2; n != NULL; n = n->next) {
+ switch (n->addr.type) {
+ case PF_ADDR_ADDRMASK:
+ continue; /* ok */
+ case PF_ADDR_DYNIFTL:
+ yyerror("dynamic addresses are not "
+ "permitted inside tables");
+ break;
+ case PF_ADDR_TABLE:
+ yyerror("tables cannot contain tables");
+ break;
+ case PF_ADDR_NOROUTE:
+ yyerror("\"no-route\" is not permitted "
+ "inside tables");
+ break;
+ default:
+ yyerror("unknown address type %d",
+ n->addr.type);
+ }
+ YYERROR;
+ }
+ if (!(ti = calloc(1, sizeof(*ti))))
+ err(1, "table_opt: calloc");
+ ti->host = $2;
+ SIMPLEQ_INSERT_TAIL(&table_opts.init_nodes, ti,
+ entries);
+ table_opts.init_addr = 1;
+ }
+ | FILENAME STRING {
+ struct node_tinit *ti;
+
+ if (!(ti = calloc(1, sizeof(*ti))))
+ err(1, "table_opt: calloc");
+ ti->file = $2;
+ SIMPLEQ_INSERT_TAIL(&table_opts.init_nodes, ti,
+ entries);
+ table_opts.init_addr = 1;
+ }
+ ;
+
+altqif : ALTQ interface queue_opts QUEUE qassign {
+ struct pf_altq a;
+
+ if (check_rulestate(PFCTL_STATE_QUEUE))
+ YYERROR;
+
+ memset(&a, 0, sizeof(a));
+ if ($3.scheduler.qtype == ALTQT_NONE) {
+ yyerror("no scheduler specified!");
+ YYERROR;
+ }
+ a.scheduler = $3.scheduler.qtype;
+ a.qlimit = $3.qlimit;
+ a.tbrsize = $3.tbrsize;
+ if ($5 == NULL) {
+ yyerror("no child queues specified");
+ YYERROR;
+ }
+ if (expand_altq(&a, $2, $5, $3.queue_bwspec,
+ &$3.scheduler))
+ YYERROR;
+ }
+ ;
+
+queuespec : QUEUE STRING interface queue_opts qassign {
+ struct pf_altq a;
+
+ if (check_rulestate(PFCTL_STATE_QUEUE)) {
+ free($2);
+ YYERROR;
+ }
+
+ memset(&a, 0, sizeof(a));
+
+ if (strlcpy(a.qname, $2, sizeof(a.qname)) >=
+ sizeof(a.qname)) {
+ yyerror("queue name too long (max "
+ "%d chars)", PF_QNAME_SIZE-1);
+ free($2);
+ YYERROR;
+ }
+ free($2);
+ if ($4.tbrsize) {
+ yyerror("cannot specify tbrsize for queue");
+ YYERROR;
+ }
+ if ($4.priority > 255) {
+ yyerror("priority out of range: max 255");
+ YYERROR;
+ }
+ a.priority = $4.priority;
+ a.qlimit = $4.qlimit;
+ a.scheduler = $4.scheduler.qtype;
+ if (expand_queue(&a, $3, $5, $4.queue_bwspec,
+ &$4.scheduler)) {
+ yyerror("errors in queue definition");
+ YYERROR;
+ }
+ }
+ ;
+
+queue_opts : {
+ bzero(&queue_opts, sizeof queue_opts);
+ queue_opts.priority = DEFAULT_PRIORITY;
+ queue_opts.qlimit = DEFAULT_QLIMIT;
+ queue_opts.scheduler.qtype = ALTQT_NONE;
+ queue_opts.queue_bwspec.bw_percent = 100;
+ }
+ queue_opts_l
+ { $$ = queue_opts; }
+ | /* empty */ {
+ bzero(&queue_opts, sizeof queue_opts);
+ queue_opts.priority = DEFAULT_PRIORITY;
+ queue_opts.qlimit = DEFAULT_QLIMIT;
+ queue_opts.scheduler.qtype = ALTQT_NONE;
+ queue_opts.queue_bwspec.bw_percent = 100;
+ $$ = queue_opts;
+ }
+ ;
+
+queue_opts_l : queue_opts_l queue_opt
+ | queue_opt
+ ;
+
+queue_opt : BANDWIDTH bandwidth {
+ if (queue_opts.marker & QOM_BWSPEC) {
+ yyerror("bandwidth cannot be respecified");
+ YYERROR;
+ }
+ queue_opts.marker |= QOM_BWSPEC;
+ queue_opts.queue_bwspec = $2;
+ }
+ | PRIORITY number {
+ if (queue_opts.marker & QOM_PRIORITY) {
+ yyerror("priority cannot be respecified");
+ YYERROR;
+ }
+ if ($2 > 255) {
+ yyerror("priority out of range: max 255");
+ YYERROR;
+ }
+ queue_opts.marker |= QOM_PRIORITY;
+ queue_opts.priority = $2;
+ }
+ | QLIMIT number {
+ if (queue_opts.marker & QOM_QLIMIT) {
+ yyerror("qlimit cannot be respecified");
+ YYERROR;
+ }
+ if ($2 > 65535) {
+ yyerror("qlimit out of range: max 65535");
+ YYERROR;
+ }
+ queue_opts.marker |= QOM_QLIMIT;
+ queue_opts.qlimit = $2;
+ }
+ | scheduler {
+ if (queue_opts.marker & QOM_SCHEDULER) {
+ yyerror("scheduler cannot be respecified");
+ YYERROR;
+ }
+ queue_opts.marker |= QOM_SCHEDULER;
+ queue_opts.scheduler = $1;
+ }
+ | TBRSIZE number {
+ if (queue_opts.marker & QOM_TBRSIZE) {
+ yyerror("tbrsize cannot be respecified");
+ YYERROR;
+ }
+ if ($2 > 65535) {
+ yyerror("tbrsize too big: max 65535");
+ YYERROR;
+ }
+ queue_opts.marker |= QOM_TBRSIZE;
+ queue_opts.tbrsize = $2;
+ }
+ ;
+
+bandwidth : STRING {
+ double bps;
+ char *cp;
+
+ $$.bw_percent = 0;
+
+ bps = strtod($1, &cp);
+ if (cp != NULL) {
+ if (!strcmp(cp, "b"))
+ ; /* nothing */
+ else if (!strcmp(cp, "Kb"))
+ bps *= 1000;
+ else if (!strcmp(cp, "Mb"))
+ bps *= 1000 * 1000;
+ else if (!strcmp(cp, "Gb"))
+ bps *= 1000 * 1000 * 1000;
+ else if (!strcmp(cp, "%")) {
+ if (bps < 0 || bps > 100) {
+ yyerror("bandwidth spec "
+ "out of range");
+ free($1);
+ YYERROR;
+ }
+ $$.bw_percent = bps;
+ bps = 0;
+ } else {
+ yyerror("unknown unit %s", cp);
+ free($1);
+ YYERROR;
+ }
+ }
+ free($1);
+ $$.bw_absolute = (u_int32_t)bps;
+ }
+ ;
+
+scheduler : CBQ {
+ $$.qtype = ALTQT_CBQ;
+ $$.data.cbq_opts.flags = 0;
+ }
+ | CBQ '(' cbqflags_list ')' {
+ $$.qtype = ALTQT_CBQ;
+ $$.data.cbq_opts.flags = $3;
+ }
+ | PRIQ {
+ $$.qtype = ALTQT_PRIQ;
+ $$.data.priq_opts.flags = 0;
+ }
+ | PRIQ '(' priqflags_list ')' {
+ $$.qtype = ALTQT_PRIQ;
+ $$.data.priq_opts.flags = $3;
+ }
+ | HFSC {
+ $$.qtype = ALTQT_HFSC;
+ bzero(&$$.data.hfsc_opts,
+ sizeof(struct node_hfsc_opts));
+ }
+ | HFSC '(' hfsc_opts ')' {
+ $$.qtype = ALTQT_HFSC;
+ $$.data.hfsc_opts = $3;
+ }
+ ;
+
+cbqflags_list : cbqflags_item { $$ |= $1; }
+ | cbqflags_list comma cbqflags_item { $$ |= $3; }
+ ;
+
+cbqflags_item : STRING {
+ if (!strcmp($1, "default"))
+ $$ = CBQCLF_DEFCLASS;
+ else if (!strcmp($1, "borrow"))
+ $$ = CBQCLF_BORROW;
+ else if (!strcmp($1, "red"))
+ $$ = CBQCLF_RED;
+ else if (!strcmp($1, "ecn"))
+ $$ = CBQCLF_RED|CBQCLF_ECN;
+ else if (!strcmp($1, "rio"))
+ $$ = CBQCLF_RIO;
+ else {
+ yyerror("unknown cbq flag \"%s\"", $1);
+ free($1);
+ YYERROR;
+ }
+ free($1);
+ }
+ ;
+
+priqflags_list : priqflags_item { $$ |= $1; }
+ | priqflags_list comma priqflags_item { $$ |= $3; }
+ ;
+
+priqflags_item : STRING {
+ if (!strcmp($1, "default"))
+ $$ = PRCF_DEFAULTCLASS;
+ else if (!strcmp($1, "red"))
+ $$ = PRCF_RED;
+ else if (!strcmp($1, "ecn"))
+ $$ = PRCF_RED|PRCF_ECN;
+ else if (!strcmp($1, "rio"))
+ $$ = PRCF_RIO;
+ else {
+ yyerror("unknown priq flag \"%s\"", $1);
+ free($1);
+ YYERROR;
+ }
+ free($1);
+ }
+ ;
+
+hfsc_opts : {
+ bzero(&hfsc_opts,
+ sizeof(struct node_hfsc_opts));
+ }
+ hfscopts_list {
+ $$ = hfsc_opts;
+ }
+ ;
+
+hfscopts_list : hfscopts_item
+ | hfscopts_list comma hfscopts_item
+ ;
+
+hfscopts_item : LINKSHARE bandwidth {
+ if (hfsc_opts.linkshare.used) {
+ yyerror("linkshare already specified");
+ YYERROR;
+ }
+ hfsc_opts.linkshare.m2 = $2;
+ hfsc_opts.linkshare.used = 1;
+ }
+ | LINKSHARE '(' bandwidth number bandwidth ')' {
+ if (hfsc_opts.linkshare.used) {
+ yyerror("linkshare already specified");
+ YYERROR;
+ }
+ hfsc_opts.linkshare.m1 = $3;
+ hfsc_opts.linkshare.d = $4;
+ hfsc_opts.linkshare.m2 = $5;
+ hfsc_opts.linkshare.used = 1;
+ }
+ | REALTIME bandwidth {
+ if (hfsc_opts.realtime.used) {
+ yyerror("realtime already specified");
+ YYERROR;
+ }
+ hfsc_opts.realtime.m2 = $2;
+ hfsc_opts.realtime.used = 1;
+ }
+ | REALTIME '(' bandwidth number bandwidth ')' {
+ if (hfsc_opts.realtime.used) {
+ yyerror("realtime already specified");
+ YYERROR;
+ }
+ hfsc_opts.realtime.m1 = $3;
+ hfsc_opts.realtime.d = $4;
+ hfsc_opts.realtime.m2 = $5;
+ hfsc_opts.realtime.used = 1;
+ }
+ | UPPERLIMIT bandwidth {
+ if (hfsc_opts.upperlimit.used) {
+ yyerror("upperlimit already specified");
+ YYERROR;
+ }
+ hfsc_opts.upperlimit.m2 = $2;
+ hfsc_opts.upperlimit.used = 1;
+ }
+ | UPPERLIMIT '(' bandwidth number bandwidth ')' {
+ if (hfsc_opts.upperlimit.used) {
+ yyerror("upperlimit already specified");
+ YYERROR;
+ }
+ hfsc_opts.upperlimit.m1 = $3;
+ hfsc_opts.upperlimit.d = $4;
+ hfsc_opts.upperlimit.m2 = $5;
+ hfsc_opts.upperlimit.used = 1;
+ }
+ | STRING {
+ if (!strcmp($1, "default"))
+ hfsc_opts.flags |= HFCF_DEFAULTCLASS;
+ else if (!strcmp($1, "red"))
+ hfsc_opts.flags |= HFCF_RED;
+ else if (!strcmp($1, "ecn"))
+ hfsc_opts.flags |= HFCF_RED|HFCF_ECN;
+ else if (!strcmp($1, "rio"))
+ hfsc_opts.flags |= HFCF_RIO;
+ else {
+ yyerror("unknown hfsc flag \"%s\"", $1);
+ free($1);
+ YYERROR;
+ }
+ free($1);
+ }
+ ;
+
+qassign : /* empty */ { $$ = NULL; }
+ | qassign_item { $$ = $1; }
+ | '{' qassign_list '}' { $$ = $2; }
+ ;
+
+qassign_list : qassign_item { $$ = $1; }
+ | qassign_list comma qassign_item {
+ $1->tail->next = $3;
+ $1->tail = $3;
+ $$ = $1;
+ }
+ ;
+
+qassign_item : STRING {
+ $$ = calloc(1, sizeof(struct node_queue));
+ if ($$ == NULL)
+ err(1, "qassign_item: calloc");
+ if (strlcpy($$->queue, $1, sizeof($$->queue)) >=
+ sizeof($$->queue)) {
+ yyerror("queue name '%s' too long (max "
+ "%d chars)", $1, sizeof($$->queue)-1);
+ free($1);
+ free($$);
+ YYERROR;
+ }
+ free($1);
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ ;
+
+pfrule : action dir logquick interface route af proto fromto
+ filter_opts
+ {
+ struct pf_rule r;
+ struct node_state_opt *o;
+ struct node_proto *proto;
+ int srctrack = 0;
+ int statelock = 0;
+
+ if (check_rulestate(PFCTL_STATE_FILTER))
+ YYERROR;
+
+ memset(&r, 0, sizeof(r));
+
+ r.action = $1.b1;
+ switch ($1.b2) {
+ case PFRULE_RETURNRST:
+ r.rule_flag |= PFRULE_RETURNRST;
+ r.return_ttl = $1.w;
+ break;
+ case PFRULE_RETURNICMP:
+ r.rule_flag |= PFRULE_RETURNICMP;
+ r.return_icmp = $1.w;
+ r.return_icmp6 = $1.w2;
+ break;
+ case PFRULE_RETURN:
+ r.rule_flag |= PFRULE_RETURN;
+ r.return_icmp = $1.w;
+ r.return_icmp6 = $1.w2;
+ break;
+ }
+ r.direction = $2;
+ r.log = $3.log;
+ r.quick = $3.quick;
+ r.prob = $9.prob;
+
+ 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;
+ r.flags = $9.flags.b1;
+ r.flagset = $9.flags.b2;
+ if (rule_label(&r, $9.label))
+ YYERROR;
+ free($9.label);
+ if ($9.flags.b1 || $9.flags.b2 || $8.src_os) {
+ for (proto = $7; proto != NULL &&
+ proto->proto != IPPROTO_TCP;
+ proto = proto->next)
+ ; /* nothing */
+ if (proto == NULL && $7 != NULL) {
+ if ($9.flags.b1 || $9.flags.b2)
+ yyerror(
+ "flags only apply to tcp");
+ if ($8.src_os)
+ yyerror(
+ "OS fingerprinting only "
+ "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;
+ while (o) {
+ struct node_state_opt *p = o;
+
+ switch (o->type) {
+ case PF_STATE_OPT_MAX:
+ if (r.max_states) {
+ yyerror("state option 'max' "
+ "multiple definitions");
+ YYERROR;
+ }
+ r.max_states = o->data.max_states;
+ break;
+ case PF_STATE_OPT_NOSYNC:
+ if (r.rule_flag & PFRULE_NOSYNC) {
+ yyerror("state option 'sync' "
+ "multiple definitions");
+ YYERROR;
+ }
+ r.rule_flag |= PFRULE_NOSYNC;
+ break;
+ case PF_STATE_OPT_SRCTRACK:
+ if (srctrack) {
+ yyerror("state option "
+ "'source-track' "
+ "multiple definitions");
+ YYERROR;
+ }
+ srctrack = o->data.src_track;
+ break;
+ case PF_STATE_OPT_MAX_SRC_STATES:
+ if (r.max_src_states) {
+ yyerror("state option "
+ "'max-src-states' "
+ "multiple definitions");
+ YYERROR;
+ }
+ if (o->data.max_src_states == 0) {
+ yyerror("'max-src-states' must "
+ "be > 0");
+ YYERROR;
+ }
+ r.max_src_states =
+ o->data.max_src_states;
+ r.rule_flag |= PFRULE_SRCTRACK;
+ break;
+ case PF_STATE_OPT_OVERLOAD:
+ if (r.overload_tblname[0]) {
+ yyerror("multiple 'overload' "
+ "table definitions");
+ YYERROR;
+ }
+ if (strlcpy(r.overload_tblname,
+ o->data.overload.tblname,
+ PF_TABLE_NAME_SIZE) >=
+ PF_TABLE_NAME_SIZE) {
+ yyerror("state option: "
+ "strlcpy");
+ YYERROR;
+ }
+ if (o->data.overload.flush)
+ r.rule_flag |=
+ PFRULE_SRCTRACK_FLUSH;
+ break;
+ case PF_STATE_OPT_MAX_SRC_CONN:
+ if (r.max_src_conn) {
+ yyerror("state option "
+ "'max-src-conn' "
+ "multiple definitions");
+ YYERROR;
+ }
+ if (o->data.max_src_conn == 0) {
+ yyerror("'max-src-conn' "
+ "must be > 0");
+ YYERROR;
+ }
+ r.max_src_conn =
+ o->data.max_src_conn;
+ r.rule_flag |= PFRULE_SRCTRACK |
+ PFRULE_RULESRCTRACK;
+ break;
+ case PF_STATE_OPT_MAX_SRC_CONN_RATE:
+ if (r.max_src_conn_rate.limit) {
+ yyerror("state option "
+ "'max-src-conn-rate' "
+ "multiple definitions");
+ YYERROR;
+ }
+ if (!o->data.max_src_conn_rate.limit ||
+ !o->data.max_src_conn_rate.seconds) {
+ yyerror("'max-src-conn-rate' "
+ "values must be > 0");
+ YYERROR;
+ }
+ if (o->data.max_src_conn_rate.limit >
+ PF_THRESHOLD_MAX) {
+ yyerror("'max-src-conn-rate' "
+ "maximum rate must be < %u",
+ PF_THRESHOLD_MAX);
+ YYERROR;
+ }
+ r.max_src_conn_rate.limit =
+ o->data.max_src_conn_rate.limit;
+ r.max_src_conn_rate.seconds =
+ o->data.max_src_conn_rate.seconds;
+ r.rule_flag |= PFRULE_SRCTRACK |
+ PFRULE_RULESRCTRACK;
+ break;
+ case PF_STATE_OPT_MAX_SRC_NODES:
+ if (r.max_src_nodes) {
+ yyerror("state option "
+ "'max-src-nodes' "
+ "multiple definitions");
+ YYERROR;
+ }
+ if (o->data.max_src_nodes == 0) {
+ yyerror("'max-src-nodes' must "
+ "be > 0");
+ YYERROR;
+ }
+ r.max_src_nodes =
+ o->data.max_src_nodes;
+ r.rule_flag |= PFRULE_SRCTRACK |
+ PFRULE_RULESRCTRACK;
+ break;
+ case PF_STATE_OPT_STATELOCK:
+ if (statelock) {
+ yyerror("state locking option: "
+ "multiple definitions");
+ YYERROR;
+ }
+ statelock = 1;
+ r.rule_flag |= o->data.statelock;
+ break;
+ case PF_STATE_OPT_TIMEOUT:
+ if (r.timeout[o->data.timeout.number]) {
+ yyerror("state timeout %s "
+ "multiple definitions",
+ pf_timeouts[o->data.
+ timeout.number].name);
+ YYERROR;
+ }
+ r.timeout[o->data.timeout.number] =
+ o->data.timeout.seconds;
+ }
+ o = o->next;
+ free(p);
+ }
+ if (r.rule_flag & PFRULE_SRCTRACK) {
+ if (srctrack == PF_SRCTRACK_GLOBAL &&
+ r.max_src_nodes) {
+ yyerror("'max-src-nodes' is "
+ "incompatible with "
+ "'source-track global'");
+ YYERROR;
+ }
+ if (srctrack == PF_SRCTRACK_GLOBAL &&
+ r.max_src_nodes) {
+ yyerror("'max-src-conn' is "
+ "incompatible with "
+ "'source-track global'");
+ YYERROR;
+ }
+ if (srctrack == PF_SRCTRACK_GLOBAL &&
+ r.max_src_nodes) {
+ yyerror("'max-src-conn-rate' is "
+ "incompatible with "
+ "'source-track global'");
+ YYERROR;
+ }
+ if (r.timeout[PFTM_SRC_NODE] <
+ r.max_src_conn_rate.seconds)
+ r.timeout[PFTM_SRC_NODE] =
+ r.max_src_conn_rate.seconds;
+ r.rule_flag |= PFRULE_SRCTRACK;
+ if (srctrack == PF_SRCTRACK_RULE)
+ r.rule_flag |= PFRULE_RULESRCTRACK;
+ }
+ 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);
+
+ if ($5.rt) {
+ if (!r.direction) {
+ yyerror("direction must be explicit "
+ "with rules that specify routing");
+ 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 && r.rt != PF_FASTROUTE) {
+ 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");
+ 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);
+ }
+
+ 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, "");
+ }
+ ;
+
+filter_opts : { bzero(&filter_opts, sizeof filter_opts); }
+ filter_opts_l
+ { $$ = filter_opts; }
+ | /* empty */ {
+ bzero(&filter_opts, sizeof filter_opts);
+ $$ = filter_opts;
+ }
+ ;
+
+filter_opts_l : filter_opts_l filter_opt
+ | filter_opt
+ ;
+
+filter_opt : USER uids {
+ if (filter_opts.uid)
+ $2->tail->next = filter_opts.uid;
+ filter_opts.uid = $2;
+ }
+ | GROUP gids {
+ if (filter_opts.gid)
+ $2->tail->next = filter_opts.gid;
+ filter_opts.gid = $2;
+ }
+ | flags {
+ if (filter_opts.marker & FOM_FLAGS) {
+ yyerror("flags cannot be redefined");
+ YYERROR;
+ }
+ filter_opts.marker |= FOM_FLAGS;
+ filter_opts.flags.b1 |= $1.b1;
+ filter_opts.flags.b2 |= $1.b2;
+ filter_opts.flags.w |= $1.w;
+ filter_opts.flags.w2 |= $1.w2;
+ }
+ | icmpspec {
+ if (filter_opts.marker & FOM_ICMP) {
+ yyerror("icmp-type cannot be redefined");
+ YYERROR;
+ }
+ filter_opts.marker |= FOM_ICMP;
+ filter_opts.icmpspec = $1;
+ }
+ | tos {
+ if (filter_opts.marker & FOM_TOS) {
+ yyerror("tos cannot be redefined");
+ YYERROR;
+ }
+ filter_opts.marker |= FOM_TOS;
+ filter_opts.tos = $1;
+ }
+ | keep {
+ if (filter_opts.marker & FOM_KEEP) {
+ yyerror("modulate or keep cannot be redefined");
+ YYERROR;
+ }
+ filter_opts.marker |= FOM_KEEP;
+ filter_opts.keep.action = $1.action;
+ filter_opts.keep.options = $1.options;
+ }
+ | FRAGMENT {
+ filter_opts.fragment = 1;
+ }
+ | ALLOWOPTS {
+ filter_opts.allowopts = 1;
+ }
+ | label {
+ if (filter_opts.label) {
+ yyerror("label cannot be redefined");
+ YYERROR;
+ }
+ filter_opts.label = $1;
+ }
+ | qname {
+ if (filter_opts.queues.qname) {
+ yyerror("queue cannot be redefined");
+ YYERROR;
+ }
+ filter_opts.queues = $1;
+ }
+ | TAG string {
+ filter_opts.tag = $2;
+ }
+ | not TAGGED string {
+ filter_opts.match_tag = $3;
+ filter_opts.match_tag_not = $1;
+ }
+ | PROBABILITY STRING {
+ char *e;
+ double p = strtod($2, &e);
+
+ if (*e == '%') {
+ p *= 0.01;
+ e++;
+ }
+ if (*e) {
+ yyerror("invalid probability: %s", $2);
+ YYERROR;
+ }
+ p = floor(p * (UINT_MAX+1.0) + 0.5);
+ if (p < 1.0 || p >= (UINT_MAX+1.0)) {
+ yyerror("invalid probability: %s", $2);
+ YYERROR;
+ }
+ filter_opts.prob = (u_int32_t)p;
+ free($2);
+ }
+ ;
+
+action : PASS { $$.b1 = PF_PASS; $$.b2 = $$.w = 0; }
+ | BLOCK blockspec { $$ = $2; $$.b1 = PF_DROP; }
+ ;
+
+blockspec : /* empty */ {
+ $$.b2 = blockpolicy;
+ $$.w = returnicmpdefault;
+ $$.w2 = returnicmp6default;
+ }
+ | DROP {
+ $$.b2 = PFRULE_DROP;
+ $$.w = 0;
+ $$.w2 = 0;
+ }
+ | RETURNRST {
+ $$.b2 = PFRULE_RETURNRST;
+ $$.w = 0;
+ $$.w2 = 0;
+ }
+ | RETURNRST '(' TTL number ')' {
+ if ($4 > 255) {
+ yyerror("illegal ttl value %d", $4);
+ YYERROR;
+ }
+ $$.b2 = PFRULE_RETURNRST;
+ $$.w = $4;
+ $$.w2 = 0;
+ }
+ | RETURNICMP {
+ $$.b2 = PFRULE_RETURNICMP;
+ $$.w = returnicmpdefault;
+ $$.w2 = returnicmp6default;
+ }
+ | RETURNICMP6 {
+ $$.b2 = PFRULE_RETURNICMP;
+ $$.w = returnicmpdefault;
+ $$.w2 = returnicmp6default;
+ }
+ | RETURNICMP '(' STRING ')' {
+ $$.b2 = PFRULE_RETURNICMP;
+ if (!($$.w = parseicmpspec($3, AF_INET))) {
+ free($3);
+ YYERROR;
+ }
+ free($3);
+ $$.w2 = returnicmp6default;
+ }
+ | RETURNICMP6 '(' STRING ')' {
+ $$.b2 = PFRULE_RETURNICMP;
+ $$.w = returnicmpdefault;
+ if (!($$.w2 = parseicmpspec($3, AF_INET6))) {
+ free($3);
+ YYERROR;
+ }
+ free($3);
+ }
+ | RETURNICMP '(' STRING comma STRING ')' {
+ $$.b2 = PFRULE_RETURNICMP;
+ if (!($$.w = parseicmpspec($3, AF_INET)) ||
+ !($$.w2 = parseicmpspec($5, AF_INET6))) {
+ free($3);
+ free($5);
+ YYERROR;
+ }
+ free($3);
+ free($5);
+ }
+ | RETURN {
+ $$.b2 = PFRULE_RETURN;
+ $$.w = returnicmpdefault;
+ $$.w2 = returnicmp6default;
+ }
+ ;
+
+dir : /* empty */ { $$ = 0; }
+ | IN { $$ = PF_IN; }
+ | OUT { $$ = PF_OUT; }
+ ;
+
+logquick : /* empty */ { $$.log = 0; $$.quick = 0; }
+ | log { $$.log = $1; $$.quick = 0; }
+ | QUICK { $$.log = 0; $$.quick = 1; }
+ | log QUICK { $$.log = $1; $$.quick = 1; }
+ | QUICK log { $$.log = $2; $$.quick = 1; }
+ ;
+
+log : LOG { $$ = 1; }
+ | LOGALL { $$ = 2; }
+ ;
+
+interface : /* empty */ { $$ = NULL; }
+ | ON if_item_not { $$ = $2; }
+ | ON '{' if_list '}' { $$ = $3; }
+ ;
+
+if_list : if_item_not { $$ = $1; }
+ | if_list comma if_item_not {
+ $1->tail->next = $3;
+ $1->tail = $3;
+ $$ = $1;
+ }
+ ;
+
+if_item_not : not if_item { $$ = $2; $$->not = $1; }
+ ;
+
+if_item : STRING {
+ struct node_host *n;
+
+ $$ = calloc(1, sizeof(struct node_if));
+ if ($$ == NULL)
+ err(1, "if_item: calloc");
+ if (strlcpy($$->ifname, $1, sizeof($$->ifname)) >=
+ sizeof($$->ifname)) {
+ free($1);
+ free($$);
+ yyerror("interface name too long");
+ YYERROR;
+ }
+
+ if ((n = ifa_exists($1, 1)) != NULL)
+ $$->ifa_flags = n->ifa_flags;
+
+ free($1);
+ $$->not = 0;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ ;
+
+af : /* empty */ { $$ = 0; }
+ | INET { $$ = AF_INET; }
+ | INET6 { $$ = AF_INET6; }
+ ;
+
+proto : /* empty */ { $$ = NULL; }
+ | PROTO proto_item { $$ = $2; }
+ | PROTO '{' proto_list '}' { $$ = $3; }
+ ;
+
+proto_list : proto_item { $$ = $1; }
+ | proto_list comma proto_item {
+ $1->tail->next = $3;
+ $1->tail = $3;
+ $$ = $1;
+ }
+ ;
+
+proto_item : STRING {
+ u_int8_t pr;
+ u_long ulval;
+
+ if (atoul($1, &ulval) == 0) {
+ if (ulval > 255) {
+ yyerror("protocol outside range");
+ free($1);
+ YYERROR;
+ }
+ pr = (u_int8_t)ulval;
+ } else {
+ struct protoent *p;
+
+ p = getprotobyname($1);
+ if (p == NULL) {
+ yyerror("unknown protocol %s", $1);
+ free($1);
+ YYERROR;
+ }
+ pr = p->p_proto;
+ }
+ free($1);
+ if (pr == 0) {
+ yyerror("proto 0 cannot be used");
+ YYERROR;
+ }
+ $$ = calloc(1, sizeof(struct node_proto));
+ if ($$ == NULL)
+ err(1, "proto_item: calloc");
+ $$->proto = pr;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ ;
+
+fromto : ALL {
+ $$.src.host = NULL;
+ $$.src.port = NULL;
+ $$.dst.host = NULL;
+ $$.dst.port = NULL;
+ $$.src_os = NULL;
+ }
+ | from os to {
+ $$.src = $1;
+ $$.src_os = $2;
+ $$.dst = $3;
+ }
+ ;
+
+os : /* empty */ { $$ = NULL; }
+ | OS xos { $$ = $2; }
+ | OS '{' os_list '}' { $$ = $3; }
+ ;
+
+xos : STRING {
+ $$ = calloc(1, sizeof(struct node_os));
+ if ($$ == NULL)
+ err(1, "os: calloc");
+ $$->os = $1;
+ $$->tail = $$;
+ }
+ ;
+
+os_list : xos { $$ = $1; }
+ | os_list comma xos {
+ $1->tail->next = $3;
+ $1->tail = $3;
+ $$ = $1;
+ }
+ ;
+
+from : /* empty */ {
+ $$.host = NULL;
+ $$.port = NULL;
+ }
+ | FROM ipportspec {
+ $$ = $2;
+ }
+ ;
+
+to : /* empty */ {
+ $$.host = NULL;
+ $$.port = NULL;
+ }
+ | TO ipportspec {
+ $$ = $2;
+ }
+ ;
+
+ipportspec : ipspec {
+ $$.host = $1;
+ $$.port = NULL;
+ }
+ | ipspec PORT portspec {
+ $$.host = $1;
+ $$.port = $3;
+ }
+ | PORT portspec {
+ $$.host = NULL;
+ $$.port = $2;
+ }
+ ;
+
+ipspec : ANY { $$ = NULL; }
+ | xhost { $$ = $1; }
+ | '{' host_list '}' { $$ = $2; }
+ ;
+
+host_list : xhost { $$ = $1; }
+ | host_list comma xhost {
+ if ($3 == NULL)
+ $$ = $1;
+ else if ($1 == NULL)
+ $$ = $3;
+ else {
+ $1->tail->next = $3;
+ $1->tail = $3->tail;
+ $$ = $1;
+ }
+ }
+ ;
+
+xhost : not host {
+ struct node_host *n;
+
+ for (n = $2; n != NULL; n = n->next)
+ n->not = $1;
+ $$ = $2;
+ }
+ | NOROUTE {
+ $$ = calloc(1, sizeof(struct node_host));
+ if ($$ == NULL)
+ err(1, "xhost: calloc");
+ $$->addr.type = PF_ADDR_NOROUTE;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ ;
+
+host : STRING {
+ if (($$ = host($1)) == NULL) {
+ /* error. "any" is handled elsewhere */
+ free($1);
+ yyerror("could not parse host specification");
+ YYERROR;
+ }
+ free($1);
+
+ }
+ | STRING '/' number {
+ char *buf;
+
+ if (asprintf(&buf, "%s/%u", $1, $3) == -1)
+ err(1, "host: asprintf");
+ free($1);
+ if (($$ = host(buf)) == NULL) {
+ /* error. "any" is handled elsewhere */
+ free(buf);
+ yyerror("could not parse host specification");
+ YYERROR;
+ }
+ free(buf);
+ }
+ | dynaddr
+ | dynaddr '/' number {
+ struct node_host *n;
+
+ $$ = $1;
+ for (n = $1; n != NULL; n = n->next)
+ set_ipmask(n, $3);
+ }
+ | '<' STRING '>' {
+ if (strlen($2) >= PF_TABLE_NAME_SIZE) {
+ yyerror("table name '%s' too long", $2);
+ free($2);
+ YYERROR;
+ }
+ $$ = calloc(1, sizeof(struct node_host));
+ if ($$ == NULL)
+ err(1, "host: calloc");
+ $$->addr.type = PF_ADDR_TABLE;
+ if (strlcpy($$->addr.v.tblname, $2,
+ sizeof($$->addr.v.tblname)) >=
+ sizeof($$->addr.v.tblname))
+ errx(1, "host: strlcpy");
+ free($2);
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ ;
+
+number : STRING {
+ u_long ulval;
+
+ if (atoul($1, &ulval) == -1) {
+ yyerror("%s is not a number", $1);
+ free($1);
+ YYERROR;
+ } else
+ $$ = ulval;
+ free($1);
+ }
+ ;
+
+dynaddr : '(' STRING ')' {
+ int flags = 0;
+ char *p, *op;
+
+ op = $2;
+ while ((p = strrchr($2, ':')) != NULL) {
+ if (!strcmp(p+1, "network"))
+ flags |= PFI_AFLAG_NETWORK;
+ else if (!strcmp(p+1, "broadcast"))
+ flags |= PFI_AFLAG_BROADCAST;
+ else if (!strcmp(p+1, "peer"))
+ flags |= PFI_AFLAG_PEER;
+ else if (!strcmp(p+1, "0"))
+ flags |= PFI_AFLAG_NOALIAS;
+ else {
+ yyerror("interface %s has bad modifier",
+ $2);
+ free(op);
+ YYERROR;
+ }
+ *p = '\0';
+ }
+ if (flags & (flags - 1) & PFI_AFLAG_MODEMASK) {
+ free(op);
+ yyerror("illegal combination of "
+ "interface modifiers");
+ YYERROR;
+ }
+ $$ = calloc(1, sizeof(struct node_host));
+ if ($$ == NULL)
+ err(1, "address: calloc");
+ $$->af = 0;
+ set_ipmask($$, 128);
+ $$->addr.type = PF_ADDR_DYNIFTL;
+ $$->addr.iflags = flags;
+ if (strlcpy($$->addr.v.ifname, $2,
+ sizeof($$->addr.v.ifname)) >=
+ sizeof($$->addr.v.ifname)) {
+ free(op);
+ free($$);
+ yyerror("interface name too long");
+ YYERROR;
+ }
+ free(op);
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ ;
+
+portspec : port_item { $$ = $1; }
+ | '{' port_list '}' { $$ = $2; }
+ ;
+
+port_list : port_item { $$ = $1; }
+ | port_list comma port_item {
+ $1->tail->next = $3;
+ $1->tail = $3;
+ $$ = $1;
+ }
+ ;
+
+port_item : port {
+ $$ = calloc(1, sizeof(struct node_port));
+ if ($$ == NULL)
+ err(1, "port_item: calloc");
+ $$->port[0] = $1.a;
+ $$->port[1] = $1.b;
+ if ($1.t)
+ $$->op = PF_OP_RRG;
+ else
+ $$->op = PF_OP_EQ;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ | unaryop port {
+ if ($2.t) {
+ yyerror("':' cannot be used with an other "
+ "port operator");
+ YYERROR;
+ }
+ $$ = calloc(1, sizeof(struct node_port));
+ if ($$ == NULL)
+ err(1, "port_item: calloc");
+ $$->port[0] = $2.a;
+ $$->port[1] = $2.b;
+ $$->op = $1;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ | port PORTBINARY port {
+ if ($1.t || $3.t) {
+ yyerror("':' cannot be used with an other "
+ "port operator");
+ YYERROR;
+ }
+ $$ = calloc(1, sizeof(struct node_port));
+ if ($$ == NULL)
+ err(1, "port_item: calloc");
+ $$->port[0] = $1.a;
+ $$->port[1] = $3.a;
+ $$->op = $2;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ ;
+
+port : STRING {
+ char *p = strchr($1, ':');
+ struct servent *s = NULL;
+ u_long ulval;
+
+ if (p == NULL) {
+ if (atoul($1, &ulval) == 0) {
+ if (ulval > 65535) {
+ free($1);
+ yyerror("illegal port value %d",
+ ulval);
+ YYERROR;
+ }
+ $$.a = htons(ulval);
+ } else {
+ s = getservbyname($1, "tcp");
+ if (s == NULL)
+ s = getservbyname($1, "udp");
+ if (s == NULL) {
+ yyerror("unknown port %s", $1);
+ free($1);
+ YYERROR;
+ }
+ $$.a = s->s_port;
+ }
+ $$.b = 0;
+ $$.t = 0;
+ } else {
+ int port[2];
+
+ *p++ = 0;
+ if ((port[0] = getservice($1)) == -1 ||
+ (port[1] = getservice(p)) == -1) {
+ free($1);
+ YYERROR;
+ }
+ $$.a = port[0];
+ $$.b = port[1];
+ $$.t = PF_OP_RRG;
+ }
+ free($1);
+ }
+ ;
+
+uids : uid_item { $$ = $1; }
+ | '{' uid_list '}' { $$ = $2; }
+ ;
+
+uid_list : uid_item { $$ = $1; }
+ | uid_list comma uid_item {
+ $1->tail->next = $3;
+ $1->tail = $3;
+ $$ = $1;
+ }
+ ;
+
+uid_item : uid {
+ $$ = calloc(1, sizeof(struct node_uid));
+ if ($$ == NULL)
+ err(1, "uid_item: calloc");
+ $$->uid[0] = $1;
+ $$->uid[1] = $1;
+ $$->op = PF_OP_EQ;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ | unaryop uid {
+ if ($2 == UID_MAX && $1 != PF_OP_EQ && $1 != PF_OP_NE) {
+ yyerror("user unknown requires operator = or "
+ "!=");
+ YYERROR;
+ }
+ $$ = calloc(1, sizeof(struct node_uid));
+ if ($$ == NULL)
+ err(1, "uid_item: calloc");
+ $$->uid[0] = $2;
+ $$->uid[1] = $2;
+ $$->op = $1;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ | uid PORTBINARY uid {
+ if ($1 == UID_MAX || $3 == UID_MAX) {
+ yyerror("user unknown requires operator = or "
+ "!=");
+ YYERROR;
+ }
+ $$ = calloc(1, sizeof(struct node_uid));
+ if ($$ == NULL)
+ err(1, "uid_item: calloc");
+ $$->uid[0] = $1;
+ $$->uid[1] = $3;
+ $$->op = $2;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ ;
+
+uid : STRING {
+ u_long ulval;
+
+ if (atoul($1, &ulval) == -1) {
+ if (!strcmp($1, "unknown"))
+ $$ = UID_MAX;
+ else {
+ struct passwd *pw;
+
+ if ((pw = getpwnam($1)) == NULL) {
+ yyerror("unknown user %s", $1);
+ free($1);
+ YYERROR;
+ }
+ $$ = pw->pw_uid;
+ }
+ } else {
+ if (ulval >= UID_MAX) {
+ free($1);
+ yyerror("illegal uid value %lu", ulval);
+ YYERROR;
+ }
+ $$ = ulval;
+ }
+ free($1);
+ }
+ ;
+
+gids : gid_item { $$ = $1; }
+ | '{' gid_list '}' { $$ = $2; }
+ ;
+
+gid_list : gid_item { $$ = $1; }
+ | gid_list comma gid_item {
+ $1->tail->next = $3;
+ $1->tail = $3;
+ $$ = $1;
+ }
+ ;
+
+gid_item : gid {
+ $$ = calloc(1, sizeof(struct node_gid));
+ if ($$ == NULL)
+ err(1, "gid_item: calloc");
+ $$->gid[0] = $1;
+ $$->gid[1] = $1;
+ $$->op = PF_OP_EQ;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ | unaryop gid {
+ if ($2 == GID_MAX && $1 != PF_OP_EQ && $1 != PF_OP_NE) {
+ yyerror("group unknown requires operator = or "
+ "!=");
+ YYERROR;
+ }
+ $$ = calloc(1, sizeof(struct node_gid));
+ if ($$ == NULL)
+ err(1, "gid_item: calloc");
+ $$->gid[0] = $2;
+ $$->gid[1] = $2;
+ $$->op = $1;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ | gid PORTBINARY gid {
+ if ($1 == GID_MAX || $3 == GID_MAX) {
+ yyerror("group unknown requires operator = or "
+ "!=");
+ YYERROR;
+ }
+ $$ = calloc(1, sizeof(struct node_gid));
+ if ($$ == NULL)
+ err(1, "gid_item: calloc");
+ $$->gid[0] = $1;
+ $$->gid[1] = $3;
+ $$->op = $2;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ ;
+
+gid : STRING {
+ u_long ulval;
+
+ if (atoul($1, &ulval) == -1) {
+ if (!strcmp($1, "unknown"))
+ $$ = GID_MAX;
+ else {
+ struct group *grp;
+
+ if ((grp = getgrnam($1)) == NULL) {
+ yyerror("unknown group %s", $1);
+ free($1);
+ YYERROR;
+ }
+ $$ = grp->gr_gid;
+ }
+ } else {
+ if (ulval >= GID_MAX) {
+ yyerror("illegal gid value %lu", ulval);
+ free($1);
+ YYERROR;
+ }
+ $$ = ulval;
+ }
+ free($1);
+ }
+ ;
+
+flag : STRING {
+ int f;
+
+ if ((f = parse_flags($1)) < 0) {
+ yyerror("bad flags %s", $1);
+ free($1);
+ YYERROR;
+ }
+ free($1);
+ $$.b1 = f;
+ }
+ ;
+
+flags : FLAGS flag '/' flag { $$.b1 = $2.b1; $$.b2 = $4.b1; }
+ | FLAGS '/' flag { $$.b1 = 0; $$.b2 = $3.b1; }
+ ;
+
+icmpspec : ICMPTYPE icmp_item { $$ = $2; }
+ | ICMPTYPE '{' icmp_list '}' { $$ = $3; }
+ | ICMP6TYPE icmp6_item { $$ = $2; }
+ | ICMP6TYPE '{' icmp6_list '}' { $$ = $3; }
+ ;
+
+icmp_list : icmp_item { $$ = $1; }
+ | icmp_list comma icmp_item {
+ $1->tail->next = $3;
+ $1->tail = $3;
+ $$ = $1;
+ }
+ ;
+
+icmp6_list : icmp6_item { $$ = $1; }
+ | icmp6_list comma icmp6_item {
+ $1->tail->next = $3;
+ $1->tail = $3;
+ $$ = $1;
+ }
+ ;
+
+icmp_item : icmptype {
+ $$ = calloc(1, sizeof(struct node_icmp));
+ if ($$ == NULL)
+ err(1, "icmp_item: calloc");
+ $$->type = $1;
+ $$->code = 0;
+ $$->proto = IPPROTO_ICMP;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ | icmptype CODE STRING {
+ const struct icmpcodeent *p;
+ u_long ulval;
+
+ if (atoul($3, &ulval) == 0) {
+ if (ulval > 255) {
+ free($3);
+ yyerror("illegal icmp-code %d", ulval);
+ YYERROR;
+ }
+ } else {
+ if ((p = geticmpcodebyname($1-1, $3,
+ AF_INET)) == NULL) {
+ yyerror("unknown icmp-code %s", $3);
+ free($3);
+ YYERROR;
+ }
+ ulval = p->code;
+ }
+ free($3);
+ $$ = calloc(1, sizeof(struct node_icmp));
+ if ($$ == NULL)
+ err(1, "icmp_item: calloc");
+ $$->type = $1;
+ $$->code = ulval + 1;
+ $$->proto = IPPROTO_ICMP;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ ;
+
+icmp6_item : icmp6type {
+ $$ = calloc(1, sizeof(struct node_icmp));
+ if ($$ == NULL)
+ err(1, "icmp_item: calloc");
+ $$->type = $1;
+ $$->code = 0;
+ $$->proto = IPPROTO_ICMPV6;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ | icmp6type CODE STRING {
+ const struct icmpcodeent *p;
+ u_long ulval;
+
+ if (atoul($3, &ulval) == 0) {
+ if (ulval > 255) {
+ yyerror("illegal icmp6-code %ld",
+ ulval);
+ free($3);
+ YYERROR;
+ }
+ } else {
+ if ((p = geticmpcodebyname($1-1, $3,
+ AF_INET6)) == NULL) {
+ yyerror("unknown icmp6-code %s", $3);
+ free($3);
+ YYERROR;
+ }
+ ulval = p->code;
+ }
+ free($3);
+ $$ = calloc(1, sizeof(struct node_icmp));
+ if ($$ == NULL)
+ err(1, "icmp_item: calloc");
+ $$->type = $1;
+ $$->code = ulval + 1;
+ $$->proto = IPPROTO_ICMPV6;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ ;
+
+icmptype : STRING {
+ const struct icmptypeent *p;
+ u_long ulval;
+
+ if (atoul($1, &ulval) == 0) {
+ if (ulval > 255) {
+ yyerror("illegal icmp-type %d", ulval);
+ free($1);
+ YYERROR;
+ }
+ $$ = ulval + 1;
+ } else {
+ if ((p = geticmptypebyname($1, AF_INET)) ==
+ NULL) {
+ yyerror("unknown icmp-type %s", $1);
+ free($1);
+ YYERROR;
+ }
+ $$ = p->type + 1;
+ }
+ free($1);
+ }
+ ;
+
+icmp6type : STRING {
+ const struct icmptypeent *p;
+ u_long ulval;
+
+ if (atoul($1, &ulval) == 0) {
+ if (ulval > 255) {
+ yyerror("illegal icmp6-type %d", ulval);
+ free($1);
+ YYERROR;
+ }
+ $$ = ulval + 1;
+ } else {
+ if ((p = geticmptypebyname($1, AF_INET6)) ==
+ NULL) {
+ yyerror("unknown icmp6-type %s", $1);
+ free($1);
+ YYERROR;
+ }
+ $$ = p->type + 1;
+ }
+ free($1);
+ }
+ ;
+
+tos : TOS STRING {
+ if (!strcmp($2, "lowdelay"))
+ $$ = IPTOS_LOWDELAY;
+ else if (!strcmp($2, "throughput"))
+ $$ = IPTOS_THROUGHPUT;
+ else if (!strcmp($2, "reliability"))
+ $$ = IPTOS_RELIABILITY;
+ else if ($2[0] == '0' && $2[1] == 'x')
+ $$ = strtoul($2, NULL, 16);
+ else
+ $$ = strtoul($2, NULL, 10);
+ if (!$$ || $$ > 255) {
+ yyerror("illegal tos value %s", $2);
+ free($2);
+ YYERROR;
+ }
+ free($2);
+ }
+ ;
+
+sourcetrack : SOURCETRACK { $$ = PF_SRCTRACK; }
+ | SOURCETRACK GLOBAL { $$ = PF_SRCTRACK_GLOBAL; }
+ | SOURCETRACK RULE { $$ = PF_SRCTRACK_RULE; }
+ ;
+
+statelock : IFBOUND {
+ $$ = PFRULE_IFBOUND;
+ }
+ | GRBOUND {
+ $$ = PFRULE_GRBOUND;
+ }
+ | FLOATING {
+ $$ = 0;
+ }
+ ;
+
+keep : KEEP STATE state_opt_spec {
+ $$.action = PF_STATE_NORMAL;
+ $$.options = $3;
+ }
+ | MODULATE STATE state_opt_spec {
+ $$.action = PF_STATE_MODULATE;
+ $$.options = $3;
+ }
+ | SYNPROXY STATE state_opt_spec {
+ $$.action = PF_STATE_SYNPROXY;
+ $$.options = $3;
+ }
+ ;
+
+flush : /* empty */ { $$ = 0; }
+ | FLUSH { $$ = 1; }
+ ;
+
+state_opt_spec : '(' state_opt_list ')' { $$ = $2; }
+ | /* empty */ { $$ = NULL; }
+ ;
+
+state_opt_list : state_opt_item { $$ = $1; }
+ | state_opt_list comma state_opt_item {
+ $1->tail->next = $3;
+ $1->tail = $3;
+ $$ = $1;
+ }
+ ;
+
+state_opt_item : MAXIMUM number {
+ $$ = calloc(1, sizeof(struct node_state_opt));
+ if ($$ == NULL)
+ err(1, "state_opt_item: calloc");
+ $$->type = PF_STATE_OPT_MAX;
+ $$->data.max_states = $2;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ | NOSYNC {
+ $$ = calloc(1, sizeof(struct node_state_opt));
+ if ($$ == NULL)
+ err(1, "state_opt_item: calloc");
+ $$->type = PF_STATE_OPT_NOSYNC;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ | MAXSRCSTATES number {
+ $$ = calloc(1, sizeof(struct node_state_opt));
+ if ($$ == NULL)
+ err(1, "state_opt_item: calloc");
+ $$->type = PF_STATE_OPT_MAX_SRC_STATES;
+ $$->data.max_src_states = $2;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ | MAXSRCCONN number {
+ $$ = calloc(1, sizeof(struct node_state_opt));
+ if ($$ == NULL)
+ err(1, "state_opt_item: calloc");
+ $$->type = PF_STATE_OPT_MAX_SRC_CONN;
+ $$->data.max_src_conn = $2;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ | MAXSRCCONNRATE number '/' number {
+ $$ = calloc(1, sizeof(struct node_state_opt));
+ if ($$ == NULL)
+ err(1, "state_opt_item: calloc");
+ $$->type = PF_STATE_OPT_MAX_SRC_CONN_RATE;
+ $$->data.max_src_conn_rate.limit = $2;
+ $$->data.max_src_conn_rate.seconds = $4;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ | OVERLOAD '<' STRING '>' flush {
+ if (strlen($3) >= PF_TABLE_NAME_SIZE) {
+ yyerror("table name '%s' too long", $3);
+ free($3);
+ YYERROR;
+ }
+ $$ = calloc(1, sizeof(struct node_state_opt));
+ if ($$ == NULL)
+ err(1, "state_opt_item: calloc");
+ if (strlcpy($$->data.overload.tblname, $3,
+ PF_TABLE_NAME_SIZE) >= PF_TABLE_NAME_SIZE)
+ errx(1, "state_opt_item: strlcpy");
+ free($3);
+ $$->type = PF_STATE_OPT_OVERLOAD;
+ $$->data.overload.flush = $5;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ | MAXSRCNODES number {
+ $$ = calloc(1, sizeof(struct node_state_opt));
+ if ($$ == NULL)
+ err(1, "state_opt_item: calloc");
+ $$->type = PF_STATE_OPT_MAX_SRC_NODES;
+ $$->data.max_src_nodes = $2;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ | sourcetrack {
+ $$ = calloc(1, sizeof(struct node_state_opt));
+ if ($$ == NULL)
+ err(1, "state_opt_item: calloc");
+ $$->type = PF_STATE_OPT_SRCTRACK;
+ $$->data.src_track = $1;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ | statelock {
+ $$ = calloc(1, sizeof(struct node_state_opt));
+ if ($$ == NULL)
+ err(1, "state_opt_item: calloc");
+ $$->type = PF_STATE_OPT_STATELOCK;
+ $$->data.statelock = $1;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ | STRING number {
+ int i;
+
+ for (i = 0; pf_timeouts[i].name &&
+ strcmp(pf_timeouts[i].name, $1); ++i)
+ ; /* nothing */
+ if (!pf_timeouts[i].name) {
+ yyerror("illegal timeout name %s", $1);
+ free($1);
+ YYERROR;
+ }
+ if (strchr(pf_timeouts[i].name, '.') == NULL) {
+ yyerror("illegal state timeout %s", $1);
+ free($1);
+ YYERROR;
+ }
+ free($1);
+ $$ = calloc(1, sizeof(struct node_state_opt));
+ if ($$ == NULL)
+ err(1, "state_opt_item: calloc");
+ $$->type = PF_STATE_OPT_TIMEOUT;
+ $$->data.timeout.number = pf_timeouts[i].timeout;
+ $$->data.timeout.seconds = $2;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ ;
+
+label : LABEL STRING {
+ $$ = $2;
+ }
+ ;
+
+qname : QUEUE STRING {
+ $$.qname = $2;
+ }
+ | QUEUE '(' STRING ')' {
+ $$.qname = $3;
+ }
+ | QUEUE '(' STRING comma STRING ')' {
+ $$.qname = $3;
+ $$.pqname = $5;
+ }
+ ;
+
+no : /* empty */ { $$ = 0; }
+ | NO { $$ = 1; }
+ ;
+
+rport : STRING {
+ char *p = strchr($1, ':');
+
+ if (p == NULL) {
+ if (($$.a = getservice($1)) == -1) {
+ free($1);
+ YYERROR;
+ }
+ $$.b = $$.t = 0;
+ } else if (!strcmp(p+1, "*")) {
+ *p = 0;
+ if (($$.a = getservice($1)) == -1) {
+ free($1);
+ YYERROR;
+ }
+ $$.b = 0;
+ $$.t = 1;
+ } else {
+ *p++ = 0;
+ if (($$.a = getservice($1)) == -1 ||
+ ($$.b = getservice(p)) == -1) {
+ free($1);
+ YYERROR;
+ }
+ if ($$.a == $$.b)
+ $$.b = 0;
+ $$.t = 0;
+ }
+ free($1);
+ }
+ ;
+
+redirspec : host { $$ = $1; }
+ | '{' redir_host_list '}' { $$ = $2; }
+ ;
+
+redir_host_list : host { $$ = $1; }
+ | redir_host_list comma host {
+ $1->tail->next = $3;
+ $1->tail = $3->tail;
+ $$ = $1;
+ }
+ ;
+
+redirpool : /* empty */ { $$ = NULL; }
+ | ARROW redirspec {
+ $$ = calloc(1, sizeof(struct redirection));
+ if ($$ == NULL)
+ err(1, "redirection: calloc");
+ $$->host = $2;
+ $$->rport.a = $$->rport.b = $$->rport.t = 0;
+ }
+ | ARROW redirspec PORT rport {
+ $$ = calloc(1, sizeof(struct redirection));
+ if ($$ == NULL)
+ err(1, "redirection: calloc");
+ $$->host = $2;
+ $$->rport = $4;
+ }
+ ;
+
+hashkey : /* empty */
+ {
+ $$ = calloc(1, sizeof(struct pf_poolhashkey));
+ if ($$ == NULL)
+ err(1, "hashkey: calloc");
+ $$->key32[0] = arc4random();
+ $$->key32[1] = arc4random();
+ $$->key32[2] = arc4random();
+ $$->key32[3] = arc4random();
+ }
+ | string
+ {
+ if (!strncmp($1, "0x", 2)) {
+ if (strlen($1) != 34) {
+ free($1);
+ yyerror("hex key must be 128 bits "
+ "(32 hex digits) long");
+ YYERROR;
+ }
+ $$ = calloc(1, sizeof(struct pf_poolhashkey));
+ if ($$ == NULL)
+ err(1, "hashkey: calloc");
+
+ if (sscanf($1, "0x%8x%8x%8x%8x",
+ &$$->key32[0], &$$->key32[1],
+ &$$->key32[2], &$$->key32[3]) != 4) {
+ free($$);
+ free($1);
+ yyerror("invalid hex key");
+ YYERROR;
+ }
+ } else {
+ MD5_CTX context;
+
+ $$ = calloc(1, sizeof(struct pf_poolhashkey));
+ if ($$ == NULL)
+ err(1, "hashkey: calloc");
+ MD5Init(&context);
+ MD5Update(&context, (unsigned char *)$1,
+ strlen($1));
+ MD5Final((unsigned char *)$$, &context);
+ HTONL($$->key32[0]);
+ HTONL($$->key32[1]);
+ HTONL($$->key32[2]);
+ HTONL($$->key32[3]);
+ }
+ free($1);
+ }
+ ;
+
+pool_opts : { bzero(&pool_opts, sizeof pool_opts); }
+ pool_opts_l
+ { $$ = pool_opts; }
+ | /* empty */ {
+ bzero(&pool_opts, sizeof pool_opts);
+ $$ = pool_opts;
+ }
+ ;
+
+pool_opts_l : pool_opts_l pool_opt
+ | pool_opt
+ ;
+
+pool_opt : BITMASK {
+ if (pool_opts.type) {
+ yyerror("pool type cannot be redefined");
+ YYERROR;
+ }
+ pool_opts.type = PF_POOL_BITMASK;
+ }
+ | RANDOM {
+ if (pool_opts.type) {
+ yyerror("pool type cannot be redefined");
+ YYERROR;
+ }
+ pool_opts.type = PF_POOL_RANDOM;
+ }
+ | SOURCEHASH hashkey {
+ if (pool_opts.type) {
+ yyerror("pool type cannot be redefined");
+ YYERROR;
+ }
+ pool_opts.type = PF_POOL_SRCHASH;
+ pool_opts.key = $2;
+ }
+ | ROUNDROBIN {
+ if (pool_opts.type) {
+ yyerror("pool type cannot be redefined");
+ YYERROR;
+ }
+ pool_opts.type = PF_POOL_ROUNDROBIN;
+ }
+ | STATICPORT {
+ if (pool_opts.staticport) {
+ yyerror("static-port cannot be redefined");
+ YYERROR;
+ }
+ pool_opts.staticport = 1;
+ }
+ | STICKYADDRESS {
+ if (filter_opts.marker & POM_STICKYADDRESS) {
+ yyerror("sticky-address cannot be redefined");
+ YYERROR;
+ }
+ pool_opts.marker |= POM_STICKYADDRESS;
+ pool_opts.opts |= PF_POOL_STICKYADDR;
+ }
+ ;
+
+redirection : /* empty */ { $$ = NULL; }
+ | ARROW host {
+ $$ = calloc(1, sizeof(struct redirection));
+ if ($$ == NULL)
+ err(1, "redirection: calloc");
+ $$->host = $2;
+ $$->rport.a = $$->rport.b = $$->rport.t = 0;
+ }
+ | ARROW host PORT rport {
+ $$ = calloc(1, sizeof(struct redirection));
+ if ($$ == NULL)
+ err(1, "redirection: calloc");
+ $$->host = $2;
+ $$->rport = $4;
+ }
+ ;
+
+natpass : /* empty */ { $$ = 0; }
+ | PASS { $$ = 1; }
+ ;
+
+nataction : no NAT natpass {
+ $$.b2 = $$.w = 0;
+ if ($1)
+ $$.b1 = PF_NONAT;
+ else
+ $$.b1 = PF_NAT;
+ $$.b2 = $3;
+ }
+ | no RDR natpass {
+ $$.b2 = $$.w = 0;
+ if ($1)
+ $$.b1 = PF_NORDR;
+ else
+ $$.b1 = PF_RDR;
+ $$.b2 = $3;
+ }
+ ;
+
+natrule : nataction interface af proto fromto tag redirpool pool_opts
+ {
+ struct pf_rule r;
+
+ if (check_rulestate(PFCTL_STATE_NAT))
+ YYERROR;
+
+ memset(&r, 0, sizeof(r));
+
+ r.action = $1.b1;
+ r.natpass = $1.b2;
+ r.af = $3;
+
+ if (!r.af) {
+ if ($5.src.host && $5.src.host->af &&
+ !$5.src.host->ifindex)
+ r.af = $5.src.host->af;
+ else if ($5.dst.host && $5.dst.host->af &&
+ !$5.dst.host->ifindex)
+ r.af = $5.dst.host->af;
+ }
+
+ if ($6 != NULL)
+ if (strlcpy(r.tagname, $6, PF_TAG_NAME_SIZE) >=
+ PF_TAG_NAME_SIZE) {
+ yyerror("tag too long, max %u chars",
+ PF_TAG_NAME_SIZE - 1);
+ YYERROR;
+ }
+
+ if (r.action == PF_NONAT || r.action == PF_NORDR) {
+ if ($7 != NULL) {
+ yyerror("translation rule with 'no' "
+ "does not need '->'");
+ YYERROR;
+ }
+ } else {
+ if ($7 == NULL || $7->host == NULL) {
+ yyerror("translation rule requires '-> "
+ "address'");
+ YYERROR;
+ }
+ if (!r.af && ! $7->host->ifindex)
+ r.af = $7->host->af;
+
+ remove_invalid_hosts(&$7->host, &r.af);
+ if (invalid_redirect($7->host, r.af))
+ YYERROR;
+ if (check_netmask($7->host, r.af))
+ YYERROR;
+
+ r.rpool.proxy_port[0] = ntohs($7->rport.a);
+
+ switch (r.action) {
+ case PF_RDR:
+ if (!$7->rport.b && $7->rport.t &&
+ $5.dst.port != NULL) {
+ r.rpool.proxy_port[1] =
+ ntohs($7->rport.a) +
+ (ntohs(
+ $5.dst.port->port[1]) -
+ ntohs(
+ $5.dst.port->port[0]));
+ } else
+ r.rpool.proxy_port[1] =
+ ntohs($7->rport.b);
+ break;
+ case PF_NAT:
+ r.rpool.proxy_port[1] =
+ ntohs($7->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 = $8.type;
+ if ((r.rpool.opts & PF_POOL_TYPEMASK) ==
+ PF_POOL_NONE && ($7->host->next != NULL ||
+ $7->host->addr.type == PF_ADDR_TABLE ||
+ DYNIF_MULTIADDR($7->host->addr)))
+ r.rpool.opts = PF_POOL_ROUNDROBIN;
+ if ((r.rpool.opts & PF_POOL_TYPEMASK) !=
+ PF_POOL_ROUNDROBIN &&
+ disallow_table($7->host, "tables are only "
+ "supported in round-robin redirection "
+ "pools"))
+ YYERROR;
+ if ((r.rpool.opts & PF_POOL_TYPEMASK) !=
+ PF_POOL_ROUNDROBIN &&
+ disallow_alias($7->host, "interface (%s) "
+ "is only supported in round-robin "
+ "redirection pools"))
+ YYERROR;
+ if ($7->host->next != NULL) {
+ if ((r.rpool.opts & PF_POOL_TYPEMASK) !=
+ PF_POOL_ROUNDROBIN) {
+ yyerror("only round-robin "
+ "valid for multiple "
+ "redirection addresses");
+ YYERROR;
+ }
+ }
+ }
+
+ if ($8.key != NULL)
+ memcpy(&r.rpool.key, $8.key,
+ sizeof(struct pf_poolhashkey));
+
+ if ($8.opts)
+ r.rpool.opts |= $8.opts;
+
+ if ($8.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;
+ }
+
+ expand_rule(&r, $2, $7 == NULL ? NULL : $7->host, $4,
+ $5.src_os, $5.src.host, $5.src.port, $5.dst.host,
+ $5.dst.port, 0, 0, 0, "");
+ free($7);
+ }
+ ;
+
+binatrule : no BINAT natpass interface af proto FROM host TO ipspec tag
+ redirection
+ {
+ struct pf_rule binat;
+ struct pf_pooladdr *pa;
+
+ if (check_rulestate(PFCTL_STATE_NAT))
+ YYERROR;
+
+ memset(&binat, 0, sizeof(binat));
+
+ if ($1)
+ binat.action = PF_NOBINAT;
+ else
+ binat.action = PF_BINAT;
+ binat.natpass = $3;
+ binat.af = $5;
+ if (!binat.af && $8 != NULL && $8->af)
+ binat.af = $8->af;
+ if (!binat.af && $10 != NULL && $10->af)
+ binat.af = $10->af;
+ if (!binat.af && $12 != NULL && $12->host)
+ binat.af = $12->host->af;
+ if (!binat.af) {
+ yyerror("address family (inet/inet6) "
+ "undefined");
+ YYERROR;
+ }
+
+ if ($4 != NULL) {
+ memcpy(binat.ifname, $4->ifname,
+ sizeof(binat.ifname));
+ binat.ifnot = $4->not;
+ free($4);
+ }
+ if ($11 != NULL)
+ if (strlcpy(binat.tagname, $11,
+ PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) {
+ yyerror("tag too long, max %u chars",
+ PF_TAG_NAME_SIZE - 1);
+ YYERROR;
+ }
+
+ if ($6 != NULL) {
+ binat.proto = $6->proto;
+ free($6);
+ }
+
+ if ($8 != NULL && disallow_table($8, "invalid use of "
+ "table <%s> as the source address of a binat rule"))
+ YYERROR;
+ if ($8 != NULL && disallow_alias($8, "invalid use of "
+ "interface (%s) as the source address of a binat "
+ "rule"))
+ YYERROR;
+ if ($12 != NULL && $12->host != NULL && disallow_table(
+ $12->host, "invalid use of table <%s> as the "
+ "redirect address of a binat rule"))
+ YYERROR;
+ if ($12 != NULL && $12->host != NULL && disallow_alias(
+ $12->host, "invalid use of interface (%s) as the "
+ "redirect address of a binat rule"))
+ YYERROR;
+
+ if ($8 != NULL) {
+ if ($8->next) {
+ yyerror("multiple binat ip addresses");
+ YYERROR;
+ }
+ if ($8->addr.type == PF_ADDR_DYNIFTL)
+ $8->af = binat.af;
+ if ($8->af != binat.af) {
+ yyerror("binat ip versions must match");
+ YYERROR;
+ }
+ if (check_netmask($8, binat.af))
+ YYERROR;
+ memcpy(&binat.src.addr, &$8->addr,
+ sizeof(binat.src.addr));
+ free($8);
+ }
+ if ($10 != NULL) {
+ if ($10->next) {
+ yyerror("multiple binat ip addresses");
+ YYERROR;
+ }
+ if ($10->af != binat.af && $10->af) {
+ yyerror("binat ip versions must match");
+ YYERROR;
+ }
+ if (check_netmask($10, binat.af))
+ YYERROR;
+ memcpy(&binat.dst.addr, &$10->addr,
+ sizeof(binat.dst.addr));
+ binat.dst.neg = $10->not;
+ free($10);
+ }
+
+ if (binat.action == PF_NOBINAT) {
+ if ($12 != NULL) {
+ yyerror("'no binat' rule does not need"
+ " '->'");
+ YYERROR;
+ }
+ } else {
+ if ($12 == NULL || $12->host == NULL) {
+ yyerror("'binat' rule requires"
+ " '-> address'");
+ YYERROR;
+ }
+
+ remove_invalid_hosts(&$12->host, &binat.af);
+ if (invalid_redirect($12->host, binat.af))
+ YYERROR;
+ if ($12->host->next != NULL) {
+ yyerror("binat rule must redirect to "
+ "a single address");
+ YYERROR;
+ }
+ if (check_netmask($12->host, binat.af))
+ YYERROR;
+
+ if (!PF_AZERO(&binat.src.addr.v.a.mask,
+ binat.af) &&
+ !PF_AEQ(&binat.src.addr.v.a.mask,
+ &$12->host->addr.v.a.mask, binat.af)) {
+ yyerror("'binat' source mask and "
+ "redirect mask must be the same");
+ YYERROR;
+ }
+
+ TAILQ_INIT(&binat.rpool.list);
+ pa = calloc(1, sizeof(struct pf_pooladdr));
+ if (pa == NULL)
+ err(1, "binat: calloc");
+ pa->addr = $12->host->addr;
+ pa->ifname[0] = 0;
+ TAILQ_INSERT_TAIL(&binat.rpool.list,
+ pa, entries);
+
+ free($12);
+ }
+
+ pfctl_add_rule(pf, &binat, "");
+ }
+ ;
+
+tag : /* empty */ { $$ = NULL; }
+ | TAG STRING { $$ = $2; }
+ ;
+
+route_host : STRING {
+ $$ = calloc(1, sizeof(struct node_host));
+ if ($$ == NULL)
+ err(1, "route_host: calloc");
+ $$->ifname = $1;
+ set_ipmask($$, 128);
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ | '(' STRING host ')' {
+ $$ = $3;
+ $$->ifname = $2;
+ }
+ ;
+
+route_host_list : route_host { $$ = $1; }
+ | route_host_list comma route_host {
+ 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;
+ }
+ ;
+
+routespec : route_host { $$ = $1; }
+ | '{' route_host_list '}' { $$ = $2; }
+ ;
+
+route : /* empty */ {
+ $$.host = NULL;
+ $$.rt = 0;
+ $$.pool_opts = 0;
+ }
+ | FASTROUTE {
+ $$.host = NULL;
+ $$.rt = PF_FASTROUTE;
+ $$.pool_opts = 0;
+ }
+ | ROUTETO routespec pool_opts {
+ $$.host = $2;
+ $$.rt = PF_ROUTETO;
+ $$.pool_opts = $3.type | $3.opts;
+ if ($3.key != NULL)
+ $$.key = $3.key;
+ }
+ | REPLYTO routespec pool_opts {
+ $$.host = $2;
+ $$.rt = PF_REPLYTO;
+ $$.pool_opts = $3.type | $3.opts;
+ if ($3.key != NULL)
+ $$.key = $3.key;
+ }
+ | DUPTO routespec pool_opts {
+ $$.host = $2;
+ $$.rt = PF_DUPTO;
+ $$.pool_opts = $3.type | $3.opts;
+ if ($3.key != NULL)
+ $$.key = $3.key;
+ }
+ ;
+
+timeout_spec : STRING number
+ {
+ if (check_rulestate(PFCTL_STATE_OPTION)) {
+ free($1);
+ YYERROR;
+ }
+ if (pfctl_set_timeout(pf, $1, $2, 0) != 0) {
+ yyerror("unknown timeout %s", $1);
+ free($1);
+ YYERROR;
+ }
+ free($1);
+ }
+ ;
+
+timeout_list : timeout_list comma timeout_spec
+ | timeout_spec
+ ;
+
+limit_spec : STRING number
+ {
+ if (check_rulestate(PFCTL_STATE_OPTION)) {
+ free($1);
+ YYERROR;
+ }
+ if (pfctl_set_limit(pf, $1, $2) != 0) {
+ yyerror("unable to set limit %s %u", $1, $2);
+ free($1);
+ YYERROR;
+ }
+ free($1);
+ }
+ ;
+
+limit_list : limit_list comma limit_spec
+ | limit_spec
+ ;
+
+comma : ','
+ | /* empty */
+ ;
+
+yesno : NO { $$ = 0; }
+ | STRING {
+ if (!strcmp($1, "yes"))
+ $$ = 1;
+ else {
+ free($1);
+ YYERROR;
+ }
+ free($1);
+ }
+ ;
+
+unaryop : '=' { $$ = PF_OP_EQ; }
+ | '!' '=' { $$ = PF_OP_NE; }
+ | '<' '=' { $$ = PF_OP_LE; }
+ | '<' { $$ = PF_OP_LT; }
+ | '>' '=' { $$ = PF_OP_GE; }
+ | '>' { $$ = PF_OP_GT; }
+ ;
+
+%%
+
+int
+yyerror(const char *fmt, ...)
{
- int i;
+ va_list ap;
+ extern char *infile;
- for (i = 0; tcpflags[i]; ++i)
- if (f & (1 << i))
- printf("%c", tcpflags[i]);
+ errors = 1;
+ va_start(ap, fmt);
+ fprintf(stderr, "%s:%d: ", infile, yylval.lineno);
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+ return (0);
}
-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
+disallow_table(struct node_host *h, const char *fmt)
{
- char buf[PF_OSFP_LEN*3];
- if (src->addr.type == PF_ADDR_ADDRMASK &&
- dst->addr.type == PF_ADDR_ADDRMASK &&
- PF_AZERO(&src->addr.v.a.addr, AF_INET6) &&
- PF_AZERO(&src->addr.v.a.mask, AF_INET6) &&
- PF_AZERO(&dst->addr.v.a.addr, AF_INET6) &&
- PF_AZERO(&dst->addr.v.a.mask, AF_INET6) &&
- !src->neg && !dst->neg &&
- !src->port_op && !dst->port_op &&
- osfp == PF_OSFP_ANY)
- printf(" all");
- else {
- printf(" from ");
- if (src->neg)
- printf("! ");
- print_addr(&src->addr, af, verbose);
- if (src->port_op)
- print_port(src->port_op, src->port[0],
- src->port[1],
- proto == IPPROTO_TCP ? "tcp" : "udp");
- if (osfp != PF_OSFP_ANY)
- printf(" os \"%s\"", pfctl_lookup_fingerprint(osfp, buf,
- sizeof(buf)));
-
- printf(" to ");
- if (dst->neg)
- printf("! ");
- print_addr(&dst->addr, af, verbose);
- if (dst->port_op)
- print_port(dst->port_op, dst->port[0],
- dst->port[1],
- proto == IPPROTO_TCP ? "tcp" : "udp");
- }
+ for (; h != NULL; h = h->next)
+ if (h->addr.type == PF_ADDR_TABLE) {
+ yyerror(fmt, h->addr.v.tblname);
+ return (1);
+ }
+ return (0);
}
-void
-print_pool(struct pf_pool *pool, u_int16_t p1, u_int16_t p2,
- sa_family_t af, int id)
+int
+disallow_alias(struct node_host *h, const char *fmt)
{
- struct pf_pooladdr *pooladdr;
-
- if ((TAILQ_FIRST(&pool->list) != NULL) &&
- TAILQ_NEXT(TAILQ_FIRST(&pool->list), entries) != NULL)
- printf("{ ");
- TAILQ_FOREACH(pooladdr, &pool->list, entries){
- switch (id) {
- case PF_NAT:
- case PF_RDR:
- case PF_BINAT:
- print_addr(&pooladdr->addr, af, 0);
- break;
- case PF_PASS:
- if (PF_AZERO(&pooladdr->addr.v.a.addr, af))
- printf("%s", pooladdr->ifname);
- else {
- printf("(%s ", pooladdr->ifname);
- print_addr(&pooladdr->addr, af, 0);
- printf(")");
- }
- break;
- default:
- break;
+ for (; h != NULL; h = h->next)
+ if (DYNIF_MULTIADDR(h->addr)) {
+ yyerror(fmt, h->addr.v.tblname);
+ return (1);
}
- if (TAILQ_NEXT(pooladdr, entries) != NULL)
- printf(", ");
- else if (TAILQ_NEXT(TAILQ_FIRST(&pool->list), entries) != NULL)
- printf(" }");
- }
- switch (id) {
+ return (0);
+}
+
+int
+rule_consistent(struct pf_rule *r)
+{
+ int problems = 0;
+
+ switch (r->action) {
+ case PF_PASS:
+ case PF_DROP:
+ case PF_SCRUB:
+ case PF_NOSCRUB:
+ problems = filter_consistent(r);
+ break;
case PF_NAT:
- if ((p1 != PF_NAT_PROXY_PORT_LOW ||
- p2 != PF_NAT_PROXY_PORT_HIGH) && (p1 != 0 || p2 != 0)) {
- if (p1 == p2)
- printf(" port %u", p1);
- else
- printf(" port %u:%u", p1, p2);
- }
+ case PF_NONAT:
+ problems = nat_consistent(r);
break;
case PF_RDR:
- if (p1) {
- printf(" port %u", p1);
- if (p2 && (p2 != p1))
- printf(":%u", p2);
- }
+ case PF_NORDR:
+ problems = rdr_consistent(r);
break;
+ case PF_BINAT:
+ case PF_NOBINAT:
default:
break;
}
- switch (pool->opts & PF_POOL_TYPEMASK) {
- case PF_POOL_NONE:
- break;
- case PF_POOL_BITMASK:
- printf(" bitmask");
- break;
- case PF_POOL_RANDOM:
- printf(" random");
- break;
- case PF_POOL_SRCHASH:
- printf(" source-hash 0x%08x%08x%08x%08x",
- pool->key.key32[0], pool->key.key32[1],
- pool->key.key32[2], pool->key.key32[3]);
- break;
- case PF_POOL_ROUNDROBIN:
- printf(" round-robin");
- break;
- }
- if (pool->opts & PF_POOL_STICKYADDR)
- printf(" sticky-address");
- if (id == PF_NAT && p1 == 0 && p2 == 0)
- printf(" static-port");
+ return (problems);
}
-const char *pf_reasons[PFRES_MAX+1] = PFRES_NAMES;
-const char *pf_lcounters[LCNT_MAX+1] = LCNT_NAMES;
-const char *pf_fcounters[FCNT_MAX+1] = FCNT_NAMES;
-const char *pf_scounters[FCNT_MAX+1] = FCNT_NAMES;
-
-void
-print_status(struct pf_status *s, int opts)
+int
+filter_consistent(struct pf_rule *r)
{
- char statline[80], *running;
- time_t runtime;
- int i;
-
- runtime = time(NULL) - s->since;
- running = s->running ? "Enabled" : "Disabled";
-
- if (s->since) {
- unsigned sec, min, hrs, day = runtime;
-
- sec = day % 60;
- day /= 60;
- min = day % 60;
- day /= 60;
- hrs = day % 24;
- day /= 24;
- snprintf(statline, sizeof(statline),
- "Status: %s for %u days %.2u:%.2u:%.2u",
- running, day, hrs, min, sec);
- } else
- snprintf(statline, sizeof(statline), "Status: %s", running);
- printf("%-44s", statline);
- switch (s->debug) {
- case PF_DEBUG_NONE:
- printf("%15s\n\n", "Debug: None");
- break;
- case PF_DEBUG_URGENT:
- printf("%15s\n\n", "Debug: Urgent");
- break;
- case PF_DEBUG_MISC:
- printf("%15s\n\n", "Debug: Misc");
- break;
- case PF_DEBUG_NOISY:
- printf("%15s\n\n", "Debug: Loud");
- break;
+ int problems = 0;
+
+ if (r->proto != IPPROTO_TCP && r->proto != IPPROTO_UDP &&
+ (r->src.port_op || r->dst.port_op)) {
+ yyerror("port only applies to tcp/udp");
+ problems++;
}
- printf("Hostid: 0x%08x\n\n", ntohl(s->hostid));
- if (s->ifname[0] != 0) {
- printf("Interface Stats for %-16s %5s %16s\n",
- s->ifname, "IPv4", "IPv6");
- printf(" %-25s %14llu %16llu\n", "Bytes In",
- (unsigned long long)s->bcounters[0][0],
- (unsigned long long)s->bcounters[1][0]);
- printf(" %-25s %14llu %16llu\n", "Bytes Out",
- (unsigned long long)s->bcounters[0][1],
- (unsigned long long)s->bcounters[1][1]);
- printf(" Packets In\n");
- printf(" %-23s %14llu %16llu\n", "Passed",
- (unsigned long long)s->pcounters[0][0][PF_PASS],
- (unsigned long long)s->pcounters[1][0][PF_PASS]);
- printf(" %-23s %14llu %16llu\n", "Blocked",
- (unsigned long long)s->pcounters[0][0][PF_DROP],
- (unsigned long long)s->pcounters[1][0][PF_DROP]);
- printf(" Packets Out\n");
- printf(" %-23s %14llu %16llu\n", "Passed",
- (unsigned long long)s->pcounters[0][1][PF_PASS],
- (unsigned long long)s->pcounters[1][1][PF_PASS]);
- printf(" %-23s %14llu %16llu\n\n", "Blocked",
- (unsigned long long)s->pcounters[0][1][PF_DROP],
- (unsigned long long)s->pcounters[1][1][PF_DROP]);
+ if (r->proto != IPPROTO_ICMP && r->proto != IPPROTO_ICMPV6 &&
+ (r->type || r->code)) {
+ yyerror("icmp-type/code only applies to icmp");
+ problems++;
}
- printf("%-27s %14s %16s\n", "State Table", "Total", "Rate");
- printf(" %-25s %14u %14s\n", "current entries", s->states, "");
- for (i = 0; i < FCNT_MAX; i++) {
- printf(" %-25s %14llu ", pf_fcounters[i],
- (unsigned long long)s->fcounters[i]);
- if (runtime > 0)
- printf("%14.1f/s\n",
- (double)s->fcounters[i] / (double)runtime);
- else
- printf("%14s\n", "");
+ if (!r->af && (r->type || r->code)) {
+ yyerror("must indicate address family with icmp-type/code");
+ problems++;
}
- if (opts & PF_OPT_VERBOSE) {
- printf("Source Tracking Table\n");
- printf(" %-25s %14u %14s\n", "current entries",
- s->src_nodes, "");
- for (i = 0; i < SCNT_MAX; i++) {
- printf(" %-25s %14lld ", pf_scounters[i],
- s->scounters[i]);
- if (runtime > 0)
- printf("%14.1f/s\n",
- (double)s->scounters[i] / (double)runtime);
- else
- printf("%14s\n", "");
- }
+ if ((r->proto == IPPROTO_ICMP && r->af == AF_INET6) ||
+ (r->proto == IPPROTO_ICMPV6 && r->af == AF_INET)) {
+ yyerror("proto %s doesn't match address family %s",
+ r->proto == IPPROTO_ICMP ? "icmp" : "icmp6",
+ r->af == AF_INET ? "inet" : "inet6");
+ problems++;
}
- printf("Counters\n");
- for (i = 0; i < PFRES_MAX; i++) {
- printf(" %-25s %14llu ", pf_reasons[i],
- (unsigned long long)s->counters[i]);
- if (runtime > 0)
- printf("%14.1f/s\n",
- (double)s->counters[i] / (double)runtime);
- else
- printf("%14s\n", "");
+ if (r->allow_opts && r->action != PF_PASS) {
+ yyerror("allow-opts can only be specified for pass rules");
+ problems++;
}
- if (opts & PF_OPT_VERBOSE) {
- printf("Limit Counters\n");
- for (i = 0; i < LCNT_MAX; i++) {
- printf(" %-25s %14lld ", pf_lcounters[i],
- s->lcounters[i]);
- if (runtime > 0)
- printf("%14.1f/s\n",
- (double)s->lcounters[i] / (double)runtime);
- else
- printf("%14s\n", "");
- }
+ if (r->rule_flag & PFRULE_FRAGMENT && (r->src.port_op ||
+ r->dst.port_op || r->flagset || r->type || r->code)) {
+ yyerror("fragments can be filtered only on IP header fields");
+ problems++;
+ }
+ if (r->rule_flag & PFRULE_RETURNRST && r->proto != IPPROTO_TCP) {
+ yyerror("return-rst can only be applied to TCP rules");
+ problems++;
}
+ if (r->max_src_nodes && !(r->rule_flag & PFRULE_RULESRCTRACK)) {
+ yyerror("max-src-nodes requires 'source-track rule'");
+ problems++;
+ }
+ if (r->action == PF_DROP && r->keep_state) {
+ yyerror("keep state on block rules doesn't make sense");
+ problems++;
+ }
+ if ((r->tagname[0] || r->match_tagname[0]) && !r->keep_state &&
+ r->action == PF_PASS) {
+ yyerror("tags cannot be used without keep state");
+ problems++;
+ }
+ return (-problems);
}
-void
-print_src_node(struct pf_src_node *sn, int opts)
+int
+nat_consistent(struct pf_rule *r)
{
- struct pf_addr_wrap aw;
- int min, sec;
+ return (0); /* yeah! */
+}
- memset(&aw, 0, sizeof(aw));
- if (sn->af == AF_INET)
- aw.v.a.mask.addr32[0] = 0xffffffff;
- else
- memset(&aw.v.a.mask, 0xff, sizeof(aw.v.a.mask));
-
- aw.v.a.addr = sn->addr;
- 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);
- 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);
- if (opts & PF_OPT_VERBOSE) {
- sec = sn->creation % 60;
- sn->creation /= 60;
- min = sn->creation % 60;
- sn->creation /= 60;
- printf(" age %.2u:%.2u:%.2u", 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",
- sn->expire, min, sec);
- }
- printf(", %u pkts, %u bytes", sn->packets, sn->bytes);
- switch (sn->ruletype) {
- case PF_NAT:
- if (sn->rule.nr != -1)
- printf(", nat rule %u", sn->rule.nr);
- break;
- case PF_RDR:
- if (sn->rule.nr != -1)
- printf(", rdr rule %u", sn->rule.nr);
- break;
- case PF_PASS:
- if (sn->rule.nr != -1)
- printf(", filter rule %u", sn->rule.nr);
- break;
+int
+rdr_consistent(struct pf_rule *r)
+{
+ int problems = 0;
+
+ if (r->proto != IPPROTO_TCP && r->proto != IPPROTO_UDP) {
+ if (r->src.port_op) {
+ yyerror("src port only applies to tcp/udp");
+ problems++;
+ }
+ if (r->dst.port_op) {
+ yyerror("dst port only applies to tcp/udp");
+ problems++;
}
- printf("\n");
+ if (r->rpool.proxy_port[0]) {
+ yyerror("rpool port only applies to tcp/udp");
+ problems++;
+ }
+ }
+ if (r->dst.port_op &&
+ r->dst.port_op != PF_OP_EQ && r->dst.port_op != PF_OP_RRG) {
+ yyerror("invalid port operator for rdr destination port");
+ problems++;
}
+ return (-problems);
}
-void
-print_rule(struct pf_rule *r, const char *anchor_call, int verbose)
+int
+process_tabledef(char *name, struct table_opts *opts)
{
- static const char *actiontypes[] = { "pass", "block", "scrub",
- "no scrub", "nat", "no nat", "binat", "no binat", "rdr", "no rdr" };
- static const char *anchortypes[] = { "anchor", "anchor", "anchor",
- "anchor", "nat-anchor", "nat-anchor", "binat-anchor",
- "binat-anchor", "rdr-anchor", "rdr-anchor" };
- int i, opts;
-
- if (verbose)
- printf("@%d ", r->nr);
- if (r->action > PF_NORDR)
- printf("action(%d)", r->action);
- else if (anchor_call[0])
- printf("%s \"%s\"", anchortypes[r->action],
- anchor_call);
- else {
- printf("%s", actiontypes[r->action]);
- if (r->natpass)
- printf(" pass");
- }
- if (r->action == PF_DROP) {
- if (r->rule_flag & PFRULE_RETURN)
- printf(" return");
- else if (r->rule_flag & PFRULE_RETURNRST) {
- if (!r->return_ttl)
- printf(" return-rst");
- else
- printf(" return-rst(ttl %d)", r->return_ttl);
- } else if (r->rule_flag & PFRULE_RETURNICMP) {
- const struct icmpcodeent *ic, *ic6;
-
- ic = geticmpcodebynumber(r->return_icmp >> 8,
- r->return_icmp & 255, AF_INET);
- ic6 = geticmpcodebynumber(r->return_icmp6 >> 8,
- r->return_icmp6 & 255, AF_INET6);
-
- switch (r->af) {
- case AF_INET:
- printf(" return-icmp");
- if (ic == NULL)
- printf("(%u)", r->return_icmp & 255);
- else
- printf("(%s)", ic->name);
- break;
- case AF_INET6:
- printf(" return-icmp6");
- if (ic6 == NULL)
- printf("(%u)", r->return_icmp6 & 255);
- else
- printf("(%s)", ic6->name);
- break;
- default:
- printf(" return-icmp");
- if (ic == NULL)
- printf("(%u, ", r->return_icmp & 255);
- else
- printf("(%s, ", ic->name);
- if (ic6 == NULL)
- printf("%u)", r->return_icmp6 & 255);
+ struct pfr_buffer ab;
+ struct node_tinit *ti;
+
+ bzero(&ab, sizeof(ab));
+ 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 (errno)
+ yyerror("cannot load \"%s\": %s",
+ ti->file, strerror(errno));
else
- printf("%s)", ic6->name);
- break;
+ yyerror("file \"%s\" contains bad data",
+ ti->file);
+ goto _error;
+ }
+ if (ti->host)
+ if (append_addr_host(&ab, ti->host, 0, 0)) {
+ yyerror("cannot create address buffer: %s",
+ strerror(errno));
+ goto _error;
}
- } else
- printf(" drop");
- }
- if (r->direction == PF_IN)
- printf(" in");
- else if (r->direction == PF_OUT)
- printf(" out");
- if (r->log == 1)
- printf(" log");
- else if (r->log == 2)
- printf(" log-all");
- if (r->quick)
- printf(" quick");
- if (r->ifname[0]) {
- if (r->ifnot)
- printf(" on ! %s", r->ifname);
- else
- printf(" on %s", r->ifname);
}
- if (r->rt) {
- if (r->rt == PF_ROUTETO)
- printf(" route-to");
- else if (r->rt == PF_REPLYTO)
- printf(" reply-to");
- else if (r->rt == PF_DUPTO)
- printf(" dup-to");
- else if (r->rt == PF_FASTROUTE)
- printf(" fastroute");
- if (r->rt != PF_FASTROUTE) {
- printf(" ");
- print_pool(&r->rpool, 0, 0, r->af, PF_PASS);
- }
+ if (pf->opts & PF_OPT_VERBOSE)
+ print_tabledef(name, opts->flags, opts->init_addr,
+ &opts->init_nodes);
+ if (!(pf->opts & PF_OPT_NOACTION) &&
+ pfctl_define_table(name, opts->flags, opts->init_addr,
+ pf->anchor, &ab, pf->tticket)) {
+ yyerror("cannot define table %s: %s", name,
+ pfr_strerror(errno));
+ goto _error;
}
- if (r->af) {
- if (r->af == AF_INET)
- printf(" inet");
- else
- printf(" inet6");
+ pf->tdirty = 1;
+ pfr_buf_clear(&ab);
+ return (0);
+_error:
+ pfr_buf_clear(&ab);
+ return (-1);
+}
+
+struct keywords {
+ const char *k_name;
+ int k_val;
+};
+
+/* macro gore, but you should've seen the prior indentation nightmare... */
+
+#define FREE_LIST(T,r) \
+ do { \
+ T *p, *node = r; \
+ while (node != NULL) { \
+ p = node; \
+ node = node->next; \
+ free(p); \
+ } \
+ } while (0)
+
+#define LOOP_THROUGH(T,n,r,C) \
+ do { \
+ T *n; \
+ if (r == NULL) { \
+ r = calloc(1, sizeof(T)); \
+ if (r == NULL) \
+ err(1, "LOOP: calloc"); \
+ r->next = NULL; \
+ } \
+ n = r; \
+ while (n != NULL) { \
+ do { \
+ C; \
+ } while (0); \
+ n = n->next; \
+ } \
+ } while (0)
+
+void
+expand_label_str(char *label, size_t len, const char *srch, const char *repl)
+{
+ char *tmp;
+ char *p, *q;
+
+ if ((tmp = calloc(1, len)) == NULL)
+ err(1, "expand_label_str: calloc");
+ 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");
+ q += strlen(srch);
+ p = q;
}
- if (r->proto) {
- struct protoent *p;
+ if (strlcat(tmp, p, len) >= len)
+ errx(1, "expand_label: label too long");
+ strlcpy(label, tmp, len); /* always fits */
+ free(tmp);
+}
- if ((p = getprotobynumber(r->proto)) != NULL)
- printf(" proto %s", p->p_name);
+void
+expand_label_if(const char *name, char *label, size_t len, const char *ifname)
+{
+ if (strstr(label, name) != NULL) {
+ if (!*ifname)
+ expand_label_str(label, len, name, "any");
else
- printf(" proto %u", r->proto);
- }
- print_fromto(&r->src, r->os_fingerprint, &r->dst, r->af, r->proto,
- verbose);
- if (r->uid.op)
- print_ugid(r->uid.op, r->uid.uid[0], r->uid.uid[1], "user",
- UID_MAX);
- if (r->gid.op)
- print_ugid(r->gid.op, r->gid.gid[0], r->gid.gid[1], "group",
- GID_MAX);
- if (r->flags || r->flagset) {
- printf(" flags ");
- print_flags(r->flags);
- printf("/");
- print_flags(r->flagset);
+ expand_label_str(label, len, name, ifname);
}
- if (r->type) {
- const struct icmptypeent *it;
+}
- it = geticmptypebynumber(r->type-1, r->af);
- if (r->af != AF_INET6)
- printf(" icmp-type");
- else
- printf(" icmp6-type");
- if (it != NULL)
- printf(" %s", it->name);
- else
- printf(" %u", r->type-1);
- if (r->code) {
- const struct icmpcodeent *ic;
+void
+expand_label_addr(const char *name, char *label, size_t len, sa_family_t af,
+ struct node_host *h)
+{
+ char tmp[64], tmp_not[66];
- ic = geticmpcodebynumber(r->type-1, r->code-1, r->af);
- if (ic != NULL)
- printf(" code %s", ic->name);
- else
- printf(" code %u", r->code-1);
- }
- }
- if (r->tos)
- printf(" tos 0x%2.2x", r->tos);
- if (r->keep_state == PF_STATE_NORMAL)
- printf(" keep state");
- else if (r->keep_state == PF_STATE_MODULATE)
- printf(" modulate state");
- else if (r->keep_state == PF_STATE_SYNPROXY)
- printf(" synproxy state");
- if (r->prob) {
- char buf[20];
-
- snprintf(buf, sizeof(buf), "%f", r->prob*100.0/(UINT_MAX+1.0));
- for (i = strlen(buf)-1; i > 0; i--) {
- if (buf[i] == '0')
- buf[i] = '\0';
+ if (strstr(label, name) != NULL) {
+ switch (h->addr.type) {
+ case PF_ADDR_DYNIFTL:
+ snprintf(tmp, sizeof(tmp), "(%s)", h->addr.v.ifname);
+ break;
+ case PF_ADDR_TABLE:
+ snprintf(tmp, sizeof(tmp), "<%s>", h->addr.v.tblname);
+ break;
+ case PF_ADDR_NOROUTE:
+ snprintf(tmp, sizeof(tmp), "no-route");
+ break;
+ case PF_ADDR_ADDRMASK:
+ if (!af || (PF_AZERO(&h->addr.v.a.addr, af) &&
+ PF_AZERO(&h->addr.v.a.mask, af)))
+ snprintf(tmp, sizeof(tmp), "any");
else {
- if (buf[i] == '.')
- buf[i] = '\0';
- break;
+ char a[48];
+ int bits;
+
+ if (inet_ntop(af, &h->addr.v.a.addr, a,
+ sizeof(a)) == NULL)
+ snprintf(tmp, sizeof(tmp), "?");
+ else {
+ bits = unmask(&h->addr.v.a.mask, af);
+ if ((af == AF_INET && bits < 32) ||
+ (af == AF_INET6 && bits < 128))
+ snprintf(tmp, sizeof(tmp),
+ "%s/%d", a, bits);
+ else
+ snprintf(tmp, sizeof(tmp),
+ "%s", a);
+ }
}
+ break;
+ default:
+ snprintf(tmp, sizeof(tmp), "?");
+ break;
}
- printf(" probability %s%%", buf);
- }
- opts = 0;
- if (r->max_states || r->max_src_nodes || r->max_src_states)
- opts = 1;
- if (r->rule_flag & PFRULE_NOSYNC)
- opts = 1;
- if (r->rule_flag & PFRULE_SRCTRACK)
- opts = 1;
- if (r->rule_flag & (PFRULE_IFBOUND | PFRULE_GRBOUND))
- opts = 1;
- for (i = 0; !opts && i < PFTM_MAX; ++i)
- if (r->timeout[i])
- opts = 1;
- if (opts) {
- printf(" (");
- if (r->max_states) {
- printf("max %u", r->max_states);
- opts = 0;
- }
- if (r->rule_flag & PFRULE_NOSYNC) {
- if (!opts)
- printf(", ");
- printf("no-sync");
- opts = 0;
- }
- if (r->rule_flag & PFRULE_SRCTRACK) {
- if (!opts)
- printf(", ");
- printf("source-track");
- if (r->rule_flag & PFRULE_RULESRCTRACK)
- printf(" rule");
- else
- printf(" global");
- opts = 0;
- }
- if (r->max_src_states) {
- if (!opts)
- printf(", ");
- printf("max-src-states %u", r->max_src_states);
- opts = 0;
- }
- if (r->max_src_conn) {
- if (!opts)
- printf(", ");
- printf("max-src-conn %u", r->max_src_conn);
- opts = 0;
- }
- if (r->max_src_conn_rate.limit) {
- if (!opts)
- printf(", ");
- printf("max-src-conn-rate %u/%u",
- r->max_src_conn_rate.limit,
- r->max_src_conn_rate.seconds);
- opts = 0;
- }
- if (r->max_src_nodes) {
- if (!opts)
- printf(", ");
- printf("max-src-nodes %u", r->max_src_nodes);
- opts = 0;
- }
- if (r->overload_tblname[0]) {
- if (!opts)
- printf(", ");
- printf("overload <%s>", r->overload_tblname);
- if (r->rule_flag & PFRULE_SRCTRACK_FLUSH)
- printf(" flush");
- }
- if (r->rule_flag & PFRULE_IFBOUND) {
- if (!opts)
- printf(", ");
- printf("if-bound");
- opts = 0;
- }
- if (r->rule_flag & PFRULE_GRBOUND) {
- if (!opts)
- printf(", ");
- printf("group-bound");
- opts = 0;
- }
- for (i = 0; i < PFTM_MAX; ++i)
- if (r->timeout[i]) {
- int j;
-
- if (!opts)
- printf(", ");
- opts = 0;
- for (j = 0; j < sizeof(pf_timeouts) /
- sizeof(pf_timeouts[0]); ++j)
- if (pf_timeouts[j].timeout == i)
- break;
- printf("%s %u", j == PFTM_MAX ? "inv.timeout" :
- pf_timeouts[j].name, r->timeout[i]);
- }
- printf(")");
- }
- if (r->rule_flag & PFRULE_FRAGMENT)
- printf(" fragment");
- if (r->rule_flag & PFRULE_NODF)
- printf(" no-df");
- if (r->rule_flag & PFRULE_RANDOMID)
- printf(" random-id");
- if (r->min_ttl)
- printf(" min-ttl %d", r->min_ttl);
- if (r->max_mss)
- printf(" max-mss %d", r->max_mss);
- if (r->allow_opts)
- printf(" allow-opts");
- if (r->action == PF_SCRUB) {
- if (r->rule_flag & PFRULE_REASSEMBLE_TCP)
- printf(" reassemble tcp");
-
- if (r->rule_flag & PFRULE_FRAGDROP)
- printf(" fragment drop-ovl");
- else if (r->rule_flag & PFRULE_FRAGCROP)
- printf(" fragment crop");
- else
- printf(" fragment reassemble");
- }
- if (r->label[0])
- printf(" label \"%s\"", r->label);
- if (r->qname[0] && r->pqname[0])
- printf(" queue(%s, %s)", r->qname, r->pqname);
- else if (r->qname[0])
- printf(" queue %s", r->qname);
- if (r->tagname[0])
- printf(" tag %s", r->tagname);
- if (r->match_tagname[0]) {
- if (r->match_tag_not)
- printf(" !");
- printf(" tagged %s", r->match_tagname);
- }
- if (!anchor_call[0] && (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);
+
+ if (h->not) {
+ snprintf(tmp_not, sizeof(tmp_not), "! %s", tmp);
+ expand_label_str(label, len, name, tmp_not);
+ } else
+ expand_label_str(label, len, name, tmp);
}
- printf("\n");
}
void
-print_tabledef(const char *name, int flags, int addrs,
- struct node_tinithead *nodes)
+expand_label_port(const char *name, char *label, size_t len,
+ struct node_port *port)
{
- struct node_tinit *ti, *nti;
- struct node_host *h;
+ char a1[6], a2[6], op[13] = "";
- printf("table <%s>", name);
- if (flags & PFR_TFLAG_CONST)
- printf(" const");
- if (flags & PFR_TFLAG_PERSIST)
- printf(" persist");
- SIMPLEQ_FOREACH(ti, nodes, entries) {
- if (ti->file) {
- printf(" file \"%s\"", ti->file);
- continue;
- }
- printf(" {");
- for (;;) {
- for (h = ti->host; h != NULL; h = h->next) {
- printf(h->not ? " !" : " ");
- print_addr(&h->addr, h->af, 0);
- }
- nti = SIMPLEQ_NEXT(ti, entries);
- if (nti != NULL && nti->file == NULL)
- ti = nti; /* merge lists */
- else
- break;
- }
- printf(" }");
+ if (strstr(label, name) != NULL) {
+ snprintf(a1, sizeof(a1), "%u", ntohs(port->port[0]));
+ snprintf(a2, sizeof(a2), "%u", ntohs(port->port[1]));
+ if (!port->op)
+ ;
+ else if (port->op == PF_OP_IRG)
+ snprintf(op, sizeof(op), "%s><%s", a1, a2);
+ else if (port->op == PF_OP_XRG)
+ snprintf(op, sizeof(op), "%s<>%s", a1, a2);
+ else if (port->op == PF_OP_EQ)
+ snprintf(op, sizeof(op), "%s", a1);
+ else if (port->op == PF_OP_NE)
+ snprintf(op, sizeof(op), "!=%s", a1);
+ else if (port->op == PF_OP_LT)
+ snprintf(op, sizeof(op), "<%s", a1);
+ else if (port->op == PF_OP_LE)
+ snprintf(op, sizeof(op), "<=%s", a1);
+ else if (port->op == PF_OP_GT)
+ snprintf(op, sizeof(op), ">%s", a1);
+ else if (port->op == PF_OP_GE)
+ snprintf(op, sizeof(op), ">=%s", a1);
+ expand_label_str(label, len, name, op);
}
- if (addrs && SIMPLEQ_EMPTY(nodes))
- printf(" { }");
- printf("\n");
}
-int
-parse_flags(char *s)
+void
+expand_label_proto(const char *name, char *label, size_t len, u_int8_t proto)
{
- char *p, *q;
- u_int8_t f = 0;
+ struct protoent *pe;
+ char n[4];
- for (p = s; *p; p++) {
- if ((q = strchr(tcpflags, *p)) == NULL)
- return -1;
- else
- f |= 1 << (q - tcpflags);
+ if (strstr(label, name) != NULL) {
+ pe = getprotobynumber(proto);
+ if (pe != NULL)
+ expand_label_str(label, len, name, pe->p_name);
+ else {
+ snprintf(n, sizeof(n), "%u", proto);
+ expand_label_str(label, len, name, n);
+ }
}
- return (f ? f : PF_TH_ALL);
}
void
-set_ipmask(struct node_host *h, u_int8_t b)
+expand_label_nr(const char *name, char *label, size_t len)
{
- struct pf_addr *m, *n;
- int i, j = 0;
-
- m = &h->addr.v.a.mask;
- memset(m, 0, sizeof(*m));
+ char n[11];
- while (b >= 32) {
- m->addr32[j++] = 0xffffffff;
- b -= 32;
+ if (strstr(label, name) != NULL) {
+ snprintf(n, sizeof(n), "%u", pf->rule_nr);
+ expand_label_str(label, len, name, n);
}
- for (i = 31; i > 31-b; --i)
- m->addr32[j] |= (1 << i);
- if (b)
- m->addr32[j] = htonl(m->addr32[j]);
-
- /* Mask off bits of the address that will never be used. */
- n = &h->addr.v.a.addr;
- if (h->addr.type == PF_ADDR_ADDRMASK)
- for (i = 0; i < 4; i++)
- n->addr32[i] = n->addr32[i] & m->addr32[i];
+}
+
+void
+expand_label(char *label, size_t len, const char *ifname, sa_family_t af,
+ struct node_host *src_host, struct node_port *src_port,
+ struct node_host *dst_host, struct node_port *dst_port,
+ u_int8_t proto)
+{
+ expand_label_if("$if", label, len, ifname);
+ expand_label_addr("$srcaddr", label, len, af, src_host);
+ expand_label_addr("$dstaddr", label, len, af, dst_host);
+ expand_label_port("$srcport", label, len, src_port);
+ expand_label_port("$dstport", label, len, dst_port);
+ expand_label_proto("$proto", label, len, proto);
+ expand_label_nr("$nr", label, len);
}
int
-check_netmask(struct node_host *h, sa_family_t af)
+expand_altq(struct pf_altq *a, struct node_if *interfaces,
+ struct node_queue *nqueues, struct node_queue_bw bwspec,
+ struct node_queue_opt *opts)
{
- struct node_host *n = NULL;
- struct pf_addr *m;
+ struct pf_altq pa, pb;
+ char qname[PF_QNAME_SIZE];
+ struct node_queue *n;
+ struct node_queue_bw bw;
+ int errs = 0;
- 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));
- return (1);
- }
+ if ((pf->loadopt & PFCTL_FLAG_ALTQ) == 0) {
+ FREE_LIST(struct node_if, interfaces);
+ FREE_LIST(struct node_queue, nqueues);
+ return (0);
}
- return (0);
-}
-/* interface lookup routines */
+ LOOP_THROUGH(struct node_if, interface, 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");
-struct node_host *iftab;
+ if (interface->not) {
+ yyerror("altq on ! <interface> is not supported");
+ errs++;
+ } else {
+ if (eval_pfaltq(pf, &pa, &bwspec, opts))
+ errs++;
+ else
+ if (pfctl_add_altq(pf, &pa))
+ errs++;
-void
-ifa_load(void)
-{
- struct ifaddrs *ifap, *ifa;
- struct node_host *n = NULL, *h = NULL;
+ if (pf->opts & PF_OPT_VERBOSE) {
+ print_altq(&pf->paltq->altq, 0,
+ &bwspec, opts);
+ if (nqueues && nqueues->tail) {
+ printf("queue { ");
+ LOOP_THROUGH(struct node_queue, queue,
+ nqueues,
+ printf("%s ",
+ queue->queue);
+ );
+ printf("}");
+ }
+ printf("\n");
+ }
- if (getifaddrs(&ifap) < 0)
- err(1, "getifaddrs");
+ if (pa.scheduler == ALTQT_CBQ ||
+ pa.scheduler == ALTQT_HFSC) {
+ /* now create a root queue */
+ memset(&pb, 0, sizeof(struct pf_altq));
+ if (strlcpy(qname, "root_", sizeof(qname)) >=
+ sizeof(qname))
+ errx(1, "expand_altq: strlcpy");
+ if (strlcat(qname, interface->ifname,
+ sizeof(qname)) >= sizeof(qname))
+ errx(1, "expand_altq: strlcat");
+ if (strlcpy(pb.qname, qname,
+ sizeof(pb.qname)) >= sizeof(pb.qname))
+ errx(1, "expand_altq: strlcpy");
+ if (strlcpy(pb.ifname, interface->ifname,
+ sizeof(pb.ifname)) >= sizeof(pb.ifname))
+ errx(1, "expand_altq: strlcpy");
+ pb.qlimit = pa.qlimit;
+ pb.scheduler = pa.scheduler;
+ bw.bw_absolute = pa.ifbandwidth;
+ bw.bw_percent = 0;
+ if (eval_pfqueue(pf, &pb, &bw, opts))
+ errs++;
+ else
+ if (pfctl_add_altq(pf, &pb))
+ errs++;
+ }
- for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
- if (!(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");
- n->af = ifa->ifa_addr->sa_family;
- n->ifa_flags = ifa->ifa_flags;
-#ifdef __KAME__
- if (n->af == AF_INET6 &&
- IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)
- ifa->ifa_addr)->sin6_addr) &&
- ((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_scope_id ==
- 0) {
- struct sockaddr_in6 *sin6;
-
- sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
- sin6->sin6_scope_id = sin6->sin6_addr.s6_addr[2] << 8 |
- sin6->sin6_addr.s6_addr[3];
- sin6->sin6_addr.s6_addr[2] = 0;
- sin6->sin6_addr.s6_addr[3] = 0;
+ LOOP_THROUGH(struct node_queue, queue, nqueues,
+ n = calloc(1, sizeof(struct node_queue));
+ if (n == NULL)
+ err(1, "expand_altq: calloc");
+ if (pa.scheduler == ALTQT_CBQ ||
+ pa.scheduler == ALTQT_HFSC)
+ if (strlcpy(n->parent, qname,
+ sizeof(n->parent)) >=
+ sizeof(n->parent))
+ errx(1, "expand_altq: strlcpy");
+ if (strlcpy(n->queue, queue->queue,
+ sizeof(n->queue)) >= sizeof(n->queue))
+ errx(1, "expand_altq: strlcpy");
+ if (strlcpy(n->ifname, interface->ifname,
+ sizeof(n->ifname)) >= sizeof(n->ifname))
+ errx(1, "expand_altq: strlcpy");
+ n->scheduler = pa.scheduler;
+ n->next = NULL;
+ n->tail = n;
+ if (queues == NULL)
+ queues = n;
+ else {
+ queues->tail->next = n;
+ queues->tail = n;
+ }
+ );
}
-#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;
- }
- if ((n->ifname = strdup(ifa->ifa_name)) == NULL)
- err(1, "ifa_load: strdup");
- n->next = NULL;
- n->tail = n;
- if (h == NULL)
- h = n;
- else {
- h->tail->next = n;
- h->tail = n;
- }
- }
+ );
+ FREE_LIST(struct node_if, interfaces);
+ FREE_LIST(struct node_queue, nqueues);
- iftab = h;
- freeifaddrs(ifap);
+ return (errs);
}
-struct node_host *
-ifa_exists(const char *ifa_name, int group_ok)
+int
+expand_queue(struct pf_altq *a, struct node_if *interfaces,
+ struct node_queue *nqueues, struct node_queue_bw bwspec,
+ struct node_queue_opt *opts)
{
- struct node_host *n;
+ struct node_queue *n, *nq;
+ struct pf_altq pa;
+ u_int8_t found = 0;
+ u_int8_t errs = 0;
- if (iftab == NULL)
- ifa_load();
+ if ((pf->loadopt & PFCTL_FLAG_ALTQ) == 0) {
+ FREE_LIST(struct node_queue, nqueues);
+ return (0);
+ }
- for (n = iftab; n; n = n->next) {
- if (n->af == AF_LINK && !strncmp(n->ifname, ifa_name, IFNAMSIZ))
- return (n);
+ if (queues == NULL) {
+ yyerror("queue %s has no parent", a->qname);
+ FREE_LIST(struct node_queue, nqueues);
+ return (1);
}
- return (NULL);
+ LOOP_THROUGH(struct node_if, interface, interfaces,
+ LOOP_THROUGH(struct node_queue, tqueue, queues,
+ if (!strncmp(a->qname, tqueue->queue, PF_QNAME_SIZE) &&
+ (interface->ifname[0] == 0 ||
+ (!interface->not && !strncmp(interface->ifname,
+ tqueue->ifname, IFNAMSIZ)) ||
+ (interface->not && strncmp(interface->ifname,
+ tqueue->ifname, IFNAMSIZ)))) {
+ /* found ourself in queues */
+ found++;
+
+ memcpy(&pa, a, sizeof(struct pf_altq));
+
+ if (pa.scheduler != ALTQT_NONE &&
+ pa.scheduler != tqueue->scheduler) {
+ yyerror("exactly one scheduler type "
+ "per interface allowed");
+ return (1);
+ }
+ pa.scheduler = tqueue->scheduler;
+
+ /* scheduler dependent error checking */
+ switch (pa.scheduler) {
+ case ALTQT_PRIQ:
+ if (nqueues != NULL) {
+ yyerror("priq queues cannot "
+ "have child queues");
+ return (1);
+ }
+ if (bwspec.bw_absolute > 0 ||
+ bwspec.bw_percent < 100) {
+ yyerror("priq doesn't take "
+ "bandwidth");
+ return (1);
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (strlcpy(pa.ifname, tqueue->ifname,
+ sizeof(pa.ifname)) >= sizeof(pa.ifname))
+ errx(1, "expand_queue: strlcpy");
+ if (strlcpy(pa.parent, tqueue->parent,
+ sizeof(pa.parent)) >= sizeof(pa.parent))
+ errx(1, "expand_queue: strlcpy");
+
+ if (eval_pfqueue(pf, &pa, &bwspec, opts))
+ errs++;
+ else
+ if (pfctl_add_altq(pf, &pa))
+ errs++;
+
+ for (nq = nqueues; nq != NULL; nq = nq->next) {
+ if (!strcmp(a->qname, nq->queue)) {
+ yyerror("queue cannot have "
+ "itself as child");
+ errs++;
+ continue;
+ }
+ n = calloc(1,
+ sizeof(struct node_queue));
+ if (n == NULL)
+ err(1, "expand_queue: calloc");
+ if (strlcpy(n->parent, a->qname,
+ sizeof(n->parent)) >=
+ sizeof(n->parent))
+ errx(1, "expand_queue strlcpy");
+ if (strlcpy(n->queue, nq->queue,
+ sizeof(n->queue)) >=
+ sizeof(n->queue))
+ errx(1, "expand_queue strlcpy");
+ if (strlcpy(n->ifname, tqueue->ifname,
+ sizeof(n->ifname)) >=
+ sizeof(n->ifname))
+ errx(1, "expand_queue strlcpy");
+ n->scheduler = tqueue->scheduler;
+ n->next = NULL;
+ n->tail = n;
+ if (queues == NULL)
+ queues = n;
+ else {
+ queues->tail->next = n;
+ queues->tail = n;
+ }
+ }
+ if ((pf->opts & PF_OPT_VERBOSE) && (
+ (found == 1 && interface->ifname[0] == 0) ||
+ (found > 0 && interface->ifname[0] != 0))) {
+ print_queue(&pf->paltq->altq, 0,
+ &bwspec, interface->ifname[0] != 0,
+ opts);
+ if (nqueues && nqueues->tail) {
+ printf("{ ");
+ LOOP_THROUGH(struct node_queue,
+ queue, nqueues,
+ printf("%s ",
+ queue->queue);
+ );
+ printf("}");
+ }
+ printf("\n");
+ }
+ }
+ );
+ );
+
+ FREE_LIST(struct node_queue, nqueues);
+ FREE_LIST(struct node_if, interfaces);
+
+ if (!found) {
+ yyerror("queue %s has no parent", a->qname);
+ errs++;
+ }
+
+ if (errs)
+ return (1);
+ else
+ return (0);
}
-struct node_host *
-ifa_lookup(const char *ifa_name, int flags)
+void
+expand_rule(struct pf_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)
{
- struct node_host *p = NULL, *h = NULL, *n = NULL;
- int got4 = 0, got6 = 0;
- const char *last_if = NULL;
+ sa_family_t af = r->af;
+ int added = 0, error = 0;
+ char ifname[IF_NAMESIZE];
+ char label[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;
+ u_int8_t flags, flagset, keep_state;
- if (!strncmp(ifa_name, "self", IFNAMSIZ))
- ifa_name = NULL;
+ if (strlcpy(label, r->label, sizeof(label)) >= sizeof(label))
+ errx(1, "expand_rule: strlcpy");
+ if (strlcpy(tagname, r->tagname, sizeof(tagname)) >= sizeof(tagname))
+ errx(1, "expand_rule: strlcpy");
+ if (strlcpy(match_tagname, r->match_tagname, sizeof(match_tagname)) >=
+ sizeof(match_tagname))
+ errx(1, "expand_rule: strlcpy");
+ flags = r->flags;
+ flagset = r->flagset;
+ keep_state = r->keep_state;
- if (iftab == NULL)
- ifa_load();
+ LOOP_THROUGH(struct node_if, interface, interfaces,
+ 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, dst_port, dst_ports,
+ LOOP_THROUGH(struct node_uid, uid, uids,
+ LOOP_THROUGH(struct node_gid, gid, gids,
- for (p = iftab; p; p = p->next) {
- if (ifa_skip_if(ifa_name, p))
- continue;
- if ((flags & PFI_AFLAG_BROADCAST) && p->af != AF_INET)
- continue;
- if ((flags & PFI_AFLAG_BROADCAST) &&
- !(p->ifa_flags & IFF_BROADCAST))
- continue;
- if ((flags & PFI_AFLAG_PEER) &&
- !(p->ifa_flags & IFF_POINTOPOINT))
- continue;
- if ((flags & PFI_AFLAG_NETWORK) && p->ifindex > 0)
- continue;
- if (last_if == NULL || strcmp(last_if, p->ifname))
- got4 = got6 = 0;
- last_if = p->ifname;
- if ((flags & PFI_AFLAG_NOALIAS) && p->af == AF_INET && got4)
+ r->af = 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) ||
+ (src_host->af && dst_host->af &&
+ src_host->af != dst_host->af) ||
+ (src_host->ifindex && dst_host->ifindex &&
+ src_host->ifindex != dst_host->ifindex) ||
+ (src_host->ifindex && *interface->ifname &&
+ src_host->ifindex != if_nametoindex(interface->ifname)) ||
+ (dst_host->ifindex && *interface->ifname &&
+ dst_host->ifindex != if_nametoindex(interface->ifname)))
continue;
- if ((flags & PFI_AFLAG_NOALIAS) && p->af == AF_INET6 && got6)
- continue;
- if (p->af == AF_INET)
- got4 = 1;
+ if (!r->af && src_host->af)
+ r->af = src_host->af;
+ else if (!r->af && dst_host->af)
+ r->af = dst_host->af;
+
+ if (*interface->ifname)
+ strlcpy(r->ifname, interface->ifname,
+ sizeof(r->ifname));
+ else if (if_indextoname(src_host->ifindex, ifname))
+ strlcpy(r->ifname, ifname, sizeof(r->ifname));
+ else if (if_indextoname(dst_host->ifindex, ifname))
+ strlcpy(r->ifname, ifname, sizeof(r->ifname));
else
- got6 = 1;
- n = calloc(1, sizeof(struct node_host));
- if (n == NULL)
- err(1, "address: calloc");
- n->af = p->af;
- if (flags & PFI_AFLAG_BROADCAST)
- memcpy(&n->addr.v.a.addr, &p->bcast,
- sizeof(struct pf_addr));
- else if (flags & PFI_AFLAG_PEER)
- memcpy(&n->addr.v.a.addr, &p->peer,
- sizeof(struct pf_addr));
+ memset(r->ifname, '\0', sizeof(r->ifname));
+
+ if (strlcpy(r->label, label, sizeof(r->label)) >=
+ sizeof(r->label))
+ errx(1, "expand_rule: strlcpy");
+ if (strlcpy(r->tagname, tagname, sizeof(r->tagname)) >=
+ sizeof(r->tagname))
+ errx(1, "expand_rule: strlcpy");
+ if (strlcpy(r->match_tagname, match_tagname,
+ sizeof(r->match_tagname)) >= sizeof(r->match_tagname))
+ errx(1, "expand_rule: strlcpy");
+ expand_label(r->label, PF_RULE_LABEL_SIZE, r->ifname, r->af,
+ src_host, src_port, dst_host, dst_port, proto->proto);
+ expand_label(r->tagname, PF_TAG_NAME_SIZE, r->ifname, r->af,
+ src_host, src_port, dst_host, dst_port, proto->proto);
+ expand_label(r->match_tagname, PF_TAG_NAME_SIZE, r->ifname,
+ r->af, src_host, src_port, dst_host, dst_port,
+ proto->proto);
+
+ error += check_netmask(src_host, r->af);
+ error += check_netmask(dst_host, r->af);
+
+ r->ifnot = interface->not;
+ r->proto = proto->proto;
+ r->src.addr = src_host->addr;
+ r->src.neg = src_host->not;
+ r->src.port[0] = src_port->port[0];
+ r->src.port[1] = src_port->port[1];
+ r->src.port_op = src_port->op;
+ r->dst.addr = dst_host->addr;
+ r->dst.neg = dst_host->not;
+ r->dst.port[0] = dst_port->port[0];
+ r->dst.port[1] = dst_port->port[1];
+ r->dst.port_op = dst_port->op;
+ r->uid.op = uid->op;
+ r->uid.uid[0] = uid->uid[0];
+ r->uid.uid[1] = uid->uid[1];
+ r->gid.op = gid->op;
+ r->gid.gid[0] = gid->gid[0];
+ r->gid.gid[1] = gid->gid[1];
+ r->type = icmp_type->type;
+ r->code = icmp_type->code;
+
+ if ((keep_state == PF_STATE_MODULATE ||
+ keep_state == PF_STATE_SYNPROXY) &&
+ r->proto && r->proto != IPPROTO_TCP)
+ r->keep_state = PF_STATE_NORMAL;
else
- 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);
+ r->keep_state = keep_state;
+
+ if (r->proto && r->proto != IPPROTO_TCP) {
+ r->flags = 0;
+ r->flagset = 0;
+ } else {
+ r->flags = flags;
+ r->flagset = flagset;
+ }
+ if (icmp_type->proto && r->proto != icmp_type->proto) {
+ yyerror("icmp-type mismatch");
+ error++;
+ }
+
+ if (src_os && src_os->os) {
+ r->os_fingerprint = pfctl_get_fingerprint(src_os->os);
+ if ((pf->opts & PF_OPT_VERBOSE2) &&
+ r->os_fingerprint == PF_OSFP_NOMATCH)
+ fprintf(stderr,
+ "warning: unknown '%s' OS fingerprint\n",
+ src_os->os);
+ } else {
+ 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
- set_ipmask(n, 128);
+ pa->ifname[0] = 0;
+ TAILQ_INSERT_TAIL(&r->rpool.list, pa, entries);
}
- n->ifindex = p->ifindex;
- n->next = NULL;
- n->tail = n;
- if (h == NULL)
- h = n;
+ if (rule_consistent(r) < 0 || error)
+ yyerror("skipping rule due to errors");
else {
- h->tail->next = n;
- h->tail = n;
+ r->nr = pf->rule_nr++;
+ pfctl_add_rule(pf, r, anchor_call);
+ added++;
}
- }
- return (h);
+
+ ))))))))));
+
+ 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 (!added)
+ yyerror("rule expands to no valid combination");
}
+#undef FREE_LIST
+#undef LOOP_THROUGH
+
int
-ifa_skip_if(const char *filter, struct node_host *p)
+check_rulestate(int desired_state)
{
- int n;
-
- if (p->af != AF_INET && p->af != AF_INET6)
+ if (require_order && (rulestate > desired_state)) {
+ yyerror("Rules must be in order: options, normalization, "
+ "queueing, translation, filtering");
return (1);
- if (filter == NULL || !*filter)
- return (0);
- if (!strcmp(p->ifname, filter))
- return (0); /* exact match */
- n = strlen(filter);
- if (n < 1 || n >= IFNAMSIZ)
- return (1); /* sanity check */
- if (filter[n-1] >= '0' && filter[n-1] <= '9')
- return (1); /* only do exact match in that case */
- if (strncmp(p->ifname, filter, n))
- return (1); /* prefix doesn't match */
- return (p->ifname[n] < '0' || p->ifname[n] > '9');
+ }
+ rulestate = desired_state;
+ return (0);
}
+int
+kw_cmp(const void *k, const void *e)
+{
+ return (strcmp(k, ((const struct keywords *)e)->k_name));
+}
-struct node_host *
-host(const char *s)
+int
+lookup(char *s)
{
- 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);
- }
- if ((ps = malloc(strlen(s) - strlen(p) + 1)) == NULL)
- err(1, "host: malloc");
- strlcpy(ps, s, strlen(s) - strlen(p) + 1);
- v4mask = v6mask = mask;
+ /* this has to be sorted always */
+ static const struct keywords keywords[] = {
+ { "all", ALL},
+ { "allow-opts", ALLOWOPTS},
+ { "altq", ALTQ},
+ { "anchor", ANCHOR},
+ { "antispoof", ANTISPOOF},
+ { "any", ANY},
+ { "bandwidth", BANDWIDTH},
+ { "binat", BINAT},
+ { "binat-anchor", BINATANCHOR},
+ { "bitmask", BITMASK},
+ { "block", BLOCK},
+ { "block-policy", BLOCKPOLICY},
+ { "cbq", CBQ},
+ { "code", CODE},
+ { "crop", FRAGCROP},
+ { "debug", DEBUG},
+ { "drop", DROP},
+ { "drop-ovl", FRAGDROP},
+ { "dup-to", DUPTO},
+ { "fastroute", FASTROUTE},
+ { "file", FILENAME},
+ { "fingerprints", FINGERPRINTS},
+ { "flags", FLAGS},
+ { "floating", FLOATING},
+ { "flush", FLUSH},
+ { "for", FOR},
+ { "fragment", FRAGMENT},
+ { "from", FROM},
+ { "global", GLOBAL},
+ { "group", GROUP},
+ { "group-bound", GRBOUND},
+ { "hfsc", HFSC},
+ { "hostid", HOSTID},
+ { "icmp-type", ICMPTYPE},
+ { "icmp6-type", ICMP6TYPE},
+ { "if-bound", IFBOUND},
+ { "in", IN},
+ { "inet", INET},
+ { "inet6", INET6},
+ { "keep", KEEP},
+ { "label", LABEL},
+ { "limit", LIMIT},
+ { "linkshare", LINKSHARE},
+ { "load", LOAD},
+ { "log", LOG},
+ { "log-all", LOGALL},
+ { "loginterface", LOGINTERFACE},
+ { "max", MAXIMUM},
+ { "max-mss", MAXMSS},
+ { "max-src-conn", MAXSRCCONN},
+ { "max-src-conn-rate", MAXSRCCONNRATE},
+ { "max-src-nodes", MAXSRCNODES},
+ { "max-src-states", MAXSRCSTATES},
+ { "min-ttl", MINTTL},
+ { "modulate", MODULATE},
+ { "nat", NAT},
+ { "nat-anchor", NATANCHOR},
+ { "no", NO},
+ { "no-df", NODF},
+ { "no-route", NOROUTE},
+ { "no-sync", NOSYNC},
+ { "on", ON},
+ { "optimization", OPTIMIZATION},
+ { "os", OS},
+ { "out", OUT},
+ { "overload", OVERLOAD},
+ { "pass", PASS},
+ { "port", PORT},
+ { "priority", PRIORITY},
+ { "priq", PRIQ},
+ { "probability", PROBABILITY},
+ { "proto", PROTO},
+ { "qlimit", QLIMIT},
+ { "queue", QUEUE},
+ { "quick", QUICK},
+ { "random", RANDOM},
+ { "random-id", RANDOMID},
+ { "rdr", RDR},
+ { "rdr-anchor", RDRANCHOR},
+ { "realtime", REALTIME},
+ { "reassemble", REASSEMBLE},
+ { "reply-to", REPLYTO},
+ { "require-order", REQUIREORDER},
+ { "return", RETURN},
+ { "return-icmp", RETURNICMP},
+ { "return-icmp6", RETURNICMP6},
+ { "return-rst", RETURNRST},
+ { "round-robin", ROUNDROBIN},
+ { "route-to", ROUTETO},
+ { "rule", RULE},
+ { "scrub", SCRUB},
+ { "set", SET},
+ { "source-hash", SOURCEHASH},
+ { "source-track", SOURCETRACK},
+ { "state", STATE},
+ { "state-policy", STATEPOLICY},
+ { "static-port", STATICPORT},
+ { "sticky-address", STICKYADDRESS},
+ { "synproxy", SYNPROXY},
+ { "table", TABLE},
+ { "tag", TAG},
+ { "tagged", TAGGED},
+ { "tbrsize", TBRSIZE},
+ { "timeout", TIMEOUT},
+ { "to", TO},
+ { "tos", TOS},
+ { "ttl", TTL},
+ { "upperlimit", UPPERLIMIT},
+ { "user", USER},
+ };
+ const struct keywords *p;
+
+ p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
+ sizeof(keywords[0]), kw_cmp);
+
+ if (p) {
+ if (debug > 1)
+ fprintf(stderr, "%s: %d\n", s, p->k_val);
+ return (p->k_val);
} else {
- if ((ps = strdup(s)) == NULL)
- err(1, "host: strdup");
- v4mask = 32;
- v6mask = 128;
- mask = -1;
+ if (debug > 1)
+ fprintf(stderr, "string: %s\n", s);
+ return (STRING);
}
+}
- /* interface with this name exists? */
- if (cont && (h = host_if(ps, mask)) != NULL)
- cont = 0;
+#define MAXPUSHBACK 128
- /* IPv4 address? */
- if (cont && (h = host_v4(s, mask)) != NULL)
- cont = 0;
+char *parsebuf;
+int parseindex;
+char pushback_buffer[MAXPUSHBACK];
+int pushback_index = 0;
- /* IPv6 address? */
- if (cont && (h = host_v6(ps, v6mask)) != NULL)
- cont = 0;
+int
+lgetc(FILE *f)
+{
+ 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++;
+ }
- /* dns lookup */
- if (cont && (h = host_dns(ps, v4mask, v6mask)) != NULL)
- cont = 0;
- free(ps);
+ if (pushback_index)
+ return (pushback_buffer[--pushback_index]);
- if (h == NULL || cont == 1) {
- fprintf(stderr, "no IP address found for %s\n", s);
- return (NULL);
+ while ((c = getc(f)) == '\\') {
+ next = getc(f);
+ if (next != '\n') {
+ if (isspace(next))
+ yyerror("whitespace after \\");
+ ungetc(next, f);
+ break;
+ }
+ yylval.lineno = lineno;
+ lineno++;
+ }
+ if (c == '\t' || c == ' ') {
+ /* Compress blanks to a single space. */
+ do {
+ c = getc(f);
+ } while (c == '\t' || c == ' ');
+ ungetc(c, f);
+ c = ' ';
}
- return (h);
+
+ return (c);
}
-struct node_host *
-host_if(const char *s, int mask)
+int
+lungetc(int c)
{
- struct node_host *n, *h = NULL;
- char *p, *ps;
- int flags = 0;
-
- if ((ps = strdup(s)) == NULL)
- err(1, "host_if: strdup");
- while ((p = strrchr(ps, ':')) != NULL) {
- if (!strcmp(p+1, "network"))
- flags |= PFI_AFLAG_NETWORK;
- else if (!strcmp(p+1, "broadcast"))
- flags |= PFI_AFLAG_BROADCAST;
- else if (!strcmp(p+1, "peer"))
- flags |= PFI_AFLAG_PEER;
- else if (!strcmp(p+1, "0"))
- flags |= PFI_AFLAG_NOALIAS;
- else {
- free(ps);
- return (NULL);
- }
- *p = '\0';
+ if (c == EOF)
+ return (EOF);
+ if (parsebuf) {
+ parseindex--;
+ if (parseindex >= 0)
+ return (c);
}
- if (flags & (flags - 1) & PFI_AFLAG_MODEMASK) { /* Yep! */
- fprintf(stderr, "illegal combination of interface modifiers\n");
- free(ps);
- return (NULL);
+ if (pushback_index < MAXPUSHBACK-1)
+ return (pushback_buffer[pushback_index++] = c);
+ else
+ return (EOF);
+}
+
+int
+findeol(void)
+{
+ int c;
+
+ parsebuf = NULL;
+ pushback_index = 0;
+
+ /* skip to either EOF or the first real EOL */
+ while (1) {
+ c = lgetc(fin);
+ if (c == '\n') {
+ lineno++;
+ break;
+ }
+ if (c == EOF)
+ break;
}
- 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);
+ return (ERROR);
+}
+
+int
+yylex(void)
+{
+ char buf[8096];
+ char *p, *val;
+ int endc, c, next;
+ int token;
+
+top:
+ p = buf;
+ while ((c = lgetc(fin)) == ' ')
+ ; /* nothing */
+
+ yylval.lineno = lineno;
+ if (c == '#')
+ while ((c = lgetc(fin)) != '\n' && c != EOF)
+ ; /* nothing */
+ if (c == '$' && parsebuf == NULL) {
+ while (1) {
+ if ((c = lgetc(fin)) == EOF)
+ return (0);
+
+ if (p + 1 >= buf + sizeof(buf) - 1) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ if (isalnum(c) || c == '_') {
+ *p++ = (char)c;
+ continue;
+ }
+ *p = '\0';
+ lungetc(c);
+ break;
+ }
+ val = symget(buf);
+ if (val == NULL) {
+ yyerror("macro '%s' not defined", buf);
+ return (findeol());
+ }
+ parsebuf = val;
+ parseindex = 0;
+ goto top;
}
- if (ifa_exists(ps, 1) || !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);
+
+ switch (c) {
+ case '\'':
+ case '"':
+ endc = c;
+ while (1) {
+ if ((c = lgetc(fin)) == EOF)
+ return (0);
+ if (c == endc) {
+ *p = '\0';
+ break;
+ }
+ if (c == '\n') {
+ lineno++;
+ continue;
+ }
+ if (p + 1 >= buf + sizeof(buf) - 1) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ *p++ = (char)c;
+ }
+ yylval.v.string = strdup(buf);
+ if (yylval.v.string == NULL)
+ err(1, "yylex: strdup");
+ return (STRING);
+ case '<':
+ next = lgetc(fin);
+ if (next == '>') {
+ yylval.v.i = PF_OP_XRG;
+ return (PORTBINARY);
+ }
+ lungetc(next);
+ break;
+ case '>':
+ next = lgetc(fin);
+ if (next == '<') {
+ yylval.v.i = PF_OP_IRG;
+ return (PORTBINARY);
+ }
+ lungetc(next);
+ break;
+ case '-':
+ next = lgetc(fin);
+ if (next == '>')
+ return (ARROW);
+ lungetc(next);
+ break;
}
- free(ps);
- return (h);
+#define allowed_in_string(x) \
+ (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
+ x != '{' && x != '}' && x != '<' && x != '>' && \
+ x != '!' && x != '=' && x != '/' && x != '#' && \
+ x != ','))
+
+ if (isalnum(c) || c == ':' || c == '_') {
+ do {
+ *p++ = c;
+ if ((unsigned)(p-buf) >= sizeof(buf)) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ } while ((c = lgetc(fin)) != EOF && (allowed_in_string(c)));
+ lungetc(c);
+ *p = '\0';
+ if ((token = lookup(buf)) == STRING)
+ if ((yylval.v.string = strdup(buf)) == NULL)
+ err(1, "yylex: strdup");
+ return (token);
+ }
+ if (c == '\n') {
+ yylval.lineno = lineno;
+ lineno++;
+ }
+ if (c == EOF)
+ return (0);
+ return (c);
}
-struct node_host *
-host_v4(const char *s, int mask)
+int
+parse_rules(FILE *input, struct pfctl *xpf)
{
- 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);
+ struct sym *sym, *next;
+
+ fin = input;
+ pf = xpf;
+ lineno = 1;
+ errors = 0;
+ rulestate = PFCTL_STATE_NONE;
+ returnicmpdefault = (ICMP_UNREACH << 8) | ICMP_UNREACH_PORT;
+ returnicmp6default =
+ (ICMP6_DST_UNREACH << 8) | ICMP6_DST_UNREACH_NOPORT;
+ blockpolicy = PFRULE_DROP;
+ require_order = 1;
+
+ yyparse();
+
+ /* Free macros and check which have not been used. */
+ for (sym = TAILQ_FIRST(&symhead); sym != NULL; sym = next) {
+ next = TAILQ_NEXT(sym, entries);
+ if ((pf->opts & PF_OPT_VERBOSE2) && !sym->used)
+ fprintf(stderr, "warning: macro '%s' not "
+ "used\n", sym->nam);
+ free(sym->nam);
+ free(sym->val);
+ TAILQ_REMOVE(&symhead, sym, entries);
+ free(sym);
}
- h = calloc(1, sizeof(struct node_host));
- 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);
+ return (errors ? -1 : 0);
}
-struct node_host *
-host_v6(const char *s, int mask)
+/*
+ * Over-designed efficiency is a French and German concept, so how about
+ * we wait until they discover this ugliness and make it all fancy.
+ */
+int
+symset(const char *nam, const char *val, int persist)
{
- struct addrinfo hints, *res;
- struct node_host *h = NULL;
-
- memset(&hints, 0, sizeof(hints));
- hints.ai_family = AF_INET6;
- 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);
- freeaddrinfo(res);
- h->next = NULL;
- h->tail = h;
+ struct sym *sym;
+
+ for (sym = TAILQ_FIRST(&symhead); sym && strcmp(nam, sym->nam);
+ sym = TAILQ_NEXT(sym, entries))
+ ; /* nothing */
+
+ if (sym != NULL) {
+ if (sym->persist == 1)
+ return (0);
+ else {
+ free(sym->nam);
+ free(sym->val);
+ TAILQ_REMOVE(&symhead, sym, entries);
+ free(sym);
+ }
}
+ if ((sym = calloc(1, sizeof(*sym))) == NULL)
+ return (-1);
- return (h);
+ sym->nam = strdup(nam);
+ if (sym->nam == NULL) {
+ free(sym);
+ return (-1);
+ }
+ sym->val = strdup(val);
+ if (sym->val == NULL) {
+ free(sym->nam);
+ free(sym);
+ return (-1);
+ }
+ sym->used = 0;
+ sym->persist = persist;
+ TAILQ_INSERT_TAIL(&symhead, sym, entries);
+ return (0);
}
-struct node_host *
-host_dns(const char *s, int v4mask, int v6mask)
+int
+pfctl_cmdline_symset(char *s)
{
- struct addrinfo hints, *res0, *res;
- struct node_host *n, *h = NULL;
- int error, noalias = 0;
- int got4 = 0, got6 = 0;
- char *p, *ps;
-
- if ((ps = strdup(s)) == NULL)
- err(1, "host_dns: strdup");
- if ((p = strrchr(ps, ':')) != NULL && !strcmp(p, ":0")) {
- noalias = 1;
- *p = '\0';
- }
- 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);
+ char *sym, *val;
+ int ret;
+
+ 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);
+
+ ret = symset(sym, val + 1, 1);
+ free(sym);
+
+ return (ret);
+}
+
+char *
+symget(const char *nam)
+{
+ struct sym *sym;
+
+ TAILQ_FOREACH(sym, &symhead, entries)
+ if (strcmp(nam, sym->nam) == 0) {
+ sym->used = 1;
+ return (sym->val);
+ }
+ return (NULL);
+}
+
+void
+decide_address_family(struct node_host *n, sa_family_t *af)
+{
+ sa_family_t target_af = 0;
+
+ while (!*af && n != NULL) {
+ if (n->af) {
+ if (target_af == 0)
+ target_af = n->af;
+ if (target_af != n->af)
+ return;
+ }
+ n = n->next;
}
+ if (!*af && target_af)
+ *af = target_af;
+}
- for (res = res0; res; res = res->ai_next) {
- if (res->ai_family != AF_INET &&
- res->ai_family != AF_INET6)
- continue;
- if (noalias) {
- if (res->ai_family == AF_INET) {
- if (got4)
- continue;
- got4 = 1;
- } else {
- if (got6)
- continue;
- got6 = 1;
- }
- }
- n = calloc(1, sizeof(struct node_host));
- if (n == NULL)
- 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);
+void
+remove_invalid_hosts(struct node_host **nh, sa_family_t *af)
+{
+ struct node_host *n = *nh, *prev = NULL;
+
+ while (n != NULL) {
+ if (*af && n->af && n->af != *af) {
+ /* unlink and free n */
+ struct node_host *next = n->next;
+
+ /* adjust tail pointer */
+ if (n == (*nh)->tail)
+ (*nh)->tail = prev;
+ /* adjust previous node's next pointer */
+ if (prev == NULL)
+ *nh = next;
+ else
+ prev->next = next;
+ /* free node */
+ if (n->ifname != NULL)
+ free(n->ifname);
+ free(n);
+ n = next;
} else {
- memcpy(&n->addr.v.a.addr,
- &((struct sockaddr_in6 *)
- res->ai_addr)->sin6_addr.s6_addr,
- sizeof(struct in6_addr));
- n->ifindex =
- ((struct sockaddr_in6 *)
- res->ai_addr)->sin6_scope_id;
- set_ipmask(n, v6mask);
- }
- n->next = NULL;
- n->tail = n;
- if (h == NULL)
- h = n;
- else {
- h->tail->next = n;
- h->tail = n;
+ if (n->af && !*af)
+ *af = n->af;
+ prev = n;
+ n = n->next;
}
}
- freeaddrinfo(res0);
- free(ps);
+}
+
+int
+invalid_redirect(struct node_host *nh, sa_family_t af)
+{
+ if (!af) {
+ struct node_host *n;
- return (h);
+ /* tables and dyniftl are ok without an address family */
+ for (n = nh; n != NULL; n = n->next) {
+ if (n->addr.type != PF_ADDR_TABLE &&
+ n->addr.type != PF_ADDR_DYNIFTL) {
+ yyerror("address family not given and "
+ "translation address expands to multiple "
+ "address families");
+ return (1);
+ }
+ }
+ }
+ if (nh == NULL) {
+ yyerror("no translation address with matching address family "
+ "found.");
+ return (1);
+ }
+ return (0);
}
-/*
- * convert a hostname to a list of addresses and put them in the given buffer.
- * test:
- * if set to 1, only simple addresses are accepted (no netblock, no "!").
- */
int
-append_addr(struct pfr_buffer *b, char *s, int test)
+atoul(char *s, u_long *ulvalp)
{
- char *r;
- struct node_host *h, *n;
- int rv, not = 0;
-
- for (r = s; *r == '!'; r++)
- not = !not;
- if ((n = host(r)) == NULL) {
- errno = 0;
+ u_long ulval;
+ char *ep;
+
+ errno = 0;
+ ulval = strtoul(s, &ep, 0);
+ if (s[0] == '\0' || *ep != '\0')
return (-1);
- }
- rv = append_addr_host(b, n, test, not);
- do {
- h = n;
- n = n->next;
- free(h);
- } while (n != NULL);
- return (rv);
+ if (errno == ERANGE && ulval == ULONG_MAX)
+ return (-1);
+ *ulvalp = ulval;
+ return (0);
}
-/*
- * same as previous function, but with a pre-parsed input and the ability
- * to "negate" the result. Does not free the node_host list.
- * not:
- * setting it to 1 is equivalent to adding "!" in front of parameter s.
- */
int
-append_addr_host(struct pfr_buffer *b, struct node_host *n, int test, int not)
+getservice(char *n)
{
- int bits;
- struct pfr_addr addr;
-
- do {
- 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);
- switch (n->af) {
- case AF_INET:
- addr.pfra_ip4addr.s_addr = n->addr.v.a.addr.addr32[0];
- bits = 32;
- break;
- case AF_INET6:
- memcpy(&addr.pfra_ip6addr, &n->addr.v.a.addr.v6,
- sizeof(struct in6_addr));
- bits = 128;
- break;
- default:
- errno = EINVAL;
+ struct servent *s;
+ u_long ulval;
+
+ if (atoul(n, &ulval) == 0) {
+ if (ulval > 65535) {
+ yyerror("illegal port value %d", ulval);
return (-1);
}
- if ((test && (not || addr.pfra_net != bits)) ||
- addr.pfra_net > bits) {
- errno = EINVAL;
+ return (htons(ulval));
+ } else {
+ s = getservbyname(n, "tcp");
+ if (s == NULL)
+ s = getservbyname(n, "udp");
+ if (s == NULL) {
+ yyerror("unknown port %s", n);
return (-1);
}
- if (pfr_buf_add(b, &addr))
- return (-1);
- } while ((n = n->next) != NULL);
-
- return (0);
+ return (s->s_port);
+ }
}
int
-pfctl_add_trans(struct pfr_buffer *buf, int rs_num, const char *anchor)
+rule_label(struct pf_rule *r, char *s)
{
- struct pfioc_trans_e trans;
-
- bzero(&trans, sizeof(trans));
- trans.rs_num = rs_num;
- if (strlcpy(trans.anchor, anchor,
- sizeof(trans.anchor)) >= sizeof(trans.anchor))
- errx(1, "pfctl_add_trans: strlcpy");
-
- return pfr_buf_add(buf, &trans);
+ if (s) {
+ if (strlcpy(r->label, s, sizeof(r->label)) >=
+ sizeof(r->label)) {
+ yyerror("rule label too long (max %d chars)",
+ sizeof(r->label)-1);
+ return (-1);
+ }
+ }
+ return (0);
}
-u_int32_t
-pfctl_get_ticket(struct pfr_buffer *buf, int rs_num, const char *anchor)
+u_int16_t
+parseicmpspec(char *w, sa_family_t af)
{
- struct pfioc_trans_e *p;
+ const struct icmpcodeent *p;
+ u_long ulval;
+ u_int8_t icmptype;
- PFRB_FOREACH(p, buf)
- if (rs_num == p->rs_num && !strcmp(anchor, p->anchor))
- return (p->ticket);
- errx(1, "pfctl_get_ticket: assertion failed");
+ if (af == AF_INET)
+ icmptype = returnicmpdefault >> 8;
+ else
+ icmptype = returnicmp6default >> 8;
+
+ if (atoul(w, &ulval) == -1) {
+ if ((p = geticmpcodebyname(icmptype, w, af)) == NULL) {
+ yyerror("unknown icmp code %s", w);
+ return (0);
+ }
+ ulval = p->code;
+ }
+ if (ulval > 255) {
+ yyerror("invalid icmp code %ld", ulval);
+ return (0);
+ }
+ return (icmptype << 8 | ulval);
}
int
-pfctl_trans(int dev, struct pfr_buffer *buf, u_long cmd, int from)
+pfctl_load_anchors(int dev, int opts, struct pfr_buffer *trans)
{
- struct pfioc_trans trans;
+ struct loadanchors *la;
- bzero(&trans, sizeof(trans));
- trans.size = buf->pfrb_size - from;
- trans.esize = sizeof(struct pfioc_trans_e);
- trans.array = ((struct pfioc_trans_e *)buf->pfrb_caddr) + from;
- return ioctl(dev, cmd, &trans);
+ TAILQ_FOREACH(la, &loadanchorshead, entries) {
+ if (opts & PF_OPT_VERBOSE)
+ fprintf(stderr, "\nLoading anchor %s from %s\n",
+ la->anchorname, la->filename);
+ if (pfctl_rules(dev, la->filename, opts, la->anchorname,
+ trans) == -1)
+ return (-1);
+ }
+
+ return (0);
}
+
diff --git a/sbin/pfctl/pfctl_parser.c b/sbin/pfctl/pfctl_parser.c
index 748f23b1c02..033d9ca4134 100644
--- a/sbin/pfctl/pfctl_parser.c
+++ b/sbin/pfctl/pfctl_parser.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pfctl_parser.c,v 1.209 2004/12/07 05:30:27 mcbride Exp $ */
+/* $OpenBSD: pfctl_parser.c,v 1.210 2004/12/07 09:38:04 deraadt Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
@@ -877,10 +877,8 @@ print_rule(struct pf_rule *r, const char *anchor_call, int verbose)
if (!opts)
printf(", ");
printf("overload <%s>", r->overload_tblname);
- if (r->flush)
+ if (r->rule_flag & PFRULE_SRCTRACK_FLUSH)
printf(" flush");
- if (r->flush & PF_FLUSH_GLOBAL)
- printf(" global");
}
if (r->rule_flag & PFRULE_IFBOUND) {
if (!opts)