Index: stable/11/etc/rc.d/pf =================================================================== --- stable/11/etc/rc.d/pf (revision 335057) +++ stable/11/etc/rc.d/pf (revision 335058) @@ -1,74 +1,76 @@ #!/bin/sh # # $FreeBSD$ # # PROVIDE: pf # REQUIRE: FILESYSTEMS netif pflog pfsync # BEFORE: routing # KEYWORD: nojail . /etc/rc.subr name="pf" desc="Packet filter" rcvar="pf_enable" load_rc_config $name start_cmd="pf_start" stop_cmd="pf_stop" check_cmd="pf_check" reload_cmd="pf_reload" resync_cmd="pf_resync" status_cmd="pf_status" extra_commands="check reload resync" required_files="$pf_rules" required_modules="pf" pf_start() { check_startmsgs && echo -n 'Enabling pf' $pf_program -F all > /dev/null 2>&1 $pf_program -f "$pf_rules" $pf_flags if ! $pf_program -s info | grep -q "Enabled" ; then $pf_program -eq fi check_startmsgs && echo '.' } pf_stop() { if $pf_program -s info | grep -q "Enabled" ; then echo -n 'Disabling pf' $pf_program -dq echo '.' fi } pf_check() { echo "Checking pf rules." $pf_program -n -f "$pf_rules" $pf_flags } pf_reload() { echo "Reloading pf rules." pf_resync } pf_resync() { $pf_program -n -f "$pf_rules" $pf_flags || return 1 $pf_program -f "$pf_rules" $pf_flags } pf_status() { if ! [ -c /dev/pf ] ; then echo "pf.ko is not loaded" + return 1 else $pf_program -s info + $pf_program -s Running >/dev/null fi } run_rc_command "$1" Index: stable/11/sbin/pfctl/pfctl.8 =================================================================== --- stable/11/sbin/pfctl/pfctl.8 (revision 335057) +++ stable/11/sbin/pfctl/pfctl.8 (revision 335058) @@ -1,690 +1,692 @@ .\" $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 October 3, 2016 .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 .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 is not 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 Running +Show the running status and provide a non-zero exit status when disabled. .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 counters { ftp.openbsd.org }\en \e pass out to \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 does not 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 do not 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 Do not 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 appeared in .Ox 3.0 . They first appeared in .Fx 5.3 ported from the version in .Ox 3.5 Index: stable/11/sbin/pfctl/pfctl.c =================================================================== --- stable/11/sbin/pfctl/pfctl.c (revision 335057) +++ stable/11/sbin/pfctl/pfctl.c (revision 335058) @@ -1,2435 +1,2453 @@ /* $OpenBSD: pfctl.c,v 1.278 2008/08/31 20:18:17 jmc Exp $ */ /*- * SPDX-License-Identifier: BSD-2-Clause * * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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_get_skip_ifaces(void); int pfctl_check_skip_ifaces(char *); int pfctl_clear_skip_ifaces(struct pfctl *); 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_running(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; static struct pfr_buffer skip_b; 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 + "Running", "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] [-a anchor] [-D macro=value] [-F modifier]\n" "\t[-f file] [-i interface] [-K host | network]\n" "\t[-k host | network | label | id] [-o level] [-p device]\n" "\t[-s modifier] [-t table -T command [address ...]] [-x level]\n", __progname); exit(1); } int pfctl_enable(int dev, int opts) { if (ioctl(dev, DIOCSTART)) { if (errno == EEXIST) errx(1, "pf already enabled"); else if (errno == ESRCH) errx(1, "pfil registeration failed"); 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_get_skip_ifaces(void) { bzero(&skip_b, sizeof(skip_b)); skip_b.pfrb_type = PFRB_IFACES; for (;;) { pfr_buf_grow(&skip_b, skip_b.pfrb_size); skip_b.pfrb_size = skip_b.pfrb_msize; if (pfi_get_ifaces(NULL, skip_b.pfrb_caddr, &skip_b.pfrb_size)) err(1, "pfi_get_ifaces"); if (skip_b.pfrb_size <= skip_b.pfrb_msize) break; } return (0); } int pfctl_check_skip_ifaces(char *ifname) { struct pfi_kif *p; PFRB_FOREACH(p, &skip_b) if ((p->pfik_flags & PFI_IFLAG_SKIP) && !strcmp(ifname, p->pfik_name)) p->pfik_flags &= ~PFI_IFLAG_SKIP; return (0); } int pfctl_clear_skip_ifaces(struct pfctl *pf) { struct pfi_kif *p; PFRB_FOREACH(p, &skip_b) if (p->pfik_flags & PFI_IFLAG_SKIP) pfctl_set_interface_flags(pf, p->pfik_name, PFI_IFLAG_SKIP, 0); 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: %-6ju]\n", (unsigned long long)rule->evaluations, (unsigned long long)(rule->packets[0] + rule->packets[1]), (unsigned long long)(rule->bytes[0] + rule->bytes[1]), (uintmax_t)rule->u_states_cur); if (!(opts & PF_OPT_DEBUG)) printf(" [ Inserted: uid %u pid %u " "State Creations: %-6ju]\n", (unsigned)rule->cuid, (unsigned)rule->cpid, (uintmax_t)rule->u_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 %ju\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], (uintmax_t)pr.rule.u_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_running(int dev) +{ + struct pf_status status; + + if (ioctl(dev, DIOCGETSTATUS, &status)) { + warn("DIOCGETSTATUS"); + return (-1); + } + + print_running(&status); + return (!status.running); +} + +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 (loadopt & PFCTL_FLAG_OPTION) pfctl_clear_skip_ifaces(&pf); 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; level = pf->debug; 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"); pfctl_check_skip_ifaces(ifname); } } 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, ""); 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_VERBOSE) 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(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 'R': + error = pfctl_show_running(dev); 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_get_skip_ifaces()) 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); } Index: stable/11/sbin/pfctl/pfctl_parser.c =================================================================== --- stable/11/sbin/pfctl/pfctl_parser.c (revision 335057) +++ stable/11/sbin/pfctl/pfctl_parser.c (revision 335058) @@ -1,1770 +1,1776 @@ /* $OpenBSD: pfctl_parser.c,v 1.240 2008/06/10 20:55:02 mcbride Exp $ */ /*- * SPDX-License-Identifier: BSD-2-Clause * * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 < nitems(icmp_type); i++) { if (type == icmp_type[i].type) return (&icmp_type[i]); } } else { for (i=0; i < nitems(icmp6_type); 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 < nitems(icmp_type); i++) { if (!strcmp(w, icmp_type[i].name)) return (&icmp_type[i]); } } else { for (i=0; i < nitems(icmp6_type); 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 < nitems(icmp_code); i++) { if (type == icmp_code[i].type && code == icmp_code[i].code) return (&icmp_code[i]); } } else { for (i=0; i < nitems(icmp6_code); 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 < nitems(icmp_code); i++) { if (type == icmp_code[i].type && !strcmp(w, icmp_code[i].name)) return (&icmp_code[i]); } } else { for (i=0; i < nitems(icmp6_code); 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_running(struct pf_status *status) +{ + printf("%s\n", status->running ? "Enabled" : "Disabled"); +} + +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->prio) printf(" prio %u", r->prio == PF_PRIO_ZERO ? 0 : r->prio); if (r->scrub_flags & PFSTATE_SETMASK) { char *comma = ""; printf(" set ("); if (r->scrub_flags & PFSTATE_SETPRIO) { if (r->set_prio[0] == r->set_prio[1]) printf("%s prio %u", comma, r->set_prio[0]); else printf("%s prio(%u, %u)", comma, r->set_prio[0], r->set_prio[1]); comma = ","; } printf(" )"); } 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"); 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); } int get_socket_domain(void) { int sdom; sdom = AF_UNSPEC; #ifdef WITH_INET6 if (sdom == AF_UNSPEC && feature_present("inet6")) sdom = AF_INET6; #endif #ifdef WITH_INET if (sdom == AF_UNSPEC && feature_present("inet")) sdom = AF_INET; #endif if (sdom == AF_UNSPEC) sdom = AF_LINK; return (sdom); } 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(get_socket_domain(), 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(get_socket_domain(), 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); } Index: stable/11/sbin/pfctl/pfctl_parser.h =================================================================== --- stable/11/sbin/pfctl/pfctl_parser.h (revision 335057) +++ stable/11/sbin/pfctl/pfctl_parser.h (revision 335058) @@ -1,324 +1,325 @@ /* $OpenBSD: pfctl_parser.h,v 1.86 2006/10/31 23:46:25 mcbride Exp $ */ /*- * SPDX-License-Identifier: BSD-2-Clause * * 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_fairq_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_fairq_opts { struct node_fairq_sc linkshare; struct node_queue_bw hogs_bw; u_int nbuckets; int flags; }; struct node_queue_opt { int qtype; union { struct cbq_opts cbq_opts; struct codel_opts codel_opts; struct priq_opts priq_opts; struct node_hfsc_opts hfsc_opts; struct node_fairq_opts fairq_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); +void print_running(struct pf_status *); 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); int get_socket_domain(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_ */ Index: stable/11 =================================================================== --- stable/11 (revision 335057) +++ stable/11 (revision 335058) Property changes on: stable/11 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r334726