Merge branch 'master' of git://1984.lsi.us.es/nf-next

Pablo says:

====================
The following patchset provides fixes for issues that were recently introduced
by my new cthelper infrastructure. They have been spotted by Randy Dunlap,
Andrew Morton and Dan Carpenter.

The patches provide:

* compilation fixes if CONFIG_NF_CONNTRACK is disabled: I moved all the
  conntrack code from nfnetlink_queue.c to nfnetlink_queue_ct.c to avoid
  peppering the entire code with lots of ifdefs. I needed to rename
  nfnetlink_queue.c to nfnetlink_queue_core.c to get it working with the
  Makefile tweaks I've added.

* fix NULL pointer dereference via ctnetlink while trying to change the helper
  for an existing conntrack entry. I don't find any reasonable use case for
  changing the helper from one to another in run-time. Thus, now ctnetlink
  returns -EOPNOTSUPP for this operation.

* fix possible out-of-bound zeroing of the conntrack extension area due to
  the helper automatic assignation routine.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2012-06-18 20:26:06 -07:00
commit 6bdc5f49e3
7 changed files with 187 additions and 67 deletions

View File

@ -0,0 +1,43 @@
#ifndef _NET_NFNL_QUEUE_H_
#define _NET_NFNL_QUEUE_H_
#include <linux/netfilter/nf_conntrack_common.h>
struct nf_conn;
#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
struct nf_conn *nfqnl_ct_get(struct sk_buff *entskb, size_t *size,
enum ip_conntrack_info *ctinfo);
struct nf_conn *nfqnl_ct_parse(const struct sk_buff *skb,
const struct nlattr *attr,
enum ip_conntrack_info *ctinfo);
int nfqnl_ct_put(struct sk_buff *skb, struct nf_conn *ct,
enum ip_conntrack_info ctinfo);
void nfqnl_ct_seq_adjust(struct sk_buff *skb, struct nf_conn *ct,
enum ip_conntrack_info ctinfo, int diff);
#else
inline struct nf_conn *
nfqnl_ct_get(struct sk_buff *entskb, size_t *size, enum ip_conntrack_info *ctinfo)
{
return NULL;
}
inline struct nf_conn *nfqnl_ct_parse(const struct sk_buff *skb,
const struct nlattr *attr,
enum ip_conntrack_info *ctinfo)
{
return NULL;
}
inline int
nfqnl_ct_put(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo)
{
return 0;
}
inline void nfqnl_ct_seq_adjust(struct sk_buff *skb, struct nf_conn *ct,
enum ip_conntrack_info ctinfo, int diff)
{
}
#endif /* NF_CONNTRACK */
#endif

View File

@ -12,14 +12,6 @@ tristate "Netfilter NFACCT over NFNETLINK interface"
If this option is enabled, the kernel will include support
for extended accounting via NFNETLINK.
config NETFILTER_NETLINK_CTHELPER
tristate "Netfilter CTHELPER over NFNETLINK interface"
depends on NETFILTER_ADVANCED
select NETFILTER_NETLINK
help
If this option is enabled, the kernel will include support
for user-space connection tracking helpers via NFNETLINK.
config NETFILTER_NETLINK_QUEUE
tristate "Netfilter NFQUEUE over NFNETLINK interface"
depends on NETFILTER_ADVANCED
@ -343,6 +335,27 @@ config NF_CT_NETLINK_TIMEOUT
If unsure, say `N'.
config NF_CT_NETLINK_HELPER
tristate 'Connection tracking helpers in user-space via Netlink'
select NETFILTER_NETLINK
depends on NF_CT_NETLINK
depends on NETFILTER_NETLINK_QUEUE
depends on NETFILTER_NETLINK_QUEUE_CT
depends on NETFILTER_ADVANCED
help
This option enables the user-space connection tracking helpers
infrastructure.
If unsure, say `N'.
config NETFILTER_NETLINK_QUEUE_CT
bool "NFQUEUE integration with Connection Tracking"
default n
depends on NETFILTER_NETLINK_QUEUE
help
If this option is enabled, NFQUEUE can include Connection Tracking
information together with the packet is the enqueued via NFNETLINK.
endif # NF_CONNTRACK
# transparent proxy support

View File

@ -9,7 +9,8 @@ obj-$(CONFIG_NETFILTER) = netfilter.o
obj-$(CONFIG_NETFILTER_NETLINK) += nfnetlink.o
obj-$(CONFIG_NETFILTER_NETLINK_ACCT) += nfnetlink_acct.o
obj-$(CONFIG_NETFILTER_NETLINK_CTHELPER) += nfnetlink_cthelper.o
nfnetlink_queue-y := nfnetlink_queue_core.o
nfnetlink_queue-$(CONFIG_NETFILTER_NETLINK_QUEUE_CT) += nfnetlink_queue_ct.o
obj-$(CONFIG_NETFILTER_NETLINK_QUEUE) += nfnetlink_queue.o
obj-$(CONFIG_NETFILTER_NETLINK_LOG) += nfnetlink_log.o
@ -25,6 +26,7 @@ obj-$(CONFIG_NF_CT_PROTO_UDPLITE) += nf_conntrack_proto_udplite.o
# netlink interface for nf_conntrack
obj-$(CONFIG_NF_CT_NETLINK) += nf_conntrack_netlink.o
obj-$(CONFIG_NF_CT_NETLINK_TIMEOUT) += nfnetlink_cttimeout.o
obj-$(CONFIG_NF_CT_NETLINK_HELPER) += nfnetlink_cthelper.o
# connection tracking helpers
nf_conntrack_h323-objs := nf_conntrack_h323_main.o nf_conntrack_h323_asn1.o

View File

@ -229,7 +229,13 @@ int __nf_ct_try_assign_helper(struct nf_conn *ct, struct nf_conn *tmpl,
goto out;
}
} else {
memset(help->data, 0, helper->data_len);
/* We only allow helper re-assignment of the same sort since
* we cannot reallocate the helper extension area.
*/
if (help->helper != helper) {
RCU_INIT_POINTER(help->helper, NULL);
goto out;
}
}
rcu_assign_pointer(help->helper, helper);

View File

@ -1224,19 +1224,12 @@ ctnetlink_change_helper(struct nf_conn *ct, const struct nlattr * const cda[])
if (helper->from_nlattr && helpinfo)
helper->from_nlattr(helpinfo, ct);
return 0;
}
if (help->helper)
} else
return -EBUSY;
/* need to zero data of old helper */
memset(help->data, 0, help->helper->data_len);
} else {
/* we cannot set a helper for an existing conntrack */
return -EOPNOTSUPP;
}
rcu_assign_pointer(help->helper, helper);
return 0;
/* we cannot set a helper for an existing conntrack */
return -EOPNOTSUPP;
}
static inline int
@ -1634,8 +1627,7 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
return err;
}
#if defined(CONFIG_NETFILTER_NETLINK_QUEUE) || \
defined(CONFIG_NETFILTER_NETLINK_QUEUE_MODULE)
#ifdef CONFIG_NETFILTER_NETLINK_QUEUE_CT
static size_t
ctnetlink_nfqueue_build_size(const struct nf_conn *ct)
{
@ -1769,7 +1761,7 @@ static struct nfq_ct_hook ctnetlink_nfqueue_hook = {
.seq_adjust = nf_nat_tcp_seq_adjust,
#endif
};
#endif /* CONFIG_NETFILTER_NETLINK_QUEUE */
#endif /* CONFIG_NETFILTER_NETLINK_QUEUE_CT */
/***********************************************************************
* EXPECT
@ -2575,8 +2567,7 @@ static int __init ctnetlink_init(void)
pr_err("ctnetlink_init: cannot register pernet operations\n");
goto err_unreg_exp_subsys;
}
#if defined(CONFIG_NETFILTER_NETLINK_QUEUE) || \
defined(CONFIG_NETFILTER_NETLINK_QUEUE_MODULE)
#ifdef CONFIG_NETFILTER_NETLINK_QUEUE_CT
/* setup interaction between nf_queue and nf_conntrack_netlink. */
RCU_INIT_POINTER(nfq_ct_hook, &ctnetlink_nfqueue_hook);
#endif
@ -2597,8 +2588,7 @@ static void __exit ctnetlink_exit(void)
unregister_pernet_subsys(&ctnetlink_net_ops);
nfnetlink_subsys_unregister(&ctnl_exp_subsys);
nfnetlink_subsys_unregister(&ctnl_subsys);
#if defined(CONFIG_NETFILTER_NETLINK_QUEUE) || \
defined(CONFIG_NETFILTER_NETLINK_QUEUE_MODULE)
#ifdef CONFIG_NETFILTER_NETLINK_QUEUE_CT
RCU_INIT_POINTER(nfq_ct_hook, NULL);
#endif
}

View File

@ -30,7 +30,7 @@
#include <linux/list.h>
#include <net/sock.h>
#include <net/netfilter/nf_queue.h>
#include <net/netfilter/nf_conntrack.h>
#include <net/netfilter/nfnetlink_queue.h>
#include <linux/atomic.h>
@ -234,7 +234,6 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue,
struct sk_buff *entskb = entry->skb;
struct net_device *indev;
struct net_device *outdev;
struct nfq_ct_hook *nfq_ct;
struct nf_conn *ct = NULL;
enum ip_conntrack_info uninitialized_var(ctinfo);
@ -270,17 +269,8 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue,
break;
}
/* rcu_read_lock()ed by __nf_queue already. */
nfq_ct = rcu_dereference(nfq_ct_hook);
if (nfq_ct != NULL && (queue->flags & NFQA_CFG_F_CONNTRACK)) {
ct = nf_ct_get(entskb, &ctinfo);
if (ct) {
if (!nf_ct_is_untracked(ct))
size += nfq_ct->build_size(ct);
else
ct = NULL;
}
}
if (queue->flags & NFQA_CFG_F_CONNTRACK)
ct = nfqnl_ct_get(entskb, &size, &ctinfo);
skb = alloc_skb(size, GFP_ATOMIC);
if (!skb)
@ -404,23 +394,8 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue,
BUG();
}
if (ct) {
struct nlattr *nest_parms;
u_int32_t tmp;
nest_parms = nla_nest_start(skb, NFQA_CT | NLA_F_NESTED);
if (!nest_parms)
goto nla_put_failure;
if (nfq_ct->build(skb, ct) < 0)
goto nla_put_failure;
nla_nest_end(skb, nest_parms);
tmp = ctinfo;
if (nla_put_u32(skb, NFQA_CT_INFO, htonl(ctinfo)))
goto nla_put_failure;
}
if (ct && nfqnl_ct_put(skb, ct, ctinfo) < 0)
goto nla_put_failure;
nlh->nlmsg_len = skb->tail - old_tail;
return skb;
@ -764,7 +739,6 @@ nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb,
struct nfqnl_instance *queue;
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;
@ -786,13 +760,8 @@ nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb,
return -ENOENT;
rcu_read_lock();
nfq_ct = rcu_dereference(nfq_ct_hook);
if (nfq_ct != NULL &&
(queue->flags & NFQA_CFG_F_CONNTRACK) && nfqa[NFQA_CT]) {
ct = nf_ct_get(entry->skb, &ctinfo);
if (ct && !nf_ct_is_untracked(ct))
nfq_ct->parse(nfqa[NFQA_CT], ct);
}
if (nfqa[NFQA_CT] && (queue->flags & NFQA_CFG_F_CONNTRACK))
ct = nfqnl_ct_parse(entry->skb, nfqa[NFQA_CT], &ctinfo);
if (nfqa[NFQA_PAYLOAD]) {
u16 payload_len = nla_len(nfqa[NFQA_PAYLOAD]);
@ -802,8 +771,8 @@ nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb,
payload_len, entry, diff) < 0)
verdict = NF_DROP;
if (ct && (ct->status & IPS_NAT_MASK) && diff)
nfq_ct->seq_adjust(skb, ct, ctinfo, diff);
if (ct)
nfqnl_ct_seq_adjust(skb, ct, ctinfo, diff);
}
rcu_read_unlock();

View File

@ -0,0 +1,97 @@
/*
* (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/skbuff.h>
#include <linux/netfilter.h>
#include <linux/netfilter/nfnetlink.h>
#include <linux/netfilter/nfnetlink_queue.h>
#include <net/netfilter/nf_conntrack.h>
struct nf_conn *nfqnl_ct_get(struct sk_buff *entskb, size_t *size,
enum ip_conntrack_info *ctinfo)
{
struct nfq_ct_hook *nfq_ct;
struct nf_conn *ct;
/* rcu_read_lock()ed by __nf_queue already. */
nfq_ct = rcu_dereference(nfq_ct_hook);
if (nfq_ct == NULL)
return NULL;
ct = nf_ct_get(entskb, ctinfo);
if (ct) {
if (!nf_ct_is_untracked(ct))
*size += nfq_ct->build_size(ct);
else
ct = NULL;
}
return ct;
}
struct nf_conn *
nfqnl_ct_parse(const struct sk_buff *skb, const struct nlattr *attr,
enum ip_conntrack_info *ctinfo)
{
struct nfq_ct_hook *nfq_ct;
struct nf_conn *ct;
/* rcu_read_lock()ed by __nf_queue already. */
nfq_ct = rcu_dereference(nfq_ct_hook);
if (nfq_ct == NULL)
return NULL;
ct = nf_ct_get(skb, ctinfo);
if (ct && !nf_ct_is_untracked(ct))
nfq_ct->parse(attr, ct);
return ct;
}
int nfqnl_ct_put(struct sk_buff *skb, struct nf_conn *ct,
enum ip_conntrack_info ctinfo)
{
struct nfq_ct_hook *nfq_ct;
struct nlattr *nest_parms;
u_int32_t tmp;
nfq_ct = rcu_dereference(nfq_ct_hook);
if (nfq_ct == NULL)
return 0;
nest_parms = nla_nest_start(skb, NFQA_CT | NLA_F_NESTED);
if (!nest_parms)
goto nla_put_failure;
if (nfq_ct->build(skb, ct) < 0)
goto nla_put_failure;
nla_nest_end(skb, nest_parms);
tmp = ctinfo;
if (nla_put_be32(skb, NFQA_CT_INFO, htonl(tmp)))
goto nla_put_failure;
return 0;
nla_put_failure:
return -1;
}
void nfqnl_ct_seq_adjust(struct sk_buff *skb, struct nf_conn *ct,
enum ip_conntrack_info ctinfo, int diff)
{
struct nfq_ct_hook *nfq_ct;
nfq_ct = rcu_dereference(nfq_ct_hook);
if (nfq_ct == NULL)
return;
if ((ct->status & IPS_NAT_MASK) && diff)
nfq_ct->seq_adjust(skb, ct, ctinfo, diff);
}