diff options
author | Theo de Raadt <deraadt@cvs.openbsd.org> | 2004-12-07 09:36:17 +0000 |
---|---|---|
committer | Theo de Raadt <deraadt@cvs.openbsd.org> | 2004-12-07 09:36:17 +0000 |
commit | ffcf866cd2eade2f8c3b4cf1340e449977f6b218 (patch) | |
tree | f82d8e15534f3fd9c3ba6f87509e214f51c2db3e /sbin/pfctl | |
parent | d6fd9cb07630888fc3d06c488e0dee919fd7fd6d (diff) |
tree does not compile, spotted by dlg (not obvious how to fix)
----
Change the default for 'overload <table> flush' to flush only states from the
offending source created by the rule. 'flush global' flushes all states
originating from the offending source. ABI change, requires kernel and pfctl
to be in sync.
ok deraadt@ henning@ dhartmei@
Diffstat (limited to 'sbin/pfctl')
-rw-r--r-- | sbin/pfctl/parse.y | 6204 |
1 files changed, 1365 insertions, 4839 deletions
diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y index a5e46d0988c..9e0da745497 100644 --- a/sbin/pfctl/parse.y +++ b/sbin/pfctl/parse.y @@ -1,34 +1,41 @@ -/* $OpenBSD: parse.y,v 1.463 2004/12/07 05:30:26 mcbride Exp $ */ +/* $OpenBSD: parse.y,v 1.464 2004/12/07 09:36:16 deraadt Exp $ */ /* - * 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. + * Copyright (c) 2001 Daniel Hartmeier + * 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. * - * 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. + * - 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. + * */ -%{ + #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> @@ -37,5033 +44,1552 @@ #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 <limits.h> -#include <pwd.h> -#include <grp.h> -#include <md5.h> +#include <ifaddrs.h> #include "pfctl_parser.h" #include "pfctl.h" -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 -}; - -struct node_proto { - u_int8_t proto; - struct node_proto *next; - struct node_proto *tail; -}; - -struct node_port { - u_int16_t port[2]; - u_int8_t op; - struct node_port *next; - struct node_port *tail; -}; - -struct node_uid { - uid_t uid[2]; - u_int8_t op; - struct node_uid *next; - struct node_uid *tail; -}; - -struct node_gid { - gid_t gid[2]; - u_int8_t op; - struct node_gid *next; - struct node_gid *tail; +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 } }; -struct node_icmp { - u_int8_t code; - u_int8_t type; - u_int8_t proto; - struct node_icmp *next; - struct node_icmp *tail; +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 } }; -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; +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_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; +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 } }; - -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; +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 } }; -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); - } - | 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); - } - | 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); - } - ; - -string : string STRING { - if (asprintf(&$$, "%s %s", $1, $2) == -1) - err(1, "string: asprintf"); - free($1); - free($2); - } - | STRING - ; - -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); - } - ; - -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); - } - | NATANCHOR 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_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); - } - | 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); - } - | BINATANCHOR 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_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); - } - ; - -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; - } - ; - -scrubrule : scrubaction dir logquick interface af proto fromto scrub_opts - { - struct pf_rule r; - - if (check_rulestate(PFCTL_STATE_SCRUB)) - YYERROR; - - memset(&r, 0, sizeof(r)); - - r.action = $1.b1; - r.direction = $2; - - r.log = $3.log; - if ($3.quick) { - yyerror("scrub rules do not support 'quick'"); - YYERROR; - } - - 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; - } - r.flush = o->data.overload.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 { $$ = PF_FLUSH; } - | FLUSH GLOBAL { - $$ = PF_FLUSH | PF_FLUSH_GLOBAL; - } - ; - -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 - ; +const struct icmptypeent * +geticmptypebynumber(u_int8_t type, sa_family_t af) +{ + unsigned int i; -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); + 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]); } - ; - -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); + } else { + for (i=0; i < (sizeof (icmp6_type) / + sizeof(icmp6_type[0])); i++) { + if (type == icmp6_type[i].type) + return (&icmp6_type[i]); } - ; - -unaryop : '=' { $$ = PF_OP_EQ; } - | '!' '=' { $$ = PF_OP_NE; } - | '<' '=' { $$ = PF_OP_LE; } - | '<' { $$ = PF_OP_LT; } - | '>' '=' { $$ = PF_OP_GE; } - | '>' { $$ = PF_OP_GT; } - ; - -%% - -int -yyerror(const char *fmt, ...) -{ - va_list ap; - extern char *infile; - - 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); + } + return (NULL); } -int -disallow_table(struct node_host *h, const char *fmt) +const struct icmptypeent * +geticmptypebyname(char *w, sa_family_t af) { - for (; h != NULL; h = h->next) - if (h->addr.type == PF_ADDR_TABLE) { - yyerror(fmt, h->addr.v.tblname); - return (1); - } - return (0); -} + unsigned int i; -int -disallow_alias(struct node_host *h, const char *fmt) -{ - for (; h != NULL; h = h->next) - if (DYNIF_MULTIADDR(h->addr)) { - yyerror(fmt, h->addr.v.tblname); - return (1); + 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]); } - return (0); + } else { + for (i=0; i < (sizeof (icmp6_type) / + sizeof(icmp6_type[0])); i++) { + if (!strcmp(w, icmp6_type[i].name)) + return (&icmp6_type[i]); + } + } + return (NULL); } -int -rule_consistent(struct pf_rule *r) +const struct icmpcodeent * +geticmpcodebynumber(u_int8_t type, u_int8_t code, sa_family_t af) { - int problems = 0; + unsigned int i; - switch (r->action) { - case PF_PASS: - case PF_DROP: - case PF_SCRUB: - case PF_NOSCRUB: - problems = filter_consistent(r); - break; - case PF_NAT: - case PF_NONAT: - problems = nat_consistent(r); - break; - case PF_RDR: - case PF_NORDR: - problems = rdr_consistent(r); - break; - case PF_BINAT: - case PF_NOBINAT: - default: - break; + 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]); + } + } 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]); + } } - return (problems); + return (NULL); } -int -filter_consistent(struct pf_rule *r) +const struct icmpcodeent * +geticmpcodebyname(u_long type, char *w, sa_family_t af) { - int problems = 0; + unsigned int i; - 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++; - } - if (r->proto != IPPROTO_ICMP && r->proto != IPPROTO_ICMPV6 && - (r->type || r->code)) { - yyerror("icmp-type/code only applies to icmp"); - problems++; - } - if (!r->af && (r->type || r->code)) { - yyerror("must indicate address family with icmp-type/code"); - problems++; - } - 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++; - } - if (r->allow_opts && r->action != PF_PASS) { - yyerror("allow-opts can only be specified for pass rules"); - problems++; - } - 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++; + 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]); + } + } 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]); + } } - return (-problems); + return (NULL); } -int -nat_consistent(struct pf_rule *r) +void +print_op(u_int8_t op, const char *a1, const char *a2) { - return (0); /* yeah! */ + 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); } -int -rdr_consistent(struct pf_rule *r) +void +print_port(u_int8_t op, u_int16_t p1, u_int16_t p2, const char *proto) { - int problems = 0; + char a1[6], a2[6]; + struct servent *s; - 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++; - } - 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); + 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); } -int -process_tabledef(char *name, struct table_opts *opts) +void +print_ugid(u_int8_t op, unsigned u1, unsigned u2, const char *t, unsigned umax) { - struct pfr_buffer ab; - struct node_tinit *ti; + char a1[11], a2[11]; - 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 - 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; - } - } - 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; - } - pf->tdirty = 1; - pfr_buf_clear(&ab); - return (0); -_error: - pfr_buf_clear(&ab); - return (-1); + 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); } -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) +print_flags(u_int8_t f) { - char *tmp; - char *p, *q; + int i; - 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 (strlcat(tmp, p, len) >= len) - errx(1, "expand_label: label too long"); - strlcpy(label, tmp, len); /* always fits */ - free(tmp); + for (i = 0; tcpflags[i]; ++i) + if (f & (1 << i)) + printf("%c", tcpflags[i]); } void -expand_label_if(const char *name, char *label, size_t len, const char *ifname) +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) { - if (strstr(label, name) != NULL) { - if (!*ifname) - expand_label_str(label, len, name, "any"); - else - expand_label_str(label, len, name, ifname); + 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"); } } void -expand_label_addr(const char *name, char *label, size_t len, sa_family_t af, - struct node_host *h) +print_pool(struct pf_pool *pool, u_int16_t p1, u_int16_t p2, + sa_family_t af, int id) { - char tmp[64], tmp_not[66]; - - 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); + 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_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"); + case PF_PASS: + if (PF_AZERO(&pooladdr->addr.v.a.addr, af)) + printf("%s", pooladdr->ifname); else { - 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); - } + printf("(%s ", pooladdr->ifname); + print_addr(&pooladdr->addr, af, 0); + printf(")"); } break; default: - snprintf(tmp, sizeof(tmp), "?"); break; } - - 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); + if (TAILQ_NEXT(pooladdr, entries) != NULL) + printf(", "); + else if (TAILQ_NEXT(TAILQ_FIRST(&pool->list), entries) != NULL) + printf(" }"); } -} - -void -expand_label_port(const char *name, char *label, size_t len, - struct node_port *port) -{ - char a1[6], a2[6], op[13] = ""; - - 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); + switch (id) { + 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); + } + break; + case PF_RDR: + if (p1) { + printf(" port %u", p1); + if (p2 && (p2 != p1)) + printf(":%u", p2); + } + break; + 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"); } +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 -expand_label_proto(const char *name, char *label, size_t len, u_int8_t proto) +print_status(struct pf_status *s, int opts) { - struct protoent *pe; - char n[4]; - - 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); + 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; + } + 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]); + } + 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 (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", ""); + } + } + 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 (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", ""); } } } void -expand_label_nr(const char *name, char *label, size_t len) +print_src_node(struct pf_src_node *sn, int opts) { - char n[11]; + struct pf_addr_wrap aw; + int min, sec; - if (strstr(label, name) != NULL) { - snprintf(n, sizeof(n), "%u", pf->rule_nr); - expand_label_str(label, len, name, n); + 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; + } + printf("\n"); } } 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) +print_rule(struct pf_rule *r, const char *anchor_call, int verbose) { - 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 -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 pf_altq pa, pb; - char qname[PF_QNAME_SIZE]; - struct node_queue *n; - struct node_queue_bw bw; - int errs = 0; - - if ((pf->loadopt & PFCTL_FLAG_ALTQ) == 0) { - FREE_LIST(struct node_if, interfaces); - FREE_LIST(struct node_queue, nqueues); - return (0); + 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"); } - - 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"); - - if (interface->not) { - yyerror("altq on ! <interface> is not supported"); - errs++; - } else { - if (eval_pfaltq(pf, &pa, &bwspec, opts)) - errs++; + 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 - if (pfctl_add_altq(pf, &pa)) - errs++; - - 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 (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++; + 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); else - if (pfctl_add_altq(pf, &pb)) - errs++; + printf("%s)", ic6->name); + break; } - - 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; - } - ); + } 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); } - ); - FREE_LIST(struct node_if, interfaces); - FREE_LIST(struct node_queue, nqueues); - - return (errs); -} - -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_queue *n, *nq; - struct pf_altq pa; - u_int8_t found = 0; - u_int8_t errs = 0; - - if ((pf->loadopt & PFCTL_FLAG_ALTQ) == 0) { - FREE_LIST(struct node_queue, nqueues); - return (0); } - - if (queues == NULL) { - yyerror("queue %s has no parent", a->qname); - FREE_LIST(struct node_queue, nqueues); - return (1); + if (r->af) { + if (r->af == AF_INET) + printf(" inet"); + else + printf(" inet6"); } + if (r->proto) { + struct protoent *p; - 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 ((p = getprotobynumber(r->proto)) != NULL) + printf(" proto %s", p->p_name); + 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); + } + if (r->type) { + const struct icmptypeent *it; - if (eval_pfqueue(pf, &pa, &bwspec, opts)) - errs++; - else - if (pfctl_add_altq(pf, &pa)) - errs++; + 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; - 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"); - } + 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'; + else { + if (buf[i] == '.') + buf[i] = '\0'; + break; } - ); - ); - - FREE_LIST(struct node_queue, nqueues); - FREE_LIST(struct node_if, interfaces); - - if (!found) { - yyerror("queue %s has no parent", a->qname); - errs++; + } + printf(" probability %s%%", buf); } - - if (errs) - return (1); - else - return (0); + 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); + } + printf("\n"); } 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) +print_tabledef(const char *name, int flags, int addrs, + struct node_tinithead *nodes) { - 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_tinit *ti, *nti; struct node_host *h; - u_int8_t flags, flagset, keep_state; - 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; - - 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, - - 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))) + 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; - 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 - 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 - 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 - pa->ifname[0] = 0; - TAILQ_INSERT_TAIL(&r->rpool.list, pa, entries); - } - - if (rule_consistent(r) < 0 || error) - yyerror("skipping rule due to errors"); - else { - r->nr = pf->rule_nr++; - pfctl_add_rule(pf, r, anchor_call); - added++; + 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; } - - )))))))))); - - 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 -check_rulestate(int desired_state) -{ - if (require_order && (rulestate > desired_state)) { - yyerror("Rules must be in order: options, normalization, " - "queueing, translation, filtering"); - return (1); + printf(" }"); } - rulestate = desired_state; - return (0); -} - -int -kw_cmp(const void *k, const void *e) -{ - return (strcmp(k, ((const struct keywords *)e)->k_name)); + if (addrs && SIMPLEQ_EMPTY(nodes)) + printf(" { }"); + printf("\n"); } int -lookup(char *s) +parse_flags(char *s) { - /* 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; + char *p, *q; + u_int8_t f = 0; - 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 (debug > 1) - fprintf(stderr, "string: %s\n", s); - return (STRING); + for (p = s; *p; p++) { + if ((q = strchr(tcpflags, *p)) == NULL) + return -1; + else + f |= 1 << (q - tcpflags); } + return (f ? f : PF_TH_ALL); } -#define MAXPUSHBACK 128 - -char *parsebuf; -int parseindex; -char pushback_buffer[MAXPUSHBACK]; -int pushback_index = 0; - -int -lgetc(FILE *f) +void +set_ipmask(struct node_host *h, u_int8_t b) { - 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++; - } + struct pf_addr *m, *n; + int i, j = 0; - if (pushback_index) - return (pushback_buffer[--pushback_index]); + m = &h->addr.v.a.mask; + memset(m, 0, sizeof(*m)); - while ((c = getc(f)) == '\\') { - next = getc(f); - if (next != '\n') { - if (isspace(next)) - yyerror("whitespace after \\"); - ungetc(next, f); - break; - } - yylval.lineno = lineno; - lineno++; + while (b >= 32) { + m->addr32[j++] = 0xffffffff; + b -= 32; } - if (c == '\t' || c == ' ') { - /* Compress blanks to a single space. */ - do { - c = getc(f); - } while (c == '\t' || c == ' '); - ungetc(c, f); - c = ' '; - } - - return (c); + 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]; } int -lungetc(int c) +check_netmask(struct node_host *h, sa_family_t af) { - if (c == EOF) - return (EOF); - if (parsebuf) { - parseindex--; - if (parseindex >= 0) - return (c); - } - if (pushback_index < MAXPUSHBACK-1) - return (pushback_buffer[pushback_index++] = c); - else - return (EOF); -} + struct node_host *n = NULL; + struct pf_addr *m; -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; + 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 (c == EOF) - break; } - return (ERROR); + return (0); } -int -yylex(void) -{ - char buf[8096]; - char *p, *val; - int endc, c, next; - int token; +/* interface lookup routines */ -top: - p = buf; - while ((c = lgetc(fin)) == ' ') - ; /* nothing */ +struct node_host *iftab; - 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); +void +ifa_load(void) +{ + struct ifaddrs *ifap, *ifa; + struct node_host *n = NULL, *h = NULL; - 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 (getifaddrs(&ifap) < 0) + err(1, "getifaddrs"); - 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++; + 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; - } - if (p + 1 >= buf + sizeof(buf) - 1) { - yyerror("string too long"); - return (findeol()); - } - *p++ = (char)c; + 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; } - 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); +#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; } - lungetc(next); - break; - case '-': - next = lgetc(fin); - if (next == '>') - return (ARROW); - lungetc(next); - break; } -#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); + iftab = h; + freeifaddrs(ifap); } -int -parse_rules(FILE *input, struct pfctl *xpf) +struct node_host * +ifa_exists(const char *ifa_name, int group_ok) { - 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; + struct node_host *n; - yyparse(); + if (iftab == NULL) + ifa_load(); - /* 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); + for (n = iftab; n; n = n->next) { + if (n->af == AF_LINK && !strncmp(n->ifname, ifa_name, IFNAMSIZ)) + return (n); } - return (errors ? -1 : 0); + return (NULL); } -/* - * 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 node_host * +ifa_lookup(const char *ifa_name, int flags) { - struct sym *sym; + struct node_host *p = NULL, *h = NULL, *n = NULL; + int got4 = 0, got6 = 0; + const char *last_if = NULL; - for (sym = TAILQ_FIRST(&symhead); sym && strcmp(nam, sym->nam); - sym = TAILQ_NEXT(sym, entries)) - ; /* nothing */ + if (!strncmp(ifa_name, "self", IFNAMSIZ)) + ifa_name = NULL; - if (sym != NULL) { - if (sym->persist == 1) - return (0); + if (iftab == NULL) + ifa_load(); + + 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) + continue; + if ((flags & PFI_AFLAG_NOALIAS) && p->af == AF_INET6 && got6) + continue; + if (p->af == AF_INET) + got4 = 1; + 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)); + 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 { - free(sym->nam); - free(sym->val); - TAILQ_REMOVE(&symhead, sym, entries); - free(sym); + if (n->af == AF_INET) { + if (p->ifa_flags & IFF_LOOPBACK && + p->ifa_flags & IFF_LINK1) + memcpy(&n->addr.v.a.mask, + &p->addr.v.a.mask, + sizeof(struct pf_addr)); + else + set_ipmask(n, 32); + } else + set_ipmask(n, 128); } - } - if ((sym = calloc(1, sizeof(*sym))) == NULL) - return (-1); + n->ifindex = p->ifindex; - 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); + n->next = NULL; + n->tail = n; + if (h == NULL) + h = n; + else { + h->tail->next = n; + h->tail = n; + } } - sym->used = 0; - sym->persist = persist; - TAILQ_INSERT_TAIL(&symhead, sym, entries); - return (0); + return (h); } int -pfctl_cmdline_symset(char *s) +ifa_skip_if(const char *filter, struct node_host *p) { - char *sym, *val; - int ret; + int n; - if ((val = strrchr(s, '=')) == NULL) - return (-1); + if (p->af != AF_INET && p->af != AF_INET6) + 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'); +} - if ((sym = malloc(strlen(s) - strlen(val) + 1)) == NULL) - err(1, "pfctl_cmdline_symset: malloc"); - strlcpy(sym, s, strlen(s) - strlen(val) + 1); +struct node_host * +host(const 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; + } else { + if ((ps = strdup(s)) == NULL) + err(1, "host: strdup"); + v4mask = 32; + v6mask = 128; + mask = -1; + } - ret = symset(sym, val + 1, 1); - free(sym); + /* interface with this name exists? */ + if (cont && (h = host_if(ps, mask)) != NULL) + cont = 0; - return (ret); -} + /* IPv4 address? */ + if (cont && (h = host_v4(s, mask)) != NULL) + cont = 0; -char * -symget(const char *nam) -{ - struct sym *sym; + /* IPv6 address? */ + if (cont && (h = host_v6(ps, v6mask)) != NULL) + cont = 0; - TAILQ_FOREACH(sym, &symhead, entries) - if (strcmp(nam, sym->nam) == 0) { - sym->used = 1; - return (sym->val); - } - return (NULL); + /* dns lookup */ + if (cont && (h = host_dns(ps, v4mask, v6mask)) != NULL) + cont = 0; + free(ps); + + if (h == NULL || cont == 1) { + fprintf(stderr, "no IP address found for %s\n", s); + return (NULL); + } + return (h); } -void -decide_address_family(struct node_host *n, sa_family_t *af) +struct node_host * +host_if(const char *s, int mask) { - 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; + 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); } - n = n->next; + *p = '\0'; + } + if (flags & (flags - 1) & PFI_AFLAG_MODEMASK) { /* Yep! */ + fprintf(stderr, "illegal combination of interface modifiers\n"); + free(ps); + return (NULL); + } + 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); + } + 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); } - if (!*af && target_af) - *af = target_af; + + free(ps); + return (h); } -void -remove_invalid_hosts(struct node_host **nh, sa_family_t *af) +struct node_host * +host_v4(const char *s, int mask) { - struct node_host *n = *nh, *prev = NULL; + 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); + } - while (n != NULL) { - if (*af && n->af && n->af != *af) { - /* unlink and free n */ - struct node_host *next = n->next; + 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); +} - /* 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 { - if (n->af && !*af) - *af = n->af; - prev = n; - n = n->next; - } +struct node_host * +host_v6(const char *s, int mask) +{ + 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; } + + return (h); } -int -invalid_redirect(struct node_host *nh, sa_family_t af) +struct node_host * +host_dns(const char *s, int v4mask, int v6mask) { - if (!af) { - struct node_host *n; + 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); + } - /* 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); - } + 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); + } 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 (nh == NULL) { - yyerror("no translation address with matching address family " - "found."); - return (1); - } - return (0); + freeaddrinfo(res0); + free(ps); + + return (h); } +/* + * 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 -atoul(char *s, u_long *ulvalp) +append_addr(struct pfr_buffer *b, char *s, int test) { - u_long ulval; - char *ep; - - errno = 0; - ulval = strtoul(s, &ep, 0); - if (s[0] == '\0' || *ep != '\0') + 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; return (-1); - if (errno == ERANGE && ulval == ULONG_MAX) - return (-1); - *ulvalp = ulval; - return (0); + } + rv = append_addr_host(b, n, test, not); + do { + h = n; + n = n->next; + free(h); + } while (n != NULL); + return (rv); } +/* + * 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 -getservice(char *n) +append_addr_host(struct pfr_buffer *b, struct node_host *n, int test, int not) { - struct servent *s; - u_long ulval; - - if (atoul(n, &ulval) == 0) { - if (ulval > 65535) { - yyerror("illegal port value %d", ulval); + 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; return (-1); } - return (htons(ulval)); - } else { - s = getservbyname(n, "tcp"); - if (s == NULL) - s = getservbyname(n, "udp"); - if (s == NULL) { - yyerror("unknown port %s", n); + if ((test && (not || addr.pfra_net != bits)) || + addr.pfra_net > bits) { + errno = EINVAL; return (-1); } - return (s->s_port); - } + if (pfr_buf_add(b, &addr)) + return (-1); + } while ((n = n->next) != NULL); + + return (0); } int -rule_label(struct pf_rule *r, char *s) +pfctl_add_trans(struct pfr_buffer *buf, int rs_num, const char *anchor) { - 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); + 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); } -u_int16_t -parseicmpspec(char *w, sa_family_t af) +u_int32_t +pfctl_get_ticket(struct pfr_buffer *buf, int rs_num, const char *anchor) { - const struct icmpcodeent *p; - u_long ulval; - u_int8_t icmptype; + struct pfioc_trans_e *p; - 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); + PFRB_FOREACH(p, buf) + if (rs_num == p->rs_num && !strcmp(anchor, p->anchor)) + return (p->ticket); + errx(1, "pfctl_get_ticket: assertion failed"); } int -pfctl_load_anchors(int dev, int opts, struct pfr_buffer *trans) +pfctl_trans(int dev, struct pfr_buffer *buf, u_long cmd, int from) { - struct loadanchors *la; + struct pfioc_trans 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); + 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); } - |