summaryrefslogtreecommitdiffstats
path: root/sys/netinet/ip_fw.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/netinet/ip_fw.c')
-rw-r--r--sys/netinet/ip_fw.c622
1 files changed, 318 insertions, 304 deletions
diff --git a/sys/netinet/ip_fw.c b/sys/netinet/ip_fw.c
index e325436..81219da 100644
--- a/sys/netinet/ip_fw.c
+++ b/sys/netinet/ip_fw.c
@@ -156,13 +156,26 @@ SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, permanent_rules, CTLFLAG_RW,
static struct ipfw_dyn_rule **ipfw_dyn_v = NULL ;
static u_int32_t dyn_buckets = 256 ; /* must be power of 2 */
static u_int32_t curr_dyn_buckets = 256 ; /* must be power of 2 */
+/**
+ * timeouts for various events in handing dynamic rules.
+ */
static u_int32_t dyn_ack_lifetime = 300 ;
static u_int32_t dyn_syn_lifetime = 20 ;
-static u_int32_t dyn_fin_lifetime = 20 ;
-static u_int32_t dyn_rst_lifetime = 5 ;
-static u_int32_t dyn_short_lifetime = 30 ;
-static u_int32_t dyn_count = 0 ;
-static u_int32_t dyn_max = 1000 ;
+static u_int32_t dyn_fin_lifetime = 1 ;
+static u_int32_t dyn_rst_lifetime = 1 ;
+static u_int32_t dyn_udp_lifetime = 10 ;
+static u_int32_t dyn_short_lifetime = 5 ;
+
+/*
+ * after reaching 0, dynamic rules are considered still valid for
+ * an additional grace time, unless there is lack of resources.
+ */
+static u_int32_t dyn_grace_time = 10 ;
+
+static u_int32_t static_count = 0 ; /* # of static rules */
+static u_int32_t dyn_count = 0 ; /* # of dynamic rules */
+static u_int32_t dyn_max = 1000 ; /* max # of dynamic rules */
+
SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, dyn_buckets, CTLFLAG_RW,
&dyn_buckets, 0, "Number of dyn. buckets");
SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, curr_dyn_buckets, CTLFLAG_RD,
@@ -171,6 +184,8 @@ SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, dyn_count, CTLFLAG_RD,
&dyn_count, 0, "Number of dyn. rules");
SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, dyn_max, CTLFLAG_RW,
&dyn_max, 0, "Max number of dyn. rules");
+SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, static_count, CTLFLAG_RD,
+ &static_count, 0, "Number of static rules");
SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, dyn_ack_lifetime, CTLFLAG_RW,
&dyn_ack_lifetime, 0, "Lifetime of dyn. rules for acks");
SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, dyn_syn_lifetime, CTLFLAG_RW,
@@ -179,8 +194,13 @@ SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, dyn_fin_lifetime, CTLFLAG_RW,
&dyn_fin_lifetime, 0, "Lifetime of dyn. rules for fin");
SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, dyn_rst_lifetime, CTLFLAG_RW,
&dyn_rst_lifetime, 0, "Lifetime of dyn. rules for rst");
+SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, dyn_udp_lifetime, CTLFLAG_RW,
+ &dyn_udp_lifetime, 0, "Lifetime of dyn. rules for UDP");
SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, dyn_short_lifetime, CTLFLAG_RW,
&dyn_short_lifetime, 0, "Lifetime of dyn. rules for other situations");
+SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, dyn_grace_time, CTLFLAG_RD,
+ &dyn_grace_time, 0, "Grace time for dyn. rules");
+
#endif
@@ -192,8 +212,7 @@ SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, dyn_short_lifetime, CTLFLAG_RW,
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 *));
-static int resetlog_entry __P((struct ip_fw *));
+static int zero_entry __P((struct ip_fw *, int));
static int check_ipfw_struct __P((struct ip_fw *m));
static __inline int
iface_match __P((struct ifnet *ifp, union ip_fw_if *ifu,
@@ -654,8 +673,28 @@ hash_packet(struct ipfw_flow_id *id)
return i ;
}
+/**
+ * unlink a dynamic rule from a chain. prev is a pointer to
+ * the previous one, q is a pointer to the rule to delete,
+ * head is a pointer to the head of the queue.
+ * Modifies q and potentially also head.
+ */
+#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", \
+ (q->id.src_ip), (q->id.src_port), \
+ (q->id.dst_ip), (q->id.dst_port), dyn_count-1 ); ) \
+ if (prev != NULL) \
+ prev->next = q = q->next ; \
+ else \
+ ipfw_dyn_v[i] = q = q->next ; \
+ dyn_count-- ; \
+ free(old_q, M_IPFW); }
+
+
#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
* delete unconditionally, otherwise only expired rules are removed.
@@ -663,7 +702,7 @@ hash_packet(struct ipfw_flow_id *id)
static void
remove_dyn_rule(struct ip_fw_chain *chain, int force)
{
- struct ipfw_dyn_rule *prev, *q, *old_q ;
+ struct ipfw_dyn_rule *prev, *q;
int i ;
static u_int32_t last_remove = 0 ;
@@ -676,19 +715,18 @@ remove_dyn_rule(struct ip_fw_chain *chain, int force)
for (i = 0 ; i < curr_dyn_buckets ; i++) {
for (prev=NULL, q = ipfw_dyn_v[i] ; q ; ) {
- if ( (chain == NULL || chain == q->chain) &&
- (force || TIME_LEQ( q->expire , time_second ) ) ) {
- DEB(printf("-- remove 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 ); )
- old_q = q ;
- if (prev != NULL)
- prev->next = q = q->next ;
- else
- ipfw_dyn_v[i] = q = q->next ;
- dyn_count-- ;
- free(old_q, M_IPFW);
- continue ;
+ /*
+ * logic can become complex here, so we split tests.
+ * First, test if we match any chain,
+ * 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);
+ if (zap)
+ zap = force || TIME_LEQ( q->expire , time_second );
+
+ if (zap) {
+ UNLINK_DYN_RULE(prev, ipfw_dyn_v[i], q);
} else {
prev = q ;
q = q->next ;
@@ -697,6 +735,14 @@ 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_CHAINS() remove_dyn_rule(NULL, 0 /* expired ones */)
+#define DELETE_DYN_CHAIN(chain) remove_dyn_rule(chain, 1 /* force removal */)
+#define DELETE_DYN_CHAINS() remove_dyn_rule(NULL, 1 /* force removal */)
+
+/**
+ * lookup a dynamic rule.
+ */
static struct ipfw_dyn_rule *
lookup_dyn_rule(struct ipfw_flow_id *pkt, int *match_direction)
{
@@ -704,7 +750,7 @@ lookup_dyn_rule(struct ipfw_flow_id *pkt, int *match_direction)
* stateful ipfw extensions.
* Lookup into dynamic session queue
*/
- struct ipfw_dyn_rule *prev, *q, *old_q ;
+ struct ipfw_dyn_rule *prev, *q ;
int i, dir = 0;
#define MATCH_FORWARD 1
@@ -713,33 +759,23 @@ lookup_dyn_rule(struct ipfw_flow_id *pkt, int *match_direction)
i = hash_packet( pkt );
for (prev=NULL, q = ipfw_dyn_v[i] ; q != NULL ; ) {
if (TIME_LEQ( q->expire , time_second ) ) { /* expire entry */
- old_q = q ;
- if (prev != NULL)
- prev->next = q = q->next ;
- else
- ipfw_dyn_v[i] = q = q->next ;
- dyn_count-- ;
- free(old_q, M_IPFW);
+ UNLINK_DYN_RULE(prev, ipfw_dyn_v[i], q);
continue ;
}
if ( pkt->proto == q->id.proto) {
- switch (q->type) {
- default: /* bidirectional rule, no masks */
- if (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 ) {
- dir = MATCH_FORWARD ;
- goto found ;
- }
- if (pkt->src_ip == q->id.dst_ip &&
- pkt->dst_ip == q->id.src_ip &&
- pkt->src_port == q->id.dst_port &&
- pkt->dst_port == q->id.src_port ) {
- dir = 0 ; /* reverse match */
- goto found ;
- }
- break ;
+ if (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 ) {
+ dir = MATCH_FORWARD ;
+ goto found ;
+ }
+ if (pkt->src_ip == q->id.dst_ip &&
+ pkt->dst_ip == q->id.src_ip &&
+ pkt->src_port == q->id.dst_port &&
+ pkt->dst_port == q->id.src_port ) {
+ dir = 0 ; /* reverse match */
+ goto found ;
}
}
prev = q ;
@@ -786,8 +822,10 @@ found:
q->expire = time_second + dyn_rst_lifetime ;
break ;
}
+ } else if (pkt->proto == IPPROTO_UDP) {
+ q->expire = time_second + dyn_udp_lifetime ;
} else {
- /* should do something for UDP and others... */
+ /* other protocols */
q->expire = time_second + dyn_short_lifetime ;
}
if (match_direction)
@@ -799,44 +837,44 @@ found:
* Install state for a dynamic session.
*/
-static void
-add_dyn_rule(struct ipfw_flow_id *id, struct ipfw_flow_id *mask,
+static struct ipfw_dyn_rule *
+add_dyn_rule(struct ipfw_flow_id *id, u_int8_t dyn_type,
struct ip_fw_chain *chain)
{
struct ipfw_dyn_rule *r ;
int i ;
if (ipfw_dyn_v == NULL ||
- (dyn_count == 0 && dyn_buckets != curr_dyn_buckets)) {
- /* try reallocation, make sure we have a power of 2 */
- u_int32_t i = dyn_buckets ;
- while ( i > 0 && (i & 1) == 0 )
- i >>= 1 ;
- if (i != 1) /* not a power of 2 */
- dyn_buckets = curr_dyn_buckets ; /* reset */
- else {
- if (ipfw_dyn_v != NULL)
+ (dyn_count == 0 && dyn_buckets != curr_dyn_buckets)) {
+ /* try reallocation, make sure we have a power of 2 */
+ u_int32_t i = dyn_buckets ;
+ while ( i > 0 && (i & 1) == 0 )
+ i >>= 1 ;
+ if (i != 1) /* not a power of 2 */
+ dyn_buckets = curr_dyn_buckets ; /* reset */
+ else {
+ curr_dyn_buckets = dyn_buckets ;
+ if (ipfw_dyn_v != NULL)
free(ipfw_dyn_v, M_IPFW);
- ipfw_dyn_v = malloc(curr_dyn_buckets * sizeof r,
+ ipfw_dyn_v = malloc(curr_dyn_buckets * sizeof r,
M_IPFW, M_DONTWAIT | M_ZERO);
- if (ipfw_dyn_v == NULL)
- return ; /* failed ! */
- }
+ if (ipfw_dyn_v == NULL)
+ return NULL; /* failed ! */
+ }
}
i = hash_packet(id);
r = malloc(sizeof *r, M_IPFW, M_DONTWAIT | M_ZERO);
if (r == NULL) {
printf ("sorry cannot allocate state\n");
- return ;
+ return NULL ;
}
- if (mask)
- r->mask = *mask ;
r->id = *id ;
r->expire = time_second + dyn_syn_lifetime ;
r->chain = chain ;
- r->type = ((struct ip_fw_ext *)chain->rule)->dyn_type ;
+ r->dyn_type = dyn_type ;
+ r->pcnt = r->bcnt = 0 ;
r->bucket = i ;
r->next = ipfw_dyn_v[i] ;
@@ -846,6 +884,7 @@ add_dyn_rule(struct ipfw_flow_id *id, struct ipfw_flow_id *mask,
(r->id.src_ip), (r->id.src_port),
(r->id.dst_ip), (r->id.dst_port),
dyn_count ); )
+ return r;
}
/*
@@ -853,14 +892,16 @@ add_dyn_rule(struct ipfw_flow_id *id, struct ipfw_flow_id *mask,
* There are different types of dynamic rules which can be installed.
* The type is in chain->dyn_type.
* Type 0 (default) is a bidirectional rule
+ *
+ * Returns 1 (failure) if state is not installed.
*/
-static void
+static int
install_state(struct ip_fw_chain *chain)
{
struct ipfw_dyn_rule *q ;
static int last_log ;
- u_long type = ((struct ip_fw_ext *)chain->rule)->dyn_type ;
+ u_int8_t type = ((struct ip_fw_ext *)chain->rule)->dyn_type ;
DEB(printf("-- install state type %d 0x%08lx %u -> 0x%08lx %u\n",
type,
@@ -868,28 +909,29 @@ install_state(struct ip_fw_chain *chain)
(last_pkt.dst_ip), (last_pkt.dst_port) );)
q = lookup_dyn_rule(&last_pkt, NULL) ;
- if (q != NULL) {
- if (last_log == time_second)
- return ;
- last_log = time_second ;
- printf(" entry already present, done\n");
- return ;
+ if (q != NULL) { /* should never occur */
+ if (last_log != time_second) {
+ last_log = time_second ;
+ printf(" entry already present, done\n");
+ }
+ return 0 ;
}
if (dyn_count >= dyn_max) /* try remove old ones... */
- remove_dyn_rule(NULL, 0 /* expire */);
+ EXPIRE_DYN_CHAINS();
if (dyn_count >= dyn_max) {
- if (last_log == time_second)
- return ;
- last_log = time_second ;
- printf(" Too many dynamic rules, sorry\n");
- return ;
+ if (last_log != time_second) {
+ last_log = time_second ;
+ printf(" Too many dynamic rules, sorry\n");
+ }
+ return 1; /* cannot install, notify caller */
}
switch (type) {
default: /* bidir rule */
- add_dyn_rule(&last_pkt, NULL, chain);
+ add_dyn_rule(&last_pkt, DYN_KEEP_STATE, chain);
break ;
}
- q = lookup_dyn_rule(&last_pkt, NULL) ; /* XXX this just sets the lifetime ... */
+ lookup_dyn_rule(&last_pkt, NULL) ; /* XXX just set the lifetime */
+ return 0;
}
/*
@@ -1001,23 +1043,26 @@ ip_fw_chk(struct ip **pip, int hlen,
ip_len = ip->ip_len;
}
if (offset == 0) {
- struct tcphdr *tcp;
- struct udphdr *udp;
-
switch (proto) {
- case IPPROTO_TCP :
+ case IPPROTO_TCP : {
+ struct tcphdr *tcp;
+
PULLUP_TO(hlen + sizeof(struct tcphdr));
tcp =(struct tcphdr *)((u_int32_t *)ip + ip->ip_hl);
dst_port = tcp->th_dport ;
src_port = tcp->th_sport ;
flags = tcp->th_flags ;
+ }
break ;
- case IPPROTO_UDP :
+ case IPPROTO_UDP : {
+ struct udphdr *udp;
+
PULLUP_TO(hlen + sizeof(struct udphdr));
udp =(struct udphdr *)((u_int32_t *)ip + ip->ip_hl);
dst_port = udp->uh_dport ;
src_port = udp->uh_sport ;
+ }
break;
case IPPROTO_ICMP:
@@ -1343,8 +1388,10 @@ got_match:
* If not a dynamic match (q == NULL) and keep-state, install
* a new dynamic entry.
*/
- if (q == NULL && f->fw_flg & IP_FW_F_KEEP_S)
- install_state(chain);
+ if (q == NULL && f->fw_flg & IP_FW_F_KEEP_S) {
+ if (install_state(chain)) /* error or limit violation */
+ goto dropit;
+ }
/* Update statistics */
f->fw_pcnt += 1;
f->fw_bcnt += ip_len;
@@ -1369,11 +1416,10 @@ got_match:
return(f->fw_divert_port | IP_FW_PORT_TEE_FLAG);
#endif
case IP_FW_F_SKIPTO: /* XXX check */
- if ( f->next_rule_ptr )
- chain = f->next_rule_ptr ;
- else
+ if ( (chain = f->next_rule_ptr) == NULL )
chain = lookup_next_rule(chain) ;
- if (! chain) goto dropit;
+ if (! chain)
+ goto dropit;
goto again ;
#ifdef DUMMYNET
case IP_FW_F_PIPE:
@@ -1418,7 +1464,7 @@ got_match:
* - The packet is not a multicast or broadcast packet
*/
if ((rule->fw_flg & IP_FW_F_COMMAND) == IP_FW_F_REJECT
- && (ip->ip_p != IPPROTO_ICMP || is_icmp_query(ip))
+ && (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) {
@@ -1483,65 +1529,66 @@ flush_rule_ptrs()
}
static int
-add_entry(struct ip_fw_head *chainptr, struct ip_fw *frwl)
+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 = 0, *fcp, *fcpl = 0;
+ struct ip_fw_chain *fwc, *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);
- ftmp = &ftmp_ext->rule ;
- if (!fwc || !ftmp) {
- dprintf(("%s malloc said no\n", err_prefix));
- if (fwc) free(fwc, M_IPFW);
- if (ftmp) free(ftmp, M_IPFW);
+ if (!ftmp_ext) {
+ free(fwc, M_IPFW);
return (ENOSPC);
}
+ fwc->rule = ftmp = &ftmp_ext->rule ;
- bcopy(frwl, ftmp, sizeof(*ftmp));
+ 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)
- ftmp_ext->dyn_type = (u_long)(ftmp->next_rule_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;
ftmp->fw_bcnt = 0L;
ftmp->next_rule_ptr = NULL ;
ftmp->pipe_ptr = NULL ;
- fwc->rule = ftmp;
s = splnet();
- if (LIST_FIRST(chainptr) == 0) {
- LIST_INSERT_HEAD(chainptr, fwc, next);
- splx(s);
- return(0);
+ if (LIST_FIRST(head) == 0) {
+ LIST_INSERT_HEAD(head, fwc, next);
+ goto done;
}
/* If entry number is 0, find highest numbered rule and add 100 */
if (ftmp->fw_number == 0) {
- LIST_FOREACH(fcp, chainptr, next) {
- if (fcp->rule->fw_number != (u_short)-1)
+ LIST_FOREACH(fcp, head, next) {
+ if (fcp->rule->fw_number != IPFW_DEFAULT_RULE)
nbr = fcp->rule->fw_number;
else
break;
}
if (nbr < IPFW_DEFAULT_RULE - 100)
nbr += 100;
- ftmp->fw_number = frwl->fw_number = nbr;
+ ftmp->fw_number = rule->fw_number = nbr;
}
/* Got a valid number; now insert it, keeping the list ordered */
- LIST_FOREACH(fcp, chainptr, next) {
+ fcpl = NULL ;
+ LIST_FOREACH(fcp, head, next) {
if (fcp->rule->fw_number > ftmp->fw_number) {
if (fcpl) {
LIST_INSERT_AFTER(fcpl, fwc, next);
} else {
- LIST_INSERT_HEAD(chainptr, fwc, next);
+ LIST_INSERT_HEAD(head, fwc, next);
}
break;
} else {
@@ -1549,150 +1596,125 @@ add_entry(struct ip_fw_head *chainptr, struct ip_fw *frwl)
}
}
flush_rule_ptrs();
-
+done:
+ static_count++;
splx(s);
+ DEB(printf("++ installed rule %d, static count now %d\n",
+ ftmp->fw_number, static_count);)
return (0);
}
-static int
-del_entry(struct ip_fw_head *chainptr, u_short number)
+/**
+ * free storage associated with a static chain entry (including
+ * dependent dynamic rules), and zeroes rule pointers to avoid
+ * dangling pointer dereferences.
+ * @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)
{
- struct ip_fw_chain *fcp;
-
- fcp = LIST_FIRST(chainptr);
- if (number != (u_short)-1) {
- for (; fcp; fcp = LIST_NEXT(fcp, next)) {
- if (fcp->rule->fw_number == number) {
- int s;
+ struct ip_fw_chain *n;
- /* prevent access to rules while removing them */
- s = splnet();
- while (fcp && fcp->rule->fw_number == number) {
- struct ip_fw_chain *next;
-
- remove_dyn_rule(fcp, 1 /* delete */);
- next = LIST_NEXT(fcp, next);
- LIST_REMOVE(fcp, next);
+ n = LIST_NEXT(fcp, next);
+ DELETE_DYN_CHAIN(fcp);
+ LIST_REMOVE(fcp, next);
+ static_count--;
#ifdef DUMMYNET
- dn_rule_delete(fcp) ;
+ dn_rule_delete(fcp) ;
#endif
- flush_rule_ptrs();
- free(fcp->rule, M_IPFW);
- free(fcp, M_IPFW);
- fcp = next;
- }
- splx(s);
- return 0;
- }
- }
- }
-
- return (EINVAL);
+ flush_rule_ptrs(); /* more efficient to do outside the loop */
+ free(fcp->rule, M_IPFW);
+ free(fcp, M_IPFW);
+ return n;
}
+/**
+ * remove all rules with given number.
+ */
static int
-zero_entry(struct ip_fw *frwl)
+del_entry(struct ip_fw_head *chainptr, u_short number)
{
- struct ip_fw_chain *fcp;
- int s, cleared;
+ struct ip_fw_chain *fcp;
- if (frwl == 0) {
- s = splnet();
- LIST_FOREACH(fcp, &ip_fw_chain_head, next) {
- fcp->rule->fw_bcnt = fcp->rule->fw_pcnt = 0;
- fcp->rule->fw_loghighest = fcp->rule->fw_logamount;
- fcp->rule->timestamp = 0;
- }
- splx(s);
- }
- else {
- cleared = 0;
-
- /*
- * It's possible to insert multiple chain entries with the
- * 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 (frwl->fw_number == fcp->rule->fw_number) {
- s = splnet();
- while (fcp && frwl->fw_number == fcp->rule->fw_number) {
- fcp->rule->fw_bcnt = fcp->rule->fw_pcnt = 0;
- fcp->rule->fw_loghighest =
- fcp->rule->fw_logamount;
- fcp->rule->timestamp = 0;
- fcp = LIST_NEXT(fcp, next);
- }
- splx(s);
- cleared = 1;
- break;
- }
- if (!cleared) /* we didn't find any matching rules */
- return (EINVAL);
- }
+ if (number != IPFW_DEFAULT_RULE) {
+ LIST_FOREACH(fcp, chainptr, next) {
+ if (fcp->rule->fw_number == number) {
+ int s ;
- if (fw_verbose) {
- if (frwl)
- log(LOG_SECURITY | LOG_NOTICE,
- "ipfw: Entry %d cleared.\n", frwl->fw_number);
- else
- log(LOG_SECURITY | LOG_NOTICE,
- "ipfw: Accounting cleared.\n");
+ s = splnet(); /* prevent access to rules while removing */
+ while (fcp && fcp->rule->fw_number == number)
+ fcp = free_chain(fcp);
+ /* XXX could move flush_rule_ptrs() here */
+ splx(s);
+ return 0 ;
+ }
}
-
- return (0);
+ }
+ return (EINVAL);
}
+/**
+ * Reset some or all counters on firewall rules.
+ * @arg frwl is null to clear all entries, or contains a specific
+ * rule number.
+ * @arg log_only is 1 if we only want to reset logs, zero otherwise.
+ */
+
static int
-resetlog_entry(struct ip_fw *frwl)
+zero_entry(struct ip_fw *frwl, int log_only)
{
- struct ip_fw_chain *fcp;
- int s, cleared;
+ struct ip_fw_chain *fcp;
+ struct ip_fw *rule;
+ int s;
+ u_short number = 0 ;
+ char *msg ;
- if (frwl == 0) {
- s = splnet();
- counter = 0;
- LIST_FOREACH(fcp, &ip_fw_chain_head, next)
- fcp->rule->fw_loghighest = fcp->rule->fw_pcnt +
- fcp->rule->fw_logamount;
- splx(s);
- }
- else {
- cleared = 0;
-
- /*
- * It's possible to insert multiple chain entries with the
- * 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 (frwl->fw_number == fcp->rule->fw_number) {
- s = splnet();
- while (fcp && frwl->fw_number == fcp->rule->fw_number) {
- fcp->rule->fw_loghighest =
- fcp->rule->fw_pcnt +
- fcp->rule->fw_logamount;
- fcp = LIST_NEXT(fcp, next);
- }
- splx(s);
- cleared = 1;
- break;
- }
- if (!cleared) /* we didn't find any matching rules */
- return (EINVAL);
- }
-
- if (fw_verbose) {
- if (frwl)
- log(LOG_SECURITY | LOG_NOTICE,
- "ipfw: Entry %d logging count reset.\n",
- frwl->fw_number);
- else
- log(LOG_SECURITY | LOG_NOTICE, "
- ipfw: All logging counts cleared.\n");
+ if (frwl == 0) {
+ s = splnet();
+ LIST_FOREACH(fcp, &ip_fw_chain_head, next) {
+ rule = fcp->rule;
+ if (log_only == 0) {
+ rule->fw_bcnt = rule->fw_pcnt = 0;
+ rule->timestamp = 0;
+ }
+ rule->fw_loghighest = rule->fw_pcnt+rule->fw_logamount;
}
+ splx(s);
+ msg = log_only ? "ipfw: All logging counts cleared.\n" :
+ "ipfw: Accounting cleared.\n";
+ } else {
+ int cleared = 0;
+ number = frwl->fw_number ;
- return (0);
+ /*
+ * It's possible to insert multiple chain entries with the
+ * 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) {
+ s = splnet();
+ while (fcp && number == (rule=fcp->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);
+ }
+ splx(s);
+ cleared = 1;
+ break;
+ }
+ if (!cleared) /* we didn't find any matching rules */
+ return (EINVAL);
+ msg = log_only ? "Entry %d logging count reset.\n" :
+ "ipfw: Entry %d cleared.\n";
+ }
+ if (fw_verbose)
+ log(LOG_SECURITY | LOG_NOTICE, msg, number);
+ return (0);
}
static int
@@ -1791,8 +1813,7 @@ check_ipfw_struct(struct ip_fw *frwl)
}
/* Check command specific stuff */
- switch (frwl->fw_flg & IP_FW_F_COMMAND)
- {
+ switch (frwl->fw_flg & IP_FW_F_COMMAND) {
case IP_FW_F_REJECT:
if (frwl->fw_reject_code >= 0x100
&& !(frwl->fw_prot == IPPROTO_TCP
@@ -1807,11 +1828,11 @@ check_ipfw_struct(struct ip_fw *frwl)
case IP_FW_F_TEE:
#endif
#ifdef DUMMYNET
- case IP_FW_F_PIPE: /* piping through 0 is invalid */
- case IP_FW_F_QUEUE: /* piping through 0 is invalid */
+ case IP_FW_F_PIPE: /* pipe 0 is invalid */
+ case IP_FW_F_QUEUE: /* queue 0 is invalid */
#endif
if (frwl->fw_divert_port == 0) {
- dprintf(("%s can't divert to port 0\n", err_prefix));
+ dprintf(("%s 0 is an invalid argument\n", err_prefix));
return (EINVAL);
}
break;
@@ -1851,32 +1872,33 @@ ip_fw_ctl(struct sockopt *sopt)
switch (sopt->sopt_name) {
case IP_FW_GET:
- size = 0 ;
s = splnet();
- LIST_FOREACH(fcp, &ip_fw_chain_head, next)
- size += sizeof(struct ip_fw) ;
- if (ipfw_dyn_v) {
- int i ;
- struct ipfw_dyn_rule *p ;
+ /* size of static rules */
+ size = static_count * sizeof(struct ip_fw) ;
+ if (ipfw_dyn_v) /* add size of dyn.rules */
+ size += (dyn_count * sizeof(struct ipfw_dyn_rule));
- for (i = 0 ; i < curr_dyn_buckets ; i++ )
- for ( p = ipfw_dyn_v[i] ; p != NULL ; p = p->next )
- size += sizeof(*p) ;
- }
+ /*
+ * XXX todo: if the user passes a short length to know how
+ * much room is needed, do not
+ * bother filling up the buffer, just jump to the
+ * sooptcopyout.
+ */
buf = malloc(size, M_TEMP, M_WAITOK);
if (buf == 0) {
+ splx(s);
error = ENOBUFS;
break;
}
bp = buf ;
LIST_FOREACH(fcp, &ip_fw_chain_head, next) {
- bcopy(fcp->rule, bp, sizeof *fcp->rule);
- bp->pipe_ptr = (void *)(intptr_t)
- ((struct ip_fw_ext *)fcp->rule)->dont_match_prob;
- bp->next_rule_ptr = (void *)(intptr_t)
- ((struct ip_fw_ext *)fcp->rule)->dyn_type;
- bp++;
+ 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);
+ bp++;
}
if (ipfw_dyn_v) {
int i ;
@@ -1904,37 +1926,24 @@ ip_fw_ctl(struct sockopt *sopt)
break;
case IP_FW_FLUSH:
+ /*
+ * Normally we cannot release the lock on each iteration.
+ * We could do it here only because we start from the head all
+ * the times so there is no risk of missing some entries.
+ * On the other hand, the risk is that we end up with
+ * a very inconsistent ruleset, so better keep the lock
+ * around the whole cycle.
+ *
+ * XXX this code can be improved by resetting the head of
+ * the list to point to the default rule, and then freeing
+ * the old list without the need for a lock.
+ */
+
s = splnet();
- remove_dyn_rule(NULL, 1 /* force delete */);
+ while ( (fcp = LIST_FIRST(&ip_fw_chain_head)) &&
+ fcp->rule->fw_number != IPFW_DEFAULT_RULE )
+ free_chain(fcp);
splx(s);
- fcp = LIST_FIRST(&ip_fw_chain_head);
- while (fcp) {
- struct ip_fw_chain *next;
- next = LIST_NEXT(fcp, next);
- if (fcp->rule->fw_number > fw_permanent_rules &&
- fcp->rule->fw_number != IPFW_DEFAULT_RULE ) {
- s = splnet();
- LIST_REMOVE(fcp, next);
-#ifdef DUMMYNET
- dn_rule_delete(fcp);
-#endif
- FREE(fcp->rule, M_IPFW);
- FREE(fcp, M_IPFW);
- splx(s);
- }
- fcp = next;
- }
- break;
-
- case IP_FW_ZERO:
- if (sopt->sopt_val != 0) {
- error = sooptcopyin(sopt, &frwl, sizeof frwl,
- sizeof frwl);
- if (error || (error = zero_entry(&frwl)))
- break;
- } else {
- error = zero_entry(0);
- }
break;
case IP_FW_ADD:
@@ -1967,16 +1976,21 @@ ip_fw_ctl(struct sockopt *sopt)
}
break;
+ case IP_FW_ZERO:
case IP_FW_RESETLOG:
+ {
+ int cmd = (sopt->sopt_name == IP_FW_RESETLOG );
+ void *arg = NULL ;
+
if (sopt->sopt_val != 0) {
- error = sooptcopyin(sopt, &frwl, sizeof frwl,
- sizeof frwl);
- if (error || (error = resetlog_entry(&frwl)))
- break;
- } else {
- error = resetlog_entry(0);
+ error = sooptcopyin(sopt, &frwl, sizeof frwl, sizeof frwl);
+ if (error)
+ break ;
+ arg = &frwl ;
}
- break;
+ error = zero_entry(arg, cmd);
+ }
+ break;
default:
printf("ip_fw_ctl invalid option %d\n", sopt->sopt_name);
@@ -1986,6 +2000,14 @@ ip_fw_ctl(struct sockopt *sopt)
return (error);
}
+/**
+ * dummynet needs a reference to the default rule, because rules can
+ * be deleted while packets hold a reference to them (e.g. to resume
+ * processing at the next rule). When this happens, dummynet changes
+ * the reference to the default rule (probably it could well be a
+ * 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 ;
void
@@ -2061,16 +2083,8 @@ ipfw_modevent(module_t mod, int type, void *unused)
s = splnet();
ip_fw_chk_ptr = old_chk_ptr;
ip_fw_ctl_ptr = old_ctl_ptr;
- remove_dyn_rule(NULL, 1 /* force delete */);
- while ( (fcp = LIST_FIRST(&ip_fw_chain_head)) != NULL) {
- LIST_REMOVE(fcp, next);
-#ifdef DUMMYNET
- dn_rule_delete(fcp);
-#endif
- free(fcp->rule, M_IPFW);
- free(fcp, M_IPFW);
- }
-
+ while ( (fcp = LIST_FIRST(&ip_fw_chain_head)) != NULL)
+ free_chain(fcp);
splx(s);
printf("IP firewall unloaded\n");
return 0;
OpenPOWER on IntegriCloud