summaryrefslogtreecommitdiffstats
path: root/net/ipv4
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv4')
-rw-r--r--net/ipv4/netfilter/Makefile4
-rw-r--r--net/ipv4/netfilter/ip_conntrack_core.c70
-rw-r--r--net/ipv4/netfilter/ip_conntrack_ftp.c24
-rw-r--r--net/ipv4/netfilter/ip_conntrack_proto_tcp.c52
-rw-r--r--net/ipv4/netfilter/ip_conntrack_proto_udp.c6
-rw-r--r--net/ipv4/netfilter/ip_conntrack_standalone.c26
-rw-r--r--net/ipv4/netfilter/ip_fw_compat.c4
-rw-r--r--net/ipv4/netfilter/ip_fw_compat_redir.c3
-rw-r--r--net/ipv4/netfilter/ip_nat_core.c22
-rw-r--r--net/ipv4/netfilter/ip_nat_ftp.c58
-rw-r--r--net/ipv4/netfilter/ip_nat_proto_udp.c3
-rw-r--r--net/ipv4/netfilter/ip_queue.c48
-rw-r--r--net/ipv4/netfilter/ip_tables.c6
13 files changed, 200 insertions, 126 deletions
diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile
index db276076a..bc892e7db 100644
--- a/net/ipv4/netfilter/Makefile
+++ b/net/ipv4/netfilter/Makefile
@@ -30,7 +30,7 @@ else
endif
ifeq ($(CONFIG_IP_NF_FTP),y)
-O_OBJS += ip_conntrack_ftp.o
+OX_OBJS += ip_conntrack_ftp.o
else
ifeq ($(CONFIG_IP_NF_FTP),m)
MX_OBJS += ip_conntrack_ftp.o
@@ -38,7 +38,7 @@ else
endif
ifeq ($(CONFIG_IP_NF_IPTABLES),y)
-O_OBJS += ip_tables.o
+OX_OBJS += ip_tables.o
else
ifeq ($(CONFIG_IP_NF_IPTABLES),m)
MX_OBJS += ip_tables.o
diff --git a/net/ipv4/netfilter/ip_conntrack_core.c b/net/ipv4/netfilter/ip_conntrack_core.c
index 14ebb46e1..da3f97821 100644
--- a/net/ipv4/netfilter/ip_conntrack_core.c
+++ b/net/ipv4/netfilter/ip_conntrack_core.c
@@ -394,7 +394,7 @@ static inline int unreplied(const struct ip_conntrack_tuple_hash *i)
{
/* Unconfirmed connections either really fresh or transitory
anyway */
- if (!(i->ctrack->status & IPS_SEEN_REPLY)
+ if (!(i->ctrack->status & IPS_ASSURED)
&& (i->ctrack->status & IPS_CONFIRMED))
return 1;
return 0;
@@ -426,17 +426,14 @@ static int early_drop(struct list_head *chain)
static inline int helper_cmp(const struct ip_conntrack_helper *i,
const struct ip_conntrack_tuple *rtuple)
{
- return i->will_help(rtuple);
+ return ip_ct_tuple_mask_cmp(rtuple, &i->tuple, &i->mask);
}
-/* Compare all but src per-proto part. */
-static int expect_cmp(const struct ip_conntrack_expect *i,
- const struct ip_conntrack_tuple *tuple)
+/* Compare parts depending on mask. */
+static inline int expect_cmp(const struct ip_conntrack_expect *i,
+ const struct ip_conntrack_tuple *tuple)
{
- return (tuple->src.ip == i->tuple.src.ip
- && tuple->dst.ip == i->tuple.dst.ip
- && tuple->dst.u.all == i->tuple.dst.u.all
- && tuple->dst.protonum == i->tuple.dst.protonum);
+ return ip_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask);
}
/* Allocate a new conntrack; we set everything up, then grab write
@@ -542,6 +539,8 @@ init_conntrack(const struct ip_conntrack_tuple *tuple,
/* Update skb to refer to this connection */
skb->nfct = &conntrack->infos[ctinfo];
+ if (expected && expected->expectfn)
+ expected->expectfn(conntrack);
return 1;
}
@@ -722,26 +721,63 @@ int invert_tuplepr(struct ip_conntrack_tuple *inverse,
return invert_tuple(inverse, orig, find_proto(orig->dst.protonum));
}
+static void unexpect_related(struct ip_conntrack *related_to)
+{
+ MUST_BE_WRITE_LOCKED(&ip_conntrack_lock);
+ list_del(&related_to->expected.list);
+ related_to->expected.expectant = NULL;
+}
+
+/* Would two expected things clash? */
+static inline int expect_clash(const struct ip_conntrack_expect *i,
+ const struct ip_conntrack_expect *new)
+{
+ /* Part covered by intersection of masks must be unequal,
+ otherwise they clash */
+ struct ip_conntrack_tuple intersect_mask
+ = { { i->mask.src.ip & new->mask.src.ip,
+ { i->mask.src.u.all & new->mask.src.u.all } },
+ { i->mask.dst.ip & new->mask.dst.ip,
+ { i->mask.dst.u.all & new->mask.dst.u.all },
+ i->mask.dst.protonum & new->mask.dst.protonum } };
+
+ return ip_ct_tuple_mask_cmp(&i->tuple, &new->tuple, &intersect_mask);
+}
+
/* Add a related connection. */
int ip_conntrack_expect_related(struct ip_conntrack *related_to,
- const struct ip_conntrack_tuple *tuple)
+ const struct ip_conntrack_tuple *tuple,
+ const struct ip_conntrack_tuple *mask,
+ int (*expectfn)(struct ip_conntrack *))
{
WRITE_LOCK(&ip_conntrack_lock);
+ if (related_to->expected.expectant)
+ unexpect_related(related_to);
+
related_to->expected.tuple = *tuple;
+ related_to->expected.mask = *mask;
+ related_to->expected.expectfn = expectfn;
- if (!related_to->expected.expectant) {
- list_prepend(&expect_list, &related_to->expected);
- related_to->expected.expectant = related_to;
- } else {
- IP_NF_ASSERT(list_inlist(&expect_list, &related_to->expected));
- IP_NF_ASSERT(related_to->expected.expectant
- == related_to);
+ if (LIST_FIND(&expect_list, expect_clash,
+ struct ip_conntrack_expect *, &related_to->expected)) {
+ WRITE_UNLOCK(&ip_conntrack_lock);
+ return -EBUSY;
}
+
+ list_prepend(&expect_list, &related_to->expected);
+ related_to->expected.expectant = related_to;
WRITE_UNLOCK(&ip_conntrack_lock);
return 0;
}
+void ip_conntrack_unexpect_related(struct ip_conntrack *related_to)
+{
+ WRITE_LOCK(&ip_conntrack_lock);
+ unexpect_related(related_to);
+ WRITE_UNLOCK(&ip_conntrack_lock);
+}
+
/* Alter reply tuple (maybe alter helper). If it's already taken,
return 0 and don't do alteration. */
int ip_conntrack_alter_reply(struct ip_conntrack *conntrack,
diff --git a/net/ipv4/netfilter/ip_conntrack_ftp.c b/net/ipv4/netfilter/ip_conntrack_ftp.c
index 2e7547c38..ce0023ec3 100644
--- a/net/ipv4/netfilter/ip_conntrack_ftp.c
+++ b/net/ipv4/netfilter/ip_conntrack_ftp.c
@@ -121,7 +121,7 @@ static int help(const struct iphdr *iph, size_t len,
u_int32_t array[6] = { 0 };
int dir = CTINFO2DIR(ctinfo);
unsigned int matchlen, matchoff;
- struct ip_conntrack_tuple t;
+ struct ip_conntrack_tuple t, mask;
struct ip_ct_ftp *info = &ct->help.ct_ftp_info;
/* Until there's been traffic both ways, don't look in packets. */
@@ -221,22 +221,21 @@ static int help(const struct iphdr *iph, size_t len,
| (array[2] << 8) | array[3]),
{ htons(array[4] << 8 | array[5]) },
IPPROTO_TCP }});
- ip_conntrack_expect_related(ct, &t);
+ mask = ((struct ip_conntrack_tuple)
+ { { 0xFFFFFFFF, { 0 } },
+ { 0xFFFFFFFF, { 0xFFFF }, 0xFFFF }});
+ /* Ignore failure; should only happen with NAT */
+ ip_conntrack_expect_related(ct, &t, &mask, NULL);
UNLOCK_BH(&ip_ftp_lock);
return NF_ACCEPT;
}
-/* Returns TRUE if it wants to help this connection (tuple is the
- tuple of REPLY packets from server). */
-static int ftp_will_help(const struct ip_conntrack_tuple *rtuple)
-{
- return (rtuple->dst.protonum == IPPROTO_TCP
- && rtuple->src.u.tcp.port == __constant_htons(21));
-}
-
static struct ip_conntrack_helper ftp = { { NULL, NULL },
- ftp_will_help,
+ { { 0, { __constant_htons(21) } },
+ { 0, { 0 }, IPPROTO_TCP } },
+ { { 0, { 0xFFFF } },
+ { 0, { 0 }, 0xFFFF } },
help };
static int __init init(void)
@@ -249,5 +248,8 @@ static void __exit fini(void)
ip_conntrack_helper_unregister(&ftp);
}
+EXPORT_SYMBOL(ip_ftp_lock);
+EXPORT_SYMBOL(ip_conntrack_ftp);
+
module_init(init);
module_exit(fini);
diff --git a/net/ipv4/netfilter/ip_conntrack_proto_tcp.c b/net/ipv4/netfilter/ip_conntrack_proto_tcp.c
index f9375d5a5..0aa8426de 100644
--- a/net/ipv4/netfilter/ip_conntrack_proto_tcp.c
+++ b/net/ipv4/netfilter/ip_conntrack_proto_tcp.c
@@ -17,7 +17,7 @@
#define DEBUGP(format, args...)
#endif
-/* Protects conntrack->proto.tcp_state */
+/* Protects conntrack->proto.tcp */
static DECLARE_RWLOCK(tcp_lock);
/* FIXME: Examine ipfilter's timeouts and conntrack transitions more
@@ -27,19 +27,6 @@ static DECLARE_RWLOCK(tcp_lock);
from) nor ipfilter do it exactly right. A new conntrack machine taking
into account packet loss (which creates uncertainty as to exactly
the conntrack of the connection) is required. RSN. --RR */
-enum tcp_conntrack {
- TCP_CONNTRACK_NONE,
- TCP_CONNTRACK_ESTABLISHED,
- TCP_CONNTRACK_SYN_SENT,
- TCP_CONNTRACK_SYN_RECV,
- TCP_CONNTRACK_FIN_WAIT,
- TCP_CONNTRACK_TIME_WAIT,
- TCP_CONNTRACK_CLOSE,
- TCP_CONNTRACK_CLOSE_WAIT,
- TCP_CONNTRACK_LAST_ACK,
- TCP_CONNTRACK_LISTEN,
- TCP_CONNTRACK_MAX
-};
static const char *tcp_conntrack_names[] = {
"NONE",
@@ -89,19 +76,19 @@ static enum tcp_conntrack tcp_conntracks[2][5][TCP_CONNTRACK_MAX] = {
{
/* ORIGINAL */
/* sNO, sES, sSS, sSR, sFW, sTW, sCL, sCW, sLA, sLI */
-/*syn*/ {sSS, sES, sSS, sES, sSS, sSS, sSS, sSS, sSS, sLI },
+/*syn*/ {sSS, sES, sSS, sSR, sSS, sSS, sSS, sSS, sSS, sLI },
/*fin*/ {sTW, sFW, sSS, sTW, sFW, sTW, sCL, sTW, sLA, sLI },
-/*ack*/ {sES, sES, sSS, sSR, sFW, sTW, sCL, sCW, sLA, sES },
+/*ack*/ {sES, sES, sSS, sES, sFW, sTW, sCL, sCW, sLA, sES },
/*rst*/ {sCL, sCL, sSS, sCL, sCL, sTW, sCL, sCL, sCL, sCL },
/*none*/{sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV }
},
{
/* REPLY */
/* sNO, sES, sSS, sSR, sFW, sTW, sCL, sCW, sLA, sLI */
-/*syn*/ {sSR, sES, sES, sSR, sSR, sSR, sSR, sSR, sSR, sSR },
+/*syn*/ {sSR, sES, sSR, sSR, sSR, sSR, sSR, sSR, sSR, sSR },
/*fin*/ {sCL, sCW, sSS, sTW, sTW, sTW, sCL, sCW, sLA, sLI },
/*ack*/ {sCL, sES, sSS, sSR, sFW, sTW, sCL, sCW, sCL, sLI },
-/*rst*/ {sCL, sCL, sCL, sSR, sCL, sCL, sCL, sCL, sLA, sLI },
+/*rst*/ {sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sLA, sLI },
/*none*/{sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV }
}
};
@@ -141,7 +128,7 @@ static unsigned int tcp_print_conntrack(char *buffer,
enum tcp_conntrack state;
READ_LOCK(&tcp_lock);
- state = conntrack->proto.tcp_state;
+ state = conntrack->proto.tcp.state;
READ_UNLOCK(&tcp_lock);
return sprintf(buffer, "%s ", tcp_conntrack_names[state]);
@@ -172,7 +159,7 @@ static int tcp_packet(struct ip_conntrack *conntrack,
}
WRITE_LOCK(&tcp_lock);
- oldtcpstate = conntrack->proto.tcp_state;
+ oldtcpstate = conntrack->proto.tcp.state;
newconntrack
= tcp_conntracks
[CTINFO2DIR(ctinfo)]
@@ -182,12 +169,19 @@ static int tcp_packet(struct ip_conntrack *conntrack,
if (newconntrack == TCP_CONNTRACK_MAX) {
DEBUGP("ip_conntrack_tcp: Invalid dir=%i index=%u conntrack=%u\n",
CTINFO2DIR(ctinfo), get_conntrack_index(tcph),
- conntrack->proto.tcp_state);
+ conntrack->proto.tcp.state);
WRITE_UNLOCK(&tcp_lock);
return -1;
}
- conntrack->proto.tcp_state = newconntrack;
+ conntrack->proto.tcp.state = newconntrack;
+
+ /* Poor man's window tracking: record SYN/ACK for handshake check */
+ if (oldtcpstate == TCP_CONNTRACK_SYN_SENT
+ && CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY
+ && tcph->syn && tcph->ack)
+ conntrack->proto.tcp.handshake_ack
+ = htonl(ntohl(tcph->seq) + 1);
WRITE_UNLOCK(&tcp_lock);
/* If only reply is a RST, we can consider ourselves not to
@@ -197,8 +191,16 @@ static int tcp_packet(struct ip_conntrack *conntrack,
if (!(conntrack->status & IPS_SEEN_REPLY) && tcph->rst) {
if (del_timer(&conntrack->timeout))
conntrack->timeout.function((unsigned long)conntrack);
- } else
+ } else {
+ /* Set ASSURED if we see see valid ack in ESTABLISHED after SYN_RECV */
+ if (oldtcpstate == TCP_CONNTRACK_SYN_RECV
+ && CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL
+ && tcph->ack && !tcph->syn
+ && tcph->ack_seq == conntrack->proto.tcp.handshake_ack)
+ set_bit(IPS_ASSURED_BIT, &conntrack->status);
+
ip_ct_refresh(conntrack, tcp_timeouts[newconntrack]);
+ }
return NF_ACCEPT;
}
@@ -221,8 +223,8 @@ static unsigned long tcp_new(struct ip_conntrack *conntrack,
return 0;
}
- conntrack->proto.tcp_state = newconntrack;
- return tcp_timeouts[conntrack->proto.tcp_state];
+ conntrack->proto.tcp.state = newconntrack;
+ return tcp_timeouts[conntrack->proto.tcp.state];
}
struct ip_conntrack_protocol ip_conntrack_protocol_tcp
diff --git a/net/ipv4/netfilter/ip_conntrack_proto_udp.c b/net/ipv4/netfilter/ip_conntrack_proto_udp.c
index 0a65a7a98..644a86a13 100644
--- a/net/ipv4/netfilter/ip_conntrack_proto_udp.c
+++ b/net/ipv4/netfilter/ip_conntrack_proto_udp.c
@@ -51,9 +51,11 @@ static int udp_packet(struct ip_conntrack *conntrack,
{
/* If we've seen traffic both ways, this is some kind of UDP
stream. Extend timeout. */
- if (conntrack->status & IPS_SEEN_REPLY)
+ if (conntrack->status & IPS_SEEN_REPLY) {
ip_ct_refresh(conntrack, UDP_STREAM_TIMEOUT);
- else
+ /* Also, more likely to be important, and not a probe */
+ set_bit(IPS_ASSURED_BIT, &conntrack->status);
+ } else
ip_ct_refresh(conntrack, UDP_TIMEOUT);
return NF_ACCEPT;
diff --git a/net/ipv4/netfilter/ip_conntrack_standalone.c b/net/ipv4/netfilter/ip_conntrack_standalone.c
index 486683bec..20e4aa426 100644
--- a/net/ipv4/netfilter/ip_conntrack_standalone.c
+++ b/net/ipv4/netfilter/ip_conntrack_standalone.c
@@ -86,19 +86,19 @@ print_conntrack(char *buffer, const struct ip_conntrack *conntrack)
len += print_tuple(buffer + len,
&conntrack->tuplehash[IP_CT_DIR_REPLY].tuple,
proto);
-#if 0
+ if (conntrack->status & IPS_ASSURED)
+ len += sprintf(buffer + len, "[ASSURED] ");
if (!(conntrack->status & IPS_CONFIRMED))
len += sprintf(buffer + len, "[UNCONFIRMED] ");
len += sprintf(buffer + len, "use=%u ",
atomic_read(&conntrack->ct_general.use));
-#endif
len += sprintf(buffer + len, "\n");
return len;
}
/* Returns true when finished. */
-static int
+static inline int
conntrack_iterate(const struct ip_conntrack_tuple_hash *hash,
char *buffer, off_t offset, off_t *upto,
unsigned int *len, unsigned int maxlen)
@@ -169,14 +169,18 @@ static unsigned int ip_confirm(unsigned int hooknum,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
- /* We've seen it coming out the other side: confirm (only if
- new packet: REJECT can generate TCP RESET response, or ICMP
- errors) */
+ /* We've seen it coming out the other side: confirm. Beware
+ REJECT generating TCP RESET response (IP_CT_REPLY), or ICMP
+ errors (IP_CT_REPLY + IP_CT_RELATED). But new expected
+ connections must be confirmed as well (eg. ftp data,
+ IP_CT_RELATED). */
if ((*pskb)->nfct) {
struct ip_conntrack *ct
= (struct ip_conntrack *)(*pskb)->nfct->master;
/* ctinfo is the index of the nfct inside the conntrack */
- if ((*pskb)->nfct - ct->infos == IP_CT_NEW
+ enum ip_conntrack_info ctinfo = (*pskb)->nfct - ct->infos;
+
+ if ((ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED)
&& !(ct->status & IPS_CONFIRMED))
ip_conntrack_confirm(ct);
}
@@ -192,13 +196,7 @@ static unsigned int ip_refrag(unsigned int hooknum,
struct rtable *rt = (struct rtable *)(*pskb)->dst;
/* We've seen it coming out the other side: confirm */
- if ((*pskb)->nfct) {
- struct ip_conntrack *ct
- = (struct ip_conntrack *)(*pskb)->nfct->master;
- if ((*pskb)->nfct - ct->infos == IP_CT_NEW
- && !(ct->status & IPS_CONFIRMED))
- ip_conntrack_confirm(ct);
- }
+ ip_confirm(hooknum, pskb, in, out, okfn);
/* Local packets are never produced too large for their
interface. We degfragment them at LOCAL_OUT, however,
diff --git a/net/ipv4/netfilter/ip_fw_compat.c b/net/ipv4/netfilter/ip_fw_compat.c
index 3c8c2e851..6bb1b240c 100644
--- a/net/ipv4/netfilter/ip_fw_compat.c
+++ b/net/ipv4/netfilter/ip_fw_compat.c
@@ -70,8 +70,10 @@ confirm_connection(struct sk_buff *skb)
if (skb->nfct) {
struct ip_conntrack *ct
= (struct ip_conntrack *)skb->nfct->master;
+ /* ctinfo is the index of the nfct inside the conntrack */
+ enum ip_conntrack_info ctinfo = skb->nfct - ct->infos;
- if (skb->nfct - ct->infos == IP_CT_NEW
+ if ((ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED)
&& !(ct->status & IPS_CONFIRMED))
ip_conntrack_confirm(ct);
}
diff --git a/net/ipv4/netfilter/ip_fw_compat_redir.c b/net/ipv4/netfilter/ip_fw_compat_redir.c
index d4d910e77..274bb1162 100644
--- a/net/ipv4/netfilter/ip_fw_compat_redir.c
+++ b/net/ipv4/netfilter/ip_fw_compat_redir.c
@@ -172,7 +172,8 @@ do_redirect(struct sk_buff *skb,
struct udphdr *udph = (struct udphdr *)((u_int32_t *)iph
+ iph->ihl);
- udph->check = cheat_check(~iph->daddr, newdst,
+ if (udph->check) /* 0 is a special case meaning no checksum */
+ udph->check = cheat_check(~iph->daddr, newdst,
cheat_check(udph->dest ^ 0xFFFF,
redirpt,
udph->check));
diff --git a/net/ipv4/netfilter/ip_nat_core.c b/net/ipv4/netfilter/ip_nat_core.c
index c8bf259b9..0f7b4f8ca 100644
--- a/net/ipv4/netfilter/ip_nat_core.c
+++ b/net/ipv4/netfilter/ip_nat_core.c
@@ -359,10 +359,14 @@ find_best_ips_proto_fast(struct ip_conntrack_tuple *tuple,
if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC)
tuple->src.ip = mr->range[0].min_ip;
else {
- tuple->dst.ip = mr->range[0].min_ip;
- if (hooknum == NF_IP_LOCAL_OUT
- && !do_extra_mangle(tuple->dst.ip, &tuple->src.ip))
+ /* Only do extra mangle when required (breaks
+ socket binding) */
+ if (tuple->dst.ip != mr->range[0].min_ip
+ && hooknum == NF_IP_LOCAL_OUT
+ && !do_extra_mangle(mr->range[0].min_ip,
+ &tuple->src.ip))
return NULL;
+ tuple->dst.ip = mr->range[0].min_ip;
}
}
@@ -456,11 +460,9 @@ get_unique_tuple(struct ip_conntrack_tuple *tuple,
static inline int
helper_cmp(const struct ip_nat_helper *helper,
- u_int16_t protocol,
- u_int16_t protocol_dst)
+ const struct ip_conntrack_tuple *tuple)
{
- return (protocol == helper->protocol
- && protocol_dst == helper->protocol_dst);
+ return ip_ct_tuple_mask_cmp(tuple, &helper->tuple, &helper->mask);
}
/* Where to manip the reply packets (will be reverse manip). */
@@ -595,8 +597,7 @@ ip_nat_setup_info(struct ip_conntrack *conntrack,
/* If there's a helper, assign it; based on new tuple. */
info->helper = LIST_FIND(&helpers, helper_cmp, struct ip_nat_helper *,
- new_tuple.dst.protonum,
- new_tuple.dst.u.all);
+ &reply);
/* It's done. */
info->initialized |= (1 << HOOK2MANIP(hooknum));
@@ -835,8 +836,7 @@ int ip_nat_helper_register(struct ip_nat_helper *me)
int ret = 0;
WRITE_LOCK(&ip_nat_lock);
- if (LIST_FIND(&helpers, helper_cmp, struct ip_nat_helper *,
- me->protocol, me->protocol_dst))
+ if (LIST_FIND(&helpers, helper_cmp, struct ip_nat_helper *,&me->tuple))
ret = -EBUSY;
else {
list_prepend(&helpers, me);
diff --git a/net/ipv4/netfilter/ip_nat_ftp.c b/net/ipv4/netfilter/ip_nat_ftp.c
index a0de5a351..d4eb36405 100644
--- a/net/ipv4/netfilter/ip_nat_ftp.c
+++ b/net/ipv4/netfilter/ip_nat_ftp.c
@@ -72,10 +72,18 @@ ftp_nat_expected(struct sk_buff **pskb,
DEBUGP("nat_expected: IP to %u.%u.%u.%u\n", IP_PARTS(newip));
mr.rangesize = 1;
- /* We don't want to manip the per-protocol, just the IPs. */
+ /* We don't want to manip the per-protocol, just the IPs... */
mr.range[0].flags = IP_NAT_RANGE_MAP_IPS;
mr.range[0].min_ip = mr.range[0].max_ip = newip;
+ /* ... unless we're doing a MANIP_DST, in which case, make
+ sure we map to the correct port */
+ if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_DST) {
+ mr.range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
+ mr.range[0].min = mr.range[0].max
+ = ((union ip_conntrack_manip_proto)
+ { htons(ftpinfo->port) });
+ }
*verdict = ip_nat_setup_info(ct, &mr, hooknum);
return 1;
@@ -118,7 +126,7 @@ mangle_packet(struct sk_buff **pskb,
NIPQUAD((*pskb)->nh.iph->saddr),
NIPQUAD((*pskb)->nh.iph->daddr),
(*pskb)->nh.iph->protocol);
- return NF_DROP;
+ return 0;
}
if (newlen > (*pskb)->len + skb_tailroom(*pskb)) {
@@ -236,10 +244,16 @@ static int ftp_data_fixup(const struct ip_ct_ftp *ct_ftp_info,
struct sk_buff **pskb)
{
u_int32_t newip;
- struct ip_conntrack_tuple t;
struct iphdr *iph = (*pskb)->nh.iph;
struct tcphdr *tcph = (void *)iph + iph->ihl*4;
-
+ u_int16_t port;
+ struct ip_conntrack_tuple tuple;
+ /* Don't care about source port */
+ const struct ip_conntrack_tuple mask
+ = { { 0xFFFFFFFF, { 0 } },
+ { 0xFFFFFFFF, { 0xFFFF }, 0xFFFF } };
+
+ memset(&tuple, 0, sizeof(tuple));
MUST_BE_LOCKED(&ip_ftp_lock);
DEBUGP("FTP_NAT: seq %u + %u in %u + %u\n",
ct_ftp_info->seq, ct_ftp_info->len,
@@ -251,27 +265,35 @@ static int ftp_data_fixup(const struct ip_ct_ftp *ct_ftp_info,
/* PASV response: must be where client thinks server
is */
newip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
+ /* Expect something from client->server */
+ tuple.src.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
+ tuple.dst.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
} else {
/* PORT command: must be where server thinks client is */
newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
+ /* Expect something from server->client */
+ tuple.src.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip;
+ tuple.dst.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
+ }
+ tuple.dst.protonum = IPPROTO_TCP;
+
+ /* Try to get same port: if not, try to change it. */
+ for (port = ct_ftp_info->port; port != 0; port++) {
+ tuple.dst.u.tcp.port = htons(port);
+
+ if (ip_conntrack_expect_related(ct, &tuple, &mask, NULL) == 0)
+ break;
}
+ if (port == 0)
+ return 0;
- if (!mangle_packet(pskb, newip, ct_ftp_info->port,
+ if (!mangle_packet(pskb, newip, port,
ct_ftp_info->seq - ntohl(tcph->seq),
ct_ftp_info->len,
&ftp[ct_ftp_info->ftptype],
&ftp[!ct_ftp_info->ftptype]))
return 0;
- /* Alter conntrack's expectations. */
-
- /* We can read expect here without conntrack lock, since it's
- only set in ip_conntrack_ftp, with ip_ftp_lock held
- writable */
- t = ct->expected.tuple;
- t.dst.ip = newip;
- ip_conntrack_expect_related(ct, &t);
-
return 1;
}
@@ -368,8 +390,12 @@ static unsigned int help(struct ip_conntrack *ct,
return NF_ACCEPT;
}
-static struct ip_nat_helper ftp
-= { { NULL, NULL }, IPPROTO_TCP, __constant_htons(21), help, "ftp" };
+static struct ip_nat_helper ftp = { { NULL, NULL },
+ { { 0, { __constant_htons(21) } },
+ { 0, { 0 }, IPPROTO_TCP } },
+ { { 0, { 0xFFFF } },
+ { 0, { 0 }, 0xFFFF } },
+ help, "ftp" };
static struct ip_nat_expect ftp_expect
= { { NULL, NULL }, ftp_nat_expected };
diff --git a/net/ipv4/netfilter/ip_nat_proto_udp.c b/net/ipv4/netfilter/ip_nat_proto_udp.c
index e0dc25910..622aee05a 100644
--- a/net/ipv4/netfilter/ip_nat_proto_udp.c
+++ b/net/ipv4/netfilter/ip_nat_proto_udp.c
@@ -90,7 +90,8 @@ udp_manip_pkt(struct iphdr *iph, size_t len,
oldip = iph->daddr;
portptr = &hdr->dest;
}
- hdr->check = ip_nat_cheat_check(~oldip, manip->ip,
+ if (hdr->check) /* 0 is a special case meaning no checksum */
+ hdr->check = ip_nat_cheat_check(~oldip, manip->ip,
ip_nat_cheat_check(*portptr ^ 0xFFFF,
manip->u.udp.port,
hdr->check));
diff --git a/net/ipv4/netfilter/ip_queue.c b/net/ipv4/netfilter/ip_queue.c
index 39574b7d4..2ac8b22e2 100644
--- a/net/ipv4/netfilter/ip_queue.c
+++ b/net/ipv4/netfilter/ip_queue.c
@@ -199,38 +199,36 @@ static void ipq_destroy_queue(ipq_queue_t *q)
static int ipq_mangle_ipv4(ipq_verdict_msg_t *v, ipq_queue_element_t *e)
{
+ int diff;
struct iphdr *user_iph = (struct iphdr *)v->payload;
if (v->data_len < sizeof(*user_iph))
return 0;
- if (e->skb->nh.iph->check != user_iph->check) {
- int diff = v->data_len - e->skb->len;
-
- if (diff < 0)
- skb_trim(e->skb, v->data_len);
- else if (diff > 0) {
- if (v->data_len > 0xFFFF)
- return -EINVAL;
- if (diff > skb_tailroom(e->skb)) {
- struct sk_buff *newskb;
-
- newskb = skb_copy_expand(e->skb,
- skb_headroom(e->skb),
- diff,
- GFP_ATOMIC);
- if (newskb == NULL) {
- printk(KERN_WARNING "ip_queue: OOM "
- "in mangle, dropping packet\n");
- return -ENOMEM;
- }
- kfree_skb(e->skb);
- e->skb = newskb;
+ diff = v->data_len - e->skb->len;
+ if (diff < 0)
+ skb_trim(e->skb, v->data_len);
+ else if (diff > 0) {
+ if (v->data_len > 0xFFFF)
+ return -EINVAL;
+ if (diff > skb_tailroom(e->skb)) {
+ struct sk_buff *newskb;
+
+ newskb = skb_copy_expand(e->skb,
+ skb_headroom(e->skb),
+ diff,
+ GFP_ATOMIC);
+ if (newskb == NULL) {
+ printk(KERN_WARNING "ip_queue: OOM "
+ "in mangle, dropping packet\n");
+ return -ENOMEM;
}
- skb_put(e->skb, diff);
+ kfree_skb(e->skb);
+ e->skb = newskb;
}
- memcpy(e->skb->data, v->payload, v->data_len);
- e->skb->nfcache |= NFC_ALTERED;
+ skb_put(e->skb, diff);
}
+ memcpy(e->skb->data, v->payload, v->data_len);
+ e->skb->nfcache |= NFC_ALTERED;
return 0;
}
diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c
index 40b19760b..7a24b21dd 100644
--- a/net/ipv4/netfilter/ip_tables.c
+++ b/net/ipv4/netfilter/ip_tables.c
@@ -1748,5 +1748,11 @@ static void __exit fini(void)
#endif
}
+EXPORT_SYMBOL(ipt_register_table);
+EXPORT_SYMBOL(ipt_unregister_table);
+EXPORT_SYMBOL(ipt_register_match);
+EXPORT_SYMBOL(ipt_unregister_match);
+EXPORT_SYMBOL(ipt_do_table);
+
module_init(init);
module_exit(fini);