diff options
Diffstat (limited to 'net/ipv4')
-rw-r--r-- | net/ipv4/netfilter/Makefile | 4 | ||||
-rw-r--r-- | net/ipv4/netfilter/ip_conntrack_core.c | 70 | ||||
-rw-r--r-- | net/ipv4/netfilter/ip_conntrack_ftp.c | 24 | ||||
-rw-r--r-- | net/ipv4/netfilter/ip_conntrack_proto_tcp.c | 52 | ||||
-rw-r--r-- | net/ipv4/netfilter/ip_conntrack_proto_udp.c | 6 | ||||
-rw-r--r-- | net/ipv4/netfilter/ip_conntrack_standalone.c | 26 | ||||
-rw-r--r-- | net/ipv4/netfilter/ip_fw_compat.c | 4 | ||||
-rw-r--r-- | net/ipv4/netfilter/ip_fw_compat_redir.c | 3 | ||||
-rw-r--r-- | net/ipv4/netfilter/ip_nat_core.c | 22 | ||||
-rw-r--r-- | net/ipv4/netfilter/ip_nat_ftp.c | 58 | ||||
-rw-r--r-- | net/ipv4/netfilter/ip_nat_proto_udp.c | 3 | ||||
-rw-r--r-- | net/ipv4/netfilter/ip_queue.c | 48 | ||||
-rw-r--r-- | net/ipv4/netfilter/ip_tables.c | 6 |
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); |