diff --git a/sbin/ipf/ipf/ipf.5 b/sbin/ipf/ipf/ipf.5 index 79e6b2c45b05..7fbba17594ba 100644 --- a/sbin/ipf/ipf/ipf.5 +++ b/sbin/ipf/ipf/ipf.5 @@ -1,1697 +1,1697 @@ .TH IPF 5 .SH NAME ipf, ipf.conf \- IPFilter firewall rules file format .SH DESCRIPTION .PP The ipf.conf file is used to specify rules for the firewall, packet authentication and packet accounting components of IPFilter. To load rules specified in the ipf.conf file, the ipf(8) program is used. .PP For use as a firewall, there are two important rule types: those that block and drop packets (block rules) and those that allow packets through (pass rules.) Accompanying the decision to apply is a collection of statements that specify under what conditions the result is to be applied and how. .PP The simplest rules that can be used in ipf.conf are expressed like this: .PP .nf block in all pass out all .fi .PP Each rule must contain at least the following three components .RS .IP * a decision keyword (pass, block, etc.) .IP * the direction of the packet (in or out) .IP * address patterns or "all" to match any address information .RE .SS Long lines .PP For rules lines that are particularly long, it is possible to split -them over multiple lines implicity like this: +them over multiple lines implicitly like this: .PP .nf pass in on bgeo proto tcp from 1.1.1.1 port > 1000 to 2.2.2.2 port < 5000 flags S keep state .fi .PP or explicitly using the backslash ('\\') character: .PP .nf pass in on bgeo proto tcp from 1.1.1.1 port > 1000 \\ to 2.2.2.2 port < 5000 flags S keep state .fi .SS Comments .PP Comments in the ipf.conf file are indicated by the use of the '#' character. This can either be at the start of the line, like this: .PP .nf # Allow all ICMP packets in pass in proto icmp from any to any .fi .PP Or at the end of a like, like this: .PP .nf pass in proto icmp from any to any # Allow all ICMP packets in .fi .SH Firewall rules .PP This section goes into detail on how to construct firewall rules that are placed in the ipf.conf file. .PP It is beyond the scope of this document to describe what makes a good firewall rule set or which packets should be blocked or allowed in. Some suggestions will be provided but further reading is expected to fully understand what is safe and unsafe to allow in/out. .SS Filter rule keywords .PP The first word found in any filter rule describes what the eventual outcome of a packet that matches it will be. Descriptions of the many and various sections that can be used to match on the contents of packet headers will follow on below. .PP The complete list of keywords, along with what they do is as follows: .RS .HP pass rules that match a packet indicate to ipfilter that it should be allowed to continue on in the direction it is flowing. .HP block rules are used when it is desirable to prevent a packet from going any further. Packets that are blocked on the "in" side are never seen by TCP/IP and those that are blocked going "out" are never seen on the wire. .HP log when IPFilter successfully matches a packet against a log rule a log record is generated and made available for ipmon(8) to read. These rules have no impact on whether or not a packet is allowed through or not. So if a packet first matched a block rule and then matched a log rule, the status of the packet after the log rule is that it will still be blocked. .HP count rules provide the administrator with the ability to count packets and bytes that match the criteria laid out in the configuration file. The count rules are applied after NAT and filter rules on the inbound path. For outbound packets, count rules are applied before NAT and before the packet is dropped. Thus the count rule cannot be used as a true indicator of link layer .HP auth rules cause the matching packet to be queued up for processing by a user space program. The user space program is responsible for making an ioctl system call to collect the information about the queued packet and another ioctl system call to return the verdict (block, pass, etc) on what to do with the packet. In the event that the queue becomes full, the packets will end up being dropped. .HP call provides access to functions built into IPFilter that allow for more complex actions to be taken as part of the decision making that goes with the rule. .HP decapsulate rules instruct ipfilter to remove any other headers (IP, UDP, AH) and then process what is inside as a new packet. For non-UDP packets, there are builtin checks that are applied in addition to whatever is specified in the rule, to only allow decapsulation of recognised protocols. After decapsulating the inner packet, any filtering result that is applied to the inner packet is also applied to the other packet. .PP The default way in which filter rules are applied is for the last matching rule to be used as the decision maker. So even if the first rule to match a packet is a pass, if there is a later matching rule that is a block and no further rules match the packet, then it will be blocked. .SS Matching Network Interfaces .PP On systems with more than one network interface, it is necessary to be able to specify different filter rules for each of them. In the first instance, this is because different networks will send us packets via each network interface but it is also because of the hosts, the role and the resulting security policy that we need to be able to distinguish which network interface a packet is on. .PP To accommodate systems where the presence of a network interface is dynamic, it is not necessary for the network interface named in a filter rule to be present in the system when the rule is loaded. This can lead to silent errors being introduced and unexpected behaviour with the simplest of keyboard mistakes - for example, typing in hem0 instead of hme0 or hme2 instead of hme3. .PP On Solaris systems prior to Solaris 10 Update 4, it is not possible to filter packets on the loopback interface (lo0) so filter rules that specify it will have no impact on the corresponding flow of packets. See below for Solaris specific tips on how to enable this. .PP Some examples of including the network interface in filter rules are: .PP .nf block in on bge0 all pass out on bge0 all .fi .SS Address matching (basic) .PP The first and most basic part of matching for filtering rules is to specify IP addresses and TCP/UDP port numbers. The source address information is matched by the "from" information in a filter rule and the destination address information is matched with the "to" information in a filter rule. .PP The typical format used for IP addresses is CIDR notation, where an IP address (or network) is followed by a '/' and a number representing the size of the netmask in bits. This notation is used for specifying address matching in both IPv4 and IPv6. If the '/' and bitmask size are excluded from the matching string, it is assumed that the address specified is a host address and that the netmask applied should be all 1's. .PP Some examples of this are: .PP .nf pass in from 10.1.0.0/24 to any block out from any to 10.1.1.1 .fi .PP It is not possible to specify a range of addresses that does not have a boundary that can be defined by a standard subnet mask. .IP .B Names instead of addresses .RS .PP Hostnames, resolved either via DNS or /etc/hosts, or network names, resolved via /etc/networks, may be used in place of actual addresses in the filter rules. WARNING: if a hostname expands to more than one address, only the *first* is used in building the filter rule. .PP Caution should be exercised when relying on DNS for filter rules in case the sending and receiving of DNS packets is blocked when ipf(8) is processing that part of the configuration file, leading to long delays, if not errors, in loading the filter rules. .RE .SS Protocol Matching .PP To match packets based on TCP/UDP port information, it is first necessary to indicate which protocol the packet must be. This is done using the "proto" keyword, followed by either the protocol number or a name which is mapped to the protocol number, usually through the /etc/protocols file. .PP .nf pass in proto tcp from 10.1.0.0/24 to any block out proto udp from any to 10.1.1.1 pass in proto icmp from any to 192.168.0.0/16 .fi .SS Sending back error packets .PP When a packet is just discarded using a block rule, there is no feedback given to the host that sent the packet. This is both good and bad. If this is the desired behaviour and it is not desirable to send any feedback about packets that are to be denied. The catch is that often a host trying to connect to a TCP port or with a UDP based application will send more than one packet because it assumes that just one packet may be discarded so a retry is required. The end result being logs can become cluttered with duplicate entries due to the retries. .PP To address this problem, a block rule can be qualified in two ways. The first of these is specific to TCP and instructs IPFilter to send back a reset (RST) packet. This packet indicates to the remote system that the packet it sent has been rejected and that it shouldn't make any further attempts( to send packets to that port. Telling IPFilter to return a TCP); RST packet in response to something that has been received is achieved with the return-rst keyword like this: .PP .nf block return-rst in proto tcp from 10.0.0.0/8 to any .fi .PP When sending back a TCP RST packet, IPFilter must construct a new packet that has the source address of the intended target, not the source address of the system it is running on (if they are different.) .PP For all of the other protocols handled by the IP protocol suite, to send back an error indicating that the received packet was dropped requires sending back an ICMP error packet. Whilst these can also be used for TCP, the sending host may not treat the received ICMP error as a hard error in( the same way as it does the TCP RST packet. To return an ICMP error); it is necessary to place return-icmp after the block keyword like this: .PP .nf block return-icmp in proto udp from any to 192.168.0.1/24 .fi .PP When( electing to return an ICMP error packet, it is also possible to); select what type of ICMP error is returned. Whilst the full compliment of ICMP unreachable codes can be used by specifying a number instead of the string below, only the following should be used in conjunction with return-icmp.( Which return code to use is a choice to be made when); weighing up the pro's and con's. Using some of the codes may make it more obvious that a firewall is being used rather than just the host not responding. .RS .HP filter-prohib (prohibited by filter) sending packets to the destination given in the received packet is prohibited due to the application of a packet filter .HP net-prohib (prohibited network) sending packets to the destination given in the received packet is administratively prohibited. .HP host-unk (host unknown) the destination host address is not known by the system receiving the packet and therefore cannot be reached. .HP host-unr (host unreachable) it is not possible to reach the host as given by the destination address in the packet header. .HP net-unk (network unknown) the destination network address is not known by the system receiving the packet and therefore cannot be reached. .HP net-unr (network unreachable) it is not possible to forward the packet on to its final destination as given by the destination address .HP port-unr (port unreachable) there is no application using the given destination port and therefore it is not possible to reach that port. .HP proto-unr (protocol unreachable) the IP protocol specified in the packet is not available to receive packets. .DE .PP An example that shows how to send back a port unreachable packet for UDP packets to 192.168.1.0/24 is as follows: .PP .nf block return-icmp(port-unr) in proto udp from any to 192.168.1.0/24 .fi .PP In the above examples, when sending the ICMP packet, IPFilter will construct a new ICMP packet with a source address of the network interface used to send the packet back to the original source. This can give away that there is an intermediate system blocking packets. To have IPFilter send back ICMP packets where the source address is the original destination, regardless of whether or not it is on the local host, return-icmp-as-dest is used like this: .PP .nf block return-icmp-as-dest(port-unr) in proto udp \\ from any to 192.168.1.0/24 .fi .SS TCP/UDP Port Matching .PP Having specified which protocol is being matched, it is then possible to indicate which port numbers a packet must have in order to match the rule. Due to port numbers being used differently to addresses, it is therefore possible to match on them in different ways. IPFilter allows you to use the following logical operations: .IP "< x" is true if the port number is greater than or equal to x and less than or equal to y is true if the port number in the packet is less than x .IP "<= x" is true if the port number in the packet is less than or equal to x .IP "> x" is true if the port number in the packet is greater than x .IP ">= x" is true if the port number in the packet is greater or equal to x .IP "= x" is true if the port number in the packet is equal to x .IP "!= x" is true if the port number in the packet is not equal to x .PP Additionally, there are a number of ways to specify a range of ports: .IP "x <> y" is true if the port number is less than a and greater than y .IP "x >< y" is true if the port number is greater than x and less than y .IP "x:y" is true if the port number is greater than or equal to x and less than or equal to y .PP Some examples of this are: .PP .nf block in proto tcp from any port >= 1024 to any port < 1024 pass in proto tcp from 10.1.0.0/24 to any port = 22 block out proto udp from any to 10.1.1.1 port = 135 pass in proto udp from 1.1.1.1 port = 123 to 10.1.1.1 port = 123 pass in proto tcp from 127.0.0.0/8 to any port 6000:6009 .fi .PP If there is no desire to mention any specific source or destintion information in a filter rule then the word "all" can be used to indicate that all addresses are considered to match the rule. .SS IPv4 or IPv6 .PP If a filter rule is constructed without any addresses then IPFilter will attempt to match both IPv4 and IPv6 packets with it. In the next list of rules, each one can be applied to either network protocol because there is no address specified from which IPFilter can derive with network protocol to expect. .PP .nf pass in proto udp from any to any port = 53 block in proto tcp from any port < 1024 to any .fi .PP To explicitly match a particular network address family with a specific rule, the family must be added to the rule. For IPv4 it is necessary to add family inet and for IPv6, family inet6. Thus the next rule will block all packets (both IPv4 and IPv6: .PP .nf block in all .fi .PP but in the following example, we block all IPv4 packets and only allow in IPv6 packets: .PP .nf block in family inet all pass in family inet6 all .fi .PP To continue on from the example where we allowed either IPv4 or IPv6 packets to port 53 in, to change that such that only IPv6 packets to port 53 need to allowed blocked then it is possible to add in a protocol family qualifier: .PP .nf pass in family inet6 proto udp from any to any port = 53 .fi .SS First match vs last match .PP To change the default behaviour from being the last matched rule decides the outcome to being the first matched rule, the word "quick" is inserted to the rule. .SH Extended Packet Matching .SS Beyond using plain addresses .PP On firewalls that are working with large numbers of hosts and networks or simply trying to filter discretely against various hosts, it can be an easier administration task to define a pool of addresses and have a filter rule reference that address pool rather than have a rule for each address. .PP In addition to being able to use address pools, it is possible to use the interface name(s) in the from/to address fields of a rule. If the name being used in the address section can be matched to any of the interface names mentioned in the rule's "on" or "via" fields then it can be used with one of the following keywords for extended effect: .HP broadcast use the primary broadcast address of the network interface for matching packets with this filter rule; .IP .nf pass in on fxp0 proto udp from any to fxp0/broadcast port = 123 .fi .HP peer use the peer address on point to point network interfaces for matching packets with this filter rule. This option typically only has meaningful use with link protocols such as SLIP and PPP. For example, this rule allows ICMP packets from the remote peer of ppp0 to be received if they're destined for the address assigned to the link at the firewall end. .IP .nf pass in on ppp0 proto icmp from ppp0/peer to ppp0/32 .fi .HP netmasked use the primary network address, with its netmask, of the network interface for matching packets with this filter rule. If a network interface had an IP address of 192.168.1.1 and its netmask was 255.255.255.0 (/24), then using the word "netmasked" after the interface name would match any addresses that would match 192.168.1.0/24. If we assume that bge0 has this IP address and netmask then the following two rules both serve to produce the same effect: .IP .nf pass in on bge0 proto icmp from any to 192.168.1.0/24 pass in on bge0 proto icmp from any to bge0/netmasked .fi .HP network using the primary network address, and its netmask, of the network interface, construct an address for exact matching. If a network interface has an address of 192.168.1.1 and its netmask is 255.255.255.0, using this option would only match packets to 192.168.1.0. .IP .nf pass in on bge0 proto icmp from any to bge0/network .fi .PP Another way to use the name of a network interface to get the address is to wrap the name in ()'s. In the above method, IPFilter looks at the interface names in use and to decide whether or not the name given is a hostname or network interface name. With the use of ()'s, it is possible to tell IPFilter that the name should be treated as a network interface name even though it doesn't appear in the list of network interface that the rule ias associated with. .IP .nf pass in proto icmp from any to (bge0)/32 .fi .SS Using address pools .PP Rather than list out multiple rules that either allow or deny specific addresses, it is possible to create a single object, call an address pool, that contains all of those addresses and reference that in the filter rule. For documentation on how to write the configuration file for those pools and load them, see ippool.conf(5) and ippool(8). There are two types of address pools that can be defined in ippool.conf(5): trees and hash tables. To refer to a tree defined in ippool.conf(5), use this syntax: .PP .nf pass in from pool/trusted to any .fi .PP Either a name or number can be used after the '/', just so long as it matches up with something that has already been defined in ipool.conf(5) and loaded in with ippool(8). Loading a filter rule that references a pool that does not exist will result in an error. .PP If hash tables have been used in ippool.conf(5) to store the addresses in instead of a tree, then replace the word pool with hash: .IP .nf pass in from any to hash/webservers .fi .PP There are different operational characteristics with each, so there may be some situations where a pool works better than hash and vice versa. .SS Matching TCP flags .PP The TCP header contains a field of flags that is used to decide if the packet is a connection request, connection termination, data, etc. By matching on the flags in conjunction with port numbers, it is possible to restrict the way in which IPFilter allows connections to be created. A quick overview of the TCP flags is below. Each is listed with the letter used in IPFilter rules, followed by its three or four letter pneumonic. .HP S SYN - this bit is set when a host is setting up a connection. The initiator typically sends a packet with the SYN bit and the responder sends back SYN plus ACK. .HP A ACK - this bit is set when the sender wishes to acknowledge the receipt of a packet from another host .HP P PUSH - this bit is set when a sending host has send some data that is yet to be acknowledged and a reply is sought .HP F FIN - this bit is set when one end of a connection starts to close the connection down .HP U URG - this bit is set to indicate that the packet contains urgent data .HP R RST - this bit is set only in packets that are a reply to another -that has been received but is not targetted at any open port +that has been received but is not targeted at any open port .HP C CWN .HP E ECN .PP When matching TCP flags, it is normal to just list the flag that you wish to be set. By default the set of flags it is compared against is "FSRPAU". Rules that say "flags S" will be displayed by ipfstat(8) as having "flags S/FSRPAU". This is normal. The last two flags, "C" and "E", are optional - they may or may not be used by an end host and have no bearing on either the acceptance of data nor control of the connection. Masking them out with "flags S/FSRPAUCE" may cause problems for remote hosts making a successful connection. .PP .nf pass in quick proto tcp from any to any port = 22 flags S/SAFR pass out quick proto tcp from any port = 22 to any flags SA .fi .PP By itself, filtering based on the TCP flags becomes more work but when combined with stateful filtering (see below), the situation changes. .SS Matching on ICMP header information .PP The TCP and UDP are not the only protocols for which filtering beyond just the IP header is possible, extended matching on ICMP packets is also available. The list of valid ICMP types is different for IPv4 vs IPv6. .PP As a practical example, to allow the ping command to work against a specific target requires allowing two different types of ICMP packets, like this: .PP .nf pass in proto icmp from any to webserver icmp-type echo pass out proto icmp from webserver to any icmp-type echorep .fi .PP The ICMP header has two fields that are of interest for filtering: the ICMP type and code. Filter rules can accept either a name or number for both the type and code. The list of names supported for ICMP types is listed below, however only ICMP unreachable errors have named codes (see above.) .PP The list of ICMP types that are available for matching an IPv4 packet are as follows: .PP echo (echo request), echorep (echo reply), inforeq (information request), inforep (information reply), maskreq (mask request), maskrep (mask reply), paramprob (parameter problem), redir (redirect), routerad (router advertisement), routersol (router solicit), squence (source quence), timest (timestamp), timestreq (timestamp reply), timex (time exceeded), unreach (unreachable). .PP The list of ICMP types that are available for matching an IPv6 packet are as follows: .PP echo (echo request), echorep (echo reply), fqdnquery (FQDN query), fqdnreply (FQDN reply), inforeq (information request), inforep (information reply), listendone (MLD listener done), listendqry (MLD listener query), listendrep (MLD listener reply), neighadvert (neighbour advert), neighborsol (neighbour solicit), paramprob (parameter problem), redir (redirect), renumber (router renumbering), routerad (router advertisement), routersol (router solicit), timex (time exceeded), toobig (packet too big), unreach (unreachable, whoreq (WRU request), whorep (WRU reply). .SH Stateful Packet Filtering .PP Stateful packet filtering is where IPFilter remembers some information from one or more packets that it has seen and is able to apply it to future packets that it receives from the network. .PP What this means for each transport layer protocol is different. For TCP it means that if IPFilter sees the very first packet of an attempt to make a connection, it has enough information to allow all other subsequent packets without there needing to be any explicit rules to match them. IPFilter uses the TCP port numbers, TCP flags, window size and sequence numbers to determine which packets should be matched. For UDP, only the UDP port numbers are available. For ICMP, the ICMP types can be combined with the ICMP id field to determine which reply packets match a request/query that has already been seen. For all other protocols, only matching on IP address and protocol number is available for determining if a packet received is a mate to one that has already been let through. .PP The difference this makes is a reduction in the number of rules from 2 or 4 to 1. For example, these 4 rules: .PP .nf pass in on bge0 proto tcp from any to any port = 22 pass out on bge1 proto tcp from any to any port = 22 pass in on bge1 proto tcp from any port = 22 to any pass out on bge0 proto tcp from any port = 22 to any .fi .PP can be replaced with this single rule: .PP .nf pass in on bge0 proto tcp from any to any port = 22 flags S keep state .fi .PP Similar rules for UDP and ICMP might be: .PP .nf pass in on bge0 proto udp from any to any port = 53 keep state pass in on bge0 proto icmp all icmp-type echo keep state .fi .PP When using stateful filtering with TCP it is best to add "flags S" to the rule to ensure that state is only created when a packet is seen that is an indication of a new connection. Although IPFilter can gather some information from packets in the middle of a TCP connection to do stateful filtering, there are some options that are only sent at the start of the connection which alter the valid window of what TCP accepts. The end result of trying to pickup TCP state in mid connection is that some later packets that are part of the connection may not match the known state information and be dropped or blocked, causing problems. If a TCP packet matches IP addresses and port numbers but does not fit into the recognised window, it will not be automatically allowed and will be flagged inside of IPFitler as "out of window" (oow). See below, "Extra packet attributes", for how to match on this attribute. .PP Once a TCP connection has reached the established state, the default timeout allows for it to be idle for 5 days before it is removed from the state table. The timeouts for the other TCP connection states vary from 240 seconds to 30 seconds. -Both UDP and ICMP state entries have asymetric timeouts where the timeout +Both UDP and ICMP state entries have asymmetric timeouts where the timeout set upon seeing packets in the forward direction is much larger than for the reverse direction. For UDP the default timeouts are 120 and 12 seconds, for ICMP 60 and 6 seconds. This is a reflection of the use of these protocols being more for query-response than for ongoing connections. For all other protocols the timeout is 60 seconds in both directions. .SS Stateful filtering options .PP The following options can be used with stateful filtering: .HP limit limit the number of state table entries that this rule can create to the number given after limit. A rule that has a limit specified is always permitted that many state table entries, even if creating an additional entry would cause the table to have more entries than the otherwise global limit. .IP .nf pass ... keep state(limit 100) .fi .HP age sets the timeout for the state entry when it sees packets going through it. Additionally it is possible to set the tieout for the reply packets that come back through the firewall to a different value than for the forward path. allowing a short timeout to be set after the reply has been seen and the state no longer required. .RS .PP .nf pass in quick proto icmp all icmp-type echo \\ keep state (age 3) pass in quick proto udp from any \\ to any port = 53 keep state (age 30/1) .fi .RE .HP strict only has an impact when used with TCP. It forces all packets that are allowed through the firewall to be sequential: no out of order delivery of packets is allowed. This can cause significant slowdown for some connections and may stall others. Use with caution. .IP .nf pass in proto tcp ... keep state(strict) .fi .HP noicmperr prevents ICMP error packets from being able to match state table entries created with this flag using the contents of the original packet included. .IP .nf pass ... keep state(noicmperr) .fi .HP sync indicates to IPFilter that it needs to provide information to the user land daemons responsible for syncing other machines state tables up with this one. .IP .nf pass ... keep state(sync) .fi .HP nolog do not generate any log records for the creation or deletion of state table entries. .IP .nf pass ... keep state(nolog) .fi .HP icmp-head rather than just precent ICMP error packets from being able to match state table entries, allow an ACL to be processed that can filter in or out ICMP error packets based as you would with normal firewall rules. The icmp-head option requires a filter rule group number or name to be present, just as you would use with head. .RS .PP .nf pass in quick proto tcp ... keep state(icmp-head 101) block in proto icmp from 10.0.0.0/8 to any group 101 .fi .RE .HP max-srcs allows the number of distinct hosts that can create a state entry to be defined. .IP .nf pass ... keep state(max-srcs 100) pass ... keep state(limit 1000, max-srcs 100) .fi .HP max-per-src whilst max-srcs limits the number of individual hosts that may cause the creation of a state table entry, each one of those hosts is still table to fill up the state table with new entries until the global maximum is reached. This option allows the number of state table entries per address to be limited. .IP .nf pass ... keep state(max-srcs 100, max-per-src 1) pass ... keep state(limit 100, max-srcs 100, max-per-src 1) .fi .IP Whilst these two rules might seem identical, in that they both ultimately limit the number of hosts and state table entries created from the rule to 100, there is a subtle difference: the second will always allow up to 100 state table entries to be created whereas the first may not if the state table fills up from other rules. .IP Further, it is possible to specify a netmask size after the per-host limit that enables the per-host limit to become a per-subnet or per-network limit. .IP .nf pass ... keep state(max-srcs 100, max-per-src 1/24) .fi .IP If there is no IP protocol implied by addresses or other features of the rule, IPFilter will assume that no netmask is an all ones netmask for both IPv4 and IPv6. .SS Tieing down a connection .PP For any connection that transits a firewall, each packet will be seen twice: once going in and once going out. Thus a connection has 4 flows of packets: .HP forward inbound packets .HP forward outbound packets .HP reverse inbound packets .HP reverse outbound packets .PP IPFilter allows you to define the network interface to be used at all four points in the flow of packets. For rules that match inbound packets, out-via is used to specify which interfaces the packets go out, For rules that match outbound packets, in-via is used to match the inbound packets. In each case, the syntax generalises to this: .PP .nf pass ... in on forward-in,reverse-in \\ out-via forward-out,reverse-out ... pass ... out on forward-out,reverse-out \\ in-via forward-in,reverse-in ... .fi .PP An example that pins down all 4 network interfaces used by an ssh connection might look something like this: .PP .nf pass in on bge0,bge1 out-via bge1,bge0 proto tcp \\ from any to any port = 22 flags S keep state .fi .SS Working with packet fragments .PP Fragmented packets result in 1 packet containing all of the layer 3 and 4 header information whilst the data is split across a number of other packets. .PP To enforce access control on fragmented packets, one of two approaches can be taken. The first is to allow through all of the data fragments (those that made up the body of the original packet) and rely on matching the header information in the "first" fragment, when it is seen. The reception of body fragments without the first will result in the receiving host being unable to completely reassemble the packet and discarding all of the fragments. The following three rules deny all fragmented packets from being received except those that are UDP and even then only allows those destined for port 2049 to be completed. .PP .nf block in all with frags pass in proto udp from any to any with frag-body pass in proto udp from any to any port = 2049 with frags .fi .PP Another mechanism that is available is to track "fragment state". This relies on the first fragment of a packet that arrives to be the fragment that contains all of the layer 3 and layer 4 header information. With the receipt of that fragment before any other, it is possible to determine which other fragments are to be allowed through without needing to explicitly allow all fragment body packets. An example of how this is done is as follows: .PP .nf pass in proto udp from any port = 2049 to any with frags keep frags .fi .SH Building a tree of rules .PP Writing your filter rules as one long list of rules can be both inefficient in terms of processing the rules and difficult to understand. To make the construction of filter rules easier, it is possible to place them in groups. A rule can be both a member of a group and the head of a new group. .PP Using filter groups requires at least two rules: one to be in the group -one one to send matchign packets to the group. If a packet matches a -filtre rule that is a group head but does not match any of the rules +one one to send matching packets to the group. If a packet matches a +filter rule that is a group head but does not match any of the rules in that group, then the packet is considered to have matched the head rule. .PP Rules that are a member of a group contain the word group followed by either a name or number that defines which group they're in. Rules that form the branch point or starting point for the group must use the word head, followed by either a group name or number. If rules are loaded in that define a group but there is no matching head then they will effectively be orphaned rules. It is possible to have more than one head rule point to the same group, allowing groups to be used like subroutines to implement specific common policies. .PP A common use of filter groups is to define head rules that exist in the filter "main line" for each direction with the interfaces in use. For example: .PP .nf block in quick on bge0 all head 100 block out quick on bge0 all head 101 block in quick on fxp0 all head internal-in block out quick on fxp0 all head internal-out pass in quick proto icmp all icmp-type echo group 100 .fi .PP In the above set of rules, there are four groups defined but only one of them has a member rule. The only packets that would be allowed through the above ruleset would be ICMP echo packets that are received on bge0. .PP Rules can be both a member of a group and the head of a new group, allowing groups to specialise. .PP .nf block in quick on bge0 all head 100 block in quick proto tcp all head 1006 group 100 .fi .PP Another use of filter rule groups is to provide a place for rules to be dynamically added without needing to worry about their specific ordering amongst the entire ruleset. For example, if I was using this simple ruleset: .PP .nf block in quick all with bad block in proto tcp from any to any port = smtp head spammers pass in quick proto tcp from any to any port = smtp flags S keep state .fi .PP and I was getting lots of connections to my email server from 10.1.1.1 to deliver spam, I could load the following rule to complement the above: .IP .nf block in quick from 10.1.1.1 to any group spammers .fi .SS Decapsulation .PP Rule groups also form a different but vital role for decapsulation rules. With the following simple rule, if IPFilter receives an IP packet that has an AH header as its layer 4 payload, IPFilter would adjust its view of the packet internally and then jump to group 1001 using the data beyond the AH header as the new transport header. .PP .nf decapsulate in proto ah all head 1001 .fi .PP For protocols that are recognised as being used with tunnelling or otherwise encapsulating IP protocols, IPFilter is able to decide what it has on the inside without any assistance. Some tunnelling protocols use UDP as the transport mechanism. In this case, it is necessary to instruct IPFilter as to what protocol is inside UDP. .PP .nf decapsulate l5-as(ip) in proto udp from any \\ to any port = 1520 head 1001 .fi .PP Currently IPFilter only supports finding IPv4 and IPv6 headers directly after the UDP header. .PP If a packet matches a decapsulate rule but fails to match any of the rules that are within the specified group, processing of the packet continues to the next rule after the decapsulate and IPFilter's internal view of the packet is returned to what it was prior to the decapsulate rule. .PP It is possible to construct a decapsulate rule without the group head at the end that ipf(8) will accept but such rules will not result in anything happening. .SS Policy Based Routing .PP With firewalls being in the position they often are, at the boundary of different networks connecting together and multiple connections that have different properties, it is often desirable to have packets flow in a direction different to what the routing table instructs the kernel. These decisions can often be extended to changing the route based on both source and destination address or even port numbers. .PP To support this kind of configuration, IPFilter allows the next hop destination to be specified with a filter rule. The next hop is given with the interface name to use for output. The syntax for this is interface:ip.address. It is expected that the address given as the next hop is directly connected to the network to which the interface is. .PP .nf pass in on bge0 to bge1:1.1.1.1 proto tcp \\ from 1.1.2.3 to any port = 80 flags S keep state .fi .PP When this feature is combined with stateful filtering, it becomes possible to influence the network interface used to transmit packets in both directions because we now have a sense for what its reverse flow of packets is. .PP .nf pass in on bge0 to bge1:1.1.1.1 reply-to hme1:2.1.1.2 \\ proto tcp from 1.1.2.3 to any port = 80 flags S keep state .fi .PP If the actions of the routing table are perfectly acceptable, but you would like to mask the presence of the firewall by not changing the TTL in IP packets as they transit it, IPFilter can be instructed to do a "fastroute" action like this: .PP .nf pass in on bge0 fastroute proto icmp all .fi .PP This should be used with caution as it can lead to endless packet loops. Additionally, policy based routing does not change the IP header's TTL value. .PP A variation on this type of rule supports a duplicate of the original packet being created and sent out a different network. This can be useful for monitoring traffic and other purposes. .PP .nf pass in on bge0 to bge1:1.1.1.1 reply-to hme1:2.1.1.2 \\ dup-to fxp0:10.0.0.1 proto tcp from 1.1.2.3 \\ to any port = 80 flags S keep state .fi .SS Matching IPv4 options .PP The design for IPv4 allows for the header to be upto 64 bytes long, however most traffic only uses the basic header which is 20 bytes long. -The other 44 bytes can be uesd to store IP options. These options are +The other 44 bytes can be used to store IP options. These options are generally not necessary for proper interaction and function on the Internet today. For most people it is sufficient to block and drop all packets that have any options set. This can be achieved with this rule: .PP .nf block in quick all with ipopts .fi .PP This rule is usually placed towards the top of the configuration so that all incoming packets are blocked. .PP If you wanted to allow in a specific IP option type, the syntax changes slightly: .PP .nf pass in quick proto igmp all with opt rtralrt .fi .PP The following is a list of IP options that most people encounter and what their use/threat is. .HP lsrr (loose source route) the sender of the packet includes a list of addresses that they wish the packet to be routed through to on the way to the destination. Because replies to such packets are expected to use the list of addresses in reverse, hackers are able to very effectively use this header option in address spoofing attacks. .HP rr (record route) the sender allocates some buffer space for recording the IP address of each router that the packet goes through. This is most often used with ping, where the ping response contains a copy of all addresses from the original packet, telling the sender what route the packet took to get there. Due to performance and security issues with IP header options, this is almost no longer used. .HP rtralrt (router alert) this option is often used in IGMP messages as a flag to routers that the packet needs to be handled differently. It is unlikely to ever be received from an unknown sender. It may be found on LANs or otherwise controlled networks where the RSVP protocol and multicast traffic is in heavy use. .HP ssrr (strict source route) the sender of the packet includes a list of addresses that they wish the packet to be routed through to on the way to the destination. Where the lsrr option allows the sender to specify only some of the nodes the packet must go through, with the ssrr option, every next hop router must be specified. .PP The complete list of IPv4 options that can be matched on is: -addext (Address Extention), +addext (Address Extension), cipso (Classical IP Security Option), dps (Dynamic Packet State), e-sec (Extended Security), eip (Extended Internet Protocol), encode (ENCODE), finn (Experimental Flow Control), imitd (IMI Traffic Descriptor), lsrr (Loose Source Route), mtup (MTU Probe - obsolete), mtur (MTU response - obsolete), nop (No Operation), nsapa (NSAP Address), rr (Record Route), rtralrt (Router Alert), satid (Stream Identifier), sdb (Selective Directed Broadcast), sec (Security), ssrr (Strict Source Route), tr (Tracerote), ts (Timestamp), ump (Upstream Multicast Packet), visa (Experimental Access Control) and zsu (Experimental Measurement). .SS Security with CIPSO and IPSO .PP IPFilter supports filtering on IPv4 packets using security attributes embedded in the IP options part of the packet. These options are usually only used on networks and systems that are using lablled security. Unless you know that you are using labelled security and your networking is also labelled, it is highly unlikely that this section will be relevant to you. .PP With the traditional IP Security Options (IPSO), packets can be tagged with a security level. The following keywords are recognised and match with the relevant RFC with respect to the bit patterns matched: confid (confidential), rserve-1 (1st reserved value), rserve-2 (2nd reserved value), rserve-3 (3rd reserved value), rserve-4 (4th reserved value), secret (secret), topsecret (top secret), unclass (unclassified). .PP .nf block in quick all with opt sec-class unclass pass in all with opt sec-class secret .fi .SS Matching IPv6 extension headers .PP Just as it is possible to filter on the various IPv4 header options, so too it is possible to filter on the IPv6 extension headers that are placed between the IPv6 header and the transport protocol header. .PP dstopts (destination options), esp (encrypted, secure, payload), frag (fragment), hopopts (hop-by-hop options), ipv6 (IPv6 header), mobility (IP mobility), none, routing. .SS Logging .PP There are two ways in which packets can be logged with IPFilter. The first is with a rule that specifically says log these types of packets and the second is a qualifier to one of the other keywords. Thus it is possible to both log and allow or deny a packet with a single rule. .PP .nf pass in log quick proto tcp from any to any port = 22 .fi .PP When using stateful filtering, the log action becomes part of the result that is remembered about a packet. Thus if the above rule was qualified with keep state, every packet in the connection would be logged. To only log the first packet from every packet flow tracked with keep state, it is necessary to indicate to IPFilter that you only wish to log the first packet. .PP .nf pass in log first quick proto tcp from any to any port = 22 \\ flags S keep state .fi .PP If it is a requirement that the logging provide an accurate representation of which connections are allowed, the log action can be qualified with the option or-block. This allows the administrator to instruct IPFilter to block the packet if the attempt to record the packet in IPFilter's kernel log records (which have an upper bound on size) failed. Unless the system shuts down or reboots, once a log record is written into the kernel buffer, it is there until ipmon(8) reads it. .PP .nf block in log proto tcp from any to any port = smtp pass in log or-block first quick proto tcp from any \\ to any port = 22 flags S keep state .fi .PP By default, IPFilter will only log the header portion of a packet received on the network. Up to 128 bytes of a packet's body can also be logged with the body keyword. ipmon(8) will display the contents of the portion of the body logged in hex. .PP .nf block in log body proto icmp all .fi .PP When logging packets from ipmon(8) to syslog, by default ipmon(8) will control what syslog facility and priority a packet will be logged with. This can be tuned on a per rule basis like this: .PP .nf block in quick log level err all with bad pass in log level local1.info proto tcp \\ from any to any port = 22 flags S keep state .fi .PP ipfstat(8) reports how many packets have been successfully logged and how many failed attempts to log a packet there were. .SS Filter rule comments .PP If there is a desire to associate a text string, be it an administrative comment or otherwise, with an IPFilter rule, this can be achieved by giving the filter rule a comment. The comment is loaded with the rule into the kernel and can be seen when the rules are listed with ipfstat. .PP .nf pass in quick proto tcp from any \\ to port = 80 comment "all web server traffic is ok" pass out quick proto tcp from any port = 80 \\ to any comment "all web server traffic is ok" .fi .SS Tags .PP To enable filtering and NAT to correctly match up packets with rules, tags can be added at with NAT (for inbound packets) and filtering (for outbound packets.) This allows a filter to be correctly mated with its NAT rule in the event that the NAT rule changed the packet in a way that would mean it is not obvious what it was. .PP For inbound packets, IPFilter can match the tag used in the filter rules with that set by NAT. For outbound rules, it is the reverse, the filter sets the tag and the NAT rule matches up with it. .PP .nf pass in ... match-tag(nat=proxy) pass out ... set-tag(nat=proxy) .fi .PP Another use of tags is to supply a number that is only used with logging. When packets match these rules, the log tag is carried over into the log file records generated by ipmon(8). With the correct use of tools such as grep, extracting log records of interest is simplified. .PP .nf block in quick log ... set-tag(log=33) .fi .SH Filter Rule Expiration .PP IPFilter allows rules to be added into the kernel that it will remove after a specific period of time by specifying rule-ttl at the end of a rule. When listing rules in the kernel using ipfstat(8), rules that are going to expire will NOT display "rule-ttl" with the timeout, rather what will be seen is a comment with how many ipfilter ticks left the rule has to live. .PP The time to live is specified in seconds. .PP .nf pass in on fxp0 proto tcp from any \\ to port = 22 flags S keep state rule-ttl 30 .fi .SH Internal packet attributes .PP In addition to being able to filter on very specific network and transport header fields, it is possible to filter on other attributes that IPFilter attaches to a packet. These attributes are placed in a rule after the keyword "with", as can be seen with frags and frag-body above. The following is a list of the other attributes available: .HP oow the packet's IP addresses and TCP ports match an existing entry in the state table but the sequence numbers indicate that it is outside of the accepted window. .IP .nf block return-rst in quick proto tcp from any to any with not oow .fi .HP bcast this is set by IPFilter when it receives notification that the link layer packet was a broadcast packet. No checking of the IP addresses is performned to determine if it is a broadcast destination or not. .IP .nf block in quick proto udp all with bcast .fi .HP mcast this is set by IPFilter when it receives notification that the link layer packet was a multicast packet. No checking of the IP addresses is performned to determine if it is a multicast destination or not. .IP .nf pass in quick proto udp from any to any port = dns with mcast .fi .HP mbcast can be used to match a packet that is either a multicast or broadcast packet at the link layer, as indicated by the operating system. .IP .nf pass in quick proto udp from any to any port = ntp with mbcast .fi .HP nat the packet positively matched a NAT table entry. .HP bad sanity checking of the packet failed. This could indicate that the layer 3/4 headers are not properly formed. .HP bad-src when reverse path verification is enabled, this flag will be set when the interface the packet is received on does not match that which would be used to send a packet out of to the source address in the received packet. .HP bad-nat an attempt to perform NAT on the packet failed. .HP not each one of the attributes matched using the "with" keyword can also be looked for to not be present. For example, to only allow in good packets, I can do this: .PP .nf block in all pass in all with not bad .fi .SH Tuning IPFilter .PP The ipf.conf file can also be used to tune the behaviour of IPFilter, allowing, for example, timeouts for the NAT/state table(s) to be set along with their sizes. The presence and names of tunables may change from one release of IPFilter to the next. The tunables that can be changed via ipf.conf is the same as those that can be seen and modified using the -T command line option to ipf(8). .PP NOTE: When parsing ipf.conf, ipf(8) will apply the settings before loading any rules. Thus if your settings are at the top, these may be applied whilst the rules not applied if there is an error further down in the configuration file. .PP To set one of the values below, the syntax is simple: "set", followed by the name of the tuneable to set and then the value to set it to. .PP .nf set state_max 9999; set state_size 10101; .fi .PP A list of the currently available variables inside IPFilter that may be tuned from ipf.conf are as follows: .HP active -set through -s command line switch of ipf(8). See ipf(8) for detals. +set through -s command line switch of ipf(8). See ipf(8) for details. .HP chksrc when set, enables reverse path verification on source addresses and for filters to match packets with bad-src attribute. .HP control_forwarding when set turns off kernel forwarding when IPFilter is disabled or unloaded. .HP default_pass the default policy - whether packets are blocked or passed, etc - is represented by the value of this variable. It is a bit field and the bits that can be set are found in . It is not recommended to tune this value directly. .HP ftp_debug set the debugging level of the in-kernel FTP proxy. Debug messages will be printed to the system console. .HP ftp_forcepasv when set the FTP proxy must see a PASV/EPSV command before creating the state/NAT entries for the 227 response. .HP ftp_insecure when set the FTP proxy will not wait for a user to login before allowing data connections to be created. .HP ftp_pasvonly when set the proxy will not create state/NAT entries for when it sees either the PORT or EPRT command. .HP ftp_pasvrdr when enabled causes the FTP proxy to create very insecure NAT/state entries that will allow any connection between the client and server hosts when a 227 reply is seen. Use with extreme caution. .HP ftp_single_xfer when set the FTP proxy will only allow one data connection at a time. .HP hostmap_size sets the size of the hostmap table used by NAT to store address mappings for use with sticky rules. .HP icmp_ack_timeout default timeout used for ICMP NAT/state when a reply packet is seen for an ICMP state that already exists .HP icmp_minfragmtu sets the minimum MTU that is considered acceptable in an ICMP error before deciding it is a bad packet. .HP icmp_timeout default timeout used for ICMP NAT/state when the packet matches the rule .HP ip_timeout default timeout used for NAT/state entries that are not TCP/UDP/ICMP. .HP ipf_flags .HP ips_proxy_debug this sets the debugging level for the proxy support code. When enabled, debugging messages will be printed to the system console. .HP log_all when set it changes the behaviour of "log body" to log the entire packet rather than just the first 128 bytes. .HP log_size sets the size of the in-kernel log buffer in bytes. .HP log_suppress when set, IPFilter will check to see if the packet it is logging is similar to the one it previously logged and if so, increases -the occurance count for that packet. The previously logged packet +the occurrence count for that packet. The previously logged packet must not have yet been read by ipmon(8). .HP min_ttl is used to set the TTL value that packets below will be marked with the low-ttl attribute. .HP nat_doflush if set it will cause the NAT code to do a more aggressive flush of the NAT table at the next opportunity. Once the flush has been done, the value is reset to 0. .HP nat_lock this should only be changed using ipfs(8) .HP nat_logging when set, NAT will create log records that can be read from /dev/ipnat. .HP nat_maxbucket maximum number of entries allowed to exist in each NAT hash bucket. This prevents an attacker trying to load up the hash table with entries in a single bucket, reducing performance. .HP nat_rules_size size of the hash table to store map rules. .HP nat_table_max maximum number of entries allowed into the NAT table .HP nat_table_size size of the hash table used for NAT .HP nat_table_wm_high when the fill percentage of the NAT table exceeds this mark, more aggressive flushing is enabled. .HP nat_table_wm_low -this sets the percentage at which the NAT table's agressive flushing -will turn itself off at. +this sets the percentage at which the NAT table's aggressive flushing +will turn itself off. .HP rdr_rules_size size of the hash table to store rdr rules. .HP state_lock this should only be changed using ipfs(8) .HP state_logging when set, the stateful filtering will create log records that can be read from /dev/ipstate. .HP state_max maximum number of entries allowed into the state table .HP state_maxbucket maximum number of entries allowed to exist in each state hash bucket. This prevents an attacker trying to load up the hash table with entries in a single bucket, reducing performance. .HP state_size size of the hash table used for stateful filtering .HP state_wm_freq -this controls how often the agressive flushing should be run once the +this controls how often the aggressive flushing should be run once the state table exceeds state_wm_high in percentage full. .HP state_wm_high when the fill percentage of the state table exceeds this mark, more aggressive flushing is enabled. .HP state_wm_low -this sets the percentage at which the state table's agressive flushing +this sets the percentage at which the state table's aggressive flushing will turn itself off at. .HP tcp_close_wait timeout used when a TCP state entry reaches the FIN_WAIT_2 state. .HP tcp_closed timeout used when a TCP state entry is ready to be removed after either a RST packet is seen. .HP tcp_half_closed timeout used when a TCP state entry reaches the CLOSE_WAIT state. .HP tcp_idle_timeout timeout used when a TCP state entry reaches the ESTABLISHED state. .HP tcp_last_ack timeout used when a TCP NAT/state entry reaches the LAST_ACK state. .HP tcp_syn_received timeout applied to a TCP NAT/state entry after SYN-ACK packet has been seen. .HP tcp_syn_sent timeout applied to a TCP NAT/state entry after SYN packet has been seen. .HP tcp_time_wait timeout used when a TCP NAT/state entry reaches the TIME_WAIT state. .HP tcp_timeout timeout used when a TCP NAT/state entry reaches either the half established state (one ack is seen after a SYN-ACK) or one side is in FIN_WAIT_1. .HP udp_ack_timeout default timeout used for UDP NAT/state when a reply packet is seen for a UDP state that already exists .HP udp_timeout default timeout used for UDP NAT/state when the packet matches the rule .HP update_ipid when set, turns on changing the IP id field in NAT'd packets to a random number. .SS Table of visible variables .PP A list of all of the tunables, their minimum, maximum and current values is as follows. .PP .nf Name Min Max Current active 0 0 0 chksrc 0 1 0 control_forwarding 0 1 0 default_pass 0 MAXUINT 134217730 ftp_debug 0 10 0 ftp_forcepasv 0 1 1 ftp_insecure 0 1 0 ftp_pasvonly 0 1 0 ftp_pasvrdr 0 1 0 ftp_single_xfer 0 1 0 hostmap_size 1 MAXINT 2047 icmp_ack_timeout 1 MAXINT 12 icmp_minfragmtu 0 1 68 icmp_timeout 1 MAXINT 120 ip_timeout 1 MAXINT 120 ipf_flags 0 MAXUINT 0 ips_proxy_debug 0 10 0 log_all 0 1 0 log_size 0 524288 32768 log_suppress 0 1 1 min_ttl 0 1 4 nat_doflush 0 1 0 nat_lock 0 1 0 nat_logging 0 1 1 nat_maxbucket 1 MAXINT 22 nat_rules_size 1 MAXINT 127 nat_table_max 1 MAXINT 30000 nat_table_size 1 MAXINT 2047 nat_table_wm_high 2 100 99 nat_table_wm_low 1 99 90 rdr_rules_size 1 MAXINT 127 state_lock 0 1 0 state_logging 0 1 1 state_max 1 MAXINT 4013 state_maxbucket 1 MAXINT 26 state_size 1 MAXINT 5737 state_wm_freq 2 999999 20 state_wm_high 2 100 99 state_wm_low 1 99 90 tcp_close_wait 1 MAXINT 480 tcp_closed 1 MAXINT 60 tcp_half_closed 1 MAXINT 14400 tcp_idle_timeout 1 MAXINT 864000 tcp_last_ack 1 MAXINT 60 tcp_syn_received 1 MAXINT 480 tcp_syn_sent 1 MAXINT 480 tcp_time_wait 1 MAXINT 480 tcp_timeout 1 MAXINT 480 udp_ack_timeout 1 MAXINT 24 udp_timeout 1 MAXINT 240 update_ipid 0 1 0 .fi .SH Calling out to internal functions .PP IPFilter provides a pair of functions that can be called from a rule that allow for a single rule to jump out to a group rather than walk through a list of rules to find the group. If you've got multiple networks, each with its own group of rules, this feature may help provide better filtering performance. .PP The lookup to find which rule group to jump to is done on either the source address or the destination address but not both. .PP In this example below, we are blocking all packets by default but then doing a lookup on the source address from group 1010. The two rules in the ipf.conf section are lone members of their group. For an incoming packet that is from 1.1.1.1, it will go through three rules: (1) the block rule, (2) the call rule and (3) the pass rule for group 1020. For a packet that is from 3.3.2.2, it will also go through three rules: (1) the block rule, (2) the call rule and (3) the pass rule for group 1030. Should a packet from 3.1.1.1 arrive, it will be blocked as it does not match any of the entries in group 1010, leaving it to only match the first rule. .PP .nf from ipf.conf ------------- block in all call now srcgrpmap/1010 in all pass in proto tcp from any to any port = 80 group 1020 pass in proto icmp all icmp-type echo group 1030 from ippool.conf ---------------- group-map in role=ipf number=1010 { 1.1.1.1 group = 1020, 3.3.0.0/16 group = 1030; }; .fi .SS IPFilter matching expressions .PP An experimental feature that has been added to filter rules is to use the same expression matching that is available with various commands to flush and list state/NAT table entries. The use of such an expression precludes the filter rule from using the normal IP header matching. .PP .nf pass in exp { "tcp.sport 23 or tcp.sport 50" } keep state .fi .SS Filter rules with BPF .PP On platforms that have the BPF built into the kernel, IPFilter can be built to allow BPF expressions in filter rules. This allows for packet matching to be on arbitrary data in the packt. The use of a BPF expression replaces all of the other protocol header matching done by IPFilter. .PP .nf pass in bpf-v4 { "tcp and (src port 23 or src port 50)" } \\ keep state .fi .PP These rules tend to be write-only because the act of compiling the filter expression into the BPF instructions loaded into the kernel can make it difficut to accurately reconstruct the original text filter. The end result is that while ipf.conf() can be easy to read, understanding the output from ipfstat might not be. .SH VARIABLES .PP This configuration file, like all others used with IPFilter, supports the use of variable substitution throughout the text. .PP .nf nif="ppp0"; pass in on $nif from any to any .fi .PP would become .PP .nf pass in on ppp0 from any to any .fi .PP Variables can be used recursively, such as 'foo="$bar baz";', so long as $bar exists when the parser reaches the assignment for foo. .PP See .B ipf(8) for instructions on how to define variables to be used from a shell environment. .DT .SH FILES /dev/ipf /etc/ipf.conf .br /usr/share/examples/ipfilter Directory with examples. .SH SEE ALSO ipf(8), ipfstat(8), ippool.conf(5), ippool(8) diff --git a/sbin/ipf/ipf/ipfilter.4 b/sbin/ipf/ipf/ipfilter.4 index f262f711b8ff..39676e3c1dae 100644 --- a/sbin/ipf/ipf/ipfilter.4 +++ b/sbin/ipf/ipf/ipfilter.4 @@ -1,239 +1,239 @@ .\" .TH IP\ FILTER 4 .SH NAME ipfilter \- Introduction to IP packet filtering .SH DESCRIPTION IP Filter is a TCP/IP packet filter, suitable for use in a firewall environment. To use, it can either be used as a loadable kernel module or incorporated into your UNIX kernel; use as a loadable kernel module where possible is highly recommended. Scripts are provided to install and patch system files, as required. .SH FEATURES The IP packet filter can: .IP explicitly deny/permit any packet from passing through .IP distinguish between various interfaces .IP filter by IP networks or hosts .IP selectively filter any IP protocol .IP selectively filter fragmented IP packets .IP selectively filter packets with IP options .IP send back an ICMP error/TCP reset for blocked packets .IP keep packet state information for TCP, UDP and ICMP packet flows .IP keep fragment state information for any IP packet, applying the same rule to all fragments. .IP act as a Network Address Translator (NAT) .IP use redirection to setup true transparent proxy connections .IP provide packet header details to a user program for authentication .IP in addition, supports temporary storage of pre-authenticated rules for passing packets through .PP Special provision is made for the three most common Internet protocols, TCP, UDP and ICMP. The IP Packet filter allows filtering of: .IP Inverted host/net matchingTCP/UDP packets by port number or a port number range .IP ICMP packets by type/code .IP "established" TCP packets .IP On any arbitrary combination of TCP flags .IP "short" (fragmented) IP packets with incomplete headers can be filtered .IP any of the 19 IP options or 8 registered IP security classes TOS (Type of Service) field in packets .PP To keep track of the performance of the IP packet filter, a logging device is used which supports logging of: .IP the TCP/UDP/ICMP and IP packet headers .IP the first 128 bytes of the packet (including headers) .PP A packet can be logged when: .IP it is successfully passed through .IP it is blocked from passing through .IP it matches a rule setup to look for suspicious packets .PP IP Filter keeps its own set of statistics on: .IP packets blocked .IP packets (and bytes!) used for accounting .IP packets passed .IP packets logged .IP attempts to log which failed (buffer full) .IP and much more, for packets going both in and out. .SH Tools The current implementation provides a small set of tools, which can easily be used and integrated with regular unix shells and tools. A brief description of the tools provided: .PP ipf(8) reads in a set of rules, from either stdin or a file, and adds them to the kernels current list (appending them). It can also be used to flush the current filter set or delete individual filter rules. The file format is described in ipf(5). .PP ipfs(8) is a utility to temporarily lock the IP Filter kernel tables (state tables and NAT mappings) and write them to disk. After that the system can be rebooted, and ipfs can be used to read these tables from disk and restore them into the kernel. This way the system can be rebooted without the connections being terminated. .PP ipfstat(8) interrogates the kernel for statistics on packet filtering, so far, and retrieves the list of filters in operation for inbound and outbound packets. .PP ipftest(1) reads in a filter rule file and then applies sample IP packets to the rule file. This allows for testing of filter list and examination of how a packet is passed along through it. .PP ipmon(8) reads buffered data from the logging device (default is /dev/ipl) for output to either: .IP screen (standard output) .IP file .IP syslog .PP ipsend(1) -generates arbitary IP packets for ethernet connected machines. +generates arbitrary IP packets for ethernet connected machines. .PP ipresend(1) reads in a data file of saved IP packets (ie snoop/tcpdump/etherfind output) and sends it back across the network. .PP iptest(1) contains a set of test "programs" which send out a series of IP packets, aimed at testing the strength of the TCP/IP stack at which it is aimed at. WARNING: this may crash machine(s) targeted! .PP ipnat(8) reads in a set of rules, from either stdin or a file and adds them to the kernels current list of active NAT rules. NAT rules can also be deleted using ipnat. The format of the configuration file to be used with ipnat is described in ipnat(5). .PP For use in your own programs (e.g. for writing of transparent application proxies), the programming interface and the associated ioctl's are documented in ipf(4). Documentation on ioctl's and the format of data saved to the logging character device is provided in ipl(4) so that you may develop your own applications to work with or in place of any of the above. Similar, the interface to the NAT code is documented in ipnat(4). .SH PACKET PROCESSING FLOW The following diagram illustrates the flow of TCP/IP packets through the various stages introduced by IP Filter. .PP .nf IN | V +-------------------------+--------------------------+ | | | | V | | Network Address Translation | | | | | authenticated | | | +-------<---------+ | | | | | | | V | | V IP Accounting | | | | | | | V | | | Fragment Cache Check--+ | | | | | | | V V V | | | Packet State Check-->+ | | | | | | | | +->--+ | | | | | | | V | | | V groups IP Filtering V | | | | | | | | | | +--<-+ | | | | | | | | | +---------------->|<-----------+ | | | | | V | | +---<----+ | | | | | | function | | | | V | | +--->----+ | | | | | V | +--|---<--- fast-route ---<--+ | | | | | | | V | | +-------------------------+--------------------------+ | | | pass only | | | V V [KERNEL TCP/IP Processing] | | | +-------------------------+--------------------------+ | | | | | | V | | | Fragment Cache Check--+ | | | | | | | | V V | | | Packet State Check-->+ | | | | | | | | V | | V | IP Filtering | | | | | V | | | |<-----------+ | | | V | | | IP Accounting | | | | | | | V | | | Network Address Translation | | | | | | | V | | +-------------------------+--------------------------+ | | | pass only V | +--------------------------->| V OUT .fi .SH MORE INFORMATION The IP Filter FAQ can be found at https://www.phildev.net/ipf/ .SH SEE ALSO ipf(4), ipf(5), ipf(8), ipfilter(5), ipfs(8), ipfstat(8), ipftest(1), ipl(4), ipmon(8), ipnat(8), ipnat(4), diff --git a/sbin/ipf/ipfstat/ipfstat.c b/sbin/ipf/ipfstat/ipfstat.c index ba9556f5421c..fd0ac83097a0 100644 --- a/sbin/ipf/ipfstat/ipfstat.c +++ b/sbin/ipf/ipfstat/ipfstat.c @@ -1,2310 +1,2310 @@ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ #include #include #include # include #include #if defined(sun) && defined(__SVR4) # include #endif #include "ipf.h" #include "netinet/ipl.h" #if defined(STATETOP) # if defined(sun) && defined(__SVR4) # include # endif # include # include # include # include # include # if SOLARIS || defined(__NetBSD__) # ifdef ERR # undef ERR # endif # include # else /* SOLARIS */ # include # endif /* SOLARIS */ #endif /* STATETOP */ #include "kmem.h" #if defined(__NetBSD__) # include #endif extern char *optarg; extern int optind; extern int opterr; #define PRINTF (void)printf #define FPRINTF (void)fprintf static char *filters[4] = { "ipfilter(in)", "ipfilter(out)", "ipacct(in)", "ipacct(out)" }; static int state_logging = -1; static wordtab_t *state_fields = NULL; int nohdrfields = 0; int opts = 0; #ifdef USE_INET6 int use_inet4 = 0; int use_inet6 = 0; #endif int live_kernel = 1; int state_fd = -1; int ipf_fd = -1; int auth_fd = -1; int nat_fd = -1; frgroup_t *grtop = NULL; frgroup_t *grtail = NULL; char *blockreasons[FRB_MAX_VALUE + 1] = { "packet blocked", "log rule failure", "pps rate exceeded", "jumbogram", "makefrip failed", "cannot add state", "IP ID update failed", "log-or-block failed", "decapsulate failure", "cannot create new auth entry", "packet queued for auth", "buffer coalesce failure", "buffer pullup failure", "auth feedback", "bad fragment", "IPv4 NAT failure", "IPv6 NAT failure" }; #ifdef STATETOP #define STSTRSIZE 80 #define STGROWSIZE 16 #define HOSTNMLEN 40 #define STSORT_PR 0 #define STSORT_PKTS 1 #define STSORT_BYTES 2 #define STSORT_TTL 3 #define STSORT_SRCIP 4 #define STSORT_SRCPT 5 #define STSORT_DSTIP 6 #define STSORT_DSTPT 7 #define STSORT_MAX STSORT_DSTPT #define STSORT_DEFAULT STSORT_BYTES typedef struct statetop { i6addr_t st_src; i6addr_t st_dst; u_short st_sport; u_short st_dport; u_char st_p; u_char st_v; u_char st_state[2]; U_QUAD_T st_pkts; U_QUAD_T st_bytes; u_long st_age; } statetop_t; #endif int main(int, char *[]); static int fetchfrag(int, int, ipfr_t *); static void showstats(friostat_t *, u_32_t); static void showfrstates(ipfrstat_t *, u_long); static void showlist(friostat_t *); static void showstatestats(ips_stat_t *); static void showipstates(ips_stat_t *, int *); static void showauthstates(ipf_authstat_t *); static void showtqtable_live(int); static void showgroups(friostat_t *); static void usage(char *); static int state_matcharray(ipstate_t *, int *); static int printlivelist(friostat_t *, int, int, frentry_t *, char *, char *); static void printdeadlist(friostat_t *, int, int, frentry_t *, char *, char *); static void printside(char *, ipf_statistics_t *); static void parse_ipportstr(const char *, i6addr_t *, int *); static void ipfstate_live(char *, friostat_t **, ips_stat_t **, ipfrstat_t **, ipf_authstat_t **, u_32_t *); static void ipfstate_dead(char *, friostat_t **, ips_stat_t **, ipfrstat_t **, ipf_authstat_t **, u_32_t *); static ipstate_t *fetchstate(ipstate_t *, ipstate_t *); #ifdef STATETOP static void topipstates(i6addr_t, i6addr_t, int, int, int, int, int, int, int *); static void sig_break(int); static void sig_resize(int); static char *getip(int, i6addr_t *); static char *ttl_to_string(long); static int sort_p(const void *, const void *); static int sort_pkts(const void *, const void *); static int sort_bytes(const void *, const void *); static int sort_ttl(const void *, const void *); static int sort_srcip(const void *, const void *); static int sort_srcpt(const void *, const void *); static int sort_dstip(const void *, const void *); static int sort_dstpt(const void *, const void *); #endif static void usage(char *name) { #ifdef USE_INET6 fprintf(stderr, "Usage: %s [-46aAdfghIilnoRsv]\n", name); #else fprintf(stderr, "Usage: %s [-4aAdfghIilnoRsv]\n", name); #endif fprintf(stderr, " %s [-M corefile] [-N symbol-list]\n", name); #ifdef STATETOP #ifdef USE_INET6 fprintf(stderr, " %s -t [-46C] ", name); #else fprintf(stderr, " %s -t [-4C] ", name); #endif #endif fprintf(stderr, "[-D destination address] [-P protocol] [-S source address] [-T refresh time]\n"); exit(1); } int main(int argc, char *argv[]) { ipf_authstat_t frauthst; ipf_authstat_t *frauthstp = &frauthst; friostat_t fio; friostat_t *fiop = &fio; ips_stat_t ipsst; ips_stat_t *ipsstp = &ipsst; ipfrstat_t ifrst; ipfrstat_t *ifrstp = &ifrst; char *options; char *kern = NULL; char *memf = NULL; int c; int myoptind; int *filter = NULL; int protocol = -1; /* -1 = wild card for any protocol */ int refreshtime = 1; /* default update time */ int sport = -1; /* -1 = wild card for any source port */ int dport = -1; /* -1 = wild card for any dest port */ int topclosed = 0; /* do not show closed tcp sessions */ i6addr_t saddr, daddr; u_32_t frf; #ifdef USE_INET6 options = "46aACdfghIilnostvD:m:M:N:O:P:RS:T:"; #else options = "4aACdfghIilnostvD:m:M:N:O:P:RS:T:"; #endif saddr.in4.s_addr = INADDR_ANY; /* default any v4 source addr */ daddr.in4.s_addr = INADDR_ANY; /* default any v4 dest addr */ #ifdef USE_INET6 saddr.in6 = in6addr_any; /* default any v6 source addr */ daddr.in6 = in6addr_any; /* default any v6 dest addr */ #endif /* Don't warn about invalid flags when we run getopt for the 1st time */ opterr = 0; /* * Parse these two arguments now lest there be any buffer overflows * in the parsing of the rest. */ myoptind = optind; while ((c = getopt(argc, argv, options)) != -1) { switch (c) { case 'M' : memf = optarg; live_kernel = 0; break; case 'N' : kern = optarg; live_kernel = 0; break; } } optind = myoptind; if (live_kernel == 1) { if ((state_fd = open(IPSTATE_NAME, O_RDONLY)) == -1) { perror("open(IPSTATE_NAME)"); exit(-1); } if ((auth_fd = open(IPAUTH_NAME, O_RDONLY)) == -1) { perror("open(IPAUTH_NAME)"); exit(-1); } if ((nat_fd = open(IPNAT_NAME, O_RDONLY)) == -1) { perror("open(IPAUTH_NAME)"); exit(-1); } if ((ipf_fd = open(IPL_NAME, O_RDONLY)) == -1) { fprintf(stderr, "open(%s)", IPL_NAME); perror(""); exit(-1); } } if (kern != NULL || memf != NULL) { (void)setgid(getgid()); (void)setuid(getuid()); } if (live_kernel == 1) { (void) checkrev(IPL_NAME); } else { if (openkmem(kern, memf) == -1) exit(-1); } (void)setgid(getgid()); (void)setuid(getuid()); opterr = 1; while ((c = getopt(argc, argv, options)) != -1) { switch (c) { #ifdef USE_INET6 case '4' : use_inet4 = 1; break; case '6' : use_inet6 = 1; break; #endif case 'a' : opts |= OPT_ACCNT|OPT_SHOWLIST; break; case 'A' : opts |= OPT_AUTHSTATS; break; case 'C' : topclosed = 1; break; case 'd' : opts |= OPT_DEBUG; break; case 'D' : parse_ipportstr(optarg, &daddr, &dport); break; case 'f' : opts |= OPT_FRSTATES; break; case 'g' : opts |= OPT_GROUPS; break; case 'h' : opts |= OPT_HITS; break; case 'i' : opts |= OPT_INQUE|OPT_SHOWLIST; break; case 'I' : opts |= OPT_INACTIVE; break; case 'l' : opts |= OPT_SHOWLIST; break; case 'm' : filter = parseipfexpr(optarg, NULL); if (filter == NULL) { - fprintf(stderr, "Error parseing '%s'\n", + fprintf(stderr, "Error parsing '%s'\n", optarg); exit(1); } break; case 'M' : break; case 'N' : break; case 'n' : opts |= OPT_SHOWLINENO; break; case 'o' : opts |= OPT_OUTQUE|OPT_SHOWLIST; break; case 'O' : state_fields = parsefields(statefields, optarg); break; case 'P' : protocol = getproto(optarg); if (protocol == -1) { fprintf(stderr, "%s: Invalid protocol: %s\n", argv[0], optarg); exit(-2); } break; case 'R' : opts |= OPT_NORESOLVE; break; case 's' : opts |= OPT_IPSTATES; break; case 'S' : parse_ipportstr(optarg, &saddr, &sport); break; case 't' : #ifdef STATETOP opts |= OPT_STATETOP; break; #else fprintf(stderr, "%s: state top facility not compiled in\n", argv[0]); exit(-2); #endif case 'T' : if (!sscanf(optarg, "%d", &refreshtime) || (refreshtime <= 0)) { fprintf(stderr, "%s: Invalid refreshtime < 1 : %s\n", argv[0], optarg); exit(-2); } break; case 'v' : opts |= OPT_VERBOSE; break; default : usage(argv[0]); break; } } #ifdef USE_INET6 if ((use_inet4 || use_inet6) && !(opts & (OPT_INQUE | OPT_OUTQUE | OPT_STATETOP))) { #ifdef STATETOP FPRINTF(stderr, "No -i, -o, or -t given with -4 or -6\n"); #else FPRINTF(stderr, "No -i or -o given with -4 or -6\n"); #endif exit(-2); } if (use_inet4 == 0 && use_inet6 == 0) use_inet4 = use_inet6 = 1; #endif if (live_kernel == 1) { bzero((char *)&fio, sizeof(fio)); bzero((char *)&ipsst, sizeof(ipsst)); bzero((char *)&ifrst, sizeof(ifrst)); ipfstate_live(IPL_NAME, &fiop, &ipsstp, &ifrstp, &frauthstp, &frf); } else { ipfstate_dead(kern, &fiop, &ipsstp, &ifrstp, &frauthstp, &frf); } if (opts & OPT_IPSTATES) { showipstates(ipsstp, filter); } else if (opts & OPT_SHOWLIST) { showlist(fiop); if ((opts & OPT_OUTQUE) && (opts & OPT_INQUE)){ opts &= ~OPT_OUTQUE; showlist(fiop); } } else if (opts & OPT_FRSTATES) showfrstates(ifrstp, fiop->f_ticks); #ifdef STATETOP else if (opts & OPT_STATETOP) topipstates(saddr, daddr, sport, dport, protocol, #ifdef USE_INET6 use_inet6 && use_inet4 ? 0 : use_inet6 && !use_inet4 ? 6 : 4, #else 4, #endif #endif refreshtime, topclosed, filter); else if (opts & OPT_AUTHSTATS) showauthstates(frauthstp); else if (opts & OPT_GROUPS) showgroups(fiop); else showstats(fiop, frf); return (0); } /* * Fill in the stats structures from the live kernel, using a combination * of ioctl's and copying directly from kernel memory. */ static void ipfstate_live(char *device, friostat_t **fiopp, ips_stat_t **ipsstpp, ipfrstat_t **ifrstpp, ipf_authstat_t **frauthstpp, u_32_t *frfp) { ipfobj_t ipfo; if (checkrev(device) == -1) { fprintf(stderr, "User/kernel version check failed\n"); exit(1); } if ((opts & OPT_AUTHSTATS) == 0) { bzero((caddr_t)&ipfo, sizeof(ipfo)); ipfo.ipfo_rev = IPFILTER_VERSION; ipfo.ipfo_type = IPFOBJ_IPFSTAT; ipfo.ipfo_size = sizeof(friostat_t); ipfo.ipfo_ptr = (void *)*fiopp; if (ioctl(ipf_fd, SIOCGETFS, &ipfo) == -1) { ipferror(ipf_fd, "ioctl(ipf:SIOCGETFS)"); exit(-1); } if (ioctl(ipf_fd, SIOCGETFF, frfp) == -1) ipferror(ipf_fd, "ioctl(SIOCGETFF)"); } if ((opts & OPT_IPSTATES) != 0) { bzero((caddr_t)&ipfo, sizeof(ipfo)); ipfo.ipfo_rev = IPFILTER_VERSION; ipfo.ipfo_type = IPFOBJ_STATESTAT; ipfo.ipfo_size = sizeof(ips_stat_t); ipfo.ipfo_ptr = (void *)*ipsstpp; if ((ioctl(state_fd, SIOCGETFS, &ipfo) == -1)) { ipferror(state_fd, "ioctl(state:SIOCGETFS)"); exit(-1); } if (ioctl(state_fd, SIOCGETLG, &state_logging) == -1) { ipferror(state_fd, "ioctl(state:SIOCGETLG)"); exit(-1); } } if ((opts & OPT_FRSTATES) != 0) { bzero((caddr_t)&ipfo, sizeof(ipfo)); ipfo.ipfo_rev = IPFILTER_VERSION; ipfo.ipfo_type = IPFOBJ_FRAGSTAT; ipfo.ipfo_size = sizeof(ipfrstat_t); ipfo.ipfo_ptr = (void *)*ifrstpp; if (ioctl(ipf_fd, SIOCGFRST, &ipfo) == -1) { ipferror(ipf_fd, "ioctl(SIOCGFRST)"); exit(-1); } } if (opts & OPT_DEBUG) PRINTF("opts %#x name %s\n", opts, device); if ((opts & OPT_AUTHSTATS) != 0) { bzero((caddr_t)&ipfo, sizeof(ipfo)); ipfo.ipfo_rev = IPFILTER_VERSION; ipfo.ipfo_type = IPFOBJ_AUTHSTAT; ipfo.ipfo_size = sizeof(ipf_authstat_t); ipfo.ipfo_ptr = (void *)*frauthstpp; if (ioctl(auth_fd, SIOCATHST, &ipfo) == -1) { ipferror(auth_fd, "ioctl(SIOCATHST)"); exit(-1); } } } /* * Build up the stats structures from data held in the "core" memory. * This is mainly useful when looking at data in crash dumps and ioctl's * just won't work any more. */ static void ipfstate_dead( char *kernel, friostat_t **fiopp, ips_stat_t **ipsstpp, ipfrstat_t **ifrstpp, ipf_authstat_t **frauthstpp, u_32_t *frfp) { static ipf_authstat_t frauthst, *frauthstp; static ipftq_t ipstcptab[IPF_TCP_NSTATES]; static ips_stat_t ipsst, *ipsstp; static ipfrstat_t ifrst, *ifrstp; static friostat_t fio, *fiop; int temp; void *rules[2][2]; struct nlist deadlist[44] = { { "ipf_auth_stats", 0, 0, 0, 0 }, /* 0 */ { "fae_list", 0, 0, 0, 0 }, { "ipauth", 0, 0, 0, 0 }, { "ipf_auth_list", 0, 0, 0, 0 }, { "ipf_auth_start", 0, 0, 0, 0 }, { "ipf_auth_end", 0, 0, 0, 0 }, /* 5 */ { "ipf_auth_next", 0, 0, 0, 0 }, { "ipf_auth", 0, 0, 0, 0 }, { "ipf_auth_used", 0, 0, 0, 0 }, { "ipf_auth_size", 0, 0, 0, 0 }, { "ipf_auth_defaultage", 0, 0, 0, 0 }, /* 10 */ { "ipf_auth_pkts", 0, 0, 0, 0 }, { "ipf_auth_lock", 0, 0, 0, 0 }, { "frstats", 0, 0, 0, 0 }, { "ips_stats", 0, 0, 0, 0 }, { "ips_num", 0, 0, 0, 0 }, /* 15 */ { "ips_wild", 0, 0, 0, 0 }, { "ips_list", 0, 0, 0, 0 }, { "ips_table", 0, 0, 0, 0 }, { "ipf_state_max", 0, 0, 0, 0 }, { "ipf_state_size", 0, 0, 0, 0 }, /* 20 */ { "ipf_state_doflush", 0, 0, 0, 0 }, { "ipf_state_lock", 0, 0, 0, 0 }, { "ipfr_heads", 0, 0, 0, 0 }, { "ipfr_nattab", 0, 0, 0, 0 }, { "ipfr_stats", 0, 0, 0, 0 }, /* 25 */ { "ipfr_inuse", 0, 0, 0, 0 }, { "ipf_ipfrttl", 0, 0, 0, 0 }, { "ipf_frag_lock", 0, 0, 0, 0 }, { "ipfr_timer_id", 0, 0, 0, 0 }, { "ipf_nat_lock", 0, 0, 0, 0 }, /* 30 */ { "ipf_rules", 0, 0, 0, 0 }, { "ipf_acct", 0, 0, 0, 0 }, { "ipl_frouteok", 0, 0, 0, 0 }, { "ipf_running", 0, 0, 0, 0 }, { "ipf_groups", 0, 0, 0, 0 }, /* 35 */ { "ipf_active", 0, 0, 0, 0 }, { "ipf_pass", 0, 0, 0, 0 }, { "ipf_flags", 0, 0, 0, 0 }, { "ipf_state_logging", 0, 0, 0, 0 }, { "ips_tqtqb", 0, 0, 0, 0 }, /* 40 */ { NULL, 0, 0, 0, 0 } }; frauthstp = &frauthst; ipsstp = &ipsst; ifrstp = &ifrst; fiop = &fio; *frfp = 0; *fiopp = fiop; *ipsstpp = ipsstp; *ifrstpp = ifrstp; *frauthstpp = frauthstp; bzero((char *)fiop, sizeof(*fiop)); bzero((char *)ipsstp, sizeof(*ipsstp)); bzero((char *)ifrstp, sizeof(*ifrstp)); bzero((char *)frauthstp, sizeof(*frauthstp)); if (nlist(kernel, deadlist) == -1) { fprintf(stderr, "nlist error\n"); return; } /* * This is for SIOCGETFF. */ kmemcpy((char *)frfp, (u_long)deadlist[40].n_value, sizeof(*frfp)); /* * f_locks is a combination of the lock variable from each part of * ipfilter (state, auth, nat, fragments). */ kmemcpy((char *)fiop, (u_long)deadlist[13].n_value, sizeof(*fiop)); kmemcpy((char *)&fiop->f_locks[0], (u_long)deadlist[22].n_value, sizeof(fiop->f_locks[0])); kmemcpy((char *)&fiop->f_locks[0], (u_long)deadlist[30].n_value, sizeof(fiop->f_locks[1])); kmemcpy((char *)&fiop->f_locks[2], (u_long)deadlist[28].n_value, sizeof(fiop->f_locks[2])); kmemcpy((char *)&fiop->f_locks[3], (u_long)deadlist[12].n_value, sizeof(fiop->f_locks[3])); /* * Get pointers to each list of rules (active, inactive, in, out) */ kmemcpy((char *)&rules, (u_long)deadlist[31].n_value, sizeof(rules)); fiop->f_fin[0] = rules[0][0]; fiop->f_fin[1] = rules[0][1]; fiop->f_fout[0] = rules[1][0]; fiop->f_fout[1] = rules[1][1]; /* * Now get accounting rules pointers. */ kmemcpy((char *)&rules, (u_long)deadlist[33].n_value, sizeof(rules)); fiop->f_acctin[0] = rules[0][0]; fiop->f_acctin[1] = rules[0][1]; fiop->f_acctout[0] = rules[1][0]; fiop->f_acctout[1] = rules[1][1]; /* * A collection of "global" variables used inside the kernel which * are all collected in friostat_t via ioctl. */ kmemcpy((char *)&fiop->f_froute, (u_long)deadlist[33].n_value, sizeof(fiop->f_froute)); kmemcpy((char *)&fiop->f_running, (u_long)deadlist[34].n_value, sizeof(fiop->f_running)); kmemcpy((char *)&fiop->f_groups, (u_long)deadlist[35].n_value, sizeof(fiop->f_groups)); kmemcpy((char *)&fiop->f_active, (u_long)deadlist[36].n_value, sizeof(fiop->f_active)); kmemcpy((char *)&fiop->f_defpass, (u_long)deadlist[37].n_value, sizeof(fiop->f_defpass)); /* * Build up the state information stats structure. */ kmemcpy((char *)ipsstp, (u_long)deadlist[14].n_value, sizeof(*ipsstp)); kmemcpy((char *)&temp, (u_long)deadlist[15].n_value, sizeof(temp)); kmemcpy((char *)ipstcptab, (u_long)deadlist[40].n_value, sizeof(ipstcptab)); ipsstp->iss_active = temp; ipsstp->iss_table = (void *)deadlist[18].n_value; ipsstp->iss_list = (void *)deadlist[17].n_value; ipsstp->iss_tcptab = ipstcptab; /* * Build up the authentiation information stats structure. */ kmemcpy((char *)frauthstp, (u_long)deadlist[0].n_value, sizeof(*frauthstp)); frauthstp->fas_faelist = (void *)deadlist[1].n_value; /* * Build up the fragment information stats structure. */ kmemcpy((char *)ifrstp, (u_long)deadlist[25].n_value, sizeof(*ifrstp)); ifrstp->ifs_table = (void *)deadlist[23].n_value; ifrstp->ifs_nattab = (void *)deadlist[24].n_value; kmemcpy((char *)&ifrstp->ifs_inuse, (u_long)deadlist[26].n_value, sizeof(ifrstp->ifs_inuse)); /* * Get logging on/off switches */ kmemcpy((char *)&state_logging, (u_long)deadlist[41].n_value, sizeof(state_logging)); } static void printside(char *side, ipf_statistics_t *frs) { int i; PRINTF("%lu\t%s bad packets\n", frs->fr_bad, side); #ifdef USE_INET6 PRINTF("%lu\t%s IPv6 packets\n", frs->fr_ipv6, side); #endif PRINTF("%lu\t%s packets blocked\n", frs->fr_block, side); PRINTF("%lu\t%s packets passed\n", frs->fr_pass, side); PRINTF("%lu\t%s packets not matched\n", frs->fr_nom, side); PRINTF("%lu\t%s packets counted\n", frs->fr_acct, side); PRINTF("%lu\t%s packets short\n", frs->fr_short, side); PRINTF("%lu\t%s packets logged and blocked\n", frs->fr_bpkl, side); PRINTF("%lu\t%s packets logged and passed\n", frs->fr_ppkl, side); PRINTF("%lu\t%s fragment state kept\n", frs->fr_nfr, side); PRINTF("%lu\t%s fragment state lost\n", frs->fr_bnfr, side); PRINTF("%lu\t%s packet state kept\n", frs->fr_ads, side); PRINTF("%lu\t%s packet state lost\n", frs->fr_bads, side); PRINTF("%lu\t%s invalid source\n", frs->fr_v4_badsrc, side); PRINTF("%lu\t%s cache hits\n", frs->fr_chit, side); PRINTF("%lu\t%s cache misses\n", frs->fr_cmiss, side); PRINTF("%lu\t%s bad coalesces\n", frs->fr_badcoalesces, side); PRINTF("%lu\t%s pullups succeeded\n", frs->fr_pull[0], side); PRINTF("%lu\t%s pullups failed\n", frs->fr_pull[1], side); PRINTF("%lu\t%s TCP checksum failures\n", frs->fr_tcpbad, side); for (i = 0; i <= FRB_MAX_VALUE; i++) PRINTF("%lu\t%s block reason %s\n", frs->fr_blocked[i], side, blockreasons[i]); } /* * Display the kernel stats for packets blocked and passed and other * associated running totals which are kept. */ static void showstats( struct friostat *fp, u_32_t frf) { printside("input", &fp->f_st[0]); printside("output", &fp->f_st[1]); PRINTF("%lu\tpackets logged\n", fp->f_log_ok); PRINTF("%lu\tlog failures\n", fp->f_log_fail); PRINTF("%lu\tred-black no memory\n", fp->f_rb_no_mem); PRINTF("%lu\tred-black node maximum\n", fp->f_rb_node_max); PRINTF("%lu\tICMP replies sent\n", fp->f_st[0].fr_ret); PRINTF("%lu\tTCP RSTs sent\n", fp->f_st[1].fr_ret); PRINTF("%lu\tfastroute successes\n", fp->f_froute[0]); PRINTF("%lu\tfastroute failures\n", fp->f_froute[1]); PRINTF("%u\tIPF Ticks\n", fp->f_ticks); PRINTF("%x\tPacket log flags set:\n", frf); if (frf & FF_LOGPASS) PRINTF("\tpackets passed through filter\n"); if (frf & FF_LOGBLOCK) PRINTF("\tpackets blocked by filter\n"); if (frf & FF_LOGNOMATCH) PRINTF("\tpackets not matched by filter\n"); if (!frf) PRINTF("\tnone\n"); } /* * Print out a list of rules from the kernel, starting at the one passed. */ static int printlivelist( struct friostat *fiop, int out, int set, frentry_t *fp, char *group, char *comment) { struct frentry fb; ipfruleiter_t rule; frentry_t zero; frgroup_t *g; ipfobj_t obj; int rules; int num; rules = 0; rule.iri_inout = out; rule.iri_active = set; rule.iri_rule = &fb; rule.iri_nrules = 1; if (group != NULL) strncpy(rule.iri_group, group, FR_GROUPLEN); else rule.iri_group[0] = '\0'; bzero((char *)&zero, sizeof(zero)); bzero((char *)&obj, sizeof(obj)); obj.ipfo_rev = IPFILTER_VERSION; obj.ipfo_type = IPFOBJ_IPFITER; obj.ipfo_size = sizeof(rule); obj.ipfo_ptr = &rule; while (rule.iri_rule != NULL) { u_long array[1000]; memset(array, 0xff, sizeof(array)); fp = (frentry_t *)array; rule.iri_rule = fp; if (ioctl(ipf_fd, SIOCIPFITER, &obj) == -1) { ipferror(ipf_fd, "ioctl(SIOCIPFITER)"); num = IPFGENITER_IPF; (void) ioctl(ipf_fd,SIOCIPFDELTOK, &num); return (rules); } if (bcmp(fp, &zero, sizeof(zero)) == 0) break; if (rule.iri_rule == NULL) break; #ifdef USE_INET6 if (use_inet6 != 0 && use_inet4 == 0) { if (fp->fr_family != 0 && fp->fr_family != AF_INET6) continue; } else if (use_inet4 != 0 && use_inet6 == 0) { #endif if (fp->fr_family != 0 && fp->fr_family != AF_INET) continue; #ifdef USE_INET6 } else { if (fp->fr_family != 0 && fp->fr_family != AF_INET && fp->fr_family != AF_INET6) continue; } #endif if (fp->fr_data != NULL) fp->fr_data = (char *)fp + fp->fr_size; rules++; if (opts & (OPT_HITS|OPT_DEBUG)) #ifdef USE_QUAD_T PRINTF("%"PRIu64" ", (unsigned long long) fp->fr_hits); #else PRINTF("%lu ", fp->fr_hits); #endif if (opts & (OPT_ACCNT|OPT_DEBUG)) #ifdef USE_QUAD_T PRINTF("%"PRIu64" ", (unsigned long long) fp->fr_bytes); #else PRINTF("%lu ", fp->fr_bytes); #endif if (opts & OPT_SHOWLINENO) PRINTF("@%d ", rules); if (fp->fr_die != 0) fp->fr_die -= fiop->f_ticks; printfr(fp, ioctl); if (opts & OPT_DEBUG) { binprint(fp, fp->fr_size); if (fp->fr_data != NULL && fp->fr_dsize > 0) binprint(fp->fr_data, fp->fr_dsize); } if (fp->fr_grhead != -1) { for (g = grtop; g != NULL; g = g->fg_next) { if (!strncmp(fp->fr_names + fp->fr_grhead, g->fg_name, FR_GROUPLEN)) break; } if (g == NULL) { g = calloc(1, sizeof(*g)); if (g != NULL) { strncpy(g->fg_name, fp->fr_names + fp->fr_grhead, FR_GROUPLEN); if (grtop == NULL) { grtop = g; grtail = g; } else { grtail->fg_next = g; grtail = g; } } } } if (fp->fr_type == FR_T_CALLFUNC) { rules += printlivelist(fiop, out, set, fp->fr_data, group, "# callfunc: "); } } num = IPFGENITER_IPF; (void) ioctl(ipf_fd,SIOCIPFDELTOK, &num); return (rules); } static void printdeadlist(friostat_t *fiop, int out, int set, frentry_t *fp, char *group, char *comment) { frgroup_t *grtop, *grtail, *g; struct frentry fb; char *data; u_32_t type; int n; fb.fr_next = fp; n = 0; grtop = NULL; grtail = NULL; for (n = 1; fp; fp = fb.fr_next, n++) { if (kmemcpy((char *)&fb, (u_long)fb.fr_next, fb.fr_size) == -1) { perror("kmemcpy"); return; } fp = &fb; #ifdef USE_INET6 if (use_inet6 != 0 && use_inet4 == 0) { if (fp->fr_family != 0 && fp->fr_family != AF_INET6) continue; } else if (use_inet4 != 0 && use_inet6 == 0) { #endif if (fp->fr_family != 0 && fp->fr_family != AF_INET) continue; #ifdef USE_INET6 } else { if (fp->fr_family != 0 && fp->fr_family != AF_INET && fp->fr_family != AF_INET6) continue; } #endif data = NULL; type = fb.fr_type & ~FR_T_BUILTIN; if (type == FR_T_IPF || type == FR_T_BPFOPC) { if (fb.fr_dsize) { data = malloc(fb.fr_dsize); if (kmemcpy(data, (u_long)fb.fr_data, fb.fr_dsize) == -1) { perror("kmemcpy"); return; } fb.fr_data = data; } } if (opts & OPT_HITS) #ifdef USE_QUAD_T PRINTF("%"PRIu64" ", (unsigned long long) fb.fr_hits); #else PRINTF("%lu ", fb.fr_hits); #endif if (opts & OPT_ACCNT) #ifdef USE_QUAD_T PRINTF("%"PRIu64" ", (unsigned long long) fb.fr_bytes); #else PRINTF("%lu ", fb.fr_bytes); #endif if (opts & OPT_SHOWLINENO) PRINTF("@%d ", n); printfr(fp, ioctl); if (opts & OPT_DEBUG) { binprint(fp, fp->fr_size); if (fb.fr_data != NULL && fb.fr_dsize > 0) binprint(fb.fr_data, fb.fr_dsize); } if (data != NULL) free(data); if (fb.fr_grhead != -1) { g = calloc(1, sizeof(*g)); if (g != NULL) { strncpy(g->fg_name, fb.fr_names + fb.fr_grhead, FR_GROUPLEN); if (grtop == NULL) { grtop = g; grtail = g; } else { grtail->fg_next = g; grtail = g; } } } if (type == FR_T_CALLFUNC) { printdeadlist(fiop, out, set, fb.fr_data, group, "# callfunc: "); } } while ((g = grtop) != NULL) { printdeadlist(fiop, out, set, NULL, g->fg_name, comment); grtop = g->fg_next; free(g); } } /* * print out all of the asked for rule sets, using the stats struct as * the base from which to get the pointers. */ static void showlist(struct friostat *fiop) { struct frentry *fp = NULL; int i, set; set = fiop->f_active; if (opts & OPT_INACTIVE) set = 1 - set; if (opts & OPT_ACCNT) { if (opts & OPT_OUTQUE) { i = F_ACOUT; fp = (struct frentry *)fiop->f_acctout[set]; } else if (opts & OPT_INQUE) { i = F_ACIN; fp = (struct frentry *)fiop->f_acctin[set]; } else { FPRINTF(stderr, "No -i or -o given with -a\n"); return; } } else { if (opts & OPT_OUTQUE) { i = F_OUT; fp = (struct frentry *)fiop->f_fout[set]; } else if (opts & OPT_INQUE) { i = F_IN; fp = (struct frentry *)fiop->f_fin[set]; } else return; } if (opts & OPT_DEBUG) FPRINTF(stderr, "showlist:opts %#x i %d\n", opts, i); if (opts & OPT_DEBUG) PRINTF("fp %p set %d\n", fp, set); if (live_kernel == 1) { int printed; printed = printlivelist(fiop, i, set, fp, NULL, NULL); if (printed == 0) { FPRINTF(stderr, "# empty list for %s%s\n", (opts & OPT_INACTIVE) ? "inactive " : "", filters[i]); } } else { if (!fp) { FPRINTF(stderr, "# empty list for %s%s\n", (opts & OPT_INACTIVE) ? "inactive " : "", filters[i]); } else { printdeadlist(fiop, i, set, fp, NULL, NULL); } } } /* * Display ipfilter stateful filtering information */ static void showipstates(ips_stat_t *ipsp, int *filter) { ipstate_t *is; int i; /* * If a list of states hasn't been asked for, only print out stats */ if (!(opts & OPT_SHOWLIST)) { showstatestats(ipsp); return; } if ((state_fields != NULL) && (nohdrfields == 0)) { for (i = 0; state_fields[i].w_value != 0; i++) { printfieldhdr(statefields, state_fields + i); if (state_fields[i + 1].w_value != 0) printf("\t"); } printf("\n"); } /* * Print out all the state information currently held in the kernel. */ for (is = ipsp->iss_list; is != NULL; ) { ipstate_t ips; is = fetchstate(is, &ips); if (is == NULL) break; is = ips.is_next; if ((filter != NULL) && (state_matcharray(&ips, filter) == 0)) { continue; } if (state_fields != NULL) { for (i = 0; state_fields[i].w_value != 0; i++) { printstatefield(&ips, state_fields[i].w_value); if (state_fields[i + 1].w_value != 0) printf("\t"); } printf("\n"); } else { printstate(&ips, opts, ipsp->iss_ticks); } } } static void showstatestats(ips_stat_t *ipsp) { int minlen, maxlen, totallen; ipftable_t table; u_int *buckets; ipfobj_t obj; int i, sz; /* * If a list of states hasn't been asked for, only print out stats */ sz = sizeof(*buckets) * ipsp->iss_state_size; buckets = (u_int *)malloc(sz); obj.ipfo_rev = IPFILTER_VERSION; obj.ipfo_type = IPFOBJ_GTABLE; obj.ipfo_size = sizeof(table); obj.ipfo_ptr = &table; table.ita_type = IPFTABLE_BUCKETS; table.ita_table = buckets; if (live_kernel == 1) { if (ioctl(state_fd, SIOCGTABL, &obj) != 0) { free(buckets); return; } } else { if (kmemcpy((char *)buckets, (u_long)ipsp->iss_bucketlen, sz)) { free(buckets); return; } } PRINTF("%u\tactive state table entries\n",ipsp->iss_active); PRINTF("%lu\tadd bad\n", ipsp->iss_add_bad); PRINTF("%lu\tadd duplicate\n", ipsp->iss_add_dup); PRINTF("%lu\tadd locked\n", ipsp->iss_add_locked); PRINTF("%lu\tadd oow\n", ipsp->iss_add_oow); PRINTF("%lu\tbucket full\n", ipsp->iss_bucket_full); PRINTF("%lu\tcheck bad\n", ipsp->iss_check_bad); PRINTF("%lu\tcheck miss\n", ipsp->iss_check_miss); PRINTF("%lu\tcheck nattag\n", ipsp->iss_check_nattag); PRINTF("%lu\tclone nomem\n", ipsp->iss_clone_nomem); PRINTF("%lu\tcheck notag\n", ipsp->iss_check_notag); PRINTF("%lu\tcheck success\n", ipsp->iss_hits); PRINTF("%lu\tcloned\n", ipsp->iss_cloned); PRINTF("%lu\texpired\n", ipsp->iss_expire); PRINTF("%lu\tflush all\n", ipsp->iss_flush_all); PRINTF("%lu\tflush closing\n", ipsp->iss_flush_closing); PRINTF("%lu\tflush queue\n", ipsp->iss_flush_queue); PRINTF("%lu\tflush state\n", ipsp->iss_flush_state); PRINTF("%lu\tflush timeout\n", ipsp->iss_flush_timeout); PRINTF("%u\thash buckets in use\n", ipsp->iss_inuse); PRINTF("%lu\tICMP bad\n", ipsp->iss_icmp_bad); PRINTF("%lu\tICMP banned\n", ipsp->iss_icmp_banned); PRINTF("%lu\tICMP errors\n", ipsp->iss_icmp_icmperr); PRINTF("%lu\tICMP head block\n", ipsp->iss_icmp_headblock); PRINTF("%lu\tICMP hits\n", ipsp->iss_icmp_hits); PRINTF("%lu\tICMP not query\n", ipsp->iss_icmp_notquery); PRINTF("%lu\tICMP short\n", ipsp->iss_icmp_short); PRINTF("%lu\tICMP too many\n", ipsp->iss_icmp_toomany); PRINTF("%lu\tICMPv6 errors\n", ipsp->iss_icmp6_icmperr); PRINTF("%lu\tICMPv6 miss\n", ipsp->iss_icmp6_miss); PRINTF("%lu\tICMPv6 not info\n", ipsp->iss_icmp6_notinfo); PRINTF("%lu\tICMPv6 not query\n", ipsp->iss_icmp6_notquery); PRINTF("%lu\tlog fail\n", ipsp->iss_log_fail); PRINTF("%lu\tlog ok\n", ipsp->iss_log_ok); PRINTF("%lu\tlookup interface mismatch\n", ipsp->iss_lookup_badifp); PRINTF("%lu\tlookup mask mismatch\n", ipsp->iss_miss_mask); PRINTF("%lu\tlookup port mismatch\n", ipsp->iss_lookup_badport); PRINTF("%lu\tlookup miss\n", ipsp->iss_lookup_miss); PRINTF("%lu\tmaximum rule references\n", ipsp->iss_max_ref); PRINTF("%lu\tmaximum hosts per rule\n", ipsp->iss_max_track); PRINTF("%lu\tno memory\n", ipsp->iss_nomem); PRINTF("%lu\tout of window\n", ipsp->iss_oow); PRINTF("%lu\torphans\n", ipsp->iss_orphan); PRINTF("%lu\tscan block\n", ipsp->iss_scan_block); PRINTF("%lu\tstate table maximum reached\n", ipsp->iss_max); PRINTF("%lu\tTCP closing\n", ipsp->iss_tcp_closing); PRINTF("%lu\tTCP OOW\n", ipsp->iss_tcp_oow); PRINTF("%lu\tTCP RST add\n", ipsp->iss_tcp_rstadd); PRINTF("%lu\tTCP too small\n", ipsp->iss_tcp_toosmall); PRINTF("%lu\tTCP bad options\n", ipsp->iss_tcp_badopt); PRINTF("%lu\tTCP removed\n", ipsp->iss_fin); PRINTF("%lu\tTCP FSM\n", ipsp->iss_tcp_fsm); PRINTF("%lu\tTCP strict\n", ipsp->iss_tcp_strict); PRINTF("%lu\tTCP wild\n", ipsp->iss_wild); PRINTF("%lu\tMicrosoft Windows SACK\n", ipsp->iss_winsack); PRINTF("State logging %sabled\n", state_logging ? "en" : "dis"); PRINTF("IP states added:\n"); for (i = 0; i < 256; i++) { if (ipsp->iss_proto[i] != 0) { struct protoent *proto; proto = getprotobynumber(i); PRINTF("%lu", ipsp->iss_proto[i]); if (proto != NULL) PRINTF("\t%s\n", proto->p_name); else PRINTF("\t%d\n", i); } } PRINTF("\nState table bucket statistics:\n"); PRINTF("%u\tin use\n", ipsp->iss_inuse); minlen = ipsp->iss_max; totallen = 0; maxlen = 0; for (i = 0; i < ipsp->iss_state_size; i++) { if (buckets[i] > maxlen) maxlen = buckets[i]; if (buckets[i] < minlen) minlen = buckets[i]; totallen += buckets[i]; } PRINTF("%d\thash efficiency\n", totallen ? ipsp->iss_inuse * 100 / totallen : 0); PRINTF("%2.2f%%\tbucket usage\n%u\tminimal length\n", ((float)ipsp->iss_inuse / ipsp->iss_state_size) * 100.0, minlen); PRINTF("%u\tmaximal length\n%.3f\taverage length\n", maxlen, ipsp->iss_inuse ? (float) totallen/ ipsp->iss_inuse : 0.0); #define ENTRIES_PER_LINE 5 if (opts & OPT_VERBOSE) { PRINTF("\nCurrent bucket sizes :\n"); for (i = 0; i < ipsp->iss_state_size; i++) { if ((i % ENTRIES_PER_LINE) == 0) PRINTF("\t"); PRINTF("%4d -> %4u", i, buckets[i]); if ((i % ENTRIES_PER_LINE) == (ENTRIES_PER_LINE - 1)) PRINTF("\n"); else PRINTF(" "); } PRINTF("\n"); } PRINTF("\n"); free(buckets); if (live_kernel == 1) { showtqtable_live(state_fd); } else { printtqtable(ipsp->iss_tcptab); } } #ifdef STATETOP static int handle_resize = 0, handle_break = 0; static void topipstates(i6addr_t saddr, i6addr_t daddr, int sport, int dport, int protocol, int ver, int refreshtime, int topclosed, int *filter) { char str1[STSTRSIZE], str2[STSTRSIZE], str3[STSTRSIZE], str4[STSTRSIZE]; int maxtsentries = 0, reverse = 0, sorting = STSORT_DEFAULT; int i, j, winy, tsentry, maxx, maxy, redraw = 0, ret = 0; int len, srclen, dstlen, forward = 1, c = 0; ips_stat_t ipsst, *ipsstp = &ipsst; int token_type = IPFGENITER_STATE; statetop_t *tstable = NULL, *tp; const char *errstr = ""; ipstate_t ips; ipfobj_t ipfo; struct timeval selecttimeout; char hostnm[HOSTNMLEN]; struct protoent *proto; fd_set readfd; time_t t; /* install signal handlers */ signal(SIGINT, sig_break); signal(SIGQUIT, sig_break); signal(SIGTERM, sig_break); signal(SIGWINCH, sig_resize); /* init ncurses stuff */ initscr(); cbreak(); noecho(); curs_set(0); timeout(0); getmaxyx(stdscr, maxy, maxx); /* init hostname */ gethostname(hostnm, sizeof(hostnm) - 1); hostnm[sizeof(hostnm) - 1] = '\0'; /* init ipfobj_t stuff */ bzero((caddr_t)&ipfo, sizeof(ipfo)); ipfo.ipfo_rev = IPFILTER_VERSION; ipfo.ipfo_type = IPFOBJ_STATESTAT; ipfo.ipfo_size = sizeof(*ipsstp); ipfo.ipfo_ptr = (void *)ipsstp; /* repeat until user aborts */ while ( 1 ) { /* get state table */ bzero((char *)&ipsst, sizeof(ipsst)); if ((ioctl(state_fd, SIOCGETFS, &ipfo) == -1)) { errstr = "ioctl(SIOCGETFS)"; ret = -1; goto out; } /* clear the history */ tsentry = -1; /* reset max str len */ srclen = dstlen = 0; /* read the state table and store in tstable */ for (; ipsstp->iss_list; ipsstp->iss_list = ips.is_next) { ipsstp->iss_list = fetchstate(ipsstp->iss_list, &ips); if (ipsstp->iss_list == NULL) break; if (ver != 0 && ips.is_v != ver) continue; if ((filter != NULL) && (state_matcharray(&ips, filter) == 0)) continue; /* check v4 src/dest addresses */ if (ips.is_v == 4) { if ((saddr.in4.s_addr != INADDR_ANY && saddr.in4.s_addr != ips.is_saddr) || (daddr.in4.s_addr != INADDR_ANY && daddr.in4.s_addr != ips.is_daddr)) continue; } #ifdef USE_INET6 /* check v6 src/dest addresses */ if (ips.is_v == 6) { if ((IP6_NEQ(&saddr, &in6addr_any) && IP6_NEQ(&saddr, &ips.is_src)) || (IP6_NEQ(&daddr, &in6addr_any) && IP6_NEQ(&daddr, &ips.is_dst))) continue; } #endif /* check protocol */ if (protocol > 0 && protocol != ips.is_p) continue; /* check ports if protocol is TCP or UDP */ if (((ips.is_p == IPPROTO_TCP) || (ips.is_p == IPPROTO_UDP)) && (((sport > 0) && (htons(sport) != ips.is_sport)) || ((dport > 0) && (htons(dport) != ips.is_dport)))) continue; /* show closed TCP sessions ? */ if ((topclosed == 0) && (ips.is_p == IPPROTO_TCP) && (ips.is_state[0] >= IPF_TCPS_LAST_ACK) && (ips.is_state[1] >= IPF_TCPS_LAST_ACK)) continue; /* * if necessary make room for this state * entry */ tsentry++; if (!maxtsentries || tsentry == maxtsentries) { maxtsentries += STGROWSIZE; tstable = reallocarray(tstable, maxtsentries, sizeof(statetop_t)); if (tstable == NULL) { perror("realloc"); exit(-1); } } /* get max src/dest address string length */ len = strlen(getip(ips.is_v, &ips.is_src)); if (srclen < len) srclen = len; len = strlen(getip(ips.is_v, &ips.is_dst)); if (dstlen < len) dstlen = len; /* fill structure */ tp = tstable + tsentry; tp->st_src = ips.is_src; tp->st_dst = ips.is_dst; tp->st_p = ips.is_p; tp->st_v = ips.is_v; tp->st_state[0] = ips.is_state[0]; tp->st_state[1] = ips.is_state[1]; if (forward) { tp->st_pkts = ips.is_pkts[0]+ips.is_pkts[1]; tp->st_bytes = ips.is_bytes[0]+ips.is_bytes[1]; } else { tp->st_pkts = ips.is_pkts[2]+ips.is_pkts[3]; tp->st_bytes = ips.is_bytes[2]+ips.is_bytes[3]; } tp->st_age = ips.is_die - ipsstp->iss_ticks; if ((ips.is_p == IPPROTO_TCP) || (ips.is_p == IPPROTO_UDP)) { tp->st_sport = ips.is_sport; tp->st_dport = ips.is_dport; } } (void) ioctl(state_fd, SIOCIPFDELTOK, &token_type); /* sort the array */ if (tsentry != -1) { switch (sorting) { case STSORT_PR: qsort(tstable, tsentry + 1, sizeof(statetop_t), sort_p); break; case STSORT_PKTS: qsort(tstable, tsentry + 1, sizeof(statetop_t), sort_pkts); break; case STSORT_BYTES: qsort(tstable, tsentry + 1, sizeof(statetop_t), sort_bytes); break; case STSORT_TTL: qsort(tstable, tsentry + 1, sizeof(statetop_t), sort_ttl); break; case STSORT_SRCIP: qsort(tstable, tsentry + 1, sizeof(statetop_t), sort_srcip); break; case STSORT_SRCPT: qsort(tstable, tsentry +1, sizeof(statetop_t), sort_srcpt); break; case STSORT_DSTIP: qsort(tstable, tsentry + 1, sizeof(statetop_t), sort_dstip); break; case STSORT_DSTPT: qsort(tstable, tsentry + 1, sizeof(statetop_t), sort_dstpt); break; default: break; } } /* handle window resizes */ if (handle_resize) { endwin(); initscr(); cbreak(); noecho(); curs_set(0); timeout(0); getmaxyx(stdscr, maxy, maxx); redraw = 1; handle_resize = 0; } /* stop program? */ if (handle_break) break; /* print title */ erase(); attron(A_BOLD); winy = 0; move(winy,0); snprintf(str1, sizeof(str1), "%s - %s - state top", hostnm, IPL_VERSION); for (j = 0 ; j < (maxx - 8 - strlen(str1)) / 2; j++) printw(" "); printw("%s", str1); attroff(A_BOLD); /* just for fun add a clock */ move(winy, maxx - 8); t = time(NULL); strftime(str1, 80, "%T", localtime(&t)); printw("%s\n", str1); /* * print the display filters, this is placed in the loop, * because someday I might add code for changing these * while the programming is running :-) */ if (sport >= 0) snprintf(str1, sizeof(str1), "%s,%d", getip(ver, &saddr), sport); else snprintf(str1, sizeof(str1), "%s", getip(ver, &saddr)); if (dport >= 0) snprintf(str2, sizeof(str2), "%s,%d", getip(ver, &daddr), dport); else snprintf(str2, sizeof(str2), "%s", getip(ver, &daddr)); if (protocol < 0) strcpy(str3, "any"); else if ((proto = getprotobynumber(protocol)) != NULL) snprintf(str3, sizeof(str3), "%s", proto->p_name); else snprintf(str3, sizeof(str3), "%d", protocol); switch (sorting) { case STSORT_PR: snprintf(str4, sizeof(str4), "proto"); break; case STSORT_PKTS: snprintf(str4, sizeof(str4), "# pkts"); break; case STSORT_BYTES: snprintf(str4, sizeof(str4), "# bytes"); break; case STSORT_TTL: snprintf(str4, sizeof(str4), "ttl"); break; case STSORT_SRCIP: snprintf(str4, sizeof(str4), "src ip"); break; case STSORT_SRCPT: snprintf(str4, sizeof(str4), "src port"); break; case STSORT_DSTIP: snprintf(str4, sizeof(str4), "dest ip"); break; case STSORT_DSTPT: snprintf(str4, sizeof(str4), "dest port"); break; default: snprintf(str4, sizeof(str4), "unknown"); break; } if (reverse) strcat(str4, " (reverse)"); winy += 2; move(winy,0); printw("Src: %s, Dest: %s, Proto: %s, Sorted by: %s\n\n", str1, str2, str3, str4); /* * For an IPv4 IP address we need at most 15 characters, * 4 tuples of 3 digits, separated by 3 dots. Enforce this * length, so the columns do not change positions based * on the size of the IP address. This length makes the * output fit in a 80 column terminal. * We are lacking a good solution for IPv6 addresses (that * can be longer that 15 characters), so we do not enforce * a maximum on the IP field size. */ if (srclen < 15) srclen = 15; if (dstlen < 15) dstlen = 15; /* print column description */ winy += 2; move(winy,0); attron(A_BOLD); printw("%-*s %-*s %3s %4s %7s %9s %9s\n", srclen + 6, "Source IP", dstlen + 6, "Destination IP", "ST", "PR", "#pkts", "#bytes", "ttl"); attroff(A_BOLD); /* print all the entries */ tp = tstable; if (reverse) tp += tsentry; if (tsentry > maxy - 6) tsentry = maxy - 6; for (i = 0; i <= tsentry; i++) { /* print src/dest and port */ if ((tp->st_p == IPPROTO_TCP) || (tp->st_p == IPPROTO_UDP)) { snprintf(str1, sizeof(str1), "%s,%hu", getip(tp->st_v, &tp->st_src), ntohs(tp->st_sport)); snprintf(str2, sizeof(str2), "%s,%hu", getip(tp->st_v, &tp->st_dst), ntohs(tp->st_dport)); } else { snprintf(str1, sizeof(str1), "%s", getip(tp->st_v, &tp->st_src)); snprintf(str2, sizeof(str2), "%s", getip(tp->st_v, &tp->st_dst)); } winy++; move(winy, 0); printw("%-*s %-*s", srclen + 6, str1, dstlen + 6, str2); /* print state */ snprintf(str1, sizeof(str1), "%X/%X", tp->st_state[0], tp->st_state[1]); printw(" %3s", str1); /* print protocol */ proto = getprotobynumber(tp->st_p); if (proto) { strncpy(str1, proto->p_name, 4); str1[4] = '\0'; } else { snprintf(str1, sizeof(str1), "%d", tp->st_p); } /* just print icmp for IPv6-ICMP */ if (tp->st_p == IPPROTO_ICMPV6) strcpy(str1, "icmp"); printw(" %4s", str1); /* print #pkt/#bytes */ #ifdef USE_QUAD_T printw(" %7qu %9qu", (unsigned long long) tp->st_pkts, (unsigned long long) tp->st_bytes); #else printw(" %7lu %9lu", tp->st_pkts, tp->st_bytes); #endif printw(" %9s", ttl_to_string(tp->st_age)); if (reverse) tp--; else tp++; } /* screen data structure is filled, now update the screen */ if (redraw) clearok(stdscr,1); if (refresh() == ERR) break; if (redraw) { clearok(stdscr,0); redraw = 0; } /* wait for key press or a 1 second time out period */ selecttimeout.tv_sec = refreshtime; selecttimeout.tv_usec = 0; FD_ZERO(&readfd); FD_SET(0, &readfd); select(1, &readfd, NULL, NULL, &selecttimeout); /* if key pressed, read all waiting keys */ if (FD_ISSET(0, &readfd)) { c = wgetch(stdscr); if (c == ERR) continue; if (ISALPHA(c) && ISUPPER(c)) c = TOLOWER(c); if (c == 'l') { redraw = 1; } else if (c == 'q') { break; } else if (c == 'r') { reverse = !reverse; } else if (c == 'b') { forward = 0; } else if (c == 'f') { forward = 1; } else if (c == 's') { if (++sorting > STSORT_MAX) sorting = 0; } } } /* while */ out: printw("\n"); curs_set(1); /* nocbreak(); XXX - endwin() should make this redundant */ endwin(); free(tstable); if (ret != 0) perror(errstr); } #endif /* * Show fragment cache information that's held in the kernel. */ static void showfrstates(ipfrstat_t *ifsp, u_long ticks) { struct ipfr *ipfrtab[IPFT_SIZE], ifr; int i; /* * print out the numeric statistics */ PRINTF("IP fragment states:\n%lu\tnew\n%lu\texpired\n%lu\thits\n", ifsp->ifs_new, ifsp->ifs_expire, ifsp->ifs_hits); PRINTF("%lu\tretrans\n%lu\ttoo short\n", ifsp->ifs_retrans0, ifsp->ifs_short); PRINTF("%lu\tno memory\n%lu\talready exist\n", ifsp->ifs_nomem, ifsp->ifs_exists); PRINTF("%lu\tinuse\n", ifsp->ifs_inuse); PRINTF("\n"); if (live_kernel == 0) { if (kmemcpy((char *)ipfrtab, (u_long)ifsp->ifs_table, sizeof(ipfrtab))) return; } /* * Print out the contents (if any) of the fragment cache table. */ if (live_kernel == 1) { do { if (fetchfrag(ipf_fd, IPFGENITER_FRAG, &ifr) != 0) break; if (ifr.ipfr_ifp == NULL) break; ifr.ipfr_ttl -= ticks; printfraginfo("", &ifr); } while (ifr.ipfr_next != NULL); } else { for (i = 0; i < IPFT_SIZE; i++) while (ipfrtab[i] != NULL) { if (kmemcpy((char *)&ifr, (u_long)ipfrtab[i], sizeof(ifr)) == -1) break; printfraginfo("", &ifr); ipfrtab[i] = ifr.ipfr_next; } } /* * Print out the contents (if any) of the NAT fragment cache table. */ if (live_kernel == 0) { if (kmemcpy((char *)ipfrtab, (u_long)ifsp->ifs_nattab, sizeof(ipfrtab))) return; } if (live_kernel == 1) { do { if (fetchfrag(nat_fd, IPFGENITER_NATFRAG, &ifr) != 0) break; if (ifr.ipfr_ifp == NULL) break; ifr.ipfr_ttl -= ticks; printfraginfo("NAT: ", &ifr); } while (ifr.ipfr_next != NULL); } else { for (i = 0; i < IPFT_SIZE; i++) while (ipfrtab[i] != NULL) { if (kmemcpy((char *)&ifr, (u_long)ipfrtab[i], sizeof(ifr)) == -1) break; printfraginfo("NAT: ", &ifr); ipfrtab[i] = ifr.ipfr_next; } } } /* * Show stats on how auth within IPFilter has been used */ static void showauthstates(ipf_authstat_t *asp) { frauthent_t *frap, fra; ipfgeniter_t auth; ipfobj_t obj; obj.ipfo_rev = IPFILTER_VERSION; obj.ipfo_type = IPFOBJ_GENITER; obj.ipfo_size = sizeof(auth); obj.ipfo_ptr = &auth; auth.igi_type = IPFGENITER_AUTH; auth.igi_nitems = 1; auth.igi_data = &fra; #ifdef USE_QUAD_T printf("Authorisation hits: %"PRIu64"\tmisses %"PRIu64"\n", (unsigned long long) asp->fas_hits, (unsigned long long) asp->fas_miss); #else printf("Authorisation hits: %ld\tmisses %ld\n", asp->fas_hits, asp->fas_miss); #endif printf("nospace %ld\nadded %ld\nsendfail %ld\nsendok %ld\n", asp->fas_nospace, asp->fas_added, asp->fas_sendfail, asp->fas_sendok); printf("queok %ld\nquefail %ld\nexpire %ld\n", asp->fas_queok, asp->fas_quefail, asp->fas_expire); frap = asp->fas_faelist; while (frap) { if (live_kernel == 1) { if (ioctl(auth_fd, SIOCGENITER, &obj)) break; } else { if (kmemcpy((char *)&fra, (u_long)frap, sizeof(fra)) == -1) break; } printf("age %ld\t", fra.fae_age); printfr(&fra.fae_fr, ioctl); frap = fra.fae_next; } } /* * Display groups used for each of filter rules, accounting rules and * authentication, separately. */ static void showgroups(struct friostat *fiop) { static char *gnames[3] = { "Filter", "Accounting", "Authentication" }; static int gnums[3] = { IPL_LOGIPF, IPL_LOGCOUNT, IPL_LOGAUTH }; frgroup_t *fp, grp; int on, off, i; on = fiop->f_active; off = 1 - on; for (i = 0; i < 3; i++) { printf("%s groups (active):\n", gnames[i]); for (fp = fiop->f_groups[gnums[i]][on]; fp != NULL; fp = grp.fg_next) if (kmemcpy((char *)&grp, (u_long)fp, sizeof(grp))) break; else printf("%s\n", grp.fg_name); printf("%s groups (inactive):\n", gnames[i]); for (fp = fiop->f_groups[gnums[i]][off]; fp != NULL; fp = grp.fg_next) if (kmemcpy((char *)&grp, (u_long)fp, sizeof(grp))) break; else printf("%s\n", grp.fg_name); } } static void parse_ipportstr(const char *argument, i6addr_t *ip, int *port) { char *s, *comma; int ok = 0; /* make working copy of argument, Theoretically you must be able * to write to optarg, but that seems very ugly to me.... */ s = strdup(argument); if (s == NULL) return; /* get port */ if ((comma = strchr(s, ',')) != NULL) { if (!strcasecmp(comma + 1, "any")) { *port = -1; } else if (!sscanf(comma + 1, "%d", port) || (*port < 0) || (*port > 65535)) { fprintf(stderr, "Invalid port specification in %s\n", argument); free(s); exit(-2); } *comma = '\0'; } /* get ip address */ if (!strcasecmp(s, "any")) { ip->in4.s_addr = INADDR_ANY; ok = 1; #ifdef USE_INET6 ip->in6 = in6addr_any; } else if (use_inet6 && !use_inet4 && inet_pton(AF_INET6, s, &ip->in6)) { ok = 1; #endif } else if (inet_aton(s, &ip->in4)) ok = 1; if (ok == 0) { fprintf(stderr, "Invalid IP address: %s\n", s); free(s); exit(-2); } /* free allocated memory */ free(s); } #ifdef STATETOP static void sig_resize(int s) { handle_resize = 1; } static void sig_break(int s) { handle_break = 1; } static char *getip(int v, i6addr_t *addr) { #ifdef USE_INET6 static char hostbuf[MAXHOSTNAMELEN+1]; #endif if (v == 0) return ("any"); if (v == 4) return (inet_ntoa(addr->in4)); #ifdef USE_INET6 (void) inet_ntop(AF_INET6, &addr->in6, hostbuf, sizeof(hostbuf) - 1); hostbuf[MAXHOSTNAMELEN] = '\0'; return (hostbuf); #else return ("IPv6"); #endif } static char *ttl_to_string(long int ttl) { static char ttlbuf[STSTRSIZE]; int hours, minutes, seconds; /* ttl is in half seconds */ ttl /= 2; hours = ttl / 3600; ttl = ttl % 3600; minutes = ttl / 60; seconds = ttl % 60; if (hours > 0) snprintf(ttlbuf, sizeof(ttlbuf), "%2d:%02d:%02d", hours, minutes, seconds); else snprintf(ttlbuf, sizeof(ttlbuf), "%2d:%02d", minutes, seconds); return (ttlbuf); } static int sort_pkts(const void *a, const void *b) { register const statetop_t *ap = a; register const statetop_t *bp = b; if (ap->st_pkts == bp->st_pkts) return (0); else if (ap->st_pkts < bp->st_pkts) return (1); return (-1); } static int sort_bytes(const void *a, const void *b) { register const statetop_t *ap = a; register const statetop_t *bp = b; if (ap->st_bytes == bp->st_bytes) return (0); else if (ap->st_bytes < bp->st_bytes) return (1); return (-1); } static int sort_p(const void *a, const void *b) { register const statetop_t *ap = a; register const statetop_t *bp = b; if (ap->st_p == bp->st_p) return (0); else if (ap->st_p < bp->st_p) return (1); return (-1); } static int sort_ttl(const void *a, const void *b) { register const statetop_t *ap = a; register const statetop_t *bp = b; if (ap->st_age == bp->st_age) return (0); else if (ap->st_age < bp->st_age) return (1); return (-1); } static int sort_srcip(const void *a, const void *b) { register const statetop_t *ap = a; register const statetop_t *bp = b; #ifdef USE_INET6 if (use_inet6 && !use_inet4) { if (IP6_EQ(&ap->st_src, &bp->st_src)) return (0); else if (IP6_GT(&ap->st_src, &bp->st_src)) return (1); } else #endif { if (ntohl(ap->st_src.in4.s_addr) == ntohl(bp->st_src.in4.s_addr)) return (0); else if (ntohl(ap->st_src.in4.s_addr) > ntohl(bp->st_src.in4.s_addr)) return (1); } return (-1); } static int sort_srcpt(const void *a, const void *b) { register const statetop_t *ap = a; register const statetop_t *bp = b; if (htons(ap->st_sport) == htons(bp->st_sport)) return (0); else if (htons(ap->st_sport) > htons(bp->st_sport)) return (1); return (-1); } static int sort_dstip(const void *a, const void *b) { register const statetop_t *ap = a; register const statetop_t *bp = b; #ifdef USE_INET6 if (use_inet6 && !use_inet4) { if (IP6_EQ(&ap->st_dst, &bp->st_dst)) return (0); else if (IP6_GT(&ap->st_dst, &bp->st_dst)) return (1); } else #endif { if (ntohl(ap->st_dst.in4.s_addr) == ntohl(bp->st_dst.in4.s_addr)) return (0); else if (ntohl(ap->st_dst.in4.s_addr) > ntohl(bp->st_dst.in4.s_addr)) return (1); } return (-1); } static int sort_dstpt(const void *a, const void *b) { register const statetop_t *ap = a; register const statetop_t *bp = b; if (htons(ap->st_dport) == htons(bp->st_dport)) return (0); else if (htons(ap->st_dport) > htons(bp->st_dport)) return (1); return (-1); } #endif ipstate_t *fetchstate(ipstate_t *src, ipstate_t *dst) { if (live_kernel == 1) { ipfgeniter_t state; ipfobj_t obj; obj.ipfo_rev = IPFILTER_VERSION; obj.ipfo_type = IPFOBJ_GENITER; obj.ipfo_size = sizeof(state); obj.ipfo_ptr = &state; state.igi_type = IPFGENITER_STATE; state.igi_nitems = 1; state.igi_data = dst; if (ioctl(state_fd, SIOCGENITER, &obj) != 0) return (NULL); if (dst->is_next == NULL) { int n = IPFGENITER_STATE; (void) ioctl(ipf_fd,SIOCIPFDELTOK, &n); } } else { if (kmemcpy((char *)dst, (u_long)src, sizeof(*dst))) return (NULL); } return (dst); } static int fetchfrag( int fd, int type, ipfr_t *frp) { ipfgeniter_t frag; ipfobj_t obj; obj.ipfo_rev = IPFILTER_VERSION; obj.ipfo_type = IPFOBJ_GENITER; obj.ipfo_size = sizeof(frag); obj.ipfo_ptr = &frag; frag.igi_type = type; frag.igi_nitems = 1; frag.igi_data = frp; if (ioctl(fd, SIOCGENITER, &obj)) return (EFAULT); return (0); } static int state_matcharray(ipstate_t *stp, int *array) { int i, n, *x, rv, p; ipfexp_t *e; rv = 0; for (n = array[0], x = array + 1; n > 0; x += e->ipfe_size) { e = (ipfexp_t *)x; if (e->ipfe_cmd == IPF_EXP_END) break; n -= e->ipfe_size; rv = 0; /* * The upper 16 bits currently store the protocol value. * This is currently used with TCP and UDP port compares and * allows "tcp.port = 80" without requiring an explicit " "ip.pr = tcp" first. */ p = e->ipfe_cmd >> 16; if ((p != 0) && (p != stp->is_p)) break; switch (e->ipfe_cmd) { case IPF_EXP_IP_PR : for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= (stp->is_p == e->ipfe_arg0[i]); } break; case IPF_EXP_IP_SRCADDR : if (stp->is_v != 4) break; for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= ((stp->is_saddr & e->ipfe_arg0[i * 2 + 1]) == e->ipfe_arg0[i * 2]); } break; case IPF_EXP_IP_DSTADDR : if (stp->is_v != 4) break; for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= ((stp->is_daddr & e->ipfe_arg0[i * 2 + 1]) == e->ipfe_arg0[i * 2]); } break; case IPF_EXP_IP_ADDR : if (stp->is_v != 4) break; for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= ((stp->is_saddr & e->ipfe_arg0[i * 2 + 1]) == e->ipfe_arg0[i * 2]) || ((stp->is_daddr & e->ipfe_arg0[i * 2 + 1]) == e->ipfe_arg0[i * 2]); } break; #ifdef USE_INET6 case IPF_EXP_IP6_SRCADDR : if (stp->is_v != 6) break; for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= IP6_MASKEQ(&stp->is_src, &e->ipfe_arg0[i * 8 + 4], &e->ipfe_arg0[i * 8]); } break; case IPF_EXP_IP6_DSTADDR : if (stp->is_v != 6) break; for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= IP6_MASKEQ(&stp->is_dst, &e->ipfe_arg0[i * 8 + 4], &e->ipfe_arg0[i * 8]); } break; case IPF_EXP_IP6_ADDR : if (stp->is_v != 6) break; for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= IP6_MASKEQ(&stp->is_src, &e->ipfe_arg0[i * 8 + 4], &e->ipfe_arg0[i * 8]) || IP6_MASKEQ(&stp->is_dst, &e->ipfe_arg0[i * 8 + 4], &e->ipfe_arg0[i * 8]); } break; #endif case IPF_EXP_UDP_PORT : case IPF_EXP_TCP_PORT : for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= (stp->is_sport == e->ipfe_arg0[i]) || (stp->is_dport == e->ipfe_arg0[i]); } break; case IPF_EXP_UDP_SPORT : case IPF_EXP_TCP_SPORT : for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= (stp->is_sport == e->ipfe_arg0[i]); } break; case IPF_EXP_UDP_DPORT : case IPF_EXP_TCP_DPORT : for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= (stp->is_dport == e->ipfe_arg0[i]); } break; case IPF_EXP_IDLE_GT : for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= (stp->is_die < e->ipfe_arg0[i]); } break; case IPF_EXP_TCP_STATE : for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= (stp->is_state[0] == e->ipfe_arg0[i]) || (stp->is_state[1] == e->ipfe_arg0[i]); } break; } rv ^= e->ipfe_not; if (rv == 0) break; } return (rv); } static void showtqtable_live(int fd) { ipftq_t table[IPF_TCP_NSTATES]; ipfobj_t obj; bzero((char *)&obj, sizeof(obj)); obj.ipfo_rev = IPFILTER_VERSION; obj.ipfo_size = sizeof(table); obj.ipfo_ptr = (void *)table; obj.ipfo_type = IPFOBJ_STATETQTAB; if (ioctl(fd, SIOCGTQTAB, &obj) == 0) { printtqtable(table); } } diff --git a/sbin/ipf/ipnat/ipnat.5 b/sbin/ipf/ipnat/ipnat.5 index 01931c6be4f4..b01892f9749d 100644 --- a/sbin/ipf/ipnat/ipnat.5 +++ b/sbin/ipf/ipnat/ipnat.5 @@ -1,727 +1,727 @@ .\" .TH IPNAT 5 .SH NAME ipnat, ipnat.conf \- IPFilter NAT file format .SH DESCRIPTION .PP The .B ipnat.conf file is used to specify rules for the Network Address Translation (NAT) component of IPFilter. To load rules specified in the .B ipnat.conf file, the .B ipnat(8) program is used. .PP For standard NAT functionality, a rule should start with \fBmap\fP and then proceeds to specify the interface for which outgoing packets will have their source address rewritten. Following this it is expected that the old source address, and optionally port number, will be specified. .PP In general, all NAT rules conform to the following layout: the first word indicates what type of NAT rule is present, this is followed by some stanzas to match a packet, followed by a "->" and this is then followed by several more stanzas describing the new data to be put in the packet. .PP In this text and in others, use of the term "left hand side" (LHS) when talking about a NAT rule refers to text that appears before the "->" and the "right hand side" (RHS) for text that appears after it. In essence, the LHS is the packet matching and the RHS is the new data to be used. .SH VARIABLES .PP This configuration file, like all others used with IPFilter, supports the use of variable substitution throughout the text. .nf nif="ppp0"; map $nif 0/0 -> 0/32 .fi .PP would become .nf map ppp0 0/0 -> 0/32 .fi .PP Variables can be used recursively, such as 'foo="$bar baz";', so long as $bar exists when the parser reaches the assignment for foo. .PP See .B ipnat(8) for instructions on how to define variables to be used from a shell environment. .SH OUTBOUND SOURCE TRANSLATION (map'ing) Changing the source address of a packet is traditionally performed using .B map rules. Both the source address and optionally port number can be changed according to various controls. .PP To start out with, a common rule used is of the form: .nf map le0 0/0 -> 0/32 .fi .PP Here we're saying change the source address of all packets going out of le0 (the address/mask pair of 0/0 matching all packets) to that of the interface le0 (0/32 is a synonym for the interface's own address at the current point in time.) If we wanted to pass the packet through with no change in address, we would write it as: .nf map le0 0/0 -> 0/0 .fi .PP If we only want to change a portion of our internal network and to a different address that is routed back through this host, we might do: .nf map le0 10.1.1.0/24 -> 192.168.55.3/32 .fi .PP In some instances, we may have an entire subnet to map internal addresses out onto, in which case we can express the translation as this: .nf map le0 10.0.0.0/8 -> 192.168.55.0/24 .fi .PP IPFilter will cycle through each of the 256 addresses in the 192.168.55.0/24 address space to ensure that they all get used. .PP Of course this poses a problem for TCP and UDP, with many connections made, each with its own port number pair. If we're unlucky, translations can be dropped because the new address/port pair mapping already exists. To mitigate this problem, we add in port translation or port mapping: .nf map le0 10.0.0.0/8 -> 192.168.55.0/24 portmap tcp/udp auto .fi .PP In this instance, the word "auto" tells IPFilter to calculate a private range of port numbers for each address on the LHS to use without fear of them being trampled by others. This can lead to problems if there are connections being generated more quickly than IPFilter can expire them. In this instance, and if we want to get away from a private range of port numbers, we can say: .nf map le0 10.0.0.0/8 -> 192.168.55.0/24 portmap tcp/udp 5000:65000 .fi .PP And now each connection through le0 will add to the enumeration of the port number space 5000-65000 as well as the IP address subnet of 192.168.55.0/24. .PP If the new addresses to be used are in a consecutive range, rather than a complete subnet, we can express this as: .nf map le0 10.0.0.0/8 -> range 192.168.55.10-192.168.55.249 portmap tcp/udp 5000:65000 .fi .PP This tells IPFilter that it has a range of 240 IP address to use, from 192.168.55.10 to 192.168.55.249, inclusive. .PP If there were several ranges of addresses for use, we can use each one in a round-robin fashion as followed: .nf map le0 10.0.0.0/8 -> range 192.168.55.10-192.168.55.29 portmap tcp/udp 5000:65000 round-robin map le0 10.0.0.0/8 -> range 192.168.55.40-192.168.55.49 portmap tcp/udp 5000:65000 round-robin .fi .PP To specify translation rules that impact a specific IP protocol, the protocol name or number is appended to the rule like this: .nf map le0 10.0.0.0/8 -> 192.168.55.0/24 tcp/udp map le0 10.0.0.0/8 -> 192.168.55.1/32 icmp map le0 10.0.0.0/8 -> 192.168.55.2/32 gre .fi .PP For TCP connections exiting a connection such as PPPoE where the MTU is slightly smaller than normal ethernet, it can be useful to reduce the Maximum Segment Size (MSS) offered by the internal machines to match, reducing the liklihood that the either end will attempt to send packets -that are too big and result in fragmentation. This is acheived using the +that are too big and result in fragmentation. This is achieved using the .B mssclamp option with TCP .B map rules like this: .nf map pppoe0 0/0 -> 0/32 mssclamp 1400 tcp .fi .PP For ICMP packets, we can map the ICMP id space in query packets: .nf map le0 10.0.0.0/8 -> 192.168.55.1/32 icmpidmap icmp 1000:20000 .fi .PP If we wish to be more specific about our initial matching criteria on the LHS, we can expand to using a syntax more similar to that in .B ipf.conf(5) : .nf map le0 from 10.0.0.0/8 to 26.0.0.0/8 -> 192.168.55.1 map le0 from 10.0.0.0/8 port > 1024 to 26.0.0.0/8 -> 192.168.55.2 portmap 5000:9999 tcp/udp map le0 from 10.0.0.0/8 ! to 26.0.0.0/8 -> 192.168.55.3 portmap 5000:9999 tcp/udp .fi .TP .B NOTE: negation matching with source addresses is .B NOT possible with .B map / .B map-block rules. .PP The NAT code has builtin default timeouts for TCP, UDP, ICMP and another for all other protocols. In general, the timeout for an entry to be deleted shrinks once a reply packet has been seen (excluding TCP.) If you wish to specify your own timeouts, this can be achieved either by setting one timeout for both directions: .nf map le0 0/0 -> 0/32 gre age 30 .fi .PP or setting a different timeout for the reply: .nf map le0 from any to any port = 53 -> 0/32 age 60/10 udp .fi .PP A pressing problem that many people encounter when using NAT is that the address protocol can be embedded inside an application's communication. To address this problem, IPFilter provides a number of built-in proxies for the more common trouble makers, such as FTP. These proxies can be used as follows: .nf map le0 0/0 -> 0/32 proxy port 21 ftp/tcp .fi .PP In this rule, the word "proxy" tells us that we want to connect up this translation with an internal proxy. The "port 21" is an extra restriction that requires the destination port number to be 21 if this rule is to be activated. The word "ftp" is the proxy identifier that the kernel will try and resolve internally, "tcp" the protocol that packets must match. .PP -See below for a list of proxies and their relative staus. +See below for a list of proxies and their relative status. .PP To associate NAT rules with filtering rules, it is possible to set and match tags during either inbound or outbound processing. At present the tags for forwarded packets are not preserved by forwarding, so once the packet leaves IPFilter, the tag is forgotten. For .B map rules, we can match tags set by filter rules like this: .nf map le0 0/0 -> 0/32 proxy portmap 5000:5999 tag lan1 tcp .fi .PP This would be used with "pass out" rules that includes a stanza such as "set-tag (nat = lan1)". .PP If the interface in which packets are received is different from the interface on which packets are sent out, then the translation rule needs to be written to take this into account: .nf map hme0,le0 0/0 -> 0/32 .fi .PP Although this might seem counterintuitive, the interfaces when listed in rules for .B ipnat.conf are always in the .I inbound , .I outbound order. In this case, hme0 would be the return interface and le0 would be the outgoing interface. If you wish to allow return packets on any interface, the correct syntax to use would be: .nf map *,le0 0/0 -> 0/32 .fi .LP A special variant of .B map rules exists, called .B map-block. This command is intended for use when there is a large network to be mapped onto a smaller network, where the difference in netmasks is upto 14 bits difference in size. This is achieved by dividing the address space and port space up to ensure that each source address has its own private range of ports to use. For example, this rule: .nf map-block ppp0 172.192.0.0/16 -> 209.1.2.0/24 ports auto .fi .PP would result in 172.192.0.0/24 being mapped to 209.1.2.0/32 with each address, from 172.192.0.0 to 172.192.0.255 having 252 ports of its own. As opposed to the above use of \fBmap\fP, if for some reason the user of (say) 172.192.0.2 wanted 260 simultaneous connections going out, they would be limited to 252 with \fBmap-block\fP but would just \fImove on\fP to the next IP address with the \fBmap\fP command. .SS Extended matching .PP If it is desirable to match on both the source and destination of a packet before applying an address translation to it, this can be achieved by using the same from-to syntax as is used in \fBipf.conf\fP(5). What follows applies equally to the .B map rules discussed above and .B rdr rules discussed below. A simple example is as follows: .nf map bge0 from 10.1.0.0/16 to 192.168.1.0/24 -> 172.12.1.4 .fi .PP This would only match packets that are coming from hosts that have a source address matching 10.1.0.0/16 and a destination matching 192.168.1.0/24. This can be expanded upon with ports for TCP like this: .nf rdr bge0 from 10.1.0.0/16 to any port = 25 -> 127.0.0.1 port 2501 tcp .fi .PP Where only TCP packets from 10.1.0.0/16 to port 25 will be redirected to port 2501. .PP As with \fBipf.conf\fR(5), if we have a large set of networks or addresses that we would like to match up with then we can define a pool using \fBippool\fR(8) in \fBippool.conf\fR(5) and then refer to it in an \fBipnat\fR rule like this: .nf map bge0 from pool/100 to any port = 25 -> 127.0.0.1 port 2501 tcp .fi .TP .B NOTE: In this situation, the rule is considered to have a netmask of "0" and thus is looked at last, after any rules with /16's or /24's in them, .I even if the defined pool only has /24's or /32's. Pools may also be used .I wherever the from-to syntax in \fBipnat.conf\fR(5) is allowed. .SH INBOUND DESTINATION TRANSLATION (redirection) .PP Redirection of packets is used to change the destination fields in a packet and is supported for packets that are moving \fIin\fP on a network interface. While the same general syntax for .B map rules is supported, there are differences and limitations. .PP Firstly, by default all redirection rules target a single IP address, not a network or range of network addresses, so a rule written like this: .nf rdr le0 0/0 -> 192.168.1.0 .fi .PP Will not spread packets across all 256 IP addresses in that class C network. If you were to try a rule like this: .nf rdr le0 0/0 -> 192.168.1.0/24 .fi .PP then you will receive a parsing error. .PP The from-to source-destination matching used with .B map rules can be used with rdr rules, along with negation, however the restriction moves - only a source address match can be negated: .nf rdr le0 from 1.1.0.0/16 to any -> 192.168.1.3 rdr le0 ! from 1.1.0.0/16 to any -> 192.168.1.4 .fi .PP -If there is a consective set of addresses you wish to spread the packets +If there is a consecutive set of addresses you wish to spread the packets over, then this can be done in one of two ways, the word "range" optional to preserve: .nf rdr le0 0/0 -> 192.168.1.1 - 192.168.1.5 rdr le0 0/0 -> range 192.168.1.1 - 192.168.1.5 .fi .PP If there are only two addresses to split the packets across, the recommended method is to use a comma (",") like this: .nf rdr le0 0/0 -> 192.168.1.1,192.168.1.2 .fi .PP If there is a large group of destination addresses that are somewhat disjoint in nature, we can cycle through them using a .B round-robin technique like this: .nf rdr le0 0/0 -> 192.168.1.1,192.168.1.2 round-robin rdr le0 0/0 -> 192.168.1.5,192.168.1.7 round-robin rdr le0 0/0 -> 192.168.1.9 round-robin .fi .PP -If there are a large number of redirect rules and hosts being targetted +If there are a large number of redirect rules and hosts being targeted then it may be desirable to have all those from a single source address -be targetted at the same destination address. To achieve this, the +be targeted at the same destination address. To achieve this, the word .B sticky is appended to the rule like this: .nf rdr le0 0/0 -> 192.168.1.1,192.168.1.2 sticky rdr le0 0/0 -> 192.168.1.5,192.168.1.7 round-robin sticky rdr le0 0/0 -> 192.168.1.9 round-robin sticky .fi .PP The .B sticky feature can only be combined with .B round-robin -and the use of comma. +and the use of a comma. .PP -For TCP and UDP packets, it is possible to both match on the destiantion +For TCP and UDP packets, it is possible to both match on the destination port number and to modify it. For example, to change the destination port from 80 to 3128, we would use a rule like this: .nf rdr de0 0/0 port 80 -> 127.0.0.1 port 3128 tcp .fi .PP If a range of ports is given on the LHS and a single port is given on the RHS, the entire range of ports is moved. For example, if we had this: .nf rdr le0 0/0 port 80-88 -> 127.0.0.1 port 3128 tcp .fi .PP then port 80 would become 3128, port 81 would become 3129, etc. If we want to redirect a number of different pots to just a single port, an equals sign ("=") is placed before the port number on the RHS like this: .nf rdr le0 0/0 port 80-88 -> 127.0.0.1 port = 3128 tcp .fi .PP In this case, port 80 goes to 3128, port 81 to 3128, etc. .PP As with .B map rules, it is possible to manually set a timeout using the .B age option, like this: .nf rdr le0 0/0 port 53 -> 127.0.0.1 port 10053 udp age 5/5 .fi .PP The use of proxies is not restricted to .B map rules and outbound sessions. Proxies can also be used with redirect rules, although the syntax is slightly different: .nf rdr ge0 0/0 port 21 -> 127.0.0.1 port 21 tcp proxy ftp .fi .PP For .B rdr rules, the interfaces supplied are in the same order as .B map rules - input first, then output. In situations where the outgoing interface is not certain, it is also possible to use a wildcard ("*") to effect a match on any interface. .nf rdr le0,* 0/0 -> 192.168.1.0 .fi .PP A single rule, with as many options set as possible would look something like this: .nf rdr le0,ppp0 9.8.7.6/32 port 80 -> 1.1.1.1,1.1.1.2 port 80 tcp round-robin frag age 40/40 sticky mssclamp 1000 tag tagged .fi .SH REWRITING SOURCE AND DESTINATION .PP Whilst the above two commands provide a lot of flexibility in changing addressing fields in packets, often it can be of benefit to translate \fIboth\fP source \fBand\fR destination at the same time or to change the source address on input or the destination address on output. Doing all of these things can be accomplished using .B rewrite NAT rules. .PP A .B rewrite rule requires the same level of packet matching as before, protocol and source/destination information but in addition allows either .B in or .B out to be specified like this: .nf rewrite in on ppp0 proto tcp from any to any port = 80 -> src 0/0 dst 127.0.0.1,3128; rewrite out on ppp0 from any to any -> src 0/32 dst 10.1.1.0/24; .fi .PP On the RHS we can specify both new source and destination information to place into the packet being sent out. As with other rules used in \fBipnat.conf\fR, there are shortcuts syntaxes available to use the original address information (\fB0/0\fR) and the address associated with the network interface (\fB0/32\fR.) For TCP and UDP, both address and port information can be changed. At present it is only possible to specify either a range of port numbers to be used (\fBX-Y\fR) or a single port number (\fB= X\fR) as follows: .nf rewrite in on le0 proto tcp from any to any port = 80 -> src 0/0,2000-20000 dst 127.0.0.1,port = 3128; .fi .PP There are four fields that are stepped through in enumerating the number space available for creating a new destination: .LP source address .LP source port .LP destination address .LP destination port .PP If one of these happens to be a static then it will be skipped and the next one incremented. As an example: .nf rewrite out on le0 proto tcp from any to any port = 80 -> src 1.0.0.0/8,5000-5999 dst 2.0.0.0/24,6000-6999; .fi .PP The translated packets would be: .LP 1st src=1.0.0.1,5000 dst=2.0.0.1,6000 .LP 2nd src=1.0.0.2,5000 dst=2.0.0.1,6000 .LP 3rd src=1.0.0.2,5001 dst=2.0.0.1,6000 .LP 4th src=1.0.0.2,5001 dst=2.0.0.2,6000 .LP 5th src=1.0.0.2,5001 dst=2.0.0.2,6001 .LP 6th src=1.0.0.3,5001 dst=2.0.0.2,6001 .PP and so on. .PP As with .B map rules, it is possible to specify a range of addresses by including the word \fIrange\fR before the addresses: .nf rewrite from any to any port = 80 -> src 1.1.2.3 - 1.1.2.6 dst 2.2.3.4 - 2.2.3.6; .fi .SH DIVERTING PACKETS .PP If you'd like to send packets to a UDP socket rather than just another computer to be decapsulated, this can be achieved using a .B divert rule. .PP Divert rules can be used with both inbound and outbound packet matching however the rule .B must specify host addresses for the outer packet, not ranges of addresses or netmasks, just single addresses. Additionally the syntax must supply required information for UDP. An example of what a divert rule looks ike is as follows: .nf divert in on le0 proto udp from any to any port = 53 -> src 192.1.1.1,54 dst 192.168.1.22.1,5300; .fi .PP On the LHS is a normal set of matching capabilities but on the RHS it is a requirement to specify both the source and destination addresses and ports. .PP -As this feature is intended to be used with targetting packets at sockets +As this feature is intended to be used with targeting packets at sockets and not IPFilter running on other systems, there is no rule provided to \fIundivert\fR packets. .TP .B NOTE: Diverted packets \fImay\fP be fragmented if the addition of the encapsulating IP header plus UDP header causes the packet to exceed the size allowed by the outbound network interface. At present it is not possible to cause Path MTU discovery to happen as this feature is intended to be transparent to both endpoints. .B Path MTU Discovery If Path MTU discovery is being used and the "do not fragment" flag is set in packets to be encapsulated, an ICMP error message will be sent back to the sender if the new packet would need to be fragmented. .SH COMMON OPTIONS This section deals with options that are available with all rules. .TP .B purge When the purge keyword is added to the end of a NAT rule, it will cause all of the active NAT sessions to be removed when the rule is removed as an individual operation. If all of the NAT rules are flushed out, it is expected that the operator will similarly flush the NAT table and thus NAT sessions are not removed when the NAT rules are flushed out. .SH RULE ORDERING .PP .B NOTE: Rules in .B ipnat.conf are read in sequentially as listed and loaded into the kernel in this fashion .B BUT packet matching is done on \fBnetmask\fR, going from 32 down to 0. If a rule uses .B pool or .B hash to reference a set of addresses or networks, the netmask value for these fields is considered to be "0". So if your .B ipnat.conf has the following rules: .nf rdr le0 192.0.0.0/8 port 80 -> 127.0.0.1 3132 tcp rdr le0 192.2.0.0/16 port 80 -> 127.0.0.1 3131 tcp rdr le0 from any to pool/100 port 80 -> 127.0.0.1 port 3130 tcp rdr le0 192.2.2.0/24 port 80 -> 127.0.0.1 3129 tcp rdr le0 192.2.2.1 port 80 -> 127.0.0.1 3128 tcp .fi .PP then the rule with 192.2.2.1 will match \fBfirst\fR, regardless of where it appears in the ordering of the above rules. In fact, the order in which they would be used to match a packet is: .nf rdr le0 192.2.2.1 port 80 -> 127.0.0.1 3128 tcp rdr le0 192.2.2.0/24 port 80 -> 127.0.0.1 3129 tcp rdr le0 192.2.0.0/16 port 80 -> 127.0.0.1 3131 tcp rdr le0 192.0.0.0/8 port 80 -> 127.0.0.1 3132 tcp rdr le0 from any to pool/100 port 80 -> 127.0.0.1 port 3130 tcp .fi .PP where the first line is actually a /32. .PP If your .B ipnat.conf file has entries with matching target fields (source address for .B map rules and destination address for .B rdr rules), then the ordering in the .B ipnat.conf file does matter. So if you had the following: .nf rdr le0 from 1.1.0.0/16 to 192.2.2.1 port 80 -> 127.0.0.1 3129 tcp rdr le0 from 1.1.1.0/24 to 192.2.2.1 port 80 -> 127.0.0.1 3128 tcp .fi .PP Then no packets will match the 2nd rule, they'll all match the first. .SH IPv6 .PP In all of the examples above, where an IPv4 address is present, an IPv6 address can also be used. All rules must use either IPv4 addresses with both halves of the NAT rule or IPv6 addresses for both halves. Mixing IPv6 addresses with IPv4 addresses, in a single rule, will result in an error. .PP For shorthand notations such as "0/32", the equivalent for IPv6 is "0/128". IPFilter will treat any netmask greater than 32 as an implicit direction that the address should be IPv6, not IPv4. To be unambiguous with 0/0, for IPv6 use ::0/0. .SH KERNEL PROXIES .PP IP Filter comes with a few, simple, proxies built into the code that is loaded into the kernel to allow secondary channels to be opened without forcing the packets through a user program. The current state of the proxies is listed below, as one of three states: .HP Aging - protocol is roughly understood from the time at which the proxy was written but it is not well tested or maintained; .HP Developmental - basic functionality exists, works most of the time but may be problematic in extended real use; .HP Experimental - rough support for the protocol at best, may or may not work as testing has been at best sporadic, possible large scale changes to the code in order to properly support the protocol. .HP Mature - well tested, protocol is properly understood by the proxy; .PP The currently compiled in proxy list is as follows: .TP FTP - Mature (map ... proxy port ftp ftp/tcp) .TP IRC - Experimental (proxy port 6667 irc/tcp) .TP rpcbind - Experimental .TP PPTP - Experimental .TP H.323 - Experimental (map ... proxy port 1720 h323/tcp) .TP Real Audio (PNA) - Aging .TP DNS - Developmental (map ... proxy port 53 dns/udp { block .cnn.com; }) .TP IPsec - Developmental (map ... proxy port 500 ipsec/tcp) .TP netbios - Experimental .TP R-command - Mature (map ... proxy port shell rcmd/tcp) .SH KERNEL PROXIES .SH FILES /dev/ipnat .br /etc/protocols .br /etc/services .br /etc/hosts .SH SEE ALSO ipnat(4), hosts(5), ipf(5), services(5), ipf(8), ipnat(8) diff --git a/sbin/ipf/ipscan/ipscan.5 b/sbin/ipf/ipscan/ipscan.5 index 345561f9bb91..72b3f92a25a0 100644 --- a/sbin/ipf/ipscan/ipscan.5 +++ b/sbin/ipf/ipscan/ipscan.5 @@ -1,51 +1,51 @@ .\" .TH IPSCAN 5 .SH NAME ipscan, ipscan.conf \- ipscan file format .SH DESCRIPTION .PP WARNING: This feature is to be considered experimental and may change significantly until a final implementation is drawn up. .PP The format for files accept by ipscan currently follow this rough grammar: .LP .nf line ::= name ":" matchup [ "," matchup ] "=" action . matchup ::= "(" ")" | "(" literal ")" | "(" literal "," match ")" . action ::= result | result "else" result . result ::= "close" | "track" | redirect . redirect ::= "redirect" ip-address [ "(" "," port-number ")" ] . match ::= { match-char } match-char ::= "*" | "?" | "." .fi .PP In this example an ip-address is a dotted-quad IPv4 address and a port-number -is a number betwee 1 and 65535, inclusive. The match string is must be of +is a number between 1 and 65535, inclusive. The match string is must be of same length as the literal string that it is matching (literal). The length of either string is limited to 16 bytes. .PP Currently, the redirect option is not yet been implemented. .LP .nf # # * = match any character, . = exact match, ? = case insensitive # # Scan for anything that looks like HTTP and redirect it to the local # proxy. One catch - this feature (redirect) is not yet implemented. # http : ("GET ", "???." ) = redirect(127.0.0.1) # # Track ssh connections (i.e do nothing) # ssh : (), ("SSH-") = track # # Things which look like smtp to be tracked else closed. # Client can start with EHLO (ESMTP) or HELO (SMTP). # smtp : ("HELO ", "**??."), ("220 ", "....") = track else close # .fi .SH FILES /etc/ipscan.conf .SH SEE ALSO ipscan(8) diff --git a/sbin/ipf/libipf/interror.c b/sbin/ipf/libipf/interror.c index a4c0b1c56b8e..981823ca6bb9 100644 --- a/sbin/ipf/libipf/interror.c +++ b/sbin/ipf/libipf/interror.c @@ -1,580 +1,580 @@ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * * $Id: interror.c,v 1.9.2.12 2012/07/22 08:03:39 darren_r Exp $ */ #include "ipf.h" #include #include typedef struct { int iee_number; char *iee_text; } ipf_error_entry_t; static ipf_error_entry_t *find_error(int); #define IPF_NUM_ERRORS 477 /* * NO REUSE OF NUMBERS! * * IF YOU WANT TO ADD AN ERROR TO THIS TABLE, _ADD_ A NEW NUMBER. * DO _NOT_ USE AN EMPTY NUMBER OR FILL IN A GAP. */ static ipf_error_entry_t ipf_errors[IPF_NUM_ERRORS] = { { 1, "auth table locked/full" }, { 2, "" }, { 3, "copyinptr received bad address" }, { 4, "copyoutptr received bad address" }, { 5, "" }, { 6, "cannot load a rule with FR_T_BUILTIN flag set" }, { 7, "internal rule without FR_T_BUILDINT flag set" }, { 8, "no data provided with filter rule" }, { 9, "invalid ioctl for rule" }, { 10, "rule protocol is not 4 or 6" }, { 11, "cannot find rule function" }, { 12, "cannot find rule group" }, { 13, "group in/out does not match rule in/out" }, { 14, "rule without in/out does not belong to a group" }, { 15, "cannot determine where to append rule" }, { 16, "malloc for rule data failed" }, { 17, "copyin for rule data failed" }, { 18, "" }, { 19, "zero data size for BPF rule" }, { 20, "BPF validation failed" }, { 21, "incorrect data size for IPF rule" }, { 22, "'keep state' rule included 'with oow'" }, { 23, "bad interface index with dynamic source address" }, { 24, "bad interface index with dynamic dest. address" }, { 25, "match array verif failed for filter rule" }, { 26, "bad filter rule type" }, { 27, "rule not found for zero'stats" }, { 28, "copyout failed for zero'ing stats" }, { 29, "rule not found for removing" }, { 30, "cannot remove internal rule" }, { 31, "rule in use" }, { 32, "rule already exists" }, { 33, "no memory for another rule" }, { 34, "could not find function" }, { 35, "copyout failed for resolving function name -> addr" }, { 36, "copyout failed for resolving function addr -> name" }, { 37, "function name/addr resolving search failed" }, { 38, "group map cannot find it's hash table" }, { 39, "group map hash-table in/out do not match rule" }, { 40, "bcopyout failed for SIOCIPFINTERROR" }, { 41, "" }, { 42, "ipfilter not enabled for NAT ioctl" }, { 43, "ipfilter not enabled for state ioctl" }, { 44, "ipfilter not enabled for auth ioctl" }, { 45, "ipfilter not enabled for sync ioctl" }, { 46, "ipfilter not enabled for scan ioctl" }, { 47, "ipfilter not enabled for lookup ioctl" }, { 48, "unrecognised device minor number for ioctl" }, { 49, "unrecognised object type for copying in ipfobj" }, { 50, "mismatching object type for copying in ipfobj" }, { 51, "object size too small for copying in ipfobj" }, { 52, "object size mismatch for copying in ipfobj" }, { 53, "compat object size too small for copying in ipfobj" }, { 54, "compat object size mismatch for copying in ipfobj" }, { 55, "error doing copyin of data for in ipfobj" }, { 56, "unrecognised object type for size copy in ipfobj" }, { 57, "object size too small for size copy in ipfobj" }, { 58, "mismatching object type for size copy in ipfobj" }, { 59, "object size mismatch for size copy in ipfobj" }, { 60, "compat object size mismatch for size copy in ipfobj" }, { 61, "error doing size copyin of data for in ipfobj" }, { 62, "bad object type for size copy out ipfobj" }, { 63, "mismatching object type for size copy out ipfobj" }, { 64, "object size mismatch for size copy out ipfobj" }, { 65, "compat object size wrong for size copy out ipfobj" }, { 66, "error doing size copyout of data for out ipfobj" }, { 67, "unrecognised object type for copying out ipfobj" }, { 68, "mismatching object type for copying out ipfobj" }, { 69, "object size too small for copying out ipfobj" }, { 70, "object size mismatch for copying out ipfobj" }, { 71, "compat object size too small for copying out ipfobj" }, { 72, "compat object size mismatch for copying out ipfobj" }, { 73, "error doing copyout of data for out ipfobj" }, { 74, "attempt to add existing tunable name" }, { 75, "cannot find tunable name to delete" }, { 76, "internal data too big for next tunable" }, { 77, "could not find tunable" }, { 78, "tunable can only be changed when ipfilter disabled" }, { 79, "new tunable value outside accepted range" }, { 80, "ipftune called for unrecognised ioctl" }, { 81, "" }, { 82, "could not find token to delete" }, { 83, "" }, { 84, "attempt to get next rule when no more exist" }, { 85, "value for iri_inout outside accepted range" }, { 86, "value for iri_active outside accepted range" }, { 87, "value for iri_nrules is 0" }, { 88, "NULL pointer specified for where to copy rule to" }, { 89, "copyout of rule failed" }, { 90, "" }, { 91, "could not get token for rule iteration" }, { 92, "unrecognised generic iterator" }, { 93, "could not find token for generic iterator" }, { 94, "need write permissions to disable/enable ipfilter" }, { 95, "error copying in enable/disable value" }, { 96, "need write permissions to set ipf tunable" }, { 97, "need write permissions to set ipf flags" }, { 98, "error doing copyin of ipf flags" }, { 99, "error doing copyout of ipf flags" }, { 100, "need write permissions to add another rule" }, { 101, "need write permissions to insert another rule" }, { 102, "need write permissions to swap active rule set" }, { 103, "error copying out current active rule set" }, { 104, "need write permissions to zero ipf stats" }, { 105, "need write permissions to flush ipf v4 rules" }, { 106, "error copying out v4 flush results" }, { 107, "error copying in v4 flush command" }, { 108, "need write permissions to flush ipf v6 rules" }, { 109, "error copying out v6 flush results" }, { 110, "error copying in v6 flush command" }, { 111, "error copying in new lock state for ipfilter" }, { 112, "need write permissions to flush ipf logs" }, { 113, "error copying out results of log flush" }, { 114, "need write permissions to resync ipf" }, { 115, "unrecognised ipf ioctl" }, { 116, "error copying in match array" }, { 117, "match array type is not IPFOBJ_IPFEXPR" }, { 118, "bad size for match array" }, - { 119, "cannot allocate memory for match aray" }, + { 119, "cannot allocate memory for match array" }, { 120, "error copying in match array" }, { 121, "error verifying contents of match array" }, { 122, "need write permissions to set ipf lock status" }, { 123, "error copying in data for function resolution" }, { 124, "error copying in ipfobj structure" }, { 125, "error copying in ipfobj structure" }, { 126, "error copying in ipfobj structure" }, { 127, "error copying in ipfobj structure" }, { 128, "no memory for filter rule comment" }, { 129, "error copying in filter rule comment" }, { 130, "error copying out filter rule comment" }, { 131, "no memory for new rule alloc buffer" }, { 132, "cannot find source lookup pool" }, { 133, "unknown source address type" }, { 134, "cannot find destination lookup pool" }, { 135, "unknown destination address type" }, { 136, "icmp head group name index incorrect" }, { 137, "group head name index incorrect" }, { 138, "group name index incorrect" }, { 139, "to interface name index incorrect" }, { 140, "dup-to interface name index incorrect" }, { 141, "reply-to interface name index incorrect" }, { 142, "could not initialise call now function" }, { 143, "could not initialise call function" }, { 144, "could not find destination list" }, { 145, "auth rules cannot have dup/to/fastroute" }, { 146, "incorrect size for object to copy out" }, { 147, "object type out of bounds for kernel copyout" }, { 148, "object size too small for kernel copyout" }, { 149, "object size validation failed for kernel copyout" }, { 150, "error copying data out for kernel copyout" }, { 151, "version mismatch for kernel copyout" }, /* -------------------------------------------------------------------------- */ { 10001, "could not find token for auth iterator" }, { 10002, "write permissions require to add/remove auth rule" }, { 10003, "need write permissions to set auth lock" }, { 10004, "error copying out results of auth flush" }, { 10005, "unknown auth ioctl" }, { 10006, "can only append or remove preauth rules" }, { 10007, "NULL pointers passed in for preauth remove" }, { 10008, "preauth rule not found to remove" }, { 10009, "could not malloc memory for preauth entry" }, { 10010, "unrecognised preauth rule ioctl command" }, { 10011, "iterator data supplied with NULL pointer" }, { 10012, "unknown auth iterator type" }, { 10013, "iterator error copying out auth data" }, { 10014, "sleep waiting for auth packet interrupted" }, { 10015, "bad index supplied in auth reply" }, { 10016, "error injecting outbound packet back into kernel" }, { 10017, "error injecting inbound packet back into kernel" }, { 10018, "could not attempt to inject packet back into kernel" }, { 10019, "packet id does not match" }, /* -------------------------------------------------------------------------- */ { 20001, "invalid frag token data pointer supplied" }, { 20002, "error copying out frag token data" }, { 20003, "can only copy one fragment state entry at a time" }, /* -------------------------------------------------------------------------- */ { 30001, "incorrect object size to get hash table stats" }, { 30002, "could not malloc memory for new hash table" }, { 30003, "error coping in hash table structure" }, { 30004, "hash table already exists" }, { 30005, "mismatch between new hash table and operation unit" }, { 30006, "could not malloc memory for hash table base" }, { 30007, "could not find hash table" }, { 30008, "mismatch between hash table and operation unit" }, { 30009, "could not find hash table for iterators next node" }, { 30010, "unknown iterator tpe" }, { 30011, "iterator error copying out hash table" }, { 30012, "iterator error copying out hash table entry" }, { 30013, "error copying out hash table statistics" }, { 30014, "table node delete structure wrong size" }, { 30015, "error copying in node to delete" }, { 30016, "table to delete node from does not exist" }, { 30017, "could not find table to remove node from" }, { 30018, "table node add structure wrong size" }, { 30019, "error copying in node to add" }, { 30020, "could not find table to add node to" }, { 30021, "node already exists in the table" }, { 30022, "could not find node to delete in table" }, { 30023, "uid mismatch on node to delete" }, { 30024, "object size incorrect for hash table" }, { 30025, "hash table size must be at least 1"}, { 30026, "cannot allocate memory for hash table context" }, /* -------------------------------------------------------------------------- */ { 40001, "invalid minor device number for log read" }, { 40002, "read size too small" }, { 40003, "interrupted waiting for log data to read" }, { 40004, "interrupted waiting for log data to read" }, { 40005, "read size too large" }, { 40006, "uiomove for read operation failed" }, /* -------------------------------------------------------------------------- */ { 50001, "unknown lookup ioctl" }, { 50002, "error copying in object data for add node" }, { 50003, "invalid unit for lookup add node" }, { 50004, "incorrect size for adding a pool node" }, { 50005, "error copying in pool node structure" }, { 50006, "mismatch in pool node address/mask families" }, { 50007, "could not find pool name" }, { 50008, "node already exists in pool" }, { 50009, "incorrect size for adding a hash node" }, { 50010, "error copying in hash node structure" }, { 50011, "could not find hash table name" }, { 50012, "unrecognised object type for lookup add node" }, { 50013, "invalid unit for lookup delete node" }, { 50014, "incorrect size for deleting a pool node" }, { 50015, "error copying in pool node structure" }, { 50016, "could not find pool name" }, { 50017, "could not find pool node" }, { 50018, "incorrect size for removing a hash node" }, { 50019, "error copying in hash node structure" }, { 50020, "could not find hash table name" }, { 50021, "unrecognised object type for lookup delete node" }, { 50022, "error copying in add table data" }, { 50023, "invalid unit for lookup add table" }, { 50024, "pool name already exists" }, { 50025, "hash table name already exists" }, { 50026, "unrecognised object type for lookup add table" }, { 50027, "error copying table data back out" }, { 50028, "error copying in remove table data" }, { 50029, "invalid unit for lookup remove table" }, { 50030, "unrecognised object type for lookup remove table" }, { 50031, "error copying in lookup stats structure" }, { 50032, "invalid unit for lookup stats" }, { 50033, "unrecognised object type for lookup stats" }, { 50034, "error copying in flush lookup data" }, { 50035, "invalid unit for lookup flush" }, { 50036, "incorrect table type for lookup flush" }, { 50037, "error copying out lookup flush results" }, { 50038, "invalid unit for lookup iterator" }, { 50039, "invalid unit for lookup iterator" }, { 50040, "could not find token for lookup iterator" }, { 50041, "unrecognised object type for lookup iterator" }, { 50042, "error copying in lookup delete node operation" }, /* -------------------------------------------------------------------------- */ { 60001, "insufficient privilege for NAT write operation" }, { 60002, "need write permissions to flush NAT logs" }, { 60003, "need write permissions to turn NAT logging on/off" }, { 60004, "error copying out current NAT log setting" }, { 60005, "error copying out bytes waiting to be read in NAT \ log" }, { 60006, "need write permissions to add NAT rule" }, { 60007, "NAT rule already exists" }, { 60008, "could not allocate memory for NAT rule" }, { 60009, "need write permissions to remove NAT rule" }, { 60010, "NAT rule could not be found" }, { 60011, "could not find NAT entry for redirect lookup" }, { 60012, "need write permissions to flush NAT table" }, { 60013, "error copying in NAT flush command" }, { 60014, "need write permissions to do matching NAT flush" }, { 60015, "need write permissions to set NAT lock" }, { 60016, "need write permissions to add entry to NAT table" }, { 60017, "NAT not locked for size retrieval" }, { 60018, "NAT not locked for fetching NAT table entry" }, { 60019, "error copying in NAT token data for deletion" }, { 60020, "unknown NAT ioctl" }, { 60021, "" }, { 60022, "resolving proxy name in NAT rule failed" }, { 60023, "only reply age specified in NAT rule" }, { 60024, "error doing copyin to determine NAT entry size" }, { 60025, "error copying out NAT size of 0" }, { 60026, "NAT entry not found" }, { 60027, "error doing copyout of NAT entry size" }, { 60028, "invalid data size for getting NAT entry" }, { 60029, "could not malloc temporary space for NAT entry" }, { 60030, "no NAT table entries present" }, { 60031, "NAT entry to get next from not found" }, { 60032, "not enough space for proxy structure" }, { 60033, "not enough space for private proxy data" }, { 60034, "NAT entry size is too large" }, { 60035, "could not malloc memory for NAT entry sratch space" }, { 60036, "" }, { 60037, "could not malloc memory for NAT entry" }, { 60038, "could not malloc memory for NAT entry rule" }, { 60039, "could not resolve NAT entry rule's proxy" }, { 60040, "cannot add outbound duplicate NAT entry" }, { 60041, "cannot add inbound duplicate NAT entry" }, { 60042, "cannot add NAT entry that is neither IN nor OUT" }, { 60043, "could not malloc memory for NAT proxy data" }, { 60044, "proxy data size too big" }, { 60045, "could not malloc proxy private data for NAT entry" }, { 60046, "could not malloc memory for new NAT filter rule" }, { 60047, "could not find existing filter rule for NAT entry" }, { 60048, "insertion into NAT table failed" }, { 60049, "iterator error copying out hostmap data" }, { 60050, "iterator error copying out NAT rule data" }, { 60051, "iterator error copying out NAT entry data" }, { 60052, "iterator data supplied with NULL pointer" }, { 60053, "unknown NAT iterator type" }, { 60054, "unknown next address type" }, { 60055, "iterator suppled with unknown type for get-next" }, { 60056, "unknown lookup group for next address" }, { 60057, "error copying out NAT log flush results" }, { 60058, "bucket table type is incorrect" }, { 60059, "error copying out NAT bucket table" }, { 60060, "function not found for lookup" }, { 60061, "address family not supported with SIOCSTPUT" }, { 60062, "unknown timeout name" }, { 60063, "cannot allocate new inbound NAT entry table" }, { 60064, "cannot allocate new outbound NAT entry table" }, { 60065, "cannot allocate new inbound NAT bucketlen table" }, { 60066, "cannot allocate new outbound NAT bucketlen table" }, { 60067, "cannot allocate new NAT rules table" }, { 60068, "cannot allocate new NAT hostmap table" }, { 60069, "new source lookup type is not dstlist" }, { 60070, "cannot allocate NAT rule scratch space" }, { 60071, "new destination lookup type is not dstlist" }, { 60072, "function not found for lookup (ipv6)" }, { 60073, "unknown lookup group for next address (ipv6)" }, { 60074, "unknown next address type (ipv6)" }, { 60075, "one object at a time must be copied" }, { 60076, "NAT ioctl denied in jail without VNET" }, /* -------------------------------------------------------------------------- */ { 70001, "incorrect object size to get pool stats" }, { 70002, "could not malloc memory for new pool node" }, { 70003, "invalid address length for new pool node" }, { 70004, "invalid mask length for new pool node" }, { 70005, "error adding node to pool" }, { 70006, "pool already exists" }, { 70007, "could not malloc memory for new pool" }, { 70008, "could not allocate radix tree for new pool" }, { 70009, "could not find pool" }, { 70010, "unknown pool name for iteration" }, { 70011, "unknown pool iterator" }, { 70012, "error copying out pool head" }, { 70013, "error copying out pool node" }, { 70014, "add node size incorrect" }, { 70015, "error copying in pool node" }, { 70016, "" }, { 70017, "cannot find pool for node" }, { 70018, "node entry already present in pool" }, { 70019, "delete node size incorrect" }, { 70020, "error copying in node to delete" }, { 70021, "cannot find pool to delete node from" }, { 70022, "cannot find node to delete in pool" }, { 70023, "pool name already exists" }, { 70024, "uid mismatch for node removal" }, { 70025, "stats device unit is invalid" }, { 70026, "error copying out statistics" }, { 70027, "could not remove node from radix tree" }, { 70028, "incorrect address length in pool node add" }, { 70029, "incorrect mask length in pool node add" }, { 70030, "incorrect address length in pool node remove" }, { 70031, "incorrect mask length in pool node remove" }, { 70032, "cannot allocate memory for pool context" }, { 70033, "cannot allocate memory for radix tree context" }, { 70034, "adding IPv6 node with incorrect address length" }, { 70035, "IPv4 address not masked" }, { 70036, "IPv6 address not masked" }, { 70037, "removing IPv6 node with incorrect address length" }, /* -------------------------------------------------------------------------- */ { 80001, "could not find proxy" }, { 80002, "proxy does not support control operations" }, { 80003, "could not allocate data to hold proxy operation" }, { 80004, "unknown proxy ioctl" }, { 80005, "could not copyin proxy control structure" }, { 80006, "DNS proxy could not find rule to delete" }, { 80007, "DNS proxy found existing matching rule" }, { 80008, "DNS proxy could not allocate memory for new rule" }, { 80009, "DNS proxy unknown command request" }, /* -------------------------------------------------------------------------- */ { 90001, "could not malloc space for new scan structure" }, { 90002, "scan tag already exists" }, { 90003, "scan structure in use" }, { 90004, "could not find matching scan tag for filter rule" }, { 90005, "could not copyout scan statistics" }, /* -------------------------------------------------------------------------- */ { 100001, "cannot find matching state entry to remove" }, { 100002, "error copying in v4 state flush command" }, { 100003, "error copying out v4 state flush results" }, { 100004, "error copying in v6 state flush command" }, { 100005, "error copying out v6 state flush results" }, { 100006, "" }, { 100007, "" }, { 100008, "need write permissions to flush state log" }, { 100009, "error copyout results of flushing state log" }, { 100010, "need write permissions to turn state logging on/off" }, { 100011, "error copying in new state logging state" }, { 100012, "error copying out current state logging state" }, { 100013, "error copying out bytes waiting to be read in state \ log" }, { 100014, "need write permissions to set state lock" }, { 100015, "need write permissions to add entry to state table" }, { 100016, "state not locked for size retrieval" }, { 100017, "error copying out hash table bucket lengths" }, { 100018, "could not find token for state iterator" }, { 100019, "error copying in state token data for deletion" }, { 100020, "unknown state ioctl" }, { 100021, "no state table entries present" }, { 100022, "state entry to get next from not found" }, { 100023, "could not malloc memory for state entry" }, { 100024, "could not malloc memory for state entry rule" }, { 100025, "could not copy back state entry to user space" }, { 100026, "iterator data supplied with NULL pointer" }, { 100027, "iterator supplied with 0 item count" }, { 100028, "iterator type is incorrect" }, { 100029, "invalid state token data pointer supplied" }, { 100030, "error copying out next state entry" }, { 100031, "unrecognised table request" }, { 100032, "error copying out bucket length data" }, { 100033, "could not find existing filter rule for state entry" }, { 100034, "could not find timeout name" }, { 100035, "could not allocate new state table" }, { 100036, "could not allocate new state bucket length table" }, /* -------------------------------------------------------------------------- */ { 110001, "sync write header magic number is incorrect" }, { 110002, "sync write header protocol is incorrect" }, { 110003, "sync write header command is incorrect" }, { 110004, "sync write header table number is incorrect" }, { 110005, "data structure too small for sync write operation" }, { 110006, "zero length data with sync write header" }, { 110007, "insufficient data for sync write" }, { 110008, "bad sync read size" }, { 110009, "interrupted sync read (solaris)" }, { 110010, "interrupted sync read (hpux)" }, { 110011, "interrupted sync read (osf)" }, { 110012, "interrupted sync read" }, { 110013, "could not malloc memory for sync'd state" }, { 110014, "could not malloc memory for sync-state list item" }, { 110015, "sync update could not find state" }, { 110016, "unrecognised sync state command" }, { 110017, "could not malloc memory for new sync'd NAT entry" }, { 110018, "could not malloc memory for sync-NAT list item" }, { 110019, "sync update could not find NAT entry" }, { 110020, "unrecognised sync NAT command" }, { 110021, "ioctls are not handled with sync" }, /* -------------------------------------------------------------------------- */ { 120001, "null data pointer for iterator" }, { 120002, "unit outside of acceptable range" }, { 120003, "unknown iterator subtype" }, { 120004, "cannot find dest. list for iteration" }, { 120005, "error copying out destination iteration list" }, { 120006, "error copying out destination iteration node" }, { 120007, "wrong size for frdest_t structure" }, { 120008, "cannot allocate memory for new destination node" }, { 120009, "error copying in destination node to add" }, { 120010, "could not find destination list to add node to" }, { 120011, "error copying in destination node to remove" }, { 120012, "could not find dest. list to remove node from" }, { 120013, "destination list already exists" }, { 120014, "could not allocate new destination table" }, { 120015, "could not find destination list to remove" }, { 120016, "destination list cannot be removed - it is busy" }, { 120017, "error copying in names for destination" }, { 120018, "destination name is too long/short" }, { 120019, "unrecognised address family in destination" }, { 120020, "" }, { 120021, "error copying in new destination table" }, { 120022, "cannot allocate memory for node table" }, { 120023, "stats object size is incorrect for dest. lists" }, { 120024, "stats device unit is invalid for dest. lists" }, { 120025, "error copying out dest. list statistics" }, { 120026, "cannot allocate memory for destination node" }, { 120027, "error copying in destination node" }, { 120028, "cannot allocate memory for destination context " }, /* -------------------------------------------------------------------------- */ { 130001, "ioctl denied by system security level" }, { 130002, "ioctl operation on invalid minor device" }, { 130003, "ioctl on device denied, ipfitler is disabled" }, { 130004, "ioctl command not allowed when disabled" }, { 130005, "ioctl denied due to insufficient authorisation" }, { 130006, "cannot read while ipfilter is disabled" }, { 130007, "read on minor device not supported" }, { 130008, "cannot write while ipfilter is disabled" }, { 130009, "write on minor device not supported" }, { 130010, "poll on minor device is not supported" }, { 130011, "error removing IPv4 filter hooks" }, { 130012, "error removing IPv6 filter hooks" }, { 130013, "attaching IPv4 hook failed" }, { 130014, "attaching IPv6 hook failed" }, { 130015, "ipf_init_all failed" }, { 130016, "finding pfil head failed" }, { 130017, "ipfilter is already initialised and running" }, { 130018, "ioctl denied in jail without VNET" }, }; static ipf_error_entry_t * find_error(int errnum) { ipf_error_entry_t *ie; int l = -1, r = IPF_NUM_ERRORS + 1, step; step = (r - l) / 2; while (step != 0) { ie = ipf_errors + l + step; if (ie->iee_number == errnum) return (ie); step = l + step; if (ie->iee_number > errnum) r = step; else l = step; step = (r - l) / 2; } return (NULL); } char * ipf_geterror(int fd, ioctlfunc_t *func) { static char text[80]; ipf_error_entry_t *ie; int errnum; if ((*func)(fd, SIOCIPFINTERROR, &errnum) == 0) { ie = find_error(errnum); if (ie != NULL) return (ie->iee_text); snprintf(text, sizeof(text), "unknown error %d", errnum); } else { snprintf(text, sizeof(text), "retrieving error number failed (%d)", errno); } return (text); } char * ipf_strerror(int errnum) { static char text[80]; ipf_error_entry_t *ie; ie = find_error(errnum); if (ie != NULL) return (ie->iee_text); snprintf(text, sizeof(text), "unknown error %d", errnum); return (text); } diff --git a/sbin/ipf/libipf/parseipfexpr.c b/sbin/ipf/libipf/parseipfexpr.c index 880258e5dd66..be86456e143b 100644 --- a/sbin/ipf/libipf/parseipfexpr.c +++ b/sbin/ipf/libipf/parseipfexpr.c @@ -1,277 +1,277 @@ #include "ipf.h" #include typedef struct ipfopentry { int ipoe_cmd; int ipoe_nbasearg; int ipoe_maxarg; int ipoe_argsize; char *ipoe_word; } ipfopentry_t; static ipfopentry_t opwords[17] = { { IPF_EXP_IP_ADDR, 2, 0, 1, "ip.addr" }, { IPF_EXP_IP6_ADDR, 2, 0, 4, "ip6.addr" }, { IPF_EXP_IP_PR, 1, 0, 1, "ip.p" }, { IPF_EXP_IP_SRCADDR, 2, 0, 1, "ip.src" }, { IPF_EXP_IP_DSTADDR, 2, 0, 1, "ip.dst" }, { IPF_EXP_IP6_SRCADDR, 2, 0, 4, "ip6.src" }, { IPF_EXP_IP6_DSTADDR, 2, 0, 4, "ip6.dst" }, { IPF_EXP_TCP_PORT, 1, 0, 1, "tcp.port" }, { IPF_EXP_TCP_DPORT, 1, 0, 1, "tcp.dport" }, { IPF_EXP_TCP_SPORT, 1, 0, 1, "tcp.sport" }, { IPF_EXP_TCP_FLAGS, 2, 0, 1, "tcp.flags" }, { IPF_EXP_UDP_PORT, 1, 0, 1, "udp.port" }, { IPF_EXP_UDP_DPORT, 1, 0, 1, "udp.dport" }, { IPF_EXP_UDP_SPORT, 1, 0, 1, "udp.sport" }, { IPF_EXP_TCP_STATE, 1, 0, 1, "tcp.state" }, { IPF_EXP_IDLE_GT, 1, 1, 1, "idle-gt" }, { -1, 0, 0, 0, NULL } }; int * parseipfexpr(char *line, char **errorptr) { int not, items, asize, *oplist, osize, i; char *temp, *arg, *s, *t, *ops, *error; ipfopentry_t *e; ipfexp_t *ipfe; asize = 0; error = NULL; oplist = NULL; temp = strdup(line); if (temp == NULL) { error = "strdup failed"; goto parseerror; } /* * Eliminate any white spaces to make parsing easier. */ for (s = temp; *s != '\0'; ) { if (ISSPACE(*s)) strcpy(s, s + 1); else s++; } /* * Parse the string. * It should be sets of "ip.dst=1.2.3.4/32;" things. * There must be a "=" or "!=" and it must end in ";". */ if (temp[strlen(temp) - 1] != ';') { error = "last character not ';'"; goto parseerror; } /* * Work through the list of complete operands present. */ for (ops = strtok(temp, ";"); ops != NULL; ops = strtok(NULL, ";")) { arg = strchr(ops, '='); if ((arg < ops + 2) || (arg == NULL)) { - error = "bad 'arg' vlaue"; + error = "bad 'arg' value"; goto parseerror; } if (*(arg - 1) == '!') { *(arg - 1) = '\0'; not = 1; } else { not = 0; } *arg++ = '\0'; for (e = opwords; e->ipoe_word; e++) { if (strcmp(ops, e->ipoe_word) == 0) break; } if (e->ipoe_word == NULL) { asprintf(&error, "keyword (%.10s) not found", ops); goto parseerror; } /* * Count the number of commas so we know how big to * build the array */ for (s = arg, items = 1; *s != '\0'; s++) if (*s == ',') items++; if ((e->ipoe_maxarg != 0) && (items > e->ipoe_maxarg)) { error = "too many items"; goto parseerror; } /* * osize will mark the end of where we have filled up to * and is thus where we start putting new data. */ osize = asize; asize += 4 + (items * e->ipoe_nbasearg * e->ipoe_argsize); if (oplist == NULL) oplist = calloc(asize + 2, sizeof(int)); else oplist = reallocarray(oplist, asize + 2, sizeof(int)); if (oplist == NULL) { error = "oplist alloc failed"; goto parseerror; } ipfe = (ipfexp_t *)(oplist + osize); osize += 4; ipfe->ipfe_cmd = e->ipoe_cmd; ipfe->ipfe_not = not; ipfe->ipfe_narg = items * e->ipoe_nbasearg; ipfe->ipfe_size = items * e->ipoe_nbasearg * e->ipoe_argsize; ipfe->ipfe_size += 4; for (s = arg; (*s != '\0') && (osize < asize); s = t) { /* * Look for the end of this arg or the ',' to say * there is another following. */ for (t = s; (*t != '\0') && (*t != ','); t++) ; if (*t == ',') *t++ = '\0'; if (!strcasecmp(ops, "ip.addr") || !strcasecmp(ops, "ip.src") || !strcasecmp(ops, "ip.dst")) { i6addr_t mask, addr; char *delim; delim = strchr(s, '/'); if (delim != NULL) { *delim++ = '\0'; if (genmask(AF_INET, delim, &mask) == -1) { error = "genmask failed"; goto parseerror; } } else { mask.in4.s_addr = 0xffffffff; } if (gethost(AF_INET, s, &addr) == -1) { error = "gethost failed"; goto parseerror; } oplist[osize++] = addr.in4.s_addr; oplist[osize++] = mask.in4.s_addr; #ifdef USE_INET6 } else if (!strcasecmp(ops, "ip6.addr") || !strcasecmp(ops, "ip6.src") || !strcasecmp(ops, "ip6.dst")) { i6addr_t mask, addr; char *delim; delim = strchr(s, '/'); if (delim != NULL) { *delim++ = '\0'; if (genmask(AF_INET6, delim, &mask) == -1) { error = "genmask failed"; goto parseerror; } } else { mask.i6[0] = 0xffffffff; mask.i6[1] = 0xffffffff; mask.i6[2] = 0xffffffff; mask.i6[3] = 0xffffffff; } if (gethost(AF_INET6, s, &addr) == -1) { error = "gethost failed"; goto parseerror; } oplist[osize++] = addr.i6[0]; oplist[osize++] = addr.i6[1]; oplist[osize++] = addr.i6[2]; oplist[osize++] = addr.i6[3]; oplist[osize++] = mask.i6[0]; oplist[osize++] = mask.i6[1]; oplist[osize++] = mask.i6[2]; oplist[osize++] = mask.i6[3]; #endif } else if (!strcasecmp(ops, "ip.p")) { int p; p = getproto(s); if (p == -1) goto parseerror; oplist[osize++] = p; } else if (!strcasecmp(ops, "tcp.flags")) { u_32_t mask, flags; char *delim; delim = strchr(s, '/'); if (delim != NULL) { *delim++ = '\0'; mask = tcpflags(delim); } else { mask = 0xff; } flags = tcpflags(s); oplist[osize++] = flags; oplist[osize++] = mask; } else if (!strcasecmp(ops, "tcp.port") || !strcasecmp(ops, "tcp.sport") || !strcasecmp(ops, "tcp.dport") || !strcasecmp(ops, "udp.port") || !strcasecmp(ops, "udp.sport") || !strcasecmp(ops, "udp.dport")) { char proto[4]; u_short port; strncpy(proto, ops, 3); proto[3] = '\0'; if (getport(NULL, s, &port, proto) == -1) goto parseerror; oplist[osize++] = port; } else if (!strcasecmp(ops, "tcp.state")) { oplist[osize++] = atoi(s); } else { error = "unknown word"; goto parseerror; } } } free(temp); if (errorptr != NULL) *errorptr = NULL; for (i = asize; i > 0; i--) oplist[i] = oplist[i - 1]; oplist[0] = asize + 2; oplist[asize + 1] = IPF_EXP_END; return (oplist); parseerror: if (errorptr != NULL) *errorptr = error; if (oplist != NULL) free(oplist); if (temp != NULL) free(temp); return (NULL); }