summaryrefslogtreecommitdiffstats
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
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
-rw-r--r--sbin/ipfw/ipfw.858
-rw-r--r--sbin/ipfw/ipfw.c80
-rw-r--r--sys/net/bridge.c4
-rw-r--r--sys/netinet/ip_dummynet.c40
-rw-r--r--sys/netinet/ip_dummynet.h4
-rw-r--r--sys/netinet/ip_fw.c406
-rw-r--r--sys/netinet/ip_fw.h127
-rw-r--r--sys/netinet/ip_input.c4
-rw-r--r--sys/netinet/ip_output.c4
9 files changed, 437 insertions, 290 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))) {
diff --git a/sys/net/bridge.c b/sys/net/bridge.c
index 6909277..f63a751 100644
--- a/sys/net/bridge.c
+++ b/sys/net/bridge.c
@@ -616,7 +616,7 @@ bdg_forward(struct mbuf *m0, struct ether_header *const eh, struct ifnet *dst)
int once = 0; /* loop only once */
struct ifnet *real_dst = dst ; /* real dst from ether_output */
#ifdef IPFIREWALL
- struct ip_fw_chain *rule = NULL ; /* did we match a firewall rule ? */
+ struct ip_fw *rule = NULL ; /* did we match a firewall rule ? */
#endif
/*
@@ -631,7 +631,7 @@ bdg_forward(struct mbuf *m0, struct ether_header *const eh, struct ifnet *dst)
#if defined(IPFIREWALL) && defined(DUMMYNET)
if (m0->m_type == MT_DUMMYNET) {
/* extract info from dummynet header */
- rule = (struct ip_fw_chain *)(m0->m_data) ;
+ rule = (struct ip_fw *)(m0->m_data) ;
m0 = m0->m_next ;
src = m0->m_pkthdr.rcvif;
shared = 0 ; /* For sure this is our own mbuf. */
diff --git a/sys/netinet/ip_dummynet.c b/sys/netinet/ip_dummynet.c
index 9e2da60..cdadd37 100644
--- a/sys/netinet/ip_dummynet.c
+++ b/sys/netinet/ip_dummynet.c
@@ -162,10 +162,10 @@ void dummynet_drain(void);
int if_tx_rdy(struct ifnet *ifp);
/*
- * ip_fw_chain is used when deleting a pipe, because ipfw rules can
+ * ip_fw_chain_head is used when deleting a pipe, because ipfw rules can
* hold references to the pipe.
*/
-extern LIST_HEAD (ip_fw_head, ip_fw_chain) ip_fw_chain_head;
+extern LIST_HEAD (ip_fw_head, ip_fw) ip_fw_chain_head;
static void
rt_unref(struct rtentry *rt)
@@ -1005,11 +1005,11 @@ red_drops(struct dn_flow_set *fs, struct dn_flow_queue *q, int len)
static __inline
struct dn_flow_set *
-locate_flowset(int pipe_nr, struct ip_fw_chain *rule)
+locate_flowset(int pipe_nr, struct ip_fw *rule)
{
struct dn_flow_set *fs = NULL ;
- if ( (rule->rule->fw_flg & IP_FW_F_COMMAND) == IP_FW_F_QUEUE )
+ if ( (rule->fw_flg & IP_FW_F_COMMAND) == IP_FW_F_QUEUE )
for (fs=all_flow_sets; fs && fs->fs_nr != pipe_nr; fs=fs->next)
;
else {
@@ -1020,7 +1020,7 @@ locate_flowset(int pipe_nr, struct ip_fw_chain *rule)
fs = &(p1->fs) ;
}
if (fs != NULL)
- rule->rule->pipe_ptr = fs ; /* record for the future */
+ rule->pipe_ptr = fs ; /* record for the future */
return fs ;
}
@@ -1032,7 +1032,7 @@ int
dummynet_io(int pipe_nr, int dir, /* pipe_nr can also be a fs_nr */
struct mbuf *m, struct ifnet *ifp, struct route *ro,
struct sockaddr_in *dst,
- struct ip_fw_chain *rule, int flags)
+ struct ip_fw *rule, int flags)
{
struct dn_pkt *pkt;
struct dn_flow_set *fs;
@@ -1045,7 +1045,7 @@ dummynet_io(int pipe_nr, int dir, /* pipe_nr can also be a fs_nr */
pipe_nr &= 0xffff ;
- if ( (fs = rule->rule->pipe_ptr) == NULL ) {
+ if ( (fs = rule->pipe_ptr) == NULL ) {
fs = locate_flowset(pipe_nr, rule);
if (fs == NULL)
goto dropit ; /* this queue/pipe does not exist! */
@@ -1090,7 +1090,7 @@ dummynet_io(int pipe_nr, int dir, /* pipe_nr can also be a fs_nr */
/* ok, i can handle the pkt now... */
/* build and enqueue packet + parameters */
pkt->hdr.mh_type = MT_DUMMYNET ;
- (struct ip_fw_chain *)pkt->hdr.mh_data = rule ;
+ (struct ip_fw *)pkt->hdr.mh_data = rule ;
DN_NEXT(pkt) = NULL;
pkt->dn_m = m;
pkt->dn_dir = dir ;
@@ -1126,7 +1126,7 @@ dummynet_io(int pipe_nr, int dir, /* pipe_nr can also be a fs_nr */
* to schedule it. This involves different actions for fixed-rate or
* WF2Q queues.
*/
- if ( (rule->rule->fw_flg & IP_FW_F_COMMAND) == IP_FW_F_PIPE ) {
+ if ( (rule->fw_flg & IP_FW_F_COMMAND) == IP_FW_F_PIPE ) {
/*
* Fixed-rate queue: just insert into the ready_heap.
*/
@@ -1276,15 +1276,15 @@ static void
dummynet_flush()
{
struct dn_pipe *curr_p, *p ;
- struct ip_fw_chain *chain ;
+ struct ip_fw *rule ;
struct dn_flow_set *fs, *curr_fs;
int s ;
s = splimp() ;
/* remove all references to pipes ...*/
- LIST_FOREACH(chain, &ip_fw_chain_head, next)
- chain->rule->pipe_ptr = NULL ;
+ LIST_FOREACH(rule, &ip_fw_chain_head, next)
+ rule->pipe_ptr = NULL ;
/* prevent future matches... */
p = all_pipes ;
all_pipes = NULL ;
@@ -1313,7 +1313,7 @@ dummynet_flush()
}
-extern struct ip_fw_chain *ip_fw_default_rule ;
+extern struct ip_fw *ip_fw_default_rule ;
static void
dn_rule_delete_fs(struct dn_flow_set *fs, void *r)
{
@@ -1635,7 +1635,7 @@ static int
delete_pipe(struct dn_pipe *p)
{
int s ;
- struct ip_fw_chain *chain ;
+ struct ip_fw *rule ;
if (p->pipe_nr == 0 && p->fs.fs_nr == 0)
return EINVAL ;
@@ -1659,9 +1659,9 @@ delete_pipe(struct dn_pipe *p)
else
a->next = b->next ;
/* remove references to this pipe from the ip_fw rules. */
- LIST_FOREACH(chain, &ip_fw_chain_head, next)
- if (chain->rule->pipe_ptr == &(b->fs))
- chain->rule->pipe_ptr = NULL ;
+ LIST_FOREACH(rule, &ip_fw_chain_head, next)
+ if (rule->pipe_ptr == &(b->fs))
+ rule->pipe_ptr = NULL ;
/* remove all references to this pipe from flow_sets */
for (fs = all_flow_sets; fs; fs= fs->next )
@@ -1693,9 +1693,9 @@ delete_pipe(struct dn_pipe *p)
else
a->next = b->next ;
/* remove references to this flow_set from the ip_fw rules. */
- LIST_FOREACH(chain, &ip_fw_chain_head, next)
- if (chain->rule->pipe_ptr == b)
- chain->rule->pipe_ptr = NULL ;
+ LIST_FOREACH(rule, &ip_fw_chain_head, next)
+ if (rule->pipe_ptr == b)
+ rule->pipe_ptr = NULL ;
if (b->pipe != NULL) {
/* Update total weight on parent pipe and cleanup parent heaps */
diff --git a/sys/netinet/ip_dummynet.h b/sys/netinet/ip_dummynet.h
index fe80718..279ae36 100644
--- a/sys/netinet/ip_dummynet.h
+++ b/sys/netinet/ip_dummynet.h
@@ -343,14 +343,14 @@ struct dn_pipe { /* a pipe */
MALLOC_DECLARE(M_IPFW);
-typedef int ip_dn_ctl_t __P((struct sockopt *)) ;
+typedef int ip_dn_ctl_t (struct sockopt *) ;
extern ip_dn_ctl_t *ip_dn_ctl_ptr;
void dn_rule_delete(void *r); /* used in ip_fw.c */
int dummynet_io(int pipe, int dir,
struct mbuf *m, struct ifnet *ifp, struct route *ro,
struct sockaddr_in * dst,
- struct ip_fw_chain *rule, int flags);
+ struct ip_fw *rule, int flags);
#endif
#endif /* _IP_DUMMYNET_H */
diff --git a/sys/netinet/ip_fw.c b/sys/netinet/ip_fw.c
index cb21e56..0ad3aae 100644
--- a/sys/netinet/ip_fw.c
+++ b/sys/netinet/ip_fw.c
@@ -2,7 +2,7 @@
* Copyright (c) 1993 Daniel Boulet
* Copyright (c) 1994 Ugen J.S.Antsilevich
* Copyright (c) 1996 Alex Nash
- * Copyright (c) 2000 Luigi Rizzo
+ * Copyright (c) 2000-2001 Luigi Rizzo
*
* Redistribution and use in source forms, with and without modification,
* are permitted provided that this entire comment appears intact.
@@ -92,7 +92,7 @@ struct ipfw_flow_id last_pkt ;
#define IPFW_DEFAULT_RULE ((u_int)(u_short)~0)
-LIST_HEAD (ip_fw_head, ip_fw_chain) ip_fw_chain_head;
+LIST_HEAD (ip_fw_head, ip_fw) ip_fw_chain_head;
MALLOC_DEFINE(M_IPFW, "IpFw/IpAcct", "IpFw/IpAcct chain's");
@@ -211,31 +211,30 @@ SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, dyn_grace_time, CTLFLAG_RD,
} while (0)
#define SNPARGS(buf, len) buf + len, sizeof(buf) > len ? sizeof(buf) - len : 0
-static int add_entry __P((struct ip_fw_head *chainptr, struct ip_fw *frwl));
-static int del_entry __P((struct ip_fw_head *chainptr, u_short number));
-static int zero_entry __P((struct ip_fw *, int));
-static int check_ipfw_struct __P((struct ip_fw *m));
+static int add_entry (struct ip_fw_head *chainptr, struct ip_fw *frwl);
+static int del_entry (struct ip_fw_head *chainptr, u_short number);
+static int zero_entry (struct ip_fw *, int);
+static int check_ipfw_struct (struct ip_fw *m);
+static int iface_match (struct ifnet *ifp, union ip_fw_if *ifu,
+ int byname);
+static int ipopts_match (struct ip *ip, struct ip_fw *f);
+static int iptos_match (struct ip *ip, struct ip_fw *f);
static __inline int
- iface_match __P((struct ifnet *ifp, union ip_fw_if *ifu,
- int byname));
-static int ipopts_match __P((struct ip *ip, struct ip_fw *f));
-static int iptos_match __P((struct ip *ip, struct ip_fw *f));
-static __inline int
- port_match __P((u_short *portptr, int nports, u_short port,
- int range_flag, int mask));
-static int tcpflg_match __P((struct tcphdr *tcp, struct ip_fw *f));
-static int icmptype_match __P((struct icmp * icmp, struct ip_fw * f));
-static void ipfw_report __P((struct ip_fw *f, struct ip *ip, int offset,
+ port_match (u_short *portptr, int nports, u_short port,
+ int range_flag, int mask);
+static int tcpflg_match (struct tcphdr *tcp, struct ip_fw *f);
+static int icmptype_match (struct icmp * icmp, struct ip_fw * f);
+static void ipfw_report (struct ip_fw *f, struct ip *ip, int offset,
int ip_len, struct ifnet *rif,
- struct ifnet *oif));
+ struct ifnet *oif);
static void flush_rule_ptrs(void);
-static int ip_fw_chk __P((struct ip **pip, int hlen,
+static int ip_fw_chk (struct ip **pip, int hlen,
struct ifnet *oif, u_int16_t *cookie, struct mbuf **m,
- struct ip_fw_chain **flow_id,
- struct sockaddr_in **next_hop));
-static int ip_fw_ctl __P((struct sockopt *sopt));
+ struct ip_fw **flow_id,
+ struct sockaddr_in **next_hop);
+static int ip_fw_ctl (struct sockopt *sopt);
static char err_prefix[] = "ip_fw_ctl:";
@@ -254,17 +253,14 @@ port_match(u_short *portptr, int nports, u_short port, int range_flag, int mask)
portptr += 2;
}
if (range_flag) {
- if (portptr[0] <= port && port <= portptr[1]) {
+ if (portptr[0] <= port && port <= portptr[1])
return 1;
- }
nports -= 2;
portptr += 2;
}
- while (nports-- > 0) {
- if (*portptr++ == port) {
+ while (nports-- > 0)
+ if (*portptr++ == port)
return 1;
- }
- }
return 0;
}
@@ -481,7 +477,7 @@ tcpopts_match(struct tcphdr *tcp, struct ip_fw *f)
return 0;
}
-static __inline int
+static int
iface_match(struct ifnet *ifp, union ip_fw_if *ifu, int byname)
{
/* Check by name or by IP address */
@@ -683,7 +679,10 @@ hash_packet(struct ipfw_flow_id *id)
#define UNLINK_DYN_RULE(prev, head, q) { \
struct ipfw_dyn_rule *old_q = q; \
\
- DEB(printf("-- unlink 0x%08x %d -> 0x%08x %d, %d left\n", \
+ /* remove a refcount to the parent */ \
+ if (q->dyn_type == DYN_LIMIT) \
+ q->parent->count--; \
+ DEB(printf("-- unlink entry 0x%08x %d -> 0x%08x %d, %d left\n", \
(q->id.src_ip), (q->id.src_port), \
(q->id.dst_ip), (q->id.dst_port), dyn_count-1 ); ) \
if (prev != NULL) \
@@ -696,15 +695,15 @@ hash_packet(struct ipfw_flow_id *id)
#define TIME_LEQ(a,b) ((int)((a)-(b)) <= 0)
/**
- * Remove all dynamic rules pointing to a given chain, or all
- * rules if chain == NULL. Second parameter is 1 if we want to
+ * Remove all dynamic rules pointing to a given rule, or all
+ * rules if rule == NULL. Second parameter is 1 if we want to
* delete unconditionally, otherwise only expired rules are removed.
*/
static void
-remove_dyn_rule(struct ip_fw_chain *chain, int force)
+remove_dyn_rule(struct ip_fw *rule, int force)
{
struct ipfw_dyn_rule *prev, *q;
- int i ;
+ int i, pass, max_pass ;
static u_int32_t last_remove = 0 ;
if (ipfw_dyn_v == NULL || dyn_count == 0)
@@ -714,18 +713,32 @@ remove_dyn_rule(struct ip_fw_chain *chain, int force)
return ;
last_remove = time_second ;
+ /*
+ * because DYN_LIMIT refer to parent rules, during the first pass only
+ * remove child and mark any pending LIMIT_PARENT, and remove
+ * them in a second pass.
+ */
+ for (pass = max_pass = 0; pass <= max_pass ; pass++ ) {
for (i = 0 ; i < curr_dyn_buckets ; i++) {
for (prev=NULL, q = ipfw_dyn_v[i] ; q ; ) {
/*
* logic can become complex here, so we split tests.
- * First, test if we match any chain,
+ * First, test if we match any rule,
* then make sure the rule is expired or we want to kill it,
* and possibly more in the future.
*/
- int zap = ( chain == NULL || chain == q->chain);
+ int zap = ( rule == NULL || rule == q->rule);
if (zap)
zap = force || TIME_LEQ( q->expire , time_second );
-
+ /* do not zap parent in first pass, record we need a second pass */
+ if (q->dyn_type == DYN_LIMIT_PARENT) {
+ max_pass = 1; /* we need a second pass */
+ if (zap == 1 && (pass == 0 || q->count != 0) ) {
+ zap = 0 ;
+ if (q->count != 0)
+ printf("cannot remove parent, count %d\n", q->count);
+ }
+ }
if (zap) {
UNLINK_DYN_RULE(prev, ipfw_dyn_v[i], q);
} else {
@@ -734,11 +747,12 @@ remove_dyn_rule(struct ip_fw_chain *chain, int force)
}
}
}
+ }
}
-#define EXPIRE_DYN_CHAIN(chain) remove_dyn_rule(chain, 0 /* expired ones */)
+#define EXPIRE_DYN_CHAIN(rule) remove_dyn_rule(rule, 0 /* expired ones */)
#define EXPIRE_DYN_CHAINS() remove_dyn_rule(NULL, 0 /* expired ones */)
-#define DELETE_DYN_CHAIN(chain) remove_dyn_rule(chain, 1 /* force removal */)
+#define DELETE_DYN_CHAIN(rule) remove_dyn_rule(rule, 1 /* force removal */)
#define DELETE_DYN_CHAINS() remove_dyn_rule(NULL, 1 /* force removal */)
/**
@@ -759,9 +773,11 @@ lookup_dyn_rule(struct ipfw_flow_id *pkt, int *match_direction)
return NULL ;
i = hash_packet( pkt );
for (prev=NULL, q = ipfw_dyn_v[i] ; q != NULL ; ) {
- if (TIME_LEQ( q->expire , time_second ) ) { /* expire entry */
- UNLINK_DYN_RULE(prev, ipfw_dyn_v[i], q);
- continue ;
+ if (q->dyn_type == DYN_LIMIT_PARENT)
+ goto next;
+ if (TIME_LEQ( q->expire , time_second ) ) { /* expire entry */
+ UNLINK_DYN_RULE(prev, ipfw_dyn_v[i], q);
+ continue ;
}
if ( pkt->proto == q->id.proto) {
if (pkt->src_ip == q->id.src_ip &&
@@ -779,6 +795,7 @@ lookup_dyn_rule(struct ipfw_flow_id *pkt, int *match_direction)
goto found ;
}
}
+next:
prev = q ;
q = q->next ;
}
@@ -834,13 +851,19 @@ found:
return q ;
}
-/*
- * Install state for a dynamic session.
+/**
+ * Install state of type 'type' for a dynamic session.
+ * The hash table contains two type of rules:
+ * - regular rules (DYN_KEEP_STATE)
+ * - rules for sessions with limited number of sess per user
+ * (DYN_LIMIT). When they are created, the parent is
+ * increased by 1, and decreased on delete. In this case,
+ * the third parameter is the parent rule and not the chain.
+ * - "parent" rules for the above (DYN_LIMIT_PARENT).
*/
static struct ipfw_dyn_rule *
-add_dyn_rule(struct ipfw_flow_id *id, u_int8_t dyn_type,
- struct ip_fw_chain *chain)
+add_dyn_rule(struct ipfw_flow_id *id, u_int8_t dyn_type, struct ip_fw *rule)
{
struct ipfw_dyn_rule *r ;
@@ -871,40 +894,79 @@ add_dyn_rule(struct ipfw_flow_id *id, u_int8_t dyn_type,
return NULL ;
}
+ /* increase refcount on parent, and set pointer */
+ if (dyn_type == DYN_LIMIT) {
+ struct ipfw_dyn_rule *parent = (struct ipfw_dyn_rule *)rule;
+ if ( parent->dyn_type != DYN_LIMIT_PARENT)
+ panic("invalid parent");
+ parent->count++ ;
+ r->parent = parent ;
+ rule = parent->rule;
+ }
+
r->id = *id ;
r->expire = time_second + dyn_syn_lifetime ;
- r->chain = chain ;
+ r->rule = rule ;
r->dyn_type = dyn_type ;
r->pcnt = r->bcnt = 0 ;
+ r->count = 0 ;
r->bucket = i ;
r->next = ipfw_dyn_v[i] ;
ipfw_dyn_v[i] = r ;
dyn_count++ ;
- DEB(printf("-- add entry 0x%08x %d -> 0x%08x %d, %d left\n",
+ DEB(printf("-- add entry 0x%08x %d -> 0x%08x %d, total %d\n",
(r->id.src_ip), (r->id.src_port),
(r->id.dst_ip), (r->id.dst_port),
dyn_count ); )
return r;
}
+/**
+ * lookup dynamic parent rule using pkt and chain as search keys.
+ * If the lookup fails, then install one.
+ */
+static struct ipfw_dyn_rule *
+lookup_dyn_parent(struct ipfw_flow_id *pkt, struct ip_fw *rule)
+{
+ struct ipfw_dyn_rule *q;
+ int i;
+
+ if (ipfw_dyn_v) {
+ i = hash_packet( pkt );
+ for (q = ipfw_dyn_v[i] ; q != NULL ; q=q->next)
+ if (q->dyn_type == DYN_LIMIT_PARENT && rule == q->rule &&
+ pkt->proto == q->id.proto &&
+ pkt->src_ip == q->id.src_ip &&
+ pkt->dst_ip == q->id.dst_ip &&
+ pkt->src_port == q->id.src_port &&
+ pkt->dst_port == q->id.dst_port) {
+ q->expire = time_second + dyn_short_lifetime ;
+ DEB(printf("lookup_dyn_parent found 0x%p\n", q);)
+ return q;
+ }
+ }
+ return add_dyn_rule(pkt, DYN_LIMIT_PARENT, rule);
+}
+
/*
* Install dynamic state.
* There are different types of dynamic rules which can be installed.
- * The type is in chain->dyn_type.
+ * The type is in rule->dyn_type.
* Type 0 (default) is a bidirectional rule
*
- * Returns 1 (failure) if state is not installed.
+ * Returns 1 (failure) if state is not installed because of errors or because
+ * session limitations are enforced.
*/
static int
-install_state(struct ip_fw_chain *chain)
+install_state(struct ip_fw *rule)
{
struct ipfw_dyn_rule *q ;
static int last_log ;
- u_int8_t type = ((struct ip_fw_ext *)chain->rule)->dyn_type ;
+ u_int8_t type = rule->dyn_type ;
- DEB(printf("-- install state type %d 0x%08lx %u -> 0x%08lx %u\n",
+ DEB(printf("-- install state type %d 0x%08x %u -> 0x%08x %u\n",
type,
(last_pkt.src_ip), (last_pkt.src_port),
(last_pkt.dst_ip), (last_pkt.dst_port) );)
@@ -927,34 +989,73 @@ install_state(struct ip_fw_chain *chain)
return 1; /* cannot install, notify caller */
}
switch (type) {
- default: /* bidir rule */
- add_dyn_rule(&last_pkt, DYN_KEEP_STATE, chain);
- break ;
+ case DYN_KEEP_STATE: /* bidir rule */
+ add_dyn_rule(&last_pkt, DYN_KEEP_STATE, rule);
+ break ;
+ case DYN_LIMIT: /* limit number of sessions */
+ {
+ u_int16_t limit_mask = rule->limit_mask ;
+ u_int16_t conn_limit = rule->conn_limit ;
+ struct ipfw_flow_id id;
+ struct ipfw_dyn_rule *parent;
+
+ DEB(printf("installing dyn-limit rule %d\n", conn_limit);)
+
+ id.dst_ip = id.src_ip = 0;
+ id.dst_port = id.src_port = 0 ;
+ id.proto = last_pkt.proto ;
+
+ if (limit_mask & DYN_SRC_ADDR)
+ id.src_ip = last_pkt.src_ip;
+ if (limit_mask & DYN_DST_ADDR)
+ id.dst_ip = last_pkt.dst_ip;
+ if (limit_mask & DYN_SRC_PORT)
+ id.src_port = last_pkt.src_port;
+ if (limit_mask & DYN_DST_PORT)
+ id.dst_port = last_pkt.dst_port;
+ parent = lookup_dyn_parent(&id, rule);
+ if (parent == NULL) {
+ printf("add parent failed\n");
+ return 1;
+ }
+ if (parent->count >= conn_limit) {
+ EXPIRE_DYN_CHAIN(rule); /* try to expire some */
+ if (parent->count >= conn_limit) {
+ printf("drop session, too many entries\n");
+ return 1;
+ }
+ }
+ add_dyn_rule(&last_pkt, DYN_LIMIT, (struct ip_fw *)parent);
+ }
+ break ;
+ default:
+ printf("unknown dynamic rule type %u\n", type);
+ return 1 ;
}
lookup_dyn_rule(&last_pkt, NULL) ; /* XXX just set the lifetime */
return 0;
}
/*
- * given an ip_fw_chain *, lookup_next_rule will return a pointer
+ * given an ip_fw *, lookup_next_rule will return a pointer
* of the same type to the next one. This can be either the jump
* target (for skipto instructions) or the next one in the chain (in
* all other cases including a missing jump target).
* Backward jumps are not allowed, so start looking from the next
* rule...
*/
-static struct ip_fw_chain * lookup_next_rule(struct ip_fw_chain *me);
+static struct ip_fw * lookup_next_rule(struct ip_fw *me);
-static struct ip_fw_chain *
-lookup_next_rule(struct ip_fw_chain *me)
+static struct ip_fw *
+lookup_next_rule(struct ip_fw *me)
{
- struct ip_fw_chain *chain ;
- int rule = me->rule->fw_skipto_rule ; /* guess... */
+ struct ip_fw *rule ;
+ int rulenum = me->fw_skipto_rule ; /* guess... */
- if ( (me->rule->fw_flg & IP_FW_F_COMMAND) == IP_FW_F_SKIPTO )
- for (chain = LIST_NEXT(me,next); chain ; chain = LIST_NEXT(chain,next))
- if (chain->rule->fw_number >= rule)
- return chain ;
+ if ( (me->fw_flg & IP_FW_F_COMMAND) == IP_FW_F_SKIPTO )
+ for (rule = LIST_NEXT(me,next); rule ; rule = LIST_NEXT(rule,next))
+ if (rule->fw_number >= rulenum)
+ return rule ;
return LIST_NEXT(me,next) ; /* failure or not a skipto */
}
@@ -989,14 +1090,14 @@ lookup_next_rule(struct ip_fw_chain *me)
static int
ip_fw_chk(struct ip **pip, int hlen,
struct ifnet *oif, u_int16_t *cookie, struct mbuf **m,
- struct ip_fw_chain **flow_id,
+ struct ip_fw **flow_id,
struct sockaddr_in **next_hop)
{
- struct ip_fw_chain *chain;
- struct ip_fw *f = NULL, *rule = NULL;
+ struct ip_fw *f = NULL; /* matching rule */
struct ip *ip = *pip;
struct ifnet *const rif = (*m)->m_pkthdr.rcvif;
struct ifnet *tif;
+
u_short offset = 0 ;
u_short src_port = 0, dst_port = 0;
struct in_addr src_ip, dst_ip; /* XXX */
@@ -1085,38 +1186,37 @@ ip_fw_chk(struct ip **pip, int hlen,
last_pkt.flags = flags;
if (*flow_id) {
- /*
- * Packet has already been tagged. Look for the next rule
- * to restart processing.
- */
- if (fw_one_pass) /* just accept if fw_one_pass is set */
- return 0;
+ /*
+ * Packet has already been tagged. Look for the next rule
+ * to restart processing.
+ */
+ if (fw_one_pass) /* just accept if fw_one_pass is set */
+ return 0;
- if ((chain = (*flow_id)->rule->next_rule_ptr) == NULL)
- chain = (*flow_id)->rule->next_rule_ptr =
- lookup_next_rule(*flow_id);
- if (chain == NULL)
- goto dropit;
+ f = (*flow_id)->next_rule_ptr ;
+ if (f == NULL)
+ f = (*flow_id)->next_rule_ptr = lookup_next_rule(*flow_id);
+ if (f == NULL)
+ goto dropit;
} else {
- /*
- * Go down the chain, looking for enlightment.
- * If we've been asked to start at a given rule, do so.
- */
- chain = LIST_FIRST(&ip_fw_chain_head);
- if (skipto != 0) {
- if (skipto >= IPFW_DEFAULT_RULE)
- goto dropit;
- while (chain && chain->rule->fw_number <= skipto)
- chain = LIST_NEXT(chain, next);
- if (chain == NULL)
- goto dropit;
- }
+ /*
+ * Go down the chain, looking for enlightment.
+ * If we've been asked to start at a given rule, do so.
+ */
+ f = LIST_FIRST(&ip_fw_chain_head);
+ if (skipto != 0) {
+ if (skipto >= IPFW_DEFAULT_RULE)
+ goto dropit;
+ while (f && f->fw_number <= skipto)
+ f = LIST_NEXT(f, next);
+ if (f == NULL)
+ goto dropit;
+ }
}
- for (; chain; chain = LIST_NEXT(chain, next)) {
+ for (; f; f = LIST_NEXT(f, next)) {
again:
- f = chain->rule;
if (f->fw_number == IPFW_DEFAULT_RULE)
goto got_match ;
@@ -1133,8 +1233,7 @@ again:
(q->id.src_ip), (q->id.src_port),
(direction == MATCH_FORWARD ? "-->" : "<--"),
(q->id.dst_ip), (q->id.dst_port) ); )
- chain = q->chain ;
- f = chain->rule ;
+ f = q->rule ;
q->pcnt++ ;
q->bcnt += ip_len;
goto got_match ; /* random not allowed here */
@@ -1308,7 +1407,7 @@ again:
* packet -- if this rule specified either one,
* we consider the rule a non-match.
*/
- if (f->fw_nports != 0 ||
+ if (IP_FW_HAVEPORTS(f) != 0 ||
f->fw_ipflg & IP_FW_IF_TCPMSK)
continue;
@@ -1338,7 +1437,7 @@ again:
* rule specifies a port, we consider the rule
* a non-match.
*/
- if (f->fw_nports != 0)
+ if (IP_FW_HAVEPORTS(f) )
continue;
break;
@@ -1379,8 +1478,7 @@ bogusfrag:
}
rnd_then_got_match:
- if ( ((struct ip_fw_ext *)f)->dont_match_prob &&
- random() < ((struct ip_fw_ext *)f)->dont_match_prob )
+ if ( f->dont_match_prob && random() < f->dont_match_prob )
continue ;
got_match:
/*
@@ -1388,7 +1486,7 @@ got_match:
* a new dynamic entry.
*/
if (q == NULL && f->fw_flg & IP_FW_F_KEEP_S) {
- if (install_state(chain)) /* error or limit violation */
+ if (install_state(f)) /* error or limit violation */
goto dropit;
}
/* Update statistics */
@@ -1415,15 +1513,15 @@ got_match:
return(f->fw_divert_port | IP_FW_PORT_TEE_FLAG);
#endif
case IP_FW_F_SKIPTO: /* XXX check */
- chain = f->next_rule_ptr ? f->next_rule_ptr :
- lookup_next_rule(chain) ;
- if (! chain)
+ f = f->next_rule_ptr ? f->next_rule_ptr :
+ lookup_next_rule(f) ;
+ if (!f)
goto dropit;
goto again ;
#ifdef DUMMYNET
case IP_FW_F_PIPE:
case IP_FW_F_QUEUE:
- *flow_id = chain;
+ *flow_id = f;
return(f->fw_pipe_nr | IP_FW_PORT_DYNT_FLAG);
#endif
#ifdef IPFIREWALL_FORWARD
@@ -1437,6 +1535,9 @@ got_match:
* ip_output.c. We hope to high [name the abode of
* your favourite deity] that ip_output doesn't modify
* the new value of next_hop (which is dst there)
+ * XXX warning-- there is a dangerous reference here
+ * from next_hop to a field within the rule. If the
+ * rule is deleted, weird things might occur.
*/
if (next_hop != NULL /* Make sure, first... */
&& (q == NULL || direction == MATCH_FORWARD) )
@@ -1446,13 +1547,11 @@ got_match:
}
/* Deny/reject this packet using this rule */
- rule = f;
break;
-
}
/* Rule IPFW_DEFAULT_RULE should always be there and match */
- KASSERT(chain != NULL, ("ip_fw: no chain"));
+ KASSERT(f != NULL, ("ip_fw: no chain"));
/*
* At this point, we're going to drop the packet.
@@ -1462,11 +1561,11 @@ got_match:
* - The packet is not an ICMP packet, or is an ICMP query packet
* - The packet is not a multicast or broadcast packet
*/
- if ((rule->fw_flg & IP_FW_F_COMMAND) == IP_FW_F_REJECT
+ if ((f->fw_flg & IP_FW_F_COMMAND) == IP_FW_F_REJECT
&& (proto != IPPROTO_ICMP || is_icmp_query(ip))
&& !((*m)->m_flags & (M_BCAST|M_MCAST))
&& !IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) {
- switch (rule->fw_reject_code) {
+ switch (f->fw_reject_code) {
case IP_FW_REJECT_RST:
{
/* XXX warning, this code writes into the mbuf */
@@ -1497,7 +1596,7 @@ got_match:
}
default: /* Send an ICMP unreachable using code */
icmp_error(*m, ICMP_UNREACH,
- rule->fw_reject_code, 0L, 0);
+ f->fw_reject_code, 0L, 0);
*m = NULL;
break;
}
@@ -1520,39 +1619,25 @@ dropit:
static void
flush_rule_ptrs()
{
- struct ip_fw_chain *fcp ;
+ struct ip_fw *fcp ;
LIST_FOREACH(fcp, &ip_fw_chain_head, next) {
- fcp->rule->next_rule_ptr = NULL ;
+ fcp->next_rule_ptr = NULL ;
}
}
static int
add_entry(struct ip_fw_head *head, struct ip_fw *rule)
{
- struct ip_fw *ftmp = 0;
- struct ip_fw_ext *ftmp_ext = 0 ;
- struct ip_fw_chain *fwc, *fcp, *fcpl;
+ struct ip_fw *ftmp, *fcp, *fcpl;
u_short nbr = 0;
int s;
- fwc = malloc(sizeof *fwc, M_IPFW, M_DONTWAIT);
- if (!fwc)
- return (ENOSPC);
- ftmp_ext = malloc(sizeof *ftmp_ext, M_IPFW, M_DONTWAIT | M_ZERO);
- if (!ftmp_ext) {
- free(fwc, M_IPFW);
+ ftmp = malloc(sizeof *ftmp, M_IPFW, M_DONTWAIT | M_ZERO);
+ if (!ftmp)
return (ENOSPC);
- }
- fwc->rule = ftmp = &ftmp_ext->rule ;
bcopy(rule, ftmp, sizeof(*ftmp));
- if (ftmp->fw_flg & IP_FW_F_RND_MATCH)
- ftmp_ext->dont_match_prob = (intptr_t)ftmp->pipe_ptr;
- if (ftmp->fw_flg & IP_FW_F_KEEP_S) {
- u_long type = (u_long)(ftmp->next_rule_ptr) ;
- ftmp_ext->dyn_type = type & 0xff;
- }
ftmp->fw_in_if.fu_via_if.name[FW_IFNLEN - 1] = '\0';
ftmp->fw_pcnt = 0L;
@@ -1563,15 +1648,15 @@ add_entry(struct ip_fw_head *head, struct ip_fw *rule)
s = splnet();
if (LIST_FIRST(head) == 0) {
- LIST_INSERT_HEAD(head, fwc, next);
+ LIST_INSERT_HEAD(head, ftmp, next);
goto done;
}
/* If entry number is 0, find highest numbered rule and add 100 */
if (ftmp->fw_number == 0) {
- LIST_FOREACH(fcp, head, next) {
- if (fcp->rule->fw_number != IPFW_DEFAULT_RULE)
- nbr = fcp->rule->fw_number;
+ LIST_FOREACH(ftmp, head, next) {
+ if (ftmp->fw_number != IPFW_DEFAULT_RULE)
+ nbr = ftmp->fw_number;
else
break;
}
@@ -1583,11 +1668,11 @@ add_entry(struct ip_fw_head *head, struct ip_fw *rule)
/* Got a valid number; now insert it, keeping the list ordered */
fcpl = NULL ;
LIST_FOREACH(fcp, head, next) {
- if (fcp->rule->fw_number > ftmp->fw_number) {
+ if (fcp->fw_number > ftmp->fw_number) {
if (fcpl) {
- LIST_INSERT_AFTER(fcpl, fwc, next);
+ LIST_INSERT_AFTER(fcpl, ftmp, next);
} else {
- LIST_INSERT_HEAD(head, fwc, next);
+ LIST_INSERT_HEAD(head, ftmp, next);
}
break;
} else {
@@ -1610,10 +1695,10 @@ done:
* @return a pointer to the next entry.
* Must be called at splnet() and with a non-null argument.
*/
-static struct ip_fw_chain *
-free_chain(struct ip_fw_chain *fcp)
+static struct ip_fw *
+free_chain(struct ip_fw *fcp)
{
- struct ip_fw_chain *n;
+ struct ip_fw *n;
n = LIST_NEXT(fcp, next);
DELETE_DYN_CHAIN(fcp);
@@ -1623,7 +1708,6 @@ free_chain(struct ip_fw_chain *fcp)
dn_rule_delete(fcp) ;
#endif
flush_rule_ptrs(); /* more efficient to do outside the loop */
- free(fcp->rule, M_IPFW);
free(fcp, M_IPFW);
return n;
}
@@ -1634,16 +1718,16 @@ free_chain(struct ip_fw_chain *fcp)
static int
del_entry(struct ip_fw_head *chainptr, u_short number)
{
- struct ip_fw_chain *fcp;
+ struct ip_fw *rule;
if (number != IPFW_DEFAULT_RULE) {
- LIST_FOREACH(fcp, chainptr, next) {
- if (fcp->rule->fw_number == number) {
+ LIST_FOREACH(rule, chainptr, next) {
+ if (rule->fw_number == number) {
int s ;
s = splnet(); /* prevent access to rules while removing */
- while (fcp && fcp->rule->fw_number == number)
- fcp = free_chain(fcp);
+ while (rule && rule->fw_number == number)
+ rule = free_chain(rule);
/* XXX could move flush_rule_ptrs() here */
splx(s);
return 0 ;
@@ -1663,7 +1747,6 @@ del_entry(struct ip_fw_head *chainptr, u_short number)
static int
zero_entry(struct ip_fw *frwl, int log_only)
{
- struct ip_fw_chain *fcp;
struct ip_fw *rule;
int s;
u_short number = 0 ;
@@ -1671,8 +1754,7 @@ zero_entry(struct ip_fw *frwl, int log_only)
if (frwl == 0) {
s = splnet();
- LIST_FOREACH(fcp, &ip_fw_chain_head, next) {
- rule = fcp->rule;
+ LIST_FOREACH(rule, &ip_fw_chain_head, next) {
if (log_only == 0) {
rule->fw_bcnt = rule->fw_pcnt = 0;
rule->timestamp = 0;
@@ -1691,16 +1773,16 @@ zero_entry(struct ip_fw *frwl, int log_only)
* same number, so we don't stop after finding the first
* match if zeroing a specific entry.
*/
- LIST_FOREACH(fcp, &ip_fw_chain_head, next)
- if (number == fcp->rule->fw_number) {
+ LIST_FOREACH(rule, &ip_fw_chain_head, next)
+ if (number == rule->fw_number) {
s = splnet();
- while (fcp && number == (rule=fcp->rule)->fw_number) {
+ while (rule && number == rule->fw_number) {
if (log_only == 0) {
rule->fw_bcnt = rule->fw_pcnt = 0;
rule->timestamp = 0;
}
rule->fw_loghighest = rule->fw_pcnt+ rule->fw_logamount;
- fcp = LIST_NEXT(fcp, next);
+ rule = LIST_NEXT(rule, next);
}
splx(s);
cleared = 1;
@@ -1791,7 +1873,7 @@ check_ipfw_struct(struct ip_fw *frwl)
if ((frwl->fw_flg & IP_FW_F_FRAG) &&
(frwl->fw_prot == IPPROTO_UDP || frwl->fw_prot == IPPROTO_TCP)) {
- if (frwl->fw_nports) {
+ if (IP_FW_HAVEPORTS(frwl)) {
dprintf(("%s cannot mix 'frag' and ports\n", err_prefix));
return (EINVAL);
}
@@ -1857,7 +1939,7 @@ ip_fw_ctl(struct sockopt *sopt)
{
int error, s;
size_t size;
- struct ip_fw_chain *fcp;
+ struct ip_fw *fcp;
struct ip_fw frwl, *bp , *buf;
/*
@@ -1896,11 +1978,7 @@ ip_fw_ctl(struct sockopt *sopt)
bp = buf ;
LIST_FOREACH(fcp, &ip_fw_chain_head, next) {
- struct ip_fw_ext *e = (struct ip_fw_ext *)fcp->rule;
-
- bcopy(e, bp, sizeof *fcp->rule);
- bp->pipe_ptr = (void *)(intptr_t) e->dont_match_prob;
- bp->next_rule_ptr = (void *)(intptr_t) (e->dyn_type);
+ bcopy(fcp, bp, sizeof *fcp);
bp++;
}
if (ipfw_dyn_v) {
@@ -1911,7 +1989,7 @@ ip_fw_ctl(struct sockopt *sopt)
for (i = 0 ; i < curr_dyn_buckets ; i++ )
for ( p = ipfw_dyn_v[i] ; p != NULL ; p = p->next, dst++ ) {
bcopy(p, dst, sizeof *p);
- (int)dst->chain = p->chain->rule->fw_number ;
+ (int)dst->rule = p->rule->fw_number ;
dst->next = dst ; /* fake non-null pointer... */
last = dst ;
if (TIME_LEQ(dst->expire, time_second) )
@@ -1944,7 +2022,7 @@ ip_fw_ctl(struct sockopt *sopt)
s = splnet();
while ( (fcp = LIST_FIRST(&ip_fw_chain_head)) &&
- fcp->rule->fw_number != IPFW_DEFAULT_RULE )
+ fcp->fw_number != IPFW_DEFAULT_RULE )
free_chain(fcp);
splx(s);
break;
@@ -2011,7 +2089,7 @@ ip_fw_ctl(struct sockopt *sopt)
* NULL pointer, but this way we do not need to check for the special
* case, plus here he have info on the default behaviour.
*/
-struct ip_fw_chain *ip_fw_default_rule ;
+struct ip_fw *ip_fw_default_rule ;
void
ip_fw_init(void)
@@ -2070,7 +2148,7 @@ static int
ipfw_modevent(module_t mod, int type, void *unused)
{
int s;
- struct ip_fw_chain *fcp;
+ struct ip_fw *fcp;
switch (type) {
case MOD_LOAD:
diff --git a/sys/netinet/ip_fw.h b/sys/netinet/ip_fw.h
index a4d3f24..5590b96 100644
--- a/sys/netinet/ip_fw.h
+++ b/sys/netinet/ip_fw.h
@@ -50,28 +50,59 @@ union ip_fw_if {
*/
struct ip_fw {
- u_int64_t fw_pcnt,fw_bcnt; /* Packet and byte counters */
- struct in_addr fw_src, fw_dst; /* Source and destination IP addr */
- struct in_addr fw_smsk, fw_dmsk; /* Mask for src and dest IP addr */
- u_short fw_number; /* Rule number */
- u_int fw_flg; /* Operational Flags word */
-#define IP_FW_MAX_PORTS 10 /* A reasonable maximum */
- union {
- u_short fw_pts[IP_FW_MAX_PORTS]; /* Array of port numbers to match */
+ LIST_ENTRY(ip_fw) next; /* bidirectional list of rules */
+ u_int fw_flg; /* Operational Flags word */
+
+ u_int64_t fw_pcnt,fw_bcnt; /* Packet and byte counters */
+ struct in_addr fw_src, fw_dst; /* Source and dest. IP addr */
+ struct in_addr fw_smsk, fw_dmsk; /* Mask for above addresses */
+ u_short fw_number; /* Rule number */
+ u_char fw_prot; /* IP protocol */
+#if 1
+ u_char fw_nports; /* # of src/dst port in array */
+#define IP_FW_GETNSRCP(rule) ((rule)->fw_nports & 0x0f)
+#define IP_FW_SETNSRCP(rule, n) do { \
+ (rule)->fw_nports &= ~0x0f; \
+ (rule)->fw_nports |= (n); \
+ } while (0)
+#define IP_FW_GETNDSTP(rule) ((rule)->fw_nports >> 4)
+#define IP_FW_SETNDSTP(rule, n) do { \
+ (rule)->fw_nports &= ~0xf0; \
+ (rule)->fw_nports |= (n) << 4;\
+ } while (0)
+#define IP_FW_HAVEPORTS(rule) ((rule)->fw_nports != 0)
+#else
+ u_char __pad[1];
+u_int _nsrcp, _ndstp;
+#define IP_FW_GETNSRCP(rule) (rule)->_nsrcp
+#define IP_FW_SETNSRCP(rule,n) (rule)->_nsrcp = n
+#define IP_FW_GETNDSTP(rule) (rule)->_ndstp
+#define IP_FW_SETNDSTP(rule,n) (rule)->_ndstp = n
+#define IP_FW_HAVEPORTS(rule) ((rule)->_ndstp + (rule)->_nsrcp != 0)
+#endif
+#define IP_FW_MAX_PORTS 10 /* A reasonable maximum */
+ union {
+ u_short fw_pts[IP_FW_MAX_PORTS]; /* port numbers to match */
#define IP_FW_ICMPTYPES_MAX 128
#define IP_FW_ICMPTYPES_DIM (IP_FW_ICMPTYPES_MAX / (sizeof(unsigned) * 8))
unsigned fw_icmptypes[IP_FW_ICMPTYPES_DIM]; /* ICMP types bitmap */
- } fw_uar;
- u_int fw_ipflg; /* IP flags word */
- u_char fw_ipopt,fw_ipnopt; /* IP options set/unset */
- u_short fw_iplen, fw_ipid; /* IP length, identification */
+ } fw_uar;
+
+ u_int fw_ipflg; /* IP flags word */
+
+ u_short fw_iplen, fw_ipid; /* IP length, identification */
+
+ u_char fw_ipopt,fw_ipnopt; /* IP options set/unset */
u_char fw_iptos, fw_ipntos; /* IP type of service set/unset */
+
u_char fw_ipttl; /* IP time to live */
u_int fw_ipver:4; /* IP version */
u_char fw_tcpopt,fw_tcpnopt; /* TCP options set/unset */
+
u_char fw_tcpf,fw_tcpnf; /* TCP flags set/unset */
- u_int32_t fw_tcpseq, fw_tcpack; /* TCP sequence and acknowledgement */
u_short fw_tcpwin; /* TCP window size */
+
+ u_int32_t fw_tcpseq, fw_tcpack; /* TCP sequence and acknowledgement */
long timestamp; /* timestamp (tv_sec) of last match */
union ip_fw_if fw_in_if, fw_out_if; /* Incoming and outgoing interfaces */
union {
@@ -81,51 +112,29 @@ struct ip_fw {
u_short fu_reject_code; /* REJECT response code */
struct sockaddr_in fu_fwd_ip;
} fw_un;
- u_char fw_prot; /* IP protocol */
- /*
- * N'of src ports and # of dst ports in ports array (dst ports
- * follow src ports; max of 10 ports in all; count of 0 means
- * match all ports)
- */
- u_char fw_nports;
- void *pipe_ptr; /* flow_set ptr for dummynet pipe */
- void *next_rule_ptr ; /* next rule in case of match */
+ void *pipe_ptr; /* flow_set ptr for dummynet pipe */
+ void *next_rule_ptr ; /* next rule in case of match */
uid_t fw_uid; /* uid to match */
gid_t fw_gid; /* gid to match */
int fw_logamount; /* amount to log */
u_int64_t fw_loghighest; /* highest number packet to log */
-};
-/*
- * extended ipfw structure... some fields in the original struct
- * can be used to pass parameters up/down, namely pointers
- * void *pipe_ptr
- * void *next_rule_ptr
- * some others can be used to pass parameters down, namely counters etc.
- * u_int64_t fw_pcnt,fw_bcnt;
- * long timestamp;
- */
-
-struct ip_fw_ext { /* extended structure */
- struct ip_fw rule; /* must be at offset 0 */
long dont_match_prob; /* 0x7fffffff means 1.0, always fail */
u_char dyn_type; /* type for dynamic rule */
-#define DYN_KEEP_STATE 0 /* type for keep-state rules */
- u_char _pad1 ; /* for future use */
- u_short _pad2 ; /* for future use */
+#define DYN_KEEP_STATE 0 /* type for keep-state rules */
+#define DYN_LIMIT 1 /* type for limit connection rules */
+#define DYN_LIMIT_PARENT 2 /* parent entry for limit connection rules */
+ /* following two fields are used to limit number of connections
+ * basing on either src,srcport,dst,dstport.
+ */
+ u_char limit_mask ; /* mask type for limit rule, can have many */
+#define DYN_SRC_ADDR 0x1
+#define DYN_SRC_PORT 0x2
+#define DYN_DST_ADDR 0x4
+#define DYN_DST_PORT 0x8
+ u_short conn_limit ; /* # of connections for limit rule */
};
-#define IP_FW_GETNSRCP(rule) ((rule)->fw_nports & 0x0f)
-#define IP_FW_SETNSRCP(rule, n) do { \
- (rule)->fw_nports &= ~0x0f; \
- (rule)->fw_nports |= (n); \
- } while (0)
-#define IP_FW_GETNDSTP(rule) ((rule)->fw_nports >> 4)
-#define IP_FW_SETNDSTP(rule, n) do { \
- (rule)->fw_nports &= ~0xf0; \
- (rule)->fw_nports |= (n) << 4;\
- } while (0)
-
#define fw_divert_port fw_un.fu_divert_port
#define fw_skipto_rule fw_un.fu_skipto_rule
#define fw_reject_code fw_un.fu_reject_code
@@ -134,21 +143,14 @@ struct ip_fw_ext { /* extended structure */
/**
*
- * chain_ptr -------------+
+ * rule_ptr -------------+
* V
* [ next.le_next ]---->[ next.le_next ]---- [ next.le_next ]--->
* [ next.le_prev ]<----[ next.le_prev ]<----[ next.le_prev ]<---
- * +--[ rule ] +--[ rule ] +--[ rule ]
- * | | |
- * +->[ <ip_fw> ] +->[ <ip_fw> ] +->[ <ip_fw> ]
+ * [ <ip_fw> body ] [ <ip_fw> body ] [ <ip_fw> body ]
*
*/
-struct ip_fw_chain {
- LIST_ENTRY(ip_fw_chain) next;
- struct ip_fw *rule;
-};
-
/*
* Flow mask/flow id for each queue.
*/
@@ -166,7 +168,8 @@ struct ipfw_dyn_rule {
struct ipfw_dyn_rule *next ;
struct ipfw_flow_id id ; /* (masked) flow id */
- struct ip_fw_chain *chain ; /* pointer to chain */
+ struct ip_fw *rule ; /* pointer to rule */
+ struct ipfw_dyn_rule *parent ; /* pointer to parent rule */
u_int32_t expire ; /* expire time */
u_int64_t pcnt, bcnt; /* match counters */
u_int32_t bucket ; /* which bucket in hash table */
@@ -311,9 +314,9 @@ void ip_fw_init __P((void));
/* Firewall hooks */
struct ip;
struct sockopt;
-typedef int ip_fw_chk_t __P((struct ip **, int, struct ifnet *, u_int16_t *,
- struct mbuf **, struct ip_fw_chain **, struct sockaddr_in **));
-typedef int ip_fw_ctl_t __P((struct sockopt *));
+typedef int ip_fw_chk_t (struct ip **, int, struct ifnet *, u_int16_t *,
+ struct mbuf **, struct ip_fw **, struct sockaddr_in **);
+typedef int ip_fw_ctl_t (struct sockopt *);
extern ip_fw_chk_t *ip_fw_chk_ptr;
extern ip_fw_ctl_t *ip_fw_ctl_ptr;
extern int fw_one_pass;
diff --git a/sys/netinet/ip_input.c b/sys/netinet/ip_input.c
index d80c7d4..9c0615b 100644
--- a/sys/netinet/ip_input.c
+++ b/sys/netinet/ip_input.c
@@ -280,7 +280,7 @@ ip_input(struct mbuf *m)
#ifdef IPDIVERT
u_int32_t divert_info = 0; /* packet divert/tee info */
#endif
- struct ip_fw_chain *rule = NULL;
+ struct ip_fw *rule = NULL;
#ifdef PFIL_HOOKS
struct packet_filter_hook *pfh;
struct mbuf *m0;
@@ -302,7 +302,7 @@ ip_input(struct mbuf *m)
* rule.
*/
if (m->m_type == MT_DUMMYNET) {
- rule = (struct ip_fw_chain *)(m->m_data) ;
+ rule = (struct ip_fw *)(m->m_data) ;
m = m->m_next ;
ip = mtod(m, struct ip *);
hlen = IP_VHL_HL(ip->ip_vhl) << 2;
diff --git a/sys/netinet/ip_output.c b/sys/netinet/ip_output.c
index bd09521..012a3c4 100644
--- a/sys/netinet/ip_output.c
+++ b/sys/netinet/ip_output.c
@@ -143,7 +143,7 @@ ip_output(m0, opt, ro, flags, imo)
#ifdef IPFIREWALL_FORWARD
int fwd_rewrite_src = 0;
#endif
- struct ip_fw_chain *rule = NULL;
+ struct ip_fw *rule = NULL;
#ifdef IPDIVERT
/* Get and reset firewall cookie */
@@ -165,7 +165,7 @@ ip_output(m0, opt, ro, flags, imo)
* processing was already done, and we need to go down.
* Get parameters from the header.
*/
- rule = (struct ip_fw_chain *)(m->m_data) ;
+ rule = (struct ip_fw *)(m->m_data) ;
opt = NULL ;
ro = & ( ((struct dn_pkt *)m)->ro ) ;
imo = NULL ;
OpenPOWER on IntegriCloud