diff options
author | glebius <glebius@FreeBSD.org> | 2012-09-14 11:51:49 +0000 |
---|---|---|
committer | glebius <glebius@FreeBSD.org> | 2012-09-14 11:51:49 +0000 |
commit | 0ccf4838d7a8b4da2c3beaac7ea1fd977aa0ed11 (patch) | |
tree | ec60da6e90cde2e87aa91ac9450c84ce3446233a /contrib/pf | |
parent | f99fc207edf21e7c05c1147864077ce3fe1f3e2c (diff) | |
download | FreeBSD-src-0ccf4838d7a8b4da2c3beaac7ea1fd977aa0ed11.zip FreeBSD-src-0ccf4838d7a8b4da2c3beaac7ea1fd977aa0ed11.tar.gz |
o Create directory sys/netpfil, where all packet filters should
reside, and move there ipfw(4) and pf(4).
o Move most modified parts of pf out of contrib.
Actual movements:
sys/contrib/pf/net/*.c -> sys/netpfil/pf/
sys/contrib/pf/net/*.h -> sys/net/
contrib/pf/pfctl/*.c -> sbin/pfctl
contrib/pf/pfctl/*.h -> sbin/pfctl
contrib/pf/pfctl/pfctl.8 -> sbin/pfctl
contrib/pf/pfctl/*.4 -> share/man/man4
contrib/pf/pfctl/*.5 -> share/man/man5
sys/netinet/ipfw -> sys/netpfil/ipfw
The arguable movement is pf/net/*.h -> sys/net. There are
future plans to refactor pf includes, so I decided not to
break things twice.
Not modified bits of pf left in contrib: authpf, ftp-proxy,
tftp-proxy, pflogd.
The ipfw(4) movement is planned to be merged to stable/9,
to make head and stable match.
Discussed with: bz, luigi
Diffstat (limited to 'contrib/pf')
-rw-r--r-- | contrib/pf/man/pf.4 | 1175 | ||||
-rw-r--r-- | contrib/pf/man/pf.conf.5 | 3066 | ||||
-rw-r--r-- | contrib/pf/man/pf.os.5 | 223 | ||||
-rw-r--r-- | contrib/pf/man/pflog.4 | 107 | ||||
-rw-r--r-- | contrib/pf/man/pfsync.4 | 229 | ||||
-rw-r--r-- | contrib/pf/pfctl/parse.y | 6038 | ||||
-rw-r--r-- | contrib/pf/pfctl/pf_print_state.c | 375 | ||||
-rw-r--r-- | contrib/pf/pfctl/pfctl.8 | 687 | ||||
-rw-r--r-- | contrib/pf/pfctl/pfctl.c | 2391 | ||||
-rw-r--r-- | contrib/pf/pfctl/pfctl.h | 130 | ||||
-rw-r--r-- | contrib/pf/pfctl/pfctl_altq.c | 1258 | ||||
-rw-r--r-- | contrib/pf/pfctl/pfctl_optimize.c | 1655 | ||||
-rw-r--r-- | contrib/pf/pfctl/pfctl_osfp.c | 1108 | ||||
-rw-r--r-- | contrib/pf/pfctl/pfctl_parser.c | 1746 | ||||
-rw-r--r-- | contrib/pf/pfctl/pfctl_parser.h | 305 | ||||
-rw-r--r-- | contrib/pf/pfctl/pfctl_qstats.c | 449 | ||||
-rw-r--r-- | contrib/pf/pfctl/pfctl_radix.c | 585 | ||||
-rw-r--r-- | contrib/pf/pfctl/pfctl_table.c | 634 |
18 files changed, 0 insertions, 22161 deletions
diff --git a/contrib/pf/man/pf.4 b/contrib/pf/man/pf.4 deleted file mode 100644 index 635078d..0000000 --- a/contrib/pf/man/pf.4 +++ /dev/null @@ -1,1175 +0,0 @@ -.\" $OpenBSD: pf.4,v 1.62 2008/09/10 14:57:37 jmc Exp $ -.\" -.\" Copyright (C) 2001, Kjell Wooding. 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. -.\" 3. Neither the name of the project nor the names of its contributors -.\" may be used to endorse or promote products derived from this software -.\" without specific prior written permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT 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 PROJECT 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. -.\" -.\" $FreeBSD$ -.\" -.Dd June 29 2012 -.Dt PF 4 -.Os -.Sh NAME -.Nm pf -.Nd packet filter -.Sh SYNOPSIS -.Cd "device pf" -.Sh DESCRIPTION -Packet filtering takes place in the kernel. -A pseudo-device, -.Pa /dev/pf , -allows userland processes to control the -behavior of the packet filter through an -.Xr ioctl 2 -interface. -There are commands to enable and disable the filter, load rulesets, -add and remove individual rules or state table entries, -and retrieve statistics. -The most commonly used functions are covered by -.Xr pfctl 8 . -.Pp -Manipulations like loading a ruleset that involve more than a single -.Xr ioctl 2 -call require a so-called -.Em ticket , -which prevents the occurrence of -multiple concurrent manipulations. -.Pp -Fields of -.Xr ioctl 2 -parameter structures that refer to packet data (like -addresses and ports) are generally expected in network byte-order. -.Pp -Rules and address tables are contained in so-called -.Em anchors . -When servicing an -.Xr ioctl 2 -request, if the anchor field of the argument structure is empty, -the kernel will use the default anchor (i.e., the main ruleset) -in operations. -Anchors are specified by name and may be nested, with components -separated by -.Sq / -characters, similar to how file system hierarchies are laid out. -The final component of the anchor path is the anchor under which -operations will be performed. -.Sh SYSCTL VARIABLES AND LOADER TUNABLES -The following -.Xr loader 8 -tunables are available. -.Bl -tag -width indent -.It Va net.pf.states_hashsize -Size of hash tables that store states. -Should be power of 2. -Default value is 32768. -.It Va net.pf.source_nodes_hashsize -Size of hash table that store source nodes. -Should be power of 2. -Default value is 8192. -.El -.Pp -Read only -.Xr sysctl 8 -variables with matching names are provided to obtain current values -at runtime. -.Sh IOCTL INTERFACE -.Nm -supports the following -.Xr ioctl 2 -commands, available through -.Aq Pa net/pfvar.h : -.Bl -tag -width xxxxxx -.It Dv DIOCSTART -Start the packet filter. -.It Dv DIOCSTOP -Stop the packet filter. -.It Dv DIOCSTARTALTQ -Start the ALTQ bandwidth control system (see -.Xr altq 9 ) . -.It Dv DIOCSTOPALTQ -Stop the ALTQ bandwidth control system. -.It Dv DIOCBEGINADDRS Fa "struct pfioc_pooladdr *pp" -.Bd -literal -struct pfioc_pooladdr { - u_int32_t action; - u_int32_t ticket; - u_int32_t nr; - u_int32_t r_num; - u_int8_t r_action; - u_int8_t r_last; - u_int8_t af; - char anchor[MAXPATHLEN]; - struct pf_pooladdr addr; -}; -.Ed -.Pp -Clear the buffer address pool and get a -.Va ticket -for subsequent -.Dv DIOCADDADDR , -.Dv DIOCADDRULE , -and -.Dv DIOCCHANGERULE -calls. -.It Dv DIOCADDADDR Fa "struct pfioc_pooladdr *pp" -.Pp -Add the pool address -.Va addr -to the buffer address pool to be used in the following -.Dv DIOCADDRULE -or -.Dv DIOCCHANGERULE -call. -All other members of the structure are ignored. -.It Dv DIOCADDRULE Fa "struct pfioc_rule *pr" -.Bd -literal -struct pfioc_rule { - u_int32_t action; - u_int32_t ticket; - u_int32_t pool_ticket; - u_int32_t nr; - char anchor[MAXPATHLEN]; - char anchor_call[MAXPATHLEN]; - struct pf_rule rule; -}; -.Ed -.Pp -Add -.Va rule -at the end of the inactive ruleset. -This call requires a -.Va ticket -obtained through a preceding -.Dv DIOCXBEGIN -call and a -.Va pool_ticket -obtained through a -.Dv DIOCBEGINADDRS -call. -.Dv DIOCADDADDR -must also be called if any pool addresses are required. -The optional -.Va anchor -name indicates the anchor in which to append the rule. -.Va nr -and -.Va action -are ignored. -.It Dv DIOCADDALTQ Fa "struct pfioc_altq *pa" -Add an ALTQ discipline or queue. -.Bd -literal -struct pfioc_altq { - u_int32_t action; - u_int32_t ticket; - u_int32_t nr; - struct pf_altq altq; -}; -.Ed -.It Dv DIOCGETRULES Fa "struct pfioc_rule *pr" -Get a -.Va ticket -for subsequent -.Dv DIOCGETRULE -calls and the number -.Va nr -of rules in the active ruleset. -.It Dv DIOCGETRULE Fa "struct pfioc_rule *pr" -Get a -.Va rule -by its number -.Va nr -using the -.Va ticket -obtained through a preceding -.Dv DIOCGETRULES -call. -If -.Va action -is set to -.Dv PF_GET_CLR_CNTR , -the per-rule statistics on the requested rule are cleared. -.It Dv DIOCGETADDRS Fa "struct pfioc_pooladdr *pp" -Get a -.Va ticket -for subsequent -.Dv DIOCGETADDR -calls and the number -.Va nr -of pool addresses in the rule specified with -.Va r_action , -.Va r_num , -and -.Va anchor . -.It Dv DIOCGETADDR Fa "struct pfioc_pooladdr *pp" -Get the pool address -.Va addr -by its number -.Va nr -from the rule specified with -.Va r_action , -.Va r_num , -and -.Va anchor -using the -.Va ticket -obtained through a preceding -.Dv DIOCGETADDRS -call. -.It Dv DIOCGETALTQS Fa "struct pfioc_altq *pa" -Get a -.Va ticket -for subsequent -.Dv DIOCGETALTQ -calls and the number -.Va nr -of queues in the active list. -.It Dv DIOCGETALTQ Fa "struct pfioc_altq *pa" -Get the queueing discipline -.Va altq -by its number -.Va nr -using the -.Va ticket -obtained through a preceding -.Dv DIOCGETALTQS -call. -.It Dv DIOCGETQSTATS Fa "struct pfioc_qstats *pq" -Get the statistics on a queue. -.Bd -literal -struct pfioc_qstats { - u_int32_t ticket; - u_int32_t nr; - void *buf; - int nbytes; - u_int8_t scheduler; -}; -.Ed -.Pp -This call fills in a pointer to the buffer of statistics -.Va buf , -of length -.Va nbytes , -for the queue specified by -.Va nr . -.It Dv DIOCGETRULESETS Fa "struct pfioc_ruleset *pr" -.Bd -literal -struct pfioc_ruleset { - u_int32_t nr; - char path[MAXPATHLEN]; - char name[PF_ANCHOR_NAME_SIZE]; -}; -.Ed -.Pp -Get the number -.Va nr -of rulesets (i.e., anchors) directly attached to the anchor named by -.Va path -for use in subsequent -.Dv DIOCGETRULESET -calls. -Nested anchors, since they are not directly attached to the given -anchor, will not be included. -This ioctl returns -.Er EINVAL -if the given anchor does not exist. -.It Dv DIOCGETRULESET Fa "struct pfioc_ruleset *pr" -Get a ruleset (i.e., an anchor) -.Va name -by its number -.Va nr -from the given anchor -.Va path , -the maximum number of which can be obtained from a preceding -.Dv DIOCGETRULESETS -call. -This ioctl returns -.Er EINVAL -if the given anchor does not exist or -.Er EBUSY -if another process is concurrently updating a ruleset. -.It Dv DIOCADDSTATE Fa "struct pfioc_state *ps" -Add a state entry. -.Bd -literal -struct pfioc_state { - struct pfsync_state state; -}; -.Ed -.It Dv DIOCGETSTATE Fa "struct pfioc_state *ps" -Extract the entry identified by the -.Va id -and -.Va creatorid -fields of the -.Va state -structure from the state table. -.It Dv DIOCKILLSTATES Fa "struct pfioc_state_kill *psk" -Remove matching entries from the state table. -This ioctl returns the number of killed states in -.Va psk_killed . -.Bd -literal -struct pfioc_state_kill { - struct pf_state_cmp psk_pfcmp; - sa_family_t psk_af; - int psk_proto; - struct pf_rule_addr psk_src; - struct pf_rule_addr psk_dst; - char psk_ifname[IFNAMSIZ]; - char psk_label[PF_RULE_LABEL_SIZE]; - u_int psk_killed; -}; -.Ed -.It Dv DIOCCLRSTATES Fa "struct pfioc_state_kill *psk" -Clear all states. -It works like -.Dv DIOCKILLSTATES , -but ignores the -.Va psk_af , -.Va psk_proto , -.Va psk_src , -and -.Va psk_dst -fields of the -.Vt pfioc_state_kill -structure. -.It Dv DIOCSETSTATUSIF Fa "struct pfioc_if *pi" -Specify the interface for which statistics are accumulated. -.Bd -literal -struct pfioc_if { - char ifname[IFNAMSIZ]; -}; -.Ed -.It Dv DIOCGETSTATUS Fa "struct pf_status *s" -Get the internal packet filter statistics. -.Bd -literal -struct pf_status { - u_int64_t counters[PFRES_MAX]; - u_int64_t lcounters[LCNT_MAX]; - u_int64_t fcounters[FCNT_MAX]; - u_int64_t scounters[SCNT_MAX]; - u_int64_t pcounters[2][2][3]; - u_int64_t bcounters[2][2]; - u_int32_t running; - u_int32_t states; - u_int32_t src_nodes; - u_int32_t since; - u_int32_t debug; - u_int32_t hostid; - char ifname[IFNAMSIZ]; - u_int8_t pf_chksum[MD5_DIGEST_LENGTH]; -}; -.Ed -.It Dv DIOCCLRSTATUS -Clear the internal packet filter statistics. -.It Dv DIOCNATLOOK Fa "struct pfioc_natlook *pnl" -Look up a state table entry by source and destination addresses and ports. -.Bd -literal -struct pfioc_natlook { - struct pf_addr saddr; - struct pf_addr daddr; - struct pf_addr rsaddr; - struct pf_addr rdaddr; - u_int16_t sport; - u_int16_t dport; - u_int16_t rsport; - u_int16_t rdport; - sa_family_t af; - u_int8_t proto; - u_int8_t direction; -}; -.Ed -.It Dv DIOCSETDEBUG Fa "u_int32_t *level" -Set the debug level. -.Bd -literal -enum { PF_DEBUG_NONE, PF_DEBUG_URGENT, PF_DEBUG_MISC, - PF_DEBUG_NOISY }; -.Ed -.It Dv DIOCGETSTATES Fa "struct pfioc_states *ps" -Get state table entries. -.Bd -literal -struct pfioc_states { - int ps_len; - union { - caddr_t psu_buf; - struct pf_state *psu_states; - } ps_u; -#define ps_buf ps_u.psu_buf -#define ps_states ps_u.psu_states -}; -.Ed -.Pp -If -.Va ps_len -is non-zero on entry, as many states as possible that can fit into this -size will be copied into the supplied buffer -.Va ps_states . -On exit, -.Va ps_len -is always set to the total size required to hold all state table entries -(i.e., it is set to -.Li sizeof(struct pf_state) * nr ) . -.It Dv DIOCCHANGERULE Fa "struct pfioc_rule *pcr" -Add or remove the -.Va rule -in the ruleset specified by -.Va rule.action . -.Pp -The type of operation to be performed is indicated by -.Va action , -which can be any of the following: -.Bd -literal -enum { PF_CHANGE_NONE, PF_CHANGE_ADD_HEAD, PF_CHANGE_ADD_TAIL, - PF_CHANGE_ADD_BEFORE, PF_CHANGE_ADD_AFTER, - PF_CHANGE_REMOVE, PF_CHANGE_GET_TICKET }; -.Ed -.Pp -.Va ticket -must be set to the value obtained with -.Dv PF_CHANGE_GET_TICKET -for all actions except -.Dv PF_CHANGE_GET_TICKET . -.Va pool_ticket -must be set to the value obtained with the -.Dv DIOCBEGINADDRS -call for all actions except -.Dv PF_CHANGE_REMOVE -and -.Dv PF_CHANGE_GET_TICKET . -.Va anchor -indicates to which anchor the operation applies. -.Va nr -indicates the rule number against which -.Dv PF_CHANGE_ADD_BEFORE , -.Dv PF_CHANGE_ADD_AFTER , -or -.Dv PF_CHANGE_REMOVE -actions are applied. -.\" It Dv DIOCCHANGEALTQ Fa "struct pfioc_altq *pcr" -.It Dv DIOCCHANGEADDR Fa "struct pfioc_pooladdr *pca" -Add or remove the pool address -.Va addr -from the rule specified by -.Va r_action , -.Va r_num , -and -.Va anchor . -.It Dv DIOCSETTIMEOUT Fa "struct pfioc_tm *pt" -.Bd -literal -struct pfioc_tm { - int timeout; - int seconds; -}; -.Ed -.Pp -Set the state timeout of -.Va timeout -to -.Va seconds . -The old value will be placed into -.Va seconds . -For possible values of -.Va timeout , -consult the -.Dv PFTM_* -values in -.Aq Pa net/pfvar.h . -.It Dv DIOCGETTIMEOUT Fa "struct pfioc_tm *pt" -Get the state timeout of -.Va timeout . -The value will be placed into the -.Va seconds -field. -.It Dv DIOCCLRRULECTRS -Clear per-rule statistics. -.It Dv DIOCSETLIMIT Fa "struct pfioc_limit *pl" -Set the hard limits on the memory pools used by the packet filter. -.Bd -literal -struct pfioc_limit { - int index; - unsigned limit; -}; - -enum { PF_LIMIT_STATES, PF_LIMIT_SRC_NODES, PF_LIMIT_FRAGS, - PF_LIMIT_TABLE_ENTRIES, PF_LIMIT_MAX }; -.Ed -.It Dv DIOCGETLIMIT Fa "struct pfioc_limit *pl" -Get the hard -.Va limit -for the memory pool indicated by -.Va index . -.It Dv DIOCRCLRTABLES Fa "struct pfioc_table *io" -Clear all tables. -All the ioctls that manipulate radix tables -use the same structure described below. -For -.Dv DIOCRCLRTABLES , -.Va pfrio_ndel -contains on exit the number of tables deleted. -.Bd -literal -struct pfioc_table { - struct pfr_table pfrio_table; - void *pfrio_buffer; - int pfrio_esize; - int pfrio_size; - int pfrio_size2; - int pfrio_nadd; - int pfrio_ndel; - int pfrio_nchange; - int pfrio_flags; - u_int32_t pfrio_ticket; -}; -#define pfrio_exists pfrio_nadd -#define pfrio_nzero pfrio_nadd -#define pfrio_nmatch pfrio_nadd -#define pfrio_naddr pfrio_size2 -#define pfrio_setflag pfrio_size2 -#define pfrio_clrflag pfrio_nadd -.Ed -.It Dv DIOCRADDTABLES Fa "struct pfioc_table *io" -Create one or more tables. -On entry, -.Va pfrio_buffer -must point to an array of -.Vt struct pfr_table -containing at least -.Vt pfrio_size -elements. -.Vt pfrio_esize -must be the size of -.Vt struct pfr_table . -On exit, -.Va pfrio_nadd -contains the number of tables effectively created. -.Bd -literal -struct pfr_table { - char pfrt_anchor[MAXPATHLEN]; - char pfrt_name[PF_TABLE_NAME_SIZE]; - u_int32_t pfrt_flags; - u_int8_t pfrt_fback; -}; -.Ed -.It Dv DIOCRDELTABLES Fa "struct pfioc_table *io" -Delete one or more tables. -On entry, -.Va pfrio_buffer -must point to an array of -.Vt struct pfr_table -containing at least -.Vt pfrio_size -elements. -.Vt pfrio_esize -must be the size of -.Vt struct pfr_table . -On exit, -.Va pfrio_ndel -contains the number of tables effectively deleted. -.It Dv DIOCRGETTABLES Fa "struct pfioc_table *io" -Get the list of all tables. -On entry, -.Va pfrio_buffer[pfrio_size] -contains a valid writeable buffer for -.Vt pfr_table -structures. -On exit, -.Va pfrio_size -contains the number of tables written into the buffer. -If the buffer is too small, the kernel does not store anything but just -returns the required buffer size, without error. -.It Dv DIOCRGETTSTATS Fa "struct pfioc_table *io" -This call is like -.Dv DIOCRGETTABLES -but is used to get an array of -.Vt pfr_tstats -structures. -.Bd -literal -struct pfr_tstats { - struct pfr_table pfrts_t; - u_int64_t pfrts_packets - [PFR_DIR_MAX][PFR_OP_TABLE_MAX]; - u_int64_t pfrts_bytes - [PFR_DIR_MAX][PFR_OP_TABLE_MAX]; - u_int64_t pfrts_match; - u_int64_t pfrts_nomatch; - long pfrts_tzero; - int pfrts_cnt; - int pfrts_refcnt[PFR_REFCNT_MAX]; -}; -#define pfrts_name pfrts_t.pfrt_name -#define pfrts_flags pfrts_t.pfrt_flags -.Ed -.It Dv DIOCRCLRTSTATS Fa "struct pfioc_table *io" -Clear the statistics of one or more tables. -On entry, -.Va pfrio_buffer -must point to an array of -.Vt struct pfr_table -containing at least -.Vt pfrio_size -elements. -.Vt pfrio_esize -must be the size of -.Vt struct pfr_table . -On exit, -.Va pfrio_nzero -contains the number of tables effectively cleared. -.It Dv DIOCRCLRADDRS Fa "struct pfioc_table *io" -Clear all addresses in a table. -On entry, -.Va pfrio_table -contains the table to clear. -On exit, -.Va pfrio_ndel -contains the number of addresses removed. -.It Dv DIOCRADDADDRS Fa "struct pfioc_table *io" -Add one or more addresses to a table. -On entry, -.Va pfrio_table -contains the table ID and -.Va pfrio_buffer -must point to an array of -.Vt struct pfr_addr -containing at least -.Vt pfrio_size -elements to add to the table. -.Vt pfrio_esize -must be the size of -.Vt struct pfr_addr . -On exit, -.Va pfrio_nadd -contains the number of addresses effectively added. -.Bd -literal -struct pfr_addr { - union { - struct in_addr _pfra_ip4addr; - struct in6_addr _pfra_ip6addr; - } pfra_u; - u_int8_t pfra_af; - u_int8_t pfra_net; - u_int8_t pfra_not; - u_int8_t pfra_fback; -}; -#define pfra_ip4addr pfra_u._pfra_ip4addr -#define pfra_ip6addr pfra_u._pfra_ip6addr -.Ed -.It Dv DIOCRDELADDRS Fa "struct pfioc_table *io" -Delete one or more addresses from a table. -On entry, -.Va pfrio_table -contains the table ID and -.Va pfrio_buffer -must point to an array of -.Vt struct pfr_addr -containing at least -.Vt pfrio_size -elements to delete from the table. -.Vt pfrio_esize -must be the size of -.Vt struct pfr_addr . -On exit, -.Va pfrio_ndel -contains the number of addresses effectively deleted. -.It Dv DIOCRSETADDRS Fa "struct pfioc_table *io" -Replace the content of a table by a new address list. -This is the most complicated command, which uses all the structure members. -.Pp -On entry, -.Va pfrio_table -contains the table ID and -.Va pfrio_buffer -must point to an array of -.Vt struct pfr_addr -containing at least -.Vt pfrio_size -elements which become the new contents of the table. -.Vt pfrio_esize -must be the size of -.Vt struct pfr_addr . -Additionally, if -.Va pfrio_size2 -is non-zero, -.Va pfrio_buffer[pfrio_size..pfrio_size2] -must be a writeable buffer, into which the kernel can copy the -addresses that have been deleted during the replace operation. -On exit, -.Va pfrio_ndel , -.Va pfrio_nadd , -and -.Va pfrio_nchange -contain the number of addresses deleted, added, and changed by the -kernel. -If -.Va pfrio_size2 -was set on entry, -.Va pfrio_size2 -will point to the size of the buffer used, exactly like -.Dv DIOCRGETADDRS . -.It Dv DIOCRGETADDRS Fa "struct pfioc_table *io" -Get all the addresses of a table. -On entry, -.Va pfrio_table -contains the table ID and -.Va pfrio_buffer[pfrio_size] -contains a valid writeable buffer for -.Vt pfr_addr -structures. -On exit, -.Va pfrio_size -contains the number of addresses written into the buffer. -If the buffer was too small, the kernel does not store anything but just -returns the required buffer size, without returning an error. -.It Dv DIOCRGETASTATS Fa "struct pfioc_table *io" -This call is like -.Dv DIOCRGETADDRS -but is used to get an array of -.Vt pfr_astats -structures. -.Bd -literal -struct pfr_astats { - struct pfr_addr pfras_a; - u_int64_t pfras_packets - [PFR_DIR_MAX][PFR_OP_ADDR_MAX]; - u_int64_t pfras_bytes - [PFR_DIR_MAX][PFR_OP_ADDR_MAX]; - long pfras_tzero; -}; -.Ed -.It Dv DIOCRCLRASTATS Fa "struct pfioc_table *io" -Clear the statistics of one or more addresses. -On entry, -.Va pfrio_table -contains the table ID and -.Va pfrio_buffer -must point to an array of -.Vt struct pfr_addr -containing at least -.Vt pfrio_size -elements to be cleared from the table. -.Vt pfrio_esize -must be the size of -.Vt struct pfr_addr . -On exit, -.Va pfrio_nzero -contains the number of addresses effectively cleared. -.It Dv DIOCRTSTADDRS Fa "struct pfioc_table *io" -Test if the given addresses match a table. -On entry, -.Va pfrio_table -contains the table ID and -.Va pfrio_buffer -must point to an array of -.Vt struct pfr_addr -containing at least -.Vt pfrio_size -elements, each of which will be tested for a match in the table. -.Vt pfrio_esize -must be the size of -.Vt struct pfr_addr . -On exit, the kernel updates the -.Vt pfr_addr -array by setting the -.Va pfra_fback -member appropriately. -.It Dv DIOCRSETTFLAGS Fa "struct pfioc_table *io" -Change the -.Dv PFR_TFLAG_CONST -or -.Dv PFR_TFLAG_PERSIST -flags of a table. -On entry, -.Va pfrio_buffer -must point to an array of -.Vt struct pfr_table -containing at least -.Vt pfrio_size -elements. -.Va pfrio_esize -must be the size of -.Vt struct pfr_table . -.Va pfrio_setflag -must contain the flags to add, while -.Va pfrio_clrflag -must contain the flags to remove. -On exit, -.Va pfrio_nchange -and -.Va pfrio_ndel -contain the number of tables altered or deleted by the kernel. -Yes, tables can be deleted if one removes the -.Dv PFR_TFLAG_PERSIST -flag of an unreferenced table. -.It Dv DIOCRINADEFINE Fa "struct pfioc_table *io" -Defines a table in the inactive set. -On entry, -.Va pfrio_table -contains the table ID and -.Va pfrio_buffer[pfrio_size] -contains an array of -.Vt pfr_addr -structures to put in the table. -A valid ticket must also be supplied to -.Va pfrio_ticket . -On exit, -.Va pfrio_nadd -contains 0 if the table was already defined in the inactive list -or 1 if a new table has been created. -.Va pfrio_naddr -contains the number of addresses effectively put in the table. -.It Dv DIOCXBEGIN Fa "struct pfioc_trans *io" -.Bd -literal -struct pfioc_trans { - int size; /* number of elements */ - int esize; /* size of each element in bytes */ - struct pfioc_trans_e { - int rs_num; - char anchor[MAXPATHLEN]; - u_int32_t ticket; - } *array; -}; -.Ed -.Pp -Clear all the inactive rulesets specified in the -.Vt pfioc_trans_e -array. -For each ruleset, a ticket is returned for subsequent "add rule" ioctls, -as well as for the -.Dv DIOCXCOMMIT -and -.Dv DIOCXROLLBACK -calls. -.Pp -Ruleset types, identified by -.Va rs_num , -include the following: -.Pp -.Bl -tag -width PF_RULESET_FILTER -offset ind -compact -.It Dv PF_RULESET_SCRUB -Scrub (packet normalization) rules. -.It Dv PF_RULESET_FILTER -Filter rules. -.It Dv PF_RULESET_NAT -NAT (Network Address Translation) rules. -.It Dv PF_RULESET_BINAT -Bidirectional NAT rules. -.It Dv PF_RULESET_RDR -Redirect rules. -.It Dv PF_RULESET_ALTQ -ALTQ disciplines. -.It Dv PF_RULESET_TABLE -Address tables. -.El -.It Dv DIOCXCOMMIT Fa "struct pfioc_trans *io" -Atomically switch a vector of inactive rulesets to the active rulesets. -This call is implemented as a standard two-phase commit, which will either -fail for all rulesets or completely succeed. -All tickets need to be valid. -This ioctl returns -.Er EBUSY -if another process is concurrently updating some of the same rulesets. -.It Dv DIOCXROLLBACK Fa "struct pfioc_trans *io" -Clean up the kernel by undoing all changes that have taken place on the -inactive rulesets since the last -.Dv DIOCXBEGIN . -.Dv DIOCXROLLBACK -will silently ignore rulesets for which the ticket is invalid. -.It Dv DIOCSETHOSTID Fa "u_int32_t *hostid" -Set the host ID, which is used by -.Xr pfsync 4 -to identify which host created state table entries. -.It Dv DIOCOSFPFLUSH -Flush the passive OS fingerprint table. -.It Dv DIOCOSFPADD Fa "struct pf_osfp_ioctl *io" -.Bd -literal -struct pf_osfp_ioctl { - struct pf_osfp_entry { - SLIST_ENTRY(pf_osfp_entry) fp_entry; - pf_osfp_t fp_os; - char fp_class_nm[PF_OSFP_LEN]; - char fp_version_nm[PF_OSFP_LEN]; - char fp_subtype_nm[PF_OSFP_LEN]; - } fp_os; - pf_tcpopts_t fp_tcpopts; - u_int16_t fp_wsize; - u_int16_t fp_psize; - u_int16_t fp_mss; - u_int16_t fp_flags; - u_int8_t fp_optcnt; - u_int8_t fp_wscale; - u_int8_t fp_ttl; - int fp_getnum; -}; -.Ed -.Pp -Add a passive OS fingerprint to the table. -Set -.Va fp_os.fp_os -to the packed fingerprint, -.Va fp_os.fp_class_nm -to the name of the class (Linux, Windows, etc), -.Va fp_os.fp_version_nm -to the name of the version (NT, 95, 98), and -.Va fp_os.fp_subtype_nm -to the name of the subtype or patchlevel. -The members -.Va fp_mss , -.Va fp_wsize , -.Va fp_psize , -.Va fp_ttl , -.Va fp_optcnt , -and -.Va fp_wscale -are set to the TCP MSS, the TCP window size, the IP length, the IP TTL, -the number of TCP options, and the TCP window scaling constant of the -TCP SYN packet, respectively. -.Pp -The -.Va fp_flags -member is filled according to the -.Aq Pa net/pfvar.h -include file -.Dv PF_OSFP_* -defines. -The -.Va fp_tcpopts -member contains packed TCP options. -Each option uses -.Dv PF_OSFP_TCPOPT_BITS -bits in the packed value. -Options include any of -.Dv PF_OSFP_TCPOPT_NOP , -.Dv PF_OSFP_TCPOPT_SACK , -.Dv PF_OSFP_TCPOPT_WSCALE , -.Dv PF_OSFP_TCPOPT_MSS , -or -.Dv PF_OSFP_TCPOPT_TS . -.Pp -The -.Va fp_getnum -member is not used with this ioctl. -.Pp -The structure's slack space must be zeroed for correct operation; -.Xr memset 3 -the whole structure to zero before filling and sending to the kernel. -.It Dv DIOCOSFPGET Fa "struct pf_osfp_ioctl *io" -Get the passive OS fingerprint number -.Va fp_getnum -from the kernel's fingerprint list. -The rest of the structure members will come back filled. -Get the whole list by repeatedly incrementing the -.Va fp_getnum -number until the ioctl returns -.Er EBUSY . -.It Dv DIOCGETSRCNODES Fa "struct pfioc_src_nodes *psn" -.Bd -literal -struct pfioc_src_nodes { - int psn_len; - union { - caddr_t psu_buf; - struct pf_src_node *psu_src_nodes; - } psn_u; -#define psn_buf psn_u.psu_buf -#define psn_src_nodes psn_u.psu_src_nodes -}; -.Ed -.Pp -Get the list of source nodes kept by sticky addresses and source -tracking. -The ioctl must be called once with -.Va psn_len -set to 0. -If the ioctl returns without error, -.Va psn_len -will be set to the size of the buffer required to hold all the -.Va pf_src_node -structures held in the table. -A buffer of this size should then be allocated, and a pointer to this buffer -placed in -.Va psn_buf . -The ioctl must then be called again to fill this buffer with the actual -source node data. -After that call, -.Va psn_len -will be set to the length of the buffer actually used. -.It Dv DIOCCLRSRCNODES -Clear the tree of source tracking nodes. -.It Dv DIOCIGETIFACES Fa "struct pfioc_iface *io" -Get the list of interfaces and interface drivers known to -.Nm . -All the ioctls that manipulate interfaces -use the same structure described below: -.Bd -literal -struct pfioc_iface { - char pfiio_name[IFNAMSIZ]; - void *pfiio_buffer; - int pfiio_esize; - int pfiio_size; - int pfiio_nzero; - int pfiio_flags; -}; -.Ed -.Pp -If not empty, -.Va pfiio_name -can be used to restrict the search to a specific interface or driver. -.Va pfiio_buffer[pfiio_size] -is the user-supplied buffer for returning the data. -On entry, -.Va pfiio_size -contains the number of -.Vt pfi_kif -entries that can fit into the buffer. -The kernel will replace this value by the real number of entries it wants -to return. -.Va pfiio_esize -should be set to -.Li sizeof(struct pfi_kif) . -.Pp -The data is returned in the -.Vt pfi_kif -structure described below: -.Bd -literal -struct pfi_kif { - RB_ENTRY(pfi_kif) pfik_tree; - char pfik_name[IFNAMSIZ]; - u_int64_t pfik_packets[2][2][2]; - u_int64_t pfik_bytes[2][2][2]; - u_int32_t pfik_tzero; - int pfik_flags; - struct pf_state_tree_lan_ext pfik_lan_ext; - struct pf_state_tree_ext_gwy pfik_ext_gwy; - TAILQ_ENTRY(pfi_kif) pfik_w_states; - void *pfik_ah_cookie; - struct ifnet *pfik_ifp; - struct ifg_group *pfik_group; - int pfik_states; - int pfik_rules; - TAILQ_HEAD(, pfi_dynaddr) pfik_dynaddrs; -}; -.Ed -.It Dv DIOCSETIFFLAG Fa "struct pfioc_iface *io" -Set the user setable flags (described above) of the -.Nm -internal interface description. -The filtering process is the same as for -.Dv DIOCIGETIFACES . -.Bd -literal -#define PFI_IFLAG_SKIP 0x0100 /* skip filtering on interface */ -.Ed -.It Dv DIOCCLRIFFLAG Fa "struct pfioc_iface *io" -Works as -.Dv DIOCSETIFFLAG -above but clears the flags. -.It Dv DIOCKILLSRCNODES Fa "struct pfioc_iface *io" -Explicitly remove source tracking nodes. -.El -.Sh FILES -.Bl -tag -width /dev/pf -compact -.It Pa /dev/pf -packet filtering device. -.El -.Sh EXAMPLES -The following example demonstrates how to use the -.Dv DIOCNATLOOK -command to find the internal host/port of a NATed connection: -.Bd -literal -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/ioctl.h> -#include <sys/fcntl.h> -#include <net/if.h> -#include <netinet/in.h> -#include <net/pfvar.h> -#include <err.h> -#include <stdio.h> -#include <stdlib.h> - -u_int32_t -read_address(const char *s) -{ - int a, b, c, d; - - sscanf(s, "%i.%i.%i.%i", &a, &b, &c, &d); - return htonl(a << 24 | b << 16 | c << 8 | d); -} - -void -print_address(u_int32_t a) -{ - a = ntohl(a); - printf("%d.%d.%d.%d", a >> 24 & 255, a >> 16 & 255, - a >> 8 & 255, a & 255); -} - -int -main(int argc, char *argv[]) -{ - struct pfioc_natlook nl; - int dev; - - if (argc != 5) { - printf("%s <gwy addr> <gwy port> <ext addr> <ext port>\\n", - argv[0]); - return 1; - } - - dev = open("/dev/pf", O_RDWR); - if (dev == -1) - err(1, "open(\\"/dev/pf\\") failed"); - - memset(&nl, 0, sizeof(struct pfioc_natlook)); - nl.saddr.v4.s_addr = read_address(argv[1]); - nl.sport = htons(atoi(argv[2])); - nl.daddr.v4.s_addr = read_address(argv[3]); - nl.dport = htons(atoi(argv[4])); - nl.af = AF_INET; - nl.proto = IPPROTO_TCP; - nl.direction = PF_IN; - - if (ioctl(dev, DIOCNATLOOK, &nl)) - err(1, "DIOCNATLOOK"); - - printf("internal host "); - print_address(nl.rsaddr.v4.s_addr); - printf(":%u\\n", ntohs(nl.rsport)); - return 0; -} -.Ed -.Sh SEE ALSO -.Xr ioctl 2 , -.Xr altq 4 , -.Xr if_bridge 4 , -.Xr pflog 4 , -.Xr pflow 4 , -.Xr pfsync 4 , -.Xr pfctl 8 , -.Xr altq 9 -.Sh HISTORY -The -.Nm -packet filtering mechanism first appeared in -.Ox 3.0 -and then -.Fx 5.2 . -.Pp -This implementation matches -.Ox 4.5 . diff --git a/contrib/pf/man/pf.conf.5 b/contrib/pf/man/pf.conf.5 deleted file mode 100644 index fc86111..0000000 --- a/contrib/pf/man/pf.conf.5 +++ /dev/null @@ -1,3066 +0,0 @@ -.\" $FreeBSD$ -.\" $OpenBSD: pf.conf.5,v 1.406 2009/01/31 19:37:12 sobrado Exp $ -.\" -.\" Copyright (c) 2002, Daniel Hartmeier -.\" All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" -.\" - 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. -.\" -.Dd June 29 2012 -.Dt PF.CONF 5 -.Os -.Sh NAME -.Nm pf.conf -.Nd packet filter configuration file -.Sh DESCRIPTION -The -.Xr pf 4 -packet filter modifies, drops or passes packets according to rules or -definitions specified in -.Nm pf.conf . -.Sh STATEMENT ORDER -There are seven types of statements in -.Nm pf.conf : -.Bl -tag -width xxxx -.It Cm Macros -User-defined variables may be defined and used later, simplifying -the configuration file. -Macros must be defined before they are referenced in -.Nm pf.conf . -.It Cm Tables -Tables provide a mechanism for increasing the performance and flexibility of -rules with large numbers of source or destination addresses. -.It Cm Options -Options tune the behaviour of the packet filtering engine. -.It Cm Traffic Normalization Li (e.g. Em scrub ) -Traffic normalization protects internal machines against inconsistencies -in Internet protocols and implementations. -.It Cm Queueing -Queueing provides rule-based bandwidth control. -.It Cm Translation Li (Various forms of NAT) -Translation rules specify how addresses are to be mapped or redirected to -other addresses. -.It Cm Packet Filtering -Packet filtering provides rule-based blocking or passing of packets. -.El -.Pp -With the exception of -.Cm macros -and -.Cm tables , -the types of statements should be grouped and appear in -.Nm pf.conf -in the order shown above, as this matches the operation of the underlying -packet filtering engine. -By default -.Xr pfctl 8 -enforces this order (see -.Ar set require-order -below). -.Pp -Comments can be put anywhere in the file using a hash mark -.Pq Sq # , -and extend to the end of the current line. -.Pp -Additional configuration files can be included with the -.Ic include -keyword, for example: -.Bd -literal -offset indent -include "/etc/pf/sub.filter.conf" -.Ed -.Sh MACROS -Macros can be defined that will later be expanded in context. -Macro names must start with a letter, and may contain letters, digits -and underscores. -Macro names may not be reserved words (for example -.Ar pass , -.Ar in , -.Ar out ) . -Macros are not expanded inside quotes. -.Pp -For example, -.Bd -literal -offset indent -ext_if = \&"kue0\&" -all_ifs = \&"{\&" $ext_if lo0 \&"}\&" -pass out on $ext_if from any to any -pass in on $ext_if proto tcp from any to any port 25 -.Ed -.Sh TABLES -Tables are named structures which can hold a collection of addresses and -networks. -Lookups against tables in -.Xr pf 4 -are relatively fast, making a single rule with tables much more efficient, -in terms of -processor usage and memory consumption, than a large number of rules which -differ only in IP address (either created explicitly or automatically by rule -expansion). -.Pp -Tables can be used as the source or destination of filter rules, -.Ar scrub -rules -or -translation rules such as -.Ar nat -or -.Ar rdr -(see below for details on the various rule types). -Tables can also be used for the redirect address of -.Ar nat -and -.Ar rdr -rules and in the routing options of filter rules, but only for -.Ar round-robin -pools. -.Pp -Tables can be defined with any of the following -.Xr pfctl 8 -mechanisms. -As with macros, reserved words may not be used as table names. -.Bl -tag -width "manually" -.It Ar manually -Persistent tables can be manually created with the -.Ar add -or -.Ar replace -option of -.Xr pfctl 8 , -before or after the ruleset has been loaded. -.It Pa pf.conf -Table definitions can be placed directly in this file, and loaded at the -same time as other rules are loaded, atomically. -Table definitions inside -.Nm pf.conf -use the -.Ar table -statement, and are especially useful to define non-persistent tables. -The contents of a pre-existing table defined without a list of addresses -to initialize it is not altered when -.Nm pf.conf -is loaded. -A table initialized with the empty list, -.Li { } , -will be cleared on load. -.El -.Pp -Tables may be defined with the following attributes: -.Bl -tag -width persist -.It Ar persist -The -.Ar persist -flag forces the kernel to keep the table even when no rules refer to it. -If the flag is not set, the kernel will automatically remove the table -when the last rule referring to it is flushed. -.It Ar const -The -.Ar const -flag prevents the user from altering the contents of the table once it -has been created. -Without that flag, -.Xr pfctl 8 -can be used to add or remove addresses from the table at any time, even -when running with -.Xr securelevel 7 -= 2. -.It Ar counters -The -.Ar counters -flag enables per-address packet and byte counters which can be displayed with -.Xr pfctl 8 . -.El -.Pp -For example, -.Bd -literal -offset indent -table \*(Ltprivate\*(Gt const { 10/8, 172.16/12, 192.168/16 } -table \*(Ltbadhosts\*(Gt persist -block on fxp0 from { \*(Ltprivate\*(Gt, \*(Ltbadhosts\*(Gt } to any -.Ed -.Pp -creates a table called private, to hold RFC 1918 private network -blocks, and a table called badhosts, which is initially empty. -A filter rule is set up to block all traffic coming from addresses listed in -either table. -The private table cannot have its contents changed and the badhosts table -will exist even when no active filter rules reference it. -Addresses may later be added to the badhosts table, so that traffic from -these hosts can be blocked by using -.Bd -literal -offset indent -# pfctl -t badhosts -Tadd 204.92.77.111 -.Ed -.Pp -A table can also be initialized with an address list specified in one or more -external files, using the following syntax: -.Bd -literal -offset indent -table \*(Ltspam\*(Gt persist file \&"/etc/spammers\&" file \&"/etc/openrelays\&" -block on fxp0 from \*(Ltspam\*(Gt to any -.Ed -.Pp -The files -.Pa /etc/spammers -and -.Pa /etc/openrelays -list IP addresses, one per line. -Any lines beginning with a # are treated as comments and ignored. -In addition to being specified by IP address, hosts may also be -specified by their hostname. -When the resolver is called to add a hostname to a table, -.Em all -resulting IPv4 and IPv6 addresses are placed into the table. -IP addresses can also be entered in a table by specifying a valid interface -name, a valid interface group or the -.Em self -keyword, in which case all addresses assigned to the interface(s) will be -added to the table. -.Sh OPTIONS -.Xr pf 4 -may be tuned for various situations using the -.Ar set -command. -.Bl -tag -width xxxx -.It Ar set timeout -.Pp -.Bl -tag -width "src.track" -compact -.It Ar interval -Interval between purging expired states and fragments. -.It Ar frag -Seconds before an unassembled fragment is expired. -.It Ar src.track -Length of time to retain a source tracking entry after the last state -expires. -.El -.Pp -When a packet matches a stateful connection, the seconds to live for the -connection will be updated to that of the -.Ar proto.modifier -which corresponds to the connection state. -Each packet which matches this state will reset the TTL. -Tuning these values may improve the performance of the -firewall at the risk of dropping valid idle connections. -.Pp -.Bl -tag -width xxxx -compact -.It Ar tcp.first -The state after the first packet. -.It Ar tcp.opening -The state before the destination host ever sends a packet. -.It Ar tcp.established -The fully established state. -.It Ar tcp.closing -The state after the first FIN has been sent. -.It Ar tcp.finwait -The state after both FINs have been exchanged and the connection is closed. -Some hosts (notably web servers on Solaris) send TCP packets even after closing -the connection. -Increasing -.Ar tcp.finwait -(and possibly -.Ar tcp.closing ) -can prevent blocking of such packets. -.It Ar tcp.closed -The state after one endpoint sends an RST. -.El -.Pp -ICMP and UDP are handled in a fashion similar to TCP, but with a much more -limited set of states: -.Pp -.Bl -tag -width xxxx -compact -.It Ar udp.first -The state after the first packet. -.It Ar udp.single -The state if the source host sends more than one packet but the destination -host has never sent one back. -.It Ar udp.multiple -The state if both hosts have sent packets. -.It Ar icmp.first -The state after the first packet. -.It Ar icmp.error -The state after an ICMP error came back in response to an ICMP packet. -.El -.Pp -Other protocols are handled similarly to UDP: -.Pp -.Bl -tag -width xxxx -compact -.It Ar other.first -.It Ar other.single -.It Ar other.multiple -.El -.Pp -Timeout values can be reduced adaptively as the number of state table -entries grows. -.Pp -.Bl -tag -width xxxx -compact -.It Ar adaptive.start -When the number of state entries exceeds this value, adaptive scaling -begins. -All timeout values are scaled linearly with factor -(adaptive.end - number of states) / (adaptive.end - adaptive.start). -.It Ar adaptive.end -When reaching this number of state entries, all timeout values become -zero, effectively purging all state entries immediately. -This value is used to define the scale factor, it should not actually -be reached (set a lower state limit, see below). -.El -.Pp -Adaptive timeouts are enabled by default, with an adaptive.start value -equal to 60% of the state limit, and an adaptive.end value equal to -120% of the state limit. -They can be disabled by setting both adaptive.start and adaptive.end to 0. -.Pp -The adaptive timeout values can be defined both globally and for each rule. -When used on a per-rule basis, the values relate to the number of -states created by the rule, otherwise to the total number of -states. -.Pp -For example: -.Bd -literal -offset indent -set timeout tcp.first 120 -set timeout tcp.established 86400 -set timeout { adaptive.start 6000, adaptive.end 12000 } -set limit states 10000 -.Ed -.Pp -With 9000 state table entries, the timeout values are scaled to 50% -(tcp.first 60, tcp.established 43200). -.Pp -.It Ar set loginterface -Enable collection of packet and byte count statistics for the given -interface or interface group. -These statistics can be viewed using -.Bd -literal -offset indent -# pfctl -s info -.Ed -.Pp -In this example -.Xr pf 4 -collects statistics on the interface named dc0: -.Bd -literal -offset indent -set loginterface dc0 -.Ed -.Pp -One can disable the loginterface using: -.Bd -literal -offset indent -set loginterface none -.Ed -.Pp -.It Ar set limit -Sets hard limits on the memory pools used by the packet filter. -See -.Xr zone 9 -for an explanation of memory pools. -.Pp -For example, -.Bd -literal -offset indent -set limit states 20000 -.Ed -.Pp -sets the maximum number of entries in the memory pool used by state table -entries (generated by -.Ar pass -rules which do not specify -.Ar no state ) -to 20000. -Using -.Bd -literal -offset indent -set limit frags 20000 -.Ed -.Pp -sets the maximum number of entries in the memory pool used for fragment -reassembly (generated by -.Ar scrub -rules) to 20000. -Using -.Bd -literal -offset indent -set limit src-nodes 2000 -.Ed -.Pp -sets the maximum number of entries in the memory pool used for tracking -source IP addresses (generated by the -.Ar sticky-address -and -.Ar src.track -options) to 2000. -Using -.Bd -literal -offset indent -set limit tables 1000 -set limit table-entries 100000 -.Ed -.Pp -sets limits on the memory pools used by tables. -The first limits the number of tables that can exist to 1000. -The second limits the overall number of addresses that can be stored -in tables to 100000. -.Pp -Various limits can be combined on a single line: -.Bd -literal -offset indent -set limit { states 20000, frags 20000, src-nodes 2000 } -.Ed -.Pp -.It Ar set ruleset-optimization -.Bl -tag -width xxxxxxxx -compact -.It Ar none -Disable the ruleset optimizer. -.It Ar basic -Enable basic ruleset optimization. -This is the default behaviour. -Basic ruleset optimization does four things to improve the -performance of ruleset evaluations: -.Pp -.Bl -enum -compact -.It -remove duplicate rules -.It -remove rules that are a subset of another rule -.It -combine multiple rules into a table when advantageous -.It -re-order the rules to improve evaluation performance -.El -.Pp -.It Ar profile -Uses the currently loaded ruleset as a feedback profile to tailor the -ordering of quick rules to actual network traffic. -.El -.Pp -It is important to note that the ruleset optimizer will modify the ruleset -to improve performance. -A side effect of the ruleset modification is that per-rule accounting -statistics will have different meanings than before. -If per-rule accounting is important for billing purposes or whatnot, -either the ruleset optimizer should not be used or a label field should -be added to all of the accounting rules to act as optimization barriers. -.Pp -Optimization can also be set as a command-line argument to -.Xr pfctl 8 , -overriding the settings in -.Nm . -.It Ar set optimization -Optimize state timeouts for one of the following network environments: -.Pp -.Bl -tag -width xxxx -compact -.It Ar normal -A normal network environment. -Suitable for almost all networks. -.It Ar high-latency -A high-latency environment (such as a satellite connection). -.It Ar satellite -Alias for -.Ar high-latency . -.It Ar aggressive -Aggressively expire connections. -This can greatly reduce the memory usage of the firewall at the cost of -dropping idle connections early. -.It Ar conservative -Extremely conservative settings. -Avoid dropping legitimate connections at the -expense of greater memory utilization (possibly much greater on a busy -network) and slightly increased processor utilization. -.El -.Pp -For example: -.Bd -literal -offset indent -set optimization aggressive -.Ed -.Pp -.It Ar set block-policy -The -.Ar block-policy -option sets the default behaviour for the packet -.Ar block -action: -.Pp -.Bl -tag -width xxxxxxxx -compact -.It Ar drop -Packet is silently dropped. -.It Ar return -A TCP RST is returned for blocked TCP packets, -an ICMP UNREACHABLE is returned for blocked UDP packets, -and all other packets are silently dropped. -.El -.Pp -For example: -.Bd -literal -offset indent -set block-policy return -.Ed -.It Ar set state-policy -The -.Ar state-policy -option sets the default behaviour for states: -.Pp -.Bl -tag -width group-bound -compact -.It Ar if-bound -States are bound to interface. -.It Ar floating -States can match packets on any interfaces (the default). -.El -.Pp -For example: -.Bd -literal -offset indent -set state-policy if-bound -.Ed -.It Ar set state-defaults -The -.Ar state-defaults -option sets the state options for states created from rules -without an explicit -.Ar keep state . -For example: -.Bd -literal -offset indent -set state-defaults pflow, no-sync -.Ed -.It Ar set hostid -The 32-bit -.Ar hostid -identifies this firewall's state table entries to other firewalls -in a -.Xr pfsync 4 -failover cluster. -By default the hostid is set to a pseudo-random value, however it may be -desirable to manually configure it, for example to more easily identify the -source of state table entries. -.Bd -literal -offset indent -set hostid 1 -.Ed -.Pp -The hostid may be specified in either decimal or hexadecimal. -.It Ar set require-order -By default -.Xr pfctl 8 -enforces an ordering of the statement types in the ruleset to: -.Em options , -.Em normalization , -.Em queueing , -.Em translation , -.Em filtering . -Setting this option to -.Ar no -disables this enforcement. -There may be non-trivial and non-obvious implications to an out of -order ruleset. -Consider carefully before disabling the order enforcement. -.It Ar set fingerprints -Load fingerprints of known operating systems from the given filename. -By default fingerprints of known operating systems are automatically -loaded from -.Xr pf.os 5 -in -.Pa /etc -but can be overridden via this option. -Setting this option may leave a small period of time where the fingerprints -referenced by the currently active ruleset are inconsistent until the new -ruleset finishes loading. -.Pp -For example: -.Pp -.Dl set fingerprints \&"/etc/pf.os.devel\&" -.Pp -.It Ar set skip on Aq Ar ifspec -List interfaces for which packets should not be filtered. -Packets passing in or out on such interfaces are passed as if pf was -disabled, i.e. pf does not process them in any way. -This can be useful on loopback and other virtual interfaces, when -packet filtering is not desired and can have unexpected effects. -For example: -.Pp -.Dl set skip on lo0 -.Pp -.It Ar set debug -Set the debug -.Ar level -to one of the following: -.Pp -.Bl -tag -width xxxxxxxxxxxx -compact -.It Ar none -Don't generate debug messages. -.It Ar urgent -Generate debug messages only for serious errors. -.It Ar misc -Generate debug messages for various errors. -.It Ar loud -Generate debug messages for common conditions. -.El -.El -.Sh TRAFFIC NORMALIZATION -Traffic normalization is used to sanitize packet content in such -a way that there are no ambiguities in packet interpretation on -the receiving side. -The normalizer does IP fragment reassembly to prevent attacks -that confuse intrusion detection systems by sending overlapping -IP fragments. -Packet normalization is invoked with the -.Ar scrub -directive. -.Pp -.Ar scrub -has the following options: -.Bl -tag -width xxxx -.It Ar no-df -Clears the -.Ar dont-fragment -bit from a matching IP packet. -Some operating systems are known to generate fragmented packets with the -.Ar dont-fragment -bit set. -This is particularly true with NFS. -.Ar Scrub -will drop such fragmented -.Ar dont-fragment -packets unless -.Ar no-df -is specified. -.Pp -Unfortunately some operating systems also generate their -.Ar dont-fragment -packets with a zero IP identification field. -Clearing the -.Ar dont-fragment -bit on packets with a zero IP ID may cause deleterious results if an -upstream router later fragments the packet. -Using the -.Ar random-id -modifier (see below) is recommended in combination with the -.Ar no-df -modifier to ensure unique IP identifiers. -.It Ar min-ttl Aq Ar number -Enforces a minimum TTL for matching IP packets. -.It Ar max-mss Aq Ar number -Enforces a maximum MSS for matching TCP packets. -.It Xo Ar set-tos Aq Ar string -.No \*(Ba Aq Ar number -.Xc -Enforces a -.Em TOS -for matching IP packets. -.Em TOS -may be -given as one of -.Ar lowdelay , -.Ar throughput , -.Ar reliability , -or as either hex or decimal. -.It Ar random-id -Replaces the IP identification field with random values to compensate -for predictable values generated by many hosts. -This option only applies to packets that are not fragmented -after the optional fragment reassembly. -.It Ar fragment reassemble -Using -.Ar scrub -rules, fragments can be reassembled by normalization. -In this case, fragments are buffered until they form a complete -packet, and only the completed packet is passed on to the filter. -The advantage is that filter rules have to deal only with complete -packets, and can ignore fragments. -The drawback of caching fragments is the additional memory cost. -But the full reassembly method is the only method that currently works -with NAT. -This is the default behavior of a -.Ar scrub -rule if no fragmentation modifier is supplied. -.It Ar fragment crop -The default fragment reassembly method is expensive, hence the option -to crop is provided. -In this case, -.Xr pf 4 -will track the fragments and cache a small range descriptor. -Duplicate fragments are dropped and overlaps are cropped. -Thus data will only occur once on the wire with ambiguities resolving to -the first occurrence. -Unlike the -.Ar fragment reassemble -modifier, fragments are not buffered, they are passed as soon as they -are received. -The -.Ar fragment crop -reassembly mechanism does not yet work with NAT. -.Pp -.It Ar fragment drop-ovl -This option is similar to the -.Ar fragment crop -modifier except that all overlapping or duplicate fragments will be -dropped, and all further corresponding fragments will be -dropped as well. -.It Ar reassemble tcp -Statefully normalizes TCP connections. -.Ar scrub reassemble tcp -rules may not have the direction (in/out) specified. -.Ar reassemble tcp -performs the following normalizations: -.Pp -.Bl -tag -width timeout -compact -.It ttl -Neither side of the connection is allowed to reduce their IP TTL. -An attacker may send a packet such that it reaches the firewall, affects -the firewall state, and expires before reaching the destination host. -.Ar reassemble tcp -will raise the TTL of all packets back up to the highest value seen on -the connection. -.It timestamp modulation -Modern TCP stacks will send a timestamp on every TCP packet and echo -the other endpoint's timestamp back to them. -Many operating systems will merely start the timestamp at zero when -first booted, and increment it several times a second. -The uptime of the host can be deduced by reading the timestamp and multiplying -by a constant. -Also observing several different timestamps can be used to count hosts -behind a NAT device. -And spoofing TCP packets into a connection requires knowing or guessing -valid timestamps. -Timestamps merely need to be monotonically increasing and not derived off a -guessable base time. -.Ar reassemble tcp -will cause -.Ar scrub -to modulate the TCP timestamps with a random number. -.It extended PAWS checks -There is a problem with TCP on long fat pipes, in that a packet might get -delayed for longer than it takes the connection to wrap its 32-bit sequence -space. -In such an occurrence, the old packet would be indistinguishable from a -new packet and would be accepted as such. -The solution to this is called PAWS: Protection Against Wrapped Sequence -numbers. -It protects against it by making sure the timestamp on each packet does -not go backwards. -.Ar reassemble tcp -also makes sure the timestamp on the packet does not go forward more -than the RFC allows. -By doing this, -.Xr pf 4 -artificially extends the security of TCP sequence numbers by 10 to 18 -bits when the host uses appropriately randomized timestamps, since a -blind attacker would have to guess the timestamp as well. -.El -.El -.Pp -For example, -.Bd -literal -offset indent -scrub in on $ext_if all fragment reassemble -.Ed -.Pp -The -.Ar no -option prefixed to a scrub rule causes matching packets to remain unscrubbed, -much in the same way as -.Ar drop quick -works in the packet filter (see below). -This mechanism should be used when it is necessary to exclude specific packets -from broader scrub rules. -.Sh QUEUEING -The ALTQ system is currently not available in the GENERIC kernel nor as -loadable modules. -In order to use the herein after called queueing options one has to use a -custom built kernel. -Please refer to -.Xr altq 4 -to learn about the related kernel options. -.Pp -Packets can be assigned to queues for the purpose of bandwidth -control. -At least two declarations are required to configure queues, and later -any packet filtering rule can reference the defined queues by name. -During the filtering component of -.Nm pf.conf , -the last referenced -.Ar queue -name is where any packets from -.Ar pass -rules will be queued, while for -.Ar block -rules it specifies where any resulting ICMP or TCP RST -packets should be queued. -The -.Ar scheduler -defines the algorithm used to decide which packets get delayed, dropped, or -sent out immediately. -There are three -.Ar schedulers -currently supported. -.Bl -tag -width xxxx -.It Ar cbq -Class Based Queueing. -.Ar Queues -attached to an interface build a tree, thus each -.Ar queue -can have further child -.Ar queues . -Each queue can have a -.Ar priority -and a -.Ar bandwidth -assigned. -.Ar Priority -mainly controls the time packets take to get sent out, while -.Ar bandwidth -has primarily effects on throughput. -.Ar cbq -achieves both partitioning and sharing of link bandwidth -by hierarchically structured classes. -Each class has its own -.Ar queue -and is assigned its share of -.Ar bandwidth . -A child class can borrow bandwidth from its parent class -as long as excess bandwidth is available -(see the option -.Ar borrow , -below). -.It Ar priq -Priority Queueing. -.Ar Queues -are flat attached to the interface, thus, -.Ar queues -cannot have further child -.Ar queues . -Each -.Ar queue -has a unique -.Ar priority -assigned, ranging from 0 to 15. -Packets in the -.Ar queue -with the highest -.Ar priority -are processed first. -.It Ar hfsc -Hierarchical Fair Service Curve. -.Ar Queues -attached to an interface build a tree, thus each -.Ar queue -can have further child -.Ar queues . -Each queue can have a -.Ar priority -and a -.Ar bandwidth -assigned. -.Ar Priority -mainly controls the time packets take to get sent out, while -.Ar bandwidth -primarily affects throughput. -.Ar hfsc -supports both link-sharing and guaranteed real-time services. -It employs a service curve based QoS model, -and its unique feature is an ability to decouple -.Ar delay -and -.Ar bandwidth -allocation. -.El -.Pp -The interfaces on which queueing should be activated are declared using -the -.Ar altq on -declaration. -.Ar altq on -has the following keywords: -.Bl -tag -width xxxx -.It Aq Ar interface -Queueing is enabled on the named interface. -.It Aq Ar scheduler -Specifies which queueing scheduler to use. -Currently supported values -are -.Ar cbq -for Class Based Queueing, -.Ar priq -for Priority Queueing and -.Ar hfsc -for the Hierarchical Fair Service Curve scheduler. -.It Ar bandwidth Aq Ar bw -The maximum bitrate for all queues on an -interface may be specified using the -.Ar bandwidth -keyword. -The value can be specified as an absolute value or as a -percentage of the interface bandwidth. -When using an absolute value, the suffixes -.Ar b , -.Ar Kb , -.Ar Mb , -and -.Ar Gb -are used to represent bits, kilobits, megabits, and -gigabits per second, respectively. -The value must not exceed the interface bandwidth. -If -.Ar bandwidth -is not specified, the interface bandwidth is used -(but take note that some interfaces do not know their bandwidth, -or can adapt their bandwidth rates). -.It Ar qlimit Aq Ar limit -The maximum number of packets held in the queue. -The default is 50. -.It Ar tbrsize Aq Ar size -Adjusts the size, in bytes, of the token bucket regulator. -If not specified, heuristics based on the -interface bandwidth are used to determine the size. -.It Ar queue Aq Ar list -Defines a list of subqueues to create on an interface. -.El -.Pp -In the following example, the interface dc0 -should queue up to 5Mbps in four second-level queues using -Class Based Queueing. -Those four queues will be shown in a later example. -.Bd -literal -offset indent -altq on dc0 cbq bandwidth 5Mb queue { std, http, mail, ssh } -.Ed -.Pp -Once interfaces are activated for queueing using the -.Ar altq -directive, a sequence of -.Ar queue -directives may be defined. -The name associated with a -.Ar queue -must match a queue defined in the -.Ar altq -directive (e.g. mail), or, except for the -.Ar priq -.Ar scheduler , -in a parent -.Ar queue -declaration. -The following keywords can be used: -.Bl -tag -width xxxx -.It Ar on Aq Ar interface -Specifies the interface the queue operates on. -If not given, it operates on all matching interfaces. -.It Ar bandwidth Aq Ar bw -Specifies the maximum bitrate to be processed by the queue. -This value must not exceed the value of the parent -.Ar queue -and can be specified as an absolute value or a percentage of the parent -queue's bandwidth. -If not specified, defaults to 100% of the parent queue's bandwidth. -The -.Ar priq -scheduler does not support bandwidth specification. -.It Ar priority Aq Ar level -Between queues a priority level can be set. -For -.Ar cbq -and -.Ar hfsc , -the range is 0 to 7 and for -.Ar priq , -the range is 0 to 15. -The default for all is 1. -.Ar Priq -queues with a higher priority are always served first. -.Ar Cbq -and -.Ar Hfsc -queues with a higher priority are preferred in the case of overload. -.It Ar qlimit Aq Ar limit -The maximum number of packets held in the queue. -The default is 50. -.El -.Pp -The -.Ar scheduler -can get additional parameters with -.Xo Aq Ar scheduler -.Pf ( Aq Ar parameters ) . -.Xc -Parameters are as follows: -.Bl -tag -width Fl -.It Ar default -Packets not matched by another queue are assigned to this one. -Exactly one default queue is required. -.It Ar red -Enable RED (Random Early Detection) on this queue. -RED drops packets with a probability proportional to the average -queue length. -.It Ar rio -Enables RIO on this queue. -RIO is RED with IN/OUT, thus running -RED two times more than RIO would achieve the same effect. -RIO is currently not supported in the GENERIC kernel. -.It Ar ecn -Enables ECN (Explicit Congestion Notification) on this queue. -ECN implies RED. -.El -.Pp -The -.Ar cbq -.Ar scheduler -supports an additional option: -.Bl -tag -width Fl -.It Ar borrow -The queue can borrow bandwidth from the parent. -.El -.Pp -The -.Ar hfsc -.Ar scheduler -supports some additional options: -.Bl -tag -width Fl -.It Ar realtime Aq Ar sc -The minimum required bandwidth for the queue. -.It Ar upperlimit Aq Ar sc -The maximum allowed bandwidth for the queue. -.It Ar linkshare Aq Ar sc -The bandwidth share of a backlogged queue. -.El -.Pp -.Aq Ar sc -is an acronym for -.Ar service curve . -.Pp -The format for service curve specifications is -.Ar ( m1 , d , m2 ) . -.Ar m2 -controls the bandwidth assigned to the queue. -.Ar m1 -and -.Ar d -are optional and can be used to control the initial bandwidth assignment. -For the first -.Ar d -milliseconds the queue gets the bandwidth given as -.Ar m1 , -afterwards the value given in -.Ar m2 . -.Pp -Furthermore, with -.Ar cbq -and -.Ar hfsc , -child queues can be specified as in an -.Ar altq -declaration, thus building a tree of queues using a part of -their parent's bandwidth. -.Pp -Packets can be assigned to queues based on filter rules by using the -.Ar queue -keyword. -Normally only one -.Ar queue -is specified; when a second one is specified it will instead be used for -packets which have a -.Em TOS -of -.Em lowdelay -and for TCP ACKs with no data payload. -.Pp -To continue the previous example, the examples below would specify the -four referenced -queues, plus a few child queues. -Interactive -.Xr ssh 1 -sessions get priority over bulk transfers like -.Xr scp 1 -and -.Xr sftp 1 . -The queues may then be referenced by filtering rules (see -.Sx PACKET FILTERING -below). -.Bd -literal -queue std bandwidth 10% cbq(default) -queue http bandwidth 60% priority 2 cbq(borrow red) \e - { employees, developers } -queue developers bandwidth 75% cbq(borrow) -queue employees bandwidth 15% -queue mail bandwidth 10% priority 0 cbq(borrow ecn) -queue ssh bandwidth 20% cbq(borrow) { ssh_interactive, ssh_bulk } -queue ssh_interactive bandwidth 50% priority 7 cbq(borrow) -queue ssh_bulk bandwidth 50% priority 0 cbq(borrow) - -block return out on dc0 inet all queue std -pass out on dc0 inet proto tcp from $developerhosts to any port 80 \e - queue developers -pass out on dc0 inet proto tcp from $employeehosts to any port 80 \e - queue employees -pass out on dc0 inet proto tcp from any to any port 22 \e - queue(ssh_bulk, ssh_interactive) -pass out on dc0 inet proto tcp from any to any port 25 \e - queue mail -.Ed -.Sh TRANSLATION -Translation rules modify either the source or destination address of the -packets associated with a stateful connection. -A stateful connection is automatically created to track packets matching -such a rule as long as they are not blocked by the filtering section of -.Nm pf.conf . -The translation engine modifies the specified address and/or port in the -packet, recalculates IP, TCP and UDP checksums as necessary, and passes it to -the packet filter for evaluation. -.Pp -Since translation occurs before filtering the filter -engine will see packets as they look after any -addresses and ports have been translated. -Filter rules will therefore have to filter based on the translated -address and port number. -Packets that match a translation rule are only automatically passed if -the -.Ar pass -modifier is given, otherwise they are -still subject to -.Ar block -and -.Ar pass -rules. -.Pp -The state entry created permits -.Xr pf 4 -to keep track of the original address for traffic associated with that state -and correctly direct return traffic for that connection. -.Pp -Various types of translation are possible with pf: -.Bl -tag -width xxxx -.It Ar binat -A -.Ar binat -rule specifies a bidirectional mapping between an external IP netblock -and an internal IP netblock. -.It Ar nat -A -.Ar nat -rule specifies that IP addresses are to be changed as the packet -traverses the given interface. -This technique allows one or more IP addresses -on the translating host to support network traffic for a larger range of -machines on an "inside" network. -Although in theory any IP address can be used on the inside, it is strongly -recommended that one of the address ranges defined by RFC 1918 be used. -These netblocks are: -.Bd -literal -10.0.0.0 - 10.255.255.255 (all of net 10, i.e., 10/8) -172.16.0.0 - 172.31.255.255 (i.e., 172.16/12) -192.168.0.0 - 192.168.255.255 (i.e., 192.168/16) -.Ed -.It Pa rdr -The packet is redirected to another destination and possibly a -different port. -.Ar rdr -rules can optionally specify port ranges instead of single ports. -rdr ... port 2000:2999 -\*(Gt ... port 4000 -redirects ports 2000 to 2999 (inclusive) to port 4000. -rdr ... port 2000:2999 -\*(Gt ... port 4000:* -redirects port 2000 to 4000, 2001 to 4001, ..., 2999 to 4999. -.El -.Pp -In addition to modifying the address, some translation rules may modify -source or destination ports for -.Xr tcp 4 -or -.Xr udp 4 -connections; implicitly in the case of -.Ar nat -rules and explicitly in the case of -.Ar rdr -rules. -Port numbers are never translated with a -.Ar binat -rule. -.Pp -Evaluation order of the translation rules is dependent on the type -of the translation rules and of the direction of a packet. -.Ar binat -rules are always evaluated first. -Then either the -.Ar rdr -rules are evaluated on an inbound packet or the -.Ar nat -rules on an outbound packet. -Rules of the same type are evaluated in the same order in which they -appear in the ruleset. -The first matching rule decides what action is taken. -.Pp -The -.Ar no -option prefixed to a translation rule causes packets to remain untranslated, -much in the same way as -.Ar drop quick -works in the packet filter (see below). -If no rule matches the packet it is passed to the filter engine unmodified. -.Pp -Translation rules apply only to packets that pass through -the specified interface, and if no interface is specified, -translation is applied to packets on all interfaces. -For instance, redirecting port 80 on an external interface to an internal -web server will only work for connections originating from the outside. -Connections to the address of the external interface from local hosts will -not be redirected, since such packets do not actually pass through the -external interface. -Redirections cannot reflect packets back through the interface they arrive -on, they can only be redirected to hosts connected to different interfaces -or to the firewall itself. -.Pp -Note that redirecting external incoming connections to the loopback -address, as in -.Bd -literal -offset indent -rdr on ne3 inet proto tcp to port smtp -\*(Gt 127.0.0.1 port spamd -.Ed -.Pp -will effectively allow an external host to connect to daemons -bound solely to the loopback address, circumventing the traditional -blocking of such connections on a real interface. -Unless this effect is desired, any of the local non-loopback addresses -should be used as redirection target instead, which allows external -connections only to daemons bound to this address or not bound to -any address. -.Pp -See -.Sx TRANSLATION EXAMPLES -below. -.Sh PACKET FILTERING -.Xr pf 4 -has the ability to -.Ar block -and -.Ar pass -packets based on attributes of their layer 3 (see -.Xr ip 4 -and -.Xr ip6 4 ) -and layer 4 (see -.Xr icmp 4 , -.Xr icmp6 4 , -.Xr tcp 4 , -.Xr udp 4 ) -headers. -In addition, packets may also be -assigned to queues for the purpose of bandwidth control. -.Pp -For each packet processed by the packet filter, the filter rules are -evaluated in sequential order, from first to last. -The last matching rule decides what action is taken. -If no rule matches the packet, the default action is to pass -the packet. -.Pp -The following actions can be used in the filter: -.Bl -tag -width xxxx -.It Ar block -The packet is blocked. -There are a number of ways in which a -.Ar block -rule can behave when blocking a packet. -The default behaviour is to -.Ar drop -packets silently, however this can be overridden or made -explicit either globally, by setting the -.Ar block-policy -option, or on a per-rule basis with one of the following options: -.Pp -.Bl -tag -width xxxx -compact -.It Ar drop -The packet is silently dropped. -.It Ar return-rst -This applies only to -.Xr tcp 4 -packets, and issues a TCP RST which closes the -connection. -.It Ar return-icmp -.It Ar return-icmp6 -This causes ICMP messages to be returned for packets which match the rule. -By default this is an ICMP UNREACHABLE message, however this -can be overridden by specifying a message as a code or number. -.It Ar return -This causes a TCP RST to be returned for -.Xr tcp 4 -packets and an ICMP UNREACHABLE for UDP and other packets. -.El -.Pp -Options returning ICMP packets currently have no effect if -.Xr pf 4 -operates on a -.Xr if_bridge 4 , -as the code to support this feature has not yet been implemented. -.Pp -The simplest mechanism to block everything by default and only pass -packets that match explicit rules is specify a first filter rule of: -.Bd -literal -offset indent -block all -.Ed -.It Ar pass -The packet is passed; -state is created unless the -.Ar no state -option is specified. -.El -.Pp -By default -.Xr pf 4 -filters packets statefully; the first time a packet matches a -.Ar pass -rule, a state entry is created; for subsequent packets the filter checks -whether the packet matches any state. -If it does, the packet is passed without evaluation of any rules. -After the connection is closed or times out, the state entry is automatically -removed. -.Pp -This has several advantages. -For TCP connections, comparing a packet to a state involves checking -its sequence numbers, as well as TCP timestamps if a -.Ar scrub reassemble tcp -rule applies to the connection. -If these values are outside the narrow windows of expected -values, the packet is dropped. -This prevents spoofing attacks, such as when an attacker sends packets with -a fake source address/port but does not know the connection's sequence -numbers. -Similarly, -.Xr pf 4 -knows how to match ICMP replies to states. -For example, -.Bd -literal -offset indent -pass out inet proto icmp all icmp-type echoreq -.Ed -.Pp -allows echo requests (such as those created by -.Xr ping 8 ) -out statefully, and matches incoming echo replies correctly to states. -.Pp -Also, looking up states is usually faster than evaluating rules. -If there are 50 rules, all of them are evaluated sequentially in O(n). -Even with 50000 states, only 16 comparisons are needed to match a -state, since states are stored in a binary search tree that allows -searches in O(log2 n). -.Pp -Furthermore, correct handling of ICMP error messages is critical to -many protocols, particularly TCP. -.Xr pf 4 -matches ICMP error messages to the correct connection, checks them against -connection parameters, and passes them if appropriate. -For example if an ICMP source quench message referring to a stateful TCP -connection arrives, it will be matched to the state and get passed. -.Pp -Finally, state tracking is required for -.Ar nat , binat No and Ar rdr -rules, in order to track address and port translations and reverse the -translation on returning packets. -.Pp -.Xr pf 4 -will also create state for other protocols which are effectively stateless by -nature. -UDP packets are matched to states using only host addresses and ports, -and other protocols are matched to states using only the host addresses. -.Pp -If stateless filtering of individual packets is desired, -the -.Ar no state -keyword can be used to specify that state will not be created -if this is the last matching rule. -A number of parameters can also be set to affect how -.Xr pf 4 -handles state tracking. -See -.Sx STATEFUL TRACKING OPTIONS -below for further details. -.Sh PARAMETERS -The rule parameters specify the packets to which a rule applies. -A packet always comes in on, or goes out through, one interface. -Most parameters are optional. -If a parameter is specified, the rule only applies to packets with -matching attributes. -Certain parameters can be expressed as lists, in which case -.Xr pfctl 8 -generates all needed rule combinations. -.Bl -tag -width xxxx -.It Ar in No or Ar out -This rule applies to incoming or outgoing packets. -If neither -.Ar in -nor -.Ar out -are specified, the rule will match packets in both directions. -.It Ar log -In addition to the action specified, a log message is generated. -Only the packet that establishes the state is logged, -unless the -.Ar no state -option is specified. -The logged packets are sent to a -.Xr pflog 4 -interface, by default -.Ar pflog0 . -This interface is monitored by the -.Xr pflogd 8 -logging daemon, which dumps the logged packets to the file -.Pa /var/log/pflog -in -.Xr pcap 3 -binary format. -.It Ar log (all) -Used to force logging of all packets for a connection. -This is not necessary when -.Ar no state -is explicitly specified. -As with -.Ar log , -packets are logged to -.Xr pflog 4 . -.It Ar log (user) -Logs the -.Ux -user ID of the user that owns the socket and the PID of the process that -has the socket open where the packet is sourced from or destined to -(depending on which socket is local). -This is in addition to the normal information logged. -.Pp -Only the first packet -logged via -.Ar log (all, user) -will have the user credentials logged when using stateful matching. -.It Ar log (to Aq Ar interface ) -Send logs to the specified -.Xr pflog 4 -interface instead of -.Ar pflog0 . -.It Ar quick -If a packet matches a rule which has the -.Ar quick -option set, this rule -is considered the last matching rule, and evaluation of subsequent rules -is skipped. -.It Ar on Aq Ar interface -This rule applies only to packets coming in on, or going out through, this -particular interface or interface group. -For more information on interface groups, -see the -.Ic group -keyword in -.Xr ifconfig 8 . -.It Aq Ar af -This rule applies only to packets of this address family. -Supported values are -.Ar inet -and -.Ar inet6 . -.It Ar proto Aq Ar protocol -This rule applies only to packets of this protocol. -Common protocols are -.Xr icmp 4 , -.Xr icmp6 4 , -.Xr tcp 4 , -and -.Xr udp 4 . -For a list of all the protocol name to number mappings used by -.Xr pfctl 8 , -see the file -.Em /etc/protocols . -.It Xo -.Ar from Aq Ar source -.Ar port Aq Ar source -.Ar os Aq Ar source -.Ar to Aq Ar dest -.Ar port Aq Ar dest -.Xc -This rule applies only to packets with the specified source and destination -addresses and ports. -.Pp -Addresses can be specified in CIDR notation (matching netblocks), as -symbolic host names, interface names or interface group names, or as any -of the following keywords: -.Pp -.Bl -tag -width xxxxxxxxxxxxxx -compact -.It Ar any -Any address. -.It Ar no-route -Any address which is not currently routable. -.It Ar urpf-failed -Any source address that fails a unicast reverse path forwarding (URPF) -check, i.e. packets coming in on an interface other than that which holds -the route back to the packet's source address. -.It Aq Ar table -Any address that matches the given table. -.El -.Pp -Ranges of addresses are specified by using the -.Sq - -operator. -For instance: -.Dq 10.1.1.10 - 10.1.1.12 -means all addresses from 10.1.1.10 to 10.1.1.12, -hence addresses 10.1.1.10, 10.1.1.11, and 10.1.1.12. -.Pp -Interface names and interface group names can have modifiers appended: -.Pp -.Bl -tag -width xxxxxxxxxxxx -compact -.It Ar :network -Translates to the network(s) attached to the interface. -.It Ar :broadcast -Translates to the interface's broadcast address(es). -.It Ar :peer -Translates to the point-to-point interface's peer address(es). -.It Ar :0 -Do not include interface aliases. -.El -.Pp -Host names may also have the -.Ar :0 -option appended to restrict the name resolution to the first of each -v4 and v6 address found. -.Pp -Host name resolution and interface to address translation are done at -ruleset load-time. -When the address of an interface (or host name) changes (under DHCP or PPP, -for instance), the ruleset must be reloaded for the change to be reflected -in the kernel. -Surrounding the interface name (and optional modifiers) in parentheses -changes this behaviour. -When the interface name is surrounded by parentheses, the rule is -automatically updated whenever the interface changes its address. -The ruleset does not need to be reloaded. -This is especially useful with -.Ar nat . -.Pp -Ports can be specified either by number or by name. -For example, port 80 can be specified as -.Em www . -For a list of all port name to number mappings used by -.Xr pfctl 8 , -see the file -.Pa /etc/services . -.Pp -Ports and ranges of ports are specified by using these operators: -.Bd -literal -offset indent -= (equal) -!= (unequal) -\*(Lt (less than) -\*(Le (less than or equal) -\*(Gt (greater than) -\*(Ge (greater than or equal) -: (range including boundaries) -\*(Gt\*(Lt (range excluding boundaries) -\*(Lt\*(Gt (except range) -.Ed -.Pp -.Sq \*(Gt\*(Lt , -.Sq \*(Lt\*(Gt -and -.Sq \&: -are binary operators (they take two arguments). -For instance: -.Bl -tag -width Fl -.It Ar port 2000:2004 -means -.Sq all ports \*(Ge 2000 and \*(Le 2004 , -hence ports 2000, 2001, 2002, 2003 and 2004. -.It Ar port 2000 \*(Gt\*(Lt 2004 -means -.Sq all ports \*(Gt 2000 and \*(Lt 2004 , -hence ports 2001, 2002 and 2003. -.It Ar port 2000 \*(Lt\*(Gt 2004 -means -.Sq all ports \*(Lt 2000 or \*(Gt 2004 , -hence ports 1-1999 and 2005-65535. -.El -.Pp -The operating system of the source host can be specified in the case of TCP -rules with the -.Ar OS -modifier. -See the -.Sx OPERATING SYSTEM FINGERPRINTING -section for more information. -.Pp -The host, port and OS specifications are optional, as in the following examples: -.Bd -literal -offset indent -pass in all -pass in from any to any -pass in proto tcp from any port \*(Le 1024 to any -pass in proto tcp from any to any port 25 -pass in proto tcp from 10.0.0.0/8 port \*(Gt 1024 \e - to ! 10.1.2.3 port != ssh -pass in proto tcp from any os "OpenBSD" -.Ed -.It Ar all -This is equivalent to "from any to any". -.It Ar group Aq Ar group -Similar to -.Ar user , -this rule only applies to packets of sockets owned by the specified group. -.It Ar user Aq Ar user -This rule only applies to packets of sockets owned by the specified user. -For outgoing connections initiated from the firewall, this is the user -that opened the connection. -For incoming connections to the firewall itself, this is the user that -listens on the destination port. -For forwarded connections, where the firewall is not a connection endpoint, -the user and group are -.Em unknown . -.Pp -All packets, both outgoing and incoming, of one connection are associated -with the same user and group. -Only TCP and UDP packets can be associated with users; for other protocols -these parameters are ignored. -.Pp -User and group refer to the effective (as opposed to the real) IDs, in -case the socket is created by a setuid/setgid process. -User and group IDs are stored when a socket is created; -when a process creates a listening socket as root (for instance, by -binding to a privileged port) and subsequently changes to another -user ID (to drop privileges), the credentials will remain root. -.Pp -User and group IDs can be specified as either numbers or names. -The syntax is similar to the one for ports. -The value -.Em unknown -matches packets of forwarded connections. -.Em unknown -can only be used with the operators -.Cm = -and -.Cm != . -Other constructs like -.Cm user \*(Ge unknown -are invalid. -Forwarded packets with unknown user and group ID match only rules -that explicitly compare against -.Em unknown -with the operators -.Cm = -or -.Cm != . -For instance -.Cm user \*(Ge 0 -does not match forwarded packets. -The following example allows only selected users to open outgoing -connections: -.Bd -literal -offset indent -block out proto { tcp, udp } all -pass out proto { tcp, udp } all user { \*(Lt 1000, dhartmei } -.Ed -.It Xo Ar flags Aq Ar a -.Pf / Ns Aq Ar b -.No \*(Ba / Ns Aq Ar b -.No \*(Ba any -.Xc -This rule only applies to TCP packets that have the flags -.Aq Ar a -set out of set -.Aq Ar b . -Flags not specified in -.Aq Ar b -are ignored. -For stateful connections, the default is -.Ar flags S/SA . -To indicate that flags should not be checked at all, specify -.Ar flags any . -The flags are: (F)IN, (S)YN, (R)ST, (P)USH, (A)CK, (U)RG, (E)CE, and C(W)R. -.Bl -tag -width Fl -.It Ar flags S/S -Flag SYN is set. -The other flags are ignored. -.It Ar flags S/SA -This is the default setting for stateful connections. -Out of SYN and ACK, exactly SYN may be set. -SYN, SYN+PSH and SYN+RST match, but SYN+ACK, ACK and ACK+RST do not. -This is more restrictive than the previous example. -.It Ar flags /SFRA -If the first set is not specified, it defaults to none. -All of SYN, FIN, RST and ACK must be unset. -.El -.Pp -Because -.Ar flags S/SA -is applied by default (unless -.Ar no state -is specified), only the initial SYN packet of a TCP handshake will create -a state for a TCP connection. -It is possible to be less restrictive, and allow state creation from -intermediate -.Pq non-SYN -packets, by specifying -.Ar flags any . -This will cause -.Xr pf 4 -to synchronize to existing connections, for instance -if one flushes the state table. -However, states created from such intermediate packets may be missing -connection details such as the TCP window scaling factor. -States which modify the packet flow, such as those affected by -.Ar nat , binat No or Ar rdr -rules, -.Ar modulate No or Ar synproxy state -options, or scrubbed with -.Ar reassemble tcp -will also not be recoverable from intermediate packets. -Such connections will stall and time out. -.It Xo Ar icmp-type Aq Ar type -.Ar code Aq Ar code -.Xc -.It Xo Ar icmp6-type Aq Ar type -.Ar code Aq Ar code -.Xc -This rule only applies to ICMP or ICMPv6 packets with the specified type -and code. -Text names for ICMP types and codes are listed in -.Xr icmp 4 -and -.Xr icmp6 4 . -This parameter is only valid for rules that cover protocols ICMP or -ICMP6. -The protocol and the ICMP type indicator -.Po -.Ar icmp-type -or -.Ar icmp6-type -.Pc -must match. -.It Xo Ar tos Aq Ar string -.No \*(Ba Aq Ar number -.Xc -This rule applies to packets with the specified -.Em TOS -bits set. -.Em TOS -may be -given as one of -.Ar lowdelay , -.Ar throughput , -.Ar reliability , -or as either hex or decimal. -.Pp -For example, the following rules are identical: -.Bd -literal -offset indent -pass all tos lowdelay -pass all tos 0x10 -pass all tos 16 -.Ed -.It Ar allow-opts -By default, IPv4 packets with IP options or IPv6 packets with routing -extension headers are blocked. -When -.Ar allow-opts -is specified for a -.Ar pass -rule, packets that pass the filter based on that rule (last matching) -do so even if they contain IP options or routing extension headers. -For packets that match state, the rule that initially created the -state is used. -The implicit -.Ar pass -rule that is used when a packet does not match any rules does not -allow IP options. -.It Ar label Aq Ar string -Adds a label (name) to the rule, which can be used to identify the rule. -For instance, -pfctl -s labels -shows per-rule statistics for rules that have labels. -.Pp -The following macros can be used in labels: -.Pp -.Bl -tag -width $srcaddr -compact -offset indent -.It Ar $if -The interface. -.It Ar $srcaddr -The source IP address. -.It Ar $dstaddr -The destination IP address. -.It Ar $srcport -The source port specification. -.It Ar $dstport -The destination port specification. -.It Ar $proto -The protocol name. -.It Ar $nr -The rule number. -.El -.Pp -For example: -.Bd -literal -offset indent -ips = \&"{ 1.2.3.4, 1.2.3.5 }\&" -pass in proto tcp from any to $ips \e - port \*(Gt 1023 label \&"$dstaddr:$dstport\&" -.Ed -.Pp -expands to -.Bd -literal -offset indent -pass in inet proto tcp from any to 1.2.3.4 \e - port \*(Gt 1023 label \&"1.2.3.4:\*(Gt1023\&" -pass in inet proto tcp from any to 1.2.3.5 \e - port \*(Gt 1023 label \&"1.2.3.5:\*(Gt1023\&" -.Ed -.Pp -The macro expansion for the -.Ar label -directive occurs only at configuration file parse time, not during runtime. -.It Xo Ar queue Aq Ar queue -.No \*(Ba ( Aq Ar queue , -.Aq Ar queue ) -.Xc -Packets matching this rule will be assigned to the specified queue. -If two queues are given, packets which have a -.Em TOS -of -.Em lowdelay -and TCP ACKs with no data payload will be assigned to the second one. -See -.Sx QUEUEING -for setup details. -.Pp -For example: -.Bd -literal -offset indent -pass in proto tcp to port 25 queue mail -pass in proto tcp to port 22 queue(ssh_bulk, ssh_prio) -.Ed -.It Ar tag Aq Ar string -Packets matching this rule will be tagged with the -specified string. -The tag acts as an internal marker that can be used to -identify these packets later on. -This can be used, for example, to provide trust between -interfaces and to determine if packets have been -processed by translation rules. -Tags are -.Qq sticky , -meaning that the packet will be tagged even if the rule -is not the last matching rule. -Further matching rules can replace the tag with a -new one but will not remove a previously applied tag. -A packet is only ever assigned one tag at a time. -Packet tagging can be done during -.Ar nat , -.Ar rdr , -or -.Ar binat -rules in addition to filter rules. -Tags take the same macros as labels (see above). -.It Ar tagged Aq Ar string -Used with filter, translation or scrub rules -to specify that packets must already -be tagged with the given tag in order to match the rule. -Inverse tag matching can also be done -by specifying the -.Cm !\& -operator before the -.Ar tagged -keyword. -.It Ar rtable Aq Ar number -Used to select an alternate routing table for the routing lookup. -Only effective before the route lookup happened, i.e. when filtering inbound. -.It Xo Ar divert-to Aq Ar host -.Ar port Aq Ar port -.Xc -Used to redirect packets to a local socket bound to -.Ar host -and -.Ar port . -The packets will not be modified, so -.Xr getsockname 2 -on the socket will return the original destination address of the packet. -.It Ar divert-reply -Used to receive replies for sockets that are bound to addresses -which are not local to the machine. -See -.Xr setsockopt 2 -for information on how to bind these sockets. -.It Ar probability Aq Ar number -A probability attribute can be attached to a rule, with a value set between -0 and 1, bounds not included. -In that case, the rule will be honoured using the given probability value -only. -For example, the following rule will drop 20% of incoming ICMP packets: -.Bd -literal -offset indent -block in proto icmp probability 20% -.Ed -.El -.Sh ROUTING -If a packet matches a rule with a route option set, the packet filter will -route the packet according to the type of route option. -When such a rule creates state, the route option is also applied to all -packets matching the same connection. -.Bl -tag -width xxxx -.It Ar fastroute -The -.Ar fastroute -option does a normal route lookup to find the next hop for the packet. -.It Ar route-to -The -.Ar route-to -option routes the packet to the specified interface with an optional address -for the next hop. -When a -.Ar route-to -rule creates state, only packets that pass in the same direction as the -filter rule specifies will be routed in this way. -Packets passing in the opposite direction (replies) are not affected -and are routed normally. -.It Ar reply-to -The -.Ar reply-to -option is similar to -.Ar route-to , -but routes packets that pass in the opposite direction (replies) to the -specified interface. -Opposite direction is only defined in the context of a state entry, and -.Ar reply-to -is useful only in rules that create state. -It can be used on systems with multiple external connections to -route all outgoing packets of a connection through the interface -the incoming connection arrived through (symmetric routing enforcement). -.It Ar dup-to -The -.Ar dup-to -option creates a duplicate of the packet and routes it like -.Ar route-to . -The original packet gets routed as it normally would. -.El -.Sh POOL OPTIONS -For -.Ar nat -and -.Ar rdr -rules, (as well as for the -.Ar route-to , -.Ar reply-to -and -.Ar dup-to -rule options) for which there is a single redirection address which has a -subnet mask smaller than 32 for IPv4 or 128 for IPv6 (more than one IP -address), a variety of different methods for assigning this address can be -used: -.Bl -tag -width xxxx -.It Ar bitmask -The -.Ar bitmask -option applies the network portion of the redirection address to the address -to be modified (source with -.Ar nat , -destination with -.Ar rdr ) . -.It Ar random -The -.Ar random -option selects an address at random within the defined block of addresses. -.It Ar source-hash -The -.Ar source-hash -option uses a hash of the source address to determine the redirection address, -ensuring that the redirection address is always the same for a given source. -An optional key can be specified after this keyword either in hex or as a -string; by default -.Xr pfctl 8 -randomly generates a key for source-hash every time the -ruleset is reloaded. -.It Ar round-robin -The -.Ar round-robin -option loops through the redirection address(es). -.Pp -When more than one redirection address is specified, -.Ar round-robin -is the only permitted pool type. -.It Ar static-port -With -.Ar nat -rules, the -.Ar static-port -option prevents -.Xr pf 4 -from modifying the source port on TCP and UDP packets. -.El -.Pp -Additionally, the -.Ar sticky-address -option can be specified to help ensure that multiple connections from the -same source are mapped to the same redirection address. -This option can be used with the -.Ar random -and -.Ar round-robin -pool options. -Note that by default these associations are destroyed as soon as there are -no longer states which refer to them; in order to make the mappings last -beyond the lifetime of the states, increase the global options with -.Ar set timeout src.track . -See -.Sx STATEFUL TRACKING OPTIONS -for more ways to control the source tracking. -.Sh STATE MODULATION -Much of the security derived from TCP is attributable to how well the -initial sequence numbers (ISNs) are chosen. -Some popular stack implementations choose -.Em very -poor ISNs and thus are normally susceptible to ISN prediction exploits. -By applying a -.Ar modulate state -rule to a TCP connection, -.Xr pf 4 -will create a high quality random sequence number for each connection -endpoint. -.Pp -The -.Ar modulate state -directive implicitly keeps state on the rule and is -only applicable to TCP connections. -.Pp -For instance: -.Bd -literal -offset indent -block all -pass out proto tcp from any to any modulate state -pass in proto tcp from any to any port 25 flags S/SFRA modulate state -.Ed -.Pp -Note that modulated connections will not recover when the state table -is lost (firewall reboot, flushing the state table, etc...). -.Xr pf 4 -will not be able to infer a connection again after the state table flushes -the connection's modulator. -When the state is lost, the connection may be left dangling until the -respective endpoints time out the connection. -It is possible on a fast local network for the endpoints to start an ACK -storm while trying to resynchronize after the loss of the modulator. -The default -.Ar flags -settings (or a more strict equivalent) should be used on -.Ar modulate state -rules to prevent ACK storms. -.Pp -Note that alternative methods are available -to prevent loss of the state table -and allow for firewall failover. -See -.Xr carp 4 -and -.Xr pfsync 4 -for further information. -.Sh SYN PROXY -By default, -.Xr pf 4 -passes packets that are part of a -.Xr tcp 4 -handshake between the endpoints. -The -.Ar synproxy state -option can be used to cause -.Xr pf 4 -itself to complete the handshake with the active endpoint, perform a handshake -with the passive endpoint, and then forward packets between the endpoints. -.Pp -No packets are sent to the passive endpoint before the active endpoint has -completed the handshake, hence so-called SYN floods with spoofed source -addresses will not reach the passive endpoint, as the sender can't complete the -handshake. -.Pp -The proxy is transparent to both endpoints, they each see a single -connection from/to the other endpoint. -.Xr pf 4 -chooses random initial sequence numbers for both handshakes. -Once the handshakes are completed, the sequence number modulators -(see previous section) are used to translate further packets of the -connection. -.Ar synproxy state -includes -.Ar modulate state . -.Pp -Rules with -.Ar synproxy -will not work if -.Xr pf 4 -operates on a -.Xr bridge 4 . -.Pp -Example: -.Bd -literal -offset indent -pass in proto tcp from any to any port www synproxy state -.Ed -.Sh STATEFUL TRACKING OPTIONS -A number of options related to stateful tracking can be applied on a -per-rule basis. -.Ar keep state , -.Ar modulate state -and -.Ar synproxy state -support these options, and -.Ar keep state -must be specified explicitly to apply options to a rule. -.Pp -.Bl -tag -width xxxx -compact -.It Ar max Aq Ar number -Limits the number of concurrent states the rule may create. -When this limit is reached, further packets that would create -state will not match this rule until existing states time out. -.It Ar no-sync -Prevent state changes for states created by this rule from appearing on the -.Xr pfsync 4 -interface. -.It Xo Aq Ar timeout -.Aq Ar seconds -.Xc -Changes the timeout values used for states created by this rule. -For a list of all valid timeout names, see -.Sx OPTIONS -above. -.It Ar sloppy -Uses a sloppy TCP connection tracker that does not check sequence -numbers at all, which makes insertion and ICMP teardown attacks way -easier. -This is intended to be used in situations where one does not see all -packets of a connection, e.g. in asymmetric routing situations. -Cannot be used with modulate or synproxy state. -.It Ar pflow -States created by this rule are exported on the -.Xr pflow 4 -interface. -.El -.Pp -Multiple options can be specified, separated by commas: -.Bd -literal -offset indent -pass in proto tcp from any to any \e - port www keep state \e - (max 100, source-track rule, max-src-nodes 75, \e - max-src-states 3, tcp.established 60, tcp.closing 5) -.Ed -.Pp -When the -.Ar source-track -keyword is specified, the number of states per source IP is tracked. -.Pp -.Bl -tag -width xxxx -compact -.It Ar source-track rule -The maximum number of states created by this rule is limited by the rule's -.Ar max-src-nodes -and -.Ar max-src-states -options. -Only state entries created by this particular rule count toward the rule's -limits. -.It Ar source-track global -The number of states created by all rules that use this option is limited. -Each rule can specify different -.Ar max-src-nodes -and -.Ar max-src-states -options, however state entries created by any participating rule count towards -each individual rule's limits. -.El -.Pp -The following limits can be set: -.Pp -.Bl -tag -width xxxx -compact -.It Ar max-src-nodes Aq Ar number -Limits the maximum number of source addresses which can simultaneously -have state table entries. -.It Ar max-src-states Aq Ar number -Limits the maximum number of simultaneous state entries that a single -source address can create with this rule. -.El -.Pp -For stateful TCP connections, limits on established connections (connections -which have completed the TCP 3-way handshake) can also be enforced -per source IP. -.Pp -.Bl -tag -width xxxx -compact -.It Ar max-src-conn Aq Ar number -Limits the maximum number of simultaneous TCP connections which have -completed the 3-way handshake that a single host can make. -.It Xo Ar max-src-conn-rate Aq Ar number -.No / Aq Ar seconds -.Xc -Limit the rate of new connections over a time interval. -The connection rate is an approximation calculated as a moving average. -.El -.Pp -Because the 3-way handshake ensures that the source address is not being -spoofed, more aggressive action can be taken based on these limits. -With the -.Ar overload Aq Ar table -state option, source IP addresses which hit either of the limits on -established connections will be added to the named table. -This table can be used in the ruleset to block further activity from -the offending host, redirect it to a tarpit process, or restrict its -bandwidth. -.Pp -The optional -.Ar flush -keyword kills all states created by the matching rule which originate -from the host which exceeds these limits. -The -.Ar global -modifier to the flush command kills all states originating from the -offending host, regardless of which rule created the state. -.Pp -For example, the following rules will protect the webserver against -hosts making more than 100 connections in 10 seconds. -Any host which connects faster than this rate will have its address added -to the -.Aq bad_hosts -table and have all states originating from it flushed. -Any new packets arriving from this host will be dropped unconditionally -by the block rule. -.Bd -literal -offset indent -block quick from \*(Ltbad_hosts\*(Gt -pass in on $ext_if proto tcp to $webserver port www keep state \e - (max-src-conn-rate 100/10, overload \*(Ltbad_hosts\*(Gt flush global) -.Ed -.Sh OPERATING SYSTEM FINGERPRINTING -Passive OS Fingerprinting is a mechanism to inspect nuances of a TCP -connection's initial SYN packet and guess at the host's operating system. -Unfortunately these nuances are easily spoofed by an attacker so the -fingerprint is not useful in making security decisions. -But the fingerprint is typically accurate enough to make policy decisions -upon. -.Pp -The fingerprints may be specified by operating system class, by -version, or by subtype/patchlevel. -The class of an operating system is typically the vendor or genre -and would be -.Ox -for the -.Xr pf 4 -firewall itself. -The version of the oldest available -.Ox -release on the main FTP site -would be 2.6 and the fingerprint would be written -.Pp -.Dl \&"OpenBSD 2.6\&" -.Pp -The subtype of an operating system is typically used to describe the -patchlevel if that patch led to changes in the TCP stack behavior. -In the case of -.Ox , -the only subtype is for a fingerprint that was -normalized by the -.Ar no-df -scrub option and would be specified as -.Pp -.Dl \&"OpenBSD 3.3 no-df\&" -.Pp -Fingerprints for most popular operating systems are provided by -.Xr pf.os 5 . -Once -.Xr pf 4 -is running, a complete list of known operating system fingerprints may -be listed by running: -.Pp -.Dl # pfctl -so -.Pp -Filter rules can enforce policy at any level of operating system specification -assuming a fingerprint is present. -Policy could limit traffic to approved operating systems or even ban traffic -from hosts that aren't at the latest service pack. -.Pp -The -.Ar unknown -class can also be used as the fingerprint which will match packets for -which no operating system fingerprint is known. -.Pp -Examples: -.Bd -literal -offset indent -pass out proto tcp from any os OpenBSD -block out proto tcp from any os Doors -block out proto tcp from any os "Doors PT" -block out proto tcp from any os "Doors PT SP3" -block out from any os "unknown" -pass on lo0 proto tcp from any os "OpenBSD 3.3 lo0" -.Ed -.Pp -Operating system fingerprinting is limited only to the TCP SYN packet. -This means that it will not work on other protocols and will not match -a currently established connection. -.Pp -Caveat: operating system fingerprints are occasionally wrong. -There are three problems: an attacker can trivially craft his packets to -appear as any operating system he chooses; -an operating system patch could change the stack behavior and no fingerprints -will match it until the database is updated; -and multiple operating systems may have the same fingerprint. -.Sh BLOCKING SPOOFED TRAFFIC -"Spoofing" is the faking of IP addresses, typically for malicious -purposes. -The -.Ar antispoof -directive expands to a set of filter rules which will block all -traffic with a source IP from the network(s) directly connected -to the specified interface(s) from entering the system through -any other interface. -.Pp -For example, the line -.Bd -literal -offset indent -antispoof for lo0 -.Ed -.Pp -expands to -.Bd -literal -offset indent -block drop in on ! lo0 inet from 127.0.0.1/8 to any -block drop in on ! lo0 inet6 from ::1 to any -.Ed -.Pp -For non-loopback interfaces, there are additional rules to block incoming -packets with a source IP address identical to the interface's IP(s). -For example, assuming the interface wi0 had an IP address of 10.0.0.1 and a -netmask of 255.255.255.0, -the line -.Bd -literal -offset indent -antispoof for wi0 inet -.Ed -.Pp -expands to -.Bd -literal -offset indent -block drop in on ! wi0 inet from 10.0.0.0/24 to any -block drop in inet from 10.0.0.1 to any -.Ed -.Pp -Caveat: Rules created by the -.Ar antispoof -directive interfere with packets sent over loopback interfaces -to local addresses. -One should pass these explicitly. -.Sh FRAGMENT HANDLING -The size of IP datagrams (packets) can be significantly larger than the -maximum transmission unit (MTU) of the network. -In cases when it is necessary or more efficient to send such large packets, -the large packet will be fragmented into many smaller packets that will each -fit onto the wire. -Unfortunately for a firewalling device, only the first logical fragment will -contain the necessary header information for the subprotocol that allows -.Xr pf 4 -to filter on things such as TCP ports or to perform NAT. -.Pp -Besides the use of -.Ar scrub -rules as described in -.Sx TRAFFIC NORMALIZATION -above, there are three options for handling fragments in the packet filter. -.Pp -One alternative is to filter individual fragments with filter rules. -If no -.Ar scrub -rule applies to a fragment, it is passed to the filter. -Filter rules with matching IP header parameters decide whether the -fragment is passed or blocked, in the same way as complete packets -are filtered. -Without reassembly, fragments can only be filtered based on IP header -fields (source/destination address, protocol), since subprotocol header -fields are not available (TCP/UDP port numbers, ICMP code/type). -The -.Ar fragment -option can be used to restrict filter rules to apply only to -fragments, but not complete packets. -Filter rules without the -.Ar fragment -option still apply to fragments, if they only specify IP header fields. -For instance, the rule -.Bd -literal -offset indent -pass in proto tcp from any to any port 80 -.Ed -.Pp -never applies to a fragment, even if the fragment is part of a TCP -packet with destination port 80, because without reassembly this information -is not available for each fragment. -This also means that fragments cannot create new or match existing -state table entries, which makes stateful filtering and address -translation (NAT, redirection) for fragments impossible. -.Pp -It's also possible to reassemble only certain fragments by specifying -source or destination addresses or protocols as parameters in -.Ar scrub -rules. -.Pp -In most cases, the benefits of reassembly outweigh the additional -memory cost, and it's recommended to use -.Ar scrub -rules to reassemble -all fragments via the -.Ar fragment reassemble -modifier. -.Pp -The memory allocated for fragment caching can be limited using -.Xr pfctl 8 . -Once this limit is reached, fragments that would have to be cached -are dropped until other entries time out. -The timeout value can also be adjusted. -.Pp -Currently, only IPv4 fragments are supported and IPv6 fragments -are blocked unconditionally. -.Sh ANCHORS -Besides the main ruleset, -.Xr pfctl 8 -can load rulesets into -.Ar anchor -attachment points. -An -.Ar anchor -is a container that can hold rules, address tables, and other anchors. -.Pp -An -.Ar anchor -has a name which specifies the path where -.Xr pfctl 8 -can be used to access the anchor to perform operations on it, such as -attaching child anchors to it or loading rules into it. -Anchors may be nested, with components separated by -.Sq / -characters, similar to how file system hierarchies are laid out. -The main ruleset is actually the default anchor, so filter and -translation rules, for example, may also be contained in any anchor. -.Pp -An anchor can reference another -.Ar anchor -attachment point -using the following kinds -of rules: -.Bl -tag -width xxxx -.It Ar nat-anchor Aq Ar name -Evaluates the -.Ar nat -rules in the specified -.Ar anchor . -.It Ar rdr-anchor Aq Ar name -Evaluates the -.Ar rdr -rules in the specified -.Ar anchor . -.It Ar binat-anchor Aq Ar name -Evaluates the -.Ar binat -rules in the specified -.Ar anchor . -.It Ar anchor Aq Ar name -Evaluates the filter rules in the specified -.Ar anchor . -.It Xo Ar load anchor -.Aq Ar name -.Ar from Aq Ar file -.Xc -Loads the rules from the specified file into the -anchor -.Ar name . -.El -.Pp -When evaluation of the main ruleset reaches an -.Ar anchor -rule, -.Xr pf 4 -will proceed to evaluate all rules specified in that anchor. -.Pp -Matching filter and translation rules marked with the -.Ar quick -option are final and abort the evaluation of the rules in other -anchors and the main ruleset. -If the -.Ar anchor -itself is marked with the -.Ar quick -option, -ruleset evaluation will terminate when the anchor is exited if the packet is -matched by any rule within the anchor. -.Pp -.Ar anchor -rules are evaluated relative to the anchor in which they are contained. -For example, all -.Ar anchor -rules specified in the main ruleset will reference anchor -attachment points underneath the main ruleset, and -.Ar anchor -rules specified in a file loaded from a -.Ar load anchor -rule will be attached under that anchor point. -.Pp -Rules may be contained in -.Ar anchor -attachment points which do not contain any rules when the main ruleset -is loaded, and later such anchors can be manipulated through -.Xr pfctl 8 -without reloading the main ruleset or other anchors. -For example, -.Bd -literal -offset indent -ext_if = \&"kue0\&" -block on $ext_if all -anchor spam -pass out on $ext_if all -pass in on $ext_if proto tcp from any \e - to $ext_if port smtp -.Ed -.Pp -blocks all packets on the external interface by default, then evaluates -all rules in the -.Ar anchor -named "spam", and finally passes all outgoing connections and -incoming connections to port 25. -.Bd -literal -offset indent -# echo \&"block in quick from 1.2.3.4 to any\&" \&| \e - pfctl -a spam -f - -.Ed -.Pp -This loads a single rule into the -.Ar anchor , -which blocks all packets from a specific address. -.Pp -The anchor can also be populated by adding a -.Ar load anchor -rule after the -.Ar anchor -rule: -.Bd -literal -offset indent -anchor spam -load anchor spam from "/etc/pf-spam.conf" -.Ed -.Pp -When -.Xr pfctl 8 -loads -.Nm pf.conf , -it will also load all the rules from the file -.Pa /etc/pf-spam.conf -into the anchor. -.Pp -Optionally, -.Ar anchor -rules can specify packet filtering parameters using the same syntax as -filter rules. -When parameters are used, the -.Ar anchor -rule is only evaluated for matching packets. -This allows conditional evaluation of anchors, like: -.Bd -literal -offset indent -block on $ext_if all -anchor spam proto tcp from any to any port smtp -pass out on $ext_if all -pass in on $ext_if proto tcp from any to $ext_if port smtp -.Ed -.Pp -The rules inside -.Ar anchor -spam are only evaluated for -.Ar tcp -packets with destination port 25. -Hence, -.Bd -literal -offset indent -# echo \&"block in quick from 1.2.3.4 to any" \&| \e - pfctl -a spam -f - -.Ed -.Pp -will only block connections from 1.2.3.4 to port 25. -.Pp -Anchors may end with the asterisk -.Pq Sq * -character, which signifies that all anchors attached at that point -should be evaluated in the alphabetical ordering of their anchor name. -For example, -.Bd -literal -offset indent -anchor "spam/*" -.Ed -.Pp -will evaluate each rule in each anchor attached to the -.Li spam -anchor. -Note that it will only evaluate anchors that are directly attached to the -.Li spam -anchor, and will not descend to evaluate anchors recursively. -.Pp -Since anchors are evaluated relative to the anchor in which they are -contained, there is a mechanism for accessing the parent and ancestor -anchors of a given anchor. -Similar to file system path name resolution, if the sequence -.Dq .. -appears as an anchor path component, the parent anchor of the current -anchor in the path evaluation at that point will become the new current -anchor. -As an example, consider the following: -.Bd -literal -offset indent -# echo ' anchor "spam/allowed" ' | pfctl -f - -# echo -e ' anchor "../banned" \en pass' | \e - pfctl -a spam/allowed -f - -.Ed -.Pp -Evaluation of the main ruleset will lead into the -.Li spam/allowed -anchor, which will evaluate the rules in the -.Li spam/banned -anchor, if any, before finally evaluating the -.Ar pass -rule. -.Pp -Filter rule -.Ar anchors -can also be loaded inline in the ruleset within a brace ('{' '}') delimited -block. -Brace delimited blocks may contain rules or other brace-delimited blocks. -When anchors are loaded this way the anchor name becomes optional. -.Bd -literal -offset indent -anchor "external" on egress { - block - anchor out { - pass proto tcp from any to port { 25, 80, 443 } - } - pass in proto tcp to any port 22 -} -.Ed -.Pp -Since the parser specification for anchor names is a string, any -reference to an anchor name containing -.Sq / -characters will require double quote -.Pq Sq \&" -characters around the anchor name. -.Sh TRANSLATION EXAMPLES -This example maps incoming requests on port 80 to port 8080, on -which a daemon is running (because, for example, it is not run as root, -and therefore lacks permission to bind to port 80). -.Bd -literal -# use a macro for the interface name, so it can be changed easily -ext_if = \&"ne3\&" - -# map daemon on 8080 to appear to be on 80 -rdr on $ext_if proto tcp from any to any port 80 -\*(Gt 127.0.0.1 port 8080 -.Ed -.Pp -If the -.Ar pass -modifier is given, packets matching the translation rule are passed without -inspecting the filter rules: -.Bd -literal -rdr pass on $ext_if proto tcp from any to any port 80 -\*(Gt 127.0.0.1 \e - port 8080 -.Ed -.Pp -In the example below, vlan12 is configured as 192.168.168.1; -the machine translates all packets coming from 192.168.168.0/24 to 204.92.77.111 -when they are going out any interface except vlan12. -This has the net effect of making traffic from the 192.168.168.0/24 -network appear as though it is the Internet routable address -204.92.77.111 to nodes behind any interface on the router except -for the nodes on vlan12. -(Thus, 192.168.168.1 can talk to the 192.168.168.0/24 nodes.) -.Bd -literal -nat on ! vlan12 from 192.168.168.0/24 to any -\*(Gt 204.92.77.111 -.Ed -.Pp -In the example below, the machine sits between a fake internal 144.19.74.* -network, and a routable external IP of 204.92.77.100. -The -.Ar no nat -rule excludes protocol AH from being translated. -.Bd -literal -# NO NAT -no nat on $ext_if proto ah from 144.19.74.0/24 to any -nat on $ext_if from 144.19.74.0/24 to any -\*(Gt 204.92.77.100 -.Ed -.Pp -In the example below, packets bound for one specific server, as well as those -generated by the sysadmins are not proxied; all other connections are. -.Bd -literal -# NO RDR -no rdr on $int_if proto { tcp, udp } from any to $server port 80 -no rdr on $int_if proto { tcp, udp } from $sysadmins to any port 80 -rdr on $int_if proto { tcp, udp } from any to any port 80 -\*(Gt 127.0.0.1 \e - port 80 -.Ed -.Pp -This longer example uses both a NAT and a redirection. -The external interface has the address 157.161.48.183. -On localhost, we are running -.Xr ftp-proxy 8 , -waiting for FTP sessions to be redirected to it. -The three mandatory anchors for -.Xr ftp-proxy 8 -are omitted from this example; see the -.Xr ftp-proxy 8 -manpage. -.Bd -literal -# NAT -# Translate outgoing packets' source addresses (any protocol). -# In this case, any address but the gateway's external address is mapped. -nat on $ext_if inet from ! ($ext_if) to any -\*(Gt ($ext_if) - -# NAT PROXYING -# Map outgoing packets' source port to an assigned proxy port instead of -# an arbitrary port. -# In this case, proxy outgoing isakmp with port 500 on the gateway. -nat on $ext_if inet proto udp from any port = isakmp to any -\*(Gt ($ext_if) \e - port 500 - -# BINAT -# Translate outgoing packets' source address (any protocol). -# Translate incoming packets' destination address to an internal machine -# (bidirectional). -binat on $ext_if from 10.1.2.150 to any -\*(Gt $ext_if - -# RDR -# Translate incoming packets' destination addresses. -# As an example, redirect a TCP and UDP port to an internal machine. -rdr on $ext_if inet proto tcp from any to ($ext_if) port 8080 \e - -\*(Gt 10.1.2.151 port 22 -rdr on $ext_if inet proto udp from any to ($ext_if) port 8080 \e - -\*(Gt 10.1.2.151 port 53 - -# RDR -# Translate outgoing ftp control connections to send them to localhost -# for proxying with ftp-proxy(8) running on port 8021. -rdr on $int_if proto tcp from any to any port 21 -\*(Gt 127.0.0.1 port 8021 -.Ed -.Pp -In this example, a NAT gateway is set up to translate internal addresses -using a pool of public addresses (192.0.2.16/28) and to redirect -incoming web server connections to a group of web servers on the internal -network. -.Bd -literal -# NAT LOAD BALANCE -# Translate outgoing packets' source addresses using an address pool. -# A given source address is always translated to the same pool address by -# using the source-hash keyword. -nat on $ext_if inet from any to any -\*(Gt 192.0.2.16/28 source-hash - -# RDR ROUND ROBIN -# Translate incoming web server connections to a group of web servers on -# the internal network. -rdr on $ext_if proto tcp from any to any port 80 \e - -\*(Gt { 10.1.2.155, 10.1.2.160, 10.1.2.161 } round-robin -.Ed -.Sh FILTER EXAMPLES -.Bd -literal -# The external interface is kue0 -# (157.161.48.183, the only routable address) -# and the private network is 10.0.0.0/8, for which we are doing NAT. - -# use a macro for the interface name, so it can be changed easily -ext_if = \&"kue0\&" - -# normalize all incoming traffic -scrub in on $ext_if all fragment reassemble - -# block and log everything by default -block return log on $ext_if all - -# block anything coming from source we have no back routes for -block in from no-route to any - -# block packets whose ingress interface does not match the one in -# the route back to their source address -block in from urpf-failed to any - -# block and log outgoing packets that do not have our address as source, -# they are either spoofed or something is misconfigured (NAT disabled, -# for instance), we want to be nice and do not send out garbage. -block out log quick on $ext_if from ! 157.161.48.183 to any - -# silently drop broadcasts (cable modem noise) -block in quick on $ext_if from any to 255.255.255.255 - -# block and log incoming packets from reserved address space and invalid -# addresses, they are either spoofed or misconfigured, we cannot reply to -# them anyway (hence, no return-rst). -block in log quick on $ext_if from { 10.0.0.0/8, 172.16.0.0/12, \e - 192.168.0.0/16, 255.255.255.255/32 } to any - -# ICMP - -# pass out/in certain ICMP queries and keep state (ping) -# state matching is done on host addresses and ICMP id (not type/code), -# so replies (like 0/0 for 8/0) will match queries -# ICMP error messages (which always refer to a TCP/UDP packet) are -# handled by the TCP/UDP states -pass on $ext_if inet proto icmp all icmp-type 8 code 0 - -# UDP - -# pass out all UDP connections and keep state -pass out on $ext_if proto udp all - -# pass in certain UDP connections and keep state (DNS) -pass in on $ext_if proto udp from any to any port domain - -# TCP - -# pass out all TCP connections and modulate state -pass out on $ext_if proto tcp all modulate state - -# pass in certain TCP connections and keep state (SSH, SMTP, DNS, IDENT) -pass in on $ext_if proto tcp from any to any port { ssh, smtp, domain, \e - auth } - -# Do not allow Windows 9x SMTP connections since they are typically -# a viral worm. Alternately we could limit these OSes to 1 connection each. -block in on $ext_if proto tcp from any os {"Windows 95", "Windows 98"} \e - to any port smtp - -# IPv6 -# pass in/out all IPv6 traffic: note that we have to enable this in two -# different ways, on both our physical interface and our tunnel -pass quick on gif0 inet6 -pass quick on $ext_if proto ipv6 - -# Packet Tagging - -# three interfaces: $int_if, $ext_if, and $wifi_if (wireless). NAT is -# being done on $ext_if for all outgoing packets. tag packets in on -# $int_if and pass those tagged packets out on $ext_if. all other -# outgoing packets (i.e., packets from the wireless network) are only -# permitted to access port 80. - -pass in on $int_if from any to any tag INTNET -pass in on $wifi_if from any to any - -block out on $ext_if from any to any -pass out quick on $ext_if tagged INTNET -pass out on $ext_if proto tcp from any to any port 80 - -# tag incoming packets as they are redirected to spamd(8). use the tag -# to pass those packets through the packet filter. - -rdr on $ext_if inet proto tcp from \*(Ltspammers\*(Gt to port smtp \e - tag SPAMD -\*(Gt 127.0.0.1 port spamd - -block in on $ext_if -pass in on $ext_if inet proto tcp tagged SPAMD -.Ed -.Sh GRAMMAR -Syntax for -.Nm -in BNF: -.Bd -literal -line = ( option | pf-rule | nat-rule | binat-rule | rdr-rule | - antispoof-rule | altq-rule | queue-rule | trans-anchors | - anchor-rule | anchor-close | load-anchor | table-rule | - include ) - -option = "set" ( [ "timeout" ( timeout | "{" timeout-list "}" ) ] | - [ "ruleset-optimization" [ "none" | "basic" | "profile" ]] | - [ "optimization" [ "default" | "normal" | - "high-latency" | "satellite" | - "aggressive" | "conservative" ] ] - [ "limit" ( limit-item | "{" limit-list "}" ) ] | - [ "loginterface" ( interface-name | "none" ) ] | - [ "block-policy" ( "drop" | "return" ) ] | - [ "state-policy" ( "if-bound" | "floating" ) ] - [ "state-defaults" state-opts ] - [ "require-order" ( "yes" | "no" ) ] - [ "fingerprints" filename ] | - [ "skip on" ifspec ] | - [ "debug" ( "none" | "urgent" | "misc" | "loud" ) ] ) - -pf-rule = action [ ( "in" | "out" ) ] - [ "log" [ "(" logopts ")"] ] [ "quick" ] - [ "on" ifspec ] [ "fastroute" | route ] [ af ] [ protospec ] - hosts [ filteropt-list ] - -logopts = logopt [ "," logopts ] -logopt = "all" | "user" | "to" interface-name - -filteropt-list = filteropt-list filteropt | filteropt -filteropt = user | group | flags | icmp-type | icmp6-type | "tos" tos | - ( "no" | "keep" | "modulate" | "synproxy" ) "state" - [ "(" state-opts ")" ] | - "fragment" | "no-df" | "min-ttl" number | "set-tos" tos | - "max-mss" number | "random-id" | "reassemble tcp" | - fragmentation | "allow-opts" | - "label" string | "tag" string | [ ! ] "tagged" string | - "queue" ( string | "(" string [ [ "," ] string ] ")" ) | - "rtable" number | "probability" number"%" - -nat-rule = [ "no" ] "nat" [ "pass" [ "log" [ "(" logopts ")" ] ] ] - [ "on" ifspec ] [ af ] - [ protospec ] hosts [ "tag" string ] [ "tagged" string ] - [ "-\*(Gt" ( redirhost | "{" redirhost-list "}" ) - [ portspec ] [ pooltype ] [ "static-port" ] ] - -binat-rule = [ "no" ] "binat" [ "pass" [ "log" [ "(" logopts ")" ] ] ] - [ "on" interface-name ] [ af ] - [ "proto" ( proto-name | proto-number ) ] - "from" address [ "/" mask-bits ] "to" ipspec - [ "tag" string ] [ "tagged" string ] - [ "-\*(Gt" address [ "/" mask-bits ] ] - -rdr-rule = [ "no" ] "rdr" [ "pass" [ "log" [ "(" logopts ")" ] ] ] - [ "on" ifspec ] [ af ] - [ protospec ] hosts [ "tag" string ] [ "tagged" string ] - [ "-\*(Gt" ( redirhost | "{" redirhost-list "}" ) - [ portspec ] [ pooltype ] ] - -antispoof-rule = "antispoof" [ "log" ] [ "quick" ] - "for" ifspec [ af ] [ "label" string ] - -table-rule = "table" "\*(Lt" string "\*(Gt" [ tableopts-list ] -tableopts-list = tableopts-list tableopts | tableopts -tableopts = "persist" | "const" | "counters" | "file" string | - "{" [ tableaddr-list ] "}" -tableaddr-list = tableaddr-list [ "," ] tableaddr-spec | tableaddr-spec -tableaddr-spec = [ "!" ] tableaddr [ "/" mask-bits ] -tableaddr = hostname | ifspec | "self" | - ipv4-dotted-quad | ipv6-coloned-hex - -altq-rule = "altq on" interface-name queueopts-list - "queue" subqueue -queue-rule = "queue" string [ "on" interface-name ] queueopts-list - subqueue - -anchor-rule = "anchor" [ string ] [ ( "in" | "out" ) ] [ "on" ifspec ] - [ af ] [ protospec ] [ hosts ] [ filteropt-list ] [ "{" ] - -anchor-close = "}" - -trans-anchors = ( "nat-anchor" | "rdr-anchor" | "binat-anchor" ) string - [ "on" ifspec ] [ af ] [ "proto" ] [ protospec ] [ hosts ] - -load-anchor = "load anchor" string "from" filename - -queueopts-list = queueopts-list queueopts | queueopts -queueopts = [ "bandwidth" bandwidth-spec ] | - [ "qlimit" number ] | [ "tbrsize" number ] | - [ "priority" number ] | [ schedulers ] -schedulers = ( cbq-def | priq-def | hfsc-def ) -bandwidth-spec = "number" ( "b" | "Kb" | "Mb" | "Gb" | "%" ) - -action = "pass" | "block" [ return ] | [ "no" ] "scrub" -return = "drop" | "return" | "return-rst" [ "( ttl" number ")" ] | - "return-icmp" [ "(" icmpcode [ [ "," ] icmp6code ] ")" ] | - "return-icmp6" [ "(" icmp6code ")" ] -icmpcode = ( icmp-code-name | icmp-code-number ) -icmp6code = ( icmp6-code-name | icmp6-code-number ) - -ifspec = ( [ "!" ] ( interface-name | interface-group ) ) | - "{" interface-list "}" -interface-list = [ "!" ] ( interface-name | interface-group ) - [ [ "," ] interface-list ] -route = ( "route-to" | "reply-to" | "dup-to" ) - ( routehost | "{" routehost-list "}" ) - [ pooltype ] -af = "inet" | "inet6" - -protospec = "proto" ( proto-name | proto-number | - "{" proto-list "}" ) -proto-list = ( proto-name | proto-number ) [ [ "," ] proto-list ] - -hosts = "all" | - "from" ( "any" | "no-route" | "urpf-failed" | "self" | host | - "{" host-list "}" ) [ port ] [ os ] - "to" ( "any" | "no-route" | "self" | host | - "{" host-list "}" ) [ port ] - -ipspec = "any" | host | "{" host-list "}" -host = [ "!" ] ( address [ "/" mask-bits ] | "\*(Lt" string "\*(Gt" ) -redirhost = address [ "/" mask-bits ] -routehost = "(" interface-name [ address [ "/" mask-bits ] ] ")" -address = ( interface-name | interface-group | - "(" ( interface-name | interface-group ) ")" | - hostname | ipv4-dotted-quad | ipv6-coloned-hex ) -host-list = host [ [ "," ] host-list ] -redirhost-list = redirhost [ [ "," ] redirhost-list ] -routehost-list = routehost [ [ "," ] routehost-list ] - -port = "port" ( unary-op | binary-op | "{" op-list "}" ) -portspec = "port" ( number | name ) [ ":" ( "*" | number | name ) ] -os = "os" ( os-name | "{" os-list "}" ) -user = "user" ( unary-op | binary-op | "{" op-list "}" ) -group = "group" ( unary-op | binary-op | "{" op-list "}" ) - -unary-op = [ "=" | "!=" | "\*(Lt" | "\*(Le" | "\*(Gt" | "\*(Ge" ] - ( name | number ) -binary-op = number ( "\*(Lt\*(Gt" | "\*(Gt\*(Lt" | ":" ) number -op-list = ( unary-op | binary-op ) [ [ "," ] op-list ] - -os-name = operating-system-name -os-list = os-name [ [ "," ] os-list ] - -flags = "flags" ( [ flag-set ] "/" flag-set | "any" ) -flag-set = [ "F" ] [ "S" ] [ "R" ] [ "P" ] [ "A" ] [ "U" ] [ "E" ] - [ "W" ] - -icmp-type = "icmp-type" ( icmp-type-code | "{" icmp-list "}" ) -icmp6-type = "icmp6-type" ( icmp-type-code | "{" icmp-list "}" ) -icmp-type-code = ( icmp-type-name | icmp-type-number ) - [ "code" ( icmp-code-name | icmp-code-number ) ] -icmp-list = icmp-type-code [ [ "," ] icmp-list ] - -tos = ( "lowdelay" | "throughput" | "reliability" | - [ "0x" ] number ) - -state-opts = state-opt [ [ "," ] state-opts ] -state-opt = ( "max" number | "no-sync" | timeout | "sloppy" | "pflow" | - "source-track" [ ( "rule" | "global" ) ] | - "max-src-nodes" number | "max-src-states" number | - "max-src-conn" number | - "max-src-conn-rate" number "/" number | - "overload" "\*(Lt" string "\*(Gt" [ "flush" ] | - "if-bound" | "floating" ) - -fragmentation = [ "fragment reassemble" | "fragment crop" | - "fragment drop-ovl" ] - -timeout-list = timeout [ [ "," ] timeout-list ] -timeout = ( "tcp.first" | "tcp.opening" | "tcp.established" | - "tcp.closing" | "tcp.finwait" | "tcp.closed" | - "udp.first" | "udp.single" | "udp.multiple" | - "icmp.first" | "icmp.error" | - "other.first" | "other.single" | "other.multiple" | - "frag" | "interval" | "src.track" | - "adaptive.start" | "adaptive.end" ) number - -limit-list = limit-item [ [ "," ] limit-list ] -limit-item = ( "states" | "frags" | "src-nodes" ) number - -pooltype = ( "bitmask" | "random" | - "source-hash" [ ( hex-key | string-key ) ] | - "round-robin" ) [ sticky-address ] - -subqueue = string | "{" queue-list "}" -queue-list = string [ [ "," ] string ] -cbq-def = "cbq" [ "(" cbq-opt [ [ "," ] cbq-opt ] ")" ] -priq-def = "priq" [ "(" priq-opt [ [ "," ] priq-opt ] ")" ] -hfsc-def = "hfsc" [ "(" hfsc-opt [ [ "," ] hfsc-opt ] ")" ] -cbq-opt = ( "default" | "borrow" | "red" | "ecn" | "rio" ) -priq-opt = ( "default" | "red" | "ecn" | "rio" ) -hfsc-opt = ( "default" | "red" | "ecn" | "rio" | - linkshare-sc | realtime-sc | upperlimit-sc ) -linkshare-sc = "linkshare" sc-spec -realtime-sc = "realtime" sc-spec -upperlimit-sc = "upperlimit" sc-spec -sc-spec = ( bandwidth-spec | - "(" bandwidth-spec number bandwidth-spec ")" ) -include = "include" filename -.Ed -.Sh FILES -.Bl -tag -width "/etc/protocols" -compact -.It Pa /etc/hosts -Host name database. -.It Pa /etc/pf.conf -Default location of the ruleset file. -.It Pa /etc/pf.os -Default location of OS fingerprints. -.It Pa /etc/protocols -Protocol name database. -.It Pa /etc/services -Service name database. -.El -.Sh SEE ALSO -.Xr altq 4 , -.Xr carp 4 , -.Xr icmp 4 , -.Xr icmp6 4 , -.Xr ip 4 , -.Xr ip6 4 , -.Xr pf 4 , -.Xr pflow 4 , -.Xr pfsync 4 , -.Xr tcp 4 , -.Xr udp 4 , -.Xr hosts 5 , -.Xr pf.os 5 , -.Xr protocols 5 , -.Xr services 5 , -.Xr ftp-proxy 8 , -.Xr pfctl 8 , -.Xr pflogd 8 , -.Sh HISTORY -The -.Nm -file format first appeared in -.Ox 3.0 . diff --git a/contrib/pf/man/pf.os.5 b/contrib/pf/man/pf.os.5 deleted file mode 100644 index 5930525..0000000 --- a/contrib/pf/man/pf.os.5 +++ /dev/null @@ -1,223 +0,0 @@ -.\" $OpenBSD: pf.os.5,v 1.8 2007/05/31 19:19:58 jmc Exp $ -.\" -.\" Copyright (c) 2003 Mike Frantzen <frantzen@w4g.org> -.\" -.\" Permission to use, copy, modify, and distribute this software for any -.\" purpose with or without fee is hereby granted, provided that the above -.\" copyright notice and this permission notice appear in all copies. -.\" -.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -.\" -.\" $FreeBSD$ -.\" -.Dd May 31 2007 -.Dt PF.OS 5 -.Os -.Sh NAME -.Nm pf.os -.Nd format of the operating system fingerprints file -.Sh DESCRIPTION -The -.Xr pf 4 -firewall and the -.Xr tcpdump 1 -program can both fingerprint the operating system of hosts that -originate an IPv4 TCP connection. -The file consists of newline-separated records, one per fingerprint, -containing nine colon -.Pq Ql \&: -separated fields. -These fields are as follows: -.Pp -.Bl -tag -width Description -offset indent -compact -.It window -The TCP window size. -.It TTL -The IP time to live. -.It df -The presence of the IPv4 don't fragment bit. -.It packet size -The size of the initial TCP packet. -.It TCP options -An ordered list of the TCP options. -.It class -The class of operating system. -.It version -The version of the operating system. -.It subtype -The subtype of patchlevel of the operating system. -.It description -The overall textual description of the operating system, version and subtype. -.El -.Pp -The -.Ar window -field corresponds to the th->th_win field in the TCP header and is the -source host's advertised TCP window size. -It may be between zero and 65,535 inclusive. -The window size may be given as a multiple of a constant by prepending -the size with a percent sign -.Sq % -and the value will be used as a modulus. -Three special values may be used for the window size: -.Pp -.Bl -tag -width xxx -offset indent -compact -.It * -An asterisk will wildcard the value so any window size will match. -.It S -Allow any window size which is a multiple of the maximum segment size (MSS). -.It T -Allow any window size which is a multiple of the maximum transmission unit -(MTU). -.El -.Pp -The -.Ar ttl -value is the initial time to live in the IP header. -The fingerprint code will account for the volatility of the packet's TTL -as it traverses a network. -.Pp -The -.Ar df -bit corresponds to the Don't Fragment bit in an IPv4 header. -It tells intermediate routers not to fragment the packet and is used for -path MTU discovery. -It may be either a zero or a one. -.Pp -The -.Ar packet size -is the literal size of the full IP packet and is a function of all of -the IP and TCP options. -.Pp -The -.Ar TCP options -field is an ordered list of the individual TCP options that appear in the -SYN packet. -Each option is described by a single character separated by a comma and -certain ones may include a value. -The options are: -.Pp -.Bl -tag -width Description -offset indent -compact -.It Mnnn -maximum segment size (MSS) option. -The value is the maximum packet size of the network link which may -include the -.Sq % -modulus or match all MSSes with the -.Sq * -value. -.It N -the NOP option (NO Operation). -.It T[0] -the timestamp option. -Certain operating systems always start with a zero timestamp in which -case a zero value is added to the option; otherwise no value is appended. -.It S -the Selective ACKnowledgement OK (SACKOK) option. -.It Wnnn -window scaling option. -The value is the size of the window scaling which may include the -.Sq % -modulus or match all window scalings with the -.Sq * -value. -.El -.Pp -No TCP options in the fingerprint may be given with a single dot -.Sq \&. . -.Pp -An example of OpenBSD's TCP options are: -.Pp -.Dl M*,N,N,S,N,W0,N,N,T -.Pp -The first option -.Ar M* -is the MSS option and will match all values. -The second and third options -.Ar N -will match two NOPs. -The fourth option -.Ar S -will match the SACKOK option. -The fifth -.Ar N -will match another NOP. -The sixth -.Ar W0 -will match a window scaling option with a zero scaling size. -The seventh and eighth -.Ar N -options will match two NOPs. -And the ninth and final option -.Ar T -will match the timestamp option with any time value. -.Pp -The TCP options in a fingerprint will only match packets with the -exact same TCP options in the same order. -.Pp -The -.Ar class -field is the class, genre or vendor of the operating system. -.Pp -The -.Ar version -is the version of the operating system. -It is used to distinguish between different fingerprints of operating -systems of the same class but different versions. -.Pp -The -.Ar subtype -is the subtype or patch level of the operating system version. -It is used to distinguish between different fingerprints of operating -systems of the same class and same version but slightly different -patches or tweaking. -.Pp -The -.Ar description -is a general description of the operating system, its version, -patchlevel and any further useful details. -.Sh EXAMPLES -The fingerprint of a plain -.Ox 3.3 -host is: -.Bd -literal - 16384:64:1:64:M*,N,N,S,N,W0,N,N,T:OpenBSD:3.3::OpenBSD 3.3 -.Ed -.Pp -The fingerprint of an -.Ox 3.3 -host behind a PF scrubbing firewall with a no-df rule would be: -.Bd -literal - 16384:64:0:64:M*,N,N,S,N,W0,N,N,T:OpenBSD:3.3:!df:OpenBSD 3.3 scrub no-df -.Ed -.Pp -An absolutely braindead embedded operating system fingerprint could be: -.Bd -literal - 65535:255:0:40:.:DUMMY:1.1:p3:Dummy embedded OS v1.1p3 -.Ed -.Pp -The -.Xr tcpdump 1 -output of -.Bd -literal - # tcpdump -s128 -c1 -nv 'tcp[13] == 2' - 03:13:48.118526 10.0.0.1.3377 > 10.0.0.2.80: S [tcp sum ok] \e - 534596083:534596083(0) win 57344 <mss 1460> (DF) [tos 0x10] \e - (ttl 64, id 11315, len 44) -.Ed -.Pp -almost translates into the following fingerprint -.Bd -literal - 57344:64:1:44:M1460: exampleOS:1.0::exampleOS 1.0 -.Ed -.Sh SEE ALSO -.Xr pf 4 , -.Xr pf.conf 5 , -.Xr pfctl 8 , -.Xr tcpdump 1 diff --git a/contrib/pf/man/pflog.4 b/contrib/pf/man/pflog.4 deleted file mode 100644 index c1039a3..0000000 --- a/contrib/pf/man/pflog.4 +++ /dev/null @@ -1,107 +0,0 @@ -.\" $OpenBSD: pflog.4,v 1.10 2007/05/31 19:19:51 jmc Exp $ -.\" -.\" Copyright (c) 2001 Tobias Weingartner -.\" 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. -.\" -.\" $FreeBSD$ -.\" -.Dd May 31 2007 -.Dt PFLOG 4 -.Os -.Sh NAME -.Nm pflog -.Nd packet filter logging interface -.Sh SYNOPSIS -.Cd "device pflog" -.Sh DESCRIPTION -The -.Nm pflog -interface is a device which makes visible all packets logged by -the packet filter, -.Xr pf 4 . -Logged packets can easily be monitored in real -time by invoking -.Xr tcpdump 1 -on the -.Nm -interface, or stored to disk using -.Xr pflogd 8 . -.Pp -The pflog0 interface is created automatically at boot if both -.Xr pf 4 -and -.Xr pflogd 8 -are enabled; -further instances can be created using -.Xr ifconfig 8 . -.Pp -Each packet retrieved on this interface has a header associated -with it of length -.Dv PFLOG_HDRLEN . -This header documents the address family, interface name, rule -number, reason, action, and direction of the packet that was logged. -This structure, defined in -.Aq Pa net/if_pflog.h -looks like -.Bd -literal -offset indent -struct pfloghdr { - u_int8_t length; - sa_family_t af; - u_int8_t action; - u_int8_t reason; - char ifname[IFNAMSIZ]; - char ruleset[PF_RULESET_NAME_SIZE]; - u_int32_t rulenr; - u_int32_t subrulenr; - uid_t uid; - pid_t pid; - uid_t rule_uid; - pid_t rule_pid; - u_int8_t dir; - u_int8_t pad[3]; -}; -.Ed -.Sh EXAMPLES -Create a -.Nm -interface -and monitor all packets logged on it: -.Bd -literal -offset indent -# ifconfig pflog1 up -# tcpdump -n -e -ttt -i pflog1 -.Ed -.Sh SEE ALSO -.Xr inet 4 , -.Xr inet6 4 , -.Xr netintro 4 , -.Xr pf 4 , -.Xr ifconfig 8 , -.Xr pflogd 8 , -.Xr tcpdump 1 -.Sh HISTORY -The -.Nm -device first appeared in -.Ox 3.0 . -.\" .Sh BUGS -.\" Anything here? diff --git a/contrib/pf/man/pfsync.4 b/contrib/pf/man/pfsync.4 deleted file mode 100644 index b00bf9d..0000000 --- a/contrib/pf/man/pfsync.4 +++ /dev/null @@ -1,229 +0,0 @@ -.\" $OpenBSD: pfsync.4,v 1.28 2009/02/17 10:05:18 dlg Exp $ -.\" -.\" Copyright (c) 2002 Michael Shalayeff -.\" Copyright (c) 2003-2004 Ryan McBride -.\" 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 MIND, -.\" 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. -.\" -.\" $FreeBSD$ -.\" -.Dd December 20 2011 -.Dt PFSYNC 4 -.Os -.Sh NAME -.Nm pfsync -.Nd packet filter state table sychronisation interface -.Sh SYNOPSIS -.Cd "device pfsync" -.Sh DESCRIPTION -The -.Nm -interface is a pseudo-device which exposes certain changes to the state -table used by -.Xr pf 4 . -State changes can be viewed by invoking -.Xr tcpdump 1 -on the -.Nm -interface. -If configured with a physical synchronisation interface, -.Nm -will also send state changes out on that interface, -and insert state changes received on that interface from other systems -into the state table. -.Pp -By default, all local changes to the state table are exposed via -.Nm . -State changes from packets received by -.Nm -over the network are not rebroadcast. -Updates to states created by a rule marked with the -.Ar no-sync -keyword are ignored by the -.Nm -interface (see -.Xr pf.conf 5 -for details). -.Pp -The -.Nm -interface will attempt to collapse multiple state updates into a single -packet where possible. -The maximum number of times a single state can be updated before a -.Nm -packet will be sent out is controlled by the -.Ar maxupd -parameter to ifconfig -(see -.Xr ifconfig 8 -and the example below for more details). -The sending out of a -.Nm -packet will be delayed by a maximum of one second. -.Sh NETWORK SYNCHRONISATION -States can be synchronised between two or more firewalls using this -interface, by specifying a synchronisation interface using -.Xr ifconfig 8 . -For example, the following command sets fxp0 as the synchronisation -interface: -.Bd -literal -offset indent -# ifconfig pfsync0 syncdev fxp0 -.Ed -.Pp -By default, state change messages are sent out on the synchronisation -interface using IP multicast packets to the 244.0.0.240 group address. -An alternative destination address for -.Nm -packets can be specified using the -.Ic syncpeer -keyword. -This can be used in combination with -.Xr ipsec 4 -to protect the synchronisation traffic. -In such a configuration, the syncdev should be set to the -.Xr enc 4 -interface, as this is where the traffic arrives when it is decapsulated, -e.g.: -.Bd -literal -offset indent -# ifconfig pfsync0 syncpeer 10.0.0.2 syncdev enc0 -.Ed -.Pp -It is important that the pfsync traffic be well secured -as there is no authentication on the protocol and it would -be trivial to spoof packets which create states, bypassing the pf ruleset. -Either run the pfsync protocol on a trusted network \- ideally a network -dedicated to pfsync messages such as a crossover cable between two firewalls, -or specify a peer address and protect the traffic with -.Xr ipsec 4 . -.Pp -.Nm -has the following -.Xr sysctl 8 -tunables: -.Bl -tag -width ".Va net.pfsync" -.It Va net.pfsync.carp_demotion_factor -Value added to -.Va net.inet.carp.demotion -while -.Nm -tries to perform its bulk update. -See -.Xr carp 4 -for more information. -Default value is 240. -.El -.Sh EXAMPLES -.Nm -and -.Xr carp 4 -can be used together to provide automatic failover of a pair of firewalls -configured in parallel. -One firewall will handle all traffic until it dies, is shut down, or is -manually demoted, at which point the second firewall will take over -automatically. -.Pp -Both firewalls in this example have three -.Xr sis 4 -interfaces. -sis0 is the external interface, on the 10.0.0.0/24 subnet; sis1 is the -internal interface, on the 192.168.0.0/24 subnet; and sis2 is the -.Nm -interface, using the 192.168.254.0/24 subnet. -A crossover cable connects the two firewalls via their sis2 interfaces. -On all three interfaces, firewall A uses the .254 address, while firewall B -uses .253. -The interfaces are configured as follows (firewall A unless otherwise -indicated): -.Pp -Interfaces configuration in -.Pa /etc/rc.conf : -.Bd -literal -offset indent -network_interfaces="lo0 sis0 sis1 sis2" -ifconfig_sis0="10.0.0.254/24" -ifconfig_sis0_alias0="inet 10.0.0.1/24 vhid 1 pass foo" -ifconfig_sis1="192.168.0.254/24" -ifconfig_sis1_alias0="inet 192.168.0.1/24 vhid 2 pass bar" -ifconfig_sis2="192.168.254.254/24" -pfsync_enable="YES" -pfsync_syncdev="sis2" -.Ed -.Pp -.Xr pf 4 -must also be configured to allow -.Nm -and -.Xr carp 4 -traffic through. -The following should be added to the top of -.Pa /etc/pf.conf : -.Bd -literal -offset indent -pass quick on { sis2 } proto pfsync keep state (no-sync) -pass on { sis0 sis1 } proto carp keep state (no-sync) -.Ed -.Pp -It is preferable that one firewall handle the forwarding of all the traffic, -therefore the -.Ar advskew -on the backup firewall's -.Xr carp 4 -vhids should be set to something higher than -the primary's. -For example, if firewall B is the backup, its -carp1 configuration would look like this: -would look like this: -.Bd -literal -offset indent -ifconfig_sis1_alias0="inet 192.168.0.1/24 vhid 2 pass bar advskew 100" -.Ed -.Pp -The following must also be added to -.Pa /etc/sysctl.conf : -.Bd -literal -offset indent -net.inet.carp.preempt=1 -.Ed -.Sh SEE ALSO -.Xr bpf 4 , -.Xr carp 4 , -.Xr enc 4 , -.Xr inet 4 , -.Xr inet6 4 , -.Xr ipsec 4 , -.Xr netintro 4 , -.Xr pf 4 , -.Xr pf.conf 5 , -.Xr protocols 5 , -.Xr rc.conf 5 , -.Xr ifconfig 8 , -.Xr tcpdump 1 -.Sh HISTORY -The -.Nm -device first appeared in -.Ox 3.3 . -It was first imported to -.Fx 5.3 . -.Pp -The -.Nm -protocol and kernel implementation were significantly modified in -.Fx 9.0 . -The newer protocol is not compatible with older one and will not interoperate -with it. diff --git a/contrib/pf/pfctl/parse.y b/contrib/pf/pfctl/parse.y deleted file mode 100644 index 99c26c0..0000000 --- a/contrib/pf/pfctl/parse.y +++ /dev/null @@ -1,6038 +0,0 @@ -/* $OpenBSD: parse.y,v 1.554 2008/10/17 12:59:53 henning 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. - * - * 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. - */ -%{ -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/stat.h> -#ifdef __FreeBSD__ -#include <sys/sysctl.h> -#endif -#include <net/if.h> -#include <netinet/in.h> -#include <netinet/in_systm.h> -#include <netinet/ip.h> -#include <netinet/ip_icmp.h> -#include <netinet/icmp6.h> -#include <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 <unistd.h> -#include <stdlib.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 "pfctl_parser.h" -#include "pfctl.h" - -static struct pfctl *pf = NULL; -static int debug = 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; - -TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files); -static struct file { - TAILQ_ENTRY(file) entry; - FILE *stream; - char *name; - int lineno; - int errors; -} *file; -struct file *pushfile(const char *, int); -int popfile(void); -int check_file_secrecy(int, const char *); -int yyparse(void); -int yylex(void); -int yyerror(const char *, ...); -int kw_cmp(const void *, const void *); -int lookup(char *); -int lgetc(int); -int lungetc(int); -int findeol(void); - -TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead); -struct sym { - TAILQ_ENTRY(sym) entry; - int used; - int persist; - char *nam; - char *val; -}; -int symset(const char *, const char *, int); -char *symget(const char *); - -int atoul(char *, u_long *); - -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; -}; - -struct node_icmp { - u_int8_t code; - u_int8_t type; - u_int8_t proto; - struct node_icmp *next; - struct node_icmp *tail; -}; - -enum { PF_STATE_OPT_MAX, PF_STATE_OPT_NOSYNC, PF_STATE_OPT_SRCTRACK, - PF_STATE_OPT_MAX_SRC_STATES, PF_STATE_OPT_MAX_SRC_CONN, - PF_STATE_OPT_MAX_SRC_CONN_RATE, PF_STATE_OPT_MAX_SRC_NODES, - PF_STATE_OPT_OVERLOAD, PF_STATE_OPT_STATELOCK, - PF_STATE_OPT_TIMEOUT, PF_STATE_OPT_SLOPPY, }; - -enum { PF_SRCTRACK_NONE, PF_SRCTRACK, PF_SRCTRACK_GLOBAL, PF_SRCTRACK_RULE }; - -struct node_state_opt { - int type; - union { - u_int32_t max_states; - u_int32_t max_src_states; - u_int32_t max_src_conn; - struct { - u_int32_t limit; - u_int32_t seconds; - } max_src_conn_rate; - struct { - u_int8_t flush; - char tblname[PF_TABLE_NAME_SIZE]; - } overload; - u_int32_t max_src_nodes; - u_int8_t src_track; - u_int32_t statelock; - struct { - int number; - u_int32_t seconds; - } timeout; - } data; - struct node_state_opt *next; - struct node_state_opt *tail; -}; - -struct peer { - struct node_host *host; - struct node_port *port; -}; - -struct node_queue { - char queue[PF_QNAME_SIZE]; - char parent[PF_QNAME_SIZE]; - char ifname[IFNAMSIZ]; - int scheduler; - struct node_queue *next; - struct node_queue *tail; -} *queues = NULL; - -struct node_qassign { - char *qname; - char *pqname; -}; - -struct filter_opts { - int marker; -#define FOM_FLAGS 0x01 -#define FOM_ICMP 0x02 -#define FOM_TOS 0x04 -#define FOM_KEEP 0x08 -#define FOM_SRCTRACK 0x10 - struct node_uid *uid; - struct node_gid *gid; - struct { - u_int8_t b1; - u_int8_t b2; - u_int16_t w; - u_int16_t w2; - } flags; - struct node_icmp *icmpspec; - u_int32_t tos; - u_int32_t prob; - struct { - int action; - struct node_state_opt *options; - } keep; - int fragment; - int allowopts; - char *label; - struct node_qassign queues; - char *tag; - char *match_tag; - u_int8_t match_tag_not; - u_int rtableid; - struct { - struct node_host *addr; - u_int16_t port; - } divert; -} filter_opts; - -struct antispoof_opts { - char *label; - u_int rtableid; -} antispoof_opts; - -struct scrub_opts { - int marker; -#define SOM_MINTTL 0x01 -#define SOM_MAXMSS 0x02 -#define SOM_FRAGCACHE 0x04 -#define SOM_SETTOS 0x08 - int nodf; - int minttl; - int maxmss; - int settos; - int fragcache; - int randomid; - int reassemble_tcp; - char *match_tag; - u_int8_t match_tag_not; - u_int rtableid; -} 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; -struct node_state_opt *keep_state_defaults = NULL; - -int disallow_table(struct node_host *, const char *); -int disallow_urpf_failed(struct node_host *, const char *); -int disallow_alias(struct node_host *, const char *); -int rule_consistent(struct pf_rule *, int); -int filter_consistent(struct pf_rule *, int); -int nat_consistent(struct pf_rule *); -int rdr_consistent(struct pf_rule *); -int process_tabledef(char *, struct table_opts *); -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 expand_skip_interface(struct node_if *); - -int check_rulestate(int); -int getservice(char *); -int rule_label(struct pf_rule *, char *); -int rt_tableid_max(void); - -void mv_rules(struct pf_ruleset *, struct pf_ruleset *); -void decide_address_family(struct node_host *, sa_family_t *); -void remove_invalid_hosts(struct node_host **, sa_family_t *); -int invalid_redirect(struct node_host *, sa_family_t); -u_int16_t parseicmpspec(char *, sa_family_t); - -TAILQ_HEAD(loadanchorshead, loadanchors) - loadanchorshead = TAILQ_HEAD_INITIALIZER(loadanchorshead); - -struct loadanchors { - TAILQ_ENTRY(loadanchors) entries; - char *anchorname; - char *filename; -}; - -typedef struct { - union { - int64_t number; - double probability; - int i; - char *string; - u_int rtableid; - 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 logif; - u_int8_t quick; - } logquick; - struct { - int neg; - char *name; - } tagged; - 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 PPORT_RANGE 1 -#define PPORT_STAR 2 -int parseport(char *, struct range *r, int); - -#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 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 URPFFAILED 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 SKIP HOSTID -%token ANTISPOOF FOR INCLUDE -%token BITMASK RANDOM SOURCEHASH ROUNDROBIN STATICPORT PROBABILITY -%token ALTQ CBQ PRIQ HFSC BANDWIDTH TBRSIZE LINKSHARE REALTIME UPPERLIMIT -%token QUEUE PRIORITY QLIMIT RTABLE -%token LOAD RULESET_OPTIMIZATION -%token STICKYADDRESS MAXSRCSTATES MAXSRCNODES SOURCETRACK GLOBAL RULE -%token MAXSRCCONN MAXSRCCONNRATE OVERLOAD FLUSH SLOPPY -%token TAGGED TAG IFBOUND FLOATING STATEPOLICY STATEDEFAULTS ROUTE SETTOS -%token DIVERTTO DIVERTREPLY -%token <v.string> STRING -%token <v.number> NUMBER -%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 -%type <v.probability> probability -%type <v.i> no dir af fragcache optimizer -%type <v.i> sourcetrack flush unaryop statelock -%type <v.b> action nataction natpasslog scrubaction -%type <v.b> flags flag blockspec -%type <v.range> portplain portstar portrange -%type <v.hashkey> hashkey -%type <v.proto> proto proto_list proto_item -%type <v.number> protoval -%type <v.icmp> icmpspec -%type <v.icmp> icmp_list icmp_item -%type <v.icmp> icmp6_list icmp6_item -%type <v.number> reticmpspec reticmp6spec -%type <v.fromto> fromto -%type <v.peer> ipportspec from to -%type <v.host> ipspec toipspec xhost host dynaddr host_list -%type <v.host> redir_host_list redirspec -%type <v.host> route_host route_host_list routespec -%type <v.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 stringall tag anchorname -%type <v.string> string varstring numberstring -%type <v.keep_state> keep -%type <v.state_opt> state_opt_spec state_opt_list state_opt_item -%type <v.logquick> logquick quick log logopts logopt -%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 -%type <v.tagged> tagged -%type <v.rtableid> rtable -%% - -ruleset : /* empty */ - | ruleset include '\n' - | 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' - | '{' fakeanchor '}' '\n'; - | ruleset error '\n' { file->errors++; } - ; - -include : INCLUDE STRING { - struct file *nfile; - - if ((nfile = pushfile($2, 0)) == NULL) { - yyerror("failed to include file %s", $2); - free($2); - YYERROR; - } - free($2); - - file = nfile; - lungetc('\n'); - } - ; - -/* - * apply to previouslys specified rule: must be careful to note - * what that is: pf or nat or binat or rdr - */ -fakeanchor : fakeanchor '\n' - | fakeanchor anchorrule '\n' - | fakeanchor binatrule '\n' - | fakeanchor natrule '\n' - | fakeanchor pfrule '\n' - | fakeanchor error '\n' - ; - -optimizer : string { - if (!strcmp($1, "none")) - $$ = 0; - else if (!strcmp($1, "basic")) - $$ = PF_OPTIMIZE_BASIC; - else if (!strcmp($1, "profile")) - $$ = PF_OPTIMIZE_BASIC | PF_OPTIMIZE_PROFILE; - else { - yyerror("unknown ruleset-optimization %s", $1); - YYERROR; - } - } - ; - -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 RULESET_OPTIMIZATION optimizer { - if (!(pf->opts & PF_OPT_OPTIMIZE)) { - pf->opts |= PF_OPT_OPTIMIZE; - pf->optimize = $3; - } - } - | SET TIMEOUT timeout_spec - | SET TIMEOUT '{' optnl timeout_list '}' - | SET LIMIT limit_spec - | SET LIMIT '{' optnl limit_list '}' - | SET LOGINTERFACE stringall { - 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 || $3 > UINT_MAX) { - 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("set fingerprints \"%s\"\n", $3); - if (check_rulestate(PFCTL_STATE_OPTION)) { - free($3); - YYERROR; - } - if (!pf->anchor->name[0]) { - 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; - } - 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); - } - | SET SKIP interface { - if (expand_skip_interface($3) != 0) { - yyerror("error setting skip interface(s)"); - YYERROR; - } - } - | SET STATEDEFAULTS state_opt_list { - if (keep_state_defaults != NULL) { - yyerror("cannot redefine state-defaults"); - YYERROR; - } - keep_state_defaults = $3; - } - ; - -stringall : STRING { $$ = $1; } - | ALL { - if (($$ = strdup("all")) == NULL) { - err(1, "stringall: strdup"); - } - } - ; - -string : STRING string { - if (asprintf(&$$, "%s %s", $1, $2) == -1) - err(1, "string: asprintf"); - free($1); - free($2); - } - | STRING - ; - -varstring : numberstring varstring { - if (asprintf(&$$, "%s %s", $1, $2) == -1) - err(1, "string: asprintf"); - free($1); - free($2); - } - | numberstring - ; - -numberstring : NUMBER { - char *s; - if (asprintf(&s, "%lld", (long long)$1) == -1) { - yyerror("string: asprintf"); - YYERROR; - } - $$ = s; - } - | STRING - ; - -varset : STRING '=' varstring { - 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); - } - ; - -anchorname : STRING { $$ = $1; } - | /* empty */ { $$ = NULL; } - ; - -pfa_anchorlist : /* empty */ - | pfa_anchorlist '\n' - | pfa_anchorlist pfrule '\n' - | pfa_anchorlist anchorrule '\n' - ; - -pfa_anchor : '{' - { - char ta[PF_ANCHOR_NAME_SIZE]; - struct pf_ruleset *rs; - - /* steping into a brace anchor */ - pf->asd++; - pf->bn++; - pf->brace = 1; - - /* create a holding ruleset in the root */ - snprintf(ta, PF_ANCHOR_NAME_SIZE, "_%d", pf->bn); - rs = pf_find_or_create_ruleset(ta); - if (rs == NULL) - err(1, "pfa_anchor: pf_find_or_create_ruleset"); - pf->astack[pf->asd] = rs->anchor; - pf->anchor = rs->anchor; - } '\n' pfa_anchorlist '}' - { - pf->alast = pf->anchor; - pf->asd--; - pf->anchor = pf->astack[pf->asd]; - } - | /* empty */ - ; - -anchorrule : ANCHOR anchorname dir quick interface af proto fromto - filter_opts pfa_anchor - { - struct pf_rule r; - struct node_proto *proto; - - if (check_rulestate(PFCTL_STATE_FILTER)) { - if ($2) - free($2); - YYERROR; - } - - if ($2 && ($2[0] == '_' || strstr($2, "/_") != NULL)) { - free($2); - yyerror("anchor names beginning with '_' " - "are reserved for internal use"); - YYERROR; - } - - memset(&r, 0, sizeof(r)); - if (pf->astack[pf->asd + 1]) { - /* move inline rules into relative location */ - pf_anchor_setup(&r, - &pf->astack[pf->asd]->ruleset, - $2 ? $2 : pf->alast->name); - - if (r.anchor == NULL) - err(1, "anchorrule: unable to " - "create ruleset"); - - if (pf->alast != r.anchor) { - if (r.anchor->match) { - yyerror("inline anchor '%s' " - "already exists", - r.anchor->name); - YYERROR; - } - mv_rules(&pf->alast->ruleset, - &r.anchor->ruleset); - } - pf_remove_if_empty_ruleset(&pf->alast->ruleset); - pf->alast = r.anchor; - } else { - if (!$2) { - yyerror("anchors without explicit " - "rules must specify a name"); - YYERROR; - } - } - r.direction = $3; - r.quick = $4.quick; - r.af = $6; - r.prob = $9.prob; - r.rtableid = $9.rtableid; - - if ($9.tag) - if (strlcpy(r.tagname, $9.tag, - PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) { - yyerror("tag too long, max %u chars", - PF_TAG_NAME_SIZE - 1); - YYERROR; - } - if ($9.match_tag) - if (strlcpy(r.match_tagname, $9.match_tag, - PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) { - yyerror("tag too long, max %u chars", - PF_TAG_NAME_SIZE - 1); - YYERROR; - } - r.match_tag_not = $9.match_tag_not; - if (rule_label(&r, $9.label)) - YYERROR; - free($9.label); - r.flags = $9.flags.b1; - r.flagset = $9.flags.b2; - if (($9.flags.b1 & $9.flags.b2) != $9.flags.b1) { - yyerror("flags always false"); - YYERROR; - } - if ($9.flags.b1 || $9.flags.b2 || $8.src_os) { - for (proto = $7; proto != NULL && - proto->proto != IPPROTO_TCP; - 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 " - "applies to tcp"); - YYERROR; - } - } - - r.tos = $9.tos; - - if ($9.keep.action) { - yyerror("cannot specify state handling " - "on anchors"); - 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; - - decide_address_family($8.src.host, &r.af); - decide_address_family($8.dst.host, &r.af); - - expand_rule(&r, $5, NULL, $7, $8.src_os, - $8.src.host, $8.src.port, $8.dst.host, $8.dst.port, - $9.uid, $9.gid, $9.icmpspec, - pf->astack[pf->asd + 1] ? pf->alast->name : $2); - free($2); - pf->astack[pf->asd + 1] = NULL; - } - | NATANCHOR string interface af proto fromto rtable { - 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; - r.rtableid = $7; - - 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 rtable { - 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; - r.rtableid = $7; - - 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 rtable { - 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; - r.rtableid = $7; - 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(pf->anchor->name) + 1 + - 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 = malloc(MAXPATHLEN)) == - NULL) - err(1, "loadrule: malloc"); - if (pf->anchor->name[0]) - snprintf(loadanchor->anchorname, MAXPATHLEN, - "%s/%s", pf->anchor->name, $3); - else - strlcpy(loadanchor->anchorname, $3, MAXPATHLEN); - 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; - r.logif = $3.logif; - 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.marker & SOM_SETTOS) { - r.rule_flag |= PFRULE_SET_TOS; - r.set_tos = $8.settos; - } - if ($8.fragcache) - r.rule_flag |= $8.fragcache; - 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; - r.rtableid = $8.rtableid; - - expand_rule(&r, $4, NULL, $6, $7.src_os, - $7.src.host, $7.src.port, $7.dst.host, $7.dst.port, - NULL, NULL, NULL, ""); - } - ; - -scrub_opts : { - bzero(&scrub_opts, sizeof scrub_opts); - scrub_opts.rtableid = -1; - } - scrub_opts_l - { $$ = scrub_opts; } - | /* empty */ { - bzero(&scrub_opts, sizeof scrub_opts); - scrub_opts.rtableid = -1; - $$ = 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 < 0 || $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 < 0 || $2 > 65535) { - yyerror("illegal max-mss value %d", $2); - YYERROR; - } - scrub_opts.marker |= SOM_MAXMSS; - scrub_opts.maxmss = $2; - } - | SETTOS tos { - if (scrub_opts.marker & SOM_SETTOS) { - yyerror("set-tos cannot be respecified"); - YYERROR; - } - scrub_opts.marker |= SOM_SETTOS; - scrub_opts.settos = $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) { - yyerror("scrub reassemble supports only tcp, " - "not '%s'", $2); - 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; - } - | RTABLE NUMBER { - if ($2 < 0 || $2 > rt_tableid_max()) { - yyerror("invalid rtable id"); - YYERROR; - } - scrub_opts.rtableid = $2; - } - | not TAGGED string { - scrub_opts.match_tag = $3; - scrub_opts.match_tag_not = $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.logif = $2.logif; - r.quick = $2.quick; - r.af = $4; - if (rule_label(&r, $5.label)) - YYERROR; - r.rtableid = $5.rtableid; - 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)) { - free(h); - 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.logif = $2.logif; - r.quick = $2.quick; - r.af = $4; - if (rule_label(&r, $5.label)) - YYERROR; - r.rtableid = $5.rtableid; - 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 '{' optnl antispoof_iflst '}' { $$ = $4; } - ; - -antispoof_iflst : antispoof_if optnl { $$ = $1; } - | antispoof_iflst comma antispoof_if optnl { - $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.rtableid = -1; - } - antispoof_opts_l - { $$ = antispoof_opts; } - | /* empty */ { - bzero(&antispoof_opts, sizeof antispoof_opts); - antispoof_opts.rtableid = -1; - $$ = 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; - } - | RTABLE NUMBER { - if ($2 < 0 || $2 > rt_tableid_max()) { - yyerror("invalid rtable id"); - YYERROR; - } - antispoof_opts.rtableid = $2; - } - ; - -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 if (!strcmp($1, "counters")) - table_opts.flags |= PFR_TFLAG_COUNTERS; - else { - yyerror("invalid table option '%s'", $1); - free($1); - YYERROR; - } - free($1); - } - | '{' optnl '}' { table_opts.init_addr = 1; } - | '{' optnl host_list '}' { - struct node_host *n; - struct node_tinit *ti; - - for (n = $3; n != NULL; n = n->next) { - switch (n->addr.type) { - case PF_ADDR_ADDRMASK: - continue; /* ok */ - case PF_ADDR_RANGE: - yyerror("address ranges are not " - "permitted inside tables"); - break; - 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; - case PF_ADDR_URPFFAILED: - yyerror("\"urpf-failed\" 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 = $3; - 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 < 0 || $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 < 0 || $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 < 0 || $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; - } - | NUMBER { - if ($1 < 0 || $1 > UINT_MAX) { - yyerror("bandwidth number too big"); - YYERROR; - } - $$.bw_percent = 0; - $$.bw_absolute = $1; - } - ; - -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 comma NUMBER comma bandwidth ')' - { - if ($5 < 0 || $5 > INT_MAX) { - yyerror("timing in curve out of range"); - YYERROR; - } - if (hfsc_opts.linkshare.used) { - yyerror("linkshare already specified"); - YYERROR; - } - hfsc_opts.linkshare.m1 = $3; - hfsc_opts.linkshare.d = $5; - hfsc_opts.linkshare.m2 = $7; - 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 comma NUMBER comma bandwidth ')' - { - if ($5 < 0 || $5 > INT_MAX) { - yyerror("timing in curve out of range"); - YYERROR; - } - if (hfsc_opts.realtime.used) { - yyerror("realtime already specified"); - YYERROR; - } - hfsc_opts.realtime.m1 = $3; - hfsc_opts.realtime.d = $5; - hfsc_opts.realtime.m2 = $7; - 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 comma NUMBER comma bandwidth ')' - { - if ($5 < 0 || $5 > INT_MAX) { - yyerror("timing in curve out of range"); - YYERROR; - } - if (hfsc_opts.upperlimit.used) { - yyerror("upperlimit already specified"); - YYERROR; - } - hfsc_opts.upperlimit.m1 = $3; - hfsc_opts.upperlimit.d = $5; - hfsc_opts.upperlimit.m2 = $7; - 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; } - | '{' optnl qassign_list '}' { $$ = $3; } - ; - -qassign_list : qassign_item optnl { $$ = $1; } - | qassign_list comma qassign_item optnl { - $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; - int adaptive = 0; - int defaults = 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.logif = $3.logif; - r.quick = $3.quick; - r.prob = $9.prob; - r.rtableid = $9.rtableid; - - r.af = $6; - if ($9.tag) - if (strlcpy(r.tagname, $9.tag, - PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) { - yyerror("tag too long, max %u chars", - PF_TAG_NAME_SIZE - 1); - YYERROR; - } - if ($9.match_tag) - if (strlcpy(r.match_tagname, $9.match_tag, - PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) { - yyerror("tag too long, max %u chars", - PF_TAG_NAME_SIZE - 1); - YYERROR; - } - r.match_tag_not = $9.match_tag_not; - if (rule_label(&r, $9.label)) - YYERROR; - free($9.label); - r.flags = $9.flags.b1; - r.flagset = $9.flags.b2; - if (($9.flags.b1 & $9.flags.b2) != $9.flags.b1) { - yyerror("flags always false"); - YYERROR; - } - if ($9.flags.b1 || $9.flags.b2 || $8.src_os) { - for (proto = $7; proto != NULL && - proto->proto != IPPROTO_TCP; - 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; - - /* 'keep state' by default on pass rules. */ - if (!r.keep_state && !r.action && - !($9.marker & FOM_KEEP)) { - r.keep_state = PF_STATE_NORMAL; - o = keep_state_defaults; - defaults = 1; - } - - 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; - r.rule_flag |= PFRULE_SRCTRACK; - 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_SLOPPY: - if (r.rule_flag & PFRULE_STATESLOPPY) { - yyerror("state sloppy option: " - "multiple definitions"); - YYERROR; - } - r.rule_flag |= PFRULE_STATESLOPPY; - break; - case PF_STATE_OPT_TIMEOUT: - if (o->data.timeout.number == - PFTM_ADAPTIVE_START || - o->data.timeout.number == - PFTM_ADAPTIVE_END) - adaptive = 1; - 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; - if (!defaults) - free(p); - } - - /* 'flags S/SA' by default on stateful rules */ - if (!r.action && !r.flags && !r.flagset && - !$9.fragment && !($9.marker & FOM_FLAGS) && - r.keep_state) { - r.flags = parse_flags("S"); - r.flagset = parse_flags("SA"); - } - if (!adaptive && r.max_states) { - r.timeout[PFTM_ADAPTIVE_START] = - (r.max_states / 10) * 6; - r.timeout[PFTM_ADAPTIVE_END] = - (r.max_states / 10) * 12; - } - 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_conn) { - yyerror("'max-src-conn' is " - "incompatible with " - "'source-track global'"); - YYERROR; - } - if (srctrack == PF_SRCTRACK_GLOBAL && - r.max_src_conn_rate.seconds) { - 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); - } -#ifdef __FreeBSD__ - r.divert.port = $9.divert.port; -#else - if ((r.divert.port = $9.divert.port)) { - if (r.direction == PF_OUT) { - if ($9.divert.addr) { - yyerror("address specified " - "for outgoing divert"); - YYERROR; - } - bzero(&r.divert.addr, - sizeof(r.divert.addr)); - } else { - if (!$9.divert.addr) { - yyerror("no address specified " - "for incoming divert"); - YYERROR; - } - if ($9.divert.addr->af != r.af) { - yyerror("address family " - "mismatch for divert"); - YYERROR; - } - r.divert.addr = - $9.divert.addr->addr.v.a.addr; - } - } -#endif - - 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.rtableid = -1; - } - filter_opts_l - { $$ = filter_opts; } - | /* empty */ { - bzero(&filter_opts, sizeof filter_opts); - filter_opts.rtableid = -1; - $$ = 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 tos { - if (filter_opts.marker & FOM_TOS) { - yyerror("tos cannot be redefined"); - YYERROR; - } - filter_opts.marker |= FOM_TOS; - filter_opts.tos = $2; - } - | 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 probability { - double p; - - p = floor($2 * UINT_MAX + 0.5); - if (p < 0.0 || p > UINT_MAX) { - yyerror("invalid probability: %lf", p); - YYERROR; - } - filter_opts.prob = (u_int32_t)p; - if (filter_opts.prob == 0) - filter_opts.prob = 1; - } - | RTABLE NUMBER { - if ($2 < 0 || $2 > rt_tableid_max()) { - yyerror("invalid rtable id"); - YYERROR; - } - filter_opts.rtableid = $2; - } - | DIVERTTO portplain { -#ifdef __FreeBSD__ - filter_opts.divert.port = $2.a; - if (!filter_opts.divert.port) { - yyerror("invalid divert port: %u", ntohs($2.a)); - YYERROR; - } -#endif - } - | DIVERTTO STRING PORT portplain { -#ifndef __FreeBSD__ - if ((filter_opts.divert.addr = host($2)) == NULL) { - yyerror("could not parse divert address: %s", - $2); - free($2); - YYERROR; - } -#else - if ($2) -#endif - free($2); - filter_opts.divert.port = $4.a; - if (!filter_opts.divert.port) { - yyerror("invalid divert port: %u", ntohs($4.a)); - YYERROR; - } - } - | DIVERTREPLY { -#ifdef __FreeBSD__ - yyerror("divert-reply has no meaning in FreeBSD pf(4)"); - YYERROR; -#else - filter_opts.divert.port = 1; /* some random value */ -#endif - } - ; - -probability : STRING { - char *e; - double p = strtod($1, &e); - - if (*e == '%') { - p *= 0.01; - e++; - } - if (*e) { - yyerror("invalid probability: %s", $1); - free($1); - YYERROR; - } - free($1); - $$ = p; - } - | NUMBER { - $$ = (double)$1; - } - ; - - -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 < 0 || $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 '(' reticmpspec ')' { - $$.b2 = PFRULE_RETURNICMP; - $$.w = $3; - $$.w2 = returnicmpdefault; - } - | RETURNICMP6 '(' reticmp6spec ')' { - $$.b2 = PFRULE_RETURNICMP; - $$.w = returnicmpdefault; - $$.w2 = $3; - } - | RETURNICMP '(' reticmpspec comma reticmp6spec ')' { - $$.b2 = PFRULE_RETURNICMP; - $$.w = $3; - $$.w2 = $5; - } - | RETURN { - $$.b2 = PFRULE_RETURN; - $$.w = returnicmpdefault; - $$.w2 = returnicmp6default; - } - ; - -reticmpspec : STRING { - if (!($$ = parseicmpspec($1, AF_INET))) { - free($1); - YYERROR; - } - free($1); - } - | NUMBER { - u_int8_t icmptype; - - if ($1 < 0 || $1 > 255) { - yyerror("invalid icmp code %lu", $1); - YYERROR; - } - icmptype = returnicmpdefault >> 8; - $$ = (icmptype << 8 | $1); - } - ; - -reticmp6spec : STRING { - if (!($$ = parseicmpspec($1, AF_INET6))) { - free($1); - YYERROR; - } - free($1); - } - | NUMBER { - u_int8_t icmptype; - - if ($1 < 0 || $1 > 255) { - yyerror("invalid icmp code %lu", $1); - YYERROR; - } - icmptype = returnicmp6default >> 8; - $$ = (icmptype << 8 | $1); - } - ; - -dir : /* empty */ { $$ = PF_INOUT; } - | IN { $$ = PF_IN; } - | OUT { $$ = PF_OUT; } - ; - -quick : /* empty */ { $$.quick = 0; } - | QUICK { $$.quick = 1; } - ; - -logquick : /* empty */ { $$.log = 0; $$.quick = 0; $$.logif = 0; } - | log { $$ = $1; $$.quick = 0; } - | QUICK { $$.quick = 1; $$.log = 0; $$.logif = 0; } - | log QUICK { $$ = $1; $$.quick = 1; } - | QUICK log { $$ = $2; $$.quick = 1; } - ; - -log : LOG { $$.log = PF_LOG; $$.logif = 0; } - | LOG '(' logopts ')' { - $$.log = PF_LOG | $3.log; - $$.logif = $3.logif; - } - ; - -logopts : logopt { $$ = $1; } - | logopts comma logopt { - $$.log = $1.log | $3.log; - $$.logif = $3.logif; - if ($$.logif == 0) - $$.logif = $1.logif; - } - ; - -logopt : ALL { $$.log = PF_LOG_ALL; $$.logif = 0; } - | USER { $$.log = PF_LOG_SOCKET_LOOKUP; $$.logif = 0; } - | GROUP { $$.log = PF_LOG_SOCKET_LOOKUP; $$.logif = 0; } - | TO string { - const char *errstr; - u_int i; - - $$.log = 0; - if (strncmp($2, "pflog", 5)) { - yyerror("%s: should be a pflog interface", $2); - free($2); - YYERROR; - } - i = strtonum($2 + 5, 0, 255, &errstr); - if (errstr) { - yyerror("%s: %s", $2, errstr); - free($2); - YYERROR; - } - free($2); - $$.logif = i; - } - ; - -interface : /* empty */ { $$ = NULL; } - | ON if_item_not { $$ = $2; } - | ON '{' optnl if_list '}' { $$ = $4; } - ; - -if_list : if_item_not optnl { $$ = $1; } - | if_list comma if_item_not optnl { - $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)) != 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 '{' optnl proto_list '}' { $$ = $4; } - ; - -proto_list : proto_item optnl { $$ = $1; } - | proto_list comma proto_item optnl { - $1->tail->next = $3; - $1->tail = $3; - $$ = $1; - } - ; - -proto_item : protoval { - u_int8_t pr; - - pr = (u_int8_t)$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 = $$; - } - ; - -protoval : STRING { - struct protoent *p; - - p = getprotobyname($1); - if (p == NULL) { - yyerror("unknown protocol %s", $1); - free($1); - YYERROR; - } - $$ = p->p_proto; - free($1); - } - | NUMBER { - if ($1 < 0 || $1 > 255) { - yyerror("protocol outside range"); - YYERROR; - } - } - ; - -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 '{' optnl os_list '}' { $$ = $4; } - ; - -xos : STRING { - $$ = calloc(1, sizeof(struct node_os)); - if ($$ == NULL) - err(1, "os: calloc"); - $$->os = $1; - $$->tail = $$; - } - ; - -os_list : xos optnl { $$ = $1; } - | os_list comma xos optnl { - $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 { - if (disallow_urpf_failed($2.host, "\"urpf-failed\" is " - "not permitted in a destination address")) - YYERROR; - $$ = $2; - } - ; - -ipportspec : ipspec { - $$.host = $1; - $$.port = NULL; - } - | ipspec PORT portspec { - $$.host = $1; - $$.port = $3; - } - | PORT portspec { - $$.host = NULL; - $$.port = $2; - } - ; - -optnl : '\n' optnl - | - ; - -ipspec : ANY { $$ = NULL; } - | xhost { $$ = $1; } - | '{' optnl host_list '}' { $$ = $3; } - ; - -toipspec : TO ipspec { $$ = $2; } - | /* empty */ { $$ = NULL; } - ; - -host_list : ipspec optnl { $$ = $1; } - | host_list comma ipspec optnl { - 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; - } - | not NOROUTE { - $$ = calloc(1, sizeof(struct node_host)); - if ($$ == NULL) - err(1, "xhost: calloc"); - $$->addr.type = PF_ADDR_NOROUTE; - $$->next = NULL; - $$->not = $1; - $$->tail = $$; - } - | not URPFFAILED { - $$ = calloc(1, sizeof(struct node_host)); - if ($$ == NULL) - err(1, "xhost: calloc"); - $$->addr.type = PF_ADDR_URPFFAILED; - $$->next = NULL; - $$->not = $1; - $$->tail = $$; - } - ; - -host : STRING { - if (($$ = host($1)) == NULL) { - /* error. "any" is handled elsewhere */ - free($1); - yyerror("could not parse host specification"); - YYERROR; - } - free($1); - - } - | STRING '-' STRING { - struct node_host *b, *e; - - if ((b = host($1)) == NULL || (e = host($3)) == NULL) { - free($1); - free($3); - yyerror("could not parse host specification"); - YYERROR; - } - if (b->af != e->af || - b->addr.type != PF_ADDR_ADDRMASK || - e->addr.type != PF_ADDR_ADDRMASK || - unmask(&b->addr.v.a.mask, b->af) != - (b->af == AF_INET ? 32 : 128) || - unmask(&e->addr.v.a.mask, e->af) != - (e->af == AF_INET ? 32 : 128) || - b->next != NULL || b->not || - e->next != NULL || e->not) { - free(b); - free(e); - free($1); - free($3); - yyerror("invalid address range"); - YYERROR; - } - memcpy(&b->addr.v.a.mask, &e->addr.v.a.addr, - sizeof(b->addr.v.a.mask)); - b->addr.type = PF_ADDR_RANGE; - $$ = b; - free(e); - free($1); - free($3); - } - | STRING '/' NUMBER { - char *buf; - - if (asprintf(&buf, "%s/%lld", $1, (long long)$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); - } - | NUMBER '/' NUMBER { - char *buf; - - /* ie. for 10/8 parsing */ -#ifdef __FreeBSD__ - if (asprintf(&buf, "%lld/%lld", (long long)$1, (long long)$3) == -1) -#else - if (asprintf(&buf, "%lld/%lld", $1, $3) == -1) -#endif - err(1, "host: asprintf"); - 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; - - if ($3 < 0 || $3 > 128) { - yyerror("bit number too big"); - YYERROR; - } - $$ = $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 : 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; - if (!isalpha(op[0])) { - yyerror("invalid interface name '%s'", op); - free(op); - YYERROR; - } - 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; } - | '{' optnl port_list '}' { $$ = $3; } - ; - -port_list : port_item optnl { $$ = $1; } - | port_list comma port_item optnl { - $1->tail->next = $3; - $1->tail = $3; - $$ = $1; - } - ; - -port_item : portrange { - $$ = 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 portrange { - 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 = $$; - } - | portrange PORTBINARY portrange { - 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 = $$; - } - ; - -portplain : numberstring { - if (parseport($1, &$$, 0) == -1) { - free($1); - YYERROR; - } - free($1); - } - ; - -portrange : numberstring { - if (parseport($1, &$$, PPORT_RANGE) == -1) { - free($1); - YYERROR; - } - free($1); - } - ; - -uids : uid_item { $$ = $1; } - | '{' optnl uid_list '}' { $$ = $3; } - ; - -uid_list : uid_item optnl { $$ = $1; } - | uid_list comma uid_item optnl { - $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 { - 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; - } - free($1); - } - | NUMBER { - if ($1 < 0 || $1 >= UID_MAX) { - yyerror("illegal uid value %lu", $1); - YYERROR; - } - $$ = $1; - } - ; - -gids : gid_item { $$ = $1; } - | '{' optnl gid_list '}' { $$ = $3; } - ; - -gid_list : gid_item optnl { $$ = $1; } - | gid_list comma gid_item optnl { - $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 { - 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; - } - free($1); - } - | NUMBER { - if ($1 < 0 || $1 >= GID_MAX) { - yyerror("illegal gid value %lu", $1); - YYERROR; - } - $$ = $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; } - | FLAGS ANY { $$.b1 = 0; $$.b2 = 0; } - ; - -icmpspec : ICMPTYPE icmp_item { $$ = $2; } - | ICMPTYPE '{' optnl icmp_list '}' { $$ = $4; } - | ICMP6TYPE icmp6_item { $$ = $2; } - | ICMP6TYPE '{' optnl icmp6_list '}' { $$ = $4; } - ; - -icmp_list : icmp_item optnl { $$ = $1; } - | icmp_list comma icmp_item optnl { - $1->tail->next = $3; - $1->tail = $3; - $$ = $1; - } - ; - -icmp6_list : icmp6_item optnl { $$ = $1; } - | icmp6_list comma icmp6_item optnl { - $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; - - if ((p = geticmpcodebyname($1-1, $3, AF_INET)) == NULL) { - yyerror("unknown icmp-code %s", $3); - free($3); - YYERROR; - } - - free($3); - $$ = calloc(1, sizeof(struct node_icmp)); - if ($$ == NULL) - err(1, "icmp_item: calloc"); - $$->type = $1; - $$->code = p->code + 1; - $$->proto = IPPROTO_ICMP; - $$->next = NULL; - $$->tail = $$; - } - | icmptype CODE NUMBER { - if ($3 < 0 || $3 > 255) { - yyerror("illegal icmp-code %lu", $3); - YYERROR; - } - $$ = calloc(1, sizeof(struct node_icmp)); - if ($$ == NULL) - err(1, "icmp_item: calloc"); - $$->type = $1; - $$->code = $3 + 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; - - if ((p = geticmpcodebyname($1-1, $3, AF_INET6)) == NULL) { - yyerror("unknown icmp6-code %s", $3); - free($3); - YYERROR; - } - free($3); - - $$ = calloc(1, sizeof(struct node_icmp)); - if ($$ == NULL) - err(1, "icmp_item: calloc"); - $$->type = $1; - $$->code = p->code + 1; - $$->proto = IPPROTO_ICMPV6; - $$->next = NULL; - $$->tail = $$; - } - | icmp6type CODE NUMBER { - if ($3 < 0 || $3 > 255) { - yyerror("illegal icmp-code %lu", $3); - YYERROR; - } - $$ = calloc(1, sizeof(struct node_icmp)); - if ($$ == NULL) - err(1, "icmp_item: calloc"); - $$->type = $1; - $$->code = $3 + 1; - $$->proto = IPPROTO_ICMPV6; - $$->next = NULL; - $$->tail = $$; - } - ; - -icmptype : STRING { - const struct icmptypeent *p; - - if ((p = geticmptypebyname($1, AF_INET)) == NULL) { - yyerror("unknown icmp-type %s", $1); - free($1); - YYERROR; - } - $$ = p->type + 1; - free($1); - } - | NUMBER { - if ($1 < 0 || $1 > 255) { - yyerror("illegal icmp-type %lu", $1); - YYERROR; - } - $$ = $1 + 1; - } - ; - -icmp6type : STRING { - const struct icmptypeent *p; - - if ((p = geticmptypebyname($1, AF_INET6)) == - NULL) { - yyerror("unknown icmp6-type %s", $1); - free($1); - YYERROR; - } - $$ = p->type + 1; - free($1); - } - | NUMBER { - if ($1 < 0 || $1 > 255) { - yyerror("illegal icmp6-type %lu", $1); - YYERROR; - } - $$ = $1 + 1; - } - ; - -tos : STRING { - if (!strcmp($1, "lowdelay")) - $$ = IPTOS_LOWDELAY; - else if (!strcmp($1, "throughput")) - $$ = IPTOS_THROUGHPUT; - else if (!strcmp($1, "reliability")) - $$ = IPTOS_RELIABILITY; - else if ($1[0] == '0' && $1[1] == 'x') - $$ = strtoul($1, NULL, 16); - else - $$ = 0; /* flag bad argument */ - if (!$$ || $$ > 255) { - yyerror("illegal tos value %s", $1); - free($1); - YYERROR; - } - free($1); - } - | NUMBER { - $$ = $1; - if (!$$ || $$ > 255) { - yyerror("illegal tos value %s", $1); - YYERROR; - } - } - ; - -sourcetrack : SOURCETRACK { $$ = PF_SRCTRACK; } - | SOURCETRACK GLOBAL { $$ = PF_SRCTRACK_GLOBAL; } - | SOURCETRACK RULE { $$ = PF_SRCTRACK_RULE; } - ; - -statelock : IFBOUND { - $$ = PFRULE_IFBOUND; - } - | FLOATING { - $$ = 0; - } - ; - -keep : NO STATE { - $$.action = 0; - $$.options = NULL; - } - | 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 { - if ($2 < 0 || $2 > UINT_MAX) { - yyerror("only positive values permitted"); - YYERROR; - } - $$ = 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 { - if ($2 < 0 || $2 > UINT_MAX) { - yyerror("only positive values permitted"); - YYERROR; - } - $$ = 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 { - if ($2 < 0 || $2 > UINT_MAX) { - yyerror("only positive values permitted"); - YYERROR; - } - $$ = 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 { - if ($2 < 0 || $2 > UINT_MAX || - $4 < 0 || $4 > UINT_MAX) { - yyerror("only positive values permitted"); - YYERROR; - } - $$ = 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 { - if ($2 < 0 || $2 > UINT_MAX) { - yyerror("only positive values permitted"); - YYERROR; - } - $$ = 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 = $$; - } - | SLOPPY { - $$ = calloc(1, sizeof(struct node_state_opt)); - if ($$ == NULL) - err(1, "state_opt_item: calloc"); - $$->type = PF_STATE_OPT_SLOPPY; - $$->next = NULL; - $$->tail = $$; - } - | STRING NUMBER { - int i; - - if ($2 < 0 || $2 > UINT_MAX) { - yyerror("only positive values permitted"); - YYERROR; - } - 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; - $$.pqname = NULL; - } - | QUEUE '(' STRING ')' { - $$.qname = $3; - $$.pqname = NULL; - } - | QUEUE '(' STRING comma STRING ')' { - $$.qname = $3; - $$.pqname = $5; - } - ; - -no : /* empty */ { $$ = 0; } - | NO { $$ = 1; } - ; - -portstar : numberstring { - if (parseport($1, &$$, PPORT_RANGE|PPORT_STAR) == -1) { - free($1); - YYERROR; - } - free($1); - } - ; - -redirspec : host { $$ = $1; } - | '{' optnl redir_host_list '}' { $$ = $3; } - ; - -redir_host_list : host optnl { $$ = $1; } - | redir_host_list comma host optnl { - $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 portstar { - $$ = 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 portstar { - $$ = calloc(1, sizeof(struct redirection)); - if ($$ == NULL) - err(1, "redirection: calloc"); - $$->host = $2; - $$->rport = $4; - } - ; - -natpasslog : /* empty */ { $$.b1 = $$.b2 = 0; $$.w2 = 0; } - | PASS { $$.b1 = 1; $$.b2 = 0; $$.w2 = 0; } - | PASS log { $$.b1 = 1; $$.b2 = $2.log; $$.w2 = $2.logif; } - | log { $$.b1 = 0; $$.b2 = $1.log; $$.w2 = $1.logif; } - ; - -nataction : no NAT natpasslog { - if ($1 && $3.b1) { - yyerror("\"pass\" not valid with \"no\""); - YYERROR; - } - if ($1) - $$.b1 = PF_NONAT; - else - $$.b1 = PF_NAT; - $$.b2 = $3.b1; - $$.w = $3.b2; - $$.w2 = $3.w2; - } - | no RDR natpasslog { - if ($1 && $3.b1) { - yyerror("\"pass\" not valid with \"no\""); - YYERROR; - } - if ($1) - $$.b1 = PF_NORDR; - else - $$.b1 = PF_RDR; - $$.b2 = $3.b1; - $$.w = $3.b2; - $$.w2 = $3.w2; - } - ; - -natrule : nataction interface af proto fromto tag tagged rtable - 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.log = $1.w; - r.logif = $1.w2; - 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 ($7.name) - if (strlcpy(r.match_tagname, $7.name, - 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 = $7.neg; - r.rtableid = $8; - - if (r.action == PF_NONAT || r.action == PF_NORDR) { - if ($9 != NULL) { - yyerror("translation rule with 'no' " - "does not need '->'"); - YYERROR; - } - } else { - if ($9 == NULL || $9->host == NULL) { - yyerror("translation rule requires '-> " - "address'"); - YYERROR; - } - if (!r.af && ! $9->host->ifindex) - r.af = $9->host->af; - - remove_invalid_hosts(&$9->host, &r.af); - if (invalid_redirect($9->host, r.af)) - YYERROR; - if (check_netmask($9->host, r.af)) - YYERROR; - - r.rpool.proxy_port[0] = ntohs($9->rport.a); - - switch (r.action) { - case PF_RDR: - if (!$9->rport.b && $9->rport.t && - $5.dst.port != NULL) { - r.rpool.proxy_port[1] = - ntohs($9->rport.a) + - (ntohs( - $5.dst.port->port[1]) - - ntohs( - $5.dst.port->port[0])); - } else - r.rpool.proxy_port[1] = - ntohs($9->rport.b); - break; - case PF_NAT: - r.rpool.proxy_port[1] = - ntohs($9->rport.b); - if (!r.rpool.proxy_port[0] && - !r.rpool.proxy_port[1]) { - r.rpool.proxy_port[0] = - PF_NAT_PROXY_PORT_LOW; - r.rpool.proxy_port[1] = - PF_NAT_PROXY_PORT_HIGH; - } else if (!r.rpool.proxy_port[1]) - r.rpool.proxy_port[1] = - r.rpool.proxy_port[0]; - break; - default: - break; - } - - r.rpool.opts = $10.type; - if ((r.rpool.opts & PF_POOL_TYPEMASK) == - PF_POOL_NONE && ($9->host->next != NULL || - $9->host->addr.type == PF_ADDR_TABLE || - DYNIF_MULTIADDR($9->host->addr))) - r.rpool.opts = PF_POOL_ROUNDROBIN; - if ((r.rpool.opts & PF_POOL_TYPEMASK) != - PF_POOL_ROUNDROBIN && - disallow_table($9->host, "tables are only " - "supported in round-robin redirection " - "pools")) - YYERROR; - if ((r.rpool.opts & PF_POOL_TYPEMASK) != - PF_POOL_ROUNDROBIN && - disallow_alias($9->host, "interface (%s) " - "is only supported in round-robin " - "redirection pools")) - YYERROR; - if ($9->host->next != NULL) { - if ((r.rpool.opts & PF_POOL_TYPEMASK) != - PF_POOL_ROUNDROBIN) { - yyerror("only round-robin " - "valid for multiple " - "redirection addresses"); - YYERROR; - } - } - } - - if ($10.key != NULL) - memcpy(&r.rpool.key, $10.key, - sizeof(struct pf_poolhashkey)); - - if ($10.opts) - r.rpool.opts |= $10.opts; - - if ($10.staticport) { - if (r.action != PF_NAT) { - yyerror("the 'static-port' option is " - "only valid with nat rules"); - YYERROR; - } - if (r.rpool.proxy_port[0] != - PF_NAT_PROXY_PORT_LOW && - r.rpool.proxy_port[1] != - PF_NAT_PROXY_PORT_HIGH) { - yyerror("the 'static-port' option can't" - " be used when specifying a port" - " range"); - YYERROR; - } - r.rpool.proxy_port[0] = 0; - r.rpool.proxy_port[1] = 0; - } - - expand_rule(&r, $2, $9 == NULL ? NULL : $9->host, $4, - $5.src_os, $5.src.host, $5.src.port, $5.dst.host, - $5.dst.port, 0, 0, 0, ""); - free($9); - } - ; - -binatrule : no BINAT natpasslog interface af proto FROM host toipspec tag - tagged rtable redirection - { - struct pf_rule binat; - struct pf_pooladdr *pa; - - if (check_rulestate(PFCTL_STATE_NAT)) - YYERROR; - if (disallow_urpf_failed($9, "\"urpf-failed\" is not " - "permitted as a binat destination")) - YYERROR; - - memset(&binat, 0, sizeof(binat)); - - if ($1 && $3.b1) { - yyerror("\"pass\" not valid with \"no\""); - YYERROR; - } - if ($1) - binat.action = PF_NOBINAT; - else - binat.action = PF_BINAT; - binat.natpass = $3.b1; - binat.log = $3.b2; - binat.logif = $3.w2; - binat.af = $5; - if (!binat.af && $8 != NULL && $8->af) - binat.af = $8->af; - if (!binat.af && $9 != NULL && $9->af) - binat.af = $9->af; - - if (!binat.af && $13 != NULL && $13->host) - binat.af = $13->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 ($10 != NULL) - if (strlcpy(binat.tagname, $10, - PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) { - yyerror("tag too long, max %u chars", - PF_TAG_NAME_SIZE - 1); - YYERROR; - } - if ($11.name) - if (strlcpy(binat.match_tagname, $11.name, - PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) { - yyerror("tag too long, max %u chars", - PF_TAG_NAME_SIZE - 1); - YYERROR; - } - binat.match_tag_not = $11.neg; - binat.rtableid = $12; - - 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 ($13 != NULL && $13->host != NULL && disallow_table( - $13->host, "invalid use of table <%s> as the " - "redirect address of a binat rule")) - YYERROR; - if ($13 != NULL && $13->host != NULL && disallow_alias( - $13->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 ($9 != NULL) { - if ($9->next) { - yyerror("multiple binat ip addresses"); - YYERROR; - } - if ($9->af != binat.af && $9->af) { - yyerror("binat ip versions must match"); - YYERROR; - } - if (check_netmask($9, binat.af)) - YYERROR; - memcpy(&binat.dst.addr, &$9->addr, - sizeof(binat.dst.addr)); - binat.dst.neg = $9->not; - free($9); - } - - if (binat.action == PF_NOBINAT) { - if ($13 != NULL) { - yyerror("'no binat' rule does not need" - " '->'"); - YYERROR; - } - } else { - if ($13 == NULL || $13->host == NULL) { - yyerror("'binat' rule requires" - " '-> address'"); - YYERROR; - } - - remove_invalid_hosts(&$13->host, &binat.af); - if (invalid_redirect($13->host, binat.af)) - YYERROR; - if ($13->host->next != NULL) { - yyerror("binat rule must redirect to " - "a single address"); - YYERROR; - } - if (check_netmask($13->host, binat.af)) - YYERROR; - - if (!PF_AZERO(&binat.src.addr.v.a.mask, - binat.af) && - !PF_AEQ(&binat.src.addr.v.a.mask, - &$13->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 = $13->host->addr; - pa->ifname[0] = 0; - TAILQ_INSERT_TAIL(&binat.rpool.list, - pa, entries); - - free($13); - } - - pfctl_add_rule(pf, &binat, ""); - } - ; - -tag : /* empty */ { $$ = NULL; } - | TAG STRING { $$ = $2; } - ; - -tagged : /* empty */ { $$.neg = 0; $$.name = NULL; } - | not TAGGED string { $$.neg = $1; $$.name = $3; } - ; - -rtable : /* empty */ { $$ = -1; } - | RTABLE NUMBER { - if ($2 < 0 || $2 > rt_tableid_max()) { - yyerror("invalid rtable id"); - YYERROR; - } - $$ = $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 optnl { $$ = $1; } - | route_host_list comma route_host optnl { - if ($1->af == 0) - $1->af = $3->af; - if ($1->af != $3->af) { - yyerror("all pool addresses must be in the " - "same address family"); - YYERROR; - } - $1->tail->next = $3; - $1->tail = $3->tail; - $$ = $1; - } - ; - -routespec : route_host { $$ = $1; } - | '{' optnl route_host_list '}' { $$ = $3; } - ; - -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 ($2 < 0 || $2 > UINT_MAX) { - yyerror("only positive values permitted"); - 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 optnl - | timeout_spec optnl - ; - -limit_spec : STRING NUMBER - { - if (check_rulestate(PFCTL_STATE_OPTION)) { - free($1); - YYERROR; - } - if ($2 < 0 || $2 > UINT_MAX) { - yyerror("only positive values permitted"); - YYERROR; - } - if (pfctl_set_limit(pf, $1, $2) != 0) { - yyerror("unable to set limit %s %u", $1, $2); - free($1); - YYERROR; - } - free($1); - } - ; - -limit_list : limit_list comma limit_spec optnl - | limit_spec optnl - ; - -comma : ',' - | /* empty */ - ; - -yesno : NO { $$ = 0; } - | STRING { - if (!strcmp($1, "yes")) - $$ = 1; - else { - yyerror("invalid value '%s', expected 'yes' " - "or 'no'", $1); - free($1); - YYERROR; - } - free($1); - } - ; - -unaryop : '=' { $$ = PF_OP_EQ; } - | '!' '=' { $$ = PF_OP_NE; } - | '<' '=' { $$ = PF_OP_LE; } - | '<' { $$ = PF_OP_LT; } - | '>' '=' { $$ = PF_OP_GE; } - | '>' { $$ = PF_OP_GT; } - ; - -%% - -int -yyerror(const char *fmt, ...) -{ - va_list ap; - - file->errors++; - va_start(ap, fmt); - fprintf(stderr, "%s:%d: ", file->name, yylval.lineno); - vfprintf(stderr, fmt, ap); - fprintf(stderr, "\n"); - va_end(ap); - return (0); -} - -int -disallow_table(struct node_host *h, const char *fmt) -{ - for (; h != NULL; h = h->next) - if (h->addr.type == PF_ADDR_TABLE) { - yyerror(fmt, h->addr.v.tblname); - return (1); - } - return (0); -} - -int -disallow_urpf_failed(struct node_host *h, const char *fmt) -{ - for (; h != NULL; h = h->next) - if (h->addr.type == PF_ADDR_URPFFAILED) { - yyerror(fmt); - return (1); - } - return (0); -} - -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); - } - return (0); -} - -int -rule_consistent(struct pf_rule *r, int anchor_call) -{ - int problems = 0; - - switch (r->action) { - case PF_PASS: - case PF_DROP: - case PF_SCRUB: - case PF_NOSCRUB: - problems = filter_consistent(r, anchor_call); - 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; - } - return (problems); -} - -int -filter_consistent(struct pf_rule *r, int anchor_call) -{ - int problems = 0; - - if (r->proto != IPPROTO_TCP && r->proto != IPPROTO_UDP && - (r->src.port_op || r->dst.port_op)) { - yyerror("port only applies to tcp/udp"); - problems++; - } - 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->overload_tblname[0] && - r->max_src_conn == 0 && r->max_src_conn_rate.seconds == 0) { - yyerror("'overload' requires 'max-src-conn' " - "or 'max-src-conn-rate'"); - 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->rule_flag & PFRULE_STATESLOPPY && - (r->keep_state == PF_STATE_MODULATE || - r->keep_state == PF_STATE_SYNPROXY)) { - yyerror("sloppy state matching cannot be used with " - "synproxy state or modulate state"); - problems++; - } - return (-problems); -} - -int -nat_consistent(struct pf_rule *r) -{ - return (0); /* yeah! */ -} - -int -rdr_consistent(struct pf_rule *r) -{ - int problems = 0; - - if (r->proto != IPPROTO_TCP && r->proto != IPPROTO_UDP) { - if (r->src.port_op) { - yyerror("src port only applies to tcp/udp"); - problems++; - } - if (r->dst.port_op) { - yyerror("dst port only applies to tcp/udp"); - problems++; - } - 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); -} - -int -process_tabledef(char *name, struct table_opts *opts) -{ - struct pfr_buffer ab; - struct node_tinit *ti; - - bzero(&ab, sizeof(ab)); - ab.pfrb_type = PFRB_ADDRS; - SIMPLEQ_FOREACH(ti, &opts->init_nodes, entries) { - if (ti->file) - if (pfr_buf_load(&ab, ti->file, 0, append_addr)) { - if (errno) - yyerror("cannot load \"%s\": %s", - ti->file, strerror(errno)); - else - 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->name, &ab, pf->anchor->ruleset.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); -} - -struct keywords { - const char *k_name; - int k_val; -}; - -/* macro gore, but you should've seen the prior indentation nightmare... */ - -#define FREE_LIST(T,r) \ - do { \ - T *p, *node = r; \ - while (node != NULL) { \ - p = node; \ - node = node->next; \ - free(p); \ - } \ - } while (0) - -#define LOOP_THROUGH(T,n,r,C) \ - do { \ - T *n; \ - if (r == NULL) { \ - r = calloc(1, sizeof(T)); \ - if (r == NULL) \ - err(1, "LOOP: calloc"); \ - r->next = NULL; \ - } \ - n = r; \ - while (n != NULL) { \ - do { \ - C; \ - } while (0); \ - n = n->next; \ - } \ - } while (0) - -void -expand_label_str(char *label, size_t len, const char *srch, const char *repl) -{ - char *tmp; - char *p, *q; - - if ((tmp = calloc(1, len)) == NULL) - err(1, "expand_label_str: calloc"); - p = q = label; - while ((q = strstr(p, srch)) != NULL) { - *q = '\0'; - if ((strlcat(tmp, p, len) >= len) || - (strlcat(tmp, repl, len) >= len)) - errx(1, "expand_label: label too long"); - q += strlen(srch); - p = q; - } - if (strlcat(tmp, p, len) >= len) - errx(1, "expand_label: label too long"); - strlcpy(label, tmp, len); /* always fits */ - free(tmp); -} - -void -expand_label_if(const char *name, char *label, size_t len, const char *ifname) -{ - if (strstr(label, name) != NULL) { - if (!*ifname) - expand_label_str(label, len, name, "any"); - else - expand_label_str(label, len, name, ifname); - } -} - -void -expand_label_addr(const char *name, char *label, size_t len, sa_family_t af, - struct node_host *h) -{ - char tmp[64], tmp_not[66]; - - if (strstr(label, name) != NULL) { - switch (h->addr.type) { - case PF_ADDR_DYNIFTL: - snprintf(tmp, sizeof(tmp), "(%s)", h->addr.v.ifname); - break; - case PF_ADDR_TABLE: - snprintf(tmp, sizeof(tmp), "<%s>", h->addr.v.tblname); - break; - case PF_ADDR_NOROUTE: - snprintf(tmp, sizeof(tmp), "no-route"); - break; - case PF_ADDR_URPFFAILED: - snprintf(tmp, sizeof(tmp), "urpf-failed"); - break; - case PF_ADDR_ADDRMASK: - if (!af || (PF_AZERO(&h->addr.v.a.addr, af) && - PF_AZERO(&h->addr.v.a.mask, af))) - snprintf(tmp, sizeof(tmp), "any"); - else { - char a[48]; - int bits; - - if (inet_ntop(af, &h->addr.v.a.addr, a, - sizeof(a)) == NULL) - snprintf(tmp, sizeof(tmp), "?"); - else { - bits = unmask(&h->addr.v.a.mask, af); - if ((af == AF_INET && bits < 32) || - (af == AF_INET6 && bits < 128)) - snprintf(tmp, sizeof(tmp), - "%s/%d", a, bits); - else - snprintf(tmp, sizeof(tmp), - "%s", a); - } - } - break; - default: - snprintf(tmp, sizeof(tmp), "?"); - break; - } - - 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); - } -} - -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); - } -} - -void -expand_label_proto(const char *name, char *label, size_t len, u_int8_t proto) -{ - 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); - } - } -} - -void -expand_label_nr(const char *name, char *label, size_t len) -{ - char n[11]; - - if (strstr(label, name) != NULL) { - snprintf(n, sizeof(n), "%u", pf->anchor->match); - expand_label_str(label, len, name, 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) -{ - 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); - } - - 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++; - 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++; - else - if (pfctl_add_altq(pf, &pb)) - errs++; - } - - 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; - } - ); - } - ); - 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); - } - - LOOP_THROUGH(struct node_if, interface, interfaces, - LOOP_THROUGH(struct node_queue, tqueue, queues, - if (!strncmp(a->qname, tqueue->queue, PF_QNAME_SIZE) && - (interface->ifname[0] == 0 || - (!interface->not && !strncmp(interface->ifname, - tqueue->ifname, IFNAMSIZ)) || - (interface->not && strncmp(interface->ifname, - tqueue->ifname, IFNAMSIZ)))) { - /* found ourself in queues */ - found++; - - memcpy(&pa, a, sizeof(struct pf_altq)); - - if (pa.scheduler != ALTQT_NONE && - pa.scheduler != tqueue->scheduler) { - yyerror("exactly one scheduler type " - "per interface allowed"); - return (1); - } - pa.scheduler = tqueue->scheduler; - - /* scheduler dependent error checking */ - switch (pa.scheduler) { - case ALTQT_PRIQ: - if (nqueues != NULL) { - yyerror("priq queues cannot " - "have child queues"); - return (1); - } - if (bwspec.bw_absolute > 0 || - bwspec.bw_percent < 100) { - yyerror("priq doesn't take " - "bandwidth"); - return (1); - } - break; - default: - break; - } - - if (strlcpy(pa.ifname, tqueue->ifname, - sizeof(pa.ifname)) >= sizeof(pa.ifname)) - errx(1, "expand_queue: strlcpy"); - if (strlcpy(pa.parent, tqueue->parent, - sizeof(pa.parent)) >= sizeof(pa.parent)) - errx(1, "expand_queue: strlcpy"); - - if (eval_pfqueue(pf, &pa, &bwspec, opts)) - errs++; - else - if (pfctl_add_altq(pf, &pa)) - errs++; - - for (nq = nqueues; nq != NULL; nq = nq->next) { - if (!strcmp(a->qname, nq->queue)) { - yyerror("queue cannot have " - "itself as child"); - errs++; - continue; - } - n = calloc(1, - sizeof(struct node_queue)); - if (n == NULL) - err(1, "expand_queue: calloc"); - if (strlcpy(n->parent, a->qname, - sizeof(n->parent)) >= - sizeof(n->parent)) - errx(1, "expand_queue strlcpy"); - if (strlcpy(n->queue, nq->queue, - sizeof(n->queue)) >= - sizeof(n->queue)) - errx(1, "expand_queue strlcpy"); - if (strlcpy(n->ifname, tqueue->ifname, - sizeof(n->ifname)) >= - sizeof(n->ifname)) - errx(1, "expand_queue strlcpy"); - n->scheduler = tqueue->scheduler; - n->next = NULL; - n->tail = n; - if (queues == NULL) - queues = n; - else { - queues->tail->next = n; - queues->tail = n; - } - } - if ((pf->opts & PF_OPT_VERBOSE) && ( - (found == 1 && interface->ifname[0] == 0) || - (found > 0 && interface->ifname[0] != 0))) { - print_queue(&pf->paltq->altq, 0, - &bwspec, interface->ifname[0] != 0, - opts); - if (nqueues && nqueues->tail) { - printf("{ "); - LOOP_THROUGH(struct node_queue, - queue, nqueues, - printf("%s ", - queue->queue); - ); - printf("}"); - } - printf("\n"); - } - } - ); - ); - - FREE_LIST(struct node_queue, nqueues); - FREE_LIST(struct node_if, interfaces); - - if (!found) { - yyerror("queue %s has no parent", a->qname); - errs++; - } - - if (errs) - return (1); - else - return (0); -} - -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) -{ - sa_family_t af = r->af; - int added = 0, error = 0; - char ifname[IF_NAMESIZE]; - char label[PF_RULE_LABEL_SIZE]; - char tagname[PF_TAG_NAME_SIZE]; - char match_tagname[PF_TAG_NAME_SIZE]; - struct pf_pooladdr *pa; - struct node_host *h; - u_int8_t flags, flagset, keep_state; - - if (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))) - 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, anchor_call[0]) < 0 || error) - yyerror("skipping rule due to errors"); - else { - r->nr = pf->astack[pf->asd]->match++; - pfctl_add_rule(pf, r, anchor_call); - added++; - } - - )))))))))); - - 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"); -} - -int -expand_skip_interface(struct node_if *interfaces) -{ - int errs = 0; - - if (!interfaces || (!interfaces->next && !interfaces->not && - !strcmp(interfaces->ifname, "none"))) { - if (pf->opts & PF_OPT_VERBOSE) - printf("set skip on none\n"); - errs = pfctl_set_interface_flags(pf, "", PFI_IFLAG_SKIP, 0); - return (errs); - } - - if (pf->opts & PF_OPT_VERBOSE) - printf("set skip on {"); - LOOP_THROUGH(struct node_if, interface, interfaces, - if (pf->opts & PF_OPT_VERBOSE) - printf(" %s", interface->ifname); - if (interface->not) { - yyerror("skip on ! <interface> is not supported"); - errs++; - } else - errs += pfctl_set_interface_flags(pf, - interface->ifname, PFI_IFLAG_SKIP, 1); - ); - if (pf->opts & PF_OPT_VERBOSE) - printf(" }\n"); - - FREE_LIST(struct node_if, interfaces); - - if (errs) - return (1); - else - return (0); -} - -#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); - } - rulestate = desired_state; - return (0); -} - -int -kw_cmp(const void *k, const void *e) -{ - return (strcmp(k, ((const struct keywords *)e)->k_name)); -} - -int -lookup(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}, - { "divert-reply", DIVERTREPLY}, - { "divert-to", DIVERTTO}, - { "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}, - { "hfsc", HFSC}, - { "hostid", HOSTID}, - { "icmp-type", ICMPTYPE}, - { "icmp6-type", ICMP6TYPE}, - { "if-bound", IFBOUND}, - { "in", IN}, - { "include", INCLUDE}, - { "inet", INET}, - { "inet6", INET6}, - { "keep", KEEP}, - { "label", LABEL}, - { "limit", LIMIT}, - { "linkshare", LINKSHARE}, - { "load", LOAD}, - { "log", LOG}, - { "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", ROUTE}, - { "route-to", ROUTETO}, - { "rtable", RTABLE}, - { "rule", RULE}, - { "ruleset-optimization", RULESET_OPTIMIZATION}, - { "scrub", SCRUB}, - { "set", SET}, - { "set-tos", SETTOS}, - { "skip", SKIP}, - { "sloppy", SLOPPY}, - { "source-hash", SOURCEHASH}, - { "source-track", SOURCETRACK}, - { "state", STATE}, - { "state-defaults", STATEDEFAULTS}, - { "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}, - { "urpf-failed", URPFFAILED}, - { "user", USER}, - }; - const struct keywords *p; - - p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]), - sizeof(keywords[0]), kw_cmp); - - if (p) { - if (debug > 1) - fprintf(stderr, "%s: %d\n", s, p->k_val); - return (p->k_val); - } else { - if (debug > 1) - fprintf(stderr, "string: %s\n", s); - return (STRING); - } -} - -#define MAXPUSHBACK 128 - -char *parsebuf; -int parseindex; -char pushback_buffer[MAXPUSHBACK]; -int pushback_index = 0; - -int -lgetc(int quotec) -{ - 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++; - } - - if (pushback_index) - return (pushback_buffer[--pushback_index]); - - if (quotec) { - if ((c = getc(file->stream)) == EOF) { - yyerror("reached end of file while parsing quoted string"); - if (popfile() == EOF) - return (EOF); - return (quotec); - } - return (c); - } - - while ((c = getc(file->stream)) == '\\') { - next = getc(file->stream); - if (next != '\n') { - c = next; - break; - } - yylval.lineno = file->lineno; - file->lineno++; - } - - while (c == EOF) { - if (popfile() == EOF) - return (EOF); - c = getc(file->stream); - } - return (c); -} - -int -lungetc(int c) -{ - 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); -} - -int -findeol(void) -{ - int c; - - parsebuf = NULL; - - /* skip to either EOF or the first real EOL */ - while (1) { - if (pushback_index) - c = pushback_buffer[--pushback_index]; - else - c = lgetc(0); - if (c == '\n') { - file->lineno++; - break; - } - if (c == EOF) - break; - } - return (ERROR); -} - -int -yylex(void) -{ - char buf[8096]; - char *p, *val; - int quotec, next, c; - int token; - -top: - p = buf; - while ((c = lgetc(0)) == ' ' || c == '\t') - ; /* nothing */ - - yylval.lineno = file->lineno; - if (c == '#') - while ((c = lgetc(0)) != '\n' && c != EOF) - ; /* nothing */ - if (c == '$' && parsebuf == NULL) { - while (1) { - if ((c = lgetc(0)) == EOF) - return (0); - - if (p + 1 >= buf + sizeof(buf) - 1) { - yyerror("string too long"); - return (findeol()); - } - if (isalnum(c) || c == '_') { - *p++ = (char)c; - continue; - } - *p = '\0'; - lungetc(c); - break; - } - val = symget(buf); - if (val == NULL) { - yyerror("macro '%s' not defined", buf); - return (findeol()); - } - parsebuf = val; - parseindex = 0; - goto top; - } - - switch (c) { - case '\'': - case '"': - quotec = c; - while (1) { - if ((c = lgetc(quotec)) == EOF) - return (0); - if (c == '\n') { - file->lineno++; - continue; - } else if (c == '\\') { - if ((next = lgetc(quotec)) == EOF) - return (0); - if (next == quotec || c == ' ' || c == '\t') - c = next; - else if (next == '\n') - continue; - else - lungetc(next); - } else if (c == quotec) { - *p = '\0'; - break; - } - if (p + 1 >= buf + sizeof(buf) - 1) { - yyerror("string too long"); - return (findeol()); - } - *p++ = (char)c; - } - yylval.v.string = strdup(buf); - if (yylval.v.string == NULL) - err(1, "yylex: strdup"); - return (STRING); - case '<': - next = lgetc(0); - if (next == '>') { - yylval.v.i = PF_OP_XRG; - return (PORTBINARY); - } - lungetc(next); - break; - case '>': - next = lgetc(0); - if (next == '<') { - yylval.v.i = PF_OP_IRG; - return (PORTBINARY); - } - lungetc(next); - break; - case '-': - next = lgetc(0); - if (next == '>') - return (ARROW); - lungetc(next); - break; - } - -#define allowed_to_end_number(x) \ - (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=') - - if (c == '-' || isdigit(c)) { - do { - *p++ = c; - if ((unsigned)(p-buf) >= sizeof(buf)) { - yyerror("string too long"); - return (findeol()); - } - } while ((c = lgetc(0)) != EOF && isdigit(c)); - lungetc(c); - if (p == buf + 1 && buf[0] == '-') - goto nodigits; - if (c == EOF || allowed_to_end_number(c)) { - const char *errstr = NULL; - - *p = '\0'; - yylval.v.number = strtonum(buf, LLONG_MIN, - LLONG_MAX, &errstr); - if (errstr) { - yyerror("\"%s\" invalid number: %s", - buf, errstr); - return (findeol()); - } - return (NUMBER); - } else { -nodigits: - while (p > buf + 1) - lungetc(*--p); - c = *--p; - if (c == '-') - return (c); - } - } - -#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(0)) != 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 = file->lineno; - file->lineno++; - } - if (c == EOF) - return (0); - return (c); -} - -int -check_file_secrecy(int fd, const char *fname) -{ - struct stat st; - - if (fstat(fd, &st)) { - warn("cannot stat %s", fname); - return (-1); - } - if (st.st_uid != 0 && st.st_uid != getuid()) { - warnx("%s: owner not root or current user", fname); - return (-1); - } - if (st.st_mode & (S_IRWXG | S_IRWXO)) { - warnx("%s: group/world readable/writeable", fname); - return (-1); - } - return (0); -} - -struct file * -pushfile(const char *name, int secret) -{ - struct file *nfile; - - if ((nfile = calloc(1, sizeof(struct file))) == NULL || - (nfile->name = strdup(name)) == NULL) { - warn("malloc"); - return (NULL); - } - if (TAILQ_FIRST(&files) == NULL && strcmp(nfile->name, "-") == 0) { - nfile->stream = stdin; - free(nfile->name); - if ((nfile->name = strdup("stdin")) == NULL) { - warn("strdup"); - free(nfile); - return (NULL); - } - } else if ((nfile->stream = fopen(nfile->name, "r")) == NULL) { - warn("%s", nfile->name); - free(nfile->name); - free(nfile); - return (NULL); - } else if (secret && - check_file_secrecy(fileno(nfile->stream), nfile->name)) { - fclose(nfile->stream); - free(nfile->name); - free(nfile); - return (NULL); - } - nfile->lineno = 1; - TAILQ_INSERT_TAIL(&files, nfile, entry); - return (nfile); -} - -int -popfile(void) -{ - struct file *prev; - - if ((prev = TAILQ_PREV(file, files, entry)) != NULL) { - prev->errors += file->errors; - TAILQ_REMOVE(&files, file, entry); - fclose(file->stream); - free(file->name); - free(file); - file = prev; - return (0); - } - return (EOF); -} - -int -parse_config(char *filename, struct pfctl *xpf) -{ - int errors = 0; - struct sym *sym; - - pf = xpf; - 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; - - if ((file = pushfile(filename, 0)) == NULL) { - warn("cannot open the main config file!"); - return (-1); - } - - yyparse(); - errors = file->errors; - popfile(); - - /* Free macros and check which have not been used. */ - while ((sym = TAILQ_FIRST(&symhead))) { - 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, entry); - free(sym); - } - - return (errors ? -1 : 0); -} - -int -symset(const char *nam, const char *val, int persist) -{ - struct sym *sym; - - for (sym = TAILQ_FIRST(&symhead); sym && strcmp(nam, sym->nam); - sym = TAILQ_NEXT(sym, entry)) - ; /* nothing */ - - if (sym != NULL) { - if (sym->persist == 1) - return (0); - else { - free(sym->nam); - free(sym->val); - TAILQ_REMOVE(&symhead, sym, entry); - free(sym); - } - } - if ((sym = calloc(1, sizeof(*sym))) == NULL) - return (-1); - - sym->nam = strdup(nam); - if (sym->nam == NULL) { - free(sym); - return (-1); - } - sym->val = strdup(val); - if (sym->val == NULL) { - free(sym->nam); - free(sym); - return (-1); - } - sym->used = 0; - sym->persist = persist; - TAILQ_INSERT_TAIL(&symhead, sym, entry); - return (0); -} - -int -pfctl_cmdline_symset(char *s) -{ - char *sym, *val; - int ret; - - if ((val = strrchr(s, '=')) == NULL) - return (-1); - - if ((sym = malloc(strlen(s) - strlen(val) + 1)) == NULL) - err(1, "pfctl_cmdline_symset: malloc"); - - strlcpy(sym, s, strlen(s) - strlen(val) + 1); - - ret = symset(sym, val + 1, 1); - free(sym); - - return (ret); -} - -char * -symget(const char *nam) -{ - struct sym *sym; - - TAILQ_FOREACH(sym, &symhead, entry) - if (strcmp(nam, sym->nam) == 0) { - sym->used = 1; - return (sym->val); - } - return (NULL); -} - -void -mv_rules(struct pf_ruleset *src, struct pf_ruleset *dst) -{ - int i; - struct pf_rule *r; - - for (i = 0; i < PF_RULESET_MAX; ++i) { - while ((r = TAILQ_FIRST(src->rules[i].active.ptr)) - != NULL) { - TAILQ_REMOVE(src->rules[i].active.ptr, r, entries); - TAILQ_INSERT_TAIL(dst->rules[i].active.ptr, r, entries); - dst->anchor->match++; - } - src->anchor->match = 0; - while ((r = TAILQ_FIRST(src->rules[i].inactive.ptr)) - != NULL) { - TAILQ_REMOVE(src->rules[i].inactive.ptr, r, entries); - TAILQ_INSERT_TAIL(dst->rules[i].inactive.ptr, - r, entries); - } - } -} - -void -decide_address_family(struct node_host *n, sa_family_t *af) -{ - if (*af != 0 || n == NULL) - return; - *af = n->af; - while ((n = n->next) != NULL) { - if (n->af != *af) { - *af = 0; - return; - } - } -} - -void -remove_invalid_hosts(struct node_host **nh, sa_family_t *af) -{ - struct node_host *n = *nh, *prev = NULL; - - while (n != NULL) { - if (*af && n->af && n->af != *af) { - /* unlink and free n */ - struct node_host *next = n->next; - - /* adjust tail pointer */ - if (n == (*nh)->tail) - (*nh)->tail = prev; - /* adjust previous node's next pointer */ - if (prev == NULL) - *nh = next; - else - prev->next = next; - /* free node */ - if (n->ifname != NULL) - free(n->ifname); - free(n); - n = next; - } else { - if (n->af && !*af) - *af = n->af; - prev = n; - n = n->next; - } - } -} - -int -invalid_redirect(struct node_host *nh, sa_family_t af) -{ - if (!af) { - struct node_host *n; - - /* tables and dyniftl are ok without an address family */ - for (n = nh; n != NULL; n = n->next) { - if (n->addr.type != PF_ADDR_TABLE && - n->addr.type != PF_ADDR_DYNIFTL) { - yyerror("address family not given and " - "translation address expands to multiple " - "address families"); - return (1); - } - } - } - if (nh == NULL) { - yyerror("no translation address with matching address family " - "found."); - return (1); - } - return (0); -} - -int -atoul(char *s, u_long *ulvalp) -{ - u_long ulval; - char *ep; - - errno = 0; - ulval = strtoul(s, &ep, 0); - if (s[0] == '\0' || *ep != '\0') - return (-1); - if (errno == ERANGE && ulval == ULONG_MAX) - return (-1); - *ulvalp = ulval; - return (0); -} - -int -getservice(char *n) -{ - struct servent *s; - u_long ulval; - - if (atoul(n, &ulval) == 0) { - if (ulval > 65535) { - yyerror("illegal port value %lu", ulval); - 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); - return (-1); - } - return (s->s_port); - } -} - -int -rule_label(struct pf_rule *r, char *s) -{ - if (s) { - if (strlcpy(r->label, s, sizeof(r->label)) >= - sizeof(r->label)) { - yyerror("rule label too long (max %d chars)", - sizeof(r->label)-1); - return (-1); - } - } - return (0); -} - -u_int16_t -parseicmpspec(char *w, sa_family_t af) -{ - const struct icmpcodeent *p; - u_long ulval; - u_int8_t icmptype; - - 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 %lu", ulval); - return (0); - } - return (icmptype << 8 | ulval); -} - -int -parseport(char *port, struct range *r, int extensions) -{ - char *p = strchr(port, ':'); - - if (p == NULL) { - if ((r->a = getservice(port)) == -1) - return (-1); - r->b = 0; - r->t = PF_OP_NONE; - return (0); - } - if ((extensions & PPORT_STAR) && !strcmp(p+1, "*")) { - *p = 0; - if ((r->a = getservice(port)) == -1) - return (-1); - r->b = 0; - r->t = PF_OP_IRG; - return (0); - } - if ((extensions & PPORT_RANGE)) { - *p++ = 0; - if ((r->a = getservice(port)) == -1 || - (r->b = getservice(p)) == -1) - return (-1); - if (r->a == r->b) { - r->b = 0; - r->t = PF_OP_NONE; - } else - r->t = PF_OP_RRG; - return (0); - } - return (-1); -} - -int -pfctl_load_anchors(int dev, struct pfctl *pf, struct pfr_buffer *trans) -{ - struct loadanchors *la; - - TAILQ_FOREACH(la, &loadanchorshead, entries) { - if (pf->opts & PF_OPT_VERBOSE) - fprintf(stderr, "\nLoading anchor %s from %s\n", - la->anchorname, la->filename); - if (pfctl_rules(dev, la->filename, pf->opts, pf->optimize, - la->anchorname, trans) == -1) - return (-1); - } - - return (0); -} - -int -rt_tableid_max(void) -{ -#ifdef __FreeBSD__ - int fibs; - size_t l = sizeof(fibs); - - if (sysctlbyname("net.fibs", &fibs, &l, NULL, 0) == -1) - fibs = 16; /* XXX RT_MAXFIBS, at least limit it some. */ - /* - * As the OpenBSD code only compares > and not >= we need to adjust - * here given we only accept values of 0..n and want to avoid #ifdefs - * in the grammer. - */ - return (fibs - 1); -#else - return (RT_TABLEID_MAX); -#endif -} diff --git a/contrib/pf/pfctl/pf_print_state.c b/contrib/pf/pfctl/pf_print_state.c deleted file mode 100644 index d6637b4..0000000 --- a/contrib/pf/pfctl/pf_print_state.c +++ /dev/null @@ -1,375 +0,0 @@ -/* $OpenBSD: pf_print_state.c,v 1.52 2008/08/12 16:40:18 david Exp $ */ - -/* - * Copyright (c) 2001 Daniel Hartmeier - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - 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/cdefs.h> -__FBSDID("$FreeBSD$"); - -#include <sys/types.h> -#include <sys/socket.h> -#ifdef __FreeBSD__ -#include <sys/endian.h> -#define betoh64 be64toh -#endif -#include <net/if.h> -#define TCPSTATES -#include <netinet/tcp_fsm.h> -#include <net/pfvar.h> -#include <arpa/inet.h> -#include <netdb.h> - -#include <stdio.h> -#include <string.h> - -#include "pfctl_parser.h" -#include "pfctl.h" - -void print_name(struct pf_addr *, sa_family_t); - -void -print_addr(struct pf_addr_wrap *addr, sa_family_t af, int verbose) -{ - switch (addr->type) { - case PF_ADDR_DYNIFTL: - printf("(%s", addr->v.ifname); - if (addr->iflags & PFI_AFLAG_NETWORK) - printf(":network"); - if (addr->iflags & PFI_AFLAG_BROADCAST) - printf(":broadcast"); - if (addr->iflags & PFI_AFLAG_PEER) - printf(":peer"); - if (addr->iflags & PFI_AFLAG_NOALIAS) - printf(":0"); - if (verbose) { - if (addr->p.dyncnt <= 0) - printf(":*"); - else - printf(":%d", addr->p.dyncnt); - } - printf(")"); - break; - case PF_ADDR_TABLE: - if (verbose) - if (addr->p.tblcnt == -1) - printf("<%s:*>", addr->v.tblname); - else - printf("<%s:%d>", addr->v.tblname, - addr->p.tblcnt); - else - printf("<%s>", addr->v.tblname); - return; - case PF_ADDR_RANGE: { - char buf[48]; - - if (inet_ntop(af, &addr->v.a.addr, buf, sizeof(buf)) == NULL) - printf("?"); - else - printf("%s", buf); - if (inet_ntop(af, &addr->v.a.mask, buf, sizeof(buf)) == NULL) - printf(" - ?"); - else - printf(" - %s", buf); - break; - } - case PF_ADDR_ADDRMASK: - if (PF_AZERO(&addr->v.a.addr, AF_INET6) && - PF_AZERO(&addr->v.a.mask, AF_INET6)) - printf("any"); - else { - char buf[48]; - - if (inet_ntop(af, &addr->v.a.addr, buf, - sizeof(buf)) == NULL) - printf("?"); - else - printf("%s", buf); - } - break; - case PF_ADDR_NOROUTE: - printf("no-route"); - return; - case PF_ADDR_URPFFAILED: - printf("urpf-failed"); - return; - default: - printf("?"); - return; - } - - /* mask if not _both_ address and mask are zero */ - if (addr->type != PF_ADDR_RANGE && - !(PF_AZERO(&addr->v.a.addr, AF_INET6) && - PF_AZERO(&addr->v.a.mask, AF_INET6))) { - int bits = unmask(&addr->v.a.mask, af); - - if (bits != (af == AF_INET ? 32 : 128)) - printf("/%d", bits); - } -} - -void -print_name(struct pf_addr *addr, sa_family_t af) -{ - char host[NI_MAXHOST]; - - strlcpy(host, "?", sizeof(host)); - switch (af) { - case AF_INET: { - struct sockaddr_in sin; - - memset(&sin, 0, sizeof(sin)); - sin.sin_len = sizeof(sin); - sin.sin_family = AF_INET; - sin.sin_addr = addr->v4; - getnameinfo((struct sockaddr *)&sin, sin.sin_len, - host, sizeof(host), NULL, 0, NI_NOFQDN); - break; - } - case AF_INET6: { - struct sockaddr_in6 sin6; - - memset(&sin6, 0, sizeof(sin6)); - sin6.sin6_len = sizeof(sin6); - sin6.sin6_family = AF_INET6; - sin6.sin6_addr = addr->v6; - getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len, - host, sizeof(host), NULL, 0, NI_NOFQDN); - break; - } - } - printf("%s", host); -} - -void -print_host(struct pf_addr *addr, u_int16_t port, sa_family_t af, int opts) -{ - if (opts & PF_OPT_USEDNS) - print_name(addr, af); - else { - struct pf_addr_wrap aw; - - memset(&aw, 0, sizeof(aw)); - aw.v.a.addr = *addr; - if (af == AF_INET) - aw.v.a.mask.addr32[0] = 0xffffffff; - else { - memset(&aw.v.a.mask, 0xff, sizeof(aw.v.a.mask)); - af = AF_INET6; - } - print_addr(&aw, af, opts & PF_OPT_VERBOSE2); - } - - if (port) { - if (af == AF_INET) - printf(":%u", ntohs(port)); - else - printf("[%u]", ntohs(port)); - } -} - -void -print_seq(struct pfsync_state_peer *p) -{ - if (p->seqdiff) - printf("[%u + %u](+%u)", ntohl(p->seqlo), - ntohl(p->seqhi) - ntohl(p->seqlo), ntohl(p->seqdiff)); - else - printf("[%u + %u]", ntohl(p->seqlo), - ntohl(p->seqhi) - ntohl(p->seqlo)); -} - -void -print_state(struct pfsync_state *s, int opts) -{ - struct pfsync_state_peer *src, *dst; - struct pfsync_state_key *sk, *nk; - struct protoent *p; - int min, sec; - - if (s->direction == PF_OUT) { - src = &s->src; - dst = &s->dst; - sk = &s->key[PF_SK_STACK]; - nk = &s->key[PF_SK_WIRE]; - if (s->proto == IPPROTO_ICMP || s->proto == IPPROTO_ICMPV6) - sk->port[0] = nk->port[0]; - } else { - src = &s->dst; - dst = &s->src; - sk = &s->key[PF_SK_WIRE]; - nk = &s->key[PF_SK_STACK]; - if (s->proto == IPPROTO_ICMP || s->proto == IPPROTO_ICMPV6) - sk->port[1] = nk->port[1]; - } - printf("%s ", s->ifname); - if ((p = getprotobynumber(s->proto)) != NULL) - printf("%s ", p->p_name); - else - printf("%u ", s->proto); - - print_host(&nk->addr[1], nk->port[1], s->af, opts); - if (PF_ANEQ(&nk->addr[1], &sk->addr[1], s->af) || - nk->port[1] != sk->port[1]) { - printf(" ("); - print_host(&sk->addr[1], sk->port[1], s->af, opts); - printf(")"); - } - if (s->direction == PF_OUT) - printf(" -> "); - else - printf(" <- "); - print_host(&nk->addr[0], nk->port[0], s->af, opts); - if (PF_ANEQ(&nk->addr[0], &sk->addr[0], s->af) || - nk->port[0] != sk->port[0]) { - printf(" ("); - print_host(&sk->addr[0], sk->port[0], s->af, opts); - printf(")"); - } - - printf(" "); - if (s->proto == IPPROTO_TCP) { - if (src->state <= TCPS_TIME_WAIT && - dst->state <= TCPS_TIME_WAIT) - printf(" %s:%s\n", tcpstates[src->state], - tcpstates[dst->state]); - else if (src->state == PF_TCPS_PROXY_SRC || - dst->state == PF_TCPS_PROXY_SRC) - printf(" PROXY:SRC\n"); - else if (src->state == PF_TCPS_PROXY_DST || - dst->state == PF_TCPS_PROXY_DST) - printf(" PROXY:DST\n"); - else - printf(" <BAD STATE LEVELS %u:%u>\n", - src->state, dst->state); - if (opts & PF_OPT_VERBOSE) { - printf(" "); - print_seq(src); - if (src->wscale && dst->wscale) - printf(" wscale %u", - src->wscale & PF_WSCALE_MASK); - printf(" "); - print_seq(dst); - if (src->wscale && dst->wscale) - printf(" wscale %u", - dst->wscale & PF_WSCALE_MASK); - printf("\n"); - } - } else if (s->proto == IPPROTO_UDP && src->state < PFUDPS_NSTATES && - dst->state < PFUDPS_NSTATES) { - const char *states[] = PFUDPS_NAMES; - - printf(" %s:%s\n", states[src->state], states[dst->state]); - } else if (s->proto != IPPROTO_ICMP && src->state < PFOTHERS_NSTATES && - dst->state < PFOTHERS_NSTATES) { - /* XXX ICMP doesn't really have state levels */ - const char *states[] = PFOTHERS_NAMES; - - printf(" %s:%s\n", states[src->state], states[dst->state]); - } else { - printf(" %u:%u\n", src->state, dst->state); - } - - if (opts & PF_OPT_VERBOSE) { - u_int64_t packets[2]; - u_int64_t bytes[2]; - u_int32_t creation = ntohl(s->creation); - u_int32_t expire = ntohl(s->expire); - - sec = creation % 60; - creation /= 60; - min = creation % 60; - creation /= 60; - printf(" age %.2u:%.2u:%.2u", creation, min, sec); - sec = expire % 60; - expire /= 60; - min = expire % 60; - expire /= 60; - printf(", expires in %.2u:%.2u:%.2u", expire, min, sec); - - bcopy(s->packets[0], &packets[0], sizeof(u_int64_t)); - bcopy(s->packets[1], &packets[1], sizeof(u_int64_t)); - bcopy(s->bytes[0], &bytes[0], sizeof(u_int64_t)); - bcopy(s->bytes[1], &bytes[1], sizeof(u_int64_t)); - printf(", %llu:%llu pkts, %llu:%llu bytes", -#ifdef __FreeBSD__ - (unsigned long long)betoh64(packets[0]), - (unsigned long long)betoh64(packets[1]), - (unsigned long long)betoh64(bytes[0]), - (unsigned long long)betoh64(bytes[1])); -#else - betoh64(packets[0]), - betoh64(packets[1]), - betoh64(bytes[0]), - betoh64(bytes[1])); -#endif - if (ntohl(s->anchor) != -1) - printf(", anchor %u", ntohl(s->anchor)); - if (ntohl(s->rule) != -1) - printf(", rule %u", ntohl(s->rule)); - if (s->state_flags & PFSTATE_SLOPPY) - printf(", sloppy"); - if (s->sync_flags & PFSYNC_FLAG_SRCNODE) - printf(", source-track"); - if (s->sync_flags & PFSYNC_FLAG_NATSRCNODE) - printf(", sticky-address"); - printf("\n"); - } - if (opts & PF_OPT_VERBOSE2) { - u_int64_t id; - - bcopy(&s->id, &id, sizeof(u_int64_t)); - printf(" id: %016llx creatorid: %08x", -#ifdef __FreeBSD__ - (unsigned long long)betoh64(id), ntohl(s->creatorid)); -#else - betoh64(id), ntohl(s->creatorid)); -#endif - printf("\n"); - } -} - -int -unmask(struct pf_addr *m, sa_family_t af) -{ - int i = 31, j = 0, b = 0; - u_int32_t tmp; - - while (j < 4 && m->addr32[j] == 0xffffffff) { - b += 32; - j++; - } - if (j < 4) { - tmp = ntohl(m->addr32[j]); - for (i = 31; tmp & (1 << i); --i) - b++; - } - return (b); -} diff --git a/contrib/pf/pfctl/pfctl.8 b/contrib/pf/pfctl/pfctl.8 deleted file mode 100644 index b178a07..0000000 --- a/contrib/pf/pfctl/pfctl.8 +++ /dev/null @@ -1,687 +0,0 @@ -.\" $OpenBSD: pfctl.8,v 1.138 2008/06/10 20:55:02 mcbride Exp $ -.\" -.\" Copyright (c) 2001 Kjell Wooding. 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. -.\" 3. The name of the author may not be used to endorse or promote products -.\" derived from this software without specific prior written permission. -.\" -.\" 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. -.\" -.\" $FreeBSD$ -.\" -.Dd June 21, 2011 -.Dt PFCTL 8 -.Os -.Sh NAME -.Nm pfctl -.Nd control the packet filter (PF) device -.Sh SYNOPSIS -.Nm pfctl -.Bk -words -.Op Fl AdeghmNnOPqRrvz -.Op Fl a Ar anchor -.Oo Fl D Ar macro Ns = -.Ar value Oc -.Op Fl F Ar modifier -.Op Fl f Ar file -.Op Fl i Ar interface -.Op Fl K Ar host | network -.Xo -.Oo Fl k -.Ar host | network | label | id -.Oc Xc -.Op Fl o Ar level -.Op Fl p Ar device -.Op Fl s Ar modifier -.Xo -.Oo Fl t Ar table -.Fl T Ar command -.Op Ar address ... Oc -.Xc -.Op Fl x Ar level -.Ek -.Sh DESCRIPTION -The -.Nm -utility communicates with the packet filter device using the -ioctl interface described in -.Xr pf 4 . -It allows ruleset and parameter configuration and retrieval of status -information from the packet filter. -.Pp -Packet filtering restricts the types of packets that pass through -network interfaces entering or leaving the host based on filter -rules as described in -.Xr pf.conf 5 . -The packet filter can also replace addresses and ports of packets. -Replacing source addresses and ports of outgoing packets is called -NAT (Network Address Translation) and is used to connect an internal -network (usually reserved address space) to an external one (the -Internet) by making all connections to external hosts appear to -come from the gateway. -Replacing destination addresses and ports of incoming packets -is used to redirect connections to different hosts and/or ports. -A combination of both translations, bidirectional NAT, is also -supported. -Translation rules are described in -.Xr pf.conf 5 . -.Pp -When the variable -.Va pf -is set to -.Dv YES -in -.Xr rc.conf 5 , -the rule file specified with the variable -.Va pf_rules -is loaded automatically by the -.Xr rc 8 -scripts and the packet filter is enabled. -.Pp -The packet filter does not itself forward packets between interfaces. -Forwarding can be enabled by setting the -.Xr sysctl 8 -variables -.Em net.inet.ip.forwarding -and/or -.Em net.inet6.ip6.forwarding -to 1. -Set them permanently in -.Xr sysctl.conf 5 . -.Pp -The -.Nm -utility provides several commands. -The options are as follows: -.Bl -tag -width Ds -.It Fl A -Load only the queue rules present in the rule file. -Other rules and options are ignored. -.It Fl a Ar anchor -Apply flags -.Fl f , -.Fl F , -and -.Fl s -only to the rules in the specified -.Ar anchor . -In addition to the main ruleset, -.Nm -can load and manipulate additional rulesets by name, -called anchors. -The main ruleset is the default anchor. -.Pp -Anchors are referenced by name and may be nested, -with the various components of the anchor path separated by -.Sq / -characters, similar to how file system hierarchies are laid out. -The last component of the anchor path is where ruleset operations are -performed. -.Pp -Evaluation of -.Ar anchor -rules from the main ruleset is described in -.Xr pf.conf 5 . -.Pp -For example, the following will show all filter rules (see the -.Fl s -flag below) inside the anchor -.Dq authpf/smith(1234) , -which would have been created for user -.Dq smith -by -.Xr authpf 8 , -PID 1234: -.Bd -literal -offset indent -# pfctl -a "authpf/smith(1234)" -s rules -.Ed -.Pp -Private tables can also be put inside anchors, either by having table -statements in the -.Xr pf.conf 5 -file that is loaded in the anchor, or by using regular table commands, as in: -.Bd -literal -offset indent -# pfctl -a foo/bar -t mytable -T add 1.2.3.4 5.6.7.8 -.Ed -.Pp -When a rule referring to a table is loaded in an anchor, the rule will use the -private table if one is defined, and then fall back to the table defined in the -main ruleset, if there is one. -This is similar to C rules for variable scope. -It is possible to create distinct tables with the same name in the global -ruleset and in an anchor, but this is often bad design and a warning will be -issued in that case. -.Pp -By default, recursive inline printing of anchors applies only to unnamed -anchors specified inline in the ruleset. -If the anchor name is terminated with a -.Sq * -character, the -.Fl s -flag will recursively print all anchors in a brace delimited block. -For example the following will print the -.Dq authpf -ruleset recursively: -.Bd -literal -offset indent -# pfctl -a 'authpf/*' -sr -.Ed -.Pp -To print the main ruleset recursively, specify only -.Sq * -as the anchor name: -.Bd -literal -offset indent -# pfctl -a '*' -sr -.Ed -.It Fl D Ar macro Ns = Ns Ar value -Define -.Ar macro -to be set to -.Ar value -on the command line. -Overrides the definition of -.Ar macro -in the ruleset. -.It Fl d -Disable the packet filter. -.It Fl e -Enable the packet filter. -.It Fl F Ar modifier -Flush the filter parameters specified by -.Ar modifier -(may be abbreviated): -.Pp -.Bl -tag -width xxxxxxxxxxxx -compact -.It Fl F Cm nat -Flush the NAT rules. -.It Fl F Cm queue -Flush the queue rules. -.It Fl F Cm rules -Flush the filter rules. -.It Fl F Cm states -Flush the state table (NAT and filter). -.It Fl F Cm Sources -Flush the source tracking table. -.It Fl F Cm info -Flush the filter information (statistics that are not bound to rules). -.It Fl F Cm Tables -Flush the tables. -.It Fl F Cm osfp -Flush the passive operating system fingerprints. -.It Fl F Cm all -Flush all of the above. -.El -.It Fl f Ar file -Load the rules contained in -.Ar file . -This -.Ar file -may contain macros, tables, options, and normalization, queueing, -translation, and filtering rules. -With the exception of macros and tables, the statements must appear in that -order. -.It Fl g -Include output helpful for debugging. -.It Fl h -Help. -.It Fl i Ar interface -Restrict the operation to the given -.Ar interface . -.It Fl K Ar host | network -Kill all of the source tracking entries originating from the specified -.Ar host -or -.Ar network . -A second -.Fl K Ar host -or -.Fl K Ar network -option may be specified, which will kill all the source tracking -entries from the first host/network to the second. -.It Xo -.Fl k -.Ar host | network | label | id -.Xc -Kill all of the state entries matching the specified -.Ar host , -.Ar network , -.Ar label , -or -.Ar id . -.Pp -For example, to kill all of the state entries originating from -.Dq host : -.Pp -.Dl # pfctl -k host -.Pp -A second -.Fl k Ar host -or -.Fl k Ar network -option may be specified, which will kill all the state entries -from the first host/network to the second. -To kill all of the state entries from -.Dq host1 -to -.Dq host2 : -.Pp -.Dl # pfctl -k host1 -k host2 -.Pp -To kill all states originating from 192.168.1.0/24 to 172.16.0.0/16: -.Pp -.Dl # pfctl -k 192.168.1.0/24 -k 172.16.0.0/16 -.Pp -A network prefix length of 0 can be used as a wildcard. -To kill all states with the target -.Dq host2 : -.Pp -.Dl # pfctl -k 0.0.0.0/0 -k host2 -.Pp -It is also possible to kill states by rule label or state ID. -In this mode the first -.Fl k -argument is used to specify the type -of the second argument. -The following command would kill all states that have been created -from rules carrying the label -.Dq foobar : -.Pp -.Dl # pfctl -k label -k foobar -.Pp -To kill one specific state by its unique state ID -(as shown by pfctl -s state -vv), -use the -.Ar id -modifier and as a second argument the state ID and optional creator ID. -To kill a state with ID 4823e84500000003 use: -.Pp -.Dl # pfctl -k id -k 4823e84500000003 -.Pp -To kill a state with ID 4823e84500000018 created from a backup -firewall with hostid 00000002 use: -.Pp -.Dl # pfctl -k id -k 4823e84500000018/2 -.Pp -.It Fl m -Merge in explicitly given options without resetting those -which are omitted. -Allows single options to be modified without disturbing the others: -.Bd -literal -offset indent -# echo "set loginterface fxp0" | pfctl -mf - -.Ed -.It Fl N -Load only the NAT rules present in the rule file. -Other rules and options are ignored. -.It Fl n -Do not actually load rules, just parse them. -.It Fl O -Load only the options present in the rule file. -Other rules and options are ignored. -.It Fl o Ar level -Control the ruleset optimizer, overriding any rule file settings. -.Pp -.Bl -tag -width xxxxxxxxxxxx -compact -.It Fl o Cm none -Disable the ruleset optimizer. -.It Fl o Cm basic -Enable basic ruleset optimizations. -This is the default behaviour. -.It Fl o Cm profile -Enable basic ruleset optimizations with profiling. -.El -For further information on the ruleset optimizer, see -.Xr pf.conf 5 . -.It Fl P -Do not perform service name lookup for port specific rules, -instead display the ports numerically. -.It Fl p Ar device -Use the device file -.Ar device -instead of the default -.Pa /dev/pf . -.It Fl q -Only print errors and warnings. -.It Fl R -Load only the filter rules present in the rule file. -Other rules and options are ignored. -.It Fl r -Perform reverse DNS lookups on states when displaying them. -.It Fl s Ar modifier -Show the filter parameters specified by -.Ar modifier -(may be abbreviated): -.Pp -.Bl -tag -width xxxxxxxxxxxxx -compact -.It Fl s Cm nat -Show the currently loaded NAT rules. -.It Fl s Cm queue -Show the currently loaded queue rules. -When used together with -.Fl v , -per-queue statistics are also shown. -When used together with -.Fl v v , -.Nm -will loop and show updated queue statistics every five seconds, including -measured bandwidth and packets per second. -.It Fl s Cm rules -Show the currently loaded filter rules. -When used together with -.Fl v , -the per-rule statistics (number of evaluations, -packets and bytes) are also shown. -Note that the -.Dq skip step -optimization done automatically by the kernel -will skip evaluation of rules where possible. -Packets passed statefully are counted in the rule that created the state -(even though the rule isn't evaluated more than once for the entire -connection). -.It Fl s Cm Anchors -Show the currently loaded anchors directly attached to the main ruleset. -If -.Fl a Ar anchor -is specified as well, the anchors loaded directly below the given -.Ar anchor -are shown instead. -If -.Fl v -is specified, all anchors attached under the target anchor will be -displayed recursively. -.It Fl s Cm states -Show the contents of the state table. -.It Fl s Cm Sources -Show the contents of the source tracking table. -.It Fl s Cm info -Show filter information (statistics and counters). -When used together with -.Fl v , -source tracking statistics are also shown. -.It Fl s Cm labels -Show per-rule statistics (label, evaluations, packets total, bytes total, -packets in, bytes in, packets out, bytes out, state creations) of -filter rules with labels, useful for accounting. -.It Fl s Cm timeouts -Show the current global timeouts. -.It Fl s Cm memory -Show the current pool memory hard limits. -.It Fl s Cm Tables -Show the list of tables. -.It Fl s Cm osfp -Show the list of operating system fingerprints. -.It Fl s Cm Interfaces -Show the list of interfaces and interface drivers available to PF. -When used together with -.Fl v , -it additionally lists which interfaces have skip rules activated. -When used together with -.Fl vv , -interface statistics are also shown. -.Fl i -can be used to select an interface or a group of interfaces. -.It Fl s Cm all -Show all of the above, except for the lists of interfaces and operating -system fingerprints. -.El -.It Fl T Ar command Op Ar address ... -Specify the -.Ar command -(may be abbreviated) to apply to the table. -Commands include: -.Pp -.Bl -tag -width xxxxxxxxxxxx -compact -.It Fl T Cm kill -Kill a table. -.It Fl T Cm flush -Flush all addresses of a table. -.It Fl T Cm add -Add one or more addresses in a table. -Automatically create a nonexisting table. -.It Fl T Cm delete -Delete one or more addresses from a table. -.It Fl T Cm expire Ar number -Delete addresses which had their statistics cleared more than -.Ar number -seconds ago. -For entries which have never had their statistics cleared, -.Ar number -refers to the time they were added to the table. -.It Fl T Cm replace -Replace the addresses of the table. -Automatically create a nonexisting table. -.It Fl T Cm show -Show the content (addresses) of a table. -.It Fl T Cm test -Test if the given addresses match a table. -.It Fl T Cm zero -Clear all the statistics of a table. -.It Fl T Cm load -Load only the table definitions from -.Xr pf.conf 5 . -This is used in conjunction with the -.Fl f -flag, as in: -.Bd -literal -offset indent -# pfctl -Tl -f pf.conf -.Ed -.El -.Pp -For the -.Cm add , -.Cm delete , -.Cm replace , -and -.Cm test -commands, the list of addresses can be specified either directly on the command -line and/or in an unformatted text file, using the -.Fl f -flag. -Comments starting with a -.Sq # -are allowed in the text file. -With these commands, the -.Fl v -flag can also be used once or twice, in which case -.Nm -will print the -detailed result of the operation for each individual address, prefixed by -one of the following letters: -.Pp -.Bl -tag -width XXX -compact -.It A -The address/network has been added. -.It C -The address/network has been changed (negated). -.It D -The address/network has been deleted. -.It M -The address matches -.Po -.Cm test -operation only -.Pc . -.It X -The address/network is duplicated and therefore ignored. -.It Y -The address/network cannot be added/deleted due to conflicting -.Sq \&! -attributes. -.It Z -The address/network has been cleared (statistics). -.El -.Pp -Each table can maintain a set of counters that can be retrieved using the -.Fl v -flag of -.Nm . -For example, the following commands define a wide open firewall which will keep -track of packets going to or coming from the -.Ox -FTP server. -The following commands configure the firewall and send 10 pings to the FTP -server: -.Bd -literal -offset indent -# printf "table <test> counters { ftp.openbsd.org }\en \e - pass out to <test>\en" | pfctl -f- -# ping -qc10 ftp.openbsd.org -.Ed -.Pp -We can now use the table -.Cm show -command to output, for each address and packet direction, the number of packets -and bytes that are being passed or blocked by rules referencing the table. -The time at which the current accounting started is also shown with the -.Dq Cleared -line. -.Bd -literal -offset indent -# pfctl -t test -vTshow - 129.128.5.191 - Cleared: Thu Feb 13 18:55:18 2003 - In/Block: [ Packets: 0 Bytes: 0 ] - In/Pass: [ Packets: 10 Bytes: 840 ] - Out/Block: [ Packets: 0 Bytes: 0 ] - Out/Pass: [ Packets: 10 Bytes: 840 ] -.Ed -.Pp -Similarly, it is possible to view global information about the tables -by using the -.Fl v -modifier twice and the -.Fl s -.Cm Tables -command. -This will display the number of addresses on each table, -the number of rules which reference the table, and the global -packet statistics for the whole table: -.Bd -literal -offset indent -# pfctl -vvsTables ---a-r-C test - Addresses: 1 - Cleared: Thu Feb 13 18:55:18 2003 - References: [ Anchors: 0 Rules: 1 ] - Evaluations: [ NoMatch: 3496 Match: 1 ] - In/Block: [ Packets: 0 Bytes: 0 ] - In/Pass: [ Packets: 10 Bytes: 840 ] - In/XPass: [ Packets: 0 Bytes: 0 ] - Out/Block: [ Packets: 0 Bytes: 0 ] - Out/Pass: [ Packets: 10 Bytes: 840 ] - Out/XPass: [ Packets: 0 Bytes: 0 ] -.Ed -.Pp -As we can see here, only one packet \- the initial ping request \- matched the -table, but all packets passing as the result of the state are correctly -accounted for. -Reloading the table(s) or ruleset will not affect packet accounting in any way. -The two -.Dq XPass -counters are incremented instead of the -.Dq Pass -counters when a -.Dq stateful -packet is passed but doesn't match the table anymore. -This will happen in our example if someone flushes the table while the -.Xr ping 8 -command is running. -.Pp -When used with a single -.Fl v , -.Nm -will only display the first line containing the table flags and name. -The flags are defined as follows: -.Pp -.Bl -tag -width XXX -compact -.It c -For constant tables, which cannot be altered outside -.Xr pf.conf 5 . -.It p -For persistent tables, which don't get automatically killed when no rules -refer to them. -.It a -For tables which are part of the -.Em active -tableset. -Tables without this flag do not really exist, cannot contain addresses, and are -only listed if the -.Fl g -flag is given. -.It i -For tables which are part of the -.Em inactive -tableset. -This flag can only be witnessed briefly during the loading of -.Xr pf.conf 5 . -.It r -For tables which are referenced (used) by rules. -.It h -This flag is set when a table in the main ruleset is hidden by one or more -tables of the same name from anchors attached below it. -.It C -This flag is set when per-address counters are enabled on the table. -.El -.It Fl t Ar table -Specify the name of the table. -.It Fl v -Produce more verbose output. -A second use of -.Fl v -will produce even more verbose output including ruleset warnings. -See the previous section for its effect on table commands. -.It Fl x Ar level -Set the debug -.Ar level -(may be abbreviated) to one of the following: -.Pp -.Bl -tag -width xxxxxxxxxxxx -compact -.It Fl x Cm none -Don't generate debug messages. -.It Fl x Cm urgent -Generate debug messages only for serious errors. -.It Fl x Cm misc -Generate debug messages for various errors. -.It Fl x Cm loud -Generate debug messages for common conditions. -.El -.It Fl z -Clear per-rule statistics. -.El -.Sh FILES -.Bl -tag -width "/etc/pf.conf" -compact -.It Pa /etc/pf.conf -Packet filter rules file. -.It Pa /etc/pf.os -Passive operating system fingerprint database. -.El -.Sh SEE ALSO -.Xr pf 4 , -.Xr pf.conf 5 , -.Xr pf.os 5 , -.Xr rc.conf 5 , -.Xr services 5 , -.Xr sysctl.conf 5 , -.Xr authpf 8 , -.Xr ftp-proxy 8 , -.Xr rc 8 , -.Xr sysctl 8 -.Sh HISTORY -The -.Nm -program and the -.Xr pf 4 -filter mechanism first appeared in -.Ox 3.0 . diff --git a/contrib/pf/pfctl/pfctl.c b/contrib/pf/pfctl/pfctl.c deleted file mode 100644 index 90a2bb5..0000000 --- a/contrib/pf/pfctl/pfctl.c +++ /dev/null @@ -1,2391 +0,0 @@ -/* $OpenBSD: pfctl.c,v 1.278 2008/08/31 20:18:17 jmc Exp $ */ - -/* - * 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: - * - * - 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/cdefs.h> -__FBSDID("$FreeBSD$"); - -#include <sys/types.h> -#include <sys/ioctl.h> -#include <sys/socket.h> -#include <sys/stat.h> - -#ifdef __FreeBSD__ -#include <sys/endian.h> -#endif - -#include <net/if.h> -#include <netinet/in.h> -#include <net/pfvar.h> -#include <arpa/inet.h> -#include <altq/altq.h> -#include <sys/sysctl.h> - -#include <err.h> -#include <errno.h> -#include <fcntl.h> -#include <limits.h> -#include <netdb.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include "pfctl_parser.h" -#include "pfctl.h" - -void usage(void); -int pfctl_enable(int, int); -int pfctl_disable(int, int); -int pfctl_clear_stats(int, int); -int pfctl_clear_interface_flags(int, int); -int pfctl_clear_rules(int, int, char *); -int pfctl_clear_nat(int, int, char *); -int pfctl_clear_altq(int, int); -int pfctl_clear_src_nodes(int, int); -int pfctl_clear_states(int, const char *, int); -void pfctl_addrprefix(char *, struct pf_addr *); -int pfctl_kill_src_nodes(int, const char *, int); -int pfctl_net_kill_states(int, const char *, int); -int pfctl_label_kill_states(int, const char *, int); -int pfctl_id_kill_states(int, const char *, int); -void pfctl_init_options(struct pfctl *); -int pfctl_load_options(struct pfctl *); -int pfctl_load_limit(struct pfctl *, unsigned int, unsigned int); -int pfctl_load_timeout(struct pfctl *, unsigned int, unsigned int); -int pfctl_load_debug(struct pfctl *, unsigned int); -int pfctl_load_logif(struct pfctl *, char *); -int pfctl_load_hostid(struct pfctl *, unsigned int); -int pfctl_get_pool(int, struct pf_pool *, u_int32_t, u_int32_t, int, - char *); -void pfctl_print_rule_counters(struct pf_rule *, int); -int pfctl_show_rules(int, char *, int, enum pfctl_show, char *, int); -int pfctl_show_nat(int, int, char *); -int pfctl_show_src_nodes(int, int); -int pfctl_show_states(int, const char *, int); -int pfctl_show_status(int, int); -int pfctl_show_timeouts(int, int); -int pfctl_show_limits(int, int); -void pfctl_debug(int, u_int32_t, int); -int pfctl_test_altqsupport(int, int); -int pfctl_show_anchors(int, int, char *); -int pfctl_ruleset_trans(struct pfctl *, char *, struct pf_anchor *); -int pfctl_load_ruleset(struct pfctl *, char *, - struct pf_ruleset *, int, int); -int pfctl_load_rule(struct pfctl *, char *, struct pf_rule *, int); -const char *pfctl_lookup_option(char *, const char **); - -struct pf_anchor_global pf_anchors; -struct pf_anchor pf_main_anchor; - -const char *clearopt; -char *rulesopt; -const char *showopt; -const char *debugopt; -char *anchoropt; -const char *optiopt = NULL; -char *pf_device = "/dev/pf"; -char *ifaceopt; -char *tableopt; -const char *tblcmdopt; -int src_node_killers; -char *src_node_kill[2]; -int state_killers; -char *state_kill[2]; -int loadopt; -int altqsupport; - -int dev = -1; -int first_title = 1; -int labels = 0; - -#define INDENT(d, o) do { \ - if (o) { \ - int i; \ - for (i=0; i < d; i++) \ - printf(" "); \ - } \ - } while (0); \ - - -static const struct { - const char *name; - int index; -} pf_limits[] = { - { "states", PF_LIMIT_STATES }, - { "src-nodes", PF_LIMIT_SRC_NODES }, - { "frags", PF_LIMIT_FRAGS }, - { "table-entries", PF_LIMIT_TABLE_ENTRIES }, - { NULL, 0 } -}; - -struct pf_hint { - const char *name; - int timeout; -}; -static const struct pf_hint pf_hint_normal[] = { - { "tcp.first", 2 * 60 }, - { "tcp.opening", 30 }, - { "tcp.established", 24 * 60 * 60 }, - { "tcp.closing", 15 * 60 }, - { "tcp.finwait", 45 }, - { "tcp.closed", 90 }, - { "tcp.tsdiff", 30 }, - { NULL, 0 } -}; -static const struct pf_hint pf_hint_satellite[] = { - { "tcp.first", 3 * 60 }, - { "tcp.opening", 30 + 5 }, - { "tcp.established", 24 * 60 * 60 }, - { "tcp.closing", 15 * 60 + 5 }, - { "tcp.finwait", 45 + 5 }, - { "tcp.closed", 90 + 5 }, - { "tcp.tsdiff", 60 }, - { NULL, 0 } -}; -static const struct pf_hint pf_hint_conservative[] = { - { "tcp.first", 60 * 60 }, - { "tcp.opening", 15 * 60 }, - { "tcp.established", 5 * 24 * 60 * 60 }, - { "tcp.closing", 60 * 60 }, - { "tcp.finwait", 10 * 60 }, - { "tcp.closed", 3 * 60 }, - { "tcp.tsdiff", 60 }, - { NULL, 0 } -}; -static const struct pf_hint pf_hint_aggressive[] = { - { "tcp.first", 30 }, - { "tcp.opening", 5 }, - { "tcp.established", 5 * 60 * 60 }, - { "tcp.closing", 60 }, - { "tcp.finwait", 30 }, - { "tcp.closed", 30 }, - { "tcp.tsdiff", 10 }, - { NULL, 0 } -}; - -static const struct { - const char *name; - const struct pf_hint *hint; -} pf_hints[] = { - { "normal", pf_hint_normal }, - { "satellite", pf_hint_satellite }, - { "high-latency", pf_hint_satellite }, - { "conservative", pf_hint_conservative }, - { "aggressive", pf_hint_aggressive }, - { NULL, NULL } -}; - -static const char *clearopt_list[] = { - "nat", "queue", "rules", "Sources", - "states", "info", "Tables", "osfp", "all", NULL -}; - -static const char *showopt_list[] = { - "nat", "queue", "rules", "Anchors", "Sources", "states", "info", - "Interfaces", "labels", "timeouts", "memory", "Tables", "osfp", - "all", NULL -}; - -static const char *tblcmdopt_list[] = { - "kill", "flush", "add", "delete", "load", "replace", "show", - "test", "zero", "expire", NULL -}; - -static const char *debugopt_list[] = { - "none", "urgent", "misc", "loud", NULL -}; - -static const char *optiopt_list[] = { - "none", "basic", "profile", NULL -}; - -void -usage(void) -{ - extern char *__progname; - - fprintf(stderr, "usage: %s [-AdeghmNnOPqRrvz] ", __progname); - fprintf(stderr, "[-a anchor] [-D macro=value] [-F modifier]\n"); - fprintf(stderr, "\t[-f file] [-i interface] [-K host | network]\n"); - fprintf(stderr, "\t[-k host | network | label | id] "); - fprintf(stderr, "[-o level] [-p device]\n"); - fprintf(stderr, "\t[-s modifier] "); - fprintf(stderr, "[-t table -T command [address ...]] [-x level]\n"); - exit(1); -} - -int -pfctl_enable(int dev, int opts) -{ - if (ioctl(dev, DIOCSTART)) { - if (errno == EEXIST) - errx(1, "pf already enabled"); -#ifdef __FreeBSD__ - else if (errno == ESRCH) - errx(1, "pfil registeration failed"); -#endif - else - err(1, "DIOCSTART"); - } - if ((opts & PF_OPT_QUIET) == 0) - fprintf(stderr, "pf enabled\n"); - - if (altqsupport && ioctl(dev, DIOCSTARTALTQ)) - if (errno != EEXIST) - err(1, "DIOCSTARTALTQ"); - - return (0); -} - -int -pfctl_disable(int dev, int opts) -{ - if (ioctl(dev, DIOCSTOP)) { - if (errno == ENOENT) - errx(1, "pf not enabled"); - else - err(1, "DIOCSTOP"); - } - if ((opts & PF_OPT_QUIET) == 0) - fprintf(stderr, "pf disabled\n"); - - if (altqsupport && ioctl(dev, DIOCSTOPALTQ)) - if (errno != ENOENT) - err(1, "DIOCSTOPALTQ"); - - return (0); -} - -int -pfctl_clear_stats(int dev, int opts) -{ - if (ioctl(dev, DIOCCLRSTATUS)) - err(1, "DIOCCLRSTATUS"); - if ((opts & PF_OPT_QUIET) == 0) - fprintf(stderr, "pf: statistics cleared\n"); - return (0); -} - -int -pfctl_clear_interface_flags(int dev, int opts) -{ - struct pfioc_iface pi; - - if ((opts & PF_OPT_NOACTION) == 0) { - bzero(&pi, sizeof(pi)); - pi.pfiio_flags = PFI_IFLAG_SKIP; - - if (ioctl(dev, DIOCCLRIFFLAG, &pi)) - err(1, "DIOCCLRIFFLAG"); - if ((opts & PF_OPT_QUIET) == 0) - fprintf(stderr, "pf: interface flags reset\n"); - } - return (0); -} - -int -pfctl_clear_rules(int dev, int opts, char *anchorname) -{ - struct pfr_buffer t; - - memset(&t, 0, sizeof(t)); - t.pfrb_type = PFRB_TRANS; - if (pfctl_add_trans(&t, PF_RULESET_SCRUB, anchorname) || - pfctl_add_trans(&t, PF_RULESET_FILTER, anchorname) || - pfctl_trans(dev, &t, DIOCXBEGIN, 0) || - pfctl_trans(dev, &t, DIOCXCOMMIT, 0)) - err(1, "pfctl_clear_rules"); - if ((opts & PF_OPT_QUIET) == 0) - fprintf(stderr, "rules cleared\n"); - return (0); -} - -int -pfctl_clear_nat(int dev, int opts, char *anchorname) -{ - struct pfr_buffer t; - - memset(&t, 0, sizeof(t)); - t.pfrb_type = PFRB_TRANS; - if (pfctl_add_trans(&t, PF_RULESET_NAT, anchorname) || - pfctl_add_trans(&t, PF_RULESET_BINAT, anchorname) || - pfctl_add_trans(&t, PF_RULESET_RDR, anchorname) || - pfctl_trans(dev, &t, DIOCXBEGIN, 0) || - pfctl_trans(dev, &t, DIOCXCOMMIT, 0)) - err(1, "pfctl_clear_nat"); - if ((opts & PF_OPT_QUIET) == 0) - fprintf(stderr, "nat cleared\n"); - return (0); -} - -int -pfctl_clear_altq(int dev, int opts) -{ - struct pfr_buffer t; - - if (!altqsupport) - return (-1); - memset(&t, 0, sizeof(t)); - t.pfrb_type = PFRB_TRANS; - if (pfctl_add_trans(&t, PF_RULESET_ALTQ, "") || - pfctl_trans(dev, &t, DIOCXBEGIN, 0) || - pfctl_trans(dev, &t, DIOCXCOMMIT, 0)) - err(1, "pfctl_clear_altq"); - if ((opts & PF_OPT_QUIET) == 0) - fprintf(stderr, "altq cleared\n"); - return (0); -} - -int -pfctl_clear_src_nodes(int dev, int opts) -{ - if (ioctl(dev, DIOCCLRSRCNODES)) - err(1, "DIOCCLRSRCNODES"); - if ((opts & PF_OPT_QUIET) == 0) - fprintf(stderr, "source tracking entries cleared\n"); - return (0); -} - -int -pfctl_clear_states(int dev, const char *iface, int opts) -{ - struct pfioc_state_kill psk; - - memset(&psk, 0, sizeof(psk)); - if (iface != NULL && strlcpy(psk.psk_ifname, iface, - sizeof(psk.psk_ifname)) >= sizeof(psk.psk_ifname)) - errx(1, "invalid interface: %s", iface); - - if (ioctl(dev, DIOCCLRSTATES, &psk)) - err(1, "DIOCCLRSTATES"); - if ((opts & PF_OPT_QUIET) == 0) - fprintf(stderr, "%d states cleared\n", psk.psk_killed); - return (0); -} - -void -pfctl_addrprefix(char *addr, struct pf_addr *mask) -{ - char *p; - const char *errstr; - int prefix, ret_ga, q, r; - struct addrinfo hints, *res; - - if ((p = strchr(addr, '/')) == NULL) - return; - - *p++ = '\0'; - prefix = strtonum(p, 0, 128, &errstr); - if (errstr) - errx(1, "prefix is %s: %s", errstr, p); - - bzero(&hints, sizeof(hints)); - /* prefix only with numeric addresses */ - hints.ai_flags |= AI_NUMERICHOST; - - if ((ret_ga = getaddrinfo(addr, NULL, &hints, &res))) { - errx(1, "getaddrinfo: %s", gai_strerror(ret_ga)); - /* NOTREACHED */ - } - - if (res->ai_family == AF_INET && prefix > 32) - errx(1, "prefix too long for AF_INET"); - else if (res->ai_family == AF_INET6 && prefix > 128) - errx(1, "prefix too long for AF_INET6"); - - q = prefix >> 3; - r = prefix & 7; - switch (res->ai_family) { - case AF_INET: - bzero(&mask->v4, sizeof(mask->v4)); - mask->v4.s_addr = htonl((u_int32_t) - (0xffffffffffULL << (32 - prefix))); - break; - case AF_INET6: - bzero(&mask->v6, sizeof(mask->v6)); - if (q > 0) - memset((void *)&mask->v6, 0xff, q); - if (r > 0) - *((u_char *)&mask->v6 + q) = - (0xff00 >> r) & 0xff; - break; - } - freeaddrinfo(res); -} - -int -pfctl_kill_src_nodes(int dev, const char *iface, int opts) -{ - struct pfioc_src_node_kill psnk; - struct addrinfo *res[2], *resp[2]; - struct sockaddr last_src, last_dst; - int killed, sources, dests; - int ret_ga; - - killed = sources = dests = 0; - - memset(&psnk, 0, sizeof(psnk)); - memset(&psnk.psnk_src.addr.v.a.mask, 0xff, - sizeof(psnk.psnk_src.addr.v.a.mask)); - memset(&last_src, 0xff, sizeof(last_src)); - memset(&last_dst, 0xff, sizeof(last_dst)); - - pfctl_addrprefix(src_node_kill[0], &psnk.psnk_src.addr.v.a.mask); - - if ((ret_ga = getaddrinfo(src_node_kill[0], NULL, NULL, &res[0]))) { - errx(1, "getaddrinfo: %s", gai_strerror(ret_ga)); - /* NOTREACHED */ - } - for (resp[0] = res[0]; resp[0]; resp[0] = resp[0]->ai_next) { - if (resp[0]->ai_addr == NULL) - continue; - /* We get lots of duplicates. Catch the easy ones */ - if (memcmp(&last_src, resp[0]->ai_addr, sizeof(last_src)) == 0) - continue; - last_src = *(struct sockaddr *)resp[0]->ai_addr; - - psnk.psnk_af = resp[0]->ai_family; - sources++; - - if (psnk.psnk_af == AF_INET) - psnk.psnk_src.addr.v.a.addr.v4 = - ((struct sockaddr_in *)resp[0]->ai_addr)->sin_addr; - else if (psnk.psnk_af == AF_INET6) - psnk.psnk_src.addr.v.a.addr.v6 = - ((struct sockaddr_in6 *)resp[0]->ai_addr)-> - sin6_addr; - else - errx(1, "Unknown address family %d", psnk.psnk_af); - - if (src_node_killers > 1) { - dests = 0; - memset(&psnk.psnk_dst.addr.v.a.mask, 0xff, - sizeof(psnk.psnk_dst.addr.v.a.mask)); - memset(&last_dst, 0xff, sizeof(last_dst)); - pfctl_addrprefix(src_node_kill[1], - &psnk.psnk_dst.addr.v.a.mask); - if ((ret_ga = getaddrinfo(src_node_kill[1], NULL, NULL, - &res[1]))) { - errx(1, "getaddrinfo: %s", - gai_strerror(ret_ga)); - /* NOTREACHED */ - } - for (resp[1] = res[1]; resp[1]; - resp[1] = resp[1]->ai_next) { - if (resp[1]->ai_addr == NULL) - continue; - if (psnk.psnk_af != resp[1]->ai_family) - continue; - - if (memcmp(&last_dst, resp[1]->ai_addr, - sizeof(last_dst)) == 0) - continue; - last_dst = *(struct sockaddr *)resp[1]->ai_addr; - - dests++; - - if (psnk.psnk_af == AF_INET) - psnk.psnk_dst.addr.v.a.addr.v4 = - ((struct sockaddr_in *)resp[1]-> - ai_addr)->sin_addr; - else if (psnk.psnk_af == AF_INET6) - psnk.psnk_dst.addr.v.a.addr.v6 = - ((struct sockaddr_in6 *)resp[1]-> - ai_addr)->sin6_addr; - else - errx(1, "Unknown address family %d", - psnk.psnk_af); - - if (ioctl(dev, DIOCKILLSRCNODES, &psnk)) - err(1, "DIOCKILLSRCNODES"); - killed += psnk.psnk_killed; - } - freeaddrinfo(res[1]); - } else { - if (ioctl(dev, DIOCKILLSRCNODES, &psnk)) - err(1, "DIOCKILLSRCNODES"); - killed += psnk.psnk_killed; - } - } - - freeaddrinfo(res[0]); - - if ((opts & PF_OPT_QUIET) == 0) - fprintf(stderr, "killed %d src nodes from %d sources and %d " - "destinations\n", killed, sources, dests); - return (0); -} - -int -pfctl_net_kill_states(int dev, const char *iface, int opts) -{ - struct pfioc_state_kill psk; - struct addrinfo *res[2], *resp[2]; - struct sockaddr last_src, last_dst; - int killed, sources, dests; - int ret_ga; - - killed = sources = dests = 0; - - memset(&psk, 0, sizeof(psk)); - memset(&psk.psk_src.addr.v.a.mask, 0xff, - sizeof(psk.psk_src.addr.v.a.mask)); - memset(&last_src, 0xff, sizeof(last_src)); - memset(&last_dst, 0xff, sizeof(last_dst)); - if (iface != NULL && strlcpy(psk.psk_ifname, iface, - sizeof(psk.psk_ifname)) >= sizeof(psk.psk_ifname)) - errx(1, "invalid interface: %s", iface); - - pfctl_addrprefix(state_kill[0], &psk.psk_src.addr.v.a.mask); - - if ((ret_ga = getaddrinfo(state_kill[0], NULL, NULL, &res[0]))) { - errx(1, "getaddrinfo: %s", gai_strerror(ret_ga)); - /* NOTREACHED */ - } - for (resp[0] = res[0]; resp[0]; resp[0] = resp[0]->ai_next) { - if (resp[0]->ai_addr == NULL) - continue; - /* We get lots of duplicates. Catch the easy ones */ - if (memcmp(&last_src, resp[0]->ai_addr, sizeof(last_src)) == 0) - continue; - last_src = *(struct sockaddr *)resp[0]->ai_addr; - - psk.psk_af = resp[0]->ai_family; - sources++; - - if (psk.psk_af == AF_INET) - psk.psk_src.addr.v.a.addr.v4 = - ((struct sockaddr_in *)resp[0]->ai_addr)->sin_addr; - else if (psk.psk_af == AF_INET6) - psk.psk_src.addr.v.a.addr.v6 = - ((struct sockaddr_in6 *)resp[0]->ai_addr)-> - sin6_addr; - else - errx(1, "Unknown address family %d", psk.psk_af); - - if (state_killers > 1) { - dests = 0; - memset(&psk.psk_dst.addr.v.a.mask, 0xff, - sizeof(psk.psk_dst.addr.v.a.mask)); - memset(&last_dst, 0xff, sizeof(last_dst)); - pfctl_addrprefix(state_kill[1], - &psk.psk_dst.addr.v.a.mask); - if ((ret_ga = getaddrinfo(state_kill[1], NULL, NULL, - &res[1]))) { - errx(1, "getaddrinfo: %s", - gai_strerror(ret_ga)); - /* NOTREACHED */ - } - for (resp[1] = res[1]; resp[1]; - resp[1] = resp[1]->ai_next) { - if (resp[1]->ai_addr == NULL) - continue; - if (psk.psk_af != resp[1]->ai_family) - continue; - - if (memcmp(&last_dst, resp[1]->ai_addr, - sizeof(last_dst)) == 0) - continue; - last_dst = *(struct sockaddr *)resp[1]->ai_addr; - - dests++; - - if (psk.psk_af == AF_INET) - psk.psk_dst.addr.v.a.addr.v4 = - ((struct sockaddr_in *)resp[1]-> - ai_addr)->sin_addr; - else if (psk.psk_af == AF_INET6) - psk.psk_dst.addr.v.a.addr.v6 = - ((struct sockaddr_in6 *)resp[1]-> - ai_addr)->sin6_addr; - else - errx(1, "Unknown address family %d", - psk.psk_af); - - if (ioctl(dev, DIOCKILLSTATES, &psk)) - err(1, "DIOCKILLSTATES"); - killed += psk.psk_killed; - } - freeaddrinfo(res[1]); - } else { - if (ioctl(dev, DIOCKILLSTATES, &psk)) - err(1, "DIOCKILLSTATES"); - killed += psk.psk_killed; - } - } - - freeaddrinfo(res[0]); - - if ((opts & PF_OPT_QUIET) == 0) - fprintf(stderr, "killed %d states from %d sources and %d " - "destinations\n", killed, sources, dests); - return (0); -} - -int -pfctl_label_kill_states(int dev, const char *iface, int opts) -{ - struct pfioc_state_kill psk; - - if (state_killers != 2 || (strlen(state_kill[1]) == 0)) { - warnx("no label specified"); - usage(); - } - memset(&psk, 0, sizeof(psk)); - if (iface != NULL && strlcpy(psk.psk_ifname, iface, - sizeof(psk.psk_ifname)) >= sizeof(psk.psk_ifname)) - errx(1, "invalid interface: %s", iface); - - if (strlcpy(psk.psk_label, state_kill[1], sizeof(psk.psk_label)) >= - sizeof(psk.psk_label)) - errx(1, "label too long: %s", state_kill[1]); - - if (ioctl(dev, DIOCKILLSTATES, &psk)) - err(1, "DIOCKILLSTATES"); - - if ((opts & PF_OPT_QUIET) == 0) - fprintf(stderr, "killed %d states\n", psk.psk_killed); - - return (0); -} - -int -pfctl_id_kill_states(int dev, const char *iface, int opts) -{ - struct pfioc_state_kill psk; - - if (state_killers != 2 || (strlen(state_kill[1]) == 0)) { - warnx("no id specified"); - usage(); - } - - memset(&psk, 0, sizeof(psk)); - if ((sscanf(state_kill[1], "%jx/%x", - &psk.psk_pfcmp.id, &psk.psk_pfcmp.creatorid)) == 2) - HTONL(psk.psk_pfcmp.creatorid); - else if ((sscanf(state_kill[1], "%jx", &psk.psk_pfcmp.id)) == 1) { - psk.psk_pfcmp.creatorid = 0; - } else { - warnx("wrong id format specified"); - usage(); - } - if (psk.psk_pfcmp.id == 0) { - warnx("cannot kill id 0"); - usage(); - } - - psk.psk_pfcmp.id = htobe64(psk.psk_pfcmp.id); - if (ioctl(dev, DIOCKILLSTATES, &psk)) - err(1, "DIOCKILLSTATES"); - - if ((opts & PF_OPT_QUIET) == 0) - fprintf(stderr, "killed %d states\n", psk.psk_killed); - - return (0); -} - -int -pfctl_get_pool(int dev, struct pf_pool *pool, u_int32_t nr, - u_int32_t ticket, int r_action, char *anchorname) -{ - struct pfioc_pooladdr pp; - struct pf_pooladdr *pa; - u_int32_t pnr, mpnr; - - memset(&pp, 0, sizeof(pp)); - memcpy(pp.anchor, anchorname, sizeof(pp.anchor)); - pp.r_action = r_action; - pp.r_num = nr; - pp.ticket = ticket; - if (ioctl(dev, DIOCGETADDRS, &pp)) { - warn("DIOCGETADDRS"); - return (-1); - } - mpnr = pp.nr; - TAILQ_INIT(&pool->list); - for (pnr = 0; pnr < mpnr; ++pnr) { - pp.nr = pnr; - if (ioctl(dev, DIOCGETADDR, &pp)) { - warn("DIOCGETADDR"); - return (-1); - } - pa = calloc(1, sizeof(struct pf_pooladdr)); - if (pa == NULL) - err(1, "calloc"); - bcopy(&pp.addr, pa, sizeof(struct pf_pooladdr)); - TAILQ_INSERT_TAIL(&pool->list, pa, entries); - } - - return (0); -} - -void -pfctl_move_pool(struct pf_pool *src, struct pf_pool *dst) -{ - struct pf_pooladdr *pa; - - while ((pa = TAILQ_FIRST(&src->list)) != NULL) { - TAILQ_REMOVE(&src->list, pa, entries); - TAILQ_INSERT_TAIL(&dst->list, pa, entries); - } -} - -void -pfctl_clear_pool(struct pf_pool *pool) -{ - struct pf_pooladdr *pa; - - while ((pa = TAILQ_FIRST(&pool->list)) != NULL) { - TAILQ_REMOVE(&pool->list, pa, entries); - free(pa); - } -} - -void -pfctl_print_rule_counters(struct pf_rule *rule, int opts) -{ - if (opts & PF_OPT_DEBUG) { - const char *t[PF_SKIP_COUNT] = { "i", "d", "f", - "p", "sa", "sp", "da", "dp" }; - int i; - - printf(" [ Skip steps: "); - for (i = 0; i < PF_SKIP_COUNT; ++i) { - if (rule->skip[i].nr == rule->nr + 1) - continue; - printf("%s=", t[i]); - if (rule->skip[i].nr == -1) - printf("end "); - else - printf("%u ", rule->skip[i].nr); - } - printf("]\n"); - - printf(" [ queue: qname=%s qid=%u pqname=%s pqid=%u ]\n", - rule->qname, rule->qid, rule->pqname, rule->pqid); - } - if (opts & PF_OPT_VERBOSE) { - printf(" [ Evaluations: %-8llu Packets: %-8llu " - "Bytes: %-10llu States: %-6u]\n", - (unsigned long long)rule->evaluations, - (unsigned long long)(rule->packets[0] + - rule->packets[1]), - (unsigned long long)(rule->bytes[0] + - rule->bytes[1]), rule->states_cur); - if (!(opts & PF_OPT_DEBUG)) - printf(" [ Inserted: uid %u pid %u " - "State Creations: %-6u]\n", - (unsigned)rule->cuid, (unsigned)rule->cpid, - rule->states_tot); - } -} - -void -pfctl_print_title(char *title) -{ - if (!first_title) - printf("\n"); - first_title = 0; - printf("%s\n", title); -} - -int -pfctl_show_rules(int dev, char *path, int opts, enum pfctl_show format, - char *anchorname, int depth) -{ - struct pfioc_rule pr; - u_int32_t nr, mnr, header = 0; - int rule_numbers = opts & (PF_OPT_VERBOSE2 | PF_OPT_DEBUG); - int numeric = opts & PF_OPT_NUMERIC; - int len = strlen(path); - int brace; - char *p; - - if (path[0]) - snprintf(&path[len], MAXPATHLEN - len, "/%s", anchorname); - else - snprintf(&path[len], MAXPATHLEN - len, "%s", anchorname); - - memset(&pr, 0, sizeof(pr)); - memcpy(pr.anchor, path, sizeof(pr.anchor)); - if (opts & PF_OPT_SHOWALL) { - pr.rule.action = PF_PASS; - if (ioctl(dev, DIOCGETRULES, &pr)) { - warn("DIOCGETRULES"); - goto error; - } - header++; - } - pr.rule.action = PF_SCRUB; - if (ioctl(dev, DIOCGETRULES, &pr)) { - warn("DIOCGETRULES"); - goto error; - } - if (opts & PF_OPT_SHOWALL) { - if (format == PFCTL_SHOW_RULES && (pr.nr > 0 || header)) - pfctl_print_title("FILTER RULES:"); - else if (format == PFCTL_SHOW_LABELS && labels) - pfctl_print_title("LABEL COUNTERS:"); - } - mnr = pr.nr; - if (opts & PF_OPT_CLRRULECTRS) - pr.action = PF_GET_CLR_CNTR; - - for (nr = 0; nr < mnr; ++nr) { - pr.nr = nr; - if (ioctl(dev, DIOCGETRULE, &pr)) { - warn("DIOCGETRULE"); - goto error; - } - - if (pfctl_get_pool(dev, &pr.rule.rpool, - nr, pr.ticket, PF_SCRUB, path) != 0) - goto error; - - switch (format) { - case PFCTL_SHOW_LABELS: - break; - case PFCTL_SHOW_RULES: - if (pr.rule.label[0] && (opts & PF_OPT_SHOWALL)) - labels = 1; - print_rule(&pr.rule, pr.anchor_call, rule_numbers, numeric); - printf("\n"); - pfctl_print_rule_counters(&pr.rule, opts); - break; - case PFCTL_SHOW_NOTHING: - break; - } - pfctl_clear_pool(&pr.rule.rpool); - } - pr.rule.action = PF_PASS; - if (ioctl(dev, DIOCGETRULES, &pr)) { - warn("DIOCGETRULES"); - goto error; - } - mnr = pr.nr; - for (nr = 0; nr < mnr; ++nr) { - pr.nr = nr; - if (ioctl(dev, DIOCGETRULE, &pr)) { - warn("DIOCGETRULE"); - goto error; - } - - if (pfctl_get_pool(dev, &pr.rule.rpool, - nr, pr.ticket, PF_PASS, path) != 0) - goto error; - - switch (format) { - case PFCTL_SHOW_LABELS: - if (pr.rule.label[0]) { - printf("%s %llu %llu %llu %llu" - " %llu %llu %llu %llu\n", - pr.rule.label, - (unsigned long long)pr.rule.evaluations, - (unsigned long long)(pr.rule.packets[0] + - pr.rule.packets[1]), - (unsigned long long)(pr.rule.bytes[0] + - pr.rule.bytes[1]), - (unsigned long long)pr.rule.packets[0], - (unsigned long long)pr.rule.bytes[0], - (unsigned long long)pr.rule.packets[1], - (unsigned long long)pr.rule.bytes[1], - (unsigned long long)pr.rule.states_tot); - } - break; - case PFCTL_SHOW_RULES: - brace = 0; - if (pr.rule.label[0] && (opts & PF_OPT_SHOWALL)) - labels = 1; - INDENT(depth, !(opts & PF_OPT_VERBOSE)); - if (pr.anchor_call[0] && - ((((p = strrchr(pr.anchor_call, '_')) != NULL) && - ((void *)p == (void *)pr.anchor_call || - *(--p) == '/')) || (opts & PF_OPT_RECURSE))) { - brace++; - if ((p = strrchr(pr.anchor_call, '/')) != - NULL) - p++; - else - p = &pr.anchor_call[0]; - } else - p = &pr.anchor_call[0]; - - print_rule(&pr.rule, p, rule_numbers, numeric); - if (brace) - printf(" {\n"); - else - printf("\n"); - pfctl_print_rule_counters(&pr.rule, opts); - if (brace) { - pfctl_show_rules(dev, path, opts, format, - p, depth + 1); - INDENT(depth, !(opts & PF_OPT_VERBOSE)); - printf("}\n"); - } - break; - case PFCTL_SHOW_NOTHING: - break; - } - pfctl_clear_pool(&pr.rule.rpool); - } - path[len] = '\0'; - return (0); - - error: - path[len] = '\0'; - return (-1); -} - -int -pfctl_show_nat(int dev, int opts, char *anchorname) -{ - struct pfioc_rule pr; - u_int32_t mnr, nr; - static int nattype[3] = { PF_NAT, PF_RDR, PF_BINAT }; - int i, dotitle = opts & PF_OPT_SHOWALL; - - memset(&pr, 0, sizeof(pr)); - memcpy(pr.anchor, anchorname, sizeof(pr.anchor)); - for (i = 0; i < 3; i++) { - pr.rule.action = nattype[i]; - if (ioctl(dev, DIOCGETRULES, &pr)) { - warn("DIOCGETRULES"); - return (-1); - } - mnr = pr.nr; - for (nr = 0; nr < mnr; ++nr) { - pr.nr = nr; - if (ioctl(dev, DIOCGETRULE, &pr)) { - warn("DIOCGETRULE"); - return (-1); - } - if (pfctl_get_pool(dev, &pr.rule.rpool, nr, - pr.ticket, nattype[i], anchorname) != 0) - return (-1); - if (dotitle) { - pfctl_print_title("TRANSLATION RULES:"); - dotitle = 0; - } - print_rule(&pr.rule, pr.anchor_call, - opts & PF_OPT_VERBOSE2, opts & PF_OPT_NUMERIC); - printf("\n"); - pfctl_print_rule_counters(&pr.rule, opts); - pfctl_clear_pool(&pr.rule.rpool); - } - } - return (0); -} - -int -pfctl_show_src_nodes(int dev, int opts) -{ - struct pfioc_src_nodes psn; - struct pf_src_node *p; - char *inbuf = NULL, *newinbuf = NULL; - unsigned int len = 0; - int i; - - memset(&psn, 0, sizeof(psn)); - for (;;) { - psn.psn_len = len; - if (len) { - newinbuf = realloc(inbuf, len); - if (newinbuf == NULL) - err(1, "realloc"); - psn.psn_buf = inbuf = newinbuf; - } - if (ioctl(dev, DIOCGETSRCNODES, &psn) < 0) { - warn("DIOCGETSRCNODES"); - free(inbuf); - return (-1); - } - if (psn.psn_len + sizeof(struct pfioc_src_nodes) < len) - break; - if (len == 0 && psn.psn_len == 0) - goto done; - if (len == 0 && psn.psn_len != 0) - len = psn.psn_len; - if (psn.psn_len == 0) - goto done; /* no src_nodes */ - len *= 2; - } - p = psn.psn_src_nodes; - if (psn.psn_len > 0 && (opts & PF_OPT_SHOWALL)) - pfctl_print_title("SOURCE TRACKING NODES:"); - for (i = 0; i < psn.psn_len; i += sizeof(*p)) { - print_src_node(p, opts); - p++; - } -done: - free(inbuf); - return (0); -} - -int -pfctl_show_states(int dev, const char *iface, int opts) -{ - struct pfioc_states ps; - struct pfsync_state *p; - char *inbuf = NULL, *newinbuf = NULL; - unsigned int len = 0; - int i, dotitle = (opts & PF_OPT_SHOWALL); - - memset(&ps, 0, sizeof(ps)); - for (;;) { - ps.ps_len = len; - if (len) { - newinbuf = realloc(inbuf, len); - if (newinbuf == NULL) - err(1, "realloc"); - ps.ps_buf = inbuf = newinbuf; - } - if (ioctl(dev, DIOCGETSTATES, &ps) < 0) { - warn("DIOCGETSTATES"); - free(inbuf); - return (-1); - } - if (ps.ps_len + sizeof(struct pfioc_states) < len) - break; - if (len == 0 && ps.ps_len == 0) - goto done; - if (len == 0 && ps.ps_len != 0) - len = ps.ps_len; - if (ps.ps_len == 0) - goto done; /* no states */ - len *= 2; - } - p = ps.ps_states; - for (i = 0; i < ps.ps_len; i += sizeof(*p), p++) { - if (iface != NULL && strcmp(p->ifname, iface)) - continue; - if (dotitle) { - pfctl_print_title("STATES:"); - dotitle = 0; - } - print_state(p, opts); - } -done: - free(inbuf); - return (0); -} - -int -pfctl_show_status(int dev, int opts) -{ - struct pf_status status; - - if (ioctl(dev, DIOCGETSTATUS, &status)) { - warn("DIOCGETSTATUS"); - return (-1); - } - if (opts & PF_OPT_SHOWALL) - pfctl_print_title("INFO:"); - print_status(&status, opts); - return (0); -} - -int -pfctl_show_timeouts(int dev, int opts) -{ - struct pfioc_tm pt; - int i; - - if (opts & PF_OPT_SHOWALL) - pfctl_print_title("TIMEOUTS:"); - memset(&pt, 0, sizeof(pt)); - for (i = 0; pf_timeouts[i].name; i++) { - pt.timeout = pf_timeouts[i].timeout; - if (ioctl(dev, DIOCGETTIMEOUT, &pt)) - err(1, "DIOCGETTIMEOUT"); - printf("%-20s %10d", pf_timeouts[i].name, pt.seconds); - if (pf_timeouts[i].timeout >= PFTM_ADAPTIVE_START && - pf_timeouts[i].timeout <= PFTM_ADAPTIVE_END) - printf(" states"); - else - printf("s"); - printf("\n"); - } - return (0); - -} - -int -pfctl_show_limits(int dev, int opts) -{ - struct pfioc_limit pl; - int i; - - if (opts & PF_OPT_SHOWALL) - pfctl_print_title("LIMITS:"); - memset(&pl, 0, sizeof(pl)); - for (i = 0; pf_limits[i].name; i++) { - pl.index = pf_limits[i].index; - if (ioctl(dev, DIOCGETLIMIT, &pl)) - err(1, "DIOCGETLIMIT"); - printf("%-13s ", pf_limits[i].name); - if (pl.limit == UINT_MAX) - printf("unlimited\n"); - else - printf("hard limit %8u\n", pl.limit); - } - return (0); -} - -/* callbacks for rule/nat/rdr/addr */ -int -pfctl_add_pool(struct pfctl *pf, struct pf_pool *p, sa_family_t af) -{ - struct pf_pooladdr *pa; - - if ((pf->opts & PF_OPT_NOACTION) == 0) { - if (ioctl(pf->dev, DIOCBEGINADDRS, &pf->paddr)) - err(1, "DIOCBEGINADDRS"); - } - - pf->paddr.af = af; - TAILQ_FOREACH(pa, &p->list, entries) { - memcpy(&pf->paddr.addr, pa, sizeof(struct pf_pooladdr)); - if ((pf->opts & PF_OPT_NOACTION) == 0) { - if (ioctl(pf->dev, DIOCADDADDR, &pf->paddr)) - err(1, "DIOCADDADDR"); - } - } - return (0); -} - -int -pfctl_add_rule(struct pfctl *pf, struct pf_rule *r, const char *anchor_call) -{ - u_int8_t rs_num; - struct pf_rule *rule; - struct pf_ruleset *rs; - char *p; - - rs_num = pf_get_ruleset_number(r->action); - if (rs_num == PF_RULESET_MAX) - errx(1, "Invalid rule type %d", r->action); - - rs = &pf->anchor->ruleset; - - if (anchor_call[0] && r->anchor == NULL) { - /* - * Don't make non-brace anchors part of the main anchor pool. - */ - if ((r->anchor = calloc(1, sizeof(*r->anchor))) == NULL) - err(1, "pfctl_add_rule: calloc"); - - pf_init_ruleset(&r->anchor->ruleset); - r->anchor->ruleset.anchor = r->anchor; - if (strlcpy(r->anchor->path, anchor_call, - sizeof(rule->anchor->path)) >= sizeof(rule->anchor->path)) - errx(1, "pfctl_add_rule: strlcpy"); - if ((p = strrchr(anchor_call, '/')) != NULL) { - if (!strlen(p)) - err(1, "pfctl_add_rule: bad anchor name %s", - anchor_call); - } else - p = (char *)anchor_call; - if (strlcpy(r->anchor->name, p, - sizeof(rule->anchor->name)) >= sizeof(rule->anchor->name)) - errx(1, "pfctl_add_rule: strlcpy"); - } - - if ((rule = calloc(1, sizeof(*rule))) == NULL) - err(1, "calloc"); - bcopy(r, rule, sizeof(*rule)); - TAILQ_INIT(&rule->rpool.list); - pfctl_move_pool(&r->rpool, &rule->rpool); - - TAILQ_INSERT_TAIL(rs->rules[rs_num].active.ptr, rule, entries); - return (0); -} - -int -pfctl_ruleset_trans(struct pfctl *pf, char *path, struct pf_anchor *a) -{ - int osize = pf->trans->pfrb_size; - - if ((pf->loadopt & PFCTL_FLAG_NAT) != 0) { - if (pfctl_add_trans(pf->trans, PF_RULESET_NAT, path) || - pfctl_add_trans(pf->trans, PF_RULESET_BINAT, path) || - pfctl_add_trans(pf->trans, PF_RULESET_RDR, path)) - return (1); - } - if (a == pf->astack[0] && ((altqsupport && - (pf->loadopt & PFCTL_FLAG_ALTQ) != 0))) { - if (pfctl_add_trans(pf->trans, PF_RULESET_ALTQ, path)) - return (2); - } - if ((pf->loadopt & PFCTL_FLAG_FILTER) != 0) { - if (pfctl_add_trans(pf->trans, PF_RULESET_SCRUB, path) || - pfctl_add_trans(pf->trans, PF_RULESET_FILTER, path)) - return (3); - } - if (pf->loadopt & PFCTL_FLAG_TABLE) - if (pfctl_add_trans(pf->trans, PF_RULESET_TABLE, path)) - return (4); - if (pfctl_trans(pf->dev, pf->trans, DIOCXBEGIN, osize)) - return (5); - - return (0); -} - -int -pfctl_load_ruleset(struct pfctl *pf, char *path, struct pf_ruleset *rs, - int rs_num, int depth) -{ - struct pf_rule *r; - int error, len = strlen(path); - int brace = 0; - - pf->anchor = rs->anchor; - - if (path[0]) - snprintf(&path[len], MAXPATHLEN - len, "/%s", pf->anchor->name); - else - snprintf(&path[len], MAXPATHLEN - len, "%s", pf->anchor->name); - - if (depth) { - if (TAILQ_FIRST(rs->rules[rs_num].active.ptr) != NULL) { - brace++; - if (pf->opts & PF_OPT_VERBOSE) - printf(" {\n"); - if ((pf->opts & PF_OPT_NOACTION) == 0 && - (error = pfctl_ruleset_trans(pf, - path, rs->anchor))) { - printf("pfctl_load_rulesets: " - "pfctl_ruleset_trans %d\n", error); - goto error; - } - } else if (pf->opts & PF_OPT_VERBOSE) - printf("\n"); - - } - - if (pf->optimize && rs_num == PF_RULESET_FILTER) - pfctl_optimize_ruleset(pf, rs); - - while ((r = TAILQ_FIRST(rs->rules[rs_num].active.ptr)) != NULL) { - TAILQ_REMOVE(rs->rules[rs_num].active.ptr, r, entries); - if ((error = pfctl_load_rule(pf, path, r, depth))) - goto error; - if (r->anchor) { - if ((error = pfctl_load_ruleset(pf, path, - &r->anchor->ruleset, rs_num, depth + 1))) - goto error; - } else if (pf->opts & PF_OPT_VERBOSE) - printf("\n"); - free(r); - } - if (brace && pf->opts & PF_OPT_VERBOSE) { - INDENT(depth - 1, (pf->opts & PF_OPT_VERBOSE)); - printf("}\n"); - } - path[len] = '\0'; - return (0); - - error: - path[len] = '\0'; - return (error); - -} - -int -pfctl_load_rule(struct pfctl *pf, char *path, struct pf_rule *r, int depth) -{ - u_int8_t rs_num = pf_get_ruleset_number(r->action); - char *name; - struct pfioc_rule pr; - int len = strlen(path); - - bzero(&pr, sizeof(pr)); - /* set up anchor before adding to path for anchor_call */ - if ((pf->opts & PF_OPT_NOACTION) == 0) - pr.ticket = pfctl_get_ticket(pf->trans, rs_num, path); - if (strlcpy(pr.anchor, path, sizeof(pr.anchor)) >= sizeof(pr.anchor)) - errx(1, "pfctl_load_rule: strlcpy"); - - if (r->anchor) { - if (r->anchor->match) { - if (path[0]) - snprintf(&path[len], MAXPATHLEN - len, - "/%s", r->anchor->name); - else - snprintf(&path[len], MAXPATHLEN - len, - "%s", r->anchor->name); - name = path; - } else - name = r->anchor->path; - } else - name = ""; - - if ((pf->opts & PF_OPT_NOACTION) == 0) { - if (pfctl_add_pool(pf, &r->rpool, r->af)) - return (1); - pr.pool_ticket = pf->paddr.ticket; - memcpy(&pr.rule, r, sizeof(pr.rule)); - if (r->anchor && strlcpy(pr.anchor_call, name, - sizeof(pr.anchor_call)) >= sizeof(pr.anchor_call)) - errx(1, "pfctl_load_rule: strlcpy"); - if (ioctl(pf->dev, DIOCADDRULE, &pr)) - err(1, "DIOCADDRULE"); - } - - if (pf->opts & PF_OPT_VERBOSE) { - INDENT(depth, !(pf->opts & PF_OPT_VERBOSE2)); - print_rule(r, r->anchor ? r->anchor->name : "", - pf->opts & PF_OPT_VERBOSE2, - pf->opts & PF_OPT_NUMERIC); - } - path[len] = '\0'; - pfctl_clear_pool(&r->rpool); - return (0); -} - -int -pfctl_add_altq(struct pfctl *pf, struct pf_altq *a) -{ - if (altqsupport && - (loadopt & PFCTL_FLAG_ALTQ) != 0) { - memcpy(&pf->paltq->altq, a, sizeof(struct pf_altq)); - if ((pf->opts & PF_OPT_NOACTION) == 0) { - if (ioctl(pf->dev, DIOCADDALTQ, pf->paltq)) { - if (errno == ENXIO) - errx(1, "qtype not configured"); - else if (errno == ENODEV) - errx(1, "%s: driver does not support " - "altq", a->ifname); - else - err(1, "DIOCADDALTQ"); - } - } - pfaltq_store(&pf->paltq->altq); - } - return (0); -} - -int -pfctl_rules(int dev, char *filename, int opts, int optimize, - char *anchorname, struct pfr_buffer *trans) -{ -#define ERR(x) do { warn(x); goto _error; } while(0) -#define ERRX(x) do { warnx(x); goto _error; } while(0) - - struct pfr_buffer *t, buf; - struct pfioc_altq pa; - struct pfctl pf; - struct pf_ruleset *rs; - struct pfr_table trs; - char *path; - int osize; - - RB_INIT(&pf_anchors); - memset(&pf_main_anchor, 0, sizeof(pf_main_anchor)); - pf_init_ruleset(&pf_main_anchor.ruleset); - pf_main_anchor.ruleset.anchor = &pf_main_anchor; - if (trans == NULL) { - bzero(&buf, sizeof(buf)); - buf.pfrb_type = PFRB_TRANS; - t = &buf; - osize = 0; - } else { - t = trans; - osize = t->pfrb_size; - } - - memset(&pa, 0, sizeof(pa)); - memset(&pf, 0, sizeof(pf)); - memset(&trs, 0, sizeof(trs)); - if ((path = calloc(1, MAXPATHLEN)) == NULL) - ERRX("pfctl_rules: calloc"); - if (strlcpy(trs.pfrt_anchor, anchorname, - sizeof(trs.pfrt_anchor)) >= sizeof(trs.pfrt_anchor)) - ERRX("pfctl_rules: strlcpy"); - pf.dev = dev; - pf.opts = opts; - pf.optimize = optimize; - pf.loadopt = loadopt; - - /* non-brace anchor, create without resolving the path */ - if ((pf.anchor = calloc(1, sizeof(*pf.anchor))) == NULL) - ERRX("pfctl_rules: calloc"); - rs = &pf.anchor->ruleset; - pf_init_ruleset(rs); - rs->anchor = pf.anchor; - if (strlcpy(pf.anchor->path, anchorname, - sizeof(pf.anchor->path)) >= sizeof(pf.anchor->path)) - errx(1, "pfctl_add_rule: strlcpy"); - if (strlcpy(pf.anchor->name, anchorname, - sizeof(pf.anchor->name)) >= sizeof(pf.anchor->name)) - errx(1, "pfctl_add_rule: strlcpy"); - - - pf.astack[0] = pf.anchor; - pf.asd = 0; - if (anchorname[0]) - pf.loadopt &= ~PFCTL_FLAG_ALTQ; - pf.paltq = &pa; - pf.trans = t; - pfctl_init_options(&pf); - - if ((opts & PF_OPT_NOACTION) == 0) { - /* - * XXX For the time being we need to open transactions for - * the main ruleset before parsing, because tables are still - * loaded at parse time. - */ - if (pfctl_ruleset_trans(&pf, anchorname, pf.anchor)) - ERRX("pfctl_rules"); - if (altqsupport && (pf.loadopt & PFCTL_FLAG_ALTQ)) - pa.ticket = - pfctl_get_ticket(t, PF_RULESET_ALTQ, anchorname); - if (pf.loadopt & PFCTL_FLAG_TABLE) - pf.astack[0]->ruleset.tticket = - pfctl_get_ticket(t, PF_RULESET_TABLE, anchorname); - } - - if (parse_config(filename, &pf) < 0) { - if ((opts & PF_OPT_NOACTION) == 0) - ERRX("Syntax error in config file: " - "pf rules not loaded"); - else - goto _error; - } - - if ((pf.loadopt & PFCTL_FLAG_FILTER && - (pfctl_load_ruleset(&pf, path, rs, PF_RULESET_SCRUB, 0))) || - (pf.loadopt & PFCTL_FLAG_NAT && - (pfctl_load_ruleset(&pf, path, rs, PF_RULESET_NAT, 0) || - pfctl_load_ruleset(&pf, path, rs, PF_RULESET_RDR, 0) || - pfctl_load_ruleset(&pf, path, rs, PF_RULESET_BINAT, 0))) || - (pf.loadopt & PFCTL_FLAG_FILTER && - pfctl_load_ruleset(&pf, path, rs, PF_RULESET_FILTER, 0))) { - if ((opts & PF_OPT_NOACTION) == 0) - ERRX("Unable to load rules into kernel"); - else - goto _error; - } - - if ((altqsupport && (pf.loadopt & PFCTL_FLAG_ALTQ) != 0)) - if (check_commit_altq(dev, opts) != 0) - ERRX("errors in altq config"); - - /* process "load anchor" directives */ - if (!anchorname[0]) - if (pfctl_load_anchors(dev, &pf, t) == -1) - ERRX("load anchors"); - - if (trans == NULL && (opts & PF_OPT_NOACTION) == 0) { - if (!anchorname[0]) - if (pfctl_load_options(&pf)) - goto _error; - if (pfctl_trans(dev, t, DIOCXCOMMIT, osize)) - ERR("DIOCXCOMMIT"); - } - return (0); - -_error: - if (trans == NULL) { /* main ruleset */ - if ((opts & PF_OPT_NOACTION) == 0) - if (pfctl_trans(dev, t, DIOCXROLLBACK, osize)) - err(1, "DIOCXROLLBACK"); - exit(1); - } else { /* sub ruleset */ - return (-1); - } - -#undef ERR -#undef ERRX -} - -FILE * -pfctl_fopen(const char *name, const char *mode) -{ - struct stat st; - FILE *fp; - - fp = fopen(name, mode); - if (fp == NULL) - return (NULL); - if (fstat(fileno(fp), &st)) { - fclose(fp); - return (NULL); - } - if (S_ISDIR(st.st_mode)) { - fclose(fp); - errno = EISDIR; - return (NULL); - } - return (fp); -} - -void -pfctl_init_options(struct pfctl *pf) -{ - - pf->timeout[PFTM_TCP_FIRST_PACKET] = PFTM_TCP_FIRST_PACKET_VAL; - pf->timeout[PFTM_TCP_OPENING] = PFTM_TCP_OPENING_VAL; - pf->timeout[PFTM_TCP_ESTABLISHED] = PFTM_TCP_ESTABLISHED_VAL; - pf->timeout[PFTM_TCP_CLOSING] = PFTM_TCP_CLOSING_VAL; - pf->timeout[PFTM_TCP_FIN_WAIT] = PFTM_TCP_FIN_WAIT_VAL; - pf->timeout[PFTM_TCP_CLOSED] = PFTM_TCP_CLOSED_VAL; - pf->timeout[PFTM_UDP_FIRST_PACKET] = PFTM_UDP_FIRST_PACKET_VAL; - pf->timeout[PFTM_UDP_SINGLE] = PFTM_UDP_SINGLE_VAL; - pf->timeout[PFTM_UDP_MULTIPLE] = PFTM_UDP_MULTIPLE_VAL; - pf->timeout[PFTM_ICMP_FIRST_PACKET] = PFTM_ICMP_FIRST_PACKET_VAL; - pf->timeout[PFTM_ICMP_ERROR_REPLY] = PFTM_ICMP_ERROR_REPLY_VAL; - pf->timeout[PFTM_OTHER_FIRST_PACKET] = PFTM_OTHER_FIRST_PACKET_VAL; - pf->timeout[PFTM_OTHER_SINGLE] = PFTM_OTHER_SINGLE_VAL; - pf->timeout[PFTM_OTHER_MULTIPLE] = PFTM_OTHER_MULTIPLE_VAL; - pf->timeout[PFTM_FRAG] = PFTM_FRAG_VAL; - pf->timeout[PFTM_INTERVAL] = PFTM_INTERVAL_VAL; - pf->timeout[PFTM_SRC_NODE] = PFTM_SRC_NODE_VAL; - pf->timeout[PFTM_TS_DIFF] = PFTM_TS_DIFF_VAL; - pf->timeout[PFTM_ADAPTIVE_START] = PFSTATE_ADAPT_START; - pf->timeout[PFTM_ADAPTIVE_END] = PFSTATE_ADAPT_END; - - pf->limit[PF_LIMIT_STATES] = PFSTATE_HIWAT; - pf->limit[PF_LIMIT_FRAGS] = PFFRAG_FRENT_HIWAT; - pf->limit[PF_LIMIT_SRC_NODES] = PFSNODE_HIWAT; - pf->limit[PF_LIMIT_TABLE_ENTRIES] = PFR_KENTRY_HIWAT; - - pf->debug = PF_DEBUG_URGENT; -} - -int -pfctl_load_options(struct pfctl *pf) -{ - int i, error = 0; - - if ((loadopt & PFCTL_FLAG_OPTION) == 0) - return (0); - - /* load limits */ - for (i = 0; i < PF_LIMIT_MAX; i++) { - if ((pf->opts & PF_OPT_MERGE) && !pf->limit_set[i]) - continue; - if (pfctl_load_limit(pf, i, pf->limit[i])) - error = 1; - } - - /* - * If we've set the limit, but haven't explicitly set adaptive - * timeouts, do it now with a start of 60% and end of 120%. - */ - if (pf->limit_set[PF_LIMIT_STATES] && - !pf->timeout_set[PFTM_ADAPTIVE_START] && - !pf->timeout_set[PFTM_ADAPTIVE_END]) { - pf->timeout[PFTM_ADAPTIVE_START] = - (pf->limit[PF_LIMIT_STATES] / 10) * 6; - pf->timeout_set[PFTM_ADAPTIVE_START] = 1; - pf->timeout[PFTM_ADAPTIVE_END] = - (pf->limit[PF_LIMIT_STATES] / 10) * 12; - pf->timeout_set[PFTM_ADAPTIVE_END] = 1; - } - - /* load timeouts */ - for (i = 0; i < PFTM_MAX; i++) { - if ((pf->opts & PF_OPT_MERGE) && !pf->timeout_set[i]) - continue; - if (pfctl_load_timeout(pf, i, pf->timeout[i])) - error = 1; - } - - /* load debug */ - if (!(pf->opts & PF_OPT_MERGE) || pf->debug_set) - if (pfctl_load_debug(pf, pf->debug)) - error = 1; - - /* load logif */ - if (!(pf->opts & PF_OPT_MERGE) || pf->ifname_set) - if (pfctl_load_logif(pf, pf->ifname)) - error = 1; - - /* load hostid */ - if (!(pf->opts & PF_OPT_MERGE) || pf->hostid_set) - if (pfctl_load_hostid(pf, pf->hostid)) - error = 1; - - return (error); -} - -int -pfctl_set_limit(struct pfctl *pf, const char *opt, unsigned int limit) -{ - int i; - - - for (i = 0; pf_limits[i].name; i++) { - if (strcasecmp(opt, pf_limits[i].name) == 0) { - pf->limit[pf_limits[i].index] = limit; - pf->limit_set[pf_limits[i].index] = 1; - break; - } - } - if (pf_limits[i].name == NULL) { - warnx("Bad pool name."); - return (1); - } - - if (pf->opts & PF_OPT_VERBOSE) - printf("set limit %s %d\n", opt, limit); - - return (0); -} - -int -pfctl_load_limit(struct pfctl *pf, unsigned int index, unsigned int limit) -{ - struct pfioc_limit pl; - - memset(&pl, 0, sizeof(pl)); - pl.index = index; - pl.limit = limit; - if (ioctl(pf->dev, DIOCSETLIMIT, &pl)) { - if (errno == EBUSY) - warnx("Current pool size exceeds requested hard limit"); - else - warnx("DIOCSETLIMIT"); - return (1); - } - return (0); -} - -int -pfctl_set_timeout(struct pfctl *pf, const char *opt, int seconds, int quiet) -{ - int i; - - if ((loadopt & PFCTL_FLAG_OPTION) == 0) - return (0); - - for (i = 0; pf_timeouts[i].name; i++) { - if (strcasecmp(opt, pf_timeouts[i].name) == 0) { - pf->timeout[pf_timeouts[i].timeout] = seconds; - pf->timeout_set[pf_timeouts[i].timeout] = 1; - break; - } - } - - if (pf_timeouts[i].name == NULL) { - warnx("Bad timeout name."); - return (1); - } - - - if (pf->opts & PF_OPT_VERBOSE && ! quiet) - printf("set timeout %s %d\n", opt, seconds); - - return (0); -} - -int -pfctl_load_timeout(struct pfctl *pf, unsigned int timeout, unsigned int seconds) -{ - struct pfioc_tm pt; - - memset(&pt, 0, sizeof(pt)); - pt.timeout = timeout; - pt.seconds = seconds; - if (ioctl(pf->dev, DIOCSETTIMEOUT, &pt)) { - warnx("DIOCSETTIMEOUT"); - return (1); - } - return (0); -} - -int -pfctl_set_optimization(struct pfctl *pf, const char *opt) -{ - const struct pf_hint *hint; - int i, r; - - if ((loadopt & PFCTL_FLAG_OPTION) == 0) - return (0); - - for (i = 0; pf_hints[i].name; i++) - if (strcasecmp(opt, pf_hints[i].name) == 0) - break; - - hint = pf_hints[i].hint; - if (hint == NULL) { - warnx("invalid state timeouts optimization"); - return (1); - } - - for (i = 0; hint[i].name; i++) - if ((r = pfctl_set_timeout(pf, hint[i].name, - hint[i].timeout, 1))) - return (r); - - if (pf->opts & PF_OPT_VERBOSE) - printf("set optimization %s\n", opt); - - return (0); -} - -int -pfctl_set_logif(struct pfctl *pf, char *ifname) -{ - - if ((loadopt & PFCTL_FLAG_OPTION) == 0) - return (0); - - if (!strcmp(ifname, "none")) { - free(pf->ifname); - pf->ifname = NULL; - } else { - pf->ifname = strdup(ifname); - if (!pf->ifname) - errx(1, "pfctl_set_logif: strdup"); - } - pf->ifname_set = 1; - - if (pf->opts & PF_OPT_VERBOSE) - printf("set loginterface %s\n", ifname); - - return (0); -} - -int -pfctl_load_logif(struct pfctl *pf, char *ifname) -{ - struct pfioc_if pi; - - memset(&pi, 0, sizeof(pi)); - if (ifname && strlcpy(pi.ifname, ifname, - sizeof(pi.ifname)) >= sizeof(pi.ifname)) { - warnx("pfctl_load_logif: strlcpy"); - return (1); - } - if (ioctl(pf->dev, DIOCSETSTATUSIF, &pi)) { - warnx("DIOCSETSTATUSIF"); - return (1); - } - return (0); -} - -int -pfctl_set_hostid(struct pfctl *pf, u_int32_t hostid) -{ - if ((loadopt & PFCTL_FLAG_OPTION) == 0) - return (0); - - HTONL(hostid); - - pf->hostid = hostid; - pf->hostid_set = 1; - - if (pf->opts & PF_OPT_VERBOSE) - printf("set hostid 0x%08x\n", ntohl(hostid)); - - return (0); -} - -int -pfctl_load_hostid(struct pfctl *pf, u_int32_t hostid) -{ - if (ioctl(dev, DIOCSETHOSTID, &hostid)) { - warnx("DIOCSETHOSTID"); - return (1); - } - return (0); -} - -int -pfctl_set_debug(struct pfctl *pf, char *d) -{ - u_int32_t level; - - if ((loadopt & PFCTL_FLAG_OPTION) == 0) - return (0); - - if (!strcmp(d, "none")) - pf->debug = PF_DEBUG_NONE; - else if (!strcmp(d, "urgent")) - pf->debug = PF_DEBUG_URGENT; - else if (!strcmp(d, "misc")) - pf->debug = PF_DEBUG_MISC; - else if (!strcmp(d, "loud")) - pf->debug = PF_DEBUG_NOISY; - else { - warnx("unknown debug level \"%s\"", d); - return (-1); - } - - pf->debug_set = 1; - - if ((pf->opts & PF_OPT_NOACTION) == 0) - if (ioctl(dev, DIOCSETDEBUG, &level)) - err(1, "DIOCSETDEBUG"); - - if (pf->opts & PF_OPT_VERBOSE) - printf("set debug %s\n", d); - - return (0); -} - -int -pfctl_load_debug(struct pfctl *pf, unsigned int level) -{ - if (ioctl(pf->dev, DIOCSETDEBUG, &level)) { - warnx("DIOCSETDEBUG"); - return (1); - } - return (0); -} - -int -pfctl_set_interface_flags(struct pfctl *pf, char *ifname, int flags, int how) -{ - struct pfioc_iface pi; - - if ((loadopt & PFCTL_FLAG_OPTION) == 0) - return (0); - - bzero(&pi, sizeof(pi)); - - pi.pfiio_flags = flags; - - if (strlcpy(pi.pfiio_name, ifname, sizeof(pi.pfiio_name)) >= - sizeof(pi.pfiio_name)) - errx(1, "pfctl_set_interface_flags: strlcpy"); - - if ((pf->opts & PF_OPT_NOACTION) == 0) { - if (how == 0) { - if (ioctl(pf->dev, DIOCCLRIFFLAG, &pi)) - err(1, "DIOCCLRIFFLAG"); - } else { - if (ioctl(pf->dev, DIOCSETIFFLAG, &pi)) - err(1, "DIOCSETIFFLAG"); - } - } - return (0); -} - -void -pfctl_debug(int dev, u_int32_t level, int opts) -{ - if (ioctl(dev, DIOCSETDEBUG, &level)) - err(1, "DIOCSETDEBUG"); - if ((opts & PF_OPT_QUIET) == 0) { - fprintf(stderr, "debug level set to '"); - switch (level) { - case PF_DEBUG_NONE: - fprintf(stderr, "none"); - break; - case PF_DEBUG_URGENT: - fprintf(stderr, "urgent"); - break; - case PF_DEBUG_MISC: - fprintf(stderr, "misc"); - break; - case PF_DEBUG_NOISY: - fprintf(stderr, "loud"); - break; - default: - fprintf(stderr, "<invalid>"); - break; - } - fprintf(stderr, "'\n"); - } -} - -int -pfctl_test_altqsupport(int dev, int opts) -{ - struct pfioc_altq pa; - - if (ioctl(dev, DIOCGETALTQS, &pa)) { - if (errno == ENODEV) { - if (!(opts & PF_OPT_QUIET)) - fprintf(stderr, "No ALTQ support in kernel\n" - "ALTQ related functions disabled\n"); - return (0); - } else - err(1, "DIOCGETALTQS"); - } - return (1); -} - -int -pfctl_show_anchors(int dev, int opts, char *anchorname) -{ - struct pfioc_ruleset pr; - u_int32_t mnr, nr; - - memset(&pr, 0, sizeof(pr)); - memcpy(pr.path, anchorname, sizeof(pr.path)); - if (ioctl(dev, DIOCGETRULESETS, &pr)) { - if (errno == EINVAL) - fprintf(stderr, "Anchor '%s' not found.\n", - anchorname); - else - err(1, "DIOCGETRULESETS"); - return (-1); - } - mnr = pr.nr; - for (nr = 0; nr < mnr; ++nr) { - char sub[MAXPATHLEN]; - - pr.nr = nr; - if (ioctl(dev, DIOCGETRULESET, &pr)) - err(1, "DIOCGETRULESET"); - if (!strcmp(pr.name, PF_RESERVED_ANCHOR)) - continue; - sub[0] = 0; - if (pr.path[0]) { - strlcat(sub, pr.path, sizeof(sub)); - strlcat(sub, "/", sizeof(sub)); - } - strlcat(sub, pr.name, sizeof(sub)); - if (sub[0] != '_' || (opts & PF_OPT_VERBOSE)) - printf(" %s\n", sub); - if ((opts & PF_OPT_VERBOSE) && pfctl_show_anchors(dev, opts, sub)) - return (-1); - } - return (0); -} - -const char * -pfctl_lookup_option(char *cmd, const char **list) -{ - if (cmd != NULL && *cmd) - for (; *list; list++) - if (!strncmp(cmd, *list, strlen(cmd))) - return (*list); - return (NULL); -} - -int -main(int argc, char *argv[]) -{ - int error = 0; - int ch; - int mode = O_RDONLY; - int opts = 0; - int optimize = PF_OPTIMIZE_BASIC; - char anchorname[MAXPATHLEN]; - char *path; - - if (argc < 2) - usage(); - - while ((ch = getopt(argc, argv, - "a:AdD:eqf:F:ghi:k:K:mnNOo:Pp:rRs:t:T:vx:z")) != -1) { - switch (ch) { - case 'a': - anchoropt = optarg; - break; - case 'd': - opts |= PF_OPT_DISABLE; - mode = O_RDWR; - break; - case 'D': - if (pfctl_cmdline_symset(optarg) < 0) - warnx("could not parse macro definition %s", - optarg); - break; - case 'e': - opts |= PF_OPT_ENABLE; - mode = O_RDWR; - break; - case 'q': - opts |= PF_OPT_QUIET; - break; - case 'F': - clearopt = pfctl_lookup_option(optarg, clearopt_list); - if (clearopt == NULL) { - warnx("Unknown flush modifier '%s'", optarg); - usage(); - } - mode = O_RDWR; - break; - case 'i': - ifaceopt = optarg; - break; - case 'k': - if (state_killers >= 2) { - warnx("can only specify -k twice"); - usage(); - /* NOTREACHED */ - } - state_kill[state_killers++] = optarg; - mode = O_RDWR; - break; - case 'K': - if (src_node_killers >= 2) { - warnx("can only specify -K twice"); - usage(); - /* NOTREACHED */ - } - src_node_kill[src_node_killers++] = optarg; - mode = O_RDWR; - break; - case 'm': - opts |= PF_OPT_MERGE; - break; - case 'n': - opts |= PF_OPT_NOACTION; - break; - case 'N': - loadopt |= PFCTL_FLAG_NAT; - break; - case 'r': - opts |= PF_OPT_USEDNS; - break; - case 'f': - rulesopt = optarg; - mode = O_RDWR; - break; - case 'g': - opts |= PF_OPT_DEBUG; - break; - case 'A': - loadopt |= PFCTL_FLAG_ALTQ; - break; - case 'R': - loadopt |= PFCTL_FLAG_FILTER; - break; - case 'o': - optiopt = pfctl_lookup_option(optarg, optiopt_list); - if (optiopt == NULL) { - warnx("Unknown optimization '%s'", optarg); - usage(); - } - opts |= PF_OPT_OPTIMIZE; - break; - case 'O': - loadopt |= PFCTL_FLAG_OPTION; - break; - case 'p': - pf_device = optarg; - break; - case 'P': - opts |= PF_OPT_NUMERIC; - break; - case 's': - showopt = pfctl_lookup_option(optarg, showopt_list); - if (showopt == NULL) { - warnx("Unknown show modifier '%s'", optarg); - usage(); - } - break; - case 't': - tableopt = optarg; - break; - case 'T': - tblcmdopt = pfctl_lookup_option(optarg, tblcmdopt_list); - if (tblcmdopt == NULL) { - warnx("Unknown table command '%s'", optarg); - usage(); - } - break; - case 'v': - if (opts & PF_OPT_VERBOSE) - opts |= PF_OPT_VERBOSE2; - opts |= PF_OPT_VERBOSE; - break; - case 'x': - debugopt = pfctl_lookup_option(optarg, debugopt_list); - if (debugopt == NULL) { - warnx("Unknown debug level '%s'", optarg); - usage(); - } - mode = O_RDWR; - break; - case 'z': - opts |= PF_OPT_CLRRULECTRS; - mode = O_RDWR; - break; - case 'h': - /* FALLTHROUGH */ - default: - usage(); - /* NOTREACHED */ - } - } - - if (tblcmdopt != NULL) { - argc -= optind; - argv += optind; - ch = *tblcmdopt; - if (ch == 'l') { - loadopt |= PFCTL_FLAG_TABLE; - tblcmdopt = NULL; - } else - mode = strchr("acdefkrz", ch) ? O_RDWR : O_RDONLY; - } else if (argc != optind) { - warnx("unknown command line argument: %s ...", argv[optind]); - usage(); - /* NOTREACHED */ - } - if (loadopt == 0) - loadopt = ~0; - - if ((path = calloc(1, MAXPATHLEN)) == NULL) - errx(1, "pfctl: calloc"); - memset(anchorname, 0, sizeof(anchorname)); - if (anchoropt != NULL) { - int len = strlen(anchoropt); - - if (anchoropt[len - 1] == '*') { - if (len >= 2 && anchoropt[len - 2] == '/') - anchoropt[len - 2] = '\0'; - else - anchoropt[len - 1] = '\0'; - opts |= PF_OPT_RECURSE; - } - if (strlcpy(anchorname, anchoropt, - sizeof(anchorname)) >= sizeof(anchorname)) - errx(1, "anchor name '%s' too long", - anchoropt); - loadopt &= PFCTL_FLAG_FILTER|PFCTL_FLAG_NAT|PFCTL_FLAG_TABLE; - } - - if ((opts & PF_OPT_NOACTION) == 0) { - dev = open(pf_device, mode); - if (dev == -1) - err(1, "%s", pf_device); - altqsupport = pfctl_test_altqsupport(dev, opts); - } else { - dev = open(pf_device, O_RDONLY); - if (dev >= 0) - opts |= PF_OPT_DUMMYACTION; - /* turn off options */ - opts &= ~ (PF_OPT_DISABLE | PF_OPT_ENABLE); - clearopt = showopt = debugopt = NULL; -#if defined(__FreeBSD__) && !defined(ENABLE_ALTQ) - altqsupport = 0; -#else - altqsupport = 1; -#endif - } - - if (opts & PF_OPT_DISABLE) - if (pfctl_disable(dev, opts)) - error = 1; - - if (showopt != NULL) { - switch (*showopt) { - case 'A': - pfctl_show_anchors(dev, opts, anchorname); - break; - case 'r': - pfctl_load_fingerprints(dev, opts); - pfctl_show_rules(dev, path, opts, PFCTL_SHOW_RULES, - anchorname, 0); - break; - case 'l': - pfctl_load_fingerprints(dev, opts); - pfctl_show_rules(dev, path, opts, PFCTL_SHOW_LABELS, - anchorname, 0); - break; - case 'n': - pfctl_load_fingerprints(dev, opts); - pfctl_show_nat(dev, opts, anchorname); - break; - case 'q': - pfctl_show_altq(dev, ifaceopt, opts, - opts & PF_OPT_VERBOSE2); - break; - case 's': - pfctl_show_states(dev, ifaceopt, opts); - break; - case 'S': - pfctl_show_src_nodes(dev, opts); - break; - case 'i': - pfctl_show_status(dev, opts); - break; - case 't': - pfctl_show_timeouts(dev, opts); - break; - case 'm': - pfctl_show_limits(dev, opts); - break; - case 'a': - opts |= PF_OPT_SHOWALL; - pfctl_load_fingerprints(dev, opts); - - pfctl_show_nat(dev, opts, anchorname); - pfctl_show_rules(dev, path, opts, 0, anchorname, 0); - pfctl_show_altq(dev, ifaceopt, opts, 0); - pfctl_show_states(dev, ifaceopt, opts); - pfctl_show_src_nodes(dev, opts); - pfctl_show_status(dev, opts); - pfctl_show_rules(dev, path, opts, 1, anchorname, 0); - pfctl_show_timeouts(dev, opts); - pfctl_show_limits(dev, opts); - pfctl_show_tables(anchorname, opts); - pfctl_show_fingerprints(opts); - break; - case 'T': - pfctl_show_tables(anchorname, opts); - break; - case 'o': - pfctl_load_fingerprints(dev, opts); - pfctl_show_fingerprints(opts); - break; - case 'I': - pfctl_show_ifaces(ifaceopt, opts); - break; - } - } - - if ((opts & PF_OPT_CLRRULECTRS) && showopt == NULL) - pfctl_show_rules(dev, path, opts, PFCTL_SHOW_NOTHING, - anchorname, 0); - - if (clearopt != NULL) { - if (anchorname[0] == '_' || strstr(anchorname, "/_") != NULL) - errx(1, "anchor names beginning with '_' cannot " - "be modified from the command line"); - - switch (*clearopt) { - case 'r': - pfctl_clear_rules(dev, opts, anchorname); - break; - case 'n': - pfctl_clear_nat(dev, opts, anchorname); - break; - case 'q': - pfctl_clear_altq(dev, opts); - break; - case 's': - pfctl_clear_states(dev, ifaceopt, opts); - break; - case 'S': - pfctl_clear_src_nodes(dev, opts); - break; - case 'i': - pfctl_clear_stats(dev, opts); - break; - case 'a': - pfctl_clear_rules(dev, opts, anchorname); - pfctl_clear_nat(dev, opts, anchorname); - pfctl_clear_tables(anchorname, opts); - if (!*anchorname) { - pfctl_clear_altq(dev, opts); - pfctl_clear_states(dev, ifaceopt, opts); - pfctl_clear_src_nodes(dev, opts); - pfctl_clear_stats(dev, opts); - pfctl_clear_fingerprints(dev, opts); - pfctl_clear_interface_flags(dev, opts); - } - break; - case 'o': - pfctl_clear_fingerprints(dev, opts); - break; - case 'T': - pfctl_clear_tables(anchorname, opts); - break; - } - } - if (state_killers) { - if (!strcmp(state_kill[0], "label")) - pfctl_label_kill_states(dev, ifaceopt, opts); - else if (!strcmp(state_kill[0], "id")) - pfctl_id_kill_states(dev, ifaceopt, opts); - else - pfctl_net_kill_states(dev, ifaceopt, opts); - } - - if (src_node_killers) - pfctl_kill_src_nodes(dev, ifaceopt, opts); - - if (tblcmdopt != NULL) { - error = pfctl_command_tables(argc, argv, tableopt, - tblcmdopt, rulesopt, anchorname, opts); - rulesopt = NULL; - } - if (optiopt != NULL) { - switch (*optiopt) { - case 'n': - optimize = 0; - break; - case 'b': - optimize |= PF_OPTIMIZE_BASIC; - break; - case 'o': - case 'p': - optimize |= PF_OPTIMIZE_PROFILE; - break; - } - } - - if ((rulesopt != NULL) && (loadopt & PFCTL_FLAG_OPTION) && - !anchorname[0]) - if (pfctl_clear_interface_flags(dev, opts | PF_OPT_QUIET)) - error = 1; - - if (rulesopt != NULL && !(opts & (PF_OPT_MERGE|PF_OPT_NOACTION)) && - !anchorname[0] && (loadopt & PFCTL_FLAG_OPTION)) - if (pfctl_file_fingerprints(dev, opts, PF_OSFP_FILE)) - error = 1; - - if (rulesopt != NULL) { - if (anchorname[0] == '_' || strstr(anchorname, "/_") != NULL) - errx(1, "anchor names beginning with '_' cannot " - "be modified from the command line"); - if (pfctl_rules(dev, rulesopt, opts, optimize, - anchorname, NULL)) - error = 1; - else if (!(opts & PF_OPT_NOACTION) && - (loadopt & PFCTL_FLAG_TABLE)) - warn_namespace_collision(NULL); - } - - if (opts & PF_OPT_ENABLE) - if (pfctl_enable(dev, opts)) - error = 1; - - if (debugopt != NULL) { - switch (*debugopt) { - case 'n': - pfctl_debug(dev, PF_DEBUG_NONE, opts); - break; - case 'u': - pfctl_debug(dev, PF_DEBUG_URGENT, opts); - break; - case 'm': - pfctl_debug(dev, PF_DEBUG_MISC, opts); - break; - case 'l': - pfctl_debug(dev, PF_DEBUG_NOISY, opts); - break; - } - } - - exit(error); -} diff --git a/contrib/pf/pfctl/pfctl.h b/contrib/pf/pfctl/pfctl.h deleted file mode 100644 index 2c69bc2..0000000 --- a/contrib/pf/pfctl/pfctl.h +++ /dev/null @@ -1,130 +0,0 @@ -/* $OpenBSD: pfctl.h,v 1.42 2007/12/05 12:01:47 chl Exp $ */ - -/* - * Copyright (c) 2001 Daniel Hartmeier - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - 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. - * - * $FreeBSD$ - */ - -#ifndef _PFCTL_H_ -#define _PFCTL_H_ - -enum pfctl_show { PFCTL_SHOW_RULES, PFCTL_SHOW_LABELS, PFCTL_SHOW_NOTHING }; - -enum { PFRB_TABLES = 1, PFRB_TSTATS, PFRB_ADDRS, PFRB_ASTATS, - PFRB_IFACES, PFRB_TRANS, PFRB_MAX }; -struct pfr_buffer { - int pfrb_type; /* type of content, see enum above */ - int pfrb_size; /* number of objects in buffer */ - int pfrb_msize; /* maximum number of objects in buffer */ - void *pfrb_caddr; /* malloc'ated memory area */ -}; -#define PFRB_FOREACH(var, buf) \ - for ((var) = pfr_buf_next((buf), NULL); \ - (var) != NULL; \ - (var) = pfr_buf_next((buf), (var))) - -int pfr_get_fd(void); -int pfr_clr_tables(struct pfr_table *, int *, int); -int pfr_add_tables(struct pfr_table *, int, int *, int); -int pfr_del_tables(struct pfr_table *, int, int *, int); -int pfr_get_tables(struct pfr_table *, struct pfr_table *, int *, int); -int pfr_get_tstats(struct pfr_table *, struct pfr_tstats *, int *, int); -int pfr_clr_tstats(struct pfr_table *, int, int *, int); -int pfr_clr_addrs(struct pfr_table *, int *, int); -int pfr_add_addrs(struct pfr_table *, struct pfr_addr *, int, int *, int); -int pfr_del_addrs(struct pfr_table *, struct pfr_addr *, int, int *, int); -int pfr_set_addrs(struct pfr_table *, struct pfr_addr *, int, int *, - int *, int *, int *, int); -int pfr_get_addrs(struct pfr_table *, struct pfr_addr *, int *, int); -int pfr_get_astats(struct pfr_table *, struct pfr_astats *, int *, int); -int pfr_tst_addrs(struct pfr_table *, struct pfr_addr *, int, int *, int); -int pfr_ina_define(struct pfr_table *, struct pfr_addr *, int, int *, - int *, int, int); -void pfr_buf_clear(struct pfr_buffer *); -int pfr_buf_add(struct pfr_buffer *, const void *); -void *pfr_buf_next(struct pfr_buffer *, const void *); -int pfr_buf_grow(struct pfr_buffer *, int); -int pfr_buf_load(struct pfr_buffer *, char *, int, - int (*)(struct pfr_buffer *, char *, int)); -char *pfr_strerror(int); -int pfi_get_ifaces(const char *, struct pfi_kif *, int *); -int pfi_clr_istats(const char *, int *, int); - -void pfctl_print_title(char *); -int pfctl_clear_tables(const char *, int); -int pfctl_show_tables(const char *, int); -int pfctl_command_tables(int, char *[], char *, const char *, char *, - const char *, int); -int pfctl_show_altq(int, const char *, int, int); -void warn_namespace_collision(const char *); -int pfctl_show_ifaces(const char *, int); -FILE *pfctl_fopen(const char *, const char *); - -#ifdef __FreeBSD__ -extern int altqsupport; -extern int dummynetsupport; -#define HTONL(x) (x) = htonl((__uint32_t)(x)) -#endif - -#ifndef DEFAULT_PRIORITY -#define DEFAULT_PRIORITY 1 -#endif - -#ifndef DEFAULT_QLIMIT -#define DEFAULT_QLIMIT 50 -#endif - -/* - * generalized service curve used for admission control - */ -struct segment { - LIST_ENTRY(segment) _next; - double x, y, d, m; -}; - -extern int loadopt; - -int check_commit_altq(int, int); -void pfaltq_store(struct pf_altq *); -struct pf_altq *pfaltq_lookup(const char *); -char *rate2str(double); - -void print_addr(struct pf_addr_wrap *, sa_family_t, int); -void print_host(struct pf_addr *, u_int16_t p, sa_family_t, int); -void print_seq(struct pfsync_state_peer *); -void print_state(struct pfsync_state *, int); -int unmask(struct pf_addr *, sa_family_t); - -int pfctl_cmdline_symset(char *); -int pfctl_add_trans(struct pfr_buffer *, int, const char *); -u_int32_t - pfctl_get_ticket(struct pfr_buffer *, int, const char *); -int pfctl_trans(int, struct pfr_buffer *, u_long, int); - -#endif /* _PFCTL_H_ */ diff --git a/contrib/pf/pfctl/pfctl_altq.c b/contrib/pf/pfctl/pfctl_altq.c deleted file mode 100644 index 40e11d5..0000000 --- a/contrib/pf/pfctl/pfctl_altq.c +++ /dev/null @@ -1,1258 +0,0 @@ -/* $OpenBSD: pfctl_altq.c,v 1.93 2007/10/15 02:16:35 deraadt Exp $ */ - -/* - * Copyright (c) 2002 - * Sony Computer Science Laboratories Inc. - * Copyright (c) 2002, 2003 Henning Brauer <henning@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - -#include <sys/types.h> -#include <sys/ioctl.h> -#include <sys/socket.h> - -#include <net/if.h> -#include <netinet/in.h> -#include <net/pfvar.h> - -#include <err.h> -#include <errno.h> -#include <limits.h> -#include <math.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include <altq/altq.h> -#include <altq/altq_cbq.h> -#include <altq/altq_priq.h> -#include <altq/altq_hfsc.h> - -#include "pfctl_parser.h" -#include "pfctl.h" - -#define is_sc_null(sc) (((sc) == NULL) || ((sc)->m1 == 0 && (sc)->m2 == 0)) - -TAILQ_HEAD(altqs, pf_altq) altqs = TAILQ_HEAD_INITIALIZER(altqs); -LIST_HEAD(gen_sc, segment) rtsc, lssc; - -struct pf_altq *qname_to_pfaltq(const char *, const char *); -u_int32_t qname_to_qid(const char *); - -static int eval_pfqueue_cbq(struct pfctl *, struct pf_altq *); -static int cbq_compute_idletime(struct pfctl *, struct pf_altq *); -static int check_commit_cbq(int, int, struct pf_altq *); -static int print_cbq_opts(const struct pf_altq *); - -static int eval_pfqueue_priq(struct pfctl *, struct pf_altq *); -static int check_commit_priq(int, int, struct pf_altq *); -static int print_priq_opts(const struct pf_altq *); - -static int eval_pfqueue_hfsc(struct pfctl *, struct pf_altq *); -static int check_commit_hfsc(int, int, struct pf_altq *); -static int print_hfsc_opts(const struct pf_altq *, - const struct node_queue_opt *); - -static void gsc_add_sc(struct gen_sc *, struct service_curve *); -static int is_gsc_under_sc(struct gen_sc *, - struct service_curve *); -static void gsc_destroy(struct gen_sc *); -static struct segment *gsc_getentry(struct gen_sc *, double); -static int gsc_add_seg(struct gen_sc *, double, double, double, - double); -static double sc_x2y(struct service_curve *, double); - -#ifdef __FreeBSD__ -u_int32_t getifspeed(int, char *); -#else -u_int32_t getifspeed(char *); -#endif -u_long getifmtu(char *); -int eval_queue_opts(struct pf_altq *, struct node_queue_opt *, - u_int32_t); -u_int32_t eval_bwspec(struct node_queue_bw *, u_int32_t); -void print_hfsc_sc(const char *, u_int, u_int, u_int, - const struct node_hfsc_sc *); - -void -pfaltq_store(struct pf_altq *a) -{ - struct pf_altq *altq; - - if ((altq = malloc(sizeof(*altq))) == NULL) - err(1, "malloc"); - memcpy(altq, a, sizeof(struct pf_altq)); - TAILQ_INSERT_TAIL(&altqs, altq, entries); -} - -struct pf_altq * -pfaltq_lookup(const char *ifname) -{ - struct pf_altq *altq; - - TAILQ_FOREACH(altq, &altqs, entries) { - if (strncmp(ifname, altq->ifname, IFNAMSIZ) == 0 && - altq->qname[0] == 0) - return (altq); - } - return (NULL); -} - -struct pf_altq * -qname_to_pfaltq(const char *qname, const char *ifname) -{ - struct pf_altq *altq; - - TAILQ_FOREACH(altq, &altqs, entries) { - if (strncmp(ifname, altq->ifname, IFNAMSIZ) == 0 && - strncmp(qname, altq->qname, PF_QNAME_SIZE) == 0) - return (altq); - } - return (NULL); -} - -u_int32_t -qname_to_qid(const char *qname) -{ - struct pf_altq *altq; - - /* - * We guarantee that same named queues on different interfaces - * have the same qid, so we do NOT need to limit matching on - * one interface! - */ - - TAILQ_FOREACH(altq, &altqs, entries) { - if (strncmp(qname, altq->qname, PF_QNAME_SIZE) == 0) - return (altq->qid); - } - return (0); -} - -void -print_altq(const struct pf_altq *a, unsigned int level, - struct node_queue_bw *bw, struct node_queue_opt *qopts) -{ - if (a->qname[0] != 0) { - print_queue(a, level, bw, 1, qopts); - return; - } - -#ifdef __FreeBSD__ - if (a->local_flags & PFALTQ_FLAG_IF_REMOVED) - printf("INACTIVE "); -#endif - - printf("altq on %s ", a->ifname); - - switch (a->scheduler) { - case ALTQT_CBQ: - if (!print_cbq_opts(a)) - printf("cbq "); - break; - case ALTQT_PRIQ: - if (!print_priq_opts(a)) - printf("priq "); - break; - case ALTQT_HFSC: - if (!print_hfsc_opts(a, qopts)) - printf("hfsc "); - break; - } - - if (bw != NULL && bw->bw_percent > 0) { - if (bw->bw_percent < 100) - printf("bandwidth %u%% ", bw->bw_percent); - } else - printf("bandwidth %s ", rate2str((double)a->ifbandwidth)); - - if (a->qlimit != DEFAULT_QLIMIT) - printf("qlimit %u ", a->qlimit); - printf("tbrsize %u ", a->tbrsize); -} - -void -print_queue(const struct pf_altq *a, unsigned int level, - struct node_queue_bw *bw, int print_interface, - struct node_queue_opt *qopts) -{ - unsigned int i; - -#ifdef __FreeBSD__ - if (a->local_flags & PFALTQ_FLAG_IF_REMOVED) - printf("INACTIVE "); -#endif - printf("queue "); - for (i = 0; i < level; ++i) - printf(" "); - printf("%s ", a->qname); - if (print_interface) - printf("on %s ", a->ifname); - if (a->scheduler == ALTQT_CBQ || a->scheduler == ALTQT_HFSC) { - if (bw != NULL && bw->bw_percent > 0) { - if (bw->bw_percent < 100) - printf("bandwidth %u%% ", bw->bw_percent); - } else - printf("bandwidth %s ", rate2str((double)a->bandwidth)); - } - if (a->priority != DEFAULT_PRIORITY) - printf("priority %u ", a->priority); - if (a->qlimit != DEFAULT_QLIMIT) - printf("qlimit %u ", a->qlimit); - switch (a->scheduler) { - case ALTQT_CBQ: - print_cbq_opts(a); - break; - case ALTQT_PRIQ: - print_priq_opts(a); - break; - case ALTQT_HFSC: - print_hfsc_opts(a, qopts); - break; - } -} - -/* - * eval_pfaltq computes the discipline parameters. - */ -int -eval_pfaltq(struct pfctl *pf, struct pf_altq *pa, struct node_queue_bw *bw, - struct node_queue_opt *opts) -{ - u_int rate, size, errors = 0; - - if (bw->bw_absolute > 0) - pa->ifbandwidth = bw->bw_absolute; - else -#ifdef __FreeBSD__ - if ((rate = getifspeed(pf->dev, pa->ifname)) == 0) { -#else - if ((rate = getifspeed(pa->ifname)) == 0) { -#endif - fprintf(stderr, "interface %s does not know its bandwidth, " - "please specify an absolute bandwidth\n", - pa->ifname); - errors++; - } else if ((pa->ifbandwidth = eval_bwspec(bw, rate)) == 0) - pa->ifbandwidth = rate; - - errors += eval_queue_opts(pa, opts, pa->ifbandwidth); - - /* if tbrsize is not specified, use heuristics */ - if (pa->tbrsize == 0) { - rate = pa->ifbandwidth; - if (rate <= 1 * 1000 * 1000) - size = 1; - else if (rate <= 10 * 1000 * 1000) - size = 4; - else if (rate <= 200 * 1000 * 1000) - size = 8; - else - size = 24; - size = size * getifmtu(pa->ifname); - if (size > 0xffff) - size = 0xffff; - pa->tbrsize = size; - } - return (errors); -} - -/* - * check_commit_altq does consistency check for each interface - */ -int -check_commit_altq(int dev, int opts) -{ - struct pf_altq *altq; - int error = 0; - - /* call the discipline check for each interface. */ - TAILQ_FOREACH(altq, &altqs, entries) { - if (altq->qname[0] == 0) { - switch (altq->scheduler) { - case ALTQT_CBQ: - error = check_commit_cbq(dev, opts, altq); - break; - case ALTQT_PRIQ: - error = check_commit_priq(dev, opts, altq); - break; - case ALTQT_HFSC: - error = check_commit_hfsc(dev, opts, altq); - break; - default: - break; - } - } - } - return (error); -} - -/* - * eval_pfqueue computes the queue parameters. - */ -int -eval_pfqueue(struct pfctl *pf, struct pf_altq *pa, struct node_queue_bw *bw, - struct node_queue_opt *opts) -{ - /* should be merged with expand_queue */ - struct pf_altq *if_pa, *parent, *altq; - u_int32_t bwsum; - int error = 0; - - /* find the corresponding interface and copy fields used by queues */ - if ((if_pa = pfaltq_lookup(pa->ifname)) == NULL) { - fprintf(stderr, "altq not defined on %s\n", pa->ifname); - return (1); - } - pa->scheduler = if_pa->scheduler; - pa->ifbandwidth = if_pa->ifbandwidth; - - if (qname_to_pfaltq(pa->qname, pa->ifname) != NULL) { - fprintf(stderr, "queue %s already exists on interface %s\n", - pa->qname, pa->ifname); - return (1); - } - pa->qid = qname_to_qid(pa->qname); - - parent = NULL; - if (pa->parent[0] != 0) { - parent = qname_to_pfaltq(pa->parent, pa->ifname); - if (parent == NULL) { - fprintf(stderr, "parent %s not found for %s\n", - pa->parent, pa->qname); - return (1); - } - pa->parent_qid = parent->qid; - } - if (pa->qlimit == 0) - pa->qlimit = DEFAULT_QLIMIT; - - if (pa->scheduler == ALTQT_CBQ || pa->scheduler == ALTQT_HFSC) { - pa->bandwidth = eval_bwspec(bw, - parent == NULL ? 0 : parent->bandwidth); - - if (pa->bandwidth > pa->ifbandwidth) { - fprintf(stderr, "bandwidth for %s higher than " - "interface\n", pa->qname); - return (1); - } - /* check the sum of the child bandwidth is under parent's */ - if (parent != NULL) { - if (pa->bandwidth > parent->bandwidth) { - warnx("bandwidth for %s higher than parent", - pa->qname); - return (1); - } - bwsum = 0; - TAILQ_FOREACH(altq, &altqs, entries) { - if (strncmp(altq->ifname, pa->ifname, - IFNAMSIZ) == 0 && - altq->qname[0] != 0 && - strncmp(altq->parent, pa->parent, - PF_QNAME_SIZE) == 0) - bwsum += altq->bandwidth; - } - bwsum += pa->bandwidth; - if (bwsum > parent->bandwidth) { - warnx("the sum of the child bandwidth higher" - " than parent \"%s\"", parent->qname); - } - } - } - - if (eval_queue_opts(pa, opts, parent == NULL? 0 : parent->bandwidth)) - return (1); - - switch (pa->scheduler) { - case ALTQT_CBQ: - error = eval_pfqueue_cbq(pf, pa); - break; - case ALTQT_PRIQ: - error = eval_pfqueue_priq(pf, pa); - break; - case ALTQT_HFSC: - error = eval_pfqueue_hfsc(pf, pa); - break; - default: - break; - } - return (error); -} - -/* - * CBQ support functions - */ -#define RM_FILTER_GAIN 5 /* log2 of gain, e.g., 5 => 31/32 */ -#define RM_NS_PER_SEC (1000000000) - -static int -eval_pfqueue_cbq(struct pfctl *pf, struct pf_altq *pa) -{ - struct cbq_opts *opts; - u_int ifmtu; - - if (pa->priority >= CBQ_MAXPRI) { - warnx("priority out of range: max %d", CBQ_MAXPRI - 1); - return (-1); - } - - ifmtu = getifmtu(pa->ifname); - opts = &pa->pq_u.cbq_opts; - - if (opts->pktsize == 0) { /* use default */ - opts->pktsize = ifmtu; - if (opts->pktsize > MCLBYTES) /* do what TCP does */ - opts->pktsize &= ~MCLBYTES; - } else if (opts->pktsize > ifmtu) - opts->pktsize = ifmtu; - if (opts->maxpktsize == 0) /* use default */ - opts->maxpktsize = ifmtu; - else if (opts->maxpktsize > ifmtu) - opts->pktsize = ifmtu; - - if (opts->pktsize > opts->maxpktsize) - opts->pktsize = opts->maxpktsize; - - if (pa->parent[0] == 0) - opts->flags |= (CBQCLF_ROOTCLASS | CBQCLF_WRR); - - cbq_compute_idletime(pf, pa); - return (0); -} - -/* - * compute ns_per_byte, maxidle, minidle, and offtime - */ -static int -cbq_compute_idletime(struct pfctl *pf, struct pf_altq *pa) -{ - struct cbq_opts *opts; - double maxidle_s, maxidle, minidle; - double offtime, nsPerByte, ifnsPerByte, ptime, cptime; - double z, g, f, gton, gtom; - u_int minburst, maxburst; - - opts = &pa->pq_u.cbq_opts; - ifnsPerByte = (1.0 / (double)pa->ifbandwidth) * RM_NS_PER_SEC * 8; - minburst = opts->minburst; - maxburst = opts->maxburst; - - if (pa->bandwidth == 0) - f = 0.0001; /* small enough? */ - else - f = ((double) pa->bandwidth / (double) pa->ifbandwidth); - - nsPerByte = ifnsPerByte / f; - ptime = (double)opts->pktsize * ifnsPerByte; - cptime = ptime * (1.0 - f) / f; - - if (nsPerByte * (double)opts->maxpktsize > (double)INT_MAX) { - /* - * this causes integer overflow in kernel! - * (bandwidth < 6Kbps when max_pkt_size=1500) - */ - if (pa->bandwidth != 0 && (pf->opts & PF_OPT_QUIET) == 0) - warnx("queue bandwidth must be larger than %s", - rate2str(ifnsPerByte * (double)opts->maxpktsize / - (double)INT_MAX * (double)pa->ifbandwidth)); - fprintf(stderr, "cbq: queue %s is too slow!\n", - pa->qname); - nsPerByte = (double)(INT_MAX / opts->maxpktsize); - } - - if (maxburst == 0) { /* use default */ - if (cptime > 10.0 * 1000000) - maxburst = 4; - else - maxburst = 16; - } - if (minburst == 0) /* use default */ - minburst = 2; - if (minburst > maxburst) - minburst = maxburst; - - z = (double)(1 << RM_FILTER_GAIN); - g = (1.0 - 1.0 / z); - gton = pow(g, (double)maxburst); - gtom = pow(g, (double)(minburst-1)); - maxidle = ((1.0 / f - 1.0) * ((1.0 - gton) / gton)); - maxidle_s = (1.0 - g); - if (maxidle > maxidle_s) - maxidle = ptime * maxidle; - else - maxidle = ptime * maxidle_s; - offtime = cptime * (1.0 + 1.0/(1.0 - g) * (1.0 - gtom) / gtom); - minidle = -((double)opts->maxpktsize * (double)nsPerByte); - - /* scale parameters */ - maxidle = ((maxidle * 8.0) / nsPerByte) * - pow(2.0, (double)RM_FILTER_GAIN); - offtime = (offtime * 8.0) / nsPerByte * - pow(2.0, (double)RM_FILTER_GAIN); - minidle = ((minidle * 8.0) / nsPerByte) * - pow(2.0, (double)RM_FILTER_GAIN); - - maxidle = maxidle / 1000.0; - offtime = offtime / 1000.0; - minidle = minidle / 1000.0; - - opts->minburst = minburst; - opts->maxburst = maxburst; - opts->ns_per_byte = (u_int)nsPerByte; - opts->maxidle = (u_int)fabs(maxidle); - opts->minidle = (int)minidle; - opts->offtime = (u_int)fabs(offtime); - - return (0); -} - -static int -check_commit_cbq(int dev, int opts, struct pf_altq *pa) -{ - struct pf_altq *altq; - int root_class, default_class; - int error = 0; - - /* - * check if cbq has one root queue and one default queue - * for this interface - */ - root_class = default_class = 0; - TAILQ_FOREACH(altq, &altqs, entries) { - if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0) - continue; - if (altq->qname[0] == 0) /* this is for interface */ - continue; - if (altq->pq_u.cbq_opts.flags & CBQCLF_ROOTCLASS) - root_class++; - if (altq->pq_u.cbq_opts.flags & CBQCLF_DEFCLASS) - default_class++; - } - if (root_class != 1) { - warnx("should have one root queue on %s", pa->ifname); - error++; - } - if (default_class != 1) { - warnx("should have one default queue on %s", pa->ifname); - error++; - } - return (error); -} - -static int -print_cbq_opts(const struct pf_altq *a) -{ - const struct cbq_opts *opts; - - opts = &a->pq_u.cbq_opts; - if (opts->flags) { - printf("cbq("); - if (opts->flags & CBQCLF_RED) - printf(" red"); - if (opts->flags & CBQCLF_ECN) - printf(" ecn"); - if (opts->flags & CBQCLF_RIO) - printf(" rio"); - if (opts->flags & CBQCLF_CLEARDSCP) - printf(" cleardscp"); - if (opts->flags & CBQCLF_FLOWVALVE) - printf(" flowvalve"); - if (opts->flags & CBQCLF_BORROW) - printf(" borrow"); - if (opts->flags & CBQCLF_WRR) - printf(" wrr"); - if (opts->flags & CBQCLF_EFFICIENT) - printf(" efficient"); - if (opts->flags & CBQCLF_ROOTCLASS) - printf(" root"); - if (opts->flags & CBQCLF_DEFCLASS) - printf(" default"); - printf(" ) "); - - return (1); - } else - return (0); -} - -/* - * PRIQ support functions - */ -static int -eval_pfqueue_priq(struct pfctl *pf, struct pf_altq *pa) -{ - struct pf_altq *altq; - - if (pa->priority >= PRIQ_MAXPRI) { - warnx("priority out of range: max %d", PRIQ_MAXPRI - 1); - return (-1); - } - /* the priority should be unique for the interface */ - TAILQ_FOREACH(altq, &altqs, entries) { - if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) == 0 && - altq->qname[0] != 0 && altq->priority == pa->priority) { - warnx("%s and %s have the same priority", - altq->qname, pa->qname); - return (-1); - } - } - - return (0); -} - -static int -check_commit_priq(int dev, int opts, struct pf_altq *pa) -{ - struct pf_altq *altq; - int default_class; - int error = 0; - - /* - * check if priq has one default class for this interface - */ - default_class = 0; - TAILQ_FOREACH(altq, &altqs, entries) { - if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0) - continue; - if (altq->qname[0] == 0) /* this is for interface */ - continue; - if (altq->pq_u.priq_opts.flags & PRCF_DEFAULTCLASS) - default_class++; - } - if (default_class != 1) { - warnx("should have one default queue on %s", pa->ifname); - error++; - } - return (error); -} - -static int -print_priq_opts(const struct pf_altq *a) -{ - const struct priq_opts *opts; - - opts = &a->pq_u.priq_opts; - - if (opts->flags) { - printf("priq("); - if (opts->flags & PRCF_RED) - printf(" red"); - if (opts->flags & PRCF_ECN) - printf(" ecn"); - if (opts->flags & PRCF_RIO) - printf(" rio"); - if (opts->flags & PRCF_CLEARDSCP) - printf(" cleardscp"); - if (opts->flags & PRCF_DEFAULTCLASS) - printf(" default"); - printf(" ) "); - - return (1); - } else - return (0); -} - -/* - * HFSC support functions - */ -static int -eval_pfqueue_hfsc(struct pfctl *pf, struct pf_altq *pa) -{ - struct pf_altq *altq, *parent; - struct hfsc_opts *opts; - struct service_curve sc; - - opts = &pa->pq_u.hfsc_opts; - - if (pa->parent[0] == 0) { - /* root queue */ - opts->lssc_m1 = pa->ifbandwidth; - opts->lssc_m2 = pa->ifbandwidth; - opts->lssc_d = 0; - return (0); - } - - LIST_INIT(&rtsc); - LIST_INIT(&lssc); - - /* if link_share is not specified, use bandwidth */ - if (opts->lssc_m2 == 0) - opts->lssc_m2 = pa->bandwidth; - - if ((opts->rtsc_m1 > 0 && opts->rtsc_m2 == 0) || - (opts->lssc_m1 > 0 && opts->lssc_m2 == 0) || - (opts->ulsc_m1 > 0 && opts->ulsc_m2 == 0)) { - warnx("m2 is zero for %s", pa->qname); - return (-1); - } - - if ((opts->rtsc_m1 < opts->rtsc_m2 && opts->rtsc_m1 != 0) || - (opts->lssc_m1 < opts->lssc_m2 && opts->lssc_m1 != 0) || - (opts->ulsc_m1 < opts->ulsc_m2 && opts->ulsc_m1 != 0)) { - warnx("m1 must be zero for convex curve: %s", pa->qname); - return (-1); - } - - /* - * admission control: - * for the real-time service curve, the sum of the service curves - * should not exceed 80% of the interface bandwidth. 20% is reserved - * not to over-commit the actual interface bandwidth. - * for the linkshare service curve, the sum of the child service - * curve should not exceed the parent service curve. - * for the upper-limit service curve, the assigned bandwidth should - * be smaller than the interface bandwidth, and the upper-limit should - * be larger than the real-time service curve when both are defined. - */ - parent = qname_to_pfaltq(pa->parent, pa->ifname); - if (parent == NULL) - errx(1, "parent %s not found for %s", pa->parent, pa->qname); - - TAILQ_FOREACH(altq, &altqs, entries) { - if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0) - continue; - if (altq->qname[0] == 0) /* this is for interface */ - continue; - - /* if the class has a real-time service curve, add it. */ - if (opts->rtsc_m2 != 0 && altq->pq_u.hfsc_opts.rtsc_m2 != 0) { - sc.m1 = altq->pq_u.hfsc_opts.rtsc_m1; - sc.d = altq->pq_u.hfsc_opts.rtsc_d; - sc.m2 = altq->pq_u.hfsc_opts.rtsc_m2; - gsc_add_sc(&rtsc, &sc); - } - - if (strncmp(altq->parent, pa->parent, PF_QNAME_SIZE) != 0) - continue; - - /* if the class has a linkshare service curve, add it. */ - if (opts->lssc_m2 != 0 && altq->pq_u.hfsc_opts.lssc_m2 != 0) { - sc.m1 = altq->pq_u.hfsc_opts.lssc_m1; - sc.d = altq->pq_u.hfsc_opts.lssc_d; - sc.m2 = altq->pq_u.hfsc_opts.lssc_m2; - gsc_add_sc(&lssc, &sc); - } - } - - /* check the real-time service curve. reserve 20% of interface bw */ - if (opts->rtsc_m2 != 0) { - /* add this queue to the sum */ - sc.m1 = opts->rtsc_m1; - sc.d = opts->rtsc_d; - sc.m2 = opts->rtsc_m2; - gsc_add_sc(&rtsc, &sc); - /* compare the sum with 80% of the interface */ - sc.m1 = 0; - sc.d = 0; - sc.m2 = pa->ifbandwidth / 100 * 80; - if (!is_gsc_under_sc(&rtsc, &sc)) { - warnx("real-time sc exceeds 80%% of the interface " - "bandwidth (%s)", rate2str((double)sc.m2)); - goto err_ret; - } - } - - /* check the linkshare service curve. */ - if (opts->lssc_m2 != 0) { - /* add this queue to the child sum */ - sc.m1 = opts->lssc_m1; - sc.d = opts->lssc_d; - sc.m2 = opts->lssc_m2; - gsc_add_sc(&lssc, &sc); - /* compare the sum of the children with parent's sc */ - sc.m1 = parent->pq_u.hfsc_opts.lssc_m1; - sc.d = parent->pq_u.hfsc_opts.lssc_d; - sc.m2 = parent->pq_u.hfsc_opts.lssc_m2; - if (!is_gsc_under_sc(&lssc, &sc)) { - warnx("linkshare sc exceeds parent's sc"); - goto err_ret; - } - } - - /* check the upper-limit service curve. */ - if (opts->ulsc_m2 != 0) { - if (opts->ulsc_m1 > pa->ifbandwidth || - opts->ulsc_m2 > pa->ifbandwidth) { - warnx("upper-limit larger than interface bandwidth"); - goto err_ret; - } - if (opts->rtsc_m2 != 0 && opts->rtsc_m2 > opts->ulsc_m2) { - warnx("upper-limit sc smaller than real-time sc"); - goto err_ret; - } - } - - gsc_destroy(&rtsc); - gsc_destroy(&lssc); - - return (0); - -err_ret: - gsc_destroy(&rtsc); - gsc_destroy(&lssc); - return (-1); -} - -static int -check_commit_hfsc(int dev, int opts, struct pf_altq *pa) -{ - struct pf_altq *altq, *def = NULL; - int default_class; - int error = 0; - - /* check if hfsc has one default queue for this interface */ - default_class = 0; - TAILQ_FOREACH(altq, &altqs, entries) { - if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0) - continue; - if (altq->qname[0] == 0) /* this is for interface */ - continue; - if (altq->parent[0] == 0) /* dummy root */ - continue; - if (altq->pq_u.hfsc_opts.flags & HFCF_DEFAULTCLASS) { - default_class++; - def = altq; - } - } - if (default_class != 1) { - warnx("should have one default queue on %s", pa->ifname); - return (1); - } - /* make sure the default queue is a leaf */ - TAILQ_FOREACH(altq, &altqs, entries) { - if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0) - continue; - if (altq->qname[0] == 0) /* this is for interface */ - continue; - if (strncmp(altq->parent, def->qname, PF_QNAME_SIZE) == 0) { - warnx("default queue is not a leaf"); - error++; - } - } - return (error); -} - -static int -print_hfsc_opts(const struct pf_altq *a, const struct node_queue_opt *qopts) -{ - const struct hfsc_opts *opts; - const struct node_hfsc_sc *rtsc, *lssc, *ulsc; - - opts = &a->pq_u.hfsc_opts; - if (qopts == NULL) - rtsc = lssc = ulsc = NULL; - else { - rtsc = &qopts->data.hfsc_opts.realtime; - lssc = &qopts->data.hfsc_opts.linkshare; - ulsc = &qopts->data.hfsc_opts.upperlimit; - } - - if (opts->flags || opts->rtsc_m2 != 0 || opts->ulsc_m2 != 0 || - (opts->lssc_m2 != 0 && (opts->lssc_m2 != a->bandwidth || - opts->lssc_d != 0))) { - printf("hfsc("); - if (opts->flags & HFCF_RED) - printf(" red"); - if (opts->flags & HFCF_ECN) - printf(" ecn"); - if (opts->flags & HFCF_RIO) - printf(" rio"); - if (opts->flags & HFCF_CLEARDSCP) - printf(" cleardscp"); - if (opts->flags & HFCF_DEFAULTCLASS) - printf(" default"); - if (opts->rtsc_m2 != 0) - print_hfsc_sc("realtime", opts->rtsc_m1, opts->rtsc_d, - opts->rtsc_m2, rtsc); - if (opts->lssc_m2 != 0 && (opts->lssc_m2 != a->bandwidth || - opts->lssc_d != 0)) - print_hfsc_sc("linkshare", opts->lssc_m1, opts->lssc_d, - opts->lssc_m2, lssc); - if (opts->ulsc_m2 != 0) - print_hfsc_sc("upperlimit", opts->ulsc_m1, opts->ulsc_d, - opts->ulsc_m2, ulsc); - printf(" ) "); - - return (1); - } else - return (0); -} - -/* - * admission control using generalized service curve - */ - -/* add a new service curve to a generalized service curve */ -static void -gsc_add_sc(struct gen_sc *gsc, struct service_curve *sc) -{ - if (is_sc_null(sc)) - return; - if (sc->d != 0) - gsc_add_seg(gsc, 0.0, 0.0, (double)sc->d, (double)sc->m1); - gsc_add_seg(gsc, (double)sc->d, 0.0, INFINITY, (double)sc->m2); -} - -/* - * check whether all points of a generalized service curve have - * their y-coordinates no larger than a given two-piece linear - * service curve. - */ -static int -is_gsc_under_sc(struct gen_sc *gsc, struct service_curve *sc) -{ - struct segment *s, *last, *end; - double y; - - if (is_sc_null(sc)) { - if (LIST_EMPTY(gsc)) - return (1); - LIST_FOREACH(s, gsc, _next) { - if (s->m != 0) - return (0); - } - return (1); - } - /* - * gsc has a dummy entry at the end with x = INFINITY. - * loop through up to this dummy entry. - */ - end = gsc_getentry(gsc, INFINITY); - if (end == NULL) - return (1); - last = NULL; - for (s = LIST_FIRST(gsc); s != end; s = LIST_NEXT(s, _next)) { - if (s->y > sc_x2y(sc, s->x)) - return (0); - last = s; - } - /* last now holds the real last segment */ - if (last == NULL) - return (1); - if (last->m > sc->m2) - return (0); - if (last->x < sc->d && last->m > sc->m1) { - y = last->y + (sc->d - last->x) * last->m; - if (y > sc_x2y(sc, sc->d)) - return (0); - } - return (1); -} - -static void -gsc_destroy(struct gen_sc *gsc) -{ - struct segment *s; - - while ((s = LIST_FIRST(gsc)) != NULL) { - LIST_REMOVE(s, _next); - free(s); - } -} - -/* - * return a segment entry starting at x. - * if gsc has no entry starting at x, a new entry is created at x. - */ -static struct segment * -gsc_getentry(struct gen_sc *gsc, double x) -{ - struct segment *new, *prev, *s; - - prev = NULL; - LIST_FOREACH(s, gsc, _next) { - if (s->x == x) - return (s); /* matching entry found */ - else if (s->x < x) - prev = s; - else - break; - } - - /* we have to create a new entry */ - if ((new = calloc(1, sizeof(struct segment))) == NULL) - return (NULL); - - new->x = x; - if (x == INFINITY || s == NULL) - new->d = 0; - else if (s->x == INFINITY) - new->d = INFINITY; - else - new->d = s->x - x; - if (prev == NULL) { - /* insert the new entry at the head of the list */ - new->y = 0; - new->m = 0; - LIST_INSERT_HEAD(gsc, new, _next); - } else { - /* - * the start point intersects with the segment pointed by - * prev. divide prev into 2 segments - */ - if (x == INFINITY) { - prev->d = INFINITY; - if (prev->m == 0) - new->y = prev->y; - else - new->y = INFINITY; - } else { - prev->d = x - prev->x; - new->y = prev->d * prev->m + prev->y; - } - new->m = prev->m; - LIST_INSERT_AFTER(prev, new, _next); - } - return (new); -} - -/* add a segment to a generalized service curve */ -static int -gsc_add_seg(struct gen_sc *gsc, double x, double y, double d, double m) -{ - struct segment *start, *end, *s; - double x2; - - if (d == INFINITY) - x2 = INFINITY; - else - x2 = x + d; - start = gsc_getentry(gsc, x); - end = gsc_getentry(gsc, x2); - if (start == NULL || end == NULL) - return (-1); - - for (s = start; s != end; s = LIST_NEXT(s, _next)) { - s->m += m; - s->y += y + (s->x - x) * m; - } - - end = gsc_getentry(gsc, INFINITY); - for (; s != end; s = LIST_NEXT(s, _next)) { - s->y += m * d; - } - - return (0); -} - -/* get y-projection of a service curve */ -static double -sc_x2y(struct service_curve *sc, double x) -{ - double y; - - if (x <= (double)sc->d) - /* y belongs to the 1st segment */ - y = x * (double)sc->m1; - else - /* y belongs to the 2nd segment */ - y = (double)sc->d * (double)sc->m1 - + (x - (double)sc->d) * (double)sc->m2; - return (y); -} - -/* - * misc utilities - */ -#define R2S_BUFS 8 -#define RATESTR_MAX 16 - -char * -rate2str(double rate) -{ - char *buf; - static char r2sbuf[R2S_BUFS][RATESTR_MAX]; /* ring bufer */ - static int idx = 0; - int i; - static const char unit[] = " KMG"; - - buf = r2sbuf[idx++]; - if (idx == R2S_BUFS) - idx = 0; - - for (i = 0; rate >= 1000 && i <= 3; i++) - rate /= 1000; - - if ((int)(rate * 100) % 100) - snprintf(buf, RATESTR_MAX, "%.2f%cb", rate, unit[i]); - else - snprintf(buf, RATESTR_MAX, "%d%cb", (int)rate, unit[i]); - - return (buf); -} - -#ifdef __FreeBSD__ -/* - * XXX - * FreeBSD does not have SIOCGIFDATA. - * To emulate this, DIOCGIFSPEED ioctl added to pf. - */ -u_int32_t -getifspeed(int pfdev, char *ifname) -{ - struct pf_ifspeed io; - - bzero(&io, sizeof io); - if (strlcpy(io.ifname, ifname, IFNAMSIZ) >= - sizeof(io.ifname)) - errx(1, "getifspeed: strlcpy"); - if (ioctl(pfdev, DIOCGIFSPEED, &io) == -1) - err(1, "DIOCGIFSPEED"); - return ((u_int32_t)io.baudrate); -} -#else -u_int32_t -getifspeed(char *ifname) -{ - int s; - struct ifreq ifr; - struct if_data ifrdat; - - if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) - err(1, "socket"); - bzero(&ifr, sizeof(ifr)); - if (strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)) >= - sizeof(ifr.ifr_name)) - errx(1, "getifspeed: strlcpy"); - ifr.ifr_data = (caddr_t)&ifrdat; - if (ioctl(s, SIOCGIFDATA, (caddr_t)&ifr) == -1) - err(1, "SIOCGIFDATA"); - if (close(s)) - err(1, "close"); - return ((u_int32_t)ifrdat.ifi_baudrate); -} -#endif - -u_long -getifmtu(char *ifname) -{ - int s; - struct ifreq ifr; - - if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) - err(1, "socket"); - bzero(&ifr, sizeof(ifr)); - if (strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)) >= - sizeof(ifr.ifr_name)) - errx(1, "getifmtu: strlcpy"); - if (ioctl(s, SIOCGIFMTU, (caddr_t)&ifr) == -1) -#ifdef __FreeBSD__ - ifr.ifr_mtu = 1500; -#else - err(1, "SIOCGIFMTU"); -#endif - if (close(s)) - err(1, "close"); - if (ifr.ifr_mtu > 0) - return (ifr.ifr_mtu); - else { - warnx("could not get mtu for %s, assuming 1500", ifname); - return (1500); - } -} - -int -eval_queue_opts(struct pf_altq *pa, struct node_queue_opt *opts, - u_int32_t ref_bw) -{ - int errors = 0; - - switch (pa->scheduler) { - case ALTQT_CBQ: - pa->pq_u.cbq_opts = opts->data.cbq_opts; - break; - case ALTQT_PRIQ: - pa->pq_u.priq_opts = opts->data.priq_opts; - break; - case ALTQT_HFSC: - pa->pq_u.hfsc_opts.flags = opts->data.hfsc_opts.flags; - if (opts->data.hfsc_opts.linkshare.used) { - pa->pq_u.hfsc_opts.lssc_m1 = - eval_bwspec(&opts->data.hfsc_opts.linkshare.m1, - ref_bw); - pa->pq_u.hfsc_opts.lssc_m2 = - eval_bwspec(&opts->data.hfsc_opts.linkshare.m2, - ref_bw); - pa->pq_u.hfsc_opts.lssc_d = - opts->data.hfsc_opts.linkshare.d; - } - if (opts->data.hfsc_opts.realtime.used) { - pa->pq_u.hfsc_opts.rtsc_m1 = - eval_bwspec(&opts->data.hfsc_opts.realtime.m1, - ref_bw); - pa->pq_u.hfsc_opts.rtsc_m2 = - eval_bwspec(&opts->data.hfsc_opts.realtime.m2, - ref_bw); - pa->pq_u.hfsc_opts.rtsc_d = - opts->data.hfsc_opts.realtime.d; - } - if (opts->data.hfsc_opts.upperlimit.used) { - pa->pq_u.hfsc_opts.ulsc_m1 = - eval_bwspec(&opts->data.hfsc_opts.upperlimit.m1, - ref_bw); - pa->pq_u.hfsc_opts.ulsc_m2 = - eval_bwspec(&opts->data.hfsc_opts.upperlimit.m2, - ref_bw); - pa->pq_u.hfsc_opts.ulsc_d = - opts->data.hfsc_opts.upperlimit.d; - } - break; - default: - warnx("eval_queue_opts: unknown scheduler type %u", - opts->qtype); - errors++; - break; - } - - return (errors); -} - -u_int32_t -eval_bwspec(struct node_queue_bw *bw, u_int32_t ref_bw) -{ - if (bw->bw_absolute > 0) - return (bw->bw_absolute); - - if (bw->bw_percent > 0) - return (ref_bw / 100 * bw->bw_percent); - - return (0); -} - -void -print_hfsc_sc(const char *scname, u_int m1, u_int d, u_int m2, - const struct node_hfsc_sc *sc) -{ - printf(" %s", scname); - - if (d != 0) { - printf("("); - if (sc != NULL && sc->m1.bw_percent > 0) - printf("%u%%", sc->m1.bw_percent); - else - printf("%s", rate2str((double)m1)); - printf(" %u", d); - } - - if (sc != NULL && sc->m2.bw_percent > 0) - printf(" %u%%", sc->m2.bw_percent); - else - printf(" %s", rate2str((double)m2)); - - if (d != 0) - printf(")"); -} diff --git a/contrib/pf/pfctl/pfctl_optimize.c b/contrib/pf/pfctl/pfctl_optimize.c deleted file mode 100644 index 9511720..0000000 --- a/contrib/pf/pfctl/pfctl_optimize.c +++ /dev/null @@ -1,1655 +0,0 @@ -/* $OpenBSD: pfctl_optimize.c,v 1.17 2008/05/06 03:45:21 mpf Exp $ */ - -/* - * Copyright (c) 2004 Mike Frantzen <frantzen@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - -#include <sys/types.h> -#include <sys/ioctl.h> -#include <sys/socket.h> - -#include <net/if.h> -#include <net/pfvar.h> - -#include <netinet/in.h> -#include <arpa/inet.h> - -#include <assert.h> -#include <ctype.h> -#include <err.h> -#include <errno.h> -#include <stddef.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "pfctl_parser.h" -#include "pfctl.h" - -/* The size at which a table becomes faster than individual rules */ -#define TABLE_THRESHOLD 6 - - -/* #define OPT_DEBUG 1 */ -#ifdef OPT_DEBUG -# define DEBUG(str, v...) \ - printf("%s: " str "\n", __FUNCTION__ , ## v) -#else -# define DEBUG(str, v...) ((void)0) -#endif - - -/* - * A container that lets us sort a superblock to optimize the skip step jumps - */ -struct pf_skip_step { - int ps_count; /* number of items */ - TAILQ_HEAD( , pf_opt_rule) ps_rules; - TAILQ_ENTRY(pf_skip_step) ps_entry; -}; - - -/* - * A superblock is a block of adjacent rules of similar action. If there - * are five PASS rules in a row, they all become members of a superblock. - * Once we have a superblock, we are free to re-order any rules within it - * in order to improve performance; if a packet is passed, it doesn't matter - * who passed it. - */ -struct superblock { - TAILQ_HEAD( , pf_opt_rule) sb_rules; - TAILQ_ENTRY(superblock) sb_entry; - struct superblock *sb_profiled_block; - TAILQ_HEAD(skiplist, pf_skip_step) sb_skipsteps[PF_SKIP_COUNT]; -}; -TAILQ_HEAD(superblocks, superblock); - - -/* - * Description of the PF rule structure. - */ -enum { - BARRIER, /* the presence of the field puts the rule in it's own block */ - BREAK, /* the field may not differ between rules in a superblock */ - NOMERGE, /* the field may not differ between rules when combined */ - COMBINED, /* the field may itself be combined with other rules */ - DC, /* we just don't care about the field */ - NEVER}; /* we should never see this field set?!? */ -struct pf_rule_field { - const char *prf_name; - int prf_type; - size_t prf_offset; - size_t prf_size; -} pf_rule_desc[] = { -#define PF_RULE_FIELD(field, ty) \ - {#field, \ - ty, \ - offsetof(struct pf_rule, field), \ - sizeof(((struct pf_rule *)0)->field)} - - - /* - * The presence of these fields in a rule put the rule in it's own - * superblock. Thus it will not be optimized. It also prevents the - * rule from being re-ordered at all. - */ - PF_RULE_FIELD(label, BARRIER), - PF_RULE_FIELD(prob, BARRIER), - PF_RULE_FIELD(max_states, BARRIER), - PF_RULE_FIELD(max_src_nodes, BARRIER), - PF_RULE_FIELD(max_src_states, BARRIER), - PF_RULE_FIELD(max_src_conn, BARRIER), - PF_RULE_FIELD(max_src_conn_rate, BARRIER), - PF_RULE_FIELD(anchor, BARRIER), /* for now */ - - /* - * These fields must be the same between all rules in the same superblock. - * These rules are allowed to be re-ordered but only among like rules. - * For instance we can re-order all 'tag "foo"' rules because they have the - * same tag. But we can not re-order between a 'tag "foo"' and a - * 'tag "bar"' since that would change the meaning of the ruleset. - */ - PF_RULE_FIELD(tagname, BREAK), - PF_RULE_FIELD(keep_state, BREAK), - PF_RULE_FIELD(qname, BREAK), - PF_RULE_FIELD(pqname, BREAK), - PF_RULE_FIELD(rt, BREAK), - PF_RULE_FIELD(allow_opts, BREAK), - PF_RULE_FIELD(rule_flag, BREAK), - PF_RULE_FIELD(action, BREAK), - PF_RULE_FIELD(log, BREAK), - PF_RULE_FIELD(quick, BREAK), - PF_RULE_FIELD(return_ttl, BREAK), - PF_RULE_FIELD(overload_tblname, BREAK), - PF_RULE_FIELD(flush, BREAK), - PF_RULE_FIELD(rpool, BREAK), - PF_RULE_FIELD(logif, BREAK), - - /* - * Any fields not listed in this structure act as BREAK fields - */ - - - /* - * These fields must not differ when we merge two rules together but - * their difference isn't enough to put the rules in different superblocks. - * There are no problems re-ordering any rules with these fields. - */ - PF_RULE_FIELD(af, NOMERGE), - PF_RULE_FIELD(ifnot, NOMERGE), - PF_RULE_FIELD(ifname, NOMERGE), /* hack for IF groups */ - PF_RULE_FIELD(match_tag_not, NOMERGE), - PF_RULE_FIELD(match_tagname, NOMERGE), - PF_RULE_FIELD(os_fingerprint, NOMERGE), - PF_RULE_FIELD(timeout, NOMERGE), - PF_RULE_FIELD(return_icmp, NOMERGE), - PF_RULE_FIELD(return_icmp6, NOMERGE), - PF_RULE_FIELD(uid, NOMERGE), - PF_RULE_FIELD(gid, NOMERGE), - PF_RULE_FIELD(direction, NOMERGE), - PF_RULE_FIELD(proto, NOMERGE), - PF_RULE_FIELD(type, NOMERGE), - PF_RULE_FIELD(code, NOMERGE), - PF_RULE_FIELD(flags, NOMERGE), - PF_RULE_FIELD(flagset, NOMERGE), - PF_RULE_FIELD(tos, NOMERGE), - PF_RULE_FIELD(src.port, NOMERGE), - PF_RULE_FIELD(dst.port, NOMERGE), - PF_RULE_FIELD(src.port_op, NOMERGE), - PF_RULE_FIELD(dst.port_op, NOMERGE), - PF_RULE_FIELD(src.neg, NOMERGE), - PF_RULE_FIELD(dst.neg, NOMERGE), - - /* These fields can be merged */ - PF_RULE_FIELD(src.addr, COMBINED), - PF_RULE_FIELD(dst.addr, COMBINED), - - /* We just don't care about these fields. They're set by the kernel */ - PF_RULE_FIELD(skip, DC), - PF_RULE_FIELD(evaluations, DC), - PF_RULE_FIELD(packets, DC), - PF_RULE_FIELD(bytes, DC), - PF_RULE_FIELD(kif, DC), - PF_RULE_FIELD(states_cur, DC), - PF_RULE_FIELD(states_tot, DC), - PF_RULE_FIELD(src_nodes, DC), - PF_RULE_FIELD(nr, DC), - PF_RULE_FIELD(entries, DC), - PF_RULE_FIELD(qid, DC), - PF_RULE_FIELD(pqid, DC), - PF_RULE_FIELD(anchor_relative, DC), - PF_RULE_FIELD(anchor_wildcard, DC), - PF_RULE_FIELD(tag, DC), - PF_RULE_FIELD(match_tag, DC), - PF_RULE_FIELD(overload_tbl, DC), - - /* These fields should never be set in a PASS/BLOCK rule */ - PF_RULE_FIELD(natpass, NEVER), - PF_RULE_FIELD(max_mss, NEVER), - PF_RULE_FIELD(min_ttl, NEVER), - PF_RULE_FIELD(set_tos, NEVER), -}; - - - -int add_opt_table(struct pfctl *, struct pf_opt_tbl **, sa_family_t, - struct pf_rule_addr *); -int addrs_combineable(struct pf_rule_addr *, struct pf_rule_addr *); -int addrs_equal(struct pf_rule_addr *, struct pf_rule_addr *); -int block_feedback(struct pfctl *, struct superblock *); -int combine_rules(struct pfctl *, struct superblock *); -void comparable_rule(struct pf_rule *, const struct pf_rule *, int); -int construct_superblocks(struct pfctl *, struct pf_opt_queue *, - struct superblocks *); -void exclude_supersets(struct pf_rule *, struct pf_rule *); -int interface_group(const char *); -int load_feedback_profile(struct pfctl *, struct superblocks *); -int optimize_superblock(struct pfctl *, struct superblock *); -int pf_opt_create_table(struct pfctl *, struct pf_opt_tbl *); -void remove_from_skipsteps(struct skiplist *, struct superblock *, - struct pf_opt_rule *, struct pf_skip_step *); -int remove_identical_rules(struct pfctl *, struct superblock *); -int reorder_rules(struct pfctl *, struct superblock *, int); -int rules_combineable(struct pf_rule *, struct pf_rule *); -void skip_append(struct superblock *, int, struct pf_skip_step *, - struct pf_opt_rule *); -int skip_compare(int, struct pf_skip_step *, struct pf_opt_rule *); -void skip_init(void); -int skip_cmp_af(struct pf_rule *, struct pf_rule *); -int skip_cmp_dir(struct pf_rule *, struct pf_rule *); -int skip_cmp_dst_addr(struct pf_rule *, struct pf_rule *); -int skip_cmp_dst_port(struct pf_rule *, struct pf_rule *); -int skip_cmp_ifp(struct pf_rule *, struct pf_rule *); -int skip_cmp_proto(struct pf_rule *, struct pf_rule *); -int skip_cmp_src_addr(struct pf_rule *, struct pf_rule *); -int skip_cmp_src_port(struct pf_rule *, struct pf_rule *); -int superblock_inclusive(struct superblock *, struct pf_opt_rule *); -void superblock_free(struct pfctl *, struct superblock *); - - -int (*skip_comparitors[PF_SKIP_COUNT])(struct pf_rule *, struct pf_rule *); -const char *skip_comparitors_names[PF_SKIP_COUNT]; -#define PF_SKIP_COMPARITORS { \ - { "ifp", PF_SKIP_IFP, skip_cmp_ifp }, \ - { "dir", PF_SKIP_DIR, skip_cmp_dir }, \ - { "af", PF_SKIP_AF, skip_cmp_af }, \ - { "proto", PF_SKIP_PROTO, skip_cmp_proto }, \ - { "saddr", PF_SKIP_SRC_ADDR, skip_cmp_src_addr }, \ - { "sport", PF_SKIP_SRC_PORT, skip_cmp_src_port }, \ - { "daddr", PF_SKIP_DST_ADDR, skip_cmp_dst_addr }, \ - { "dport", PF_SKIP_DST_PORT, skip_cmp_dst_port } \ -} - -struct pfr_buffer table_buffer; -int table_identifier; - - -int -pfctl_optimize_ruleset(struct pfctl *pf, struct pf_ruleset *rs) -{ - struct superblocks superblocks; - struct pf_opt_queue opt_queue; - struct superblock *block; - struct pf_opt_rule *por; - struct pf_rule *r; - struct pf_rulequeue *old_rules; - - DEBUG("optimizing ruleset"); - memset(&table_buffer, 0, sizeof(table_buffer)); - skip_init(); - TAILQ_INIT(&opt_queue); - - old_rules = rs->rules[PF_RULESET_FILTER].active.ptr; - rs->rules[PF_RULESET_FILTER].active.ptr = - rs->rules[PF_RULESET_FILTER].inactive.ptr; - rs->rules[PF_RULESET_FILTER].inactive.ptr = old_rules; - - /* - * XXX expanding the pf_opt_rule format throughout pfctl might allow - * us to avoid all this copying. - */ - while ((r = TAILQ_FIRST(rs->rules[PF_RULESET_FILTER].inactive.ptr)) - != NULL) { - TAILQ_REMOVE(rs->rules[PF_RULESET_FILTER].inactive.ptr, r, - entries); - if ((por = calloc(1, sizeof(*por))) == NULL) - err(1, "calloc"); - memcpy(&por->por_rule, r, sizeof(*r)); - if (TAILQ_FIRST(&r->rpool.list) != NULL) { - TAILQ_INIT(&por->por_rule.rpool.list); - pfctl_move_pool(&r->rpool, &por->por_rule.rpool); - } else - bzero(&por->por_rule.rpool, - sizeof(por->por_rule.rpool)); - - - TAILQ_INSERT_TAIL(&opt_queue, por, por_entry); - } - - TAILQ_INIT(&superblocks); - if (construct_superblocks(pf, &opt_queue, &superblocks)) - goto error; - - if (pf->optimize & PF_OPTIMIZE_PROFILE) { - if (load_feedback_profile(pf, &superblocks)) - goto error; - } - - TAILQ_FOREACH(block, &superblocks, sb_entry) { - if (optimize_superblock(pf, block)) - goto error; - } - - rs->anchor->refcnt = 0; - while ((block = TAILQ_FIRST(&superblocks))) { - TAILQ_REMOVE(&superblocks, block, sb_entry); - - while ((por = TAILQ_FIRST(&block->sb_rules))) { - TAILQ_REMOVE(&block->sb_rules, por, por_entry); - por->por_rule.nr = rs->anchor->refcnt++; - if ((r = calloc(1, sizeof(*r))) == NULL) - err(1, "calloc"); - memcpy(r, &por->por_rule, sizeof(*r)); - TAILQ_INIT(&r->rpool.list); - pfctl_move_pool(&por->por_rule.rpool, &r->rpool); - TAILQ_INSERT_TAIL( - rs->rules[PF_RULESET_FILTER].active.ptr, - r, entries); - free(por); - } - free(block); - } - - return (0); - -error: - while ((por = TAILQ_FIRST(&opt_queue))) { - TAILQ_REMOVE(&opt_queue, por, por_entry); - if (por->por_src_tbl) { - pfr_buf_clear(por->por_src_tbl->pt_buf); - free(por->por_src_tbl->pt_buf); - free(por->por_src_tbl); - } - if (por->por_dst_tbl) { - pfr_buf_clear(por->por_dst_tbl->pt_buf); - free(por->por_dst_tbl->pt_buf); - free(por->por_dst_tbl); - } - free(por); - } - while ((block = TAILQ_FIRST(&superblocks))) { - TAILQ_REMOVE(&superblocks, block, sb_entry); - superblock_free(pf, block); - } - return (1); -} - - -/* - * Go ahead and optimize a superblock - */ -int -optimize_superblock(struct pfctl *pf, struct superblock *block) -{ -#ifdef OPT_DEBUG - struct pf_opt_rule *por; -#endif /* OPT_DEBUG */ - - /* We have a few optimization passes: - * 1) remove duplicate rules or rules that are a subset of other - * rules - * 2) combine otherwise identical rules with different IP addresses - * into a single rule and put the addresses in a table. - * 3) re-order the rules to improve kernel skip steps - * 4) re-order the 'quick' rules based on feedback from the - * active ruleset statistics - * - * XXX combine_rules() doesn't combine v4 and v6 rules. would just - * have to keep af in the table container, make af 'COMBINE' and - * twiddle the af on the merged rule - * XXX maybe add a weighting to the metric on skipsteps when doing - * reordering. sometimes two sequential tables will be better - * that four consecutive interfaces. - * XXX need to adjust the skipstep count of everything after PROTO, - * since they aren't actually checked on a proto mismatch in - * pf_test_{tcp, udp, icmp}() - * XXX should i treat proto=0, af=0 or dir=0 special in skepstep - * calculation since they are a DC? - * XXX keep last skiplist of last superblock to influence this - * superblock. '5 inet6 log' should make '3 inet6' come before '4 - * inet' in the next superblock. - * XXX would be useful to add tables for ports - * XXX we can also re-order some mutually exclusive superblocks to - * try merging superblocks before any of these optimization passes. - * for instance a single 'log in' rule in the middle of non-logging - * out rules. - */ - - /* shortcut. there will be a lot of 1-rule superblocks */ - if (!TAILQ_NEXT(TAILQ_FIRST(&block->sb_rules), por_entry)) - return (0); - -#ifdef OPT_DEBUG - printf("--- Superblock ---\n"); - TAILQ_FOREACH(por, &block->sb_rules, por_entry) { - printf(" "); - print_rule(&por->por_rule, por->por_rule.anchor ? - por->por_rule.anchor->name : "", 1, 0); - } -#endif /* OPT_DEBUG */ - - - if (remove_identical_rules(pf, block)) - return (1); - if (combine_rules(pf, block)) - return (1); - if ((pf->optimize & PF_OPTIMIZE_PROFILE) && - TAILQ_FIRST(&block->sb_rules)->por_rule.quick && - block->sb_profiled_block) { - if (block_feedback(pf, block)) - return (1); - } else if (reorder_rules(pf, block, 0)) { - return (1); - } - - /* - * Don't add any optimization passes below reorder_rules(). It will - * have divided superblocks into smaller blocks for further refinement - * and doesn't put them back together again. What once was a true - * superblock might have been split into multiple superblocks. - */ - -#ifdef OPT_DEBUG - printf("--- END Superblock ---\n"); -#endif /* OPT_DEBUG */ - return (0); -} - - -/* - * Optimization pass #1: remove identical rules - */ -int -remove_identical_rules(struct pfctl *pf, struct superblock *block) -{ - struct pf_opt_rule *por1, *por2, *por_next, *por2_next; - struct pf_rule a, a2, b, b2; - - for (por1 = TAILQ_FIRST(&block->sb_rules); por1; por1 = por_next) { - por_next = TAILQ_NEXT(por1, por_entry); - for (por2 = por_next; por2; por2 = por2_next) { - por2_next = TAILQ_NEXT(por2, por_entry); - comparable_rule(&a, &por1->por_rule, DC); - comparable_rule(&b, &por2->por_rule, DC); - memcpy(&a2, &a, sizeof(a2)); - memcpy(&b2, &b, sizeof(b2)); - - exclude_supersets(&a, &b); - exclude_supersets(&b2, &a2); - if (memcmp(&a, &b, sizeof(a)) == 0) { - DEBUG("removing identical rule nr%d = *nr%d*", - por1->por_rule.nr, por2->por_rule.nr); - TAILQ_REMOVE(&block->sb_rules, por2, por_entry); - if (por_next == por2) - por_next = TAILQ_NEXT(por1, por_entry); - free(por2); - } else if (memcmp(&a2, &b2, sizeof(a2)) == 0) { - DEBUG("removing identical rule *nr%d* = nr%d", - por1->por_rule.nr, por2->por_rule.nr); - TAILQ_REMOVE(&block->sb_rules, por1, por_entry); - free(por1); - break; - } - } - } - - return (0); -} - - -/* - * Optimization pass #2: combine similar rules with different addresses - * into a single rule and a table - */ -int -combine_rules(struct pfctl *pf, struct superblock *block) -{ - struct pf_opt_rule *p1, *p2, *por_next; - int src_eq, dst_eq; - - if ((pf->loadopt & PFCTL_FLAG_TABLE) == 0) { - warnx("Must enable table loading for optimizations"); - return (1); - } - - /* First we make a pass to combine the rules. O(n log n) */ - TAILQ_FOREACH(p1, &block->sb_rules, por_entry) { - for (p2 = TAILQ_NEXT(p1, por_entry); p2; p2 = por_next) { - por_next = TAILQ_NEXT(p2, por_entry); - - src_eq = addrs_equal(&p1->por_rule.src, - &p2->por_rule.src); - dst_eq = addrs_equal(&p1->por_rule.dst, - &p2->por_rule.dst); - - if (src_eq && !dst_eq && p1->por_src_tbl == NULL && - p2->por_dst_tbl == NULL && - p2->por_src_tbl == NULL && - rules_combineable(&p1->por_rule, &p2->por_rule) && - addrs_combineable(&p1->por_rule.dst, - &p2->por_rule.dst)) { - DEBUG("can combine rules nr%d = nr%d", - p1->por_rule.nr, p2->por_rule.nr); - if (p1->por_dst_tbl == NULL && - add_opt_table(pf, &p1->por_dst_tbl, - p1->por_rule.af, &p1->por_rule.dst)) - return (1); - if (add_opt_table(pf, &p1->por_dst_tbl, - p1->por_rule.af, &p2->por_rule.dst)) - return (1); - p2->por_dst_tbl = p1->por_dst_tbl; - if (p1->por_dst_tbl->pt_rulecount >= - TABLE_THRESHOLD) { - TAILQ_REMOVE(&block->sb_rules, p2, - por_entry); - free(p2); - } - } else if (!src_eq && dst_eq && p1->por_dst_tbl == NULL - && p2->por_src_tbl == NULL && - p2->por_dst_tbl == NULL && - rules_combineable(&p1->por_rule, &p2->por_rule) && - addrs_combineable(&p1->por_rule.src, - &p2->por_rule.src)) { - DEBUG("can combine rules nr%d = nr%d", - p1->por_rule.nr, p2->por_rule.nr); - if (p1->por_src_tbl == NULL && - add_opt_table(pf, &p1->por_src_tbl, - p1->por_rule.af, &p1->por_rule.src)) - return (1); - if (add_opt_table(pf, &p1->por_src_tbl, - p1->por_rule.af, &p2->por_rule.src)) - return (1); - p2->por_src_tbl = p1->por_src_tbl; - if (p1->por_src_tbl->pt_rulecount >= - TABLE_THRESHOLD) { - TAILQ_REMOVE(&block->sb_rules, p2, - por_entry); - free(p2); - } - } - } - } - - - /* - * Then we make a final pass to create a valid table name and - * insert the name into the rules. - */ - for (p1 = TAILQ_FIRST(&block->sb_rules); p1; p1 = por_next) { - por_next = TAILQ_NEXT(p1, por_entry); - assert(p1->por_src_tbl == NULL || p1->por_dst_tbl == NULL); - - if (p1->por_src_tbl && p1->por_src_tbl->pt_rulecount >= - TABLE_THRESHOLD) { - if (p1->por_src_tbl->pt_generated) { - /* This rule is included in a table */ - TAILQ_REMOVE(&block->sb_rules, p1, por_entry); - free(p1); - continue; - } - p1->por_src_tbl->pt_generated = 1; - - if ((pf->opts & PF_OPT_NOACTION) == 0 && - pf_opt_create_table(pf, p1->por_src_tbl)) - return (1); - - pf->tdirty = 1; - - if (pf->opts & PF_OPT_VERBOSE) - print_tabledef(p1->por_src_tbl->pt_name, - PFR_TFLAG_CONST, 1, - &p1->por_src_tbl->pt_nodes); - - memset(&p1->por_rule.src.addr, 0, - sizeof(p1->por_rule.src.addr)); - p1->por_rule.src.addr.type = PF_ADDR_TABLE; - strlcpy(p1->por_rule.src.addr.v.tblname, - p1->por_src_tbl->pt_name, - sizeof(p1->por_rule.src.addr.v.tblname)); - - pfr_buf_clear(p1->por_src_tbl->pt_buf); - free(p1->por_src_tbl->pt_buf); - p1->por_src_tbl->pt_buf = NULL; - } - if (p1->por_dst_tbl && p1->por_dst_tbl->pt_rulecount >= - TABLE_THRESHOLD) { - if (p1->por_dst_tbl->pt_generated) { - /* This rule is included in a table */ - TAILQ_REMOVE(&block->sb_rules, p1, por_entry); - free(p1); - continue; - } - p1->por_dst_tbl->pt_generated = 1; - - if ((pf->opts & PF_OPT_NOACTION) == 0 && - pf_opt_create_table(pf, p1->por_dst_tbl)) - return (1); - pf->tdirty = 1; - - if (pf->opts & PF_OPT_VERBOSE) - print_tabledef(p1->por_dst_tbl->pt_name, - PFR_TFLAG_CONST, 1, - &p1->por_dst_tbl->pt_nodes); - - memset(&p1->por_rule.dst.addr, 0, - sizeof(p1->por_rule.dst.addr)); - p1->por_rule.dst.addr.type = PF_ADDR_TABLE; - strlcpy(p1->por_rule.dst.addr.v.tblname, - p1->por_dst_tbl->pt_name, - sizeof(p1->por_rule.dst.addr.v.tblname)); - - pfr_buf_clear(p1->por_dst_tbl->pt_buf); - free(p1->por_dst_tbl->pt_buf); - p1->por_dst_tbl->pt_buf = NULL; - } - } - - return (0); -} - - -/* - * Optimization pass #3: re-order rules to improve skip steps - */ -int -reorder_rules(struct pfctl *pf, struct superblock *block, int depth) -{ - struct superblock *newblock; - struct pf_skip_step *skiplist; - struct pf_opt_rule *por; - int i, largest, largest_list, rule_count = 0; - TAILQ_HEAD( , pf_opt_rule) head; - - /* - * Calculate the best-case skip steps. We put each rule in a list - * of other rules with common fields - */ - for (i = 0; i < PF_SKIP_COUNT; i++) { - TAILQ_FOREACH(por, &block->sb_rules, por_entry) { - TAILQ_FOREACH(skiplist, &block->sb_skipsteps[i], - ps_entry) { - if (skip_compare(i, skiplist, por) == 0) - break; - } - if (skiplist == NULL) { - if ((skiplist = calloc(1, sizeof(*skiplist))) == - NULL) - err(1, "calloc"); - TAILQ_INIT(&skiplist->ps_rules); - TAILQ_INSERT_TAIL(&block->sb_skipsteps[i], - skiplist, ps_entry); - } - skip_append(block, i, skiplist, por); - } - } - - TAILQ_FOREACH(por, &block->sb_rules, por_entry) - rule_count++; - - /* - * Now we're going to ignore any fields that are identical between - * all of the rules in the superblock and those fields which differ - * between every rule in the superblock. - */ - largest = 0; - for (i = 0; i < PF_SKIP_COUNT; i++) { - skiplist = TAILQ_FIRST(&block->sb_skipsteps[i]); - if (skiplist->ps_count == rule_count) { - DEBUG("(%d) original skipstep '%s' is all rules", - depth, skip_comparitors_names[i]); - skiplist->ps_count = 0; - } else if (skiplist->ps_count == 1) { - skiplist->ps_count = 0; - } else { - DEBUG("(%d) original skipstep '%s' largest jump is %d", - depth, skip_comparitors_names[i], - skiplist->ps_count); - if (skiplist->ps_count > largest) - largest = skiplist->ps_count; - } - } - if (largest == 0) { - /* Ugh. There is NO commonality in the superblock on which - * optimize the skipsteps optimization. - */ - goto done; - } - - /* - * Now we're going to empty the superblock rule list and re-create - * it based on a more optimal skipstep order. - */ - TAILQ_INIT(&head); - while ((por = TAILQ_FIRST(&block->sb_rules))) { - TAILQ_REMOVE(&block->sb_rules, por, por_entry); - TAILQ_INSERT_TAIL(&head, por, por_entry); - } - - - while (!TAILQ_EMPTY(&head)) { - largest = 1; - - /* - * Find the most useful skip steps remaining - */ - for (i = 0; i < PF_SKIP_COUNT; i++) { - skiplist = TAILQ_FIRST(&block->sb_skipsteps[i]); - if (skiplist->ps_count > largest) { - largest = skiplist->ps_count; - largest_list = i; - } - } - - if (largest <= 1) { - /* - * Nothing useful left. Leave remaining rules in order. - */ - DEBUG("(%d) no more commonality for skip steps", depth); - while ((por = TAILQ_FIRST(&head))) { - TAILQ_REMOVE(&head, por, por_entry); - TAILQ_INSERT_TAIL(&block->sb_rules, por, - por_entry); - } - } else { - /* - * There is commonality. Extract those common rules - * and place them in the ruleset adjacent to each - * other. - */ - skiplist = TAILQ_FIRST(&block->sb_skipsteps[ - largest_list]); - DEBUG("(%d) skipstep '%s' largest jump is %d @ #%d", - depth, skip_comparitors_names[largest_list], - largest, TAILQ_FIRST(&TAILQ_FIRST(&block-> - sb_skipsteps [largest_list])->ps_rules)-> - por_rule.nr); - TAILQ_REMOVE(&block->sb_skipsteps[largest_list], - skiplist, ps_entry); - - - /* - * There may be further commonality inside these - * rules. So we'll split them off into they're own - * superblock and pass it back into the optimizer. - */ - if (skiplist->ps_count > 2) { - if ((newblock = calloc(1, sizeof(*newblock))) - == NULL) { - warn("calloc"); - return (1); - } - TAILQ_INIT(&newblock->sb_rules); - for (i = 0; i < PF_SKIP_COUNT; i++) - TAILQ_INIT(&newblock->sb_skipsteps[i]); - TAILQ_INSERT_BEFORE(block, newblock, sb_entry); - DEBUG("(%d) splitting off %d rules from superblock @ #%d", - depth, skiplist->ps_count, - TAILQ_FIRST(&skiplist->ps_rules)-> - por_rule.nr); - } else { - newblock = block; - } - - while ((por = TAILQ_FIRST(&skiplist->ps_rules))) { - TAILQ_REMOVE(&head, por, por_entry); - TAILQ_REMOVE(&skiplist->ps_rules, por, - por_skip_entry[largest_list]); - TAILQ_INSERT_TAIL(&newblock->sb_rules, por, - por_entry); - - /* Remove this rule from all other skiplists */ - remove_from_skipsteps(&block->sb_skipsteps[ - largest_list], block, por, skiplist); - } - free(skiplist); - if (newblock != block) - if (reorder_rules(pf, newblock, depth + 1)) - return (1); - } - } - -done: - for (i = 0; i < PF_SKIP_COUNT; i++) { - while ((skiplist = TAILQ_FIRST(&block->sb_skipsteps[i]))) { - TAILQ_REMOVE(&block->sb_skipsteps[i], skiplist, - ps_entry); - free(skiplist); - } - } - - return (0); -} - - -/* - * Optimization pass #4: re-order 'quick' rules based on feedback from the - * currently running ruleset - */ -int -block_feedback(struct pfctl *pf, struct superblock *block) -{ - TAILQ_HEAD( , pf_opt_rule) queue; - struct pf_opt_rule *por1, *por2; - u_int64_t total_count = 0; - struct pf_rule a, b; - - - /* - * Walk through all of the profiled superblock's rules and copy - * the counters onto our rules. - */ - TAILQ_FOREACH(por1, &block->sb_profiled_block->sb_rules, por_entry) { - comparable_rule(&a, &por1->por_rule, DC); - total_count += por1->por_rule.packets[0] + - por1->por_rule.packets[1]; - TAILQ_FOREACH(por2, &block->sb_rules, por_entry) { - if (por2->por_profile_count) - continue; - comparable_rule(&b, &por2->por_rule, DC); - if (memcmp(&a, &b, sizeof(a)) == 0) { - por2->por_profile_count = - por1->por_rule.packets[0] + - por1->por_rule.packets[1]; - break; - } - } - } - superblock_free(pf, block->sb_profiled_block); - block->sb_profiled_block = NULL; - - /* - * Now we pull all of the rules off the superblock and re-insert them - * in sorted order. - */ - - TAILQ_INIT(&queue); - while ((por1 = TAILQ_FIRST(&block->sb_rules)) != NULL) { - TAILQ_REMOVE(&block->sb_rules, por1, por_entry); - TAILQ_INSERT_TAIL(&queue, por1, por_entry); - } - - while ((por1 = TAILQ_FIRST(&queue)) != NULL) { - TAILQ_REMOVE(&queue, por1, por_entry); -/* XXX I should sort all of the unused rules based on skip steps */ - TAILQ_FOREACH(por2, &block->sb_rules, por_entry) { - if (por1->por_profile_count > por2->por_profile_count) { - TAILQ_INSERT_BEFORE(por2, por1, por_entry); - break; - } - } -#ifdef __FreeBSD__ - if (por2 == NULL) -#else - if (por2 == TAILQ_END(&block->sb_rules)) -#endif - TAILQ_INSERT_TAIL(&block->sb_rules, por1, por_entry); - } - - return (0); -} - - -/* - * Load the current ruleset from the kernel and try to associate them with - * the ruleset we're optimizing. - */ -int -load_feedback_profile(struct pfctl *pf, struct superblocks *superblocks) -{ - struct superblock *block, *blockcur; - struct superblocks prof_superblocks; - struct pf_opt_rule *por; - struct pf_opt_queue queue; - struct pfioc_rule pr; - struct pf_rule a, b; - int nr, mnr; - - TAILQ_INIT(&queue); - TAILQ_INIT(&prof_superblocks); - - memset(&pr, 0, sizeof(pr)); - pr.rule.action = PF_PASS; - if (ioctl(pf->dev, DIOCGETRULES, &pr)) { - warn("DIOCGETRULES"); - return (1); - } - mnr = pr.nr; - - DEBUG("Loading %d active rules for a feedback profile", mnr); - for (nr = 0; nr < mnr; ++nr) { - struct pf_ruleset *rs; - if ((por = calloc(1, sizeof(*por))) == NULL) { - warn("calloc"); - return (1); - } - pr.nr = nr; - if (ioctl(pf->dev, DIOCGETRULE, &pr)) { - warn("DIOCGETRULES"); - return (1); - } - memcpy(&por->por_rule, &pr.rule, sizeof(por->por_rule)); - rs = pf_find_or_create_ruleset(pr.anchor_call); - por->por_rule.anchor = rs->anchor; - if (TAILQ_EMPTY(&por->por_rule.rpool.list)) - memset(&por->por_rule.rpool, 0, - sizeof(por->por_rule.rpool)); - TAILQ_INSERT_TAIL(&queue, por, por_entry); - - /* XXX pfctl_get_pool(pf->dev, &pr.rule.rpool, nr, pr.ticket, - * PF_PASS, pf->anchor) ??? - * ... pfctl_clear_pool(&pr.rule.rpool) - */ - } - - if (construct_superblocks(pf, &queue, &prof_superblocks)) - return (1); - - - /* - * Now we try to associate the active ruleset's superblocks with - * the superblocks we're compiling. - */ - block = TAILQ_FIRST(superblocks); - blockcur = TAILQ_FIRST(&prof_superblocks); - while (block && blockcur) { - comparable_rule(&a, &TAILQ_FIRST(&block->sb_rules)->por_rule, - BREAK); - comparable_rule(&b, &TAILQ_FIRST(&blockcur->sb_rules)->por_rule, - BREAK); - if (memcmp(&a, &b, sizeof(a)) == 0) { - /* The two superblocks lined up */ - block->sb_profiled_block = blockcur; - } else { - DEBUG("superblocks don't line up between #%d and #%d", - TAILQ_FIRST(&block->sb_rules)->por_rule.nr, - TAILQ_FIRST(&blockcur->sb_rules)->por_rule.nr); - break; - } - block = TAILQ_NEXT(block, sb_entry); - blockcur = TAILQ_NEXT(blockcur, sb_entry); - } - - - - /* Free any superblocks we couldn't link */ - while (blockcur) { - block = TAILQ_NEXT(blockcur, sb_entry); - superblock_free(pf, blockcur); - blockcur = block; - } - return (0); -} - - -/* - * Compare a rule to a skiplist to see if the rule is a member - */ -int -skip_compare(int skipnum, struct pf_skip_step *skiplist, - struct pf_opt_rule *por) -{ - struct pf_rule *a, *b; - if (skipnum >= PF_SKIP_COUNT || skipnum < 0) - errx(1, "skip_compare() out of bounds"); - a = &por->por_rule; - b = &TAILQ_FIRST(&skiplist->ps_rules)->por_rule; - - return ((skip_comparitors[skipnum])(a, b)); -} - - -/* - * Add a rule to a skiplist - */ -void -skip_append(struct superblock *superblock, int skipnum, - struct pf_skip_step *skiplist, struct pf_opt_rule *por) -{ - struct pf_skip_step *prev; - - skiplist->ps_count++; - TAILQ_INSERT_TAIL(&skiplist->ps_rules, por, por_skip_entry[skipnum]); - - /* Keep the list of skiplists sorted by whichever is larger */ - while ((prev = TAILQ_PREV(skiplist, skiplist, ps_entry)) && - prev->ps_count < skiplist->ps_count) { - TAILQ_REMOVE(&superblock->sb_skipsteps[skipnum], - skiplist, ps_entry); - TAILQ_INSERT_BEFORE(prev, skiplist, ps_entry); - } -} - - -/* - * Remove a rule from the other skiplist calculations. - */ -void -remove_from_skipsteps(struct skiplist *head, struct superblock *block, - struct pf_opt_rule *por, struct pf_skip_step *active_list) -{ - struct pf_skip_step *sk, *next; - struct pf_opt_rule *p2; - int i, found; - - for (i = 0; i < PF_SKIP_COUNT; i++) { - sk = TAILQ_FIRST(&block->sb_skipsteps[i]); - if (sk == NULL || sk == active_list || sk->ps_count <= 1) - continue; - found = 0; - do { - TAILQ_FOREACH(p2, &sk->ps_rules, por_skip_entry[i]) - if (p2 == por) { - TAILQ_REMOVE(&sk->ps_rules, p2, - por_skip_entry[i]); - found = 1; - sk->ps_count--; - break; - } - } while (!found && (sk = TAILQ_NEXT(sk, ps_entry))); - if (found && sk) { - /* Does this change the sorting order? */ - while ((next = TAILQ_NEXT(sk, ps_entry)) && - next->ps_count > sk->ps_count) { - TAILQ_REMOVE(head, sk, ps_entry); - TAILQ_INSERT_AFTER(head, next, sk, ps_entry); - } -#ifdef OPT_DEBUG - next = TAILQ_NEXT(sk, ps_entry); - assert(next == NULL || next->ps_count <= sk->ps_count); -#endif /* OPT_DEBUG */ - } - } -} - - -/* Compare two rules AF field for skiplist construction */ -int -skip_cmp_af(struct pf_rule *a, struct pf_rule *b) -{ - if (a->af != b->af || a->af == 0) - return (1); - return (0); -} - -/* Compare two rules DIRECTION field for skiplist construction */ -int -skip_cmp_dir(struct pf_rule *a, struct pf_rule *b) -{ - if (a->direction == 0 || a->direction != b->direction) - return (1); - return (0); -} - -/* Compare two rules DST Address field for skiplist construction */ -int -skip_cmp_dst_addr(struct pf_rule *a, struct pf_rule *b) -{ - if (a->dst.neg != b->dst.neg || - a->dst.addr.type != b->dst.addr.type) - return (1); - /* XXX if (a->proto != b->proto && a->proto != 0 && b->proto != 0 - * && (a->proto == IPPROTO_TCP || a->proto == IPPROTO_UDP || - * a->proto == IPPROTO_ICMP - * return (1); - */ - switch (a->dst.addr.type) { - case PF_ADDR_ADDRMASK: - if (memcmp(&a->dst.addr.v.a.addr, &b->dst.addr.v.a.addr, - sizeof(a->dst.addr.v.a.addr)) || - memcmp(&a->dst.addr.v.a.mask, &b->dst.addr.v.a.mask, - sizeof(a->dst.addr.v.a.mask)) || - (a->dst.addr.v.a.addr.addr32[0] == 0 && - a->dst.addr.v.a.addr.addr32[1] == 0 && - a->dst.addr.v.a.addr.addr32[2] == 0 && - a->dst.addr.v.a.addr.addr32[3] == 0)) - return (1); - return (0); - case PF_ADDR_DYNIFTL: - if (strcmp(a->dst.addr.v.ifname, b->dst.addr.v.ifname) != 0 || - a->dst.addr.iflags != a->dst.addr.iflags || - memcmp(&a->dst.addr.v.a.mask, &b->dst.addr.v.a.mask, - sizeof(a->dst.addr.v.a.mask))) - return (1); - return (0); - case PF_ADDR_NOROUTE: - case PF_ADDR_URPFFAILED: - return (0); - case PF_ADDR_TABLE: - return (strcmp(a->dst.addr.v.tblname, b->dst.addr.v.tblname)); - } - return (1); -} - -/* Compare two rules DST port field for skiplist construction */ -int -skip_cmp_dst_port(struct pf_rule *a, struct pf_rule *b) -{ - /* XXX if (a->proto != b->proto && a->proto != 0 && b->proto != 0 - * && (a->proto == IPPROTO_TCP || a->proto == IPPROTO_UDP || - * a->proto == IPPROTO_ICMP - * return (1); - */ - if (a->dst.port_op == PF_OP_NONE || a->dst.port_op != b->dst.port_op || - a->dst.port[0] != b->dst.port[0] || - a->dst.port[1] != b->dst.port[1]) - return (1); - return (0); -} - -/* Compare two rules IFP field for skiplist construction */ -int -skip_cmp_ifp(struct pf_rule *a, struct pf_rule *b) -{ - if (strcmp(a->ifname, b->ifname) || a->ifname[0] == '\0') - return (1); - return (a->ifnot != b->ifnot); -} - -/* Compare two rules PROTO field for skiplist construction */ -int -skip_cmp_proto(struct pf_rule *a, struct pf_rule *b) -{ - return (a->proto != b->proto || a->proto == 0); -} - -/* Compare two rules SRC addr field for skiplist construction */ -int -skip_cmp_src_addr(struct pf_rule *a, struct pf_rule *b) -{ - if (a->src.neg != b->src.neg || - a->src.addr.type != b->src.addr.type) - return (1); - /* XXX if (a->proto != b->proto && a->proto != 0 && b->proto != 0 - * && (a->proto == IPPROTO_TCP || a->proto == IPPROTO_UDP || - * a->proto == IPPROTO_ICMP - * return (1); - */ - switch (a->src.addr.type) { - case PF_ADDR_ADDRMASK: - if (memcmp(&a->src.addr.v.a.addr, &b->src.addr.v.a.addr, - sizeof(a->src.addr.v.a.addr)) || - memcmp(&a->src.addr.v.a.mask, &b->src.addr.v.a.mask, - sizeof(a->src.addr.v.a.mask)) || - (a->src.addr.v.a.addr.addr32[0] == 0 && - a->src.addr.v.a.addr.addr32[1] == 0 && - a->src.addr.v.a.addr.addr32[2] == 0 && - a->src.addr.v.a.addr.addr32[3] == 0)) - return (1); - return (0); - case PF_ADDR_DYNIFTL: - if (strcmp(a->src.addr.v.ifname, b->src.addr.v.ifname) != 0 || - a->src.addr.iflags != a->src.addr.iflags || - memcmp(&a->src.addr.v.a.mask, &b->src.addr.v.a.mask, - sizeof(a->src.addr.v.a.mask))) - return (1); - return (0); - case PF_ADDR_NOROUTE: - case PF_ADDR_URPFFAILED: - return (0); - case PF_ADDR_TABLE: - return (strcmp(a->src.addr.v.tblname, b->src.addr.v.tblname)); - } - return (1); -} - -/* Compare two rules SRC port field for skiplist construction */ -int -skip_cmp_src_port(struct pf_rule *a, struct pf_rule *b) -{ - if (a->src.port_op == PF_OP_NONE || a->src.port_op != b->src.port_op || - a->src.port[0] != b->src.port[0] || - a->src.port[1] != b->src.port[1]) - return (1); - /* XXX if (a->proto != b->proto && a->proto != 0 && b->proto != 0 - * && (a->proto == IPPROTO_TCP || a->proto == IPPROTO_UDP || - * a->proto == IPPROTO_ICMP - * return (1); - */ - return (0); -} - - -void -skip_init(void) -{ - struct { - char *name; - int skipnum; - int (*func)(struct pf_rule *, struct pf_rule *); - } comps[] = PF_SKIP_COMPARITORS; - int skipnum, i; - - for (skipnum = 0; skipnum < PF_SKIP_COUNT; skipnum++) { - for (i = 0; i < sizeof(comps)/sizeof(*comps); i++) - if (comps[i].skipnum == skipnum) { - skip_comparitors[skipnum] = comps[i].func; - skip_comparitors_names[skipnum] = comps[i].name; - } - } - for (skipnum = 0; skipnum < PF_SKIP_COUNT; skipnum++) - if (skip_comparitors[skipnum] == NULL) - errx(1, "Need to add skip step comparitor to pfctl?!"); -} - -/* - * Add a host/netmask to a table - */ -int -add_opt_table(struct pfctl *pf, struct pf_opt_tbl **tbl, sa_family_t af, - struct pf_rule_addr *addr) -{ -#ifdef OPT_DEBUG - char buf[128]; -#endif /* OPT_DEBUG */ - static int tablenum = 0; - struct node_host node_host; - - if (*tbl == NULL) { - if ((*tbl = calloc(1, sizeof(**tbl))) == NULL || - ((*tbl)->pt_buf = calloc(1, sizeof(*(*tbl)->pt_buf))) == - NULL) - err(1, "calloc"); - (*tbl)->pt_buf->pfrb_type = PFRB_ADDRS; - SIMPLEQ_INIT(&(*tbl)->pt_nodes); - - /* This is just a temporary table name */ - snprintf((*tbl)->pt_name, sizeof((*tbl)->pt_name), "%s%d", - PF_OPT_TABLE_PREFIX, tablenum++); - DEBUG("creating table <%s>", (*tbl)->pt_name); - } - - memset(&node_host, 0, sizeof(node_host)); - node_host.af = af; - node_host.addr = addr->addr; - -#ifdef OPT_DEBUG - DEBUG("<%s> adding %s/%d", (*tbl)->pt_name, inet_ntop(af, - &node_host.addr.v.a.addr, buf, sizeof(buf)), - unmask(&node_host.addr.v.a.mask, af)); -#endif /* OPT_DEBUG */ - - if (append_addr_host((*tbl)->pt_buf, &node_host, 0, 0)) { - warn("failed to add host"); - return (1); - } - if (pf->opts & PF_OPT_VERBOSE) { - struct node_tinit *ti; - - if ((ti = calloc(1, sizeof(*ti))) == NULL) - err(1, "malloc"); - if ((ti->host = malloc(sizeof(*ti->host))) == NULL) - err(1, "malloc"); - memcpy(ti->host, &node_host, sizeof(*ti->host)); - SIMPLEQ_INSERT_TAIL(&(*tbl)->pt_nodes, ti, entries); - } - - (*tbl)->pt_rulecount++; - if ((*tbl)->pt_rulecount == TABLE_THRESHOLD) - DEBUG("table <%s> now faster than skip steps", (*tbl)->pt_name); - - return (0); -} - - -/* - * Do the dirty work of choosing an unused table name and creating it. - * (be careful with the table name, it might already be used in another anchor) - */ -int -pf_opt_create_table(struct pfctl *pf, struct pf_opt_tbl *tbl) -{ - static int tablenum; - struct pfr_table *t; - - if (table_buffer.pfrb_type == 0) { - /* Initialize the list of tables */ - table_buffer.pfrb_type = PFRB_TABLES; - for (;;) { - pfr_buf_grow(&table_buffer, table_buffer.pfrb_size); - table_buffer.pfrb_size = table_buffer.pfrb_msize; - if (pfr_get_tables(NULL, table_buffer.pfrb_caddr, - &table_buffer.pfrb_size, PFR_FLAG_ALLRSETS)) - err(1, "pfr_get_tables"); - if (table_buffer.pfrb_size <= table_buffer.pfrb_msize) - break; - } - table_identifier = arc4random(); - } - - /* XXX would be *really* nice to avoid duplicating identical tables */ - - /* Now we have to pick a table name that isn't used */ -again: - DEBUG("translating temporary table <%s> to <%s%x_%d>", tbl->pt_name, - PF_OPT_TABLE_PREFIX, table_identifier, tablenum); - snprintf(tbl->pt_name, sizeof(tbl->pt_name), "%s%x_%d", - PF_OPT_TABLE_PREFIX, table_identifier, tablenum); - PFRB_FOREACH(t, &table_buffer) { - if (strcasecmp(t->pfrt_name, tbl->pt_name) == 0) { - /* Collision. Try again */ - DEBUG("wow, table <%s> in use. trying again", - tbl->pt_name); - table_identifier = arc4random(); - goto again; - } - } - tablenum++; - - - if (pfctl_define_table(tbl->pt_name, PFR_TFLAG_CONST, 1, - pf->astack[0]->name, tbl->pt_buf, pf->astack[0]->ruleset.tticket)) { - warn("failed to create table %s in %s", - tbl->pt_name, pf->astack[0]->name); - return (1); - } - return (0); -} - -/* - * Partition the flat ruleset into a list of distinct superblocks - */ -int -construct_superblocks(struct pfctl *pf, struct pf_opt_queue *opt_queue, - struct superblocks *superblocks) -{ - struct superblock *block = NULL; - struct pf_opt_rule *por; - int i; - - while (!TAILQ_EMPTY(opt_queue)) { - por = TAILQ_FIRST(opt_queue); - TAILQ_REMOVE(opt_queue, por, por_entry); - if (block == NULL || !superblock_inclusive(block, por)) { - if ((block = calloc(1, sizeof(*block))) == NULL) { - warn("calloc"); - return (1); - } - TAILQ_INIT(&block->sb_rules); - for (i = 0; i < PF_SKIP_COUNT; i++) - TAILQ_INIT(&block->sb_skipsteps[i]); - TAILQ_INSERT_TAIL(superblocks, block, sb_entry); - } - TAILQ_INSERT_TAIL(&block->sb_rules, por, por_entry); - } - - return (0); -} - - -/* - * Compare two rule addresses - */ -int -addrs_equal(struct pf_rule_addr *a, struct pf_rule_addr *b) -{ - if (a->neg != b->neg) - return (0); - return (memcmp(&a->addr, &b->addr, sizeof(a->addr)) == 0); -} - - -/* - * The addresses are not equal, but can we combine them into one table? - */ -int -addrs_combineable(struct pf_rule_addr *a, struct pf_rule_addr *b) -{ - if (a->addr.type != PF_ADDR_ADDRMASK || - b->addr.type != PF_ADDR_ADDRMASK) - return (0); - if (a->neg != b->neg || a->port_op != b->port_op || - a->port[0] != b->port[0] || a->port[1] != b->port[1]) - return (0); - return (1); -} - - -/* - * Are we allowed to combine these two rules - */ -int -rules_combineable(struct pf_rule *p1, struct pf_rule *p2) -{ - struct pf_rule a, b; - - comparable_rule(&a, p1, COMBINED); - comparable_rule(&b, p2, COMBINED); - return (memcmp(&a, &b, sizeof(a)) == 0); -} - - -/* - * Can a rule be included inside a superblock - */ -int -superblock_inclusive(struct superblock *block, struct pf_opt_rule *por) -{ - struct pf_rule a, b; - int i, j; - - /* First check for hard breaks */ - for (i = 0; i < sizeof(pf_rule_desc)/sizeof(*pf_rule_desc); i++) { - if (pf_rule_desc[i].prf_type == BARRIER) { - for (j = 0; j < pf_rule_desc[i].prf_size; j++) - if (((char *)&por->por_rule)[j + - pf_rule_desc[i].prf_offset] != 0) - return (0); - } - } - - /* per-rule src-track is also a hard break */ - if (por->por_rule.rule_flag & PFRULE_RULESRCTRACK) - return (0); - - /* - * Have to handle interface groups separately. Consider the following - * rules: - * block on EXTIFS to any port 22 - * pass on em0 to any port 22 - * (where EXTIFS is an arbitrary interface group) - * The optimizer may decide to re-order the pass rule in front of the - * block rule. But what if EXTIFS includes em0??? Such a reordering - * would change the meaning of the ruleset. - * We can't just lookup the EXTIFS group and check if em0 is a member - * because the user is allowed to add interfaces to a group during - * runtime. - * Ergo interface groups become a defacto superblock break :-( - */ - if (interface_group(por->por_rule.ifname) || - interface_group(TAILQ_FIRST(&block->sb_rules)->por_rule.ifname)) { - if (strcasecmp(por->por_rule.ifname, - TAILQ_FIRST(&block->sb_rules)->por_rule.ifname) != 0) - return (0); - } - - comparable_rule(&a, &TAILQ_FIRST(&block->sb_rules)->por_rule, NOMERGE); - comparable_rule(&b, &por->por_rule, NOMERGE); - if (memcmp(&a, &b, sizeof(a)) == 0) - return (1); - -#ifdef OPT_DEBUG - for (i = 0; i < sizeof(por->por_rule); i++) { - int closest = -1; - if (((u_int8_t *)&a)[i] != ((u_int8_t *)&b)[i]) { - for (j = 0; j < sizeof(pf_rule_desc) / - sizeof(*pf_rule_desc); j++) { - if (i >= pf_rule_desc[j].prf_offset && - i < pf_rule_desc[j].prf_offset + - pf_rule_desc[j].prf_size) { - DEBUG("superblock break @ %d due to %s", - por->por_rule.nr, - pf_rule_desc[j].prf_name); - return (0); - } - if (i > pf_rule_desc[j].prf_offset) { - if (closest == -1 || - i-pf_rule_desc[j].prf_offset < - i-pf_rule_desc[closest].prf_offset) - closest = j; - } - } - - if (closest >= 0) - DEBUG("superblock break @ %d on %s+%xh", - por->por_rule.nr, - pf_rule_desc[closest].prf_name, - i - pf_rule_desc[closest].prf_offset - - pf_rule_desc[closest].prf_size); - else - DEBUG("superblock break @ %d on field @ %d", - por->por_rule.nr, i); - return (0); - } - } -#endif /* OPT_DEBUG */ - - return (0); -} - - -/* - * Figure out if an interface name is an actual interface or actually a - * group of interfaces. - */ -int -interface_group(const char *ifname) -{ - if (ifname == NULL || !ifname[0]) - return (0); - - /* Real interfaces must end in a number, interface groups do not */ - if (isdigit(ifname[strlen(ifname) - 1])) - return (0); - else - return (1); -} - - -/* - * Make a rule that can directly compared by memcmp() - */ -void -comparable_rule(struct pf_rule *dst, const struct pf_rule *src, int type) -{ - int i; - /* - * To simplify the comparison, we just zero out the fields that are - * allowed to be different and then do a simple memcmp() - */ - memcpy(dst, src, sizeof(*dst)); - for (i = 0; i < sizeof(pf_rule_desc)/sizeof(*pf_rule_desc); i++) - if (pf_rule_desc[i].prf_type >= type) { -#ifdef OPT_DEBUG - assert(pf_rule_desc[i].prf_type != NEVER || - *(((char *)dst) + pf_rule_desc[i].prf_offset) == 0); -#endif /* OPT_DEBUG */ - memset(((char *)dst) + pf_rule_desc[i].prf_offset, 0, - pf_rule_desc[i].prf_size); - } -} - - -/* - * Remove superset information from two rules so we can directly compare them - * with memcmp() - */ -void -exclude_supersets(struct pf_rule *super, struct pf_rule *sub) -{ - if (super->ifname[0] == '\0') - memset(sub->ifname, 0, sizeof(sub->ifname)); - if (super->direction == PF_INOUT) - sub->direction = PF_INOUT; - if ((super->proto == 0 || super->proto == sub->proto) && - super->flags == 0 && super->flagset == 0 && (sub->flags || - sub->flagset)) { - sub->flags = super->flags; - sub->flagset = super->flagset; - } - if (super->proto == 0) - sub->proto = 0; - - if (super->src.port_op == 0) { - sub->src.port_op = 0; - sub->src.port[0] = 0; - sub->src.port[1] = 0; - } - if (super->dst.port_op == 0) { - sub->dst.port_op = 0; - sub->dst.port[0] = 0; - sub->dst.port[1] = 0; - } - - if (super->src.addr.type == PF_ADDR_ADDRMASK && !super->src.neg && - !sub->src.neg && super->src.addr.v.a.mask.addr32[0] == 0 && - super->src.addr.v.a.mask.addr32[1] == 0 && - super->src.addr.v.a.mask.addr32[2] == 0 && - super->src.addr.v.a.mask.addr32[3] == 0) - memset(&sub->src.addr, 0, sizeof(sub->src.addr)); - else if (super->src.addr.type == PF_ADDR_ADDRMASK && - sub->src.addr.type == PF_ADDR_ADDRMASK && - super->src.neg == sub->src.neg && - super->af == sub->af && - unmask(&super->src.addr.v.a.mask, super->af) < - unmask(&sub->src.addr.v.a.mask, sub->af) && - super->src.addr.v.a.addr.addr32[0] == - (sub->src.addr.v.a.addr.addr32[0] & - super->src.addr.v.a.mask.addr32[0]) && - super->src.addr.v.a.addr.addr32[1] == - (sub->src.addr.v.a.addr.addr32[1] & - super->src.addr.v.a.mask.addr32[1]) && - super->src.addr.v.a.addr.addr32[2] == - (sub->src.addr.v.a.addr.addr32[2] & - super->src.addr.v.a.mask.addr32[2]) && - super->src.addr.v.a.addr.addr32[3] == - (sub->src.addr.v.a.addr.addr32[3] & - super->src.addr.v.a.mask.addr32[3])) { - /* sub->src.addr is a subset of super->src.addr/mask */ - memcpy(&sub->src.addr, &super->src.addr, sizeof(sub->src.addr)); - } - - if (super->dst.addr.type == PF_ADDR_ADDRMASK && !super->dst.neg && - !sub->dst.neg && super->dst.addr.v.a.mask.addr32[0] == 0 && - super->dst.addr.v.a.mask.addr32[1] == 0 && - super->dst.addr.v.a.mask.addr32[2] == 0 && - super->dst.addr.v.a.mask.addr32[3] == 0) - memset(&sub->dst.addr, 0, sizeof(sub->dst.addr)); - else if (super->dst.addr.type == PF_ADDR_ADDRMASK && - sub->dst.addr.type == PF_ADDR_ADDRMASK && - super->dst.neg == sub->dst.neg && - super->af == sub->af && - unmask(&super->dst.addr.v.a.mask, super->af) < - unmask(&sub->dst.addr.v.a.mask, sub->af) && - super->dst.addr.v.a.addr.addr32[0] == - (sub->dst.addr.v.a.addr.addr32[0] & - super->dst.addr.v.a.mask.addr32[0]) && - super->dst.addr.v.a.addr.addr32[1] == - (sub->dst.addr.v.a.addr.addr32[1] & - super->dst.addr.v.a.mask.addr32[1]) && - super->dst.addr.v.a.addr.addr32[2] == - (sub->dst.addr.v.a.addr.addr32[2] & - super->dst.addr.v.a.mask.addr32[2]) && - super->dst.addr.v.a.addr.addr32[3] == - (sub->dst.addr.v.a.addr.addr32[3] & - super->dst.addr.v.a.mask.addr32[3])) { - /* sub->dst.addr is a subset of super->dst.addr/mask */ - memcpy(&sub->dst.addr, &super->dst.addr, sizeof(sub->dst.addr)); - } - - if (super->af == 0) - sub->af = 0; -} - - -void -superblock_free(struct pfctl *pf, struct superblock *block) -{ - struct pf_opt_rule *por; - while ((por = TAILQ_FIRST(&block->sb_rules))) { - TAILQ_REMOVE(&block->sb_rules, por, por_entry); - if (por->por_src_tbl) { - if (por->por_src_tbl->pt_buf) { - pfr_buf_clear(por->por_src_tbl->pt_buf); - free(por->por_src_tbl->pt_buf); - } - free(por->por_src_tbl); - } - if (por->por_dst_tbl) { - if (por->por_dst_tbl->pt_buf) { - pfr_buf_clear(por->por_dst_tbl->pt_buf); - free(por->por_dst_tbl->pt_buf); - } - free(por->por_dst_tbl); - } - free(por); - } - if (block->sb_profiled_block) - superblock_free(pf, block->sb_profiled_block); - free(block); -} - diff --git a/contrib/pf/pfctl/pfctl_osfp.c b/contrib/pf/pfctl/pfctl_osfp.c deleted file mode 100644 index df78981..0000000 --- a/contrib/pf/pfctl/pfctl_osfp.c +++ /dev/null @@ -1,1108 +0,0 @@ -/* $OpenBSD: pfctl_osfp.c,v 1.14 2006/04/08 02:13:14 ray Exp $ */ - -/* - * Copyright (c) 2003 Mike Frantzen <frantzen@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <sys/types.h> -#include <sys/ioctl.h> -#include <sys/socket.h> - -#include <net/if.h> -#include <net/pfvar.h> - -#include <netinet/in_systm.h> -#include <netinet/ip.h> -#include <netinet/ip6.h> - -#include <ctype.h> -#include <err.h> -#include <errno.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "pfctl_parser.h" -#include "pfctl.h" - -#ifndef MIN -# define MIN(a,b) (((a) < (b)) ? (a) : (b)) -#endif /* MIN */ -#ifndef MAX -# define MAX(a,b) (((a) > (b)) ? (a) : (b)) -#endif /* MAX */ - - -#if 0 -# define DEBUG(fp, str, v...) \ - fprintf(stderr, "%s:%s:%s " str "\n", (fp)->fp_os.fp_class_nm, \ - (fp)->fp_os.fp_version_nm, (fp)->fp_os.fp_subtype_nm , ## v); -#else -# define DEBUG(fp, str, v...) ((void)0) -#endif - - -struct name_entry; -LIST_HEAD(name_list, name_entry); -struct name_entry { - LIST_ENTRY(name_entry) nm_entry; - int nm_num; - char nm_name[PF_OSFP_LEN]; - - struct name_list nm_sublist; - int nm_sublist_num; -}; -struct name_list classes = LIST_HEAD_INITIALIZER(&classes); -int class_count; -int fingerprint_count; - -void add_fingerprint(int, int, struct pf_osfp_ioctl *); -struct name_entry *fingerprint_name_entry(struct name_list *, char *); -void pfctl_flush_my_fingerprints(struct name_list *); -char *get_field(char **, size_t *, int *); -int get_int(char **, size_t *, int *, int *, const char *, - int, int, const char *, int); -int get_str(char **, size_t *, char **, const char *, int, - const char *, int); -int get_tcpopts(const char *, int, const char *, - pf_tcpopts_t *, int *, int *, int *, int *, int *, - int *); -void import_fingerprint(struct pf_osfp_ioctl *); -const char *print_ioctl(struct pf_osfp_ioctl *); -void print_name_list(int, struct name_list *, const char *); -void sort_name_list(int, struct name_list *); -struct name_entry *lookup_name_list(struct name_list *, const char *); - -/* Load fingerprints from a file */ -int -pfctl_file_fingerprints(int dev, int opts, const char *fp_filename) -{ - FILE *in; - char *line; - size_t len; - int i, lineno = 0; - int window, w_mod, ttl, df, psize, p_mod, mss, mss_mod, wscale, - wscale_mod, optcnt, ts0; - pf_tcpopts_t packed_tcpopts; - char *class, *version, *subtype, *desc, *tcpopts; - struct pf_osfp_ioctl fp; - - pfctl_flush_my_fingerprints(&classes); - - if ((in = pfctl_fopen(fp_filename, "r")) == NULL) { - warn("%s", fp_filename); - return (1); - } - class = version = subtype = desc = tcpopts = NULL; - - if ((opts & PF_OPT_NOACTION) == 0) - pfctl_clear_fingerprints(dev, opts); - - while ((line = fgetln(in, &len)) != NULL) { - lineno++; - if (class) - free(class); - if (version) - free(version); - if (subtype) - free(subtype); - if (desc) - free(desc); - if (tcpopts) - free(tcpopts); - class = version = subtype = desc = tcpopts = NULL; - memset(&fp, 0, sizeof(fp)); - - /* Chop off comment */ - for (i = 0; i < len; i++) - if (line[i] == '#') { - len = i; - break; - } - /* Chop off whitespace */ - while (len > 0 && isspace(line[len - 1])) - len--; - while (len > 0 && isspace(line[0])) { - len--; - line++; - } - if (len == 0) - continue; - -#define T_DC 0x01 /* Allow don't care */ -#define T_MSS 0x02 /* Allow MSS multiple */ -#define T_MTU 0x04 /* Allow MTU multiple */ -#define T_MOD 0x08 /* Allow modulus */ - -#define GET_INT(v, mod, n, ty, mx) \ - get_int(&line, &len, &v, mod, n, ty, mx, fp_filename, lineno) -#define GET_STR(v, n, mn) \ - get_str(&line, &len, &v, n, mn, fp_filename, lineno) - - if (GET_INT(window, &w_mod, "window size", T_DC|T_MSS|T_MTU| - T_MOD, 0xffff) || - GET_INT(ttl, NULL, "ttl", 0, 0xff) || - GET_INT(df, NULL, "don't fragment frag", 0, 1) || - GET_INT(psize, &p_mod, "overall packet size", T_MOD|T_DC, - 8192) || - GET_STR(tcpopts, "TCP Options", 1) || - GET_STR(class, "OS class", 1) || - GET_STR(version, "OS version", 0) || - GET_STR(subtype, "OS subtype", 0) || - GET_STR(desc, "OS description", 2)) - continue; - if (get_tcpopts(fp_filename, lineno, tcpopts, &packed_tcpopts, - &optcnt, &mss, &mss_mod, &wscale, &wscale_mod, &ts0)) - continue; - if (len != 0) { - fprintf(stderr, "%s:%d excess field\n", fp_filename, - lineno); - continue; - } - - fp.fp_ttl = ttl; - if (df) - fp.fp_flags |= PF_OSFP_DF; - switch (w_mod) { - case 0: - break; - case T_DC: - fp.fp_flags |= PF_OSFP_WSIZE_DC; - break; - case T_MSS: - fp.fp_flags |= PF_OSFP_WSIZE_MSS; - break; - case T_MTU: - fp.fp_flags |= PF_OSFP_WSIZE_MTU; - break; - case T_MOD: - fp.fp_flags |= PF_OSFP_WSIZE_MOD; - break; - } - fp.fp_wsize = window; - - switch (p_mod) { - case T_DC: - fp.fp_flags |= PF_OSFP_PSIZE_DC; - break; - case T_MOD: - fp.fp_flags |= PF_OSFP_PSIZE_MOD; - } - fp.fp_psize = psize; - - - switch (wscale_mod) { - case T_DC: - fp.fp_flags |= PF_OSFP_WSCALE_DC; - break; - case T_MOD: - fp.fp_flags |= PF_OSFP_WSCALE_MOD; - } - fp.fp_wscale = wscale; - - switch (mss_mod) { - case T_DC: - fp.fp_flags |= PF_OSFP_MSS_DC; - break; - case T_MOD: - fp.fp_flags |= PF_OSFP_MSS_MOD; - break; - } - fp.fp_mss = mss; - - fp.fp_tcpopts = packed_tcpopts; - fp.fp_optcnt = optcnt; - if (ts0) - fp.fp_flags |= PF_OSFP_TS0; - - if (class[0] == '@') - fp.fp_os.fp_enflags |= PF_OSFP_GENERIC; - if (class[0] == '*') - fp.fp_os.fp_enflags |= PF_OSFP_NODETAIL; - - if (class[0] == '@' || class[0] == '*') - strlcpy(fp.fp_os.fp_class_nm, class + 1, - sizeof(fp.fp_os.fp_class_nm)); - else - strlcpy(fp.fp_os.fp_class_nm, class, - sizeof(fp.fp_os.fp_class_nm)); - strlcpy(fp.fp_os.fp_version_nm, version, - sizeof(fp.fp_os.fp_version_nm)); - strlcpy(fp.fp_os.fp_subtype_nm, subtype, - sizeof(fp.fp_os.fp_subtype_nm)); - - add_fingerprint(dev, opts, &fp); - - fp.fp_flags |= (PF_OSFP_DF | PF_OSFP_INET6); - fp.fp_psize += sizeof(struct ip6_hdr) - sizeof(struct ip); - add_fingerprint(dev, opts, &fp); - } - - if (class) - free(class); - if (version) - free(version); - if (subtype) - free(subtype); - if (desc) - free(desc); - if (tcpopts) - free(tcpopts); - - fclose(in); - - if (opts & PF_OPT_VERBOSE2) - printf("Loaded %d passive OS fingerprints\n", - fingerprint_count); - return (0); -} - -/* flush the kernel's fingerprints */ -void -pfctl_clear_fingerprints(int dev, int opts) -{ - if (ioctl(dev, DIOCOSFPFLUSH)) - err(1, "DIOCOSFPFLUSH"); -} - -/* flush pfctl's view of the fingerprints */ -void -pfctl_flush_my_fingerprints(struct name_list *list) -{ - struct name_entry *nm; - - while ((nm = LIST_FIRST(list)) != NULL) { - LIST_REMOVE(nm, nm_entry); - pfctl_flush_my_fingerprints(&nm->nm_sublist); - free(nm); - } - fingerprint_count = 0; - class_count = 0; -} - -/* Fetch the active fingerprints from the kernel */ -int -pfctl_load_fingerprints(int dev, int opts) -{ - struct pf_osfp_ioctl io; - int i; - - pfctl_flush_my_fingerprints(&classes); - - for (i = 0; i >= 0; i++) { - memset(&io, 0, sizeof(io)); - io.fp_getnum = i; - if (ioctl(dev, DIOCOSFPGET, &io)) { - if (errno == EBUSY) - break; - warn("DIOCOSFPGET"); - return (1); - } - import_fingerprint(&io); - } - return (0); -} - -/* List the fingerprints */ -void -pfctl_show_fingerprints(int opts) -{ - if (LIST_FIRST(&classes) != NULL) { - if (opts & PF_OPT_SHOWALL) { - pfctl_print_title("OS FINGERPRINTS:"); - printf("%u fingerprints loaded\n", fingerprint_count); - } else { - printf("Class\tVersion\tSubtype(subversion)\n"); - printf("-----\t-------\t-------------------\n"); - sort_name_list(opts, &classes); - print_name_list(opts, &classes, ""); - } - } -} - -/* Lookup a fingerprint */ -pf_osfp_t -pfctl_get_fingerprint(const char *name) -{ - struct name_entry *nm, *class_nm, *version_nm, *subtype_nm; - pf_osfp_t ret = PF_OSFP_NOMATCH; - int class, version, subtype; - int unp_class, unp_version, unp_subtype; - int wr_len, version_len, subtype_len; - char *ptr, *wr_name; - - if (strcasecmp(name, "unknown") == 0) - return (PF_OSFP_UNKNOWN); - - /* Try most likely no version and no subtype */ - if ((nm = lookup_name_list(&classes, name))) { - class = nm->nm_num; - version = PF_OSFP_ANY; - subtype = PF_OSFP_ANY; - goto found; - } else { - - /* Chop it up into class/version/subtype */ - - if ((wr_name = strdup(name)) == NULL) - err(1, "malloc"); - if ((ptr = strchr(wr_name, ' ')) == NULL) { - free(wr_name); - return (PF_OSFP_NOMATCH); - } - *ptr++ = '\0'; - - /* The class is easy to find since it is delimited by a space */ - if ((class_nm = lookup_name_list(&classes, wr_name)) == NULL) { - free(wr_name); - return (PF_OSFP_NOMATCH); - } - class = class_nm->nm_num; - - /* Try no subtype */ - if ((version_nm = lookup_name_list(&class_nm->nm_sublist, ptr))) - { - version = version_nm->nm_num; - subtype = PF_OSFP_ANY; - free(wr_name); - goto found; - } - - - /* - * There must be a version and a subtype. - * We'll do some fuzzy matching to pick up things like: - * Linux 2.2.14 (version=2.2 subtype=14) - * FreeBSD 4.0-STABLE (version=4.0 subtype=STABLE) - * Windows 2000 SP2 (version=2000 subtype=SP2) - */ -#define CONNECTOR(x) ((x) == '.' || (x) == ' ' || (x) == '\t' || (x) == '-') - wr_len = strlen(ptr); - LIST_FOREACH(version_nm, &class_nm->nm_sublist, nm_entry) { - version_len = strlen(version_nm->nm_name); - if (wr_len < version_len + 2 || - !CONNECTOR(ptr[version_len])) - continue; - /* first part of the string must be version */ - if (strncasecmp(ptr, version_nm->nm_name, - version_len)) - continue; - - LIST_FOREACH(subtype_nm, &version_nm->nm_sublist, - nm_entry) { - subtype_len = strlen(subtype_nm->nm_name); - if (wr_len != version_len + subtype_len + 1) - continue; - - /* last part of the string must be subtype */ - if (strcasecmp(&ptr[version_len+1], - subtype_nm->nm_name) != 0) - continue; - - /* Found it!! */ - version = version_nm->nm_num; - subtype = subtype_nm->nm_num; - free(wr_name); - goto found; - } - } - - free(wr_name); - return (PF_OSFP_NOMATCH); - } - -found: - PF_OSFP_PACK(ret, class, version, subtype); - if (ret != PF_OSFP_NOMATCH) { - PF_OSFP_UNPACK(ret, unp_class, unp_version, unp_subtype); - if (class != unp_class) { - fprintf(stderr, "warning: fingerprint table overflowed " - "classes\n"); - return (PF_OSFP_NOMATCH); - } - if (version != unp_version) { - fprintf(stderr, "warning: fingerprint table overflowed " - "versions\n"); - return (PF_OSFP_NOMATCH); - } - if (subtype != unp_subtype) { - fprintf(stderr, "warning: fingerprint table overflowed " - "subtypes\n"); - return (PF_OSFP_NOMATCH); - } - } - if (ret == PF_OSFP_ANY) { - /* should never happen */ - fprintf(stderr, "warning: fingerprint packed to 'any'\n"); - return (PF_OSFP_NOMATCH); - } - - return (ret); -} - -/* Lookup a fingerprint name by ID */ -char * -pfctl_lookup_fingerprint(pf_osfp_t fp, char *buf, size_t len) -{ - int class, version, subtype; - struct name_list *list; - struct name_entry *nm; - - char *class_name, *version_name, *subtype_name; - class_name = version_name = subtype_name = NULL; - - if (fp == PF_OSFP_UNKNOWN) { - strlcpy(buf, "unknown", len); - return (buf); - } - if (fp == PF_OSFP_ANY) { - strlcpy(buf, "any", len); - return (buf); - } - - PF_OSFP_UNPACK(fp, class, version, subtype); - if (class >= (1 << _FP_CLASS_BITS) || - version >= (1 << _FP_VERSION_BITS) || - subtype >= (1 << _FP_SUBTYPE_BITS)) { - warnx("PF_OSFP_UNPACK(0x%x) failed!!", fp); - strlcpy(buf, "nomatch", len); - return (buf); - } - - LIST_FOREACH(nm, &classes, nm_entry) { - if (nm->nm_num == class) { - class_name = nm->nm_name; - if (version == PF_OSFP_ANY) - goto found; - list = &nm->nm_sublist; - LIST_FOREACH(nm, list, nm_entry) { - if (nm->nm_num == version) { - version_name = nm->nm_name; - if (subtype == PF_OSFP_ANY) - goto found; - list = &nm->nm_sublist; - LIST_FOREACH(nm, list, nm_entry) { - if (nm->nm_num == subtype) { - subtype_name = - nm->nm_name; - goto found; - } - } /* foreach subtype */ - strlcpy(buf, "nomatch", len); - return (buf); - } - } /* foreach version */ - strlcpy(buf, "nomatch", len); - return (buf); - } - } /* foreach class */ - - strlcpy(buf, "nomatch", len); - return (buf); - -found: - snprintf(buf, len, "%s", class_name); - if (version_name) { - strlcat(buf, " ", len); - strlcat(buf, version_name, len); - if (subtype_name) { - if (strchr(version_name, ' ')) - strlcat(buf, " ", len); - else if (strchr(version_name, '.') && - isdigit(*subtype_name)) - strlcat(buf, ".", len); - else - strlcat(buf, " ", len); - strlcat(buf, subtype_name, len); - } - } - return (buf); -} - -/* lookup a name in a list */ -struct name_entry * -lookup_name_list(struct name_list *list, const char *name) -{ - struct name_entry *nm; - LIST_FOREACH(nm, list, nm_entry) - if (strcasecmp(name, nm->nm_name) == 0) - return (nm); - - return (NULL); -} - - -void -add_fingerprint(int dev, int opts, struct pf_osfp_ioctl *fp) -{ - struct pf_osfp_ioctl fptmp; - struct name_entry *nm_class, *nm_version, *nm_subtype; - int class, version, subtype; - -/* We expand #-# or #.#-#.# version/subtypes into multiple fingerprints */ -#define EXPAND(field) do { \ - int _dot = -1, _start = -1, _end = -1, _i = 0; \ - /* pick major version out of #.# */ \ - if (isdigit(fp->field[_i]) && fp->field[_i+1] == '.') { \ - _dot = fp->field[_i] - '0'; \ - _i += 2; \ - } \ - if (isdigit(fp->field[_i])) \ - _start = fp->field[_i++] - '0'; \ - else \ - break; \ - if (isdigit(fp->field[_i])) \ - _start = (_start * 10) + fp->field[_i++] - '0'; \ - if (fp->field[_i++] != '-') \ - break; \ - if (isdigit(fp->field[_i]) && fp->field[_i+1] == '.' && \ - fp->field[_i] - '0' == _dot) \ - _i += 2; \ - else if (_dot != -1) \ - break; \ - if (isdigit(fp->field[_i])) \ - _end = fp->field[_i++] - '0'; \ - else \ - break; \ - if (isdigit(fp->field[_i])) \ - _end = (_end * 10) + fp->field[_i++] - '0'; \ - if (isdigit(fp->field[_i])) \ - _end = (_end * 10) + fp->field[_i++] - '0'; \ - if (fp->field[_i] != '\0') \ - break; \ - memcpy(&fptmp, fp, sizeof(fptmp)); \ - for (;_start <= _end; _start++) { \ - memset(fptmp.field, 0, sizeof(fptmp.field)); \ - fptmp.fp_os.fp_enflags |= PF_OSFP_EXPANDED; \ - if (_dot == -1) \ - snprintf(fptmp.field, sizeof(fptmp.field), \ - "%d", _start); \ - else \ - snprintf(fptmp.field, sizeof(fptmp.field), \ - "%d.%d", _dot, _start); \ - add_fingerprint(dev, opts, &fptmp); \ - } \ -} while(0) - - /* We allow "#-#" as a version or subtype and we'll expand it */ - EXPAND(fp_os.fp_version_nm); - EXPAND(fp_os.fp_subtype_nm); - - if (strcasecmp(fp->fp_os.fp_class_nm, "nomatch") == 0) - errx(1, "fingerprint class \"nomatch\" is reserved"); - - version = PF_OSFP_ANY; - subtype = PF_OSFP_ANY; - - nm_class = fingerprint_name_entry(&classes, fp->fp_os.fp_class_nm); - if (nm_class->nm_num == 0) - nm_class->nm_num = ++class_count; - class = nm_class->nm_num; - - nm_version = fingerprint_name_entry(&nm_class->nm_sublist, - fp->fp_os.fp_version_nm); - if (nm_version) { - if (nm_version->nm_num == 0) - nm_version->nm_num = ++nm_class->nm_sublist_num; - version = nm_version->nm_num; - nm_subtype = fingerprint_name_entry(&nm_version->nm_sublist, - fp->fp_os.fp_subtype_nm); - if (nm_subtype) { - if (nm_subtype->nm_num == 0) - nm_subtype->nm_num = - ++nm_version->nm_sublist_num; - subtype = nm_subtype->nm_num; - } - } - - - DEBUG(fp, "\tsignature %d:%d:%d %s", class, version, subtype, - print_ioctl(fp)); - - PF_OSFP_PACK(fp->fp_os.fp_os, class, version, subtype); - fingerprint_count++; - -#ifdef FAKE_PF_KERNEL - /* Linked to the sys/net/pf_osfp.c. Call pf_osfp_add() */ - if ((errno = pf_osfp_add(fp))) -#else - if ((opts & PF_OPT_NOACTION) == 0 && ioctl(dev, DIOCOSFPADD, fp)) -#endif /* FAKE_PF_KERNEL */ - { - if (errno == EEXIST) { - warn("Duplicate signature for %s %s %s", - fp->fp_os.fp_class_nm, - fp->fp_os.fp_version_nm, - fp->fp_os.fp_subtype_nm); - - } else { - err(1, "DIOCOSFPADD"); - } - } -} - -/* import a fingerprint from the kernel */ -void -import_fingerprint(struct pf_osfp_ioctl *fp) -{ - struct name_entry *nm_class, *nm_version, *nm_subtype; - int class, version, subtype; - - PF_OSFP_UNPACK(fp->fp_os.fp_os, class, version, subtype); - - nm_class = fingerprint_name_entry(&classes, fp->fp_os.fp_class_nm); - if (nm_class->nm_num == 0) { - nm_class->nm_num = class; - class_count = MAX(class_count, class); - } - - nm_version = fingerprint_name_entry(&nm_class->nm_sublist, - fp->fp_os.fp_version_nm); - if (nm_version) { - if (nm_version->nm_num == 0) { - nm_version->nm_num = version; - nm_class->nm_sublist_num = MAX(nm_class->nm_sublist_num, - version); - } - nm_subtype = fingerprint_name_entry(&nm_version->nm_sublist, - fp->fp_os.fp_subtype_nm); - if (nm_subtype) { - if (nm_subtype->nm_num == 0) { - nm_subtype->nm_num = subtype; - nm_version->nm_sublist_num = - MAX(nm_version->nm_sublist_num, subtype); - } - } - } - - - fingerprint_count++; - DEBUG(fp, "import signature %d:%d:%d", class, version, subtype); -} - -/* Find an entry for a fingerprints class/version/subtype */ -struct name_entry * -fingerprint_name_entry(struct name_list *list, char *name) -{ - struct name_entry *nm_entry; - - if (name == NULL || strlen(name) == 0) - return (NULL); - - LIST_FOREACH(nm_entry, list, nm_entry) { - if (strcasecmp(nm_entry->nm_name, name) == 0) { - /* We'll move this to the front of the list later */ - LIST_REMOVE(nm_entry, nm_entry); - break; - } - } - if (nm_entry == NULL) { - nm_entry = calloc(1, sizeof(*nm_entry)); - if (nm_entry == NULL) - err(1, "calloc"); - LIST_INIT(&nm_entry->nm_sublist); - strlcpy(nm_entry->nm_name, name, sizeof(nm_entry->nm_name)); - } - LIST_INSERT_HEAD(list, nm_entry, nm_entry); - return (nm_entry); -} - - -void -print_name_list(int opts, struct name_list *nml, const char *prefix) -{ - char newprefix[32]; - struct name_entry *nm; - - LIST_FOREACH(nm, nml, nm_entry) { - snprintf(newprefix, sizeof(newprefix), "%s%s\t", prefix, - nm->nm_name); - printf("%s\n", newprefix); - print_name_list(opts, &nm->nm_sublist, newprefix); - } -} - -void -sort_name_list(int opts, struct name_list *nml) -{ - struct name_list new; - struct name_entry *nm, *nmsearch, *nmlast; - - /* yes yes, it's a very slow sort. so sue me */ - - LIST_INIT(&new); - - while ((nm = LIST_FIRST(nml)) != NULL) { - LIST_REMOVE(nm, nm_entry); - nmlast = NULL; - LIST_FOREACH(nmsearch, &new, nm_entry) { - if (strcasecmp(nmsearch->nm_name, nm->nm_name) > 0) { - LIST_INSERT_BEFORE(nmsearch, nm, nm_entry); - break; - } - nmlast = nmsearch; - } - if (nmsearch == NULL) { - if (nmlast) - LIST_INSERT_AFTER(nmlast, nm, nm_entry); - else - LIST_INSERT_HEAD(&new, nm, nm_entry); - } - - sort_name_list(opts, &nm->nm_sublist); - } - nmlast = NULL; - while ((nm = LIST_FIRST(&new)) != NULL) { - LIST_REMOVE(nm, nm_entry); - if (nmlast == NULL) - LIST_INSERT_HEAD(nml, nm, nm_entry); - else - LIST_INSERT_AFTER(nmlast, nm, nm_entry); - nmlast = nm; - } -} - -/* parse the next integer in a formatted config file line */ -int -get_int(char **line, size_t *len, int *var, int *mod, - const char *name, int flags, int max, const char *filename, int lineno) -{ - int fieldlen, i; - char *field; - long val = 0; - - if (mod) - *mod = 0; - *var = 0; - - field = get_field(line, len, &fieldlen); - if (field == NULL) - return (1); - if (fieldlen == 0) { - fprintf(stderr, "%s:%d empty %s\n", filename, lineno, name); - return (1); - } - - i = 0; - if ((*field == '%' || *field == 'S' || *field == 'T' || *field == '*') - && fieldlen >= 1) { - switch (*field) { - case 'S': - if (mod && (flags & T_MSS)) - *mod = T_MSS; - if (fieldlen == 1) - return (0); - break; - case 'T': - if (mod && (flags & T_MTU)) - *mod = T_MTU; - if (fieldlen == 1) - return (0); - break; - case '*': - if (fieldlen != 1) { - fprintf(stderr, "%s:%d long '%c' %s\n", - filename, lineno, *field, name); - return (1); - } - if (mod && (flags & T_DC)) { - *mod = T_DC; - return (0); - } - case '%': - if (mod && (flags & T_MOD)) - *mod = T_MOD; - if (fieldlen == 1) { - fprintf(stderr, "%s:%d modulus %s must have a " - "value\n", filename, lineno, name); - return (1); - } - break; - } - if (mod == NULL || *mod == 0) { - fprintf(stderr, "%s:%d does not allow %c' %s\n", - filename, lineno, *field, name); - return (1); - } - i++; - } - - for (; i < fieldlen; i++) { - if (field[i] < '0' || field[i] > '9') { - fprintf(stderr, "%s:%d non-digit character in %s\n", - filename, lineno, name); - return (1); - } - val = val * 10 + field[i] - '0'; - if (val < 0) { - fprintf(stderr, "%s:%d %s overflowed\n", filename, - lineno, name); - return (1); - } - } - - if (val > max) { - fprintf(stderr, "%s:%d %s value %ld > %d\n", filename, lineno, - name, val, max); - return (1); - } - *var = (int)val; - - return (0); -} - -/* parse the next string in a formatted config file line */ -int -get_str(char **line, size_t *len, char **v, const char *name, int minlen, - const char *filename, int lineno) -{ - int fieldlen; - char *ptr; - - ptr = get_field(line, len, &fieldlen); - if (ptr == NULL) - return (1); - if (fieldlen < minlen) { - fprintf(stderr, "%s:%d too short %s\n", filename, lineno, name); - return (1); - } - if ((*v = malloc(fieldlen + 1)) == NULL) { - perror("malloc()"); - return (1); - } - memcpy(*v, ptr, fieldlen); - (*v)[fieldlen] = '\0'; - - return (0); -} - -/* Parse out the TCP opts */ -int -get_tcpopts(const char *filename, int lineno, const char *tcpopts, - pf_tcpopts_t *packed, int *optcnt, int *mss, int *mss_mod, int *wscale, - int *wscale_mod, int *ts0) -{ - int i, opt; - - *packed = 0; - *optcnt = 0; - *wscale = 0; - *wscale_mod = T_DC; - *mss = 0; - *mss_mod = T_DC; - *ts0 = 0; - if (strcmp(tcpopts, ".") == 0) - return (0); - - for (i = 0; tcpopts[i] && *optcnt < PF_OSFP_MAX_OPTS;) { - switch ((opt = toupper(tcpopts[i++]))) { - case 'N': /* FALLTHROUGH */ - case 'S': - *packed = (*packed << PF_OSFP_TCPOPT_BITS) | - (opt == 'N' ? PF_OSFP_TCPOPT_NOP : - PF_OSFP_TCPOPT_SACK); - break; - case 'W': /* FALLTHROUGH */ - case 'M': { - int *this_mod, *this; - - if (opt == 'W') { - this = wscale; - this_mod = wscale_mod; - } else { - this = mss; - this_mod = mss_mod; - } - *this = 0; - *this_mod = 0; - - *packed = (*packed << PF_OSFP_TCPOPT_BITS) | - (opt == 'W' ? PF_OSFP_TCPOPT_WSCALE : - PF_OSFP_TCPOPT_MSS); - if (tcpopts[i] == '*' && (tcpopts[i + 1] == '\0' || - tcpopts[i + 1] == ',')) { - *this_mod = T_DC; - i++; - break; - } - - if (tcpopts[i] == '%') { - *this_mod = T_MOD; - i++; - } - do { - if (!isdigit(tcpopts[i])) { - fprintf(stderr, "%s:%d unknown " - "character '%c' in %c TCP opt\n", - filename, lineno, tcpopts[i], opt); - return (1); - } - *this = (*this * 10) + tcpopts[i++] - '0'; - } while(tcpopts[i] != ',' && tcpopts[i] != '\0'); - break; - } - case 'T': - if (tcpopts[i] == '0') { - *ts0 = 1; - i++; - } - *packed = (*packed << PF_OSFP_TCPOPT_BITS) | - PF_OSFP_TCPOPT_TS; - break; - } - (*optcnt) ++; - if (tcpopts[i] == '\0') - break; - if (tcpopts[i] != ',') { - fprintf(stderr, "%s:%d unknown option to %c TCP opt\n", - filename, lineno, opt); - return (1); - } - i++; - } - - return (0); -} - -/* rip the next field ouf of a formatted config file line */ -char * -get_field(char **line, size_t *len, int *fieldlen) -{ - char *ret, *ptr = *line; - size_t plen = *len; - - - while (plen && isspace(*ptr)) { - plen--; - ptr++; - } - ret = ptr; - *fieldlen = 0; - - for (; plen > 0 && *ptr != ':'; plen--, ptr++) - (*fieldlen)++; - if (plen) { - *line = ptr + 1; - *len = plen - 1; - } else { - *len = 0; - } - while (*fieldlen && isspace(ret[*fieldlen - 1])) - (*fieldlen)--; - return (ret); -} - - -const char * -print_ioctl(struct pf_osfp_ioctl *fp) -{ - static char buf[1024]; - char tmp[32]; - int i, opt; - - *buf = '\0'; - if (fp->fp_flags & PF_OSFP_WSIZE_DC) - strlcat(buf, "*", sizeof(buf)); - else if (fp->fp_flags & PF_OSFP_WSIZE_MSS) - strlcat(buf, "S", sizeof(buf)); - else if (fp->fp_flags & PF_OSFP_WSIZE_MTU) - strlcat(buf, "T", sizeof(buf)); - else { - if (fp->fp_flags & PF_OSFP_WSIZE_MOD) - strlcat(buf, "%", sizeof(buf)); - snprintf(tmp, sizeof(tmp), "%d", fp->fp_wsize); - strlcat(buf, tmp, sizeof(buf)); - } - strlcat(buf, ":", sizeof(buf)); - - snprintf(tmp, sizeof(tmp), "%d", fp->fp_ttl); - strlcat(buf, tmp, sizeof(buf)); - strlcat(buf, ":", sizeof(buf)); - - if (fp->fp_flags & PF_OSFP_DF) - strlcat(buf, "1", sizeof(buf)); - else - strlcat(buf, "0", sizeof(buf)); - strlcat(buf, ":", sizeof(buf)); - - if (fp->fp_flags & PF_OSFP_PSIZE_DC) - strlcat(buf, "*", sizeof(buf)); - else { - if (fp->fp_flags & PF_OSFP_PSIZE_MOD) - strlcat(buf, "%", sizeof(buf)); - snprintf(tmp, sizeof(tmp), "%d", fp->fp_psize); - strlcat(buf, tmp, sizeof(buf)); - } - strlcat(buf, ":", sizeof(buf)); - - if (fp->fp_optcnt == 0) - strlcat(buf, ".", sizeof(buf)); - for (i = fp->fp_optcnt - 1; i >= 0; i--) { - opt = fp->fp_tcpopts >> (i * PF_OSFP_TCPOPT_BITS); - opt &= (1 << PF_OSFP_TCPOPT_BITS) - 1; - switch (opt) { - case PF_OSFP_TCPOPT_NOP: - strlcat(buf, "N", sizeof(buf)); - break; - case PF_OSFP_TCPOPT_SACK: - strlcat(buf, "S", sizeof(buf)); - break; - case PF_OSFP_TCPOPT_TS: - strlcat(buf, "T", sizeof(buf)); - if (fp->fp_flags & PF_OSFP_TS0) - strlcat(buf, "0", sizeof(buf)); - break; - case PF_OSFP_TCPOPT_MSS: - strlcat(buf, "M", sizeof(buf)); - if (fp->fp_flags & PF_OSFP_MSS_DC) - strlcat(buf, "*", sizeof(buf)); - else { - if (fp->fp_flags & PF_OSFP_MSS_MOD) - strlcat(buf, "%", sizeof(buf)); - snprintf(tmp, sizeof(tmp), "%d", fp->fp_mss); - strlcat(buf, tmp, sizeof(buf)); - } - break; - case PF_OSFP_TCPOPT_WSCALE: - strlcat(buf, "W", sizeof(buf)); - if (fp->fp_flags & PF_OSFP_WSCALE_DC) - strlcat(buf, "*", sizeof(buf)); - else { - if (fp->fp_flags & PF_OSFP_WSCALE_MOD) - strlcat(buf, "%", sizeof(buf)); - snprintf(tmp, sizeof(tmp), "%d", fp->fp_wscale); - strlcat(buf, tmp, sizeof(buf)); - } - break; - } - - if (i != 0) - strlcat(buf, ",", sizeof(buf)); - } - strlcat(buf, ":", sizeof(buf)); - - strlcat(buf, fp->fp_os.fp_class_nm, sizeof(buf)); - strlcat(buf, ":", sizeof(buf)); - strlcat(buf, fp->fp_os.fp_version_nm, sizeof(buf)); - strlcat(buf, ":", sizeof(buf)); - strlcat(buf, fp->fp_os.fp_subtype_nm, sizeof(buf)); - strlcat(buf, ":", sizeof(buf)); - - snprintf(tmp, sizeof(tmp), "TcpOpts %d 0x%llx", fp->fp_optcnt, - (long long int)fp->fp_tcpopts); - strlcat(buf, tmp, sizeof(buf)); - - return (buf); -} diff --git a/contrib/pf/pfctl/pfctl_parser.c b/contrib/pf/pfctl/pfctl_parser.c deleted file mode 100644 index f248995..0000000 --- a/contrib/pf/pfctl/pfctl_parser.c +++ /dev/null @@ -1,1746 +0,0 @@ -/* $OpenBSD: pfctl_parser.c,v 1.240 2008/06/10 20:55:02 mcbride Exp $ */ - -/* - * 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: - * - * - 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/cdefs.h> -__FBSDID("$FreeBSD$"); - -#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> -#include <netinet/ip.h> -#include <netinet/ip_icmp.h> -#include <netinet/icmp6.h> -#include <net/pfvar.h> -#include <arpa/inet.h> - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <ctype.h> -#include <netdb.h> -#include <stdarg.h> -#include <errno.h> -#include <err.h> -#include <ifaddrs.h> -#include <unistd.h> - -#include "pfctl_parser.h" -#include "pfctl.h" - -void print_op (u_int8_t, const char *, const char *); -void print_port (u_int8_t, u_int16_t, u_int16_t, const char *, int); -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); -int ifa_skip_if(const char *filter, struct node_host *p); - -struct node_host *ifa_grouplookup(const char *, int); -struct node_host *host_if(const char *, int); -struct node_host *host_v4(const char *, int); -struct node_host *host_v6(const char *, int); -struct node_host *host_dns(const char *, int, int); - -const char *tcpflags = "FSRPAUEW"; - -static const struct icmptypeent icmp_type[] = { - { "echoreq", ICMP_ECHO }, - { "echorep", ICMP_ECHOREPLY }, - { "unreach", ICMP_UNREACH }, - { "squench", ICMP_SOURCEQUENCH }, - { "redir", ICMP_REDIRECT }, - { "althost", ICMP_ALTHOSTADDR }, - { "routeradv", ICMP_ROUTERADVERT }, - { "routersol", ICMP_ROUTERSOLICIT }, - { "timex", ICMP_TIMXCEED }, - { "paramprob", ICMP_PARAMPROB }, - { "timereq", ICMP_TSTAMP }, - { "timerep", ICMP_TSTAMPREPLY }, - { "inforeq", ICMP_IREQ }, - { "inforep", ICMP_IREQREPLY }, - { "maskreq", ICMP_MASKREQ }, - { "maskrep", ICMP_MASKREPLY }, - { "trace", ICMP_TRACEROUTE }, - { "dataconv", ICMP_DATACONVERR }, - { "mobredir", ICMP_MOBILE_REDIRECT }, - { "ipv6-where", ICMP_IPV6_WHEREAREYOU }, - { "ipv6-here", ICMP_IPV6_IAMHERE }, - { "mobregreq", ICMP_MOBILE_REGREQUEST }, - { "mobregrep", ICMP_MOBILE_REGREPLY }, - { "skip", ICMP_SKIP }, - { "photuris", ICMP_PHOTURIS } -}; - -static 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 } -}; - -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 } -}; - -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 } -}; - -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 } -}; - -const struct icmptypeent * -geticmptypebynumber(u_int8_t type, sa_family_t af) -{ - unsigned int i; - - 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]); - } - } else { - for (i=0; i < (sizeof (icmp6_type) / - sizeof(icmp6_type[0])); i++) { - if (type == icmp6_type[i].type) - return (&icmp6_type[i]); - } - } - return (NULL); -} - -const struct icmptypeent * -geticmptypebyname(char *w, sa_family_t af) -{ - unsigned int i; - - 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]); - } - } 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); -} - -const struct icmpcodeent * -geticmpcodebynumber(u_int8_t type, u_int8_t code, sa_family_t af) -{ - unsigned int i; - - 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 (NULL); -} - -const struct icmpcodeent * -geticmpcodebyname(u_long type, char *w, sa_family_t af) -{ - unsigned int i; - - 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 (NULL); -} - -void -print_op(u_int8_t op, const char *a1, const char *a2) -{ - if (op == PF_OP_IRG) - printf(" %s >< %s", a1, a2); - else if (op == PF_OP_XRG) - printf(" %s <> %s", a1, a2); - else if (op == PF_OP_EQ) - printf(" = %s", a1); - else if (op == PF_OP_NE) - printf(" != %s", a1); - else if (op == PF_OP_LT) - printf(" < %s", a1); - else if (op == PF_OP_LE) - printf(" <= %s", a1); - else if (op == PF_OP_GT) - printf(" > %s", a1); - else if (op == PF_OP_GE) - printf(" >= %s", a1); - else if (op == PF_OP_RRG) - printf(" %s:%s", a1, a2); -} - -void -print_port(u_int8_t op, u_int16_t p1, u_int16_t p2, const char *proto, int numeric) -{ - char a1[6], a2[6]; - struct servent *s; - - if (!numeric) - s = getservbyport(p1, proto); - else - s = NULL; - 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); -} - -void -print_ugid(u_int8_t op, unsigned u1, unsigned u2, const char *t, unsigned umax) -{ - char a1[11], a2[11]; - - 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); -} - -void -print_flags(u_int8_t f) -{ - int i; - - for (i = 0; tcpflags[i]; ++i) - if (f & (1 << i)) - printf("%c", tcpflags[i]); -} - -void -print_fromto(struct pf_rule_addr *src, pf_osfp_t osfp, struct pf_rule_addr *dst, - sa_family_t af, u_int8_t proto, int verbose, int numeric) -{ - 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", - numeric); - 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", - numeric); - } -} - -void -print_pool(struct pf_pool *pool, u_int16_t p1, u_int16_t p2, - sa_family_t af, int id) -{ - struct pf_pooladdr *pooladdr; - - if ((TAILQ_FIRST(&pool->list) != NULL) && - TAILQ_NEXT(TAILQ_FIRST(&pool->list), entries) != NULL) - printf("{ "); - TAILQ_FOREACH(pooladdr, &pool->list, entries){ - switch (id) { - case PF_NAT: - case PF_RDR: - case PF_BINAT: - print_addr(&pooladdr->addr, af, 0); - break; - case PF_PASS: - if (PF_AZERO(&pooladdr->addr.v.a.addr, af)) - printf("%s", pooladdr->ifname); - else { - printf("(%s ", pooladdr->ifname); - print_addr(&pooladdr->addr, af, 0); - printf(")"); - } - break; - default: - break; - } - if (TAILQ_NEXT(pooladdr, entries) != NULL) - printf(", "); - else if (TAILQ_NEXT(TAILQ_FIRST(&pool->list), entries) != NULL) - printf(" }"); - } - 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 -print_status(struct pf_status *s, int opts) -{ - char statline[80], *running; - time_t runtime; - int i; - char buf[PF_MD5_DIGEST_LENGTH * 2 + 1]; - static const char hex[] = "0123456789abcdef"; - - runtime = time(NULL) - s->since; - running = s->running ? "Enabled" : "Disabled"; - - if (s->since) { - unsigned int 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; - } - - if (opts & PF_OPT_VERBOSE) { - printf("Hostid: 0x%08x\n", ntohl(s->hostid)); - - for (i = 0; i < PF_MD5_DIGEST_LENGTH; i++) { - buf[i + i] = hex[s->pf_chksum[i] >> 4]; - buf[i + i + 1] = hex[s->pf_chksum[i] & 0x0f]; - } - buf[i + i] = '\0'; - printf("Checksum: 0x%s\n\n", buf); - } - - 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], -#ifdef __FreeBSD__ - (long long)s->scounters[i]); -#else - s->scounters[i]); -#endif - 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], -#ifdef __FreeBSD__ - (unsigned long long)s->lcounters[i]); -#else - s->lcounters[i]); -#endif - if (runtime > 0) - printf("%14.1f/s\n", - (double)s->lcounters[i] / (double)runtime); - else - printf("%14s\n", ""); - } - } -} - -void -print_src_node(struct pf_src_node *sn, int opts) -{ - struct pf_addr_wrap aw; - int min, sec; - - 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(", %llu pkts, %llu bytes", -#ifdef __FreeBSD__ - (unsigned long long)(sn->packets[0] + sn->packets[1]), - (unsigned long long)(sn->bytes[0] + sn->bytes[1])); -#else - sn->packets[0] + sn->packets[1], - sn->bytes[0] + sn->bytes[1]); -#endif - switch (sn->ruletype) { - case PF_NAT: - if (sn->rule.nr != -1) - printf(", nat rule %u", sn->rule.nr); - 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 -print_rule(struct pf_rule *r, const char *anchor_call, int verbose, int numeric) -{ - 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]) { - if (anchor_call[0] == '_') { - printf("%s", anchortypes[r->action]); - } else - printf("%s \"%s\"", anchortypes[r->action], - anchor_call); - } else { - printf("%s", actiontypes[r->action]); - if (r->natpass) - printf(" pass"); - } - if (r->action == PF_DROP) { - if (r->rule_flag & PFRULE_RETURN) - printf(" return"); - else if (r->rule_flag & PFRULE_RETURNRST) { - if (!r->return_ttl) - printf(" return-rst"); - else - printf(" return-rst(ttl %d)", r->return_ttl); - } else if (r->rule_flag & PFRULE_RETURNICMP) { - const struct icmpcodeent *ic, *ic6; - - ic = geticmpcodebynumber(r->return_icmp >> 8, - r->return_icmp & 255, AF_INET); - ic6 = geticmpcodebynumber(r->return_icmp6 >> 8, - r->return_icmp6 & 255, AF_INET6); - - switch (r->af) { - case AF_INET: - printf(" return-icmp"); - if (ic == NULL) - printf("(%u)", r->return_icmp & 255); - else - printf("(%s)", ic->name); - break; - case AF_INET6: - printf(" return-icmp6"); - if (ic6 == NULL) - printf("(%u)", r->return_icmp6 & 255); - else - printf("(%s)", ic6->name); - break; - default: - printf(" return-icmp"); - if (ic == NULL) - printf("(%u, ", r->return_icmp & 255); - else - printf("(%s, ", ic->name); - if (ic6 == NULL) - printf("%u)", r->return_icmp6 & 255); - else - printf("%s)", ic6->name); - break; - } - } else - printf(" drop"); - } - if (r->direction == PF_IN) - printf(" in"); - else if (r->direction == PF_OUT) - printf(" out"); - if (r->log) { - printf(" log"); - if (r->log & ~PF_LOG || r->logif) { - int count = 0; - - printf(" ("); - if (r->log & PF_LOG_ALL) - printf("%sall", count++ ? ", " : ""); - if (r->log & PF_LOG_SOCKET_LOOKUP) - printf("%suser", count++ ? ", " : ""); - if (r->logif) - printf("%sto pflog%u", count++ ? ", " : "", - r->logif); - printf(")"); - } - } - if (r->quick) - printf(" quick"); - if (r->ifname[0]) { - if (r->ifnot) - printf(" on ! %s", r->ifname); - else - printf(" on %s", r->ifname); - } - if (r->rt) { - if (r->rt == PF_ROUTETO) - printf(" route-to"); - else if (r->rt == PF_REPLYTO) - printf(" reply-to"); - else if (r->rt == PF_DUPTO) - printf(" dup-to"); - else if (r->rt == PF_FASTROUTE) - printf(" fastroute"); - if (r->rt != PF_FASTROUTE) { - printf(" "); - print_pool(&r->rpool, 0, 0, r->af, PF_PASS); - } - } - if (r->af) { - if (r->af == AF_INET) - printf(" inet"); - else - printf(" inet6"); - } - if (r->proto) { - struct protoent *p; - - 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, numeric); - 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); - } else if (r->action == PF_PASS && - (!r->proto || r->proto == IPPROTO_TCP) && - !(r->rule_flag & PFRULE_FRAGMENT) && - !anchor_call[0] && r->keep_state) - printf(" flags any"); - if (r->type) { - const struct icmptypeent *it; - - it = geticmptypebynumber(r->type-1, r->af); - if (r->af != AF_INET6) - printf(" icmp-type"); - else - printf(" icmp6-type"); - if (it != NULL) - printf(" %s", it->name); - else - printf(" %u", r->type-1); - if (r->code) { - const struct icmpcodeent *ic; - - 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 && r->action == PF_PASS && !anchor_call[0]) - printf(" no state"); - else 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; - } - } - printf(" probability %s%%", buf); - } - opts = 0; - if (r->max_states || r->max_src_nodes || r->max_src_states) - opts = 1; - if (r->rule_flag & PFRULE_NOSYNC) - opts = 1; - if (r->rule_flag & PFRULE_SRCTRACK) - opts = 1; - if (r->rule_flag & PFRULE_IFBOUND) - opts = 1; - if (r->rule_flag & PFRULE_STATESLOPPY) - 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->flush) - printf(" flush"); - if (r->flush & PF_FLUSH_GLOBAL) - printf(" global"); - } - if (r->rule_flag & PFRULE_IFBOUND) { - if (!opts) - printf(", "); - printf("if-bound"); - opts = 0; - } - if (r->rule_flag & PFRULE_STATESLOPPY) { - if (!opts) - printf(", "); - printf("sloppy"); - opts = 0; - } - for (i = 0; i < PFTM_MAX; ++i) - if (r->timeout[i]) { - int j; - - if (!opts) - printf(", "); - opts = 0; - for (j = 0; pf_timeouts[j].name != NULL; - ++j) - if (pf_timeouts[j].timeout == i) - break; - printf("%s %u", pf_timeouts[j].name == NULL ? - "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->rule_flag & PFRULE_SET_TOS) - printf(" set-tos 0x%2.2x", r->set_tos); - 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 (r->rtableid != -1) - printf(" rtable %u", r->rtableid); - if (r->divert.port) { -#ifdef __FreeBSD__ - printf(" divert-to %u", ntohs(r->divert.port)); -#else - if (PF_AZERO(&r->divert.addr, r->af)) { - printf(" divert-reply"); - } else { - /* XXX cut&paste from print_addr */ - char buf[48]; - - printf(" divert-to "); - if (inet_ntop(r->af, &r->divert.addr, buf, - sizeof(buf)) == NULL) - printf("?"); - else - printf("%s", buf); - printf(" port %u", ntohs(r->divert.port)); - } -#endif - } - 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); - } -} - -void -print_tabledef(const char *name, int flags, int addrs, - struct node_tinithead *nodes) -{ - struct node_tinit *ti, *nti; - struct node_host *h; - - printf("table <%s>", name); - if (flags & PFR_TFLAG_CONST) - printf(" const"); - if (flags & PFR_TFLAG_PERSIST) - printf(" persist"); - if (flags & PFR_TFLAG_COUNTERS) - printf(" counters"); - SIMPLEQ_FOREACH(ti, nodes, entries) { - if (ti->file) { - printf(" file \"%s\"", ti->file); - continue; - } - printf(" {"); - for (;;) { - for (h = ti->host; h != NULL; h = h->next) { - printf(h->not ? " !" : " "); - print_addr(&h->addr, h->af, 0); - } - nti = SIMPLEQ_NEXT(ti, entries); - if (nti != NULL && nti->file == NULL) - ti = nti; /* merge lists */ - else - break; - } - printf(" }"); - } - if (addrs && SIMPLEQ_EMPTY(nodes)) - printf(" { }"); - printf("\n"); -} - -int -parse_flags(char *s) -{ - char *p, *q; - u_int8_t f = 0; - - for (p = s; *p; p++) { - if ((q = strchr(tcpflags, *p)) == NULL) - return -1; - else - f |= 1 << (q - tcpflags); - } - return (f ? f : PF_TH_ALL); -} - -void -set_ipmask(struct node_host *h, u_int8_t b) -{ - struct pf_addr *m, *n; - int i, j = 0; - - m = &h->addr.v.a.mask; - memset(m, 0, sizeof(*m)); - - while (b >= 32) { - m->addr32[j++] = 0xffffffff; - b -= 32; - } - 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 -check_netmask(struct node_host *h, sa_family_t af) -{ - struct node_host *n = NULL; - struct pf_addr *m; - - for (n = h; n != NULL; n = n->next) { - if (h->addr.type == PF_ADDR_TABLE) - continue; - m = &h->addr.v.a.mask; - /* fix up netmask for dynaddr */ - if (af == AF_INET && h->addr.type == PF_ADDR_DYNIFTL && - unmask(m, AF_INET6) > 32) - set_ipmask(n, 32); - /* netmasks > 32 bit are invalid on v4 */ - if (af == AF_INET && - (m->addr32[1] || m->addr32[2] || m->addr32[3])) { - fprintf(stderr, "netmask %u invalid for IPv4 address\n", - unmask(m, AF_INET6)); - return (1); - } - } - return (0); -} - -/* interface lookup routines */ - -struct node_host *iftab; - -void -ifa_load(void) -{ - struct ifaddrs *ifap, *ifa; - struct node_host *n = NULL, *h = NULL; - - if (getifaddrs(&ifap) < 0) - err(1, "getifaddrs"); - - for (ifa = ifap; ifa; ifa = ifa->ifa_next) { - if (!(ifa->ifa_addr->sa_family == AF_INET || - ifa->ifa_addr->sa_family == AF_INET6 || - ifa->ifa_addr->sa_family == AF_LINK)) - continue; - n = calloc(1, sizeof(struct node_host)); - if (n == NULL) - err(1, "address: calloc"); - n->af = ifa->ifa_addr->sa_family; - n->ifa_flags = ifa->ifa_flags; -#ifdef __KAME__ - if (n->af == AF_INET6 && - IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *) - ifa->ifa_addr)->sin6_addr) && - ((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_scope_id == - 0) { - struct sockaddr_in6 *sin6; - - sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; - sin6->sin6_scope_id = sin6->sin6_addr.s6_addr[2] << 8 | - sin6->sin6_addr.s6_addr[3]; - sin6->sin6_addr.s6_addr[2] = 0; - sin6->sin6_addr.s6_addr[3] = 0; - } -#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; - } - } - - iftab = h; - freeifaddrs(ifap); -} - -struct node_host * -ifa_exists(const char *ifa_name) -{ - struct node_host *n; - struct ifgroupreq ifgr; - int s; - - if (iftab == NULL) - ifa_load(); - - /* check wether this is a group */ - if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) - err(1, "socket"); - bzero(&ifgr, sizeof(ifgr)); - strlcpy(ifgr.ifgr_name, ifa_name, sizeof(ifgr.ifgr_name)); - if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) == 0) { - /* fake a node_host */ - if ((n = calloc(1, sizeof(*n))) == NULL) - err(1, "calloc"); - if ((n->ifname = strdup(ifa_name)) == NULL) - err(1, "strdup"); - close(s); - return (n); - } - close(s); - - for (n = iftab; n; n = n->next) { - if (n->af == AF_LINK && !strncmp(n->ifname, ifa_name, IFNAMSIZ)) - return (n); - } - - return (NULL); -} - -struct node_host * -ifa_grouplookup(const char *ifa_name, int flags) -{ - struct ifg_req *ifg; - struct ifgroupreq ifgr; - int s, len; - struct node_host *n, *h = NULL; - - if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) - err(1, "socket"); - bzero(&ifgr, sizeof(ifgr)); - strlcpy(ifgr.ifgr_name, ifa_name, sizeof(ifgr.ifgr_name)); - if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) == -1) { - close(s); - return (NULL); - } - - len = ifgr.ifgr_len; - if ((ifgr.ifgr_groups = calloc(1, len)) == NULL) - err(1, "calloc"); - if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) == -1) - err(1, "SIOCGIFGMEMB"); - - for (ifg = ifgr.ifgr_groups; ifg && len >= sizeof(struct ifg_req); - ifg++) { - len -= sizeof(struct ifg_req); - if ((n = ifa_lookup(ifg->ifgrq_member, flags)) == NULL) - continue; - if (h == NULL) - h = n; - else { - h->tail->next = n; - h->tail = n->tail; - } - } - free(ifgr.ifgr_groups); - close(s); - - return (h); -} - -struct node_host * -ifa_lookup(const char *ifa_name, int flags) -{ - struct node_host *p = NULL, *h = NULL, *n = NULL; - int got4 = 0, got6 = 0; - const char *last_if = NULL; - - if ((h = ifa_grouplookup(ifa_name, flags)) != NULL) - return (h); - - if (!strncmp(ifa_name, "self", IFNAMSIZ)) - ifa_name = NULL; - - 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 { - 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); - } - n->ifindex = p->ifindex; - - n->next = NULL; - n->tail = n; - if (h == NULL) - h = n; - else { - h->tail->next = n; - h->tail = n; - } - } - return (h); -} - -int -ifa_skip_if(const char *filter, struct node_host *p) -{ - int n; - - 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'); -} - - -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; - } - - /* interface with this name exists? */ - if (cont && (h = host_if(ps, mask)) != NULL) - cont = 0; - - /* IPv4 address? */ - if (cont && (h = host_v4(s, mask)) != NULL) - cont = 0; - - /* IPv6 address? */ - if (cont && (h = host_v6(ps, v6mask)) != NULL) - cont = 0; - - /* 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); -} - -struct node_host * -host_if(const char *s, int mask) -{ - struct node_host *n, *h = NULL; - char *p, *ps; - int flags = 0; - - if ((ps = strdup(s)) == NULL) - err(1, "host_if: strdup"); - while ((p = strrchr(ps, ':')) != NULL) { - if (!strcmp(p+1, "network")) - flags |= PFI_AFLAG_NETWORK; - else if (!strcmp(p+1, "broadcast")) - flags |= PFI_AFLAG_BROADCAST; - else if (!strcmp(p+1, "peer")) - flags |= PFI_AFLAG_PEER; - else if (!strcmp(p+1, "0")) - flags |= PFI_AFLAG_NOALIAS; - else { - free(ps); - return (NULL); - } - *p = '\0'; - } - if (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) || !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); - } - - free(ps); - return (h); -} - -struct node_host * -host_v4(const char *s, int mask) -{ - struct node_host *h = NULL; - struct in_addr ina; - int bits = 32; - - memset(&ina, 0, sizeof(struct in_addr)); - if (strrchr(s, '/') != NULL) { - if ((bits = inet_net_pton(AF_INET, s, &ina, sizeof(ina))) == -1) - return (NULL); - } else { - if (inet_pton(AF_INET, s, &ina) != 1) - return (NULL); - } - - h = calloc(1, sizeof(struct node_host)); - if (h == NULL) - err(1, "address: calloc"); - h->ifname = NULL; - h->af = AF_INET; - h->addr.v.a.addr.addr32[0] = ina.s_addr; - set_ipmask(h, bits); - h->next = NULL; - h->tail = h; - - return (h); -} - -struct node_host * -host_v6(const char *s, int mask) -{ - struct addrinfo hints, *res; - struct node_host *h = NULL; - - 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); -} - -struct node_host * -host_dns(const char *s, int v4mask, int v6mask) -{ - 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); - } - - 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; - } - } - 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 -append_addr(struct pfr_buffer *b, char *s, int test) -{ - 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); - } - 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 -append_addr_host(struct pfr_buffer *b, struct node_host *n, int test, int not) -{ - 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); - } - if ((test && (not || addr.pfra_net != bits)) || - addr.pfra_net > bits) { - errno = EINVAL; - return (-1); - } - if (pfr_buf_add(b, &addr)) - return (-1); - } while ((n = n->next) != NULL); - - return (0); -} - -int -pfctl_add_trans(struct pfr_buffer *buf, int rs_num, const char *anchor) -{ - 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_int32_t -pfctl_get_ticket(struct pfr_buffer *buf, int rs_num, const char *anchor) -{ - struct pfioc_trans_e *p; - - 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_trans(int dev, struct pfr_buffer *buf, u_long cmd, int from) -{ - struct pfioc_trans trans; - - 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); -} diff --git a/contrib/pf/pfctl/pfctl_parser.h b/contrib/pf/pfctl/pfctl_parser.h deleted file mode 100644 index 4560d66..0000000 --- a/contrib/pf/pfctl/pfctl_parser.h +++ /dev/null @@ -1,305 +0,0 @@ -/* $OpenBSD: pfctl_parser.h,v 1.86 2006/10/31 23:46:25 mcbride Exp $ */ - -/* - * Copyright (c) 2001 Daniel Hartmeier - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - 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. - * - * $FreeBSD$ - */ - -#ifndef _PFCTL_PARSER_H_ -#define _PFCTL_PARSER_H_ - -#define PF_OSFP_FILE "/etc/pf.os" - -#define PF_OPT_DISABLE 0x0001 -#define PF_OPT_ENABLE 0x0002 -#define PF_OPT_VERBOSE 0x0004 -#define PF_OPT_NOACTION 0x0008 -#define PF_OPT_QUIET 0x0010 -#define PF_OPT_CLRRULECTRS 0x0020 -#define PF_OPT_USEDNS 0x0040 -#define PF_OPT_VERBOSE2 0x0080 -#define PF_OPT_DUMMYACTION 0x0100 -#define PF_OPT_DEBUG 0x0200 -#define PF_OPT_SHOWALL 0x0400 -#define PF_OPT_OPTIMIZE 0x0800 -#define PF_OPT_NUMERIC 0x1000 -#define PF_OPT_MERGE 0x2000 -#define PF_OPT_RECURSE 0x4000 - -#define PF_TH_ALL 0xFF - -#define PF_NAT_PROXY_PORT_LOW 50001 -#define PF_NAT_PROXY_PORT_HIGH 65535 - -#define PF_OPTIMIZE_BASIC 0x0001 -#define PF_OPTIMIZE_PROFILE 0x0002 - -#define FCNT_NAMES { \ - "searches", \ - "inserts", \ - "removals", \ - NULL \ -} - -struct pfr_buffer; /* forward definition */ - - -struct pfctl { - int dev; - int opts; - int optimize; - int loadopt; - int asd; /* anchor stack depth */ - int bn; /* brace number */ - int brace; - int tdirty; /* kernel dirty */ -#define PFCTL_ANCHOR_STACK_DEPTH 64 - struct pf_anchor *astack[PFCTL_ANCHOR_STACK_DEPTH]; - struct pfioc_pooladdr paddr; - struct pfioc_altq *paltq; - struct pfioc_queue *pqueue; - struct pfr_buffer *trans; - struct pf_anchor *anchor, *alast; - const char *ruleset; - - /* 'set foo' options */ - u_int32_t timeout[PFTM_MAX]; - u_int32_t limit[PF_LIMIT_MAX]; - u_int32_t debug; - u_int32_t hostid; - char *ifname; - - u_int8_t timeout_set[PFTM_MAX]; - u_int8_t limit_set[PF_LIMIT_MAX]; - u_int8_t debug_set; - u_int8_t hostid_set; - u_int8_t ifname_set; -}; - -struct node_if { - char ifname[IFNAMSIZ]; - u_int8_t not; - u_int8_t dynamic; /* antispoof */ - u_int ifa_flags; - struct node_if *next; - struct node_if *tail; -}; - -struct node_host { - struct pf_addr_wrap addr; - struct pf_addr bcast; - struct pf_addr peer; - sa_family_t af; - u_int8_t not; - u_int32_t ifindex; /* link-local IPv6 addrs */ - char *ifname; - u_int ifa_flags; - struct node_host *next; - struct node_host *tail; -}; - -struct node_os { - char *os; - pf_osfp_t fingerprint; - struct node_os *next; - struct node_os *tail; -}; - -struct node_queue_bw { - u_int32_t bw_absolute; - u_int16_t bw_percent; -}; - -struct node_hfsc_sc { - struct node_queue_bw m1; /* slope of 1st segment; bps */ - u_int d; /* x-projection of m1; msec */ - struct node_queue_bw m2; /* slope of 2nd segment; bps */ - u_int8_t used; -}; - -struct node_hfsc_opts { - struct node_hfsc_sc realtime; - struct node_hfsc_sc linkshare; - struct node_hfsc_sc upperlimit; - int flags; -}; - -struct node_queue_opt { - int qtype; - union { - struct cbq_opts cbq_opts; - struct priq_opts priq_opts; - struct node_hfsc_opts hfsc_opts; - } data; -}; - -#ifdef __FreeBSD__ -/* - * XXX - * Absolutely this is not correct location to define this. - * Should we use an another sperate header file? - */ -#define SIMPLEQ_HEAD STAILQ_HEAD -#define SIMPLEQ_HEAD_INITIALIZER STAILQ_HEAD_INITIALIZER -#define SIMPLEQ_ENTRY STAILQ_ENTRY -#define SIMPLEQ_FIRST STAILQ_FIRST -#define SIMPLEQ_END(head) NULL -#define SIMPLEQ_EMPTY STAILQ_EMPTY -#define SIMPLEQ_NEXT STAILQ_NEXT -/*#define SIMPLEQ_FOREACH STAILQ_FOREACH*/ -#define SIMPLEQ_FOREACH(var, head, field) \ - for((var) = SIMPLEQ_FIRST(head); \ - (var) != SIMPLEQ_END(head); \ - (var) = SIMPLEQ_NEXT(var, field)) -#define SIMPLEQ_INIT STAILQ_INIT -#define SIMPLEQ_INSERT_HEAD STAILQ_INSERT_HEAD -#define SIMPLEQ_INSERT_TAIL STAILQ_INSERT_TAIL -#define SIMPLEQ_INSERT_AFTER STAILQ_INSERT_AFTER -#define SIMPLEQ_REMOVE_HEAD STAILQ_REMOVE_HEAD -#endif -SIMPLEQ_HEAD(node_tinithead, node_tinit); -struct node_tinit { /* table initializer */ - SIMPLEQ_ENTRY(node_tinit) entries; - struct node_host *host; - char *file; -}; - - -/* optimizer created tables */ -struct pf_opt_tbl { - char pt_name[PF_TABLE_NAME_SIZE]; - int pt_rulecount; - int pt_generated; - struct node_tinithead pt_nodes; - struct pfr_buffer *pt_buf; -}; -#define PF_OPT_TABLE_PREFIX "__automatic_" - -/* optimizer pf_rule container */ -struct pf_opt_rule { - struct pf_rule por_rule; - struct pf_opt_tbl *por_src_tbl; - struct pf_opt_tbl *por_dst_tbl; - u_int64_t por_profile_count; - TAILQ_ENTRY(pf_opt_rule) por_entry; - TAILQ_ENTRY(pf_opt_rule) por_skip_entry[PF_SKIP_COUNT]; -}; - -TAILQ_HEAD(pf_opt_queue, pf_opt_rule); - -int pfctl_rules(int, char *, int, int, char *, struct pfr_buffer *); -int pfctl_optimize_ruleset(struct pfctl *, struct pf_ruleset *); - -int pfctl_add_rule(struct pfctl *, struct pf_rule *, const char *); -int pfctl_add_altq(struct pfctl *, struct pf_altq *); -int pfctl_add_pool(struct pfctl *, struct pf_pool *, sa_family_t); -void pfctl_move_pool(struct pf_pool *, struct pf_pool *); -void pfctl_clear_pool(struct pf_pool *); - -int pfctl_set_timeout(struct pfctl *, const char *, int, int); -int pfctl_set_optimization(struct pfctl *, const char *); -int pfctl_set_limit(struct pfctl *, const char *, unsigned int); -int pfctl_set_logif(struct pfctl *, char *); -int pfctl_set_hostid(struct pfctl *, u_int32_t); -int pfctl_set_debug(struct pfctl *, char *); -int pfctl_set_interface_flags(struct pfctl *, char *, int, int); - -int parse_config(char *, struct pfctl *); -int parse_flags(char *); -int pfctl_load_anchors(int, struct pfctl *, struct pfr_buffer *); - -void print_pool(struct pf_pool *, u_int16_t, u_int16_t, sa_family_t, int); -void print_src_node(struct pf_src_node *, int); -void print_rule(struct pf_rule *, const char *, int, int); -void print_tabledef(const char *, int, int, struct node_tinithead *); -void print_status(struct pf_status *, int); - -int eval_pfaltq(struct pfctl *, struct pf_altq *, struct node_queue_bw *, - struct node_queue_opt *); -int eval_pfqueue(struct pfctl *, struct pf_altq *, struct node_queue_bw *, - struct node_queue_opt *); - -void print_altq(const struct pf_altq *, unsigned, struct node_queue_bw *, - struct node_queue_opt *); -void print_queue(const struct pf_altq *, unsigned, struct node_queue_bw *, - int, struct node_queue_opt *); - -int pfctl_define_table(char *, int, int, const char *, struct pfr_buffer *, - u_int32_t); - -void pfctl_clear_fingerprints(int, int); -int pfctl_file_fingerprints(int, int, const char *); -pf_osfp_t pfctl_get_fingerprint(const char *); -int pfctl_load_fingerprints(int, int); -char *pfctl_lookup_fingerprint(pf_osfp_t, char *, size_t); -void pfctl_show_fingerprints(int); - - -struct icmptypeent { - const char *name; - u_int8_t type; -}; - -struct icmpcodeent { - const char *name; - u_int8_t type; - u_int8_t code; -}; - -const struct icmptypeent *geticmptypebynumber(u_int8_t, u_int8_t); -const struct icmptypeent *geticmptypebyname(char *, u_int8_t); -const struct icmpcodeent *geticmpcodebynumber(u_int8_t, u_int8_t, u_int8_t); -const struct icmpcodeent *geticmpcodebyname(u_long, char *, u_int8_t); - -struct pf_timeout { - const char *name; - int timeout; -}; - -#define PFCTL_FLAG_FILTER 0x02 -#define PFCTL_FLAG_NAT 0x04 -#define PFCTL_FLAG_OPTION 0x08 -#define PFCTL_FLAG_ALTQ 0x10 -#define PFCTL_FLAG_TABLE 0x20 - -extern const struct pf_timeout pf_timeouts[]; - -void set_ipmask(struct node_host *, u_int8_t); -int check_netmask(struct node_host *, sa_family_t); -int unmask(struct pf_addr *, sa_family_t); -void ifa_load(void); -struct node_host *ifa_exists(const char *); -struct node_host *ifa_lookup(const char *, int); -struct node_host *host(const char *); - -int append_addr(struct pfr_buffer *, char *, int); -int append_addr_host(struct pfr_buffer *, - struct node_host *, int, int); - -#endif /* _PFCTL_PARSER_H_ */ diff --git a/contrib/pf/pfctl/pfctl_qstats.c b/contrib/pf/pfctl/pfctl_qstats.c deleted file mode 100644 index 95371e4..0000000 --- a/contrib/pf/pfctl/pfctl_qstats.c +++ /dev/null @@ -1,449 +0,0 @@ -/* $OpenBSD: pfctl_qstats.c,v 1.30 2004/04/27 21:47:32 kjc Exp $ */ - -/* - * Copyright (c) Henning Brauer <henning@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - -#include <sys/types.h> -#include <sys/ioctl.h> -#include <sys/socket.h> - -#include <net/if.h> -#include <netinet/in.h> -#include <net/pfvar.h> -#include <arpa/inet.h> - -#include <err.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include <altq/altq.h> -#include <altq/altq_cbq.h> -#include <altq/altq_priq.h> -#include <altq/altq_hfsc.h> - -#include "pfctl.h" -#include "pfctl_parser.h" - -union class_stats { - class_stats_t cbq_stats; - struct priq_classstats priq_stats; - struct hfsc_classstats hfsc_stats; -}; - -#define AVGN_MAX 8 -#define STAT_INTERVAL 5 - -struct queue_stats { - union class_stats data; - int avgn; - double avg_bytes; - double avg_packets; - u_int64_t prev_bytes; - u_int64_t prev_packets; -}; - -struct pf_altq_node { - struct pf_altq altq; - struct pf_altq_node *next; - struct pf_altq_node *children; - struct queue_stats qstats; -}; - -int pfctl_update_qstats(int, struct pf_altq_node **); -void pfctl_insert_altq_node(struct pf_altq_node **, - const struct pf_altq, const struct queue_stats); -struct pf_altq_node *pfctl_find_altq_node(struct pf_altq_node *, - const char *, const char *); -void pfctl_print_altq_node(int, const struct pf_altq_node *, - unsigned, int); -void print_cbqstats(struct queue_stats); -void print_priqstats(struct queue_stats); -void print_hfscstats(struct queue_stats); -void pfctl_free_altq_node(struct pf_altq_node *); -void pfctl_print_altq_nodestat(int, - const struct pf_altq_node *); - -void update_avg(struct pf_altq_node *); - -int -pfctl_show_altq(int dev, const char *iface, int opts, int verbose2) -{ - struct pf_altq_node *root = NULL, *node; - int nodes, dotitle = (opts & PF_OPT_SHOWALL); - -#ifdef __FreeBSD__ - if (!altqsupport) - return (-1); -#endif - - if ((nodes = pfctl_update_qstats(dev, &root)) < 0) - return (-1); - - if (nodes == 0) - printf("No queue in use\n"); - for (node = root; node != NULL; node = node->next) { - if (iface != NULL && strcmp(node->altq.ifname, iface)) - continue; - if (dotitle) { - pfctl_print_title("ALTQ:"); - dotitle = 0; - } - pfctl_print_altq_node(dev, node, 0, opts); - } - - while (verbose2 && nodes > 0) { - printf("\n"); - fflush(stdout); - sleep(STAT_INTERVAL); - if ((nodes = pfctl_update_qstats(dev, &root)) == -1) - return (-1); - for (node = root; node != NULL; node = node->next) { - if (iface != NULL && strcmp(node->altq.ifname, iface)) - continue; -#ifdef __FreeBSD__ - if (node->altq.local_flags & PFALTQ_FLAG_IF_REMOVED) - continue; -#endif - pfctl_print_altq_node(dev, node, 0, opts); - } - } - pfctl_free_altq_node(root); - return (0); -} - -int -pfctl_update_qstats(int dev, struct pf_altq_node **root) -{ - struct pf_altq_node *node; - struct pfioc_altq pa; - struct pfioc_qstats pq; - u_int32_t mnr, nr; - struct queue_stats qstats; - static u_int32_t last_ticket; - - memset(&pa, 0, sizeof(pa)); - memset(&pq, 0, sizeof(pq)); - memset(&qstats, 0, sizeof(qstats)); - if (ioctl(dev, DIOCGETALTQS, &pa)) { - warn("DIOCGETALTQS"); - return (-1); - } - - /* if a new set is found, start over */ - if (pa.ticket != last_ticket && *root != NULL) { - pfctl_free_altq_node(*root); - *root = NULL; - } - last_ticket = pa.ticket; - - mnr = pa.nr; - for (nr = 0; nr < mnr; ++nr) { - pa.nr = nr; - if (ioctl(dev, DIOCGETALTQ, &pa)) { - warn("DIOCGETALTQ"); - return (-1); - } -#ifdef __FreeBSD__ - if (pa.altq.qid > 0 && - !(pa.altq.local_flags & PFALTQ_FLAG_IF_REMOVED)) { -#else - if (pa.altq.qid > 0) { -#endif - pq.nr = nr; - pq.ticket = pa.ticket; - pq.buf = &qstats.data; - pq.nbytes = sizeof(qstats.data); - if (ioctl(dev, DIOCGETQSTATS, &pq)) { - warn("DIOCGETQSTATS"); - return (-1); - } - if ((node = pfctl_find_altq_node(*root, pa.altq.qname, - pa.altq.ifname)) != NULL) { - memcpy(&node->qstats.data, &qstats.data, - sizeof(qstats.data)); - update_avg(node); - } else { - pfctl_insert_altq_node(root, pa.altq, qstats); - } - } -#ifdef __FreeBSD__ - else if (pa.altq.local_flags & PFALTQ_FLAG_IF_REMOVED) { - memset(&qstats.data, 0, sizeof(qstats.data)); - if ((node = pfctl_find_altq_node(*root, pa.altq.qname, - pa.altq.ifname)) != NULL) { - memcpy(&node->qstats.data, &qstats.data, - sizeof(qstats.data)); - update_avg(node); - } else { - pfctl_insert_altq_node(root, pa.altq, qstats); - } - } -#endif - } - return (mnr); -} - -void -pfctl_insert_altq_node(struct pf_altq_node **root, - const struct pf_altq altq, const struct queue_stats qstats) -{ - struct pf_altq_node *node; - - node = calloc(1, sizeof(struct pf_altq_node)); - if (node == NULL) - err(1, "pfctl_insert_altq_node: calloc"); - memcpy(&node->altq, &altq, sizeof(struct pf_altq)); - memcpy(&node->qstats, &qstats, sizeof(qstats)); - node->next = node->children = NULL; - - if (*root == NULL) - *root = node; - else if (!altq.parent[0]) { - struct pf_altq_node *prev = *root; - - while (prev->next != NULL) - prev = prev->next; - prev->next = node; - } else { - struct pf_altq_node *parent; - - parent = pfctl_find_altq_node(*root, altq.parent, altq.ifname); - if (parent == NULL) - errx(1, "parent %s not found", altq.parent); - if (parent->children == NULL) - parent->children = node; - else { - struct pf_altq_node *prev = parent->children; - - while (prev->next != NULL) - prev = prev->next; - prev->next = node; - } - } - update_avg(node); -} - -struct pf_altq_node * -pfctl_find_altq_node(struct pf_altq_node *root, const char *qname, - const char *ifname) -{ - struct pf_altq_node *node, *child; - - for (node = root; node != NULL; node = node->next) { - if (!strcmp(node->altq.qname, qname) - && !(strcmp(node->altq.ifname, ifname))) - return (node); - if (node->children != NULL) { - child = pfctl_find_altq_node(node->children, qname, - ifname); - if (child != NULL) - return (child); - } - } - return (NULL); -} - -void -pfctl_print_altq_node(int dev, const struct pf_altq_node *node, - unsigned int level, int opts) -{ - const struct pf_altq_node *child; - - if (node == NULL) - return; - - print_altq(&node->altq, level, NULL, NULL); - - if (node->children != NULL) { - printf("{"); - for (child = node->children; child != NULL; - child = child->next) { - printf("%s", child->altq.qname); - if (child->next != NULL) - printf(", "); - } - printf("}"); - } - printf("\n"); - - if (opts & PF_OPT_VERBOSE) - pfctl_print_altq_nodestat(dev, node); - - if (opts & PF_OPT_DEBUG) - printf(" [ qid=%u ifname=%s ifbandwidth=%s ]\n", - node->altq.qid, node->altq.ifname, - rate2str((double)(node->altq.ifbandwidth))); - - for (child = node->children; child != NULL; - child = child->next) - pfctl_print_altq_node(dev, child, level + 1, opts); -} - -void -pfctl_print_altq_nodestat(int dev, const struct pf_altq_node *a) -{ - if (a->altq.qid == 0) - return; - -#ifdef __FreeBSD__ - if (a->altq.local_flags & PFALTQ_FLAG_IF_REMOVED) - return; -#endif - switch (a->altq.scheduler) { - case ALTQT_CBQ: - print_cbqstats(a->qstats); - break; - case ALTQT_PRIQ: - print_priqstats(a->qstats); - break; - case ALTQT_HFSC: - print_hfscstats(a->qstats); - break; - } -} - -void -print_cbqstats(struct queue_stats cur) -{ - printf(" [ pkts: %10llu bytes: %10llu " - "dropped pkts: %6llu bytes: %6llu ]\n", - (unsigned long long)cur.data.cbq_stats.xmit_cnt.packets, - (unsigned long long)cur.data.cbq_stats.xmit_cnt.bytes, - (unsigned long long)cur.data.cbq_stats.drop_cnt.packets, - (unsigned long long)cur.data.cbq_stats.drop_cnt.bytes); - printf(" [ qlength: %3d/%3d borrows: %6u suspends: %6u ]\n", - cur.data.cbq_stats.qcnt, cur.data.cbq_stats.qmax, - cur.data.cbq_stats.borrows, cur.data.cbq_stats.delays); - - if (cur.avgn < 2) - return; - - printf(" [ measured: %7.1f packets/s, %s/s ]\n", - cur.avg_packets / STAT_INTERVAL, - rate2str((8 * cur.avg_bytes) / STAT_INTERVAL)); -} - -void -print_priqstats(struct queue_stats cur) -{ - printf(" [ pkts: %10llu bytes: %10llu " - "dropped pkts: %6llu bytes: %6llu ]\n", - (unsigned long long)cur.data.priq_stats.xmitcnt.packets, - (unsigned long long)cur.data.priq_stats.xmitcnt.bytes, - (unsigned long long)cur.data.priq_stats.dropcnt.packets, - (unsigned long long)cur.data.priq_stats.dropcnt.bytes); - printf(" [ qlength: %3d/%3d ]\n", - cur.data.priq_stats.qlength, cur.data.priq_stats.qlimit); - - if (cur.avgn < 2) - return; - - printf(" [ measured: %7.1f packets/s, %s/s ]\n", - cur.avg_packets / STAT_INTERVAL, - rate2str((8 * cur.avg_bytes) / STAT_INTERVAL)); -} - -void -print_hfscstats(struct queue_stats cur) -{ - printf(" [ pkts: %10llu bytes: %10llu " - "dropped pkts: %6llu bytes: %6llu ]\n", - (unsigned long long)cur.data.hfsc_stats.xmit_cnt.packets, - (unsigned long long)cur.data.hfsc_stats.xmit_cnt.bytes, - (unsigned long long)cur.data.hfsc_stats.drop_cnt.packets, - (unsigned long long)cur.data.hfsc_stats.drop_cnt.bytes); - printf(" [ qlength: %3d/%3d ]\n", - cur.data.hfsc_stats.qlength, cur.data.hfsc_stats.qlimit); - - if (cur.avgn < 2) - return; - - printf(" [ measured: %7.1f packets/s, %s/s ]\n", - cur.avg_packets / STAT_INTERVAL, - rate2str((8 * cur.avg_bytes) / STAT_INTERVAL)); -} - -void -pfctl_free_altq_node(struct pf_altq_node *node) -{ - while (node != NULL) { - struct pf_altq_node *prev; - - if (node->children != NULL) - pfctl_free_altq_node(node->children); - prev = node; - node = node->next; - free(prev); - } -} - -void -update_avg(struct pf_altq_node *a) -{ - struct queue_stats *qs; - u_int64_t b, p; - int n; - - if (a->altq.qid == 0) - return; - - qs = &a->qstats; - n = qs->avgn; - - switch (a->altq.scheduler) { - case ALTQT_CBQ: - b = qs->data.cbq_stats.xmit_cnt.bytes; - p = qs->data.cbq_stats.xmit_cnt.packets; - break; - case ALTQT_PRIQ: - b = qs->data.priq_stats.xmitcnt.bytes; - p = qs->data.priq_stats.xmitcnt.packets; - break; - case ALTQT_HFSC: - b = qs->data.hfsc_stats.xmit_cnt.bytes; - p = qs->data.hfsc_stats.xmit_cnt.packets; - break; - default: - b = 0; - p = 0; - break; - } - - if (n == 0) { - qs->prev_bytes = b; - qs->prev_packets = p; - qs->avgn++; - return; - } - - if (b >= qs->prev_bytes) - qs->avg_bytes = ((qs->avg_bytes * (n - 1)) + - (b - qs->prev_bytes)) / n; - - if (p >= qs->prev_packets) - qs->avg_packets = ((qs->avg_packets * (n - 1)) + - (p - qs->prev_packets)) / n; - - qs->prev_bytes = b; - qs->prev_packets = p; - if (n < AVGN_MAX) - qs->avgn++; -} diff --git a/contrib/pf/pfctl/pfctl_radix.c b/contrib/pf/pfctl/pfctl_radix.c deleted file mode 100644 index 38f16c4..0000000 --- a/contrib/pf/pfctl/pfctl_radix.c +++ /dev/null @@ -1,585 +0,0 @@ -/* $OpenBSD: pfctl_radix.c,v 1.27 2005/05/21 21:03:58 henning Exp $ */ - -/* - * Copyright (c) 2002 Cedric Berger - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - 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/cdefs.h> -__FBSDID("$FreeBSD$"); - -#include <sys/types.h> -#include <sys/ioctl.h> -#include <sys/socket.h> - -#include <net/if.h> -#include <net/pfvar.h> - -#include <errno.h> -#include <string.h> -#include <ctype.h> -#include <stdio.h> -#include <stdlib.h> -#include <limits.h> -#include <err.h> - -#include "pfctl.h" - -#define BUF_SIZE 256 - -extern int dev; - -static int pfr_next_token(char buf[], FILE *); - - -int -pfr_clr_tables(struct pfr_table *filter, int *ndel, int flags) -{ - struct pfioc_table io; - - bzero(&io, sizeof io); - io.pfrio_flags = flags; - if (filter != NULL) - io.pfrio_table = *filter; - if (ioctl(dev, DIOCRCLRTABLES, &io)) - return (-1); - if (ndel != NULL) - *ndel = io.pfrio_ndel; - return (0); -} - -int -pfr_add_tables(struct pfr_table *tbl, int size, int *nadd, int flags) -{ - struct pfioc_table io; - - if (size < 0 || (size && tbl == NULL)) { - errno = EINVAL; - return (-1); - } - bzero(&io, sizeof io); - io.pfrio_flags = flags; - io.pfrio_buffer = tbl; - io.pfrio_esize = sizeof(*tbl); - io.pfrio_size = size; - if (ioctl(dev, DIOCRADDTABLES, &io)) - return (-1); - if (nadd != NULL) - *nadd = io.pfrio_nadd; - return (0); -} - -int -pfr_del_tables(struct pfr_table *tbl, int size, int *ndel, int flags) -{ - struct pfioc_table io; - - if (size < 0 || (size && tbl == NULL)) { - errno = EINVAL; - return (-1); - } - bzero(&io, sizeof io); - io.pfrio_flags = flags; - io.pfrio_buffer = tbl; - io.pfrio_esize = sizeof(*tbl); - io.pfrio_size = size; - if (ioctl(dev, DIOCRDELTABLES, &io)) - return (-1); - if (ndel != NULL) - *ndel = io.pfrio_ndel; - return (0); -} - -int -pfr_get_tables(struct pfr_table *filter, struct pfr_table *tbl, int *size, - int flags) -{ - struct pfioc_table io; - - if (size == NULL || *size < 0 || (*size && tbl == NULL)) { - errno = EINVAL; - return (-1); - } - bzero(&io, sizeof io); - io.pfrio_flags = flags; - if (filter != NULL) - io.pfrio_table = *filter; - io.pfrio_buffer = tbl; - io.pfrio_esize = sizeof(*tbl); - io.pfrio_size = *size; - if (ioctl(dev, DIOCRGETTABLES, &io)) - return (-1); - *size = io.pfrio_size; - return (0); -} - -int -pfr_get_tstats(struct pfr_table *filter, struct pfr_tstats *tbl, int *size, - int flags) -{ - struct pfioc_table io; - - if (size == NULL || *size < 0 || (*size && tbl == NULL)) { - errno = EINVAL; - return (-1); - } - bzero(&io, sizeof io); - io.pfrio_flags = flags; - if (filter != NULL) - io.pfrio_table = *filter; - io.pfrio_buffer = tbl; - io.pfrio_esize = sizeof(*tbl); - io.pfrio_size = *size; - if (ioctl(dev, DIOCRGETTSTATS, &io)) - return (-1); - *size = io.pfrio_size; - return (0); -} - -int -pfr_clr_addrs(struct pfr_table *tbl, int *ndel, int flags) -{ - struct pfioc_table io; - - if (tbl == NULL) { - errno = EINVAL; - return (-1); - } - bzero(&io, sizeof io); - io.pfrio_flags = flags; - io.pfrio_table = *tbl; - if (ioctl(dev, DIOCRCLRADDRS, &io)) - return (-1); - if (ndel != NULL) - *ndel = io.pfrio_ndel; - return (0); -} - -int -pfr_add_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, - int *nadd, int flags) -{ - struct pfioc_table io; - - if (tbl == NULL || size < 0 || (size && addr == NULL)) { - errno = EINVAL; - return (-1); - } - bzero(&io, sizeof io); - io.pfrio_flags = flags; - io.pfrio_table = *tbl; - io.pfrio_buffer = addr; - io.pfrio_esize = sizeof(*addr); - io.pfrio_size = size; - if (ioctl(dev, DIOCRADDADDRS, &io)) - return (-1); - if (nadd != NULL) - *nadd = io.pfrio_nadd; - return (0); -} - -int -pfr_del_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, - int *ndel, int flags) -{ - struct pfioc_table io; - - if (tbl == NULL || size < 0 || (size && addr == NULL)) { - errno = EINVAL; - return (-1); - } - bzero(&io, sizeof io); - io.pfrio_flags = flags; - io.pfrio_table = *tbl; - io.pfrio_buffer = addr; - io.pfrio_esize = sizeof(*addr); - io.pfrio_size = size; - if (ioctl(dev, DIOCRDELADDRS, &io)) - return (-1); - if (ndel != NULL) - *ndel = io.pfrio_ndel; - return (0); -} - -int -pfr_set_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, - int *size2, int *nadd, int *ndel, int *nchange, int flags) -{ - struct pfioc_table io; - - if (tbl == NULL || size < 0 || (size && addr == NULL)) { - errno = EINVAL; - return (-1); - } - bzero(&io, sizeof io); - io.pfrio_flags = flags; - io.pfrio_table = *tbl; - io.pfrio_buffer = addr; - io.pfrio_esize = sizeof(*addr); - io.pfrio_size = size; - io.pfrio_size2 = (size2 != NULL) ? *size2 : 0; - if (ioctl(dev, DIOCRSETADDRS, &io)) - return (-1); - if (nadd != NULL) - *nadd = io.pfrio_nadd; - if (ndel != NULL) - *ndel = io.pfrio_ndel; - if (nchange != NULL) - *nchange = io.pfrio_nchange; - if (size2 != NULL) - *size2 = io.pfrio_size2; - return (0); -} - -int -pfr_get_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int *size, - int flags) -{ - struct pfioc_table io; - - if (tbl == NULL || size == NULL || *size < 0 || - (*size && addr == NULL)) { - errno = EINVAL; - return (-1); - } - bzero(&io, sizeof io); - io.pfrio_flags = flags; - io.pfrio_table = *tbl; - io.pfrio_buffer = addr; - io.pfrio_esize = sizeof(*addr); - io.pfrio_size = *size; - if (ioctl(dev, DIOCRGETADDRS, &io)) - return (-1); - *size = io.pfrio_size; - return (0); -} - -int -pfr_get_astats(struct pfr_table *tbl, struct pfr_astats *addr, int *size, - int flags) -{ - struct pfioc_table io; - - if (tbl == NULL || size == NULL || *size < 0 || - (*size && addr == NULL)) { - errno = EINVAL; - return (-1); - } - bzero(&io, sizeof io); - io.pfrio_flags = flags; - io.pfrio_table = *tbl; - io.pfrio_buffer = addr; - io.pfrio_esize = sizeof(*addr); - io.pfrio_size = *size; - if (ioctl(dev, DIOCRGETASTATS, &io)) - return (-1); - *size = io.pfrio_size; - return (0); -} - -int -pfr_clr_tstats(struct pfr_table *tbl, int size, int *nzero, int flags) -{ - struct pfioc_table io; - - if (size < 0 || (size && !tbl)) { - errno = EINVAL; - return (-1); - } - bzero(&io, sizeof io); - io.pfrio_flags = flags; - io.pfrio_buffer = tbl; - io.pfrio_esize = sizeof(*tbl); - io.pfrio_size = size; - if (ioctl(dev, DIOCRCLRTSTATS, &io)) - return (-1); - if (nzero) - *nzero = io.pfrio_nzero; - return (0); -} - -int -pfr_tst_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, - int *nmatch, int flags) -{ - struct pfioc_table io; - - if (tbl == NULL || size < 0 || (size && addr == NULL)) { - errno = EINVAL; - return (-1); - } - bzero(&io, sizeof io); - io.pfrio_flags = flags; - io.pfrio_table = *tbl; - io.pfrio_buffer = addr; - io.pfrio_esize = sizeof(*addr); - io.pfrio_size = size; - if (ioctl(dev, DIOCRTSTADDRS, &io)) - return (-1); - if (nmatch) - *nmatch = io.pfrio_nmatch; - return (0); -} - -int -pfr_ina_define(struct pfr_table *tbl, struct pfr_addr *addr, int size, - int *nadd, int *naddr, int ticket, int flags) -{ - struct pfioc_table io; - - if (tbl == NULL || size < 0 || (size && addr == NULL)) { - errno = EINVAL; - return (-1); - } - bzero(&io, sizeof io); - io.pfrio_flags = flags; - io.pfrio_table = *tbl; - io.pfrio_buffer = addr; - io.pfrio_esize = sizeof(*addr); - io.pfrio_size = size; - io.pfrio_ticket = ticket; - if (ioctl(dev, DIOCRINADEFINE, &io)) - return (-1); - if (nadd != NULL) - *nadd = io.pfrio_nadd; - if (naddr != NULL) - *naddr = io.pfrio_naddr; - return (0); -} - -/* interface management code */ - -int -pfi_get_ifaces(const char *filter, struct pfi_kif *buf, int *size) -{ - struct pfioc_iface io; - - if (size == NULL || *size < 0 || (*size && buf == NULL)) { - errno = EINVAL; - return (-1); - } - bzero(&io, sizeof io); - if (filter != NULL) - if (strlcpy(io.pfiio_name, filter, sizeof(io.pfiio_name)) >= - sizeof(io.pfiio_name)) { - errno = EINVAL; - return (-1); - } - io.pfiio_buffer = buf; - io.pfiio_esize = sizeof(*buf); - io.pfiio_size = *size; - if (ioctl(dev, DIOCIGETIFACES, &io)) - return (-1); - *size = io.pfiio_size; - return (0); -} - -/* buffer management code */ - -size_t buf_esize[PFRB_MAX] = { 0, - sizeof(struct pfr_table), sizeof(struct pfr_tstats), - sizeof(struct pfr_addr), sizeof(struct pfr_astats), - sizeof(struct pfi_kif), sizeof(struct pfioc_trans_e) -}; - -/* - * add one element to the buffer - */ -int -pfr_buf_add(struct pfr_buffer *b, const void *e) -{ - size_t bs; - - if (b == NULL || b->pfrb_type <= 0 || b->pfrb_type >= PFRB_MAX || - e == NULL) { - errno = EINVAL; - return (-1); - } - bs = buf_esize[b->pfrb_type]; - if (b->pfrb_size == b->pfrb_msize) - if (pfr_buf_grow(b, 0)) - return (-1); - memcpy(((caddr_t)b->pfrb_caddr) + bs * b->pfrb_size, e, bs); - b->pfrb_size++; - return (0); -} - -/* - * return next element of the buffer (or first one if prev is NULL) - * see PFRB_FOREACH macro - */ -void * -pfr_buf_next(struct pfr_buffer *b, const void *prev) -{ - size_t bs; - - if (b == NULL || b->pfrb_type <= 0 || b->pfrb_type >= PFRB_MAX) - return (NULL); - if (b->pfrb_size == 0) - return (NULL); - if (prev == NULL) - return (b->pfrb_caddr); - bs = buf_esize[b->pfrb_type]; - if ((((caddr_t)prev)-((caddr_t)b->pfrb_caddr)) / bs >= b->pfrb_size-1) - return (NULL); - return (((caddr_t)prev) + bs); -} - -/* - * minsize: - * 0: make the buffer somewhat bigger - * n: make room for "n" entries in the buffer - */ -int -pfr_buf_grow(struct pfr_buffer *b, int minsize) -{ - caddr_t p; - size_t bs; - - if (b == NULL || b->pfrb_type <= 0 || b->pfrb_type >= PFRB_MAX) { - errno = EINVAL; - return (-1); - } - if (minsize != 0 && minsize <= b->pfrb_msize) - return (0); - bs = buf_esize[b->pfrb_type]; - if (!b->pfrb_msize) { - if (minsize < 64) - minsize = 64; - b->pfrb_caddr = calloc(bs, minsize); - if (b->pfrb_caddr == NULL) - return (-1); - b->pfrb_msize = minsize; - } else { - if (minsize == 0) - minsize = b->pfrb_msize * 2; - if (minsize < 0 || minsize >= SIZE_T_MAX / bs) { - /* msize overflow */ - errno = ENOMEM; - return (-1); - } - p = realloc(b->pfrb_caddr, minsize * bs); - if (p == NULL) - return (-1); - bzero(p + b->pfrb_msize * bs, (minsize - b->pfrb_msize) * bs); - b->pfrb_caddr = p; - b->pfrb_msize = minsize; - } - return (0); -} - -/* - * reset buffer and free memory. - */ -void -pfr_buf_clear(struct pfr_buffer *b) -{ - if (b == NULL) - return; - if (b->pfrb_caddr != NULL) - free(b->pfrb_caddr); - b->pfrb_caddr = NULL; - b->pfrb_size = b->pfrb_msize = 0; -} - -int -pfr_buf_load(struct pfr_buffer *b, char *file, int nonetwork, - int (*append_addr)(struct pfr_buffer *, char *, int)) -{ - FILE *fp; - char buf[BUF_SIZE]; - int rv; - - if (file == NULL) - return (0); - if (!strcmp(file, "-")) - fp = stdin; - else { - fp = pfctl_fopen(file, "r"); - if (fp == NULL) - return (-1); - } - while ((rv = pfr_next_token(buf, fp)) == 1) - if (append_addr(b, buf, nonetwork)) { - rv = -1; - break; - } - if (fp != stdin) - fclose(fp); - return (rv); -} - -int -pfr_next_token(char buf[BUF_SIZE], FILE *fp) -{ - static char next_ch = ' '; - int i = 0; - - for (;;) { - /* skip spaces */ - while (isspace(next_ch) && !feof(fp)) - next_ch = fgetc(fp); - /* remove from '#' until end of line */ - if (next_ch == '#') - while (!feof(fp)) { - next_ch = fgetc(fp); - if (next_ch == '\n') - break; - } - else - break; - } - if (feof(fp)) { - next_ch = ' '; - return (0); - } - do { - if (i < BUF_SIZE) - buf[i++] = next_ch; - next_ch = fgetc(fp); - } while (!feof(fp) && !isspace(next_ch)); - if (i >= BUF_SIZE) { - errno = EINVAL; - return (-1); - } - buf[i] = '\0'; - return (1); -} - -char * -pfr_strerror(int errnum) -{ - switch (errnum) { - case ESRCH: - return "Table does not exist"; - case ENOENT: - return "Anchor or Ruleset does not exist"; - default: - return strerror(errnum); - } -} diff --git a/contrib/pf/pfctl/pfctl_table.c b/contrib/pf/pfctl/pfctl_table.c deleted file mode 100644 index f3a1efd..0000000 --- a/contrib/pf/pfctl/pfctl_table.c +++ /dev/null @@ -1,634 +0,0 @@ -/* $OpenBSD: pfctl_table.c,v 1.67 2008/06/10 20:55:02 mcbride Exp $ */ - -/* - * Copyright (c) 2002 Cedric Berger - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - 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/cdefs.h> -__FBSDID("$FreeBSD$"); - -#include <sys/types.h> -#include <sys/ioctl.h> -#include <sys/socket.h> - -#include <net/if.h> -#include <net/pfvar.h> -#include <arpa/inet.h> - -#include <ctype.h> -#include <err.h> -#include <errno.h> -#include <netdb.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> - -#include "pfctl_parser.h" -#include "pfctl.h" - -extern void usage(void); -static int pfctl_table(int, char *[], char *, const char *, char *, - const char *, int); -static void print_table(struct pfr_table *, int, int); -static void print_tstats(struct pfr_tstats *, int); -static int load_addr(struct pfr_buffer *, int, char *[], char *, int); -static void print_addrx(struct pfr_addr *, struct pfr_addr *, int); -static void print_astats(struct pfr_astats *, int); -static void radix_perror(void); -static void xprintf(int, const char *, ...); -static void print_iface(struct pfi_kif *, int); - -static const char *stats_text[PFR_DIR_MAX][PFR_OP_TABLE_MAX] = { - { "In/Block:", "In/Pass:", "In/XPass:" }, - { "Out/Block:", "Out/Pass:", "Out/XPass:" } -}; - -static const char *istats_text[2][2][2] = { - { { "In4/Pass:", "In4/Block:" }, { "Out4/Pass:", "Out4/Block:" } }, - { { "In6/Pass:", "In6/Block:" }, { "Out6/Pass:", "Out6/Block:" } } -}; - -#define RVTEST(fct) do { \ - if ((!(opts & PF_OPT_NOACTION) || \ - (opts & PF_OPT_DUMMYACTION)) && \ - (fct)) { \ - radix_perror(); \ - goto _error; \ - } \ - } while (0) - -#define CREATE_TABLE do { \ - table.pfrt_flags |= PFR_TFLAG_PERSIST; \ - if ((!(opts & PF_OPT_NOACTION) || \ - (opts & PF_OPT_DUMMYACTION)) && \ - (pfr_add_tables(&table, 1, &nadd, flags)) && \ - (errno != EPERM)) { \ - radix_perror(); \ - goto _error; \ - } \ - if (nadd) { \ - warn_namespace_collision(table.pfrt_name); \ - xprintf(opts, "%d table created", nadd); \ - if (opts & PF_OPT_NOACTION) \ - return (0); \ - } \ - table.pfrt_flags &= ~PFR_TFLAG_PERSIST; \ - } while(0) - -int -pfctl_clear_tables(const char *anchor, int opts) -{ - return pfctl_table(0, NULL, NULL, "-F", NULL, anchor, opts); -} - -int -pfctl_show_tables(const char *anchor, int opts) -{ - return pfctl_table(0, NULL, NULL, "-s", NULL, anchor, opts); -} - -int -pfctl_command_tables(int argc, char *argv[], char *tname, - const char *command, char *file, const char *anchor, int opts) -{ - if (tname == NULL || command == NULL) - usage(); - return pfctl_table(argc, argv, tname, command, file, anchor, opts); -} - -int -pfctl_table(int argc, char *argv[], char *tname, const char *command, - char *file, const char *anchor, int opts) -{ - struct pfr_table table; - struct pfr_buffer b, b2; - struct pfr_addr *a, *a2; - int nadd = 0, ndel = 0, nchange = 0, nzero = 0; - int rv = 0, flags = 0, nmatch = 0; - void *p; - - if (command == NULL) - usage(); - if (opts & PF_OPT_NOACTION) - flags |= PFR_FLAG_DUMMY; - - bzero(&b, sizeof(b)); - bzero(&b2, sizeof(b2)); - bzero(&table, sizeof(table)); - if (tname != NULL) { - if (strlen(tname) >= PF_TABLE_NAME_SIZE) - usage(); - if (strlcpy(table.pfrt_name, tname, - sizeof(table.pfrt_name)) >= sizeof(table.pfrt_name)) - errx(1, "pfctl_table: strlcpy"); - } - if (strlcpy(table.pfrt_anchor, anchor, - sizeof(table.pfrt_anchor)) >= sizeof(table.pfrt_anchor)) - errx(1, "pfctl_table: strlcpy"); - - if (!strcmp(command, "-F")) { - if (argc || file != NULL) - usage(); - RVTEST(pfr_clr_tables(&table, &ndel, flags)); - xprintf(opts, "%d tables deleted", ndel); - } else if (!strcmp(command, "-s")) { - b.pfrb_type = (opts & PF_OPT_VERBOSE2) ? - PFRB_TSTATS : PFRB_TABLES; - if (argc || file != NULL) - usage(); - for (;;) { - pfr_buf_grow(&b, b.pfrb_size); - b.pfrb_size = b.pfrb_msize; - if (opts & PF_OPT_VERBOSE2) - RVTEST(pfr_get_tstats(&table, - b.pfrb_caddr, &b.pfrb_size, flags)); - else - RVTEST(pfr_get_tables(&table, - b.pfrb_caddr, &b.pfrb_size, flags)); - if (b.pfrb_size <= b.pfrb_msize) - break; - } - - if ((opts & PF_OPT_SHOWALL) && b.pfrb_size > 0) - pfctl_print_title("TABLES:"); - - PFRB_FOREACH(p, &b) - if (opts & PF_OPT_VERBOSE2) - print_tstats(p, opts & PF_OPT_DEBUG); - else - print_table(p, opts & PF_OPT_VERBOSE, - opts & PF_OPT_DEBUG); - } else if (!strcmp(command, "kill")) { - if (argc || file != NULL) - usage(); - RVTEST(pfr_del_tables(&table, 1, &ndel, flags)); - xprintf(opts, "%d table deleted", ndel); - } else if (!strcmp(command, "flush")) { - if (argc || file != NULL) - usage(); - RVTEST(pfr_clr_addrs(&table, &ndel, flags)); - xprintf(opts, "%d addresses deleted", ndel); - } else if (!strcmp(command, "add")) { - b.pfrb_type = PFRB_ADDRS; - if (load_addr(&b, argc, argv, file, 0)) - goto _error; - CREATE_TABLE; - if (opts & PF_OPT_VERBOSE) - flags |= PFR_FLAG_FEEDBACK; - RVTEST(pfr_add_addrs(&table, b.pfrb_caddr, b.pfrb_size, - &nadd, flags)); - xprintf(opts, "%d/%d addresses added", nadd, b.pfrb_size); - if (opts & PF_OPT_VERBOSE) - PFRB_FOREACH(a, &b) - if ((opts & PF_OPT_VERBOSE2) || a->pfra_fback) - print_addrx(a, NULL, - opts & PF_OPT_USEDNS); - } else if (!strcmp(command, "delete")) { - b.pfrb_type = PFRB_ADDRS; - if (load_addr(&b, argc, argv, file, 0)) - goto _error; - if (opts & PF_OPT_VERBOSE) - flags |= PFR_FLAG_FEEDBACK; - RVTEST(pfr_del_addrs(&table, b.pfrb_caddr, b.pfrb_size, - &ndel, flags)); - xprintf(opts, "%d/%d addresses deleted", ndel, b.pfrb_size); - if (opts & PF_OPT_VERBOSE) - PFRB_FOREACH(a, &b) - if ((opts & PF_OPT_VERBOSE2) || a->pfra_fback) - print_addrx(a, NULL, - opts & PF_OPT_USEDNS); - } else if (!strcmp(command, "replace")) { - b.pfrb_type = PFRB_ADDRS; - if (load_addr(&b, argc, argv, file, 0)) - goto _error; - CREATE_TABLE; - if (opts & PF_OPT_VERBOSE) - flags |= PFR_FLAG_FEEDBACK; - for (;;) { - int sz2 = b.pfrb_msize; - - RVTEST(pfr_set_addrs(&table, b.pfrb_caddr, b.pfrb_size, - &sz2, &nadd, &ndel, &nchange, flags)); - if (sz2 <= b.pfrb_msize) { - b.pfrb_size = sz2; - break; - } else - pfr_buf_grow(&b, sz2); - } - if (nadd) - xprintf(opts, "%d addresses added", nadd); - if (ndel) - xprintf(opts, "%d addresses deleted", ndel); - if (nchange) - xprintf(opts, "%d addresses changed", nchange); - if (!nadd && !ndel && !nchange) - xprintf(opts, "no changes"); - if (opts & PF_OPT_VERBOSE) - PFRB_FOREACH(a, &b) - if ((opts & PF_OPT_VERBOSE2) || a->pfra_fback) - print_addrx(a, NULL, - opts & PF_OPT_USEDNS); - } else if (!strcmp(command, "expire")) { - const char *errstr; - u_int lifetime; - - b.pfrb_type = PFRB_ASTATS; - b2.pfrb_type = PFRB_ADDRS; - if (argc != 1 || file != NULL) - usage(); - lifetime = strtonum(*argv, 0, UINT_MAX, &errstr); - if (errstr) - errx(1, "expiry time: %s", errstr); - for (;;) { - pfr_buf_grow(&b, b.pfrb_size); - b.pfrb_size = b.pfrb_msize; - RVTEST(pfr_get_astats(&table, b.pfrb_caddr, - &b.pfrb_size, flags)); - if (b.pfrb_size <= b.pfrb_msize) - break; - } - PFRB_FOREACH(p, &b) { - ((struct pfr_astats *)p)->pfras_a.pfra_fback = 0; - if (time(NULL) - ((struct pfr_astats *)p)->pfras_tzero > - lifetime) - if (pfr_buf_add(&b2, - &((struct pfr_astats *)p)->pfras_a)) - err(1, "duplicate buffer"); - } - - if (opts & PF_OPT_VERBOSE) - flags |= PFR_FLAG_FEEDBACK; - RVTEST(pfr_del_addrs(&table, b2.pfrb_caddr, b2.pfrb_size, - &ndel, flags)); - xprintf(opts, "%d/%d addresses expired", ndel, b2.pfrb_size); - if (opts & PF_OPT_VERBOSE) - PFRB_FOREACH(a, &b2) - if ((opts & PF_OPT_VERBOSE2) || a->pfra_fback) - print_addrx(a, NULL, - opts & PF_OPT_USEDNS); - } else if (!strcmp(command, "show")) { - b.pfrb_type = (opts & PF_OPT_VERBOSE) ? - PFRB_ASTATS : PFRB_ADDRS; - if (argc || file != NULL) - usage(); - for (;;) { - pfr_buf_grow(&b, b.pfrb_size); - b.pfrb_size = b.pfrb_msize; - if (opts & PF_OPT_VERBOSE) - RVTEST(pfr_get_astats(&table, b.pfrb_caddr, - &b.pfrb_size, flags)); - else - RVTEST(pfr_get_addrs(&table, b.pfrb_caddr, - &b.pfrb_size, flags)); - if (b.pfrb_size <= b.pfrb_msize) - break; - } - PFRB_FOREACH(p, &b) - if (opts & PF_OPT_VERBOSE) - print_astats(p, opts & PF_OPT_USEDNS); - else - print_addrx(p, NULL, opts & PF_OPT_USEDNS); - } else if (!strcmp(command, "test")) { - b.pfrb_type = PFRB_ADDRS; - b2.pfrb_type = PFRB_ADDRS; - - if (load_addr(&b, argc, argv, file, 1)) - goto _error; - if (opts & PF_OPT_VERBOSE2) { - flags |= PFR_FLAG_REPLACE; - PFRB_FOREACH(a, &b) - if (pfr_buf_add(&b2, a)) - err(1, "duplicate buffer"); - } - RVTEST(pfr_tst_addrs(&table, b.pfrb_caddr, b.pfrb_size, - &nmatch, flags)); - xprintf(opts, "%d/%d addresses match", nmatch, b.pfrb_size); - if ((opts & PF_OPT_VERBOSE) && !(opts & PF_OPT_VERBOSE2)) - PFRB_FOREACH(a, &b) - if (a->pfra_fback == PFR_FB_MATCH) - print_addrx(a, NULL, - opts & PF_OPT_USEDNS); - if (opts & PF_OPT_VERBOSE2) { - a2 = NULL; - PFRB_FOREACH(a, &b) { - a2 = pfr_buf_next(&b2, a2); - print_addrx(a2, a, opts & PF_OPT_USEDNS); - } - } - if (nmatch < b.pfrb_size) - rv = 2; - } else if (!strcmp(command, "zero")) { - if (argc || file != NULL) - usage(); - flags |= PFR_FLAG_ADDRSTOO; - RVTEST(pfr_clr_tstats(&table, 1, &nzero, flags)); - xprintf(opts, "%d table/stats cleared", nzero); - } else - warnx("pfctl_table: unknown command '%s'", command); - goto _cleanup; - -_error: - rv = -1; -_cleanup: - pfr_buf_clear(&b); - pfr_buf_clear(&b2); - return (rv); -} - -void -print_table(struct pfr_table *ta, int verbose, int debug) -{ - if (!debug && !(ta->pfrt_flags & PFR_TFLAG_ACTIVE)) - return; - if (verbose) { - printf("%c%c%c%c%c%c%c\t%s", - (ta->pfrt_flags & PFR_TFLAG_CONST) ? 'c' : '-', - (ta->pfrt_flags & PFR_TFLAG_PERSIST) ? 'p' : '-', - (ta->pfrt_flags & PFR_TFLAG_ACTIVE) ? 'a' : '-', - (ta->pfrt_flags & PFR_TFLAG_INACTIVE) ? 'i' : '-', - (ta->pfrt_flags & PFR_TFLAG_REFERENCED) ? 'r' : '-', - (ta->pfrt_flags & PFR_TFLAG_REFDANCHOR) ? 'h' : '-', - (ta->pfrt_flags & PFR_TFLAG_COUNTERS) ? 'C' : '-', - ta->pfrt_name); - if (ta->pfrt_anchor[0]) - printf("\t%s", ta->pfrt_anchor); - puts(""); - } else - puts(ta->pfrt_name); -} - -void -print_tstats(struct pfr_tstats *ts, int debug) -{ - time_t time = ts->pfrts_tzero; - int dir, op; - - if (!debug && !(ts->pfrts_flags & PFR_TFLAG_ACTIVE)) - return; - print_table(&ts->pfrts_t, 1, debug); - printf("\tAddresses: %d\n", ts->pfrts_cnt); - printf("\tCleared: %s", ctime(&time)); - printf("\tReferences: [ Anchors: %-18d Rules: %-18d ]\n", - ts->pfrts_refcnt[PFR_REFCNT_ANCHOR], - ts->pfrts_refcnt[PFR_REFCNT_RULE]); - printf("\tEvaluations: [ NoMatch: %-18llu Match: %-18llu ]\n", - (unsigned long long)ts->pfrts_nomatch, - (unsigned long long)ts->pfrts_match); - for (dir = 0; dir < PFR_DIR_MAX; dir++) - for (op = 0; op < PFR_OP_TABLE_MAX; op++) - printf("\t%-12s [ Packets: %-18llu Bytes: %-18llu ]\n", - stats_text[dir][op], - (unsigned long long)ts->pfrts_packets[dir][op], - (unsigned long long)ts->pfrts_bytes[dir][op]); -} - -int -load_addr(struct pfr_buffer *b, int argc, char *argv[], char *file, - int nonetwork) -{ - while (argc--) - if (append_addr(b, *argv++, nonetwork)) { - if (errno) - warn("cannot decode %s", argv[-1]); - return (-1); - } - if (pfr_buf_load(b, file, nonetwork, append_addr)) { - warn("cannot load %s", file); - return (-1); - } - return (0); -} - -void -print_addrx(struct pfr_addr *ad, struct pfr_addr *rad, int dns) -{ - char ch, buf[256] = "{error}"; - char fb[] = { ' ', 'M', 'A', 'D', 'C', 'Z', 'X', ' ', 'Y', ' ' }; - unsigned int fback, hostnet; - - fback = (rad != NULL) ? rad->pfra_fback : ad->pfra_fback; - ch = (fback < sizeof(fb)/sizeof(*fb)) ? fb[fback] : '?'; - hostnet = (ad->pfra_af == AF_INET6) ? 128 : 32; - inet_ntop(ad->pfra_af, &ad->pfra_u, buf, sizeof(buf)); - printf("%c %c%s", ch, (ad->pfra_not?'!':' '), buf); - if (ad->pfra_net < hostnet) - printf("/%d", ad->pfra_net); - if (rad != NULL && fback != PFR_FB_NONE) { - if (strlcpy(buf, "{error}", sizeof(buf)) >= sizeof(buf)) - errx(1, "print_addrx: strlcpy"); - inet_ntop(rad->pfra_af, &rad->pfra_u, buf, sizeof(buf)); - printf("\t%c%s", (rad->pfra_not?'!':' '), buf); - if (rad->pfra_net < hostnet) - printf("/%d", rad->pfra_net); - } - if (rad != NULL && fback == PFR_FB_NONE) - printf("\t nomatch"); - if (dns && ad->pfra_net == hostnet) { - char host[NI_MAXHOST]; - union sockaddr_union sa; - - strlcpy(host, "?", sizeof(host)); - bzero(&sa, sizeof(sa)); - sa.sa.sa_family = ad->pfra_af; - if (sa.sa.sa_family == AF_INET) { - sa.sa.sa_len = sizeof(sa.sin); - sa.sin.sin_addr = ad->pfra_ip4addr; - } else { - sa.sa.sa_len = sizeof(sa.sin6); - sa.sin6.sin6_addr = ad->pfra_ip6addr; - } - if (getnameinfo(&sa.sa, sa.sa.sa_len, host, sizeof(host), - NULL, 0, NI_NAMEREQD) == 0) - printf("\t(%s)", host); - } - printf("\n"); -} - -void -print_astats(struct pfr_astats *as, int dns) -{ - time_t time = as->pfras_tzero; - int dir, op; - - print_addrx(&as->pfras_a, NULL, dns); - printf("\tCleared: %s", ctime(&time)); - if (as->pfras_a.pfra_fback == PFR_FB_NOCOUNT) - return; - for (dir = 0; dir < PFR_DIR_MAX; dir++) - for (op = 0; op < PFR_OP_ADDR_MAX; op++) - printf("\t%-12s [ Packets: %-18llu Bytes: %-18llu ]\n", - stats_text[dir][op], - (unsigned long long)as->pfras_packets[dir][op], - (unsigned long long)as->pfras_bytes[dir][op]); -} - -void -radix_perror(void) -{ - extern char *__progname; - fprintf(stderr, "%s: %s.\n", __progname, pfr_strerror(errno)); -} - -int -pfctl_define_table(char *name, int flags, int addrs, const char *anchor, - struct pfr_buffer *ab, u_int32_t ticket) -{ - struct pfr_table tbl; - - bzero(&tbl, sizeof(tbl)); - if (strlcpy(tbl.pfrt_name, name, sizeof(tbl.pfrt_name)) >= - sizeof(tbl.pfrt_name) || strlcpy(tbl.pfrt_anchor, anchor, - sizeof(tbl.pfrt_anchor)) >= sizeof(tbl.pfrt_anchor)) - errx(1, "pfctl_define_table: strlcpy"); - tbl.pfrt_flags = flags; - - return pfr_ina_define(&tbl, ab->pfrb_caddr, ab->pfrb_size, NULL, - NULL, ticket, addrs ? PFR_FLAG_ADDRSTOO : 0); -} - -void -warn_namespace_collision(const char *filter) -{ - struct pfr_buffer b; - struct pfr_table *t; - const char *name = NULL, *lastcoll; - int coll = 0; - - bzero(&b, sizeof(b)); - b.pfrb_type = PFRB_TABLES; - for (;;) { - pfr_buf_grow(&b, b.pfrb_size); - b.pfrb_size = b.pfrb_msize; - if (pfr_get_tables(NULL, b.pfrb_caddr, - &b.pfrb_size, PFR_FLAG_ALLRSETS)) - err(1, "pfr_get_tables"); - if (b.pfrb_size <= b.pfrb_msize) - break; - } - PFRB_FOREACH(t, &b) { - if (!(t->pfrt_flags & PFR_TFLAG_ACTIVE)) - continue; - if (filter != NULL && strcmp(filter, t->pfrt_name)) - continue; - if (!t->pfrt_anchor[0]) - name = t->pfrt_name; - else if (name != NULL && !strcmp(name, t->pfrt_name)) { - coll++; - lastcoll = name; - name = NULL; - } - } - if (coll == 1) - warnx("warning: namespace collision with <%s> global table.", - lastcoll); - else if (coll > 1) - warnx("warning: namespace collisions with %d global tables.", - coll); - pfr_buf_clear(&b); -} - -void -xprintf(int opts, const char *fmt, ...) -{ - va_list args; - - if (opts & PF_OPT_QUIET) - return; - - va_start(args, fmt); - vfprintf(stderr, fmt, args); - va_end(args); - - if (opts & PF_OPT_DUMMYACTION) - fprintf(stderr, " (dummy).\n"); - else if (opts & PF_OPT_NOACTION) - fprintf(stderr, " (syntax only).\n"); - else - fprintf(stderr, ".\n"); -} - - -/* interface stuff */ - -int -pfctl_show_ifaces(const char *filter, int opts) -{ - struct pfr_buffer b; - struct pfi_kif *p; - int i = 0; - - bzero(&b, sizeof(b)); - b.pfrb_type = PFRB_IFACES; - for (;;) { - pfr_buf_grow(&b, b.pfrb_size); - b.pfrb_size = b.pfrb_msize; - if (pfi_get_ifaces(filter, b.pfrb_caddr, &b.pfrb_size)) { - radix_perror(); - return (1); - } - if (b.pfrb_size <= b.pfrb_msize) - break; - i++; - } - if (opts & PF_OPT_SHOWALL) - pfctl_print_title("INTERFACES:"); - PFRB_FOREACH(p, &b) - print_iface(p, opts); - return (0); -} - -void -print_iface(struct pfi_kif *p, int opts) -{ - time_t tzero = p->pfik_tzero; - int i, af, dir, act; - - printf("%s", p->pfik_name); - if (opts & PF_OPT_VERBOSE) { - if (p->pfik_flags & PFI_IFLAG_SKIP) - printf(" (skip)"); - } - printf("\n"); - - if (!(opts & PF_OPT_VERBOSE2)) - return; - printf("\tCleared: %s", ctime(&tzero)); - printf("\tReferences: %-18d\n", p->pfik_rulerefs); - for (i = 0; i < 8; i++) { - af = (i>>2) & 1; - dir = (i>>1) &1; - act = i & 1; - printf("\t%-12s [ Packets: %-18llu Bytes: %-18llu ]\n", - istats_text[af][dir][act], - (unsigned long long)p->pfik_packets[af][dir][act], - (unsigned long long)p->pfik_bytes[af][dir][act]); - } -} |