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
This commit is contained in:
luigi 2001-09-27 23:44:27 +00:00
parent 2854bb2840
commit 0fb106cc3f
9 changed files with 437 additions and 290 deletions

View File

@ -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.

View File

@ -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))) {

View File

@ -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. */

View File

@ -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 */

View File

@ -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 */

View File

@ -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 @@ lookup_dyn_rule(struct ipfw_flow_id *pkt, int *match_direction)
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 @@ ip_fw_chk(struct ip **pip, int hlen,
(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 @@ ip_fw_chk(struct ip **pip, int hlen,
* 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 @@ ip_fw_chk(struct ip **pip, int hlen,
* 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 @@ ip_fw_chk(struct ip **pip, int hlen,
}
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 @@ ip_fw_chk(struct ip **pip, int hlen,
* 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 @@ ip_fw_chk(struct ip **pip, int hlen,
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 @@ ip_fw_chk(struct ip **pip, int hlen,
* 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 @@ ip_fw_chk(struct ip **pip, int hlen,
}
/* 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 @@ ip_fw_chk(struct ip **pip, int hlen,
* - 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 @@ ip_fw_chk(struct ip **pip, int hlen,
}
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 @@ ip_fw_chk(struct ip **pip, int hlen,
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)
ftmp = malloc(sizeof *ftmp, M_IPFW, M_DONTWAIT | M_ZERO);
if (!ftmp)
return (ENOSPC);
ftmp_ext = malloc(sizeof *ftmp_ext, M_IPFW, M_DONTWAIT | M_ZERO);
if (!ftmp_ext) {
free(fwc, M_IPFW);
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 @@ add_entry(struct ip_fw_head *head, struct ip_fw *rule)
* @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:

View File

@ -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;

View File

@ -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;

View File

@ -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 ;