sys/netinet/ip_fw2.c:

Implement the M_SKIP_FIREWALL bit in m_flags to avoid loops
    for firewall-generated packets (the constant has to go in sys/mbuf.h).

    Better comments on keepalive generation, and enforce dyn_rst_lifetime
    and dyn_fin_lifetime to be less than dyn_keepalive_period.

    Enforce limits (up to 64k) on the number of dynamic buckets, and
    retry allocation with smaller sizes.

    Raise default number of dynamic rules to 4096.

    Improved handling of set of rules -- now you can atomically
    enable/disable multiple sets, move rules from one set to another,
    and swap sets.

sbin/ipfw/ipfw2.c:

    userland support for "noerror" pipe attribute.

    userland support for sets of rules.

    minor improvements on rule parsing and printing.

sbin/ipfw/ipfw.8:

    more documentation on ipfw2 extensions, differences from ipfw1
    (so we can use the same manpage for both), stateful rules,
    and some additional examples.
    Feedback and more examples needed here.
This commit is contained in:
Luigi Rizzo 2002-08-16 10:31:47 +00:00
parent ffd711039f
commit 99e5e64504
3 changed files with 644 additions and 165 deletions

View File

@ -1,7 +1,12 @@
.\"
.\" $FreeBSD$
.\"
.Dd May 31, 2001
.de NOIPFW
.br
(\\$1 NOT IN IPFW)
.br
..
.Dd August 13, 2002
.Dt IPFW 8
.Os
.Sh NAME
@ -13,11 +18,6 @@
.Cm add
.Ar rule
.Nm
.Op Fl q
.Cm delete
.Op Cm set
.Op Ar number ...
.Nm
.Op Fl adeftNS
.Brq Cm list | show
.Op Ar number ...
@ -26,16 +26,20 @@
.Cm flush
.Nm
.Op Fl q
.Brq Cm zero | resetlog
.Brq Cm delete | zero | resetlog
.Op Cm set
.Op Ar number ...
.Pp
.Nm
.Op Fl q
.Brq Cm disable | enable
.Cm set
.Op Ar number ...
.Cm set Oo Cm disable Ar number ... Oc Op Cm enable Ar number ...
.Nm
.Cm show sets
.Cm set move
.Op Cm rule
.Ar number Cm to Ar number
.Nm
.Cm set swap Ar number number
.Nm
.Cm set show
.Pp
.Nm
.Brq Cm pipe | queue
@ -68,6 +72,22 @@ firewall and the
traffic shaper in
.Fx .
.Pp
.Em NOTE:
this manual page refers to the newer version of
.Nm
introduced in July 2002, also known as
.Nm ipfw2 .
The commands listed here are a superset of the old
firewall, which we will call
.Nm ipfw1
when it is necessary to distinguish between the two.
See the
.Sx IPFW2 ENHANCEMENTS
Section for a list of features which are not present in
.Nm ipfw1 .
This list can also be useful to revise your ruleset and
write them more efficiently.
.Pp
An
.Nm
configuration, or
@ -126,10 +146,10 @@ or
rule, and are typically used to open the firewall on-demand to
legitimate traffic only.
See the
.Sx RULE FORMAT
.Sx STATEFUL FIREWALL
and
.Sx EXAMPLES
sections below for more information on the stateful behaviour of
Sections below for more information on the stateful behaviour of
.Nm .
.Pp
All rules (including dynamic ones) have a few associated counters:
@ -157,6 +177,19 @@ and
.Cm resetlog
commands.
.Pp
Also, each rule belongs to one of 32 different
.Em sets
, and there are
.Nm
commands to atomically manipulate sets, such as enable,
disable, swap sets, move all rules in a set to another
one, delete all rules in a set. These can be useful to
install temporary configurations, or to test them.
See Section
.Sx SETS OF RULES
for more information on
.Em sets .
.Pp
The following options are available:
.Bl -tag -width indent
.It Fl a
@ -174,8 +207,7 @@ option was specified, also show expired dynamic rules.
Don't ask for confirmation for commands that can cause problems
if misused,
.No i.e. Cm flush .
.Em Note ,
if there is no tty associated with the process, this is implied.
If there is no tty associated with the process, this is implied.
.It Fl N
Try to resolve addresses and service names in output.
.It Fl q
@ -206,7 +238,9 @@ to the login session, causing the remote login session to be closed
and the remainder of the ruleset is not processed.
Access to the console would then be required to recover.
.It Fl S
While listing rules, show the set each rule belongs to.
While listing rules, show the
.Em set
each rule belongs to.
If this flag is not specified, disabled rules will not be
listed.
.It Fl s Op Ar field
@ -265,7 +299,7 @@ and
.Cm queue
commands are used to configure the traffic shaper, as shown in the
.Sx TRAFFIC SHAPER CONFIGURATION
section below.
Section below.
.Sh PACKET FLOW
.Nm
can be invoked from multiple places in the protocol stack,
@ -404,7 +438,7 @@ which defaults to 100.
If this is not possible (e.g. because we would go beyond the
maximum allowed rule number), the same number of the last
non-default value is used instead.
.It Ar set_number
.It Cm set Ar set_number
Each rule is associated to a
.Ar set_number
in the range 0..31, with the latter reserved for the
@ -535,7 +569,7 @@ Pass packet to a
(for bandwidth limitation, delay, etc.).
See the
.Sx TRAFFIC SHAPER CONFIGURATION
section for further information.
Section for further information.
The search terminates; however, on exit from the pipe and if
the
.Xr sysctl 8
@ -568,7 +602,7 @@ Send a copy of packets matching this rule to the
socket bound to port
.Ar port .
The search terminates and the original packet is accepted
(but see section
(but see Section
.Sx BUGS
below).
.It Cm unreach Ar code
@ -630,13 +664,17 @@ The general rule body format is one of the following:
.Op Ar options
.br
.Cm MAC Ar dst-mac src-mac mac-type
.Op Cm from Ar src Cm to Ar dst
.Op Ar options
.Ed
.Pp
where fields have the following meaning:
where the second format allows you to specify MAC header fields
instead (or in addition) of the IPv4 header fields.
.Pp
Rule fields have the following meaning:
.Bl -tag -width indent
.It Ar proto
An IP protocol specified by number or name (for a complete
An IPv4 protocol specified by number or name (for a complete
list see
.Pa /etc/protocols ) .
The
@ -652,7 +690,6 @@ A single
containing one or more of them,
optionally followed by
.Em port numbers.
followed by a set of port numbers.
.It Ar ip address :
An address (or set of addresses) specified in one of the following
ways, optionally preceded by a
@ -699,7 +736,7 @@ within a single rule. Because the matching occurs using a
bitmask, it takes constant time and dramatically reduces
the complexity of rulesets.
.El
.It Cm port numbers
.It port numbers
With protocols which support port numbers (such as TCP and UDP), optional
.Cm ports
may be specified as one or more ports or port ranges, separated
@ -741,6 +778,28 @@ specifications.
See the
.Cm frag
option for details on matching fragmented packets.
.It dst-mac, src-mac
Destination and source MAC addresses, specified as
groups of hex digits separated by commas, and optionally
followed by a mask indicating how many bits are significant:
.Pp
.Dl "ipfw add allow MAC 10:20:30:40:50:60/30 any any
.Pp
Note that the order of MAC addresses (destination first,
source second) is
the same as on the wire, but the opposite of the one used for
IP addresses.
.It mac-type
The value of the Ethernet Type field, specified in the same way as
.Cm port numbers
(i.e. one or more comma-separated single values or ranges).
You can use symbolic names for known values such as
.Em vlan , ipv4, ipv6 .
The values can be enter as decimal or hexadecimal, but they
are always printed as hexadecimal (unless the
.Cm -N
option is used, in which case symbolic resolution will be
attempted).
.El
.Ss RULE OPTIONS
Additional match patterns can be used within
@ -1016,12 +1075,127 @@ A
.Ar user
may be matched by name or identification number.
.El
.Sh STATEFUL FIREWALL
To be completed.
.Sh TRAFFIC SHAPER CONFIGURATION
The
.Sh SETS OF RULES
Each rule belongs to one of 32 different
.Em sets
, numbered 0 to 31.
Set 31 is reserved for the default rule.
.Pp
By default, rules are put in set 0, unless you use the
.Cm set N
attribute when entering a new rule.
Sets can be individually and atomically enabled or disabled,
so this mechanism permits an easy way to store multiple configurations
of the firewall and quickly (and atomically) switch between them.
The command to enable/disable sets is
.Pp
.Nm
utility is also the user interface for the
.Cm set disable Ar number ... Op Cm enable Ar number ...
.Pp
where multiple
.Cm enable
or
.Cm disable
sections can be specified.
Command execution is atomic on all the sets specified in the command.
By default, all sets are enabled.
.Pp
When you disable a set, its rules behave as if they were not existing
in the firewall configuration, with only one exception:
.Bl -bullet
.It
dynamic rules created from a rule before it had been disabled
will still be active until they expire. In order to delete
dynamic rules you have to explicitly delete the parent rule
which generated them;
.El
The set number of rules can be changed with the command
.Pp
.Nm
.Cm set move
.Brq Cm rule Ar rule-number | old-set
.Cm to Ar new-set
.Pp
Also, you can atomically swap two rulesets with the command
.Pp
.Nm
.Cm set swap Ar first-set second-set
.Pp
See the
.Sx EXAMPLES
Section on some possible uses of sets of rules.
.Sh STATEFUL FIREWALL
Stateful operation is a way for the firewall to dynamically
create rules for specific flows when packets that
match a given pattern are detected. Support for stateful
operation comes through the
.Cm check-state , keep-state
and
.Cm limit
options of
.Nm rules.
.Pp
Dynamic rules are created when a packet matches a
.Cm keep-state
or
.Cm limit
rule, causing the creation of a
.Em dynamic
rule which will match all and only packets with
a given
.Em protocol
between a
.Em src-ip/src-port dst-ip/dst-port
pair of addresses (
.Em src
and
.Em dst
are used here only to denote the initial match addresses, but they
are completely equivalent afterwards).
Dynamic rules will be checked at the first
.Cm check-state, keep-state
or
.Cm limit
occurrence, and the action performed upon a match will be the same
as in the parent rule.
.Pp
Note that no additional attributes other than protocol and IP addresses
and ports are checked on dynamic rules.
.Pp
The typical use of dynamic rules is to keep a closed firewall configuration,
but let the first TCP SYN packet from the inside network install a
dynamic rule for the flow so that packets belonging to that session
will be allowed through the firewall:
.Pp
.Dl "ipfw add check-state"
.Dl "ipfw add allow tcp from my-subnet to any setup"
.Dl "ipfw add deny tcp from any to any"
.Pp
A similar approach can be used for UDP, where an UDP packet coming
from the inside will install a dynamic rule to let the response through
the firewall:
.Pp
.Dl "ipfw add check-state"
.Dl "ipfw add allow udp from my-subnet to any"
.Dl "ipfw add deny udp from any to any"
.Pp
Dynamic rules expire after some time, which depends on the status
of the flow and the setting of some
.Cm sysctl
variables.
See Section
.Sx SYSCTL VARIABLES
for more details.
For TCP sessions, dynamic rules can be instructed to periodically
send keepalive packets to refresh the state of the rule when it is
about to expire.
.Pp
See Section
.Sx EXAMPLES
for more examples on how to use dynamic rules.
.Sh TRAFFIC SHAPER CONFIGURATION
.Nm
is also the user interface for the
.Xr dummynet 4
traffic shaper.
The shaper operates by dividing packets into
@ -1124,22 +1298,6 @@ variable
.Em net.inet.ip.dummynet.hash_size ,
allowed range is 16 to 1024.
.Pp
.It Cm queue Brq Ar slots | size Ns Cm Kbytes
Queue size, in
.Ar slots
or
.Cm KBytes .
Default value is 50 slots, which
is the typical queue size for Ethernet devices.
Note that for slow speed links you should keep the queue
size short or your traffic might be affected by a significant
queueing delay.
E.g., 50 max-sized ethernet packets (1500 bytes) mean 600Kbit
or 20s of queue on a 30Kbit/s pipe.
Even worse effect can result if you get packets from an
interface with a much larger MTU, e.g. the loopback interface
with its 16KB packets.
.Pp
.It Cm mask Ar mask-specifier
The
.Xr dummynet 4
@ -1167,6 +1325,14 @@ configuration, each flow is assigned a weight equal to the
weight of the queue, and all flows insisting on the same pipe
share bandwidth proportionally to their weight.
.Pp
.It Cm noerror
When a packet is dropped by a dummynet queue or pipe, the error
is normally reported to the caller routine in the kernel, in the
same way as it happens when a device queue fills up. Setting this
option reports the packet as successfully delivered, which can be
needed for some experimental setups where you want to simulate
loss or congestion at a remote router.
.Pp
.It Cm plr Ar packet-loss-rate
Packet loss rate.
Argument
@ -1175,6 +1341,22 @@ is a floating-point number between 0 and 1, with 0 meaning no
loss, 1 meaning 100% loss.
The loss rate is internally represented on 31 bits.
.Pp
.It Cm queue Brq Ar slots | size Ns Cm Kbytes
Queue size, in
.Ar slots
or
.Cm KBytes .
Default value is 50 slots, which
is the typical queue size for Ethernet devices.
Note that for slow speed links you should keep the queue
size short or your traffic might be affected by a significant
queueing delay.
E.g., 50 max-sized ethernet packets (1500 bytes) mean 600Kbit
or 20s of queue on a 30Kbit/s pipe.
Even worse effect can result if you get packets from an
interface with a much larger MTU, e.g. the loopback interface
with its 16KB packets.
.Pp
.It Cm red | gred Ar w_q Ns / Ns Ar min_th Ns / Ns Ar max_th Ns / Ns Ar max_p
Make use of the RED (Random Early Detection) queue management algorithm.
.Ar w_q
@ -1290,36 +1472,32 @@ These are shown below together with their default value
.Xr sysctl 8
command what value is actually in use) and meaning:
.Bl -tag -width indent
.It Em net.inet.ip.fw.autoinc_step : No 100
Delta beween rule numbers when auto-generating them.
The value must be in the range 1..1000.
.It Em net.inet.ip.fw.curr_dyn_buckets : Em net.inet.ip.fw.dyn_buckets
The current number of buckets in the hash table for dynamic rules
(readonly).
.It Em net.inet.ip.fw.debug : No 1
Controls debugging messages produced by
.Nm .
.It Em net.inet.ip.fw.one_pass : No 1
When set, the packet exiting from the
.Xr dummynet 4
pipe is not passed though the firewall again.
Otherwise, after a pipe action, the packet is
reinjected into the firewall at the next rule.
.It Em net.inet.ip.fw.verbose : No 1
Enables verbose messages.
.It Em net.inet.ip.fw.enable : No 1
Enables the firewall.
Setting this variable to 0 lets you run your machine without
firewall even if compiled in.
.It Em net.inet.ip.fw.verbose_limit : No 0
Limits the number of messages produced by a verbose firewall.
.It Em net.inet.ip.fw.dyn_buckets : No 256
.It Em net.inet.ip.fw.curr_dyn_buckets : No 256
The configured and current size of the hash table used to
hold dynamic rules.
This must be a power of 2.
The table can only be resized when empty, so in order to
resize it on the fly you will probably have to
The number of buckets in the hash table for dynamic rules.
Must be a power of 2, up to 1^^20.
It only takes effect when all dynamic rules have expired, so you
are advised to use a
.Cm flush
and reload the ruleset.
command to make sure that the hash table is resized.
.It Em net.inet.ip.fw.dyn_count : No 3
Current number of dynamic rules
(read-only).
.It Em net.inet.ip.fw.dyn_max : No 1000
.It Em net.inet.ip.fw.dyn_keepalive : No 1
Enables generation of keepalive packets for
.Cm keep-state
rules on TCP sessions. A keepalive is generated to both
sides of the connection every 5 seconds for the last 20
seconds of the lifetime of the rule.
.It Em net.inet.ip.fw.dyn_max : No 8192
Maximum number of dynamic rules.
When you hit this limit, no more dynamic rules can be
installed until old ones expire.
@ -1333,7 +1511,31 @@ These variables control the lifetime, in seconds, of dynamic
rules.
Upon the initial SYN exchange the lifetime is kept short,
then increased after both SYN have been seen, then decreased
again during the final FIN exchange or when a RST
again during the final FIN exchange or when a RST is received.
Both
.Em dyn_fin_lifetime
and
.Em dyn_rst_lifetime
must be strictly lower than 5 seconds, the period of
repetition of keepalives. The firewall enforces that.
.It Em net.inet.ip.fw.enable : No 1
Enables the firewall.
Setting this variable to 0 lets you run your machine without
firewall even if compiled in.
.It Em net.inet.ip.fw.one_pass : No 1
When set, the packet exiting from the
.Xr dummynet 4
pipe is not passed though the firewall again.
Otherwise, after a pipe action, the packet is
reinjected into the firewall at the next rule.
.Pp
Note: bridged and layer 2 packets coming out of a pipe
are never reinjected in the firewall irrespective of the
value of this variable.
.It Em net.inet.ip.fw.verbose : No 1
Enables verbose messages.
.It Em net.inet.ip.fw.verbose_limit : No 0
Limits the number of messages produced by a verbose firewall.
.It Em net.link.ether.ipfw : No 0
Controls whether layer-2 packets are passed to
.Nm .
@ -1343,7 +1545,68 @@ Controls whether bridged packets are passed to
.Nm .
Default is no.
.El
.Sh IPFW2 ENHANCEMENTS
This Section lists the features that have been introduced in
.Nm ipfw2
and were not present in
.Nm ipfw1 .
We list them in order of the potential impact that they can
have in writing your rulesets.
You might want to consider using these features in order to
write your rulesets in a more efficient way.
.Bl -tag -width indent
.It Address sets
.Nm ipfw1
does not supports address sets (those in the form
.Ar addr/masklen{num,num,...}
)
.It Port specifications
.Nm ipfw1
only allows one port range when specifying TCP and UDP ports, and
is limited to 10 entries instead of the 15 allowed by
.Nm ipfw2 .
Also, in
.Nm ipfw1
you can only specify ports when the rule is requesting
.Cm tcp
or
.Cm udp
packets. With
.Nm ipfw2
you can put port specifications in rules matching all packets,
and the match will be attempted only on those packets carrying
protocols which include port identifiers.
.It Or-blocks
.Nm ipfw1
does not support Or-blocks. All match operators are implicitly
connected by
.Cm and
operators.
.It keepalives
.Nm ipfw1
does not generate keepalives for stateful sessions.
As a consequence, it might cause idle sessions to drop because
the lifetime of the dynamic rules expires.
.It Sets of rules
.Nm ipfw1
does not implement sets of rules.
.It MAC header filtering and Layer-2 firewalling.
.Nm ipfw1
does not implement filtering on MAC header fields, nor it is
invoked on packets from
.Cm ether_demux()
and
.Cm ether_output_frame().
The sysctl variable
.Em net.link.ether.ipfw
has no effect there.
.El
.Sh EXAMPLES
There are far too many possible uses of
.Nm
so this Section will only give a small set of examples.
.Pp
.Ss BASIC PACKET FILTERING
This command adds an entry which denies all tcp packets from
.Em cracker.evil.org
to the telnet port of
@ -1375,6 +1638,24 @@ All other SYN packets will be rejected by the final
.Cm deny
rule.
.Pp
If you administer one or more subnets, you can take advantage of the
.Nm ipfw2
syntax to specify address sets and or-blocks and write extremely
compact rulesets which selectively enable services to blocks
of clients, as below:
.Pp
.Dl "goodguys=\*q{ 10.1.2.0/24{20,35,66,18} or 10.2.3.0/28{6,3,11} }\*q"
.Dl "badguys=\*q10.1.2.0/24{8,38,60}\*q"
.Dl ""
.Dl "ipfw add allow ip from ${goodguys} to any"
.Dl "ipfw add deny ip from ${badguys} to any"
.Dl "... normal policies ..."
.Pp
The
.Nm ipfw1
syntax would require a separate rule for each IP in the above
example.
.Ss DYNAMIC RULES
In order to protect a site from flood attacks involving fake
TCP packets, it is safer to use dynamic rules:
.Pp
@ -1434,6 +1715,7 @@ to divert port 5000:
.Pp
.Dl ipfw divert 5000 ip from 192.168.2.0/24 to any in
.Pp
.Ss TRAFFIC SHAPING
The following rules show some of the applications of
.Nm
and
@ -1525,6 +1807,27 @@ on a net with per-host limits, rather than per-network limits:
.Dl "ipfw add pipe 2 ip from any to 192.168.2.0/24 in"
.Dl "ipfw pipe 1 config mask src-ip 0x000000ff bw 200Kbit/s queue 20Kbytes"
.Dl "ipfw pipe 2 config mask dst-ip 0x000000ff bw 200Kbit/s queue 20Kbytes"
.Ss SETS OF RULES
To add a set of rules atomically, e.g. set 18:
.Pp
.Dl "ipfw disable set 18"
.Dl "ipfw add NN set 18 ... # repeat as needed"
.Dl "ipfw enable set 18"
.Pp
To delete a set of rules atomically the command is simply:
.Pp
.Dl "ipfw delete set 18"
.Pp
To test a ruleset and disable it and regain control if something goes wrong:
.Pp
.Dl "ipfw disable set 18"
.Dl "ipfw add NN set 18 ... # repeat as needed"
.Dl "ipfw enable set 18 ; echo done; sleep 30 && ipfw disable set 18"
.Pp
Here if everything goes well, you press control-C before the "sleep"
terminates, and your ruleset will be left active. Otherwise, e.g. if
you cannot access your box, the ruleset will be disabled after
the sleep terminates thus restoring the previous situation.
.Sh SEE ALSO
.Xr cpp 1 ,
.Xr m4 1 ,

View File

@ -223,6 +223,7 @@ enum tokens {
TOK_ICMPTYPES,
TOK_PLR,
TOK_NOERROR,
TOK_BUCKETS,
TOK_DSTIP,
TOK_SRCIP,
@ -241,6 +242,7 @@ enum tokens {
struct _s_x dummynet_params[] = {
{ "plr", TOK_PLR },
{ "noerror", TOK_NOERROR },
{ "buckets", TOK_BUCKETS },
{ "dst-ip", TOK_DSTIP },
{ "src-ip", TOK_SRCIP },
@ -502,8 +504,10 @@ fill_newports(ipfw_insn_u16 *cmd, char *av, int proto)
p[1] = b;
} else if (*s == ',' || *s == '\0' ) {
p[0] = p[1] = a;
} else /* invalid separator */
break;
} else { /* invalid separator */
errx(EX_DATAERR, "invalid separator <%c> in <%s>\n",
*s, av);
}
av = s+1;
}
if (i > 0) {
@ -737,17 +741,29 @@ print_icmptypes(ipfw_insn_u32 *cmd)
* show_ipfw() prints the body of an ipfw rule.
* Because the standard rule has at least proto src_ip dst_ip, we use
* a helper function to produce these entries if not provided explicitly.
*
* Special case: if we have provided a MAC header, and no IP specs,
* just leave it alone.
* Also, if we have providea a MAC header and no IP protocol, print it
* as "all" instead of "ip".
*/
#define HAVE_PROTO 1
#define HAVE_SRCIP 2
#define HAVE_DSTIP 4
#define HAVE_MAC 8
#define HAVE_PROTO 0x0001
#define HAVE_SRCIP 0x0002
#define HAVE_DSTIP 0x0004
#define HAVE_MAC 0x0008
#define HAVE_MACTYPE 0x0010
#define HAVE_IP (HAVE_PROTO | HAVE_SRCIP | HAVE_DSTIP)
static void
show_prerequisites(int *flags, int want)
{
if ( (*flags & (HAVE_MAC | HAVE_MACTYPE)) == HAVE_MAC) {
printf(" any"); /* MAC type */
*flags |= HAVE_MACTYPE;
}
if ( !(*flags & HAVE_PROTO) && (want & HAVE_PROTO))
printf(" ip");
printf( (*flags & HAVE_MAC) ? " all" : " ip");
if ( !(*flags & HAVE_SRCIP) && (want & HAVE_SRCIP))
printf(" from any");
if ( !(*flags & HAVE_DSTIP) && (want & HAVE_DSTIP))
@ -907,6 +923,9 @@ show_ipfw(struct ip_fw *rule)
break;
case O_MAC_TYPE:
if ( (flags & HAVE_MAC) == 0)
printf(" MAC");
flags |= (HAVE_MAC | HAVE_MACTYPE);
print_newports((ipfw_insn_u16 *)cmd, IPPROTO_ETHERTYPE);
break;
@ -1340,6 +1359,115 @@ list_pipes(void *data, int nbytes, int ac, char *av[])
}
}
/*
* This one handles all set-related commands
* ipfw set { show | enable | disable }
* ipfw set swap X Y
* ipfw set move X to Y
* ipfw set move rule X to Y
*/
static void
sets_handler(int ac, char *av[])
{
u_int32_t set_disable, masks[2];
int i, nbytes;
u_int16_t rulenum;
u_int8_t cmd, new_set;
ac--;
av++;
if (!ac)
errx(EX_USAGE, "set needs command");
if (!strncmp(*av, "show", strlen(*av)) ) {
void *data;
char *msg;
nbytes = sizeof(struct ip_fw);
if ((data = malloc(nbytes)) == NULL)
err(EX_OSERR, "malloc");
if (getsockopt(s, IPPROTO_IP, IP_FW_GET, data, &nbytes) < 0)
err(EX_OSERR, "getsockopt(IP_FW_GET)");
set_disable = (u_int32_t)(((struct ip_fw *)data)->next_rule);
for (i = 0, msg = "disable" ; i < 31; i++)
if ( (set_disable & (1<<i))) {
printf("%s %d", msg, i);
msg = "";
}
msg = (set_disable) ? " enable" : "enable";
for (i = 0; i < 31; i++)
if ( !(set_disable & (1<<i))) {
printf("%s %d", msg, i);
msg = "";
}
printf("\n");
} else if (!strncmp(*av, "swap", strlen(*av))) {
ac--; av++;
if (ac != 2)
errx(EX_USAGE, "set swap needs 2 set numbers\n");
rulenum = atoi(av[0]);
new_set = atoi(av[1]);
if (!isdigit(*(av[0])) || rulenum > 30)
errx(EX_DATAERR, "invalid set number %s\n", av[0]);
if (!isdigit(*(av[1])) || new_set > 30)
errx(EX_DATAERR, "invalid set number %s\n", av[1]);
masks[0] = (4 << 24) | (new_set << 16) | (rulenum);
i = setsockopt(s, IPPROTO_IP, IP_FW_DEL,
masks, sizeof(u_int32_t));
} else if (!strncmp(*av, "move", strlen(*av))) {
ac--; av++;
if (ac && !strncmp(*av, "rule", strlen(*av))) {
cmd = 2;
ac--; av++;
} else
cmd = 3;
if (ac != 3 || strncmp(av[1], "to", strlen(*av)))
errx(EX_USAGE, "syntax: set move [rule] X to Y\n");
rulenum = atoi(av[0]);
new_set = atoi(av[2]);
if (!isdigit(*(av[0])) || (cmd == 3 && rulenum > 30) ||
(cmd == 2 && rulenum == 65535) )
errx(EX_DATAERR, "invalid source number %s\n", av[0]);
if (!isdigit(*(av[2])) || new_set > 30)
errx(EX_DATAERR, "invalid dest. set %s\n", av[1]);
masks[0] = (cmd << 24) | (new_set << 16) | (rulenum);
i = setsockopt(s, IPPROTO_IP, IP_FW_DEL,
masks, sizeof(u_int32_t));
} else if (!strncmp(*av, "disable", strlen(*av)) ||
!strncmp(*av, "enable", strlen(*av)) ) {
int which = !strncmp(*av, "enable", strlen(*av)) ? 1 : 0;
ac--; av++;
masks[0] = masks[1] = 0;
while (ac) {
if (isdigit(**av)) {
i = atoi(*av);
if (i < 0 || i > 30)
errx(EX_DATAERR,
"invalid set number %d\n", i);
masks[which] |= (1<<i);
} else if (!strncmp(*av, "disable", strlen(*av)))
which = 0;
else if (!strncmp(*av, "enable", strlen(*av)))
which = 1;
else
errx(EX_DATAERR,
"invalid set command %s\n", *av);
av++; ac--;
}
if ( (masks[0] & masks[1]) != 0 )
errx(EX_DATAERR,
"cannot enable and disable the same set\n");
i = setsockopt(s, IPPROTO_IP, IP_FW_DEL, masks, sizeof(masks));
if (i)
warn("set enable/disable: setsockopt(IP_FW_DEL)");
} else
errx(EX_USAGE, "invalid set command %s\n", *av);
}
static void
list(int ac, char *av[])
{
@ -1361,23 +1489,6 @@ list(int ac, char *av[])
ac--;
av++;
if (ac && !strncmp(*av, "sets", strlen(*av)) ) {
int i;
u_int32_t set_disable;
nbytes = sizeof(struct ip_fw);
if ((data = malloc(nbytes)) == NULL)
err(EX_OSERR, "malloc");
if (getsockopt(s, IPPROTO_IP, IP_FW_GET, data, &nbytes) < 0)
err(EX_OSERR, "getsockopt(IP_FW_GET)");
set_disable = (u_int32_t)(((struct ip_fw *)data)->next_rule);
for (i = 0; i < 31; i++)
printf("%s set %d\n",
set_disable & (1<<i) ? "disable" : "enable", i);
return;
}
/* get rules or pipes from kernel, resizing array as necessary */
nbytes = nalloc;
@ -1691,7 +1802,7 @@ fill_flags(ipfw_insn *cmd, enum ipfw_opcodes opcode,
static void
delete(int ac, char *av[])
{
int rulenum;
u_int32_t rulenum;
struct dn_pipe pipe;
int i;
int exitval = EX_OK;
@ -1699,17 +1810,11 @@ delete(int ac, char *av[])
memset(&pipe, 0, sizeof pipe);
if (!strncmp(*av, "disable", strlen(*av)))
do_set = 2; /* disable set */
else if (!strncmp(*av, "enable", strlen(*av)))
do_set = 3; /* enable set */
av++; ac--;
if (ac > 0 && !strncmp(*av, "set", strlen(*av))) {
if (do_set == 0)
do_set = 1; /* delete set */
do_set = 1; /* delete set */
ac--; av++;
} else if (do_set != 0)
errx(EX_DATAERR, "missing 'set' keyword");
}
/* Rule number */
while (ac && isdigit(**av)) {
@ -1728,7 +1833,7 @@ delete(int ac, char *av[])
pipe.fs.fs_nr);
}
} else {
rulenum = (i & 0xffff) | (do_set << 16);
rulenum = (i & 0xffff) | (do_set << 24);
i = setsockopt(s, IPPROTO_IP, IP_FW_DEL, &rulenum,
sizeof rulenum);
if (i) {
@ -1804,6 +1909,10 @@ config_pipe(int ac, char **av)
ac--; av++;
switch(tok) {
case TOK_NOERROR:
pipe.fs.flags_fs |= DN_NOERROR;
break;
case TOK_PLR:
NEED1("plr needs argument 0..1\n");
d = strtod(av[0], NULL);
@ -2173,21 +2282,20 @@ add_mac(ipfw_insn *cmd, int ac, char *av[])
{
ipfw_insn_mac *mac; /* also *src */
if (ac <3)
errx(EX_DATAERR, "MAC dst src type");
if (ac <2)
errx(EX_DATAERR, "MAC dst src [type]");
cmd->opcode = O_MACADDR2;
cmd->len = (cmd->len & (F_NOT | F_OR)) | F_INSN_SIZE(ipfw_insn_mac);
mac = (ipfw_insn_mac *)cmd;
get_mac_addr_mask(av[0], mac->addr, mac->mask); /* dst */
get_mac_addr_mask(av[0], mac->addr, mac->mask); /* dst */
get_mac_addr_mask(av[1], &(mac->addr[6]), &(mac->mask[6])); /* src */
av += 2;
if (strcmp(av[0], "any") != 0) { /* we have a non-null port */
if (ac>2 && strcmp(av[2], "any") != 0) { /* we have a non-null type */
cmd += F_LEN(cmd);
fill_newports((ipfw_insn_u16 *)cmd, av[0], IPPROTO_ETHERTYPE);
fill_newports((ipfw_insn_u16 *)cmd, av[2], IPPROTO_ETHERTYPE);
cmd->opcode = O_MAC_TYPE;
}
@ -2467,10 +2575,9 @@ add(int ac, char *av[])
if (!strncmp(*av, "all", strlen(*av)))
; /* same as "ip" */
else if (!strncmp(*av, "MAC", strlen(*av))) {
/* need exactly 3 fields */
cmd = add_mac(cmd, ac-1, av+1); /* exits in case of errors */
ac -= 3;
cmd = add_mac(cmd, ac-1, av+1); /* exits in case of errors */
av += 3;
ac -= 3;
have_mac = 1;
} else if ((proto = atoi(*av)) > 0)
; /* all done! */
@ -2916,7 +3023,7 @@ add(int ac, char *av[])
}
static void
zero (int ac, char *av[])
zero(int ac, char *av[])
{
int rulenum;
int failed = EX_OK;
@ -2955,7 +3062,7 @@ zero (int ac, char *av[])
}
static void
resetlog (int ac, char *av[])
resetlog(int ac, char *av[])
{
int rulenum;
int failed = EX_OK;
@ -3105,9 +3212,7 @@ ipfw_main(int ac, char **av)
add(ac, av);
else if (do_pipe && !strncmp(*av, "config", strlen(*av)))
config_pipe(ac, av);
else if (!strncmp(*av, "delete", strlen(*av)) ||
!strncmp(*av, "disable", strlen(*av)) ||
!strncmp(*av, "enable", strlen(*av)))
else if (!strncmp(*av, "delete", strlen(*av)))
delete(ac, av);
else if (!strncmp(*av, "flush", strlen(*av)))
flush();
@ -3118,6 +3223,8 @@ ipfw_main(int ac, char **av)
else if (!strncmp(*av, "print", strlen(*av)) ||
!strncmp(*av, "list", strlen(*av)))
list(ac, av);
else if (!strncmp(*av, "set", strlen(*av)))
sets_handler(ac, av);
else if (!strncmp(*av, "show", strlen(*av))) {
do_acct++;
list(ac, av);

View File

@ -77,6 +77,14 @@
#include <machine/in_cksum.h> /* XXX for in_cksum */
/*
* XXX This one should go in sys/mbuf.h. It is used to avoid that
* a firewall-generated packet loops forever through the firewall.
*/
#ifndef M_SKIP_FIREWALL
#define M_SKIP_FIREWALL 0x4000
#endif
/*
* set_disable contains one bit per set value (0..31).
* If the bit is set, all rules with the corresponding set
@ -168,12 +176,22 @@ static u_int32_t dyn_rst_lifetime = 1;
static u_int32_t dyn_udp_lifetime = 10;
static u_int32_t dyn_short_lifetime = 5;
/*
* Keepalives are sent if dyn_keepalive is set. They are sent every
* dyn_keepalive_period seconds, in the last dyn_keepalive_interval
* seconds of lifetime of a rule.
* dyn_rst_lifetime and dyn_fin_lifetime should be strictly lower
* than dyn_keepalive_period.
*/
static u_int32_t dyn_keepalive_interval = 20;
static u_int32_t dyn_keepalive_period = 5;
static u_int32_t dyn_keepalive = 1; /* do send keepalives */
static u_int32_t static_count; /* # of static rules */
static u_int32_t static_len; /* size in bytes of static rules */
static u_int32_t dyn_count; /* # of dynamic rules */
static u_int32_t dyn_max = 1000; /* max # of dynamic rules */
static u_int32_t dyn_max = 4096; /* max # of dynamic rules */
SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, dyn_buckets, CTLFLAG_RW,
&dyn_buckets, 0, "Number of dyn. buckets");
@ -754,6 +772,7 @@ lookup_dyn_rule(struct ipfw_flow_id *pkt, int *match_direction,
case TH_SYN: /* opening */
q->expire = time_second + dyn_syn_lifetime;
break;
case BOTH_SYN: /* move to established */
case BOTH_SYN | TH_FIN : /* one side tries to close */
case BOTH_SYN | (TH_FIN << 8) :
@ -776,9 +795,13 @@ lookup_dyn_rule(struct ipfw_flow_id *pkt, int *match_direction,
}
q->expire = time_second + dyn_ack_lifetime;
break;
case BOTH_SYN | BOTH_FIN: /* both sides closed */
if (dyn_fin_lifetime >= dyn_keepalive_period)
dyn_fin_lifetime = dyn_keepalive_period - 1;
q->expire = time_second + dyn_fin_lifetime;
break;
default:
#if 0
/*
@ -788,6 +811,8 @@ lookup_dyn_rule(struct ipfw_flow_id *pkt, int *match_direction,
if ( (q->state & ((TH_RST << 8)|TH_RST)) == 0)
printf("invalid state: 0x%x\n", q->state);
#endif
if (dyn_rst_lifetime >= dyn_keepalive_period)
dyn_rst_lifetime = dyn_keepalive_period - 1;
q->expire = time_second + dyn_rst_lifetime;
break;
}
@ -806,8 +831,14 @@ lookup_dyn_rule(struct ipfw_flow_id *pkt, int *match_direction,
static void
realloc_dynamic_table(void)
{
/* try reallocation, make sure we have a power of 2 */
/*
* Try reallocation, make sure we have a power of 2 and do
* not allow more than 64k entries. In case of overflow,
* default to 1024.
*/
if (dyn_buckets > 65536)
dyn_buckets = 1024;
if ((dyn_buckets & (dyn_buckets-1)) != 0) { /* not a power of 2 */
dyn_buckets = curr_dyn_buckets; /* reset */
return;
@ -815,8 +846,13 @@ realloc_dynamic_table(void)
curr_dyn_buckets = dyn_buckets;
if (ipfw_dyn_v != NULL)
free(ipfw_dyn_v, M_IPFW);
ipfw_dyn_v = malloc(curr_dyn_buckets * sizeof(ipfw_dyn_rule *),
for (;;) {
ipfw_dyn_v = malloc(curr_dyn_buckets * sizeof(ipfw_dyn_rule *),
M_IPFW, M_DONTWAIT | M_ZERO);
if (ipfw_dyn_v != NULL || curr_dyn_buckets <= 2)
break;
curr_dyn_buckets /= 2;
}
}
/**
@ -1084,6 +1120,7 @@ send_pkt(struct ipfw_flow_id *id, u_int32_t seq, u_int32_t ack, int flags)
ip->ip_len = m->m_pkthdr.len;
bzero (&sro, sizeof (sro));
ip_rtaddr(ip->ip_dst, &sro);
m->m_flags |= M_SKIP_FIREWALL;
ip_output(m, NULL, &sro, 0, NULL);
if (sro.ro_rt)
RTFREE(sro.ro_rt);
@ -1191,8 +1228,7 @@ ipfw_chk(struct ip_fw_args *args)
* are documented here. Should you change them, please check
* the implementation of the various instructions to make sure
* that they still work.
*/
/*
*
* args->eh The MAC header. It is non-null for a layer2
* packet, it is NULL for a layer-3 packet.
*
@ -1254,6 +1290,8 @@ ipfw_chk(struct ip_fw_args *args)
int dyn_dir = MATCH_UNKNOWN;
ipfw_dyn_rule *q = NULL;
if (m->m_flags & M_SKIP_FIREWALL)
return 0; /* accept */
/*
* dyn_dir = MATCH_UNKNOWN when rules unchecked,
* MATCH_NONE when checked and not matched (q = NULL),
@ -2057,39 +2095,41 @@ free_chain(struct ip_fw **chain, int kill_default)
/**
* Remove all rules with given number, and also do set manipulation.
*
* The argument is an int. The low 16 bit are the
* rule or set number, the upper 16 bits are the
* function, namely:
* The argument is an u_int32_t. The low 16 bit are the rule or set number,
* the next 8 bits are the new set, the top 8 bits are the command:
*
* 0 DEL_SINGLE_RULE
* 1 DELETE_RULESET
* 2 DISABLE_SET
* 3 ENABLE_SET
* 0 delete rules with given number
* 1 delete rules with given set number
* 2 move rules with given number to new set
* 3 move rules with given set number to new set
* 4 swap sets with given numbers
*/
static int
del_entry(struct ip_fw **chain, u_int32_t arg)
{
struct ip_fw *prev, *rule;
int s;
u_int16_t rulenum, cmd;
u_int16_t rulenum;
u_int8_t cmd, new_set;
rulenum = arg & 0xffff;
cmd = (arg >> 16) & 0xffff;
cmd = (arg >> 24) & 0xff;
new_set = (arg >> 16) & 0xff;
if (cmd > 3)
if (cmd > 4)
return EINVAL;
if (cmd == 0 && rulenum == IPFW_DEFAULT_RULE)
return EINVAL;
if (cmd != 0 && rulenum > 30) {
printf("ipfw: del_entry: invalid set number %d\n",
rulenum);
if (new_set > 30)
return EINVAL;
if (cmd == 0 || cmd == 2) {
if (rulenum == IPFW_DEFAULT_RULE)
return EINVAL;
} else {
if (rulenum > 30)
return EINVAL;
}
switch (cmd) {
case 0: /* DEL_SINGLE_RULE */
case 0: /* delete rules with given number */
/*
* locate first rule to delete
*/
@ -2101,18 +2141,19 @@ del_entry(struct ip_fw **chain, u_int32_t arg)
return EINVAL;
s = splimp(); /* no access to rules while removing */
flush_rule_ptrs(); /* more efficient to do outside the loop */
/*
* prev remains the same throughout the cycle
* flush pointers outside the loop, then delete all matching
* rules. prev remains the same throughout the cycle.
*/
flush_rule_ptrs();
while (rule && rule->rulenum == rulenum)
rule = delete_rule(chain, prev, rule);
splx(s);
break;
case 1: /* DELETE_RULESET */
s = splimp(); /* no access to rules while removing */
flush_rule_ptrs(); /* more efficient to do outside the loop */
case 1: /* delete all rules with given set number */
s = splimp();
flush_rule_ptrs();
for (prev = NULL, rule = *chain; rule ; )
if (rule->set == rulenum)
rule = delete_rule(chain, prev, rule);
@ -2123,15 +2164,29 @@ del_entry(struct ip_fw **chain, u_int32_t arg)
splx(s);
break;
case 2: /* DISABLE SET */
case 2: /* move rules with given number to new set */
s = splimp();
set_disable |= 1 << rulenum;
for (rule = *chain; rule ; rule = rule->next)
if (rule->rulenum == rulenum)
rule->set = new_set;
splx(s);
break;
case 3: /* ENABLE SET */
case 3: /* move rules with given set number to new set */
s = splimp();
set_disable &= ~(1 << rulenum);
for (rule = *chain; rule ; rule = rule->next)
if (rule->set == rulenum)
rule->set = new_set;
splx(s);
break;
case 4: /* swap two sets */
s = splimp();
for (rule = *chain; rule ; rule = rule->next)
if (rule->set == rulenum)
rule->set = new_set;
else if (rule->set == new_set)
rule->set = rulenum;
splx(s);
break;
}
@ -2515,23 +2570,32 @@ ipfw_ctl(struct sockopt *sopt)
error = sooptcopyout(sopt, rule, size);
break;
case IP_FW_DEL: /* argument is an int, the rule number */
case IP_FW_DEL:
/*
* IP_FW_DEL is used for deleting single rules,
* set of rules, and manipulating set_disable.
*
* Everything is managed in del_entry();
* IP_FW_DEL is used for deleting single rules or sets,
* and (ab)used to atomically manipulate sets. Argument size
* is used to distinguish between the two:
* sizeof(u_int32_t)
* delete single rule or set of rules,
* or reassign rules (or sets) to a different set.
* 2*sizeof(u_int32_t)
* atomic disable/enable sets.
* first u_int32_t contains sets to be disabled,
* second u_int32_t contains sets to be enabled.
*/
error = sooptcopyin(sopt, &rulenum, sizeof(int), sizeof(int));
error = sooptcopyin(sopt, rule_buf,
2*sizeof(u_int32_t), sizeof(u_int32_t));
if (error)
break;
if (rulenum == IPFW_DEFAULT_RULE) {
if (fw_debug)
printf("ipfw: can't delete rule %u\n",
(unsigned)IPFW_DEFAULT_RULE);
size = sopt->sopt_valsize;
if (size == sizeof(u_int32_t)) /* delete or reassign */
error = del_entry(&layer3_chain, rule_buf[0]);
else if (size == 2*sizeof(u_int32_t)) /* set enable/disable */
set_disable =
(set_disable | rule_buf[0]) & ~rule_buf[1] &
~(1<<31); /* set 31 always enabled */
else
error = EINVAL;
} else
error = del_entry(&layer3_chain, rulenum);
break;
case IP_FW_ZERO:
@ -2564,6 +2628,10 @@ ipfw_ctl(struct sockopt *sopt)
*/
struct ip_fw *ip_fw_default_rule;
/*
* This procedure is only used to handle keepalives. It is invoked
* every dyn_keepalive_period
*/
static void
ipfw_tick(void * __unused unused)
{
@ -2583,7 +2651,8 @@ ipfw_tick(void * __unused unused)
continue;
if ( (q->state & BOTH_SYN) != BOTH_SYN)
continue;
if (TIME_LEQ( time_second+20, q->expire))
if (TIME_LEQ( time_second+dyn_keepalive_interval,
q->expire))
continue; /* too early */
if (TIME_LEQ(q->expire, time_second))
continue; /* too late, rule expired */
@ -2594,7 +2663,7 @@ ipfw_tick(void * __unused unused)
}
splx(s);
done:
ipfw_timeout_h = timeout(ipfw_tick, NULL, 5*hz);
ipfw_timeout_h = timeout(ipfw_tick, NULL, dyn_keepalive_period*hz);
}
static void