summaryrefslogtreecommitdiffstats
path: root/sbin/ipfw
diff options
context:
space:
mode:
authorluigi <luigi@FreeBSD.org>2001-09-27 23:44:27 +0000
committerluigi <luigi@FreeBSD.org>2001-09-27 23:44:27 +0000
commit0fb106cc3f40524759012ac12baf28dccec9e571 (patch)
treea41a22ccd419ef5d2a0238988e2c3fae83a51ade /sbin/ipfw
parent2854bb2840809c802db31285bc55e9fc5e73ac20 (diff)
downloadFreeBSD-src-0fb106cc3f40524759012ac12baf28dccec9e571.zip
FreeBSD-src-0fb106cc3f40524759012ac12baf28dccec9e571.tar.gz
Two main changes here:
+ implement "limit" rules, which permit to limit the number of sessions between certain host pairs (according to masks). These are a special type of stateful rules, which might be of interest in some cases. See the ipfw manpage for details. + merge the list pointers and ipfw rule descriptors in the kernel, so the code is smaller, faster and more readable. This patch basically consists in replacing "foo->rule->bar" with "rule->bar" all over the place. I have been willing to do this for ages! MFC after: 1 week
Diffstat (limited to 'sbin/ipfw')
-rw-r--r--sbin/ipfw/ipfw.858
-rw-r--r--sbin/ipfw/ipfw.c80
2 files changed, 102 insertions, 36 deletions
diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8
index 16f2873..475a746 100644
--- a/sbin/ipfw/ipfw.8
+++ b/sbin/ipfw/ipfw.8
@@ -69,14 +69,20 @@ traffic shaper in
Each incoming or outgoing packet is passed through the
.Nm
rules.
-If host is acting as a gateway, packets forwarded by
-the gateway are processed by
+The number of times a packet is processed by
.Nm
-twice.
-In case a host is acting as a bridge, packets forwarded by
-the bridge are processed by
+varies -- basically,
.Nm
-once.
+is invoked every time the kernel functions
+.Em ip_input() , ip_output()
+and
+.Em bdg_forward()
+are invoked.
+This means that packets are processed once for connections having
+only one endpoint on the local host, twice for connections with
+both endpoints on the local host, or for packet routed by the host
+(acting as a gateway), and once for packets bridged by the host
+(acting as a bridge).
.Pp
A firewall configuration is made of a list of numbered rules,
which is scanned for each packet until a match is found and
@@ -90,8 +96,8 @@ way as to minimize the number of checks.
.Pp
A configuration always includes a
.Em DEFAULT
-rule (numbered 65535) which cannot be modified by the programmer
-and always matches packets.
+rule (numbered 65535) which cannot be modified,
+and matches all packets.
The action associated with the default rule can be either
.Cm deny
or
@@ -100,11 +106,13 @@ depending on how the kernel is configured.
.Pp
If the ruleset includes one or more rules with the
.Cm keep-state
+or
+.Cm limit
option, then
.Nm
assumes a
.Em stateful
-behaviour, i.e. upon a match will create dynamic rules matching
+behaviour, i.e. upon a match it 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
@@ -632,7 +640,7 @@ while packets destined for the local host have no transmit
interface.
.It Ar options :
.Bl -tag -width indent
-.It Cm keep-state Op Ar method
+.It Cm keep-state
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.
@@ -641,9 +649,11 @@ The rule has a limited lifetime (controlled by a set of
variables), and the lifetime is refreshed every time a matching
packet is found.
.Pp
-The actual behaviour can be modified by specifying a different
-.Ar method ,
-although at the moment only the default one is specified.
+.It Cm limit {src-addr src-port dst-addr dst-port} N
+The firewall will only allow N connections with the same
+set of parameters as specified in the rule. One or more
+of source and destination addresses and ports can be
+specified.
.It Cm bridged
Matches only bridged packets.
This can be useful for multicast or broadcast traffic, which
@@ -1118,8 +1128,10 @@ dropped.
A set of
.Xr sysctl 8
variables controls the behaviour of the firewall.
-These are shown below together with their default value and
-meaning:
+These are shown below together with their default value
+(but always check with the
+.Nm sysctl
+command what value is actually in use) and meaning:
.Bl -tag -width indent
.It Em net.inet.ip.fw.debug : No 1
Controls debugging messages produced by
@@ -1156,8 +1168,9 @@ When you hit this limit, no more dynamic rules can be
installed until old ones expire.
.It Em net.inet.ip.fw.dyn_ack_lifetime : No 300
.It Em net.inet.ip.fw.dyn_syn_lifetime : No 20
-.It Em net.inet.ip.fw.dyn_fin_lifetime : No 20
-.It Em net.inet.ip.fw.dyn_rst_lifetime : No 5
+.It Em net.inet.ip.fw.dyn_fin_lifetime : No 1
+.It Em net.inet.ip.fw.dyn_rst_lifetime : No 1
+.It Em net.inet.ip.fw.dyn_udp_lifetime : No 5
.It Em net.inet.ip.fw.dyn_short_lifetime : No 30
These variables control the lifetime, in seconds, of dynamic
rules.
@@ -1218,6 +1231,17 @@ 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
+To limit the number of connections a user can open
+you can use the following type of rules:
+.Pp
+.Dl "ipfw add allow tcp from my-net/24 to any setup limit src-addr 10"
+.Dl "ipfw add allow tcp from any to me setup limit src-addr 4"
+.Pp
+The former (assuming it runs on a gateway) will allow each host
+on a /24 net to open at most 10 TCP connections.
+The latter can be placed on a server to make sure that a single
+client does not use more than 4 simultaneous connections.
+.Pp
.Em BEWARE :
stateful rules can be subject to denial-of-service attacks
by a SYN-flood which opens a huge number of dynamic rules.
diff --git a/sbin/ipfw/ipfw.c b/sbin/ipfw/ipfw.c
index ba5d36c..c7fa1d5 100644
--- a/sbin/ipfw/ipfw.c
+++ b/sbin/ipfw/ipfw.c
@@ -168,6 +168,18 @@ print_reject_code(int code)
printf("%u", code);
}
+/**
+ * _s_x holds a string-int pair for various lookups.
+ * s=NULL terminates the struct.
+ */
+struct _s_x { char *s; int x; };
+static struct _s_x limit_masks[] = {
+ {"src-addr", DYN_SRC_ADDR},
+ {"src-port", DYN_SRC_PORT},
+ {"dst-addr", DYN_DST_ADDR},
+ {"dst-port", DYN_DST_PORT},
+ {NULL, 0} };
+
static void
show_ipfw(struct ip_fw *chain)
{
@@ -204,7 +216,7 @@ show_ipfw(struct ip_fw *chain)
}
if (chain->fw_flg & IP_FW_F_RND_MATCH) {
- double d = 1.0 * (int)(chain->pipe_ptr);
+ double d = 1.0 * chain->dont_match_prob;
d = 1 - (d / 0x7fffffff);
printf("prob %f ", d);
}
@@ -373,16 +385,22 @@ show_ipfw(struct ip_fw *chain)
}
if (chain->fw_flg & IP_FW_F_KEEP_S) {
- u_long x = (u_long)chain->next_rule_ptr;
- u_char type = (x) & 0xff ;
+ struct _s_x *p = limit_masks;
- switch(type) {
+ switch(chain->dyn_type) {
default:
printf(" *** unknown type ***");
break ;
case DYN_KEEP_STATE:
printf(" keep-state");
break;
+ case DYN_LIMIT:
+ printf(" limit");
+ for ( ; p->s != NULL ; p++)
+ if (chain->limit_mask & p->x)
+ printf(" %s", p->s);
+ printf(" %d", chain->conn_limit);
+ break ;
}
}
/* Direction */
@@ -573,11 +591,17 @@ show_dyn_ipfw(struct ipfw_dyn_rule *d)
return;
printf("%05d %qu %qu (T %ds, slot %d)",
- (int)(d->chain),
+ (int)(d->rule),
d->pcnt, d->bcnt,
d->expire,
d->bucket);
switch (d->dyn_type) {
+ case DYN_LIMIT_PARENT:
+ printf(" PARENT %d", d->count);
+ break;
+ case DYN_LIMIT:
+ printf(" LIMIT");
+ break;
case DYN_KEEP_STATE: /* bidir, no mask */
printf(" <->");
break;
@@ -589,17 +613,10 @@ show_dyn_ipfw(struct ipfw_dyn_rule *d)
printf(" %u,", d->id.proto);
a.s_addr = htonl(d->id.src_ip);
- printf(" %s", inet_ntoa(a));
- printf(" %d", d->id.src_port);
+ printf(" %si %d", inet_ntoa(a), d->id.src_port);
- switch (d->dyn_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("<-> %s %d", inet_ntoa(a), d->id.dst_port);
printf("\n");
}
@@ -864,9 +881,9 @@ list(int ac, char *av[])
/* already warned */
continue;
for (n = 0, d = dynrules; n < ndyn; n++, d++) {
- if ((int)(d->chain) > rnum)
+ if ((int)(d->rule) > rnum)
break;
- if ((int)(d->chain) == rnum)
+ if ((int)(d->rule) == rnum)
show_dyn_ipfw(d);
}
}
@@ -1714,8 +1731,7 @@ add(int ac, char *av[])
errx(EX_DATAERR, "illegal match prob. %s", av[1]);
if (d != 1) { /* 1 means always match */
rule.fw_flg |= IP_FW_F_RND_MATCH;
- /* we really store dont_match probability */
- (long)rule.pipe_ptr = (long)((1 - d) * 0x7fffffff);
+ rule.dont_match_prob = (long)((1 - d) * 0x7fffffff);
}
av += 2; ac -= 2;
}
@@ -1976,13 +1992,39 @@ add(int ac, char *av[])
} else if (!strncmp(*av, "in", strlen(*av))) {
rule.fw_flg |= IP_FW_F_IN;
av++; ac--;
+ } else if (!strncmp(*av,"limit",strlen(*av))) {
+ /* keep-state rules used to limit number of connections. */
+ rule.fw_flg |= IP_FW_F_KEEP_S;
+ rule.dyn_type = DYN_LIMIT ;
+ rule.limit_mask = 0 ;
+ av++; ac--;
+ for (; ac >1 ;) {
+ struct _s_x *p = limit_masks;
+ int found = 0;
+ for ( ; p->s != NULL ; p++)
+ if (!strncmp(*av, p->s, strlen(*av))) {
+ rule.limit_mask |= p->x ;
+ av++; ac-- ;
+ }
+ if (found == 0) {
+ if (rule.limit_mask == 0)
+ errx(EX_USAGE, "missing limit mask");
+ break ;
+ }
+ }
+ if (ac < 1)
+ errx(EX_USAGE, "limit needs mask and # of connections");
+ rule.conn_limit = atoi(*av);
+ if (rule.conn_limit == 0)
+ errx(EX_USAGE, "limit: limit must be >0");
+ av++; ac--;
} else 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;
+ rule.dyn_type = type;
av++; ac--;
}
} else if (!strncmp(*av, "bridged", strlen(*av))) {
OpenPOWER on IntegriCloud