summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Hartmeier <dhartmei@cvs.openbsd.org>2003-05-12 01:25:33 +0000
committerDaniel Hartmeier <dhartmei@cvs.openbsd.org>2003-05-12 01:25:33 +0000
commit4e1a3e6db441cb67c340c3d1fd13c9f1afcb96a1 (patch)
tree795327f86f48dfc5eba3746a29ed6c42d8188a6d
parent03cf287269743a3ba7ad0bac6bfd4e03e3c3da27 (diff)
Adaptive timeout value scaling. Allows to reduce timeout values as the
number of state table entries grows, so entries time out faster before the table fills up. Works both globally and per-rule. ok frantzen@
-rw-r--r--sbin/pfctl/pfctl.c9
-rw-r--r--sbin/pfctl/pfctl_parser.c4
-rw-r--r--share/man/man5/pf.conf.532
-rw-r--r--sys/net/pf.c89
-rw-r--r--sys/net/pf_ioctl.c29
-rw-r--r--sys/net/pfvar.h11
6 files changed, 123 insertions, 51 deletions
diff --git a/sbin/pfctl/pfctl.c b/sbin/pfctl/pfctl.c
index 3e7067cb981..b7b70cdc45b 100644
--- a/sbin/pfctl/pfctl.c
+++ b/sbin/pfctl/pfctl.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pfctl.c,v 1.170 2003/05/10 00:45:24 henning Exp $ */
+/* $OpenBSD: pfctl.c,v 1.171 2003/05/12 01:25:31 dhartmei Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
@@ -795,7 +795,12 @@ pfctl_show_timeouts(int dev)
pt.timeout = pf_timeouts[i].timeout;
if (ioctl(dev, DIOCGETTIMEOUT, &pt))
err(1, "DIOCGETTIMEOUT");
- printf("%-20s %10ds\n", pf_timeouts[i].name, pt.seconds);
+ printf("%-20s %10d", pf_timeouts[i].name, pt.seconds);
+ if (i >= PFTM_ADAPTIVE_START && i <= PFTM_ADAPTIVE_END)
+ printf(" states");
+ else
+ printf("s");
+ printf("\n");
}
return (0);
diff --git a/sbin/pfctl/pfctl_parser.c b/sbin/pfctl/pfctl_parser.c
index 6208994e8bf..225090d2c81 100644
--- a/sbin/pfctl/pfctl_parser.c
+++ b/sbin/pfctl/pfctl_parser.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pfctl_parser.c,v 1.151 2003/04/25 19:44:57 henning Exp $ */
+/* $OpenBSD: pfctl_parser.c,v 1.152 2003/05/12 01:25:31 dhartmei Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
@@ -190,6 +190,8 @@ const struct pf_timeout pf_timeouts[] = {
{ "other.multiple", PFTM_OTHER_MULTIPLE },
{ "frag", PFTM_FRAG },
{ "interval", PFTM_INTERVAL },
+ { "adaptive.start", PFTM_ADAPTIVE_START },
+ { "adaptive.end", PFTM_ADAPTIVE_END },
{ NULL, 0 }
};
diff --git a/share/man/man5/pf.conf.5 b/share/man/man5/pf.conf.5
index 870ddc00620..2bb9eaa0aa1 100644
--- a/share/man/man5/pf.conf.5
+++ b/share/man/man5/pf.conf.5
@@ -1,4 +1,4 @@
-.\" $OpenBSD: pf.conf.5,v 1.234 2003/05/11 20:46:11 frantzen Exp $
+.\" $OpenBSD: pf.conf.5,v 1.235 2003/05/12 01:25:32 dhartmei Exp $
.\"
.\" Copyright (c) 2002, Daniel Hartmeier
.\" All rights reserved.
@@ -290,12 +290,38 @@ Other protocols are handled similarly to UDP:
.It Ar other.multiple
.El
.Pp
+Timeout values can be reduced adaptively as the number of state table
+entries grows.
+.Pp
+.Bl -tag -width xxxx -compact
+.It Ar adaptive.start
+When the number of state entries exceeds this value, adaptive scaling
+begins.
+All timeout values are scaled linearly with factor
+(adaptive.end - number of states) / (adaptive.end - adaptive.start).
+.It Ar adaptive.end
+When reaching this number of state entries, all timeout values become
+zero, effectively purging all state entries immediately.
+This value is used to define the scale factor, it should not actually
+be reached (set a lower state limit, see below).
+.El
+.Pp
+These values can be defined both globally and for each rule.
+When used on a per-rule basis, the values relate to the number of
+states created by the rule, otherwise to the total number of
+states.
+.Pp
For example:
.Bd -literal -offset indent
-set timeout tcp.established 3600
-set timeout { tcp.opening 30, tcp.closing 900 }
+set timeout tcp.first 120
+set timeout tcp.established 86400
+set timeout { adaptive.start 6000, adaptive.end 12000 }
+set limit states 10000
.Ed
.Pp
+With 10500 state table entries, the timeout values are scaled to 25%
+(tcp.first 30, tcp.established 21600).
+.Pp
.It Ar set loginterface
Enable collection of packet and byte count statistics for the given interface.
These statistics can be viewed using
diff --git a/sys/net/pf.c b/sys/net/pf.c
index 6f2fcf66365..5b9ff9f03a9 100644
--- a/sys/net/pf.c
+++ b/sys/net/pf.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pf.c,v 1.345 2003/05/11 20:44:03 frantzen Exp $ */
+/* $OpenBSD: pf.c,v 1.346 2003/05/12 01:25:31 dhartmei Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
@@ -235,9 +235,6 @@ struct pf_pool_limit pf_pool_limits[PF_LIMIT_MAX] =
(s)->lan.addr.addr32[3] != (s)->gwy.addr.addr32[3])) || \
(s)->lan.port != (s)->gwy.port
-#define TIMEOUT(r,i) \
- ((r)->timeout[(i)] ? (r)->timeout[(i)] : pf_default_rule.timeout[(i)])
-
static __inline int pf_state_compare(struct pf_tree_node *,
struct pf_tree_node *);
@@ -437,6 +434,42 @@ pf_purge_timeout(void *arg)
timeout_add(to, pf_default_rule.timeout[PFTM_INTERVAL] * hz);
}
+u_int32_t
+pf_state_expires(const struct pf_state *state)
+{
+ u_int32_t timeout;
+ u_int32_t start;
+ u_int32_t end;
+ u_int32_t states;
+
+ /* handle all PFTM_* > PFTM_MAX here */
+ if (state->timeout == PFTM_PURGE)
+ return (time.tv_sec);
+ if (state->timeout == PFTM_UNTIL_PACKET)
+ return (0);
+ KASSERT(state->timeout < PFTM_MAX);
+ timeout = state->rule.ptr->timeout[state->timeout];
+ if (!timeout)
+ timeout = pf_default_rule.timeout[state->timeout];
+ start = state->rule.ptr->timeout[PFTM_ADAPTIVE_START];
+ if (start) {
+ end = state->rule.ptr->timeout[PFTM_ADAPTIVE_END];
+ states = state->rule.ptr->states;
+ } else {
+ start = pf_default_rule.timeout[PFTM_ADAPTIVE_START];
+ end = pf_default_rule.timeout[PFTM_ADAPTIVE_END];
+ states = pf_status.states;
+ }
+ if (end && states > start && start < end) {
+ if (states < end)
+ return (state->expire + timeout * (end - states) /
+ (end - start));
+ else
+ return (time.tv_sec);
+ }
+ return (state->expire + timeout);
+}
+
void
pf_purge_expired_states(void)
{
@@ -446,7 +479,7 @@ pf_purge_expired_states(void)
for (cur = RB_MIN(pf_state_tree, &tree_ext_gwy); cur; cur = next) {
next = RB_NEXT(pf_state_tree, &tree_ext_gwy, cur);
- if (cur->state->expire <= (unsigned)time.tv_sec) {
+ if (pf_state_expires(cur->state) <= time.tv_sec) {
RB_REMOVE(pf_state_tree, &tree_ext_gwy, cur);
/* Need this key's peer (in the other tree) */
@@ -2122,7 +2155,8 @@ pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction,
s->src.state = TCPS_SYN_SENT;
s->dst.state = TCPS_CLOSED;
s->creation = time.tv_sec;
- s->expire = s->creation + TIMEOUT(r, PFTM_TCP_FIRST_PACKET);
+ s->expire = time.tv_sec;
+ s->timeout = PFTM_TCP_FIRST_PACKET;
s->packets = 1;
s->bytes = pd->tot_len;
@@ -2349,7 +2383,8 @@ pf_test_udp(struct pf_rule **rm, struct pf_state **sm, int direction,
s->dst.max_win = 0;
s->dst.state = PFUDPS_NO_TRAFFIC;
s->creation = time.tv_sec;
- s->expire = s->creation + TIMEOUT(r, PFTM_UDP_FIRST_PACKET);
+ s->expire = time.tv_sec;
+ s->timeout = PFTM_UDP_FIRST_PACKET;
s->packets = 1;
s->bytes = pd->tot_len;
if (pf_insert_state(s)) {
@@ -2583,7 +2618,8 @@ pf_test_icmp(struct pf_rule **rm, struct pf_state **sm, int direction,
s->dst.max_win = 0;
s->dst.state = 0;
s->creation = time.tv_sec;
- s->expire = s->creation + TIMEOUT(r, PFTM_ICMP_FIRST_PACKET);
+ s->expire = time.tv_sec;
+ s->timeout = PFTM_ICMP_FIRST_PACKET;
s->packets = 1;
s->bytes = pd->tot_len;
if (pf_insert_state(s)) {
@@ -2800,7 +2836,8 @@ pf_test_other(struct pf_rule **rm, struct pf_state **sm, int direction,
s->dst.max_win = 0;
s->dst.state = PFOTHERS_NO_TRAFFIC;
s->creation = time.tv_sec;
- s->expire = s->creation + TIMEOUT(r, PFTM_OTHER_FIRST_PACKET);
+ s->expire = time.tv_sec;
+ s->timeout = PFTM_OTHER_FIRST_PACKET;
s->packets = 1;
s->bytes = pd->tot_len;
if (pf_insert_state(s)) {
@@ -3050,25 +3087,21 @@ pf_test_state_tcp(struct pf_state **state, int direction, struct ifnet *ifp,
src->state = dst->state = TCPS_TIME_WAIT;
/* update expire time */
+ (*state)->expire = time.tv_sec;
if (src->state >= TCPS_FIN_WAIT_2 &&
dst->state >= TCPS_FIN_WAIT_2)
- (*state)->expire = time.tv_sec +
- TIMEOUT((*state)->rule.ptr, PFTM_TCP_CLOSED);
+ (*state)->timeout = PFTM_TCP_CLOSED;
else if (src->state >= TCPS_FIN_WAIT_2 ||
dst->state >= TCPS_FIN_WAIT_2)
- (*state)->expire = time.tv_sec +
- TIMEOUT((*state)->rule.ptr, PFTM_TCP_FIN_WAIT);
+ (*state)->timeout = PFTM_TCP_FIN_WAIT;
else if (src->state < TCPS_ESTABLISHED ||
dst->state < TCPS_ESTABLISHED)
- (*state)->expire = time.tv_sec +
- TIMEOUT((*state)->rule.ptr, PFTM_TCP_OPENING);
+ (*state)->timeout = PFTM_TCP_OPENING;
else if (src->state >= TCPS_CLOSING ||
dst->state >= TCPS_CLOSING)
- (*state)->expire = time.tv_sec +
- TIMEOUT((*state)->rule.ptr, PFTM_TCP_CLOSING);
+ (*state)->timeout = PFTM_TCP_CLOSING;
else
- (*state)->expire = time.tv_sec +
- TIMEOUT((*state)->rule.ptr, PFTM_TCP_ESTABLISHED);
+ (*state)->timeout = PFTM_TCP_ESTABLISHED;
/* Fall through to PASS packet */
@@ -3236,12 +3269,11 @@ pf_test_state_udp(struct pf_state **state, int direction, struct ifnet *ifp,
dst->state = PFUDPS_MULTIPLE;
/* update expire time */
+ (*state)->expire = time.tv_sec;
if (src->state == PFUDPS_MULTIPLE && dst->state == PFUDPS_MULTIPLE)
- (*state)->expire = time.tv_sec +
- TIMEOUT((*state)->rule.ptr, PFTM_UDP_MULTIPLE);
+ (*state)->timeout = PFTM_UDP_MULTIPLE;
else
- (*state)->expire = time.tv_sec +
- TIMEOUT((*state)->rule.ptr, PFTM_UDP_SINGLE);
+ (*state)->timeout = PFTM_UDP_SINGLE;
/* translate source/destination address, if necessary */
if (STATE_TRANSLATE(*state)) {
@@ -3327,8 +3359,8 @@ pf_test_state_icmp(struct pf_state **state, int direction, struct ifnet *ifp,
(*state)->packets++;
(*state)->bytes += pd->tot_len;
- (*state)->expire = time.tv_sec +
- TIMEOUT((*state)->rule.ptr, PFTM_ICMP_ERROR_REPLY);
+ (*state)->expire = time.tv_sec;
+ (*state)->timeout = PFTM_ICMP_ERROR_REPLY;
/* translate source/destination address, if needed */
if (PF_ANEQ(&(*state)->lan.addr, &(*state)->gwy.addr, pd->af)) {
@@ -3822,12 +3854,11 @@ pf_test_state_other(struct pf_state **state, int direction, struct ifnet *ifp,
dst->state = PFOTHERS_MULTIPLE;
/* update expire time */
+ (*state)->expire = time.tv_sec;
if (src->state == PFOTHERS_MULTIPLE && dst->state == PFOTHERS_MULTIPLE)
- (*state)->expire = time.tv_sec +
- TIMEOUT((*state)->rule.ptr, PFTM_OTHER_MULTIPLE);
+ (*state)->timeout = PFTM_OTHER_MULTIPLE;
else
- (*state)->expire = time.tv_sec +
- TIMEOUT((*state)->rule.ptr, PFTM_OTHER_SINGLE);
+ (*state)->timeout = PFTM_OTHER_SINGLE;
/* translate source/destination address, if necessary */
if (STATE_TRANSLATE(*state)) {
diff --git a/sys/net/pf_ioctl.c b/sys/net/pf_ioctl.c
index 4f2a372089e..2cf188aedfc 100644
--- a/sys/net/pf_ioctl.c
+++ b/sys/net/pf_ioctl.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pf_ioctl.c,v 1.61 2003/05/12 00:02:32 henning Exp $ */
+/* $OpenBSD: pf_ioctl.c,v 1.62 2003/05/12 01:25:31 dhartmei Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
@@ -929,7 +929,7 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p)
s = splsoftnet();
RB_FOREACH(n, pf_state_tree, &tree_ext_gwy)
- n->state->expire = 0;
+ n->state->timeout = PFTM_PURGE;
pf_purge_expired_states();
pf_status.states = 0;
splx(s);
@@ -963,7 +963,7 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p)
pf_match_port(psk->psk_dst.port_op,
psk->psk_dst.port[0], psk->psk_dst.port[1],
st->ext.port))) {
- st->expire = 0;
+ st->timeout = PFTM_PURGE;
killed++;
}
}
@@ -977,6 +977,11 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p)
struct pfioc_state *ps = (struct pfioc_state *)addr;
struct pf_state *state;
+ if (ps->state.timeout >= PFTM_MAX &&
+ ps->state.timeout != PFTM_UNTIL_PACKET) {
+ error = EINVAL;
+ break;
+ }
state = pool_get(&pf_state_pl, PR_NOWAIT);
if (state == NULL) {
error = ENOMEM;
@@ -989,7 +994,6 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p)
state->anchor.ptr = NULL;
state->rt_ifp = NULL;
state->creation = time.tv_sec;
- state->expire += state->creation;
state->packets = 0;
state->bytes = 0;
if (pf_insert_state(state)) {
@@ -1004,7 +1008,6 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p)
struct pfioc_state *ps = (struct pfioc_state *)addr;
struct pf_tree_node *n;
u_int32_t nr;
- int secs;
nr = 0;
s = splsoftnet();
@@ -1025,12 +1028,11 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p)
ps->state.anchor.nr = (n->state->anchor.ptr == NULL) ?
-1 : n->state->anchor.ptr->nr;
splx(s);
- secs = time.tv_sec;
- ps->state.creation = secs - ps->state.creation;
- if (ps->state.expire <= (unsigned)secs)
- ps->state.expire = 0;
+ ps->state.expire = pf_state_expires(n->state);
+ if (ps->state.expire > time.tv_sec)
+ ps->state.expire -= time.tv_sec;
else
- ps->state.expire -= secs;
+ ps->state.expire = 0;
break;
}
@@ -1065,10 +1067,11 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p)
pstore.anchor.nr = (n->state->anchor.ptr == NULL) ?
-1 : n->state->anchor.ptr->nr;
pstore.creation = secs - pstore.creation;
- if (pstore.expire <= (unsigned)secs)
- pstore.expire = 0;
- else
+ pstore.expire = pf_state_expires(n->state);
+ if (pstore.expire > secs)
pstore.expire -= secs;
+ else
+ pstore.expire = 0;
error = copyout(&pstore, p, sizeof(*p));
if (error) {
splx(s);
diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
index 17adab061bd..4e9f2490eee 100644
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: pfvar.h,v 1.143 2003/05/11 20:44:03 frantzen Exp $ */
+/* $OpenBSD: pfvar.h,v 1.144 2003/05/12 01:25:31 dhartmei Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
@@ -56,7 +56,9 @@ enum { PFTM_TCP_FIRST_PACKET, PFTM_TCP_OPENING, PFTM_TCP_ESTABLISHED,
PFTM_UDP_FIRST_PACKET, PFTM_UDP_SINGLE, PFTM_UDP_MULTIPLE,
PFTM_ICMP_FIRST_PACKET, PFTM_ICMP_ERROR_REPLY,
PFTM_OTHER_FIRST_PACKET, PFTM_OTHER_SINGLE,
- PFTM_OTHER_MULTIPLE, PFTM_FRAG, PFTM_INTERVAL, PFTM_MAX };
+ PFTM_OTHER_MULTIPLE, PFTM_FRAG, PFTM_INTERVAL,
+ PFTM_ADAPTIVE_START, PFTM_ADAPTIVE_END, PFTM_MAX,
+ PFTM_PURGE, PFTM_UNTIL_PACKET };
enum { PF_NOPFROUTE, PF_FASTROUTE, PF_ROUTETO, PF_DUPTO, PF_REPLYTO };
enum { PF_LIMIT_STATES, PF_LIMIT_FRAGS, PF_LIMIT_MAX };
#define PF_POOL_IDMASK 0x0f
@@ -442,7 +444,8 @@ struct pf_state {
u_int8_t direction;
u_int8_t log;
u_int8_t allow_opts;
- u_int8_t pad[3];
+ u_int8_t timeout;
+ u_int8_t pad[2];
};
struct pf_tree_node {
@@ -1026,6 +1029,8 @@ int pf_normalize_tcp_init(struct mbuf *, struct pf_pdesc *, struct tcphdr *,
struct pf_state_peer *, struct pf_state_peer *);
int pf_normalize_tcp_stateful(struct mbuf *, struct pf_pdesc *, u_short *,
struct tcphdr *, struct pf_state_peer *, struct pf_state_peer *);
+u_int32_t
+ pf_state_expires(const struct pf_state *);
void pf_purge_expired_fragments(void);
int pf_routable(struct pf_addr *addr, sa_family_t af);
void pfr_initialize(void);