aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordavem <davem>2002-01-15 08:53:21 +0000
committerdavem <davem>2002-01-15 08:53:21 +0000
commit6373d1582fba1a9562f778852407b34dbbeaf75f (patch)
tree54ab054327e3859f7d922e685a38fe35d7173109
parent1e812cd6f27d0db6dbef28a663f06569e8506345 (diff)
downloadnetdev-vger-cvs-6373d1582fba1a9562f778852407b34dbbeaf75f.tar.gz
Fix destroyme timer handling, for real.
From Paul Russell.
-rw-r--r--net/ipv4/netfilter/ip_fw_compat_redir.c52
1 files changed, 34 insertions, 18 deletions
diff --git a/net/ipv4/netfilter/ip_fw_compat_redir.c b/net/ipv4/netfilter/ip_fw_compat_redir.c
index f55680e7e..6760147c4 100644
--- a/net/ipv4/netfilter/ip_fw_compat_redir.c
+++ b/net/ipv4/netfilter/ip_fw_compat_redir.c
@@ -20,6 +20,9 @@
#include <linux/netfilter_ipv4/lockhelp.h>
+/* Very simple timeout pushed back by each packet */
+#define REDIR_TIMEOUT (240*HZ)
+
static DECLARE_LOCK(redir_lock);
#define ASSERT_READ_LOCK(x) MUST_BE_LOCKED(&redir_lock)
#define ASSERT_WRITE_LOCK(x) MUST_BE_LOCKED(&redir_lock)
@@ -150,6 +153,14 @@ static void do_tcp_unredir(struct sk_buff *skb, struct redir *redir)
skb->nfcache |= NFC_ALTERED;
}
+static void destroyme(unsigned long me)
+{
+ LOCK_BH(&redir_lock);
+ LIST_DELETE(&redirs, (struct redir *)me);
+ UNLOCK_BH(&redir_lock);
+ kfree((struct redir *)me);
+}
+
/* REDIRECT a packet. */
unsigned int
do_redirect(struct sk_buff *skb,
@@ -172,6 +183,10 @@ do_redirect(struct sk_buff *skb,
struct udphdr *udph = (struct udphdr *)((u_int32_t *)iph
+ iph->ihl);
+ /* Must have whole header */
+ if (skb->len < iph->ihl*4 + sizeof(*udph))
+ return NF_DROP;
+
if (udph->check) /* 0 is a special case meaning no checksum */
udph->check = cheat_check(~iph->daddr, newdst,
cheat_check(udph->dest ^ 0xFFFF,
@@ -191,6 +206,10 @@ do_redirect(struct sk_buff *skb,
struct redir *redir;
int ret;
+ /* Must have whole header */
+ if (skb->len < iph->ihl*4 + sizeof(*tcph))
+ return NF_DROP;
+
DEBUGP("Doing tcp redirect. %08X:%u %08X:%u -> %08X:%u\n",
iph->saddr, tcph->source, iph->daddr, tcph->dest,
newdst, redirpt);
@@ -206,7 +225,9 @@ do_redirect(struct sk_buff *skb,
}
list_prepend(&redirs, redir);
init_timer(&redir->destroyme);
- redir->destroyme.expires = jiffies + 75*HZ;
+ redir->destroyme.function = destroyme;
+ redir->destroyme.data = (unsigned long)redir;
+ redir->destroyme.expires = jiffies + REDIR_TIMEOUT;
add_timer(&redir->destroyme);
}
/* In case mangling has changed, rewrite this part. */
@@ -227,13 +248,6 @@ do_redirect(struct sk_buff *skb,
}
}
-static void destroyme(unsigned long me)
-{
- LOCK_BH(&redir_lock);
- LIST_DELETE(&redirs, (struct redir *)me);
- UNLOCK_BH(&redir_lock);
-}
-
/* Incoming packet: is it a reply to a masqueraded connection, or
part of an already-redirected TCP connection? */
void
@@ -247,16 +261,17 @@ check_for_redirect(struct sk_buff *skb)
if (iph->protocol != IPPROTO_TCP)
return;
+ /* Must have whole header */
+ if (skb->len < iph->ihl*4 + sizeof(*tcph))
+ return;
+
LOCK_BH(&redir_lock);
redir = find_redir(iph->saddr, iph->daddr, tcph->source, tcph->dest);
if (redir) {
DEBUGP("Doing tcp redirect again.\n");
do_tcp_redir(skb, redir);
- if (tcph->rst || tcph->fin) {
- redir->destroyme.function = destroyme;
- redir->destroyme.data = (unsigned long)redir;
- mod_timer(&redir->destroyme, 75*HZ);
- }
+ if (del_timer(&redir->destroyme))
+ add_timer(&redir->destroyme, jiffies + REDIR_TIMEOUT);
}
UNLOCK_BH(&redir_lock);
}
@@ -272,16 +287,17 @@ check_for_unredirect(struct sk_buff *skb)
if (iph->protocol != IPPROTO_TCP)
return;
+ /* Must have whole header */
+ if (skb->len < iph->ihl*4 + sizeof(*tcph))
+ return;
+
LOCK_BH(&redir_lock);
redir = find_unredir(iph->saddr, iph->daddr, tcph->source, tcph->dest);
if (redir) {
DEBUGP("Doing tcp unredirect.\n");
do_tcp_unredir(skb, redir);
- if (tcph->rst || tcph->fin) {
- redir->destroyme.function = destroyme;
- redir->destroyme.data = (unsigned long)redir;
- mod_timer(&redir->destroyme, 75*HZ);
- }
+ if (del_timer(&redir->destroyme))
+ add_timer(&redir->destroyme, jiffies + REDIR_TIMEOUT);
}
UNLOCK_BH(&redir_lock);
}