aboutsummaryrefslogtreecommitdiff
path: root/sbin
diff options
context:
space:
mode:
authorLuigi Rizzo <luigi@FreeBSD.org>2000-02-10 14:25:26 +0000
committerLuigi Rizzo <luigi@FreeBSD.org>2000-02-10 14:25:26 +0000
commitd69f84c0b4fb2437fed0e70be2a0b2c681927f24 (patch)
tree05a5d039b150f4d105b2942558dded495cd5f77f /sbin
parent6bc748b05740261a679b672dd9ef1199abfd6e88 (diff)
downloadsrc-d69f84c0b4fb2437fed0e70be2a0b2c681927f24.tar.gz
src-d69f84c0b4fb2437fed0e70be2a0b2c681927f24.zip
Support and document new stateful ipfw features.
Approved-by: jordan
Notes
Notes: svn path=/head/; revision=57115
Diffstat (limited to 'sbin')
-rw-r--r--sbin/ipfw/ipfw.8161
-rw-r--r--sbin/ipfw/ipfw.c144
2 files changed, 290 insertions, 15 deletions
diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8
index 7057f3958dbe..3994cab3b58d 100644
--- a/sbin/ipfw/ipfw.8
+++ b/sbin/ipfw/ipfw.8
@@ -30,6 +30,7 @@ flush
{zero|resetlog|delete}
.Op Ar number ...
.Nm ipfw
+.Op Fl s Op field
.Op Fl aftN
{list|show}
.Op Ar number ...
@@ -83,7 +84,32 @@ or
.Ar allow
depending on how the kernel is configured.
.Pp
-All rules have a few associated counters: a packet count and
+If the ruleset includes one or more rules with the
+.Ar keep-state
+option, then
+.Nm ipfw
+assumes a
+.Nm stateful
+behaviour, i.e. upon a match
+will create dynamic rules matching the exact parameters
+(addresses and ports) of the matching packet.
+.Pp
+These dynamic rules, which have a limited lifetime,
+are checked at the first occurrence of a
+.Ar check-state
+or
+.Ar keep-state
+rule, and are typically
+used to open the firewall on-demand to legitimate traffic
+only. See the
+.Xr options
+and
+.Xr EXAMPLES
+sections for more information on the stateful behaviour of
+.Nm ipfw
+.Pp
+All rules (including dynamic ones)
+have a few associated counters: a packet count and
a byte count, a log count, and a timestamp indicating the time
of the last match. Counters can be visualized or reset with
.Nm
@@ -140,6 +166,9 @@ not processed. Access to the console is required to recover.
While listing, show last match timestamp.
.It Fl N
Try to resolve addresses and service names in output.
+.It Fl s Op field
+While listing pipes, sort according one of the four
+counters (total and current packets or bytes).
.El
.Pp
To ease configuration, rules can be put into a file which is processed
@@ -281,6 +310,15 @@ The search terminates.
.It Ar count
Update counters for all packets that match rule.
The search continues with the next rule.
+.It Ar check-state
+Checks the packet against the dynamic ruleset. If a match is
+found then the search terminates, otherwise we move to the
+next rule.
+If no
+.Ar check-state
+rule is found, the dynamic ruleset is checked at the first
+.Ar keep-state
+rule.
.It Ar divert port
Divert packets that match this rule to the
.Xr divert 4
@@ -508,6 +546,30 @@ the local host have no transmit interface.
.Pp
.Ar options :
.Bl -hang -offset flag -width 1234567890123456
+.It keep-state Op method
+Upon a match, the firewall will create a dynamic rule,
+whose default behaviour is to
+matching bidirectional traffic between source and destination
+IP/port using the same protocol. The rule has a limited lifetime
+(controlled by a set of
+.Nm sysctl
+variables), and the lifetime is refreshed every time a matching packet
+is found.
+.Pp
+The actual behaviour can be modified by specifying a different
+.Op method ,
+although at the moment only the default one is specified.
+.It bridged
+Matches only bridged packets. This can be useful for multicast
+or broadcast traffic, which would otherwise pass through the
+firewall twice: once during bridging, and a second time
+when the packet is delivered to the local stack.
+.Pp
+Apart from a small performance penalty, this would be a problem
+when using
+.Ar pipes
+because the same packet would be accounted for twice
+in terms of bandwidth, queue occupation, and also counters.
.It frag
Match if the packet is a fragment and this is not the first fragment
of the datagram.
@@ -724,6 +786,51 @@ to that port; see
.Xr divert 4 .
If no socket is bound to the destination port, or if the kernel
wasn't compiled with divert socket support, the packets are dropped.
+.Pp
+.Sh SYSCTL VARIABLES
+A set of
+.Nm sysctl
+variables controls the behaviour of the firewall. These are shown
+below together with their default value and meaning
+.Bl -tag -offset flag -width 1234567890
+.It "net.inet.ip.fw.debug: 1"
+Controls debugging messages produced by ipfw.
+.It "net.inet.ip.fw.one_pass: 1"
+When set, permits only one pass through the firewall.
+Otherwise, after a
+pipe or divert
+action, the packet is reinjected in the firewall starting from
+the next rule.
+.It net.inet.ip.fw.verbose: 1
+Enables verbose messages.
+.It net.inet.ip.fw.enable: 1
+Enables the firewall. Setting this variable to 0 lets you run your
+machine without firewall even if compiled in.
+.It net.inet.ip.fw.verbose_limit: 0
+Limits the number of messages produced by a verbose firewall.
+.It net.inet.ip.fw.dyn_buckets: 256
+.It net.inet.ip.fw.curr_dyn_buckets: 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
+.Ar flush
+and reload the ruleset.
+.It net.inet.ip.fw.dyn_count: 3
+(readonly) current number of dynamic rules.
+.It net.inet.ip.fw.dyn_max: 1000
+Maximum number of dynamic rules. When you hit this limit,
+no more dynamic rules can be installed until old ones expire.
+.It net.inet.ip.fw.dyn_ack_lifetime: 300
+.It net.inet.ip.fw.dyn_syn_lifetime: 20
+.It net.inet.ip.fw.dyn_fin_lifetime: 20
+.It net.inet.ip.fw.dyn_rst_lifetime: 5
+.It net.inet.ip.fw.dyn_short_lifetime: 30
+These variable 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
+.El
.Sh EXAMPLES
This command adds an entry which denies all tcp packets from
.Em cracker.evil.org
@@ -738,6 +845,54 @@ my host:
.Pp
.Dl ipfw add deny all from 123.45.67.0/24 to my.host.org
.Pp
+A first and efficient way to limit access (not using dynamic rules)
+is the use of the following rules
+.Pp
+.Dl "ipfw add allow tcp from any to any established"
+.Dl "ipfw add allow tcp from net1 portlist1 to net2 portlist2 setup"
+.Dl "ipfw add allow tcp from net3 portlist3 to net3 portlist3 setup"
+.Dl "..."
+.Dl "ipfw add deny tcp from any to any"
+.Pp
+The first rule will be a quick match for normal TCP packets, but
+it will not match the initial SYN packet, which will be
+matched by the
+.Ar setup
+rules only for selected source/destination pairs.
+All other SYN packets will be rejected by the final
+.Ar deny
+rule.
+.Pp
+In order to protect a site from flood attacks involving fake
+TCP packets, it is safer to use dynamic rules:
+.Pp
+.Dl "ipfw add check-state"
+.Dl "ipfw add deny tcp from any to any established"
+.Dl "ipfw add allow tcp from my-net to any setup keep-state"
+.Pp
+This will let the firewall install dynamic rules only for
+those connection which start with a regular SYN
+packet coming from the inside of our network. Dynamic rules
+are checked when encountering the first
+.Ar check-state
+or
+.Ar keep-state
+rule. A
+.Ar check-state
+rule should be usually placed near the beginning of the ruleset
+to minimize the amount of work scanning the ruleset. Your mileage
+may vary.
+.Pp
+BEWARE:
+stateful rules can be subject to denial-of-service attacks
+by a SYN-flood which opens a huge number of dynamic rules.
+The effects of such attacks can be partially limited by acting on
+a set of
+.Nm sysctl
+variables which control the operation of the firewall.
+.Pp
+There is a number of sysctl variables which controls the
+.Pp
Here is a good usage of the
.Ar list
command to see accounting records
@@ -884,4 +1039,6 @@ first appeared in
.Fx 2.0 .
.Nm dummynet
was introduced in
-.Fx 2.2.8
+.Fx 2.2.8 .
+Stateful extensions were introduced in
+.Fx 4.0-RELEASE
diff --git a/sbin/ipfw/ipfw.c b/sbin/ipfw/ipfw.c
index 04d02ffb03f1..7cea1a2e9498 100644
--- a/sbin/ipfw/ipfw.c
+++ b/sbin/ipfw/ipfw.c
@@ -68,6 +68,7 @@ int do_time=0; /* Show time stamps */
int do_quiet=0; /* Be quiet in add and flush */
int do_force=0; /* Don't ask for confirmation */
int do_pipe=0; /* this cmd refers to a pipe */
+int do_sort=0; /* field to sort results (0=no) */
struct icmpcode {
int code;
@@ -211,6 +212,10 @@ show_ipfw(struct ip_fw *chain, int pcwidth, int bcwidth)
else
printf(" ");
}
+ if (chain->fw_flg == IP_FW_F_CHECK_S) {
+ printf("check-state\n");
+ goto done ;
+ }
switch (chain->fw_flg & IP_FW_F_COMMAND)
{
@@ -371,7 +376,15 @@ show_ipfw(struct ip_fw *chain, int pcwidth, int bcwidth)
printf(" gid %u", chain->fw_gid);
}
- /* Direction */
+ if (chain->fw_flg & IP_FW_F_KEEP_S) {
+ if (chain->next_rule_ptr)
+ printf(" keep-state %d", (int)chain->next_rule_ptr);
+ else
+ printf(" keep-state");
+ }
+ /* Direction */
+ if (chain->fw_flg & IP_FW_BRIDGED)
+ printf(" bridged");
if ((chain->fw_flg & IP_FW_F_IN) && !(chain->fw_flg & IP_FW_F_OUT))
printf(" in");
if (!(chain->fw_flg & IP_FW_F_IN) && (chain->fw_flg & IP_FW_F_OUT))
@@ -449,10 +462,41 @@ show_ipfw(struct ip_fw *chain, int pcwidth, int bcwidth)
}
}
printf("\n");
+done:
if (do_resolv)
endservent();
}
+int
+sort_q(const void *pa, const void *pb)
+{
+ int rev = (do_sort < 0) ;
+ int field = rev ? -do_sort : do_sort ;
+ long long res=0 ;
+ const struct dn_flow_queue *a = pa ;
+ const struct dn_flow_queue *b = pb ;
+
+ switch (field) {
+ case 1: /* pkts */
+ res = a->len - b->len ;
+ break ;
+ case 2 : /* bytes */
+ res = a->len_bytes - b->len_bytes ;
+ break ;
+
+ case 3 : /* tot pkts */
+ res = a->tot_pkts - b->tot_pkts ;
+ break ;
+
+ case 4 : /* tot bytes */
+ res = a->tot_bytes - b->tot_bytes ;
+ break ;
+ }
+ if (res < 0) res = -1 ;
+ if (res > 0) res = 1 ;
+ return (int)(rev ? res : -res) ;
+}
+
static void
list(ac, av)
int ac;
@@ -537,33 +581,41 @@ list(ac, av)
p->flow_mask.proto,
p->flow_mask.src_ip, p->flow_mask.src_port,
p->flow_mask.dst_ip, p->flow_mask.src_port);
+ printf("BKT Prot ___Source IP/port____ "
+ "____Dest. IP/port____ Tot_pkt/bytes Pkt/Byte Drop\n");
+ if (do_sort != 0)
+ heapsort(q, p->rq_elements, sizeof( *q), sort_q);
for (l = 0 ; l < p->rq_elements ; l++) {
struct in_addr ina ;
struct protoent *pe ;
ina.s_addr = htonl(q[l].id.src_ip) ;
- printf(" (%d) ", q[l].hash_slot);
+ printf("%3d ", q[l].hash_slot);
pe = getprotobynumber(q[l].id.proto);
if (pe)
- printf(" %s", pe->p_name);
+ printf("%-4s ", pe->p_name);
else
- printf(" %u", q[l].id.proto);
- printf(" %s/%d -> ",
+ printf("%4u ", q[l].id.proto);
+ printf("%15s/%-5d ",
inet_ntoa(ina), q[l].id.src_port);
ina.s_addr = htonl(q[l].id.dst_ip) ;
- printf("%s/%d\n",
+ printf("%15s/%-5d ",
inet_ntoa(ina), q[l].id.dst_port);
- printf("\t%u pkts %u bytes, tot %qu pkts %qu bytes %u drops\n",
- q[l].len, q[l].len_bytes,
- q[l].tot_pkts, q[l].tot_bytes, q[l].drops);
+ printf("%4qu %8qu %2u %4u %3u\n",
+ q[l].tot_pkts, q[l].tot_bytes,
+ q[l].len, q[l].len_bytes, q[l].drops);
}
}
free(data);
return;
}
-
- /* if showing stats, figure out column widths ahead of time */
rules = (struct ip_fw *) data;
+ /* determine num more accurately */
+ num = 0;
+ while (rules[num].fw_number < 65535)
+ num++ ;
+ num++ ; /* counting starts from 0 ... */
+ /* if showing stats, figure out column widths ahead of time */
if (do_acct) {
for (n = 0; n < num; n++) {
struct ip_fw *const r = &rules[n];
@@ -624,6 +676,45 @@ list(ac, av)
if (exitval != EX_OK)
exit(exitval);
}
+ /*
+ * show dynamic rules
+ */
+ if (num * sizeof (rules[0]) != nbytes ) {
+ struct ipfw_dyn_rule *d =
+ (struct ipfw_dyn_rule *)&rules[num] ;
+ struct in_addr a ;
+ struct protoent *pe;
+
+ printf("## Dynamic rules:\n");
+ for (;; d++) {
+ printf("%05d %qu %qu (T %d, # %d) ty %d",
+ (int)(d->chain),
+ d->pcnt, d->bcnt,
+ d->expire,
+ d->bucket,
+ d->type);
+ pe = getprotobynumber(d->id.proto);
+ if (pe)
+ printf(" %s,", pe->p_name);
+ else
+ printf(" %u,", d->id.proto);
+ a.s_addr = htonl(d->id.src_ip);
+ printf(" %s", inet_ntoa(a));
+ printf(" %d", d->id.src_port);
+ switch (d->type) {
+ default: /* bidir, no mask */
+ printf(" <->");
+ break ;
+ }
+ a.s_addr = htonl(d->id.dst_ip);
+ printf(" %s", inet_ntoa(a));
+ printf(" %d", d->id.dst_port);
+ printf("\n");
+ if (d->next == NULL)
+ break ;
+ }
+ }
+
free(data);
}
@@ -1303,6 +1394,9 @@ add(ac,av)
} else if (!strncmp(*av,"unreach",strlen(*av))) {
rule.fw_flg |= IP_FW_F_REJECT; av++; ac--;
fill_reject_code(&rule.fw_reject_code, *av); av++; ac--;
+ } else if (!strncmp(*av,"check-state",strlen(*av))) {
+ rule.fw_flg |= IP_FW_F_CHECK_S ; av++; ac--;
+ goto done ;
} else {
show_usage("invalid action ``%s''", *av);
}
@@ -1450,6 +1544,21 @@ add(ac,av)
rule.fw_flg |= IP_FW_F_IN;
av++; ac--; continue;
}
+ if (!strncmp(*av,"keep-state",strlen(*av))) {
+ u_long type ;
+ rule.fw_flg |= IP_FW_F_KEEP_S;
+
+ av++; ac--;
+ if (ac > 0 && (type = atoi(*av)) != 0) {
+ (int)rule.next_rule_ptr = type ;
+ av++; ac--;
+ }
+ continue;
+ }
+ if (!strncmp(*av,"bridged",strlen(*av))) {
+ rule.fw_flg |= IP_FW_BRIDGED;
+ av++; ac--; continue;
+ }
if (!strncmp(*av,"out",strlen(*av))) {
rule.fw_flg |= IP_FW_F_OUT;
av++; ac--; continue;
@@ -1579,7 +1688,7 @@ badviacombo:
}
rule.fw_loghighest = rule.fw_logamount;
}
-
+done:
if (!do_quiet)
show_ipfw(&rule, 10, 10);
i = setsockopt(s, IPPROTO_IP, IP_FW_ADD, &rule, sizeof rule);
@@ -1682,8 +1791,11 @@ ipfw_main(ac,av)
do_force = !isatty(STDIN_FILENO);
optind = optreset = 1;
- while ((ch = getopt(ac, av, "afqtN")) != -1)
+ while ((ch = getopt(ac, av, "s:afqtN")) != -1)
switch(ch) {
+ case 's': /* sort */
+ do_sort= atoi(optarg);
+ break;
case 'a':
do_acct=1;
break;
@@ -1795,6 +1907,12 @@ main(ac, av)
setbuf(stdout,0);
+ /*
+ * this is a nasty check on the last argument!!!
+ * If there happens to be a filename matching a keyword in the current
+ * directory, things will fail miserably.
+ */
+
if (ac > 1 && access(av[ac - 1], R_OK) == 0) {
qflag = pflag = i = 0;
lineno = 0;