Вы находитесь на странице: 1из 33

V.0.4 If there are any questions or comments, please direct them to walt@erudition.net.

The newest copy of this HowTo can always be retrieved from www.freebsd-howto.com. All rights for the reproduction of this document are reserved. Summary. 1. 2. General Introduction to Packet Filters Enabling Ipfirewall(4) 2.1. 2.2. rc.firewall and OPEN firewalls Loading Rulesets 2.2.1. Pre-defined Firewall Types 2.2.2. Custom Firewall Types 3. Basic Ipfw(8) Rule Syntax 3.1. 3.2. 3.3. 3.4. Listing Rules Basic Commands and Actions Specifying Protocols Specifying the Source and Destination Addresses 3.4.1. Introduction to Bitmasks and Netmasks 3.4.2. Specifying Ports and Port Ranges 4. Advanced ipfw(8) Rule Syntax 4.1. 4.2. 4.3. "unreach" Action Interface and Flow Control Matching specific ICMP and TCP Packet Types 4.3.1. icmptypes 4.3.2. tcpflags, setup and established 4.3.3. ipoptions 4.4. 4.5. 5. Logging 5.1. 5.2. Logging Issues System Logging Configuration 5.2.1. Kernel Options 5.2.2. Configuring syslog(8) for Logging 5.2.3. Configuring newsyslog(8) for Log Rotation 5.3. 6. Rule Logging Configuration Catching Fragmented Packets UID and GID Based Filtering

Introduction to Stateless and Stateful Filtering 6.1. 6.2. 6.3. Basic Stateful Configuration Advanced Stateful Configuration Anatomy of a Dynamic Rule

7.

Traffic Shaping

7.1. 7.2.

Probability Matching Dummynet 7.2.1. Pipe Queues 7.2.2. Pipe Masks 7.2.3. Pipe Packet Reinjection

8.

Traffic Flow

Appendix A: Example Firewall Configurations

1.

General Introduction to Packet Filters

Ipfw(8), the command frontend to ipfirewall(4), is the most common IP filtering and traffic shaping facility in FreeBSD, and the one for which FreeBSD is ready to handle by default (although the firewall itself is disabled by default in the kernel). The logical operation if its rules is similar to many other packet filters, with the exception of IPFilter, whose default operation in handling rules is rather less efficient and requires greater care to tune it (if you're familiar with it, note the 'quick' keyword required for ipf(8) not to traverse the entire ruleset every time, etc). This is not to minimize the power of ipf(8), which has its own advantages. The ultimate decision as to which packet filtering facility one uses is a personal choice, unless one requires particular functionality not available in one or the other, although, we will delve into a rough comparison of the two later on. As indicated above, ipfirewall(4) is a packeting filtering firewall, which means that it acts by inspecting connexions on a packet-by-packet basis, and as of FreeBSD 4.0, can also perform rudimentary connexion oriented (stateful) filtering. On either count, it acts by filtering packets through one or more network interfaces. This behaviour is always transparent, that is, one will probably not be aware that a firewall is present until something is blocked. Firewall designs take on many shapes, but all can be broken down into two general policies: open and closed. The open firewall approach lets all packets through by default and only blocks that which is NOT desired, while on the other hand, the closed approach blocks all packets by default, and only lets through was IS desired. The latter allows for a much tighter firewall configuration, but is much trickier to setup because one can easily block out traffic that one's net requires, but one isn't aware of. 2. Enabling Ipfirewall(4)

Ipfirewall(4) can be enabled in two ways: add the appropriate options into your kernel configuration file and rebuild the kernel, or use kldload(8) to dynamically load the basic ipfw.ko module into the kernel. Either approach works well for enabling basic ipfirewall(4) operations, however, only the former allows you to add additional configuration options, such as logging. To dynamically load ipfw one can simply issue the following command: (root@nu)~># kldload ipfw (root@nu)~># Enabling ipfirewall(4) statically, the equivalent would be to add the following line into your kernel configuration file: options IPFIREWALL

Then, rebuilding and rebooting would enable ipfirewall(4) in the kernel statically. However, things are not as simple as they may seem; one can only do things exactly as above when one is in front of the console. Additional options are necessary to make the box usable. If you recall the discussion above concerning firewall policies (open and closed), you will understand why things can get very messy when you realize that the default firewall

policy is closed. As such, if you simply enable ipfirewall(4) without any further actions, all network traffic will be blocked. This can become a nightmare when enabling ipfirewall(4) remotely. Diasaster can be avoided, although, it is never recommened to remotely enable ipfirewall(4), either dynamically or statically. If you wish to dynamically load ipfirewall(4) remotely, anyhow, the follow command is recommended: (root@nu)~># kldload ipfw && \ ipfw -q add 65000 allow all from any to any (root@nu)~># This will automatically set a rule to allow all traffic instead of blocking it, so you don't cut yourself off from your remote box. Likewise, this is recommended on local boxes as well if they are connected to a network and you don't want to lose your connexion. Enabling ipfirewall(4) in the kernel statically in a remote box takes a little extra work. Once one has added the kernel option specified earlier, in the kernel configuration file, and rebuilt the kernel, one has to then set at least two ipfirewall(4) options in rc.conf so that when the box reboots, it will not be locked out by its own firewall with the default-to-close policy. firewall_enable="YES" firewall_type="OPEN" There are other firewall types defined in /etc/rc.firewall, but we will concern ourselves with those later. For now, a default open policy is good practice for the beginner. Alternatively, one can enable a default open policy for ipfw(8) in the kernel, instead, by also adding the following option into the kernel configuration file: options IPFIREWALL_DEFAULT_TO_ACCEPT

In this case, the rc.conf options noted above will not be *necessary* as we will not *need* to use /etc/rc.firewall to enable an open policy because it will be enabled by default in the kernel. However, even if one chooses to enable this in the kernel, it is good practice to enable the /etc/rc.conf options anyhow, because later we will be using /etc/rc.firewall to load custom rulesets. This also applies if one loads the kernel dynamically, because eventually one will reboot, and the kernel ipfw.ko module will not be automatically loaded. The /etc/rc.conf firewall enabling functions allow for a convenient place to load the ipfw.ko module. With the additional options for ipfirewall(4) that are available for enabling statically in the kernel, one soon realizes this is the better method for enabling ipfirewall(4) in earlier versions of FreeBSD. Aside from IPFIREWALL_DEFAULT_TO_ACCEPT, we also have: options options options IPFIREWALL_VERBOSE IPFIREWALL_FORWARD IPFIREWALL_VERBOSE_LIMIT=#

IPFIREWALL_VERBOSE allows one to log traffic with ipfirewall(4) by verbosely printing packet activity to syslogd(8) for every rule that has the "log" keyword. This will be more clearly explained later.

IPFIREWALL_FORWARD allows one to forward packets to other hosts with the 'fwd' command for ipfirewall(4), which will be dealt with in more depth later. IPFIREWALL_VERBOSE_LIMIT=# specifies a limit to logging packets from a particular rule. With this, syslogd(8) and one's console (unless disabled in /etc/syslog.conf, as will be shown later) will not be overwhelmed with messages from ipfirewall(4) activity. The "#" is replaced with the number of consecutive times one wishes to log a given rule being activated. For the most recent 4.x builds of FreeBSD, additional sysctl variables have been added which makes enabling ipfirewall(4) options with sysctl more practical. For the corresponding options enabled above in the kernel configuration file, one can issue the following sysctl commands: sysctl -w net.inet.ip.fw.verbose=1 sysctl -w net.inet.ip.fw.verbose_limit=# sysctl -w net.inet.ip.forwarding=1 If one has IPv6 enabled, then the following rules will apply to the corresponding firewall actions on IPv6 packets: options options options options OR sysctl -w net.inet6.ip6.forwarding=1 Note that there are no sysctl variables for controlling inet6 ipfirewall(4) verbosity. There are additional options associated with ipfirewall(4) that can be enabled in the kernel, but will not be discussed at this moment as they are not necessary for the basic firewall activities, and involve more complex routing situations. 2.1. rc.firewall and OPEN firewalls IPV6FIREWALL IPV6FIREWALL_VERBOSE IPV6FIREWALL_VERBOSE_LIMIT=100 IPV6FIREWALL_DEFAULT_TO_ACCEPT

Whether one specifies a firewall type or not, once firewall_enable="YES" is put into rc.conf and the system reboots, /etc/rc.firewall is started from rc.conf, and the following two commands are issued to ipfw(8) from within it: ${fwcmd} add 100 pass all from any to any via lo0 ${fwcmd} add 200 deny all from any to 127.0.0.0/8 {fwcmd} is defined early on in the rc.firewall script, depending on whether one specified that ipfw(8) run quietly (with the -q option) or not. The first rule allows all traffic via the loopback device (lo0) to pass, and the second rule blocks all traffic aimed at the localhost (127.0.0.0) network. The first rule is necessary to allow local IPC (inter-process communication) traffic, and the second rule is necessary so that no external packets can ever be allowed to reach the localhost address, which is the address on the loopback device, thus protecting one's local traffic. If these rules are missing, and the firewall defaults

to a closed policy, you will see RPC(3) services break during startup, among other things. Next, when one specifies a firewall type of "OPEN" in rc.conf, the following line in rc.firewall is activated: ${fwcmd} add 65000 pass all from any to any This allows all external traffic in (except to the localhost), and all internal traffic out. It fulfills the same task as enabling the IPFIREWALL_DEFAULT_TO_ACCEPT in the kernel. If the open policy is enabled in the kernel, then rule # 65535 will be automatically set to "allow ip from any to any" instead of "deny ip from any to any," thus making rule # 65000 as set in rc.firewall for the open policy redundant. As such, it is more apropos to indicate firewall type "UNKNOWN" if one enables an open policy in the kernel, and does not wish to enable any other rules. For one wishes to, for instance, simply enable the firewall to play with and see how it work,s or simply block packets from a single host, then leaving the firewall type open like this is sufficient and one can safely skip to section 3. However, if one wishes to use one of the pre-built rulesets in rc.firewall, or create one's own custom rulesets, then neither options (OPEN or UNKNOWN) are sufficient. 2.2. Loading Rulesets

There are two generally different things that can be done concerning rulesets: use one of the pre-built ones in rc.firewall, or create your own. The author recommends the latter for two reasons: - You can customize the firewall rules to your liking and needs without touching rc.firewall, which can be kept as a general reference. - You will be forced harder to become familiarized with ipfw(8) syntax, and as such, will become more comfortable with using ipfw(8). 2.2.1. Pre-defined Firewall Types Of course, the final decision is the administrator's. If one wishes to use one of the pre-built rulesets, then one should read through each of them in rc.firewall to become familiar with them before activating any of them. Which ruleset is loaded is controlled by the firewall_type="" option in rc.conf. Aside from the "OPEN" type, there are three more predefined types available: "CLIENT" This ruleset enables some basic rules. It allows all traffic from the local network (which could be a private network behind NAT) to itself. It blocks fragmented packets. It allows mail, DNS and NTP packets in and out of the network, and does not allow any host outside the private network to initiate TCP connexions with internal hosts. This would already be impossible if the network was behind a vanilla NAT configuration without any special proxies. This configuration will work with both a default open or closed policy. "SIMPLE" This ruleset is somewhat of any oxymoron - it is more

complex than the CLIENT configuration and requires some knowledge of internet RFCs to make sense of at first glance. It will attempt to stop spoofing by not allowing in external packets that have return addresses the same as that of any internal host. It will block all private-net-addressesed packets as defined by RFC 1918 from leaking in or out, and will block all additional non-routable networks as defined in the Manning internet draft (http://www.ietf.org/internet-drafts/draft-manning-dsua-03.txt). It will allow mail, www, DNS, NTP traffic and fragmented packets to pass through, and will not only block attempts for connexions to be initiated by outside hosts, like CLIENT, but will also log these attempts. "CLOSED" This is technically not a rulest, because it does not enable any rules. In fact, it does everything we've been warning not to do: allow the default closed policy to take hold over all traffic (except for traffic via lo0 as controlled by the rules explained earlier). It will essentially disable all IP services, unless, one enabled the default open policy in the kernel. Don't do this. 2.2.2. Custom Firewall Types If one has decided to instead load one's oen ruleset(s), then one should specify a file instead of one of the above types in the firewall_type option in rc.conf. For instance, one might have the following in their rc.conf: firewall_enable="YES" firewall_type="/etc/rc.firewall.rules" This will allow one to define one's custom ipfirewall(4) ruleset in /etc/rc.firewall.rules and have it run everytime during bootup. Furthermore, if one wanted to have the rules start quietly, one could also include the following in rc.conf: firewall_quiet="YES" The format of the ruleset in this file will be slightly different from that which is encountered in rc.firewall. This is because rc.firewall is an sh(1) script designed to run on its own. The ipfirewall(4) rule file is there solely to be processed by ipfw(8). The primary difference will be that where the shell variable {fwcmd} is invoked in rc.firewall, you will see nothing corresponding to it invoked in the ipfirewall(4) rule file simply the rules on their own. Later on, when we construct a sample rule file, we will go through this step by step. 3. Basic Ipfw(8) Rule Syntax

The rule syntax for ipfw(8) is pretty simple. Any rule can be enabled from the console with the ipfw(8) command. Before we delve into the rule syntax, however, we will quickly overview how to list the ipfirewall(4) rules that have been activated. 3.1. Listing Rules

In its simplest form, we can list the rules with: ipfw list

This will list all of the rules ordered by their rule number. To also list the timestamp of the last moment a packet was matched on a specific rule, the following command will accomplish this: ipfw -t list Finally, if we wish to list the packet count for matched rules along with the rules themselves, we can issue the following: ipfw -a list OR ipfw show Both will display the same information in the same way. The first column is the rule number, followed by the number of outgoing matched packets, followed by the number of incoming matched packets, and finally followed by the rule itself. 3.2. Basic Commands and Actions

We will now gradually go through the various options available for the construction of a stateless filtering ruleset. In our examples we will only state the rule not including the firewall control utility (/sbin/ipfw) which must precede each one if we're manually setting these rules from the command prompt; otherwise, if we're constructing a rule file to be passed to ipfw(8) we can use the sample lines as-is. add 1000 allow all from any to any This is the most benign example of a rule. We have already encountered the same rule, except for the rule #, in section 2.1 when discussing the OPEN firewall type. Note: the "pass" parameter used in that rule, as written in rc.firewall, is synonym for "allow" and "permit" they are interchangable. In this rule, "all" packets from "any" source to "any" destination are allowed to pass. With ipfirewall(4), under most circumstances, the moment a rule matches a particular packet, then ruleset examination halts there. As we see, the simplest syntax for ipfw(4) is: <command> [<rule #>] <action> <proto> from <source> to <destination> The important commands are "add" and "delete." They are self-explanatory. Rule numbers start count at 0 and end at 65535. The last rule number is always defined by the default firewall policy in the kernel. Even if you have an open policy defined in rc.conf, the last rule will always reflect the kernel policy. This is fine because ruleset search halts at the first matching rule (usually), so if the penultimate (second to last) rule is number 65000 and defined by rc.firewall to allow all packets, all packets will be allowed by default even if the last rule (65535) defines a closed kernel firewall policy, because the last rule will never be reached.

"action" can be one of a number of things: "allow" "pass" "permit" - Any packets matching a rule with this action are allowed to pass through the firewall, and search of ruleset terminates. "deny" "drop" - Any packets matching a rule with this action are silently blocked by the firewall and search of ruleset terminates. add 1100 deny all from any to any This would deny all packets from anywhere to anywhere. "reset" - Any packets matching a rule with this action are blocked and the ipfirewall(4) attempts to send a TCP reset (RST) notice to the source. The ruleset search is terminated. Naturally, because this only applies for TCP packets, the protocol must be "tcp," which matches only TCP packets, and not "all," which matches all IP packets. This action is sometimes useful for fooling network scanners that would otherwise be able to detect a service behind a filtered port. On the other hand, it can become a liability if one is flooded at a particular IP and port for which ipfirewall(4) is set to reply with a RST packet, thus doubling the usage of your bandwidth. add 1200 reset tcp from any to any This would deny all TCP packets from any to anywhere, and sent a TCP RST responce packet to the source for each. "count" - Any packets matching a rule with this action will prompt ipfirewall(4) to increment its packet counter. Search through the ruleset continues. add 1300 count all from any to any This would increment the packet counter for this rule, which matches all packets coming from anywhere and going anywhere. "skipto <number>" - Any packets matching a rule with this action will prompt ipfirewall(4) to continue its search through the ruleset starting with the rule number equal to or greater than that which is indicated by <number>. add 1400 skipto 1800 all from any to any This would skip ruleset search to rule 1800 for any packets that matched this rule in the first place. 3.3. Specifying Protocols

The "proto" is the protocol that is desired to be matched. The keywords "ip" or "all" are catch-alls that match all protocols. The commonly matched packet procotols are icmp, udp, and tcp, although, that is by no means an exhaustive list. For the complete list of possible protocols one can match, 'more /etc/protocols'. 3.4. Specifying the Source and Destination Addresses

The "source" and "destination" both take on the same format. They can be a name, as defined in /etc/hosts or through DNS, an IP address, a network address with bitmask (or netmask), and can be optionally followed by one or more ports numbers if the protocol is udp or tcp. Using names or IPs is straightforward, for instance: add 1000 allow all from myhost to hishost add 1100 deny all from 10.0.0.5 to any The first rule will allow all traffic from "myhost" to "hishost," and the second rule will deny all traffic from 10.0.0.5 to any host. Once a packet matches one of these, ruleset examination for that packet ceases, and it is either passed or dropped, according to the action specified in the rule it matched. This is a simple example of host-based filtering; that is, of filtering according to which hosts a packet is destined for, or arriving from. Network-based filtering works similarly, and the network notation there utilizes either bitmasks or netmasks, for instance: add 2000 allow all from 192.168.0.0/16 to any add 2100 deny all from any to 10.0.0.0:255.0.0.0 The first rule allows all traffic from the network whose IP range is 192.168.0.0-192.168.255.255. It uses a bitmask to indicate this. A bitmask specifies how many bits from the network address (192.168.0.0) should remain the same for matching packets. In this instance, the first 16 bits out of the 32 bit address will remain the same, and as the first 16 bits happen to be the first two octets, 192.168, all addresses whose source addresses have the first two octets as 192.168 will be matched by this rule. The second rule accomplishes a similar thing using netmasks. The netmask indicate how many bits from the indicated network address should be used for rule matching. In the above example, for rule two, the netmask is 255.0.0.0. Its first octet is set with high bits; in other words, the first 8 bits are set high. This indicates to ipfw(8) that only packets with the first 8 bits of the network address (10.0.0.0) should be matched. As the first 8 bits of the network address equal 10, then all packets whose destination address have a 10 for the first octet (all addresses between 10.0.0.0 and 10.255.255.255) will be matched by this rule, and then dropped, as indicated by the action. Rule matches can also be inverted with the "not" keyword. For instance, in the following ipfw(8) commands, all packets not from 192.168.0.3 are dropped: add 1000 deny all from not 192.168.0.3 3.4.1. Introduction to Bitmasks and Netmasks The principle behind bitmasks and netmasks is simple, but often confusing to new users, as it requires knowledge of binary numbers. It makes far more sense if one worked with IP addresses in their binary form, however, the confounding of decimal and binary concepts easily throws newcomers off. For a quick reference, the following table illustrates what network ranges are indicated by the corresponding bitmasks/netmasks up to a default class C netmask and a couple quick examples of additional bitmask/netmask entries for larger networks: Bitmask 32 Netmask 255.255.255.255 Total IPs / Usable IPs 1 1

31 30 29 28 27 26 25 24 ... 22 20 16 12 8 0

255.255.255.254 255.255.255.252 255.255.255.248 255.255.255.240 255.255.255.224 255.255.255.192 255.255.255.128 255.255.255.0

2 4 8 16 32 64 128 256

1 2 6 14 30 62 126 254

255.255.192.0 255.255.128.0 255.255.0.0 255.128.0.0 255.0.0.0 0.0.0.0 (all IPs)

16320 32768 65536 8.388608+e6 256^3 256^4

16318 32766 65534 8.388606+e6 (256^3)-2 (256^4)-2

As you can see, there is a definite pattern. The number of total IPs always doubles, and the number of usable IPs is always total - 2. This is because for every IP network/subnet there are two IPs reserved for the network and broadcast addresses. The netmask's last octet starts at 255 and constantly decreases by multiples of 2, while the bitmask decreases by multiples of 1, because in binary, each shift over to the left halves the number, not divides by ten, like in the decimal number system. This same pattern goes for all possible netmasks and bitmasks. For a quick example in using the above table/pattern, let us figure out the IP range for the subnet indicated by: 172.16.100.32/28 First we notice that the network address is 172.16.100.32, so we know that the subnet begins with this address. Second, we notice that the bitmask of 28 indicates that the last 4 bits (32-28) are set low and 28 bits set high. Because there are far less bits set low, it'll be easier to compute this using them. Because each bit has two possible values, 2^4 indicates how many hosts are referenced by this bitmask. In this case, 16. 172.16.100.32 + 16 = 172.16.100.48, so the IP range is 172.16.100.32 172.16.100.48. Looking at the table, we see that 16 IPs correspond to a bitmask of 28, so we could've used that to add to our network address and avoided the other math, but it's so much better to know how to do it all on your own - learn once and use always. 3.4.2. Specifying Ports and Port Ranges One can also do port-based filtering along with host and network-based filtering. Ports can be simply specified following the address of either a source of destination. Port ranges can be specified with a dash, be comma-separated, or use a bitmask to specify a range. Most importantly, one can not use the "all" protocol when specifying ports because not all protocols are port-sensitive. add add add add 1000 1100 1200 1300 allow tcp from any to 172.16.0.5 allow tcp from any to 172.16.0.5 allow tcp from any to 172.16.0.5 deny udp from any to 192.168.0.5 25 1021-1023 21,22,23 1024:8

In the first rule all TCP packets which are destined for port 25

on 172.16.0.5 are matched. In the second rule, all TCP packets which are destined for ports 1021 through 1023, inclusive, on host 172.16.0.5 are matched. In the third rule, all TCP packets which are destined for ports 21, 22 or 23 on host 172.15.0.5 are matched; and finally, in the fourth rule, all UDP packets which are destined for ports 1024 through 1028 on host 172.16.0.5 are matched. The last rule can be tricky as it uses a bitmask on the port to make matches. The port 1024 contains 10 bits. The bitmask indicates that all hosts matching the last 8 bits on that port, destined for host 192.168.0.5, are matched. 10 - 8 gives one 2 bits which can be anything. 2^2 = 4, so we have 4 port numbers, starting with 1024, that can be the destination ports for packets aiming for that host, and will result in a match. Bitmasks for ports are rarely used and are even trickier than bitmasks or netmasks for IP addresses, because the number of bits in a port varies depending on the port specified before the mask. As such, it is recommended that one stick to specifying port ranges with a dash ( ) or separate the list of ports with commas. 4. Advanced ipfw(8) Rule Syntax

Although the above overview of ipfw(8) rule creation will cover many of the simple scenarios, it sorely falls short for many more complex situations, such as when a system has more than one network interface, one wishes to make special responces to certain matches, or one wants more control over the direction of traffic flow. We will first expand the template for the ipfw(8) syntax to the follow: <command> [<rule #>] <action> [log [logamount <number>]] <proto> from <source> to <destination> [<interface-spec>] [<options>] Everything in brackets comprises new functionality we will discuss in this section. We will also cover an additional "action" that was not covered earlier. The syntax may suddenly seem daunting, but we will take it slowly, and add each part as we go along, so as not to overwhelm you. 4.1. "unreach" Action

Firstly, we will introduce a new "action:" "unreach <code>" - Any packet which matches a rule with this action will reply with an ICMP unreach code, after which time the ruleset search will terminate. The possible unreach codes can be indicated by number or name. The following is a quick list of ICMP unreach codes and corresponding names. If you don't know what these are used for, you won't have a reason to use them: net host protocol port needfrag srcfail net-unknown host-unknown isolated 0 1 2 3 4 5 6 7 8 net-prohib host-prohib tosnet toshost filter-prohib host-precedence precedence-cutoff 9 10 11 12 13 14 15

4.2.

Interface and Flow Control

One important functionality missing from the basic description of ipfw(8) syntax in part 3 was interface and flow control; that is, the ability to match packets according to which interface (if you have a multihomed system) packets are moving through, and in which direction they're moving. Up until now, direction was only loosely gauged by using the source and destination addresses, but using just them to guesstimate whether a packet is really coming or going when it moves through the firewall is unreliable. If you wish to match packets only coming in or going out, the keywords "in" and "out" can be used. Both correspond to the "interface-spec" area of the syntax template given earlier, and therefore, are placed near the end of every rule, prior to any posible options. For instance, if we wish to match all packets coming in from anywhere and going anywhere, we could have: add 1000 allow all from any to any in To match packets going through a particular interface, use the "via" option followed by the interface name. For instance, if you are using a PCI 3Com 3c59x, then your interface device will be xl0. To match all packets coming in through that interface specifically, sourced from anywhere and destined anywhere, the following would suffice: add 1100 allow all from any to any in via xl0 Or, perhaps, if one has a multihomed system and wishes to match any packets coming from anywhere and going anywhere at least moving outside through *some* interface, he can do the following: add 1200 allow all from any to any out via any One will notice, when listing firewall rules, that when using either "in" or "out" in combination with "via" the rule as it actually looks does not contain a "via" but either "recv" or "xmit," depending on whether an "in" or "out" was specified, respectively. For instance: (root@nu)~># ipfw add 7000 allow all from any to any out via xl0 (root@nu)~># ipfw list grep 7000 07000 allow ip from any to any out xmit xl0 (root@nu)~># Indeed, one can use either "recv" or "xmit" in place of "via" when using "in" or "out," however, doing so is not required, and can add to some confusion for the newcomer. In all, these options allow a lot more control over network traffic on a multihomed system and any system in general, by allowing one to filter packets specifically coming into the firewall, exiting it, and moving through a specified interface. 4.3. Matching specific ICMP and TCP Packet Types

ICMP, TCP, and IP packets come in various types. These types are defined by the various flags that each of those packets sets. We can match each of those types by using one of the following ipfw(8) options at the end of our rules.

4.3.1. icmptypes "icmptypes <type>" - This will match the specified ICMP packet <type>, and conversely, if a '!' is put before the <type> then all ICMP packets that are not of this type are match. There are currently 15 different ICMP packet types that can be matched; each is specified by the correct number. Ranges can be specified with dashes or be comma-separated. The 15 possible ICMP types are: 0 3 4 5 8 9 10 11 12 13 14 15 16 17 18 Echo Reply Destination Unreachable Source Quench Redirect Echo Request Router Advertisement Router Silicitation Time-to-Live Exceeded IP header bad Timestamp Request Timestamp Reply Information Request Information Reply Address Mask Request Address Mask Reply

If one is curious how these ICMP type, specifically type 3, correspond with the Unreach codes that can be generated with the "unreach" action, then, simply type 3 matches any of those Unreach codes. Filtering ICMP packet types can be very useful for controlling ping; specifically, for allowing internal hosts to ping out while blocking outside hosts from pinging the gateway or any other host. The following three rules can accomplish this easily: 1000 allow icmp from any to any out icmptypes 8 1100 allow icmp from any to any in icmptypes 0 1200 deny icmp from any to any in icmptypes 8 The first rule allows all icmp packets of type 8 (echo request) to go out. The second rule allows all icmp packets of type 0 (echo reply) in, and the final rule blocks all icmp packets of type 8 from entering. In short, it allows echo requests to go out and echo replies to come in, but blocks echo requests from coming in. As such, hosts behind the firewall can ping anyone on the outside, while hosts on the outside can't ping anyone behind the firewall. Naturally, this option can only be specified when the indicated procotol is "icmp." 4.3.2. tcpflags, setup and established "tcpflags <flag>" - This will match any TCP packet whose header contains one of the following flags, or conversely, if '!' is presented before the <flag>, match all TCP packets that do not have the <flag> set: fin syn rst psh ack urg Request for connexion termination Request for connexion initiation Reset Connexion Push Flag Acknowledgement Indicate Urgent OOB data

The SYN flag is of most interest as it is sent for initiation of TCP connecions. Because it is so important, there is a separate ipfw(8) option dedicated specifically for matching TCP packets with the SYN flag set. This is called "setup." Naturally, this option can only be specified when the indicated protocol is "tcp." "setup" - Any rule containing this option will match any TCP packet with the SYN flag set. For instance, if we wished to deny all incoming TCP SYN packets, we could issue the following: add deny tcp from any to any in tcpflags syn OR add deny tcp from any to any in setup On either count, the same action is performed: all TCP SYN packets from "any" destined to "any" will be matched, and denied. As stated above for "tcpflags", this option can only be used for rules when the indicated protocol is "tcp." "established" - Just as there is a special option for indicating the request for TCP connexion initiation ("setup") there is a special option for matching an already established TCP connexion. Because it is of paramount importance to easily control TCP connexions, "established" and "setup" are available for quick rule formation. Given these options (or their corresponding "tcpflags" incarnations) we can have some simplistic control of TCP connexion activity. This is the very base of stateful firewall functionality, which shall be dealt with in more detail later. 4.3.3. ipoptions "ipoptions <flag>" - Finally, we can match for some specific IP packet flags, namely, for SSRR (Strict Source Route), LSRR Loose Source Route, RR Record Packet Route, and TS (Timestamp) flags. If you do not know what any of these IP options do then you will not need to match for them specifically. 4.4. Catching Fragmented Packets

Fragmented packets are matched with the "frag" ipfw(8) option. Under most circumstances fragmented packets should be blocked. Receiving many fragmented packets may indicate a DoS (Denial of Service) attack, although FreeBSD and most other UNIX and UNIX-like systems will not be phased by such attacks, Windows systems are often quite vulnerable. As such, if one has one or more Windows systems on their network behind the firewall, it is advisable to block fragmented packets. When using the "frag" option to match [and block] fragmented packets, there are a couple guidelines that must be followed. Firstly, one can not use the "frag" option when also specifying "tcpflags." Secondly, one can not use "frag" if also specifying any TCP or UDP ports. Given these guidelines, we can easily issue a rule to block all incoming fragmented packets: add deny all from any to any in frag

4.5.

UID and GID Based Filtering

One powerful function that IPFilter does not have is UID/GID-based filtering. Ipfirewall(4) is able to filter packets according to the UID and/or the GID of the process from which they are arriving. Naturally, this can only be used to match packets which originate from processes on the local host, however, it still offers a powerful functionality. The options to be used are "uid" and "gid" followed by the UID/GID or name of the user or group by which we will be filtering. One potential use is to restrict the use of IP vhosts on a shell server. If one wants to ensure that one or more vhosts can not be used by anyone else, one can easily use UID-based filtering to block everyone's but one's own traffic from that vhost: add allow tcp from any to 172.16.0.10 in add allow tcp from 172.16.0.10 to any out uid george add deny tcp from 172.168.0.10 to any With the above rules, only user george would be able to use the aliased IP (IP vhost) 172.168.0.10 to establish TCP connexions to the outside. No one else would be able to bind bots, IRC chat clients, or what have you, to that IP and establish connexions with anything that required TCP (most things). Likewise, the UDP protocol can be used with UID/GID-based filtering, however, no other protocol can be. Another possible use of UID/GID-based filtering would be to enable bandwidth limiting on a per-user basis, as opposed to per-host or per-network basis. As such, one could, for example, have a group of users that all have fast FTP accounts, and only moderately limit their GID, while on the other hand, have another group of shell users, who don't require much bandwidth, and therefore significantly cap the bandwidth on the GID they all belong to. Such GID-based bandwidth capping will be illustrated later, once we cover the traffic shaping facilities of ipfirewall(8). For security purposes, one may wish to log the traffic of a particular user, and here too UID-based filtering would come in handy. In short, whenever one would wish to conduct firewall behaviour differently for one or more users, UID/GID-based filtering would come in handy. Because, in general, once a rule is matched, search through the ruleset stops, UID/GID matching rules must be invoked before other sweeping rules can match the traffic. So, when creating one's ruleset, one must take this into careful account if one wishes to enable UID/GID-based filtering. 5. 5.1. Logging Logging Issues

The virtues of logging are obvious. The ability to go back and see what what connexions had been dropped, what addresses they came from, where they were going, whether they were composed of many fragmented packets (indicative of many DoS attacks), and so on gives you a significant edge in both knowing where connexions are being made, by whom, and when. Especially in the case of tracking down crackers and the like, firewall logs may give one the all-important edge.

Logging also has a down-side. If you're not careful, you can both lose yourself in the abundant data (for lack of proper log analyzing strategies) and lose your HD space to growing log files. DoS attacks that fill up HDs are one of the oldest around, and still just as dangerous to the imprudent administrator. Although, they more often strike the poorly configured email server, they are just as much a threat to any system keeping extensive log data. It is important that you have sufficient HD space and rotate logs prudently, so they do not grow indefinitely. On the other hand, what many newcomers experience that once they enable ipfirewall(4) logging their terminal is overwhelmed with messages concerning packet activity. This is the result of any combination of: - logging too many often-matched rules - not disabling logging to console & root terminals (bad idea!) - not controlling logging with the IPFIREWALL_VERBOSE_LIMIT 5.2. System Logging Configuration

5.2.1. Kernel Options To Enable ipfirewall(4) logging in FreeBSD, one has to include at least the following option in the kernel (don't forget to reboot after you build the new kernel): options IPFIREWALL_VERBOSE

In addition, one may wish to enable the following option: options IPFIREWALL_VERBOSE_LIMIT=#

We have already mentioned this option early on when reviewing firewall activation. This kernel option limits the number of consecutive messages sent to the system logger, syslogd(8), concerning the activation of a given rule. When this option is enabled in the kernel, the number of consecutive messages concerning a particular connexion are capped to the number specified. For instance, consider the following: options IPFIREWALL_VERBOSE_LIMIT=10

With this, only 10 consecutive messages concerning a particular connexion would be logged to syslogd(8), with the remainder being subsumed under a general statement like: Jan 29 03:26:55 myserver last message repeated 45 times These messages are generally logged to /var/log/messages in addition to the console. If one wishes to modify this behaviour, one will have to edit the /etc/syslog.conf. syslog.conf(5) offers considerable flexibility in how syslogd(8) will deal with system messages. The limit which one defines for IPFIREWALL_VERBOSE_LIMIT is up to the administrator, but values above 10 or so on a busy server may still sorely populate the console. On the other hand, if one wishes to disable kernel messages to the console altogether and log everything to a separate file (not the default /var/log/messages) this can be done also. Indeed, in this configuration, one need not specify IPFIREWALL_VERBOSE_LIMIT at all if one is certain that HD space is sufficient and log rotation will keep things tidy.

5.2.2. Configuring syslog(8) for Logging One can setup syslogd(8) to log ipfirewall(4) messages to a separate file in three relatively simple steps: 1) Create your log file, and alternative, log directory. For instance, if you wish to have all of your ipfirewall(4) logs in /var/log/ipfw/ , create the directory, and then inside, create the log file. You may call it ipfw.log, for instance. You can easily do this with the touch(1) command: (root@nu)~># mkdir /var/log/ipfw && touch /var/log/ipfw/ipfw.log (root@nu)~># Make sure the directory and file are not world-readable so other users can not poke around. 2) Configure syslog.conf(5) to send ipfirewall(4) messages to /var/log/ipfw/ipfw.log. In its most basic configuration, this can be done easily by added the following two lines to the bottom of syslog.conf(5). !ipfw *.* /var/log/ipfw/ipfw.log

Make sure that you use tabs and not spaces in this file. Although tabs are not required in FreeBSD for syslog.conf, it is good practice in case you work with other UNIX systems, which may not accept spaces in their syslog.conf(5). So, even though you can ignore the scary message at the top of /etc/syslog.conf, it is wiser to abide by it for the sake of maintaining a safe habit. One should note that the "last messages repeated #" messages will be logged to this file and also any other file that is specified in syslog.conf(5) to log *.err, kern.debug, *.notice messages, and so on. In addition, the console will always receive these messages as defined by: *.err;kern.debug;auth.notice;mail.crit /dev/console

Remember, the the console is ttyv0 (ALT + F1). Messaging to virtual and pseudo terminals will behave differently. Virtual and psudeo terminals are controlled by the following lines in the default syslog.conf(5): *.err *.notice;news.err *.alert *.emerg root root root *

Both lines that contain *.err and *.notice will log kernel messages concerning rule matches to the terminal on which the user specified to the right is logged in. Notice that this user is root; the best way to avoid annoying messages is to not log in as root constantly. Logging in as root and su'ing will not save you from the messages, either. It is strongly recommended that these lines not be commented out - having such messages logged to any terminal in which root is logged in is very useful as a quick notification of something possibly wrong happening. Indeed, you may wish to log these or other messages to other UIDs that you use often. Instead setup your rules appropriately so only important rule

matches are logged and log in with your personal account, not root. Keep a spare virtual terminal logged in as root and check it occasionally. To sum up, when dealing with syslog.conf(5) configuration and terminal notification: - Add the two aforementioned lines in syslog.conf(5) to log rule matching messages to a separate file for easy examination at a later time. - Do not worry about root terminal messages, instead do not do your normal activity as root. This is a good all-around piece of security advice. Instead, keep a spare terminal logged in as root and check it occasionally. 3) Send a Hangup signal to the syslogd(8) process. The easiest way is with the following command: (root@nu)~># killall -HUP syslogd (root@nu)~># 5.2.3. Configuring newsyslog(8) for Log Rotation Now that ipfirewall(4) logging is enabled, one should consider configuring newsyslog.conf(5) so that newsyslog(8) rotates your ipfirewall(4) logs; or, alternatively, use some other log rotation mechanism. For a full explanation of all possibler configuration options one should consult the newsyslog.conf(5) man page, however, the following entry should suffice for most occasions (in this example, we are using the example log file name as was presented earlier): /var/log/ipfw/ipfw.log 600 10 * $W0D2 Z

This entry can be added to the bottom of newsyslog.conf(5). The first part is self-explanatory. The second comprises the permission bits for the rotated file The third the number of log files to keep rotating back until the oldest is deleted. The fourth is a wildcard instead of a specific size which to use as a signal to rotate the files. The fifth part is the time at which to rotate the logs, and the sixth is a special option. As one may have already surmised, log rotation can be done by one of two criteria: size, and time/date. We can specify to rotate a certain log based on when it reaches a particular size, or instead, based on what time/date it is. In the above entry, we used the time/date criterion. We specified to rotate the log at 2 AM every sunday morning, and then to gzip(1) the archive (the Z option), only allowing for a 10 archive history (10 weeks). If one wishes to hold their logs longer than this, one need only change the third parameter from 10 to whatever they like. Once newsyslog.conf(5) has been configured, log rotation is all done. Because newsyslog(8) is a program run from cron(8) there is no daemon that needs to be sent the Hangup signal as was necessary for syslogd(8). There are other ways of enabling log rotation. One can write and run one's own log-rotation script, or use a friend's that you've been impressed with. Either way, it is important that on such potentially large log files, there be some method of archiving and controlling their growth. 5.3. Rule Logging Configuration

Once the system is fully configured to handle the ipfirewall(4) logging, we can begin specifying which rules, when matched, should be

logged. There are two simple parameters to be used in conjunction with rule formation to enable logging for the given rule; they are: "log" - Logs everytime the rule containing this keyword is matched by a packet. The keyword, if present, must follow the "action". Make sure you do NOT put this in wide sweeping rules such as: add 0500 allow log all from any to any Unless there is some extensive filtering occuring prior to this rule, most traffic will be matched to it and one's log files will grow very large very fast. On the other hand, it could well be safe to enable logging on a sweeping deny rule such as: add 65000 deny log all from any to any This rule is much safer because it is both near the end (rule # 65000 versus 500) and in rulesets that fulfill a closed firewall policy, there are generally many rules prior to the last deny rule that allow through important traffic, which should comprise much of the total traffic experienced, except in unusual circumstances - although it can still be risky. Take careful consideration into which rules should be logged and which shouldn't. "logamount <number>" - This parameter, following the "log" parameter, specifies the maximum number of matches that a rule can experience before logging halts. This gives the administrator some extra control over logging. If, for instance, he enables 10000 matches for a given rule, and then reset the counter once a day, he can alleviate potentially large logs if someone tries to flood the server and is blocked by the given rule. After 10000 matches, the logging for that rule will stop, and the flooder's attack will not swell the log file. Alternatively, one may wish to log *everything* and not use this parameter. In FreeBSD 4.x, 3.4+, and 2.2.x this is allowed, however, in earlier versions of FreeBSD 3.x, if this keyword is not used, a default "logamount" is set to 10. When rules are logged, the following information will be saved: Date & Time Rule number Action Source & Destination IP addresses Source & Destination Port numbers Direction flow Device over which this occured

For instance, a firewall log line may look like this: Jun 12 13:55:59 mybox1 /kernel: ipfw: 65000 Deny TCP 172.16.0.1:62307 192.168.0.1:23 in via xl0 6. Introduction to Stateless and Stateful Filtering

Stateful and stateless filtering are two terms often encountered in debates between proponents of ipfilter and ipfirewall(4). Stateless filtering treats each packet going through it as an individual that has no association with the other traffic going through the given interface. This type of filtering is easier to implement and can be used effectively to:

- filter corrupt (fragmented) packets - filter particular protocol packets (icmp, udp, igmp, etc) - do host-based filtering (filtering according to where a packet is destined, or where it came from) Stateful filtering is more complex to implement. It treats traffic not as an aggregate of individual, independent packets, but as composed of connexions. All communication via any of the protocols uses sequence numbers to indicate in which order packets should be read on a socket(2). A stateful firewall would be aware of these sequence numbers. Connexion oriented protocols such as TCP also have special packets which indicate connexion initation (SYN) and termination (FIN), and a stateful firewall would also be aware of these. In short, it would: - know what state a connexion is in - be able to determine if a connexion is following valid proceedure, or is breaking the rules, and would be able to filter the packets in these connections accordingly. Stateful firewalls create dynamic rules for live connexions, and clear out these rules when the connexions time out. All of this allows for a more intelligent awareness of the higher level activity of network traffic by the firewall. On the downside, statefull firewalls are unable to treat each packet individually, because special dynamic rules are built to allow entire connexions through, precluding the examination of packets in those rules except in the context of whether they are behaving properly in the overall connexion. As such, it is wise to mix and match stateful and stateless rules in any firewall configuration, so one is able to benefit from both. Almost all of the example rules that have been given so far have been stateless. The only exceptions are the rules concerning the option "tcpflags," "setup," and "established" which allow one to check the state of a TCP connexion. Indeed a combination of these will be used in the first stateful ruleset examples shortly. Using these options to make primitive stateful rulesets has been functionality that has been available in ipfirewall(4) for a long time, however, because of its very limited stateful capabilities, ipfirewall(4) has long been regarded as a stateless firewall, with IPFilter the stateful alternative. Starting with FreeBSD 4.0, ipfirewall(4) has been enabled with more extensive stateful functionality, with more promised to come. 6.1. Basic Stateful Configuration

For our first example, we will use the older, more basic, stateful capabilities of ipfirewall(4). Many people, following after the example in ~rc.firewall, where all of the pre-defined rulesets use this most basic of stateful functionality, make heavy use of the "setup" and "established" keywords for controlling TCP connexions. Indeed, this can only be used to control TCP connexions in a stateful manner, showing its limiting nature on at least one count. In our example, we will create a simple stateful firewall rulest that only allows ssh connexions through: add 1000 allow tcp from any to any established add 2000 allow tcp from any to any 22 in setup Presuming a closed firewall policy (firewall_type is NOT "OPEN" and kernel does NOT have IPFIREWALL_DEFAULT_TO_ACCEPT set) the above two

lines will first allow all established TCP connexions to pass through. Specifically, any packets part of an established TCP connexion will match rule 1000 and then ruleset searching will cease for those packets. If, on the other hand, any packet is not part of an established TCP connexion, rule 1000 will not match it, and ruleset analysis will move to rule 2000, where, if the packet is a SYN TCP packet destined for port 22 (port for ssh), the rule will match it and and let pass. Subsequent packets for that connexion will be passed with rule 1000. In this manner, the above rules are stateful as they are aware of the existence of a TCP connexion at large and not just individual packets. One could have easily accomplished the same with stateless rules; for instance: add 1000 allow tcp from any to any out add 2000 allow tcp from any to any 22 in In this example, all packets moving out from any to anywhere are allowed to pass through the firewall, and all packets moving to port 22 of anywhere from anywhere in, are passed through. In this case, the rules are not aware of the TCP connexions - they do not test for initiation and established TCP connexions, but instead, let all TCP packets move out, no matter what they are, and let all TCP packets in to port 22, no matter what they are. This is the essence of stateful behaviour in ipfirewall(4) using the "setup" and "established" flags: pass through TCP setup requests to specified addresses:ports and then let these established connexions through. Let us look at a more involved example that handles ssh, email, FTP, and DNS queries for network 172.16.0.0/27: add add add add 1000 2000 3000 3100 allow allow allow allow tcp tcp udp udp from from from from any to any established any to 172.16.0.0/27 21,22,25 setup 172.16.0.0/27 to any 53 any 53 to 172.16.0.0/27

In this example, setup of TCP connexions is allowed for ports 21, 22, 25 - FTP, ssh, and email respectively, when packets are destined for the 172.16.0.0 network. Afterwards, all established TCP connexions are allowed to pass. Rules 3000 and 3100 pass UDP packets to port 53 on other hosts and allow UDP packets from port 53 on other hosts to pass in through the firewall. Both of these rules are stateless. Port 53 is the port on which DNS servers run. Rule 1000 must have "from any to any" because TCP packets for established connexions must be allowed to both originate from anywhere and to arrive at anywhere. If one was to write a similar ruleset which was completely stateless, one would have to keep most TCP ports between 1024 and 65000 open for FTP connexions. FTP is definitely a wild protocol as it randomly binds to non-reserved ports across a wide range. Allowing active FTP through a firewall is always difficult; only stateful firewalls offer a really clean approach to it, without opening massive ranges of ports. Most notably the shortcoming of opening the ports with a stateless firewall is that anyone can attempt to connect to any port within that range. With our setup, only TCP connexions that had been initiated through rule 2000 can be let through with the "established" option in rule 1000, effectively limiting random and uncontrolled connexions to the wide range of ports needed for clean active FTP operation.

6.2.

Advanced Stateful Configuration

As has been stated before, stateful firewall configuration with just the "setup" and "established" options is very limiting. Aside from only allowing stateful control over TCP connexions, this stateful control is simplistic at best. Starting with FreeBSD 4.0 the stateful capabilities of ipfirewall(4) have been greatly augmented. Now, ipfirewall(4) can be configured for stateful handling of TCP, UDP, ICMP and other packets types with use of dynamic rules. Dynamic rules are another new addition to ipfirewall(4) in FreeBSD 4.0. Dynamic rules, as the name suggests, are dynamically generated for individual connexions. Each dynamic rule, after lack of use past a period of time, times out. The specific timeout period for TCP connexions can be controlled by several(8) sysctl variables. This allows control of not only connexion initiation, but connexion termination; that is, in a manner of speaking, a ruleset can be designed which is aware of the beginning end ending of a particular connexion, and that adjusts itself accordingly. One option and one command are used to control this advanced stateful behaviour: "set-state" - Any rule with this option initiates a dynamic rule whenever it is matched by a packet. "check-state" - This command allows the firewall search to first check the dynamic rules. If a rule with this command is missing from all of the rules in a ruleset, then dynamic rules are checked at the first instance of the "set-state" option. If a rule with this command is a match, then the search stops, otherwise the search continues. Let us return to our earlier example of a ruleset designed to only allow ssh connexions through, only this time using the advanced stateful ipfirewall(4) facilities: add 1000 check-state add 2000 allow tcp from any to any 22 in setup keep-state Remember, these rules presume that your firewall policy is a closed one (the default rule is to deny everything). In the above rules, the first rule prompts ipfirewall(4) to check the dynamic rules. If the immediate packet passing through the firewall do not belong to any already established connexions, that is, they do not match any of the dynamic rules, then the search continues to rule 2000, where if the packet is a TCP SYN, then "keep-state" prompts the creation of a dynamic rule. Subsequent packets through that connexion will be passed through the dynamic rule, which is checked for with rule 1000. All other packets would be rejected by the default deny rule. Even though, we have already demonstrated to do this with the older stateful approach and also a stateless approach, this new approach using the "set-state" option and "check-state" command has a distinct advantage above the other approaches. In the older stateful approach, the option "established" matched any TCP packets from an established TCP connexion, even if they were spoofed and not from any current legitimate TCP connexion. This new approach precludes such an occurance. Each dynamic rule is for a specific connexion between two hosts and their respective ports. Spoofed TCP packets will be able to spoof the source and destination IP addresses, but not the correct ports (unless they're very

lucky) on which a legitimate TCP connexion is being maintained, and thus, rule 1000 with the "check-state" command would fail, after which rule 2000 would fail unless the spoofed packet was a TCP SYN packet, and the packet would fall to the final deny rule. In summary, the criteria with which packets are tested against when trying to match a dynamic rule for them are: Protocol Source IP & Port Destination IP & Port Whether the rule has timed out

As noted before, the dynamic rules time out after lack of use over a particular period of time. Depending on how a dynamic rule is used, the rule's lifetime is given a particular period. The timeout values for the different types of rule uses can be viewed by printing the correct sysctl variables; moreover, one can modify these values. Here is the list of sysctl variables and their default values: (root@nu)~># sysctl -a grep 'dyn.*lifetime' net.inet.ip.fw.dyn_ack_lifetime: 300 net.inet.ip.fw.dyn_syn_lifetime: 20 net.inet.ip.fw.dyn_fin_lifetime: 20 net.inet.ip.fw.dyn_rst_lifetime: 5 net.inet.ip.fw.dyn_short_lifetime: 5 (root@nu)~># The first sysctl variable indicates that the default lifetime value to which a dynamic rule used by a TCP ACK packet is immediately set to upon use is 300 seconds. The second variable indicates that the lifetime value to which a dynamic rule used by a TCP SYN packet is immediately set to upon use is 20 seconds. The third variable indicates that 20 seconds is also the value to which the lifetime of a dynamic rule used by a TCP FIN packet is set to. Dynamic rules used both by TCP RST packets and any other packets (UDP, ICMP, etc) are set with a 5 second lifetime, as indicated by the last two sysctl variables. Let's use an example to clarify how all of this works in action: 1) A legitimate TCP connexion request from host 172.16.0.1 on port 1234 is sent to port 22 on a server 192.168.0.1 behind the firewall. This connexion request consists of a TCP synchronization packet (TCP SYN). 2) Rule 1000 on the firewall has the firewall check the dynamic rules and finds none that correspond to any sort of TCP packets coming from 172.15.0.1:1234 and destined for 192.168.0.1:22. 3) Rule 2000 is checked is a match. The "keep-state" has a dynamic rule created for a TCP connexion from 172.16.0.1:1234 to 192.168.0.1:22 with the lifetime of 20 seconds (default for TCP SYN packets). 4) Within a second a TCP ACK packet is sent to 192.168.0.1:22 in responce to the TCP ACK sent from the server to the client confirming the TCP connexion request. 5) As the packet encounters the firewall, rule 1000 once again has the dynamic rules checked. This time a dynamic rule still within its lifetime exists which matches the protocol source IP:port and destination IP:port, so it is let through the dynamic rule, and the packet passes

through the firewall safely. 6) a spoofed TCP ACK packet from an attacker enters the firewall which under normal circumstances could knock out the networking capability of certain unpatched Windows machines behind the firewall. 7) Rule 1000 checks the dynamic rules and finds that the spoofed packet has a destination IP:port that is an IP:port of an existing dynamic rule, its return IP:port does not match that of any rule (because it was randomly generated by the attacker). So, rule 1000 fails and checking moves to rule 2000. 8) Rule 2000 finds the packet not to be a TCP SYN, so the check-state option is not run as the rule is not matched. 9) Consequently, the packet falls to the default deny rule and is lost. Were this had been a stateful ruleset by the older method, the spoofed TCP packet would've been accepted with the first rule containing the "established" option because in our original example the rule accepted all TCP packets destined for a particular network behind the firewall that had at least the ACK flag set, and the spoofed packet indeed fulfilled these criteria. This is a first, but powerful reason for why the advanced stateful operations of ipfirewall(4) are extremely useful, and what advantages IPFilter has in this regard also. In our above illustration, it would have gone differently had the spoofed packet been a TCP SYN packet, but before we describe why, let's examine a popular use of spoofed TCP SYN packets in computer attacks, also known as SYN floods. Spoofed TCP SYN packets are often used in network attacks. The most common of such attack consists of sending a flood of TCP SYN packets (SYN floods) to a host so that the entire connexion queue in the prey's kernel is saturated with open connexion attempts, thus denying the activation of new legitimate connexions. Even though, the FreeBSD TCP/IP stack is designed to randomly drop TCP connexion attempts from its queue past a particular threshold of maximum tolerated TCP connexion attempts, this type of attack can be devastating. If the TCP SYN packets come fast enough they will initiate fake connexions faster than they can be dropped low enough to free sufficient room for all legitimate connexions. The primary variables are speed of the attack and speed of the box in its ability to process packets moving through its TCP/IP stack. Fortunately, FreeBSD's TCP/IP stack is faster than that of Linux and many other systesm, however, it's not always fast enough. Just as in our previous illustration of spoofed TCP ACK packet handling with the advanced stateful functionality, randomly spoofed TCP packets of any sort, SYN or ACK, would be unsuccessful because each new packet with a random source IP:port would be unable to find a dynamic rule to pass through. However, if the original TCP SYN and all subsequently spoofed TCP packets maintain the same source IP:port, then the stream could open and maintain a TCP connexion based on invalid source:port information. Also, one must be very careful about too many dynamic rules being opened by spoofed TCP SYN packets. Although, the TCP/IP stack wouldn't be overwhelmed by too many connexion attempts, the firewall dynamic rule queue could be saturated. One way to try to avoid this is to shorten the lifetime of dynamic rules started by TCP SYN packets with the appropriate sysctl mentioned earlier and extending the maximum number of

dynamic rules, which is done via another sysctl: net.inet.ip.fw.dyn_max: 1000 (default) To get a quick count of how many dynamic rules exist, one should print the value of the sysctl variable: net.inet.ip.fw.dyn_count One sure way to avoid the possibility of spoofed packets from using up all of your dynamic rules is to disallow external hosts from initiating TCP connexions. This can be done easily with the following rules, presuming that 192.168.0.0/27 is the net behind the firewall: add 1000 check-state add 2000 allow tcp from 192.168.0.0/27 to any out setup \ keep-state When TCP SYN packets reach rule 2000 they fail to match and are dropped by the default deny because only TCP SYN packets moving out and with the source address from the protected net, 192.168.0.0/27 will match the rule. Incidentally, this is the same type of protection that NAT, and transparent proxies in general, affords for internal hosts. Until now most of the discussion has revolved around stateful handling of TCP connexions. However, using the advanced stateful functionality one can, as has been stated, control other packet types in a stateful manner. For instance, during our discussion of the option "icmptypes" we presented an example of how one could use them to allow internal hosts to ping external hosts, and deny it the other way around. This can be easily done with the "check-state" command and "set-state" option as well: add 1000 check-state add 2000 allow icmp from any to any out icmptypes 8 keep-state This works simply by creating a dynamic rule for every ICMP echo request sent out. When the reply comes, it uses the dynamic rule and is passed through. However, if someone tries to send in an ICMP echo, it is denied, unless they happen to send it during the moment our dynamic rule is alive. ICMPs use the net.inet.ip.fw.dyn_short_lifetime sysctl variable which is by default set to 5 seconds. If the ICMP echo reply takes longer than 5 seconds to reply, the dynamic rule's life will timeout and it won't be able to pass through. If long ping replies are suspecting for being a possibility, then that sysctl's value should be raised. Most network lag is under a second, however. Incidentally, rule 2000 could also be written with the source address being a network address if there were more than one network behind the firewall and one wanted to specify which network these rules should apply to. Likewise, one could limit the rule to a single host address. We used this format because it was closest to what our first ping controlling rules used. 6.3. Anatomy of a Dynamic Rule

Let us take a look at what it may exactly look like when one lists their stateful firewall rules:

00100 allow ip from any to any via lo0 00200 deny ip from any to 127.0.0.0/8 01000 check-state 02000 allow tcp from any to any keep-state 65535 deny ip from any to any ## Dynamic rules: 02000 9 1255 (T 54, # 0) ty 0 tcp, 192.168.0.1 2007 <-> 204.71.200.245 80 We are already familiar with the static rules, however, this is the first time we're showing an example of a dynamic rule. Let us examine it closely: The first part of the dynamic rule is the rule # of the static rule that started it, in this case, rule 2000, which has a "keep-state" option. The second part is the count of bytes that have been sent out through that dynamic rule, and the third part is the count of bytes that have been received through that rule. In the parentheses, the 'T' value is the timeout value - the rule lifetime - in seconds. In this case, 54 seconds are life for the rule. The hash mark indicates the rule number, in this case being rule 0. The 'ty 0' part indicates what type of dynamic rule this is. The rule type corresponds to the flow of the rule - whether it allows traffic only from source to destination, the other way around, or both (bidirectional). Currently, only one type is available, which is the default: bidirectional. This is visually indicated by the "<->" symbol between the source and destination IP:port. After the type, we see the protocol that the dynamic rule passes through, followed by the source IP:port, a bidirectional indicator "<->" as mentioned above, and finally the destination IP:port. Even after dynamic rules timeout, you will still see them listed with "ipfw list," although, with a 0 T value. Once a rule times out, it will no longer accept packets as it would have normally unless it is revived with the same static rule with a "keep-state." Also, once they timeout, they can be replaced by newly activated dynamic rules. Unless all of the dynamic rules are alive, they will be continuously replaced with new ones, especially so as the number of dynamic rules approaches the maximum. Once many dynamic rules are created it may become somewhat of a nuisance to list the rules with 'ipfw list' as all of the dynamic rules will stream off the terminal. To only list the static rules, one can do something like: ipfw list grep -v '[<->#]'

Or, if one wishes to page down all of the rules, both static and dynamic, one can: ipfw list 7. more

Traffic Shaping

Traffic shaping refers to the controlling of traffic in various ways, such as bandwidth capping, delays, flow queues, and so on. It allows one to control the general intensity, direction and breakup of the traffic. Since the introduction of dummynet(4) in FreeBSD, extensive traffic shaping capabilities have been available. Indeed, this is another region in which IPFilter is unable to offer corresponding functionality.

If one needs traffic shaping capabilities to control individual user bandwidth consumption, or enable delays in traffic for experimental and testing purposes, one must use dummynet(4) with ipfirewall(4), with only one exception: probability matching, which is supported completely by ipfirewall(4) without dummynet(4). No traffic shaping rules can use dynamic rules, because as each dynamic rule is created it will not observe the bandwidth caps, delays, separate flow queues, etc. 7.1. Probability Matching

ipfirewall(4) supports a useful tool for networking testing by allowing one to simulate random packet drops at various probability ranges. This option uses the keyword "prob" followed by a floating point number between 0 and 1 which corresponds to the probability with which packets will be passed. So, a "prob 0.9" will pass packets matched by that rule with a 90% probability, while a "prob 0.1" will do so with a 10% probability. The following is an extended syntax for ipfw(8) for use with native ipfw(8) probability matching: <command> [<rule #>] [prob <match_probability>] <action> [log [logamount <number>]] <proto> from <source> to <destination> [<interface-spec>] [<options>] For example, if we wished to drop ICMP echo requests 20% of the time, we could have the following rule: add 1000 prob 0.8 allow icmp from any to any in icmptypes 8 Or, perhaps, we may wish to deny 50% of TCP SYN packets to the web server to simulate heavy web traffic via the ep0 interface: add 1000 prob 0.5 allow tcp from any to any in setup via ep0 7.2. Dummynet

All additional traffic filtering capabilities require dummynet(4), which was introduced into FreeBSD in version 2.2.8. Before they can be used, the kernel has to be compiled with the following option: options DUMMYNET

Once compiled in, one will be able to specify pipes for traffic control. A pipe is a traffic shaping rule that controls the traffic in the specified manner, and is created with the ipfw(8) "pipe" command. Traffic is redirected to pipes with ipfw(8) and the use of the "pipe <pipe #>" action. First let us construct a simple pipe (note: each "pipe" command must be preceeded with a call to /sbin/ipfw: ipfw pipe # ...): pipe 10 config bw 100Kbit/s This simple pipe will cap traffic flowing through it to a maximum of 100 Kilobits per second. There are a several different ways to indicate bandwidth measure: bit/s, Byte/s, Kbit/s, KByte/s Mbit/s, MByte/s. Each bandwidth limiting pipe must use the "bw" keyword. Another way to control traffic is to use a delay, which could be used to simulate system lag:

pipe 10 config delay 100 The value following "delay" is in milliseconds. In this example, all traffic moving through this pipe will be delayed 100ms. We could also accomplish the same thing as the "prob" indicator built into ipfirewall(4) with the "plr" pipe keyword. For instance, to simulate 20% packet loss as we did with "prob 0.8," we could construct the following pipe: pipe 10 config plr 0.2 "plr" stands for "packet loss rate" so the value indicates at what rate packets will be lost, while the "prob" keyword for ipfw(8) indicates the probability with which packets will pass. Therefore, "plr" values are (1 - "prob") values. To simulate 20% packet loss with "prob" we indicate that 0.8 of the traffic will make it through; with "plr" we indicate that 0.2 of the traffic will not make it through. Try not to get confused by the difference. 7.2.1. Pipe Queues Next, one may need to control the queue sizes of their pipes, especially if the MTU of their network device is relatively large. The MTU of a network device defines the "maximum transmission unit" for that interface, or, in other words, the maximum size a packet can take on that interface. To learn the size of the MTU of a given network interface one need only use ifconfig(8) to view its info; for instance: (root@nu)~># ifconfig xl0 xl0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500 inet 192.168.0.1 netmask 0xffffffe0 broadcast 192.168.0.31 ether 00:70:18:d4:a4:ac media: 10baseT/UTP (10baseT/UTP <half-duplex>) supported media: 10base5/AUI 10baseT/UTP <full-duplex> 10baseT/UTP <half-duplex> 10baseT/UTP (root@nu)~># Here we see that the NIC's MTU is 1500 (bytes). Indeed, the MTU for all ethernet devices is 1500 bytes. Queues are used by pipes to enforce the configured bandwidth limitations and delays. Queues can be configured by either specifying their size in "Kbytes" or slot amounts. Slots correspond to packets; in other words, specifying that a queue have "10" slots is to specify that it can only hold 10 packets. The maximum size of the packets is defined by the MTU of the given network device. For ethernet devices, the MTU is 1500 bytes. This means that if one specifies that a pipe use a queue with 10 slots on an ethernet network, the queue size would be 10 x 1500 bytes, or 15 Kbytes. This is important to understand because the default queue for pipes is 50 slots and it may especially be too large on network devices with a large MTU and a very low bandwidth limitation. 50 was chosen as the default because it is the typical queue size for ethernet devices. Under normal circumstances it is imperceptible, however, when small bandwidth limitations are imposed it'd take a long time to fill the queue and this would create horrible network delays. For instance, if we set the following pipe on an ethernet LAN to simulate a 56K modem:

pipe 10 config bw 56Kbit/s ... and we do not set a smaller MTU for the device with ifconfig(8) or set a smaller queue (preferred approach), the queue into which packets would be pumped for the pipe to enforce its bandwidth limitation would be 1500 bytes (12000 bits) x 50 = 600Kbits. For a pipe which is capping the bandwidth to 56Kbit/s it would take roughly 10.7 seconds (600Kbits / 56Kbit/s) to fill a 600Kbit queue. Such delays would be a severe monkey wrench in the experiment. To avoid such complications, it is strongly advisable to manually set the queue sizes that each pipe uses. As we have noted earlier, the default queue size is set using slots - 50 of them. The queue can also be set by specifying a size in "Kbits". This latter approach is safer because using slots to specify the queue size leaves the open variable of MTU size, which, if one isn't paying attention, could cause additional complications. The smaller the bandwidth cap, the smaller the queue size should be. For instance, using our above example, a reasonable configuration would be: pipe 10 config bw 56Kbit/s queue 5Kbytes 7.2.2. Pipe Masks A powerful capability of pipes is to allow multiple queues per traffic flow. For instance, if one has several boxes behind a firewall and wants to cap the bandwidth of each one to 100Kbit/s and not just the aggregate bandwidth of all traffic moving through the gateway, one can either manually set up a pipe and ipfw rule for each server, or instead, use dynamic queueing with pipe masks. The masks define which hosts belong to the same pipe, just as netmasks and bitmasks define which groups of hosts belong to the same network/subnet. Masks can be specified in six different ways: "dst-ip" - mask for the destination IP of the packets being sent through the pipe. "src-ip" - mask for the source. "dst-port" - mask for the destination port. "src-port" - mask for the source ports. "proto" - mask for the protocol. "all" - mask for all hosts; specifies that all bits in all fields (dst-ip, src-ip, etc) are significant. For example, let us take the above example of a network behind a firewall for which all of the hosts are desired a 100Kbit/s bandwidth cap. If we simply send all traffic through a pipe like described earlier, the cap will be applied to the aggregate traffic from all of the hosts and not each one individually. To apply masks to the hosts such that each host's traffic is sent into a separate queue and applied the bandwidth limit separately, one could do the following: pipe 10 config mask src-ip 0x000000ff bw 100Kbit/s queue 10Kbytes pipe 20 config mask dst-ip 0x000000ff bw 100Kbit/s queue 10Kbytes add 1000 add pipe 10 all from 192.168.0.0/16 to any out via <device> add 2000 add pipe 20 all from 192.168.0.0/16 to any in via <device> At first glance this may seem confusing. We have also for the first time included the ipfw(8) rules that divert packets to the pipes. We did this because the two pipe rules on their own would not make as much

sense without seeing what we are diverting to them. Pipe 10 caps the traffic passing through it to 100Kbit/s as well as does the pipe 20. Rule 1000 diverts its traffic to pipe 10, and rule 2000 to pipe 20. Rule 1000 matches all traffic moving out and rule 2000 matches all traffic moving in. There are two reasons to have a pipe for incoming and outgoing traffic, but one will be addressed later. The primary reason that one needs to concern oneself with now is that each pipe configures a different mask. Pipe 10 configures mask 0x000000ff for source addresses; because rule 1000 diverts traffic leaving the internal network, the mask *must* be applied to source addresses if we wish to break the flows from each internal network host to a separate queues. Likewise, for traffic coming in, the queues must be broken up according to the destination addresses behind the firewall. As you noticed, we specified the masks in hexadecimal instead of decimal. Either should work. The masks work in the exact same manner in which netmasks work; this becomes clear when we realize they're done in reverse. When netmasking we are trying to break up hosts into groups so the high bits are at the beginning, here we are trying to break up a group into hosts, so the high bits in the mask will be near the end. Observing this reverse goal, it makes sense that the pipe masks look backwards from netmasks. The hex mask we specified corresponds to a decimal mask of 0.0.0.255. In simple terms, the last octet indicates that only one host should be alotted per queue (256 - 255). Thus, a separate queue for bandwidth control is set aside for each address that has a different host number (different last octet). This presumes, of course, that there are no more than 254 hosts on a network. If there are more hosts, then the mask must be adjusted. For instance, if there are 254^2 hosts in the network behind the firewall, then the mask would have to be 0.0.255.255 (0000ffff) to indicate that any address that has a different bit within the last two octets must get its own queue. 7.2.3. Pipe Packet Reinjection Under most circumstances, once a packet is diverted to a pipe, the traffic shaping configured for that pipe takes effect and rule searching ends. However, one can have the packet become reinjected into the firewall, starting at the next rule, after it passes through the pipe by disabling the following sysctl: net.inet.ip.fw.one_pass: 1 8. Traffic Flow

It must be always remembered that rules that do not specify "in" or "out" flags will be checked for traffic coming in AND out. This has a number of implications. For instance, pipes to which rules divert traffic that haven't "in" or "out" flags will be activated twice, once when packets leave and once when they enter. In addition, not specifying an interface with the "via" keyword can cause unwarranted confusion. If a multi-homed system does not have its firewall using "via" then traffic coming both ways across any interface will be treated with the "in" and "out" keywords. "in" will both match traffic coming from the outside AND local network, because both are coming "in"-to the gateway box. Another concern is with half-duplex and full-duplex connexions. If inward and outward traffic is diverted through the same pipe, then the pipe will simulate half-duplex traffic, simply because, a pipe can not

simulate traffic going in both directions at the same time. If one is simulating ethernet traffic, or simply using the pipes to control ethernet traffic, then this is not a problem, for ethernet is a half-duplex network. However, many other network connexions are full-duplex; as such, it is safer to usually setup one pipe for inward traffic and one for outward. This is the second reason for having two rules, each controlling a direction, as was mentioned in the previous section on pipe masks. Appendix A: Example Firewall Configurations Here follow a number of scenarios requiring firewalling. Each scenario is answered by a firewall rulest and a quick explanation as to how it works. For all examples, 12.18.123.0/24 will be used as the local subnet, xl0 will be used as the external NIC, and xl1 will be used as the internal NIC. Q) How do I block external pings, but allow myself to ping out to any external host? A) Stateful solution. The dynamic rules for icmp packets use the net.inet.ip.fw.dyn_short_lifetime setting, which is 5 seconds by default. The advantage of the stateful solution is that echo replies from only the specific host you pinged will be accepted. add add add icmptypes 8 add 1000 deny icmp from any to 12.18.123.0/24 in via xl0 icmptypes 8 1010 check-state 1020 allow icmp from 12.18.123.0/24 to any out via xl0 keep-state 1030 deny icmp from any to any

The reason for having the deny rule before the check-state rule is that the dynamic rules are bi-directional. As such, during the check-state echo requests can come from external hosts and they will be answered; essentially, during the short lives of the dynamic rules, your host will be pingable. Because of this, echo requests from external hosts are filtered prior to the check-state rule. Stateless Solution. The advantage of the stateless solution is less overhead because of less rules to process; but, overhead in dealing with occasional pings shouldn't be an issue for the most part, so this advantage is negligible. add 1000 deny icmp from any to 12.18.123.0/24 in via xl0 icmptypes 8 add 1010 allow icmp from 12.18.123.0/24 to any out via xl0 icmptypes 8 add 1020 allow icmp from any to 12.18.123.0/24 in via xl0 icmtypes 0 The disadvantage of the stateless approach is that it will always accept echo replies from any host, as opposed to the stateful approach which will only accept echo replies from the specific host that was pinged. Q) How do I block private subnets as defined in RFC 1918 from entering or exiting my network? A) add 1000 deny all from 192.168.0.0/16 to any via xl0 add 1010 deny all from any to 192.168.0.0/16 via xl0

add add add add

1020 1030 1040 1050

deny deny deny deny

all all all all

from from from from

172.16.0.0/12 to any via xl0 any to 172.16.0.0/12 via xl0 10.0.0.0/8 to any via xl0 any to 10.0.0.0/8 via xl0

Q) How would I enforce rate limiting on each host in my network individually? I want to enforce an upstream limit of 64Kbit/s and a downstream of 384Kbit/s for each host; in addition, I want to disallow all external hosts from initiating connexions with the hosts on my network so that no one can run any servers. A) This might be similar to a setup enforced at a university. It can be easily set with the following rules: pipe 10 config mask src-ip 0x000000ff bw 64kbit/s queue 8Kbytes pipe 20 config mask dst-ip 0x000000ff bw 384kbit/s queue 8Kbytes add 100 deny icmp from any to 12.18.123.0/24 in via xl0 icmptypes 8 add 110 check-state add 1000 pipe 10 all from 12.18.123.0/24 to any out via xl0 add 1100 pipe 20 all from any to 12.18.123.0/24 in via xl0 add 1200 allow tcp from 12.18.123.0/24 to any out via xl0 setup keep-state add 1200 allow udp from 12.18.123.0/24 to any out via xl0 keep-state add 1300 allow icmp from 12.18.123.0/24 to any out icmptypes 8 keep-state add 65535 deny all from any to any

Вам также может понравиться