HardenedBSD/sbin/ipfw/ipfw.1
Jordan K. Hubbard 5d39ab9169 Fix up the man page a little more, delete the README that crept in
(but I'm actually just as happy to have in the attic, for reference).
1994-10-28 15:12:22 +00:00

528 lines
24 KiB
Groff

.\"
.\" ipfw - a utility for manipulating the configuration of an IP firewall.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" @(#)ipfw.1
.\"
.TH ipfw 1 "October 27, 1994" "" "FreeBSD"
.SH NAME
ipfw - a utility for manipulating the configuration of an IP firewall.
.SH SYNOPSIS
.na
.B ipfw
.RB [options]
.SH DESCRIPTION
The
.B ipfw
command is used to configure an active IP firewall, setting masks on just
what sites are allowed to connect through it, which packets are rejected,
etc.
.SH OPTIONS
The command-line syntax of this command is rather involved, and rather than
spend a lot of time that I just don't have at the moment creating a
.B real
man page, with properly formatted sections and all, I'm just going to loosely
format the README I got. This really needs an nroff expert to go through
it with a chainsaw and do a
.N REAL
job of formatting it! This all looks rather horrible at present, and
I would actually almost recommend that you simply read the man page text
directly, rather than trying to format it. Sorry, but I do NOT speak
nroff, nor do I ever wish to learn how! :-) [-jkh].
.PP
For a sample kernel configuration file that will enable the right kernel
features necessary for firewalling, see
.I /sys/i386/conf/IPFIREWALL
.
.PP
.B WARNING!!! WARNING!!! WARNING!!! WARNING!!! WARNING!!! WARNING!!!
.PP
This utility can be used to put your machine into very dysfunctional state,
so if you want to test it then you should first make sure to read this man page
all the way through, and don't run it anywhere from the system console!
Using
.I ipfw
incorrectly is a really good way to kick yourself off your own machine
if you're logged in over a network! Also make sure to never set this
utility to be setuid root! It's a blatant security hole that way.
Instead, run it as root or from "/etc/rc.local" as part of the boot process.
It's also a good idea to use the checkb or checkf command options (see below)
to pass some test packets through the firewalls that you've defined before
going "live".
.PP
You may find it useful to create a file in which the first line is
.I firewall flush
to flush any existing firewalls before defining the explicit firewalls
that you wish to use. This will ensure that you're always working from a
known state.
.PP
The syntax for the
.BI ipfirewall
command option is rather complex and yet simple at the same time (if you know
what I mean). There are seven sub-commands, and probably the easiest way to
get into this is to give you a roughly BNF style grammar for the command
(curly brackets are used for precedence, alternatives are separated by |,
optional things are enclosed in square brackets, white space is required if
it appears below and must not appear if there isn't any between the tokens
below (i.e. no white space around periods, colons or slashes, whitespace
required between all other tokens)):
.PP
.nf
command ::= ipfirewall <list> | <flush> | <check> | <add> | <del>
<list> ::= list
<flush> ::= flush
<check> ::= { checkb[locking] | checkf[orwarding] } <chkparms>
<add> ::= { addb[locking] | addf[orwarding] } <add-del-parms>
<del> ::= { delb[locking] | delf[orwarding] } <add-del-parms>
<chkparms> ::= <protocol> from <ipaddr> <port> to <ipaddr> <port>
<protocol> ::= tcp | udp
<ipaddr> ::= <int>.<int>.<int>.<int> | <hostname>
<hostname> ::= a host name from /etc/hosts
<port> ::= <int> | <service>
<service> ::= a service from /etc/services
<int> ::= a non-negative integer
<add-del-parms> ::= { accept | deny } { <universal_firewall> | <protocol_firewall> }
<universal_firewall> ::= all from <masked_ipaddr> to <masked_ipaddr>
<masked_ipaddr> ::= { <ipaddr>/<bits> } | { <ipaddr>:<ipaddr> } | <ipaddr>
<bits> ::= integer in the range 0 to 32 inclusive
<protocol_firewall> ::= <protocol> from <end_firewall> to <end_firewall>
<end_firewall> ::= <masked_ipaddr> <port_list>
<port_list> ::= [ <port>:<port> ] <sub_port_list>
<sub_port_list> ::= <port> [ <sub_port_list> ]
.fi
.PP
Although I think that the above grammar is complete, it isn't exactly what
one would call easy to comprehend! Here's the basic idea along with what
each of the forms mean:
.PP
The
.I ipfirewall list
command prints a list of the firewalls on both the
forwarding and blocking chain in some more or less comprehensible format.
.PP
The
.I ipfirewall flush
command empties the two firewall chains.
.PP
The
.I ipfirewall addblocking
and
.I ipfirewall addforwarding
commands take a firewall description and add the firewall to the appropriate
firewall chain. Note that you'll probably need to add some descriptions more
then once, which will naturally take more then one entry in memory. It does
not lead to significant degradation of performance, so don't worry about it.
.PP
The
.I ipfirewall delblocking
and
.I ipfirewall delforwarding
commands take a firewall description and delete the firewall from the
appropriate firewall chain. The description must exactly match that given
to an earlier add command. One delete command removes ALL matching entries
from firewall chains.
.PP
There are two basic kinds of firewall descriptions. Universal firewall
descriptions match all IP packets between specified pairs of hosts.
Universal firewalls only check IP addresses (e.g. they match any combination
of protocol and port numbers). Protocol-specific firewalls match either
TCP/IP or UDP/IP packets between specified pairs of hosts. In addition
to host descriptions, protocol-specific firewalls optionally take a
description of which port numbers to match.
.PP
A host description consists of an IP address and a mask. The IP address
is specified as either a domain name or in the familiar
nn.nn.nn.nn format. The mask indicates how much of the IP address
should be looked at when vetting packets. There are two ways to
specify the mask. The first way is to suffix the IP address in the
firewall with a slash and an integer in the range 0 through 32 inclusive.
This integer is taken to be the number of high order bits of the IP
address which are to be checked (for example, 192.153.211.0/24 checks
the top 24 bits of the IP address, 192.153.211.17/32 checks all the
bits and 0.0.0.0/0 checks none of the bits (i.e. all IP addresses are
matched by this example)). The second way to specify a mask is to
suffix the IP address with a colon followed by another IP address.
This second address is the mask. Specifications equivalent to the
above three examples using this syntax would be
.PP
.nf
192.153.211.0:255.255.255.0
192.153.211.17:255.255.255.255
0.0.0.0:0.0.0.0
.fi
.PP
The first form is taken from the syntax accepted by a Telebit NetBlazer.
The second form is more along the lines of how a netmask is specified
in /etc/netmasks. Finally, if no mask is specified then a mask of all
1's is supplied (i.e. no mask is equivalent to /32 or :255.255.255.255).
.PP
The optional description of port numbers to mask can take three forms.
The simplest form is to omit the list in which case all port numbers
match. The next form is to specify a list of port numbers (either as
positive integers or service names from /etc/services). The final form
is actually a special case of the second form in which the first pair
of port numbers is separated by a colon instead of white space. This
pair specifies a range of port numbers (i.e. x:y specifies that all
ports between x and y inclusive should match). A port description
matches a particular port number if any of the following is true:
.nf
- the port description is null
- the first pair of port numbers is a range and the port number
is in the range (inclusive)
- the port number is equal to any of the port numbers in the list
.fi
.PP
There is a limit of a total of 10 port numbers in the source and
destination port lists. This limit is arbitrary and easy to increase.
It is determined by the value of the IP_FIREWALL_MAX_PORTS #define
variable in ip_firewall.h. Each increase of 1 for this value adds two
bytes to the size of each firewall. Since the size of a firewall is only
slightly over 30 bytes right now, this limit of 10 could probably
be increased by quite a bit before it became a concern. I've been
thinking of increasing it to 20 which would be longer than any
reasonable firewall would need and would only consume 20 more bytes
per firewall. The counter argument to any increase is that it is
always possible to construct an equivalent set of two or more firewalls
that behaves like a single firewall with a really long port list.
.PP
This probably all sounds hopelessly complicated. It is actually not
all that tricky (I'm just not very good at explaining it yet). A few
examples will probably help a lot now:
.PP
Block all IP packets originating from the host hackers-den:
.PP
.nf
ipfirewall addb deny all from hackers-den to 0.0.0.0/0
.fi
.PP
Block all telnet packets to our telnet server from anywhere:
.PP
.nf
ipfirewall addb deny tcp from 0.0.0.0/0 to mymachine/32 telnet
.fi
.PP
Don't forward telnet, rlogin and rsh packets onto our local
class C network:
.PP
.nf
ipfirewall addf deny tcp from 0.0.0.0/0 to ournetwork/24 telnet login shell
.fi
.PP
Don't let anyone on the local machine or any machine inside
our local network ftp access to games.com:
.PP
.nf
ipfirewall addb deny tcp from games.com ftp to 0.0.0.0/0
.fi
.PP
This last one might look a little strange. It doesn't prevent
anyone from sending packets to the games.com ftp server. What it
does do is block any packets that the games.com ftp server sends
back!
.PP
The
.I ipfirewall checkblocking
and
.I ipfirewall checkforwarding
commands take a description of an IP packet and check to see if the blocking
or forwarding chain of firewalls respectively accept or reject the packet.
It is used to make sure that the firewalls that you've defined work as
expected. The basic syntax is probably best understood by looking at
a couple of examples:
.PP
.nf
ipfirewall checkb from bsdi.com 3001 to mymachine telnet
.fi
.PP
checks to see if the blocking firewall will block a telnet packet from
a telnet session originating on bsdi.com to the host mymachine will be
blocked or not. Note that someone connecting to our telnet server
could be using practically any port number. To be really sure, the
firewall used to prevent access should be as simple as possible and/or
you should try a variety of port numbers in addition to the rather
arbitrarily chosen port of 3001.
.PP
One final note on the check* ,add* and del* command syntax. The noise word
"to" exists in the syntax so that I can detect the end of a list of
port numbers in the from description. Since I needed a noise word to
detect this case, I added the noise word "from" in front of the from
case for consistency.
.PP
Finally, have a look at the file
.I "/usr/share/misc/ipfw.samp.filters"
. It is the set of filters that I run at home [Danny].
.PP
Also check
.I "/usr/share/misc/ipfw.samp.scripts"
For examples of individual access restrictions.
We [NetVision] use those for our dial-in PPP/SLIP users to allow some of them
to access our internal networks, while disallowing others.
This way we open access for the user's IP when he enters the system and shut it
down when he leaves. All such changes may be applyed at any time,
and so entries added and deleted from firewall while the system is
is working have no other side effects [Ugen].
.SH "TECHNICAL DETAILS"
A bit of a description of how the firewalls are applied (i.e. what happens in
the kernel) may be instructive to the advanced firewall-builder:
.PP
When an IP packet is received, the ipintr() routine in ip_input.c is
called. This routine does a bit of basic error checking. If it
detects any errors in the packet it generally drops the packet on
the floor. The idea behind the ipfirewall facility is to treat packets
that we don't want to accept as bad packets (i.e. drop them on the
floor). The ipfirewall facility intercedes in the normal processing
at two points. Just after the basic sanity checks are done, we pass
any packets not targeted at the loopback network (127.0.0.0/8) to the
firewall checker along with the chain of blocking firewalls.If the firewall
checker tells us to block the packet then we branch to the "bad:" label
in ipintr() which is where all bad packets are dropped on the floor.
Otherwise, we allow normal processing of the packet to continue. The
exact point at which we intercede was chosen to be after the basic
sanity checking and before the option processing is done. We want to
be after the basic sanity checking so that we don't have to be able
to handle complete garbage. We want to be before the option processing
because option processing is done in separate rather complex routine.
Why bother doing this special processing if we might be dropping the
packet?
.PP
The second point at which we intercede is when a packet is about to be
forwarded to another host. All such packets are passed to the ip_forward
routine. The ipfirewall code is at the very top of this routine. If
the packet isn't targetted at the loopback interface (is it possible
that it could be when we reach this point? I doubt it but safety first)
then pass the packet to the firewall checker along with the forwarding
firewall chain. If the firewall checker indicates that the packet should
not be forwarded then we drop in (using code copied from a few lines
further into the routine which drops broadcast packets which are not
to be forwarded).
.PP
There are a couple of consequences of this approach:
.PP
1) Packets which are blocked are never forwarded (something to keep
in mind when designing firewalls).
.PP
2) Packets targeted at the loopback interface (127.0.0.0/8) are never
blocked. Blocking packets to the loopback interface seems pointless
and potentially quite confusing. It also makes a possibly common
case very cheap.
.PP
3) The sender of a packet which is blocked receives no indication that
the packet was dropped. The Telebit NetBlazer can be configured to
silently drop a blocked packet or to send back a "you can't get there
from here" packet to the sender. Implementing the later would have
been more work (possibly quite a bit more, I don't really know). Also,
I don't see any reason to give a potential hacker any more information
than necessary. Dropping the packet into the bit bucket seems like
the best way to keep a hacker guessing. [Danny]
.PP
(I am working on this feature, it would be made optional and
configurable by some ICMP_UNREACH_ON_DROP option, or such [Ugen]).
.PP
The firewall checker takes two parameters. The first parameter is a pointer
to the packet in question. The second parameter is a pointer to the
appropriate firewall chain. At the present time, the firewall checker passes
these parameters to a second routine which is the real firewall checker.
If the real checker says NO then an appropriate message is printed
onto the console. This is useful for debugging purposes. Whether or
not it remains in the long term depends on whether it is considered useful
for logging purposes (I'm a little reluctant to leave it in since it
provides a hacker with a way to commit a "denial of service" offense
against you by filling up your /var/log/messages file's file system
with error messages. There are ways of preventing this but ... [Danny]).
In default configuration now no information about dropped packets
printed.You may, however, define it as i do by adding
.I options IPFIREWALL_VERBOSE
to your kernel configuration file. Very useful thingy! [Ugen]
.PP
A return value of 0 from this routine (or the real firewall checker)
indicates that the packet is to be dropped. A value of 1 indicates
that the packet is to be accepted. In the early testing stages you
might want to make the top level firewall checker always return 1 even
if the real checker returns 0 just in case the real firewall checker
screws up (or your firewalls aren't as well designed as they should be).
In fact, this might be a useful optional feature (providing a way to
leave a door unlocked doesn't seem all that wise but it has to be
balanced against the inconvenience to legitimate users who might get
screwed up by poorly designed firewalls).
.PP
The real firewall returns 1 (accept the packet) if the chain is empty. If
efficiency is a concern (which it is in this code), this check should
be done in ip_input.c before calling the firewall checker.
.PP
Assuming that there is a firewall chain to scan through, the real firewall
checker picks up the src and dst IP addresses from the IP packet. It
then goes through the firewall chain looking for the first firewall that
matches the packet. Once a matching firewall has been found, a value of
1 is returned if the firewall is an accept firewall and a value of 0 is
returned otherwise.
.PP
The following processing is done for each firewall on the chain:
.PP
1) check the src and dst IP addresses. If they don't match then
there isn't any point in looking any further at this firewall.
This check is done by anding the packet's IP addresses the
with appropriate masks and comparing the results to the
appropriate addresses in the firewall. Note that the mask is
NOT applied to the address in the firewall. If it has any 1
bits that are 0 bits in the mask then the firewall will never
match (this will be checked in ipfirewall soon). If the addresses
match then we continue with the next step.
.PP
2) If the firewall is a universal firewall then we've got a match.
Return either 0 or 1 as appropriate. Otherwise, continue with
the next step.
.PP
3) Examine the IP protocol from the packet. If we havn't had to
look at it before then we get it and set a local variable to
IP_FIREWALL_TCP for TCP/IP packets, IP_FIREWALL_UDP for UDP/IP
packets, IP_FIREWALL_ICMP for ICMP packets, and IP_FIREWALL_UNIVERSAL
for all other packet types. Also, if the packet is a TCP/IP or
a UDP/IP packet, save the source and destination port numbers
at this point (taking advantage of the fact that the port numbers
are stored in the same place in either a TCP/IP or a UDP/IP
packet header). If the packet is neither a TCP/IP or a UDP/IP
packet then this firewall won't match it (on to the next firewall).
If this packet's protocol doesn't match this firewall's protocol
(which can't be universal or we wouldn't be here) then on to
the next firewall. Otherwise, continue with the next step.
.PP
4) We're checking either a TCP/IP or a UDP/IP packet. If the
firewall's source port list is empty or the packet's source
port matches something in the source port list AND if the firewall's
destination port list is empty or the packet's destination
port matches something in the destination port list then
we've got a match (return 0 or 1 as appropriate). Otherwise,
on to the next firewall.
.PP
As indicated above, if no packet on the chain matches the packet then
it is accepted if the first firewall was a deny firewall and it is rejected
if the first firewall was an accept packet. This is equivalent to the
default behaviour of a Telebit NetBlazer. They provide a way to override
this behaviour. I'm not convinced that it is necessary (I'm open to
suggestions).
.PP
That's about it for the firewall checker. The
.I ipfw
program communicates with the kernel part of the firewall facility by making
setsockopt calls on RAW IP sockets. Only root is allowed to open a RAW IP
socket. This ensures that only root uses
.I ipfw to manipulate the firewall facility.
Also, somewhere in the kernel source or on a man page, I read that the
RAW IP setsockopt calls are intended for manipulating the IP protocol layer
as opposed to manipulating any particular instance of a socket. This seems
like a reasonable description of what the firewall setsockopt command
codes do.
.PP
There are seven setsockopt command codes defined by the firewall facility
(in netinet/in.h). They are:
.PP
.nf
IP_FLUSH_FIREWALLS flush (i.e. free) both firewall chains.
IP_ADD_FORWARDING_FIREWALL add firewall pointed at by optval parm to
the end of the forwarding firewall chain.
IP_ADD_BLOCKING_FIREWALL add firewall pointed at by optval parm to
the end of the blocking firewall chain.
IP_DEL_FORWARDING_FIREWALL delete firewall pointed at by optval parm
from the forwarding firewall chain.
IP_DEL_BLOCKING_FIREWALL delete firewall pointed at by optval parm
from the blocking firewall chain.
IP_CHECK_FORWARDING_FIREWALL pass the IP packet do the firewall checker
along with the forwarding firewall chain.
Return 0 if packet was accepted, -1 (with
errno set to EACCES) if it wasn't.
IP_CHECK_BLOCKING_FIREWALL pass the IP packet do the firewall checker
along with the blocking firewall chain.
Return 0 if packet was accepted, -1 (with
errno set to EACCES) if it wasn't.
The IP_ADD_* and IP_DEL_* command codes do a fair bit of validity checking.
It is quite unlikely that a garbage firewall could get past them that
would cause major problems in the firewall checker. It IS possible for
a garbage packet to get past the checks which causes major grief because
it either blocks or accepts packets according to unusual rules (the rules
will conform to the ones described above but will probably come as quite
a surprise).
The IP_CHECK_* command codes expect the optval parameter to point
to a struct ip immediately followed by a header appropriate to the protocol
value described in the ip_p field of the ip header. The exact requirements
are as follows:
- The length of the optval parameter must be at least
sizeof(struct ip) + 2 * sizeof(u_short)
since this is the amount of memory that might be referenced by
the firewall checker.
- The ip_hl field of the ip structure must be equal to
sizeof(struct ip) / sizeof(int)
since this value indicates that the tcp/udp/??? header immediately
follows the ip header (appropriate for the purposes that this
interface is intended for).
Failure to follow these rules (for either the IP_ADD_*,IP_DEL_* or the
IP_CHECK_*_FIREWALL commands) will result in a return value of -1 with
errno set to EINVAL (for now, it will also result in an appropriate
message on the console).
To read current configuration of firewalls,the kvm_read() function used.
Symbols,which you have to find are :
struct ip_firewall * ip_firewall_blocking_chain ;
struct ip_firewall * ip_firewall_forwarding_chain ;
Both are pointers to the linked list of firewall entries.
Of course, you must at least be a member of group kmem to read kernel
symbols.
.fi
.PP
There are a couple of additional details that are worth reading about in
the ip_firewall.h file. Other than that, let the authors know how you do!
If you have any problems, you may call Danny Boulet at home (403 449-1835)
or send e-mail to <danny@BouletFermat.ab.ca>. If you call, please keep in
mind that Danny lives in the Canadian Mountain timezone (GMT-0600).
.PP
You may also reach some commercial users of this package (and also those
responsible for porting it to FreeBSD and adding several additional
commands), at 972-4-550-330, or via email at <ugen@NetVision.net.il>.
If you call, remember that Ugen lives in the Israel timezone, which is GMT+02.
.SH FILES
/usr/share/misc/ipfw.samp.filters
/usr/share/misc/ipfw.samp.scripts
.SH "BUGS"
You can very easily hose your machine utterly if you don't know what you're
doing. Dieses Befehl ist nur fuer Experten!
.SH "SEE ALSO"
.BR reboot (1) ,
.PP
.BR /sys/i386/conf/IPFIREWALL
.SH AUTHORS
Daniel Boulet <danny@BouletFermat.ab.ca>
.PP
Ugen J.S.Antsilevich <ugen@NetVision.net.il>
.PP
Jordan K. Hubbard <jkh@FreeBSD.org> [Crimes committed in this manpage]