diff options
author | Pablo Neira Ayuso <pablo@netfilter.org> | 2012-06-07 07:31:25 -0400 |
---|---|---|
committer | Pablo Neira Ayuso <pablo@netfilter.org> | 2012-06-16 09:09:08 -0400 |
commit | 8c88f87cb27ad09086940bdd3e6955e5325ec89a (patch) | |
tree | 1acfa54ba78602eb4eb053110ffc0a54025318c7 /net/netfilter | |
parent | 9cb0176654a7dc33a32af8a0bc9e0b2f9f9ebb0f (diff) |
netfilter: nfnetlink_queue: add NAT TCP sequence adjustment if packet mangled
User-space programs that receive traffic via NFQUEUE may mangle packets.
If NAT is enabled, this usually puzzles sequence tracking, leading to
traffic disruptions.
With this patch, nfnl_queue will make the corresponding NAT TCP sequence
adjustment if:
1) The packet has been mangled,
2) the NFQA_CFG_F_CONNTRACK flag has been set, and
3) NAT is detected.
There are some records on the Internet complaning about this issue:
http://stackoverflow.com/questions/260757/packet-mangling-utilities-besides-iptables
By now, we only support TCP since we have no helpers for DCCP or SCTP.
Better to add this if we ever have some helper over those layer 4 protocols.
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'net/netfilter')
-rw-r--r-- | net/netfilter/nf_conntrack_netlink.c | 4 | ||||
-rw-r--r-- | net/netfilter/nfnetlink_queue.c | 19 |
2 files changed, 15 insertions, 8 deletions
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index d304d5917950..8be0ab9b4758 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c | |||
@@ -46,6 +46,7 @@ | |||
46 | #ifdef CONFIG_NF_NAT_NEEDED | 46 | #ifdef CONFIG_NF_NAT_NEEDED |
47 | #include <net/netfilter/nf_nat_core.h> | 47 | #include <net/netfilter/nf_nat_core.h> |
48 | #include <net/netfilter/nf_nat_protocol.h> | 48 | #include <net/netfilter/nf_nat_protocol.h> |
49 | #include <net/netfilter/nf_nat_helper.h> | ||
49 | #endif | 50 | #endif |
50 | 51 | ||
51 | #include <linux/netfilter/nfnetlink.h> | 52 | #include <linux/netfilter/nfnetlink.h> |
@@ -1751,6 +1752,9 @@ static struct nfq_ct_hook ctnetlink_nfqueue_hook = { | |||
1751 | .build_size = ctnetlink_nfqueue_build_size, | 1752 | .build_size = ctnetlink_nfqueue_build_size, |
1752 | .build = ctnetlink_nfqueue_build, | 1753 | .build = ctnetlink_nfqueue_build, |
1753 | .parse = ctnetlink_nfqueue_parse, | 1754 | .parse = ctnetlink_nfqueue_parse, |
1755 | #ifdef CONFIG_NF_NAT_NEEDED | ||
1756 | .seq_adjust = nf_nat_tcp_seq_adjust, | ||
1757 | #endif | ||
1754 | }; | 1758 | }; |
1755 | #endif /* CONFIG_NETFILTER_NETLINK_QUEUE */ | 1759 | #endif /* CONFIG_NETFILTER_NETLINK_QUEUE */ |
1756 | 1760 | ||
diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c index 647923ae9230..ff82c7933dfd 100644 --- a/net/netfilter/nfnetlink_queue.c +++ b/net/netfilter/nfnetlink_queue.c | |||
@@ -502,12 +502,10 @@ err_out: | |||
502 | } | 502 | } |
503 | 503 | ||
504 | static int | 504 | static int |
505 | nfqnl_mangle(void *data, int data_len, struct nf_queue_entry *e) | 505 | nfqnl_mangle(void *data, int data_len, struct nf_queue_entry *e, int diff) |
506 | { | 506 | { |
507 | struct sk_buff *nskb; | 507 | struct sk_buff *nskb; |
508 | int diff; | ||
509 | 508 | ||
510 | diff = data_len - e->skb->len; | ||
511 | if (diff < 0) { | 509 | if (diff < 0) { |
512 | if (pskb_trim(e->skb, data_len)) | 510 | if (pskb_trim(e->skb, data_len)) |
513 | return -ENOMEM; | 511 | return -ENOMEM; |
@@ -767,6 +765,8 @@ nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb, | |||
767 | unsigned int verdict; | 765 | unsigned int verdict; |
768 | struct nf_queue_entry *entry; | 766 | struct nf_queue_entry *entry; |
769 | struct nfq_ct_hook *nfq_ct; | 767 | struct nfq_ct_hook *nfq_ct; |
768 | enum ip_conntrack_info uninitialized_var(ctinfo); | ||
769 | struct nf_conn *ct = NULL; | ||
770 | 770 | ||
771 | queue = instance_lookup(queue_num); | 771 | queue = instance_lookup(queue_num); |
772 | if (!queue) | 772 | if (!queue) |
@@ -789,20 +789,23 @@ nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb, | |||
789 | nfq_ct = rcu_dereference(nfq_ct_hook); | 789 | nfq_ct = rcu_dereference(nfq_ct_hook); |
790 | if (nfq_ct != NULL && | 790 | if (nfq_ct != NULL && |
791 | (queue->flags & NFQA_CFG_F_CONNTRACK) && nfqa[NFQA_CT]) { | 791 | (queue->flags & NFQA_CFG_F_CONNTRACK) && nfqa[NFQA_CT]) { |
792 | enum ip_conntrack_info ctinfo; | ||
793 | struct nf_conn *ct; | ||
794 | |||
795 | ct = nf_ct_get(entry->skb, &ctinfo); | 792 | ct = nf_ct_get(entry->skb, &ctinfo); |
796 | if (ct && !nf_ct_is_untracked(ct)) | 793 | if (ct && !nf_ct_is_untracked(ct)) |
797 | nfq_ct->parse(nfqa[NFQA_CT], ct); | 794 | nfq_ct->parse(nfqa[NFQA_CT], ct); |
798 | } | 795 | } |
799 | rcu_read_unlock(); | ||
800 | 796 | ||
801 | if (nfqa[NFQA_PAYLOAD]) { | 797 | if (nfqa[NFQA_PAYLOAD]) { |
798 | u16 payload_len = nla_len(nfqa[NFQA_PAYLOAD]); | ||
799 | int diff = payload_len - entry->skb->len; | ||
800 | |||
802 | if (nfqnl_mangle(nla_data(nfqa[NFQA_PAYLOAD]), | 801 | if (nfqnl_mangle(nla_data(nfqa[NFQA_PAYLOAD]), |
803 | nla_len(nfqa[NFQA_PAYLOAD]), entry) < 0) | 802 | payload_len, entry, diff) < 0) |
804 | verdict = NF_DROP; | 803 | verdict = NF_DROP; |
804 | |||
805 | if (ct && (ct->status & IPS_NAT_MASK) && diff) | ||
806 | nfq_ct->seq_adjust(skb, ct, ctinfo, diff); | ||
805 | } | 807 | } |
808 | rcu_read_unlock(); | ||
806 | 809 | ||
807 | if (nfqa[NFQA_MARK]) | 810 | if (nfqa[NFQA_MARK]) |
808 | entry->skb->mark = ntohl(nla_get_be32(nfqa[NFQA_MARK])); | 811 | entry->skb->mark = ntohl(nla_get_be32(nfqa[NFQA_MARK])); |