From patchwork Mon Jun 4 12:21:23 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pablo Neira Ayuso X-Patchwork-Id: 162781 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 84521B7006 for ; Mon, 4 Jun 2012 22:22:30 +1000 (EST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1760420Ab2FDMW0 (ORCPT ); Mon, 4 Jun 2012 08:22:26 -0400 Received: from mail.us.es ([193.147.175.20]:45961 "EHLO mail.us.es" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1760404Ab2FDMW0 (ORCPT ); Mon, 4 Jun 2012 08:22:26 -0400 Received: (qmail 7122 invoked from network); 4 Jun 2012 14:22:24 +0200 Received: from unknown (HELO us.es) (192.168.2.11) by us.es with SMTP; 4 Jun 2012 14:22:24 +0200 Received: (qmail 14228 invoked by uid 507); 4 Jun 2012 12:22:23 -0000 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on antivirus1 X-Spam-Level: X-Spam-Status: No, score=-97.0 required=7.5 tests=BAYES_50,KHOP_DYNAMIC, RCVD_IN_BRBL_LASTEXT,RCVD_IN_PBL,RCVD_IN_SORBS_DUL,RDNS_DYNAMIC, USER_IN_WHITELIST autolearn=disabled version=3.3.1 Received: from 127.0.0.1 by antivirus1 (envelope-from , uid 501) with qmail-scanner-2.08 (clamdscan: 0.97.4/14995. Clear:RC:1(127.0.0.1):. Processed in 0.024414 secs); 04 Jun 2012 12:22:23 -0000 Received: from unknown (HELO antivirus1) (127.0.0.1) by us.es with SMTP; 4 Jun 2012 12:22:23 -0000 Received: from 192.168.1.13 (192.168.1.13) by antivirus1 (F-Secure/fsigk_smtp/407/antivirus1); Mon, 04 Jun 2012 14:22:23 +0200 (CEST) X-Virus-Status: clean(F-Secure/fsigk_smtp/407/antivirus1) Received: (qmail 6721 invoked from network); 4 Jun 2012 14:23:18 +0200 Received: from 185.74.221.87.dynamic.jazztel.es (HELO localhost.localdomain) (pneira@us.es@87.221.74.185) by us.es with SMTP; 4 Jun 2012 14:23:18 +0200 From: pablo@netfilter.org To: netfilter-devel@vger.kernel.org Cc: netdev@vger.kernel.org Subject: [PATCH 5/7] netfilter: nfnl_queue: support NAT TCP sequence adjustment if packet mangled Date: Mon, 4 Jun 2012 14:21:23 +0200 Message-Id: <1338812485-4232-6-git-send-email-pablo@netfilter.org> X-Mailer: git-send-email 1.7.10 In-Reply-To: <1338812485-4232-1-git-send-email-pablo@netfilter.org> References: <1338812485-4232-1-git-send-email-pablo@netfilter.org> Sender: netfilter-devel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netfilter-devel@vger.kernel.org From: Pablo Neira Ayuso 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 NFQNL_F_CONNTRACK flag has been set, and 3) NAT is detected. This is required by the new user-space cthelper infrastructure. By now, we only support TCP since we have no helpers for DCCP or SCTP. Better to add this if we do ever need it. You can still use this without the cthelper infrastructure. There are several post on the Internet complaning about this (mostly people using NFQUEUE for IDS). Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_nat_helper.h | 7 ++++++ net/ipv4/netfilter/nf_nat_helper.c | 13 ++++++++++ net/netfilter/nfnetlink_queue.c | 42 ++++++++++++++++++++++----------- 3 files changed, 48 insertions(+), 14 deletions(-) diff --git a/include/net/netfilter/nf_nat_helper.h b/include/net/netfilter/nf_nat_helper.h index 02bb6c2..ee92a0c 100644 --- a/include/net/netfilter/nf_nat_helper.h +++ b/include/net/netfilter/nf_nat_helper.h @@ -46,6 +46,13 @@ extern int (*nf_nat_seq_adjust_hook)(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo); +extern void nf_nat_tcp_seq_adjust(struct sk_buff *skb, + struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + int off); + +struct nf_conntrack_expect; + /* Setup NAT on this expected conntrack so it follows master, but goes * to port ct->master->saved_proto. */ extern void nf_nat_follow_master(struct nf_conn *ct, diff --git a/net/ipv4/netfilter/nf_nat_helper.c b/net/ipv4/netfilter/nf_nat_helper.c index af65958..7c7b5b8 100644 --- a/net/ipv4/netfilter/nf_nat_helper.c +++ b/net/ipv4/netfilter/nf_nat_helper.c @@ -153,6 +153,19 @@ void nf_nat_set_seq_adjust(struct nf_conn *ct, enum ip_conntrack_info ctinfo, } EXPORT_SYMBOL_GPL(nf_nat_set_seq_adjust); +void nf_nat_tcp_seq_adjust(struct sk_buff *skb, struct nf_conn *ct, + enum ip_conntrack_info ctinfo, int off) +{ + const struct tcphdr *th; + + if (nf_ct_protonum(ct) != IPPROTO_TCP) + return; + + th = (struct tcphdr *)(skb_network_header(skb)+ ip_hdrlen(skb)); + nf_nat_set_seq_adjust(ct, ctinfo, th->seq, off); +} +EXPORT_SYMBOL_GPL(nf_nat_tcp_seq_adjust); + static void nf_nat_csum(struct sk_buff *skb, const struct iphdr *iph, void *data, int datalen, __sum16 *check, int oldlen) { diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c index b007779..b18a367 100644 --- a/net/netfilter/nfnetlink_queue.c +++ b/net/netfilter/nfnetlink_queue.c @@ -38,6 +38,10 @@ #include "../bridge/br_private.h" #endif +#ifdef CONFIG_NF_NAT_NEEDED +#include +#endif + #define NFQNL_QMAX_DEFAULT 1024 struct nfqnl_instance { @@ -497,12 +501,10 @@ err_out: } static int -nfqnl_mangle(void *data, int data_len, struct nf_queue_entry *e) +nfqnl_mangle(void *data, int data_len, struct nf_queue_entry *e, int diff) { struct sk_buff *nskb; - int diff; - diff = data_len - e->skb->len; if (diff < 0) { if (pskb_trim(e->skb, data_len)) return -ENOMEM; @@ -761,6 +763,8 @@ nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb, unsigned int verdict; struct nf_queue_entry *entry; struct nfq_ct_hook *nfq_ct; + enum ip_conntrack_info uninitialized_var(ctinfo); + struct nf_conn *ct = NULL; queue = instance_lookup(queue_num); if (!queue) @@ -779,26 +783,36 @@ nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb, if (entry == NULL) return -ENOENT; + rcu_read_lock(); + nfq_ct = rcu_dereference(nfq_ct_hook); + if (nfq_ct != NULL && (queue->flags & NFQNL_F_CONNTRACK) && + nfqa[NFQA_CT]) { + ct = nf_ct_get(entry->skb, &ctinfo); + if (ct && nf_ct_is_untracked(ct)) + ct = NULL; + } + if (nfqa[NFQA_PAYLOAD]) { + u16 payload_len = nla_len(nfqa[NFQA_PAYLOAD]); + int diff = payload_len - entry->skb->len; + if (nfqnl_mangle(nla_data(nfqa[NFQA_PAYLOAD]), - nla_len(nfqa[NFQA_PAYLOAD]), entry) < 0) + payload_len, entry, diff) < 0) verdict = NF_DROP; + +#ifdef CONFIG_NF_NAT_NEEDED + /* Adjust sequence numbers to avoid puzzling conntrack */ + if (ct && (ct->status & IPS_NAT_MASK) && diff) + nf_nat_tcp_seq_adjust(skb, ct, ctinfo, diff); +#endif } if (nfqa[NFQA_MARK]) entry->skb->mark = ntohl(nla_get_be32(nfqa[NFQA_MARK])); - rcu_read_lock(); - nfq_ct = rcu_dereference(nfq_ct_hook); - if (nfq_ct != NULL && - (queue->flags & NFQNL_F_CONNTRACK) && nfqa[NFQA_CT]) { - enum ip_conntrack_info ctinfo; - struct nf_conn *ct; + if (ct) + nfq_ct->parse(nfqa[NFQA_CT], ct); - ct = nf_ct_get(entry->skb, &ctinfo); - if (ct && !nf_ct_is_untracked(ct)) - nfq_ct->parse(nfqa[NFQA_CT], ct); - } rcu_read_unlock(); nf_reinject(entry, verdict);