Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9cb01766 authored by Pablo Neira Ayuso's avatar Pablo Neira Ayuso
Browse files

netfilter: add glue code to integrate nfnetlink_queue and ctnetlink



This patch allows you to include the conntrack information together
with the packet that is sent to user-space via NFQUEUE.

Previously, there was no integration between ctnetlink and
nfnetlink_queue. If you wanted to access conntrack information
from your libnetfilter_queue program, you required to query
ctnetlink from user-space to obtain it. Thus, delaying the packet
processing even more.

Including the conntrack information is optional, you can set it
via NFQA_CFG_F_CONNTRACK flag with the new NFQA_CFG_FLAGS attribute.

Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent 1afc5679
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -393,6 +393,16 @@ nf_nat_decode_session(struct sk_buff *skb, struct flowi *fl, u_int8_t family)
extern void (*ip_ct_attach)(struct sk_buff *, struct sk_buff *) __rcu;
extern void nf_ct_attach(struct sk_buff *, struct sk_buff *);
extern void (*nf_ct_destroy)(struct nf_conntrack *) __rcu;

struct nf_conn;
struct nlattr;

struct nfq_ct_hook {
	size_t (*build_size)(const struct nf_conn *ct);
	int (*build)(struct sk_buff *skb, struct nf_conn *ct);
	int (*parse)(const struct nlattr *attr, struct nf_conn *ct);
};
extern struct nfq_ct_hook *nfq_ct_hook;
#else
static inline void nf_ct_attach(struct sk_buff *new, struct sk_buff *skb) {}
#endif
+3 −0
Original line number Diff line number Diff line
@@ -42,6 +42,8 @@ enum nfqnl_attr_type {
	NFQA_IFINDEX_PHYSOUTDEV,	/* __u32 ifindex */
	NFQA_HWADDR,			/* nfqnl_msg_packet_hw */
	NFQA_PAYLOAD,			/* opaque data payload */
	NFQA_CT,			/* nf_conntrack_netlink.h */
	NFQA_CT_INFO,			/* enum ip_conntrack_info */

	__NFQA_MAX
};
@@ -92,5 +94,6 @@ enum nfqnl_attr_config {

/* Flags for NFQA_CFG_FLAGS */
#define NFQA_CFG_F_FAIL_OPEN			(1 << 0)
#define NFQA_CFG_F_CONNTRACK			(1 << 1)

#endif /* _NFNETLINK_QUEUE_H */
+4 −0
Original line number Diff line number Diff line
@@ -264,6 +264,10 @@ void nf_conntrack_destroy(struct nf_conntrack *nfct)
	rcu_read_unlock();
}
EXPORT_SYMBOL(nf_conntrack_destroy);

struct nfq_ct_hook *nfq_ct_hook;
EXPORT_SYMBOL_GPL(nfq_ct_hook);

#endif /* CONFIG_NF_CONNTRACK */

#ifdef CONFIG_PROC_FS
+143 −1
Original line number Diff line number Diff line
@@ -1620,6 +1620,140 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
	return err;
}

#if defined(CONFIG_NETFILTER_NETLINK_QUEUE) ||	\
    defined(CONFIG_NETFILTER_NETLINK_QUEUE_MODULE)
static size_t
ctnetlink_nfqueue_build_size(const struct nf_conn *ct)
{
	return 3 * nla_total_size(0) /* CTA_TUPLE_ORIG|REPL|MASTER */
	       + 3 * nla_total_size(0) /* CTA_TUPLE_IP */
	       + 3 * nla_total_size(0) /* CTA_TUPLE_PROTO */
	       + 3 * nla_total_size(sizeof(u_int8_t)) /* CTA_PROTO_NUM */
	       + nla_total_size(sizeof(u_int32_t)) /* CTA_ID */
	       + nla_total_size(sizeof(u_int32_t)) /* CTA_STATUS */
	       + nla_total_size(sizeof(u_int32_t)) /* CTA_TIMEOUT */
	       + nla_total_size(0) /* CTA_PROTOINFO */
	       + nla_total_size(0) /* CTA_HELP */
	       + nla_total_size(NF_CT_HELPER_NAME_LEN) /* CTA_HELP_NAME */
	       + ctnetlink_secctx_size(ct)
#ifdef CONFIG_NF_NAT_NEEDED
	       + 2 * nla_total_size(0) /* CTA_NAT_SEQ_ADJ_ORIG|REPL */
	       + 6 * nla_total_size(sizeof(u_int32_t)) /* CTA_NAT_SEQ_OFFSET */
#endif
#ifdef CONFIG_NF_CONNTRACK_MARK
	       + nla_total_size(sizeof(u_int32_t)) /* CTA_MARK */
#endif
	       + ctnetlink_proto_size(ct)
	       ;
}

static int
ctnetlink_nfqueue_build(struct sk_buff *skb, struct nf_conn *ct)
{
	struct nlattr *nest_parms;

	rcu_read_lock();
	nest_parms = nla_nest_start(skb, CTA_TUPLE_ORIG | NLA_F_NESTED);
	if (!nest_parms)
		goto nla_put_failure;
	if (ctnetlink_dump_tuples(skb, nf_ct_tuple(ct, IP_CT_DIR_ORIGINAL)) < 0)
		goto nla_put_failure;
	nla_nest_end(skb, nest_parms);

	nest_parms = nla_nest_start(skb, CTA_TUPLE_REPLY | NLA_F_NESTED);
	if (!nest_parms)
		goto nla_put_failure;
	if (ctnetlink_dump_tuples(skb, nf_ct_tuple(ct, IP_CT_DIR_REPLY)) < 0)
		goto nla_put_failure;
	nla_nest_end(skb, nest_parms);

	if (nf_ct_zone(ct)) {
		if (nla_put_be16(skb, CTA_ZONE, htons(nf_ct_zone(ct))))
			goto nla_put_failure;
	}

	if (ctnetlink_dump_id(skb, ct) < 0)
		goto nla_put_failure;

	if (ctnetlink_dump_status(skb, ct) < 0)
		goto nla_put_failure;

	if (ctnetlink_dump_timeout(skb, ct) < 0)
		goto nla_put_failure;

	if (ctnetlink_dump_protoinfo(skb, ct) < 0)
		goto nla_put_failure;

	if (ctnetlink_dump_helpinfo(skb, ct) < 0)
		goto nla_put_failure;

#ifdef CONFIG_NF_CONNTRACK_SECMARK
	if (ct->secmark && ctnetlink_dump_secctx(skb, ct) < 0)
		goto nla_put_failure;
#endif
	if (ct->master && ctnetlink_dump_master(skb, ct) < 0)
		goto nla_put_failure;

	if ((ct->status & IPS_SEQ_ADJUST) &&
	    ctnetlink_dump_nat_seq_adj(skb, ct) < 0)
		goto nla_put_failure;

#ifdef CONFIG_NF_CONNTRACK_MARK
	if (ct->mark && ctnetlink_dump_mark(skb, ct) < 0)
		goto nla_put_failure;
#endif
	rcu_read_unlock();
	return 0;

nla_put_failure:
	rcu_read_unlock();
	return -ENOSPC;
}

static int
ctnetlink_nfqueue_parse_ct(const struct nlattr *cda[], struct nf_conn *ct)
{
	int err;

	if (cda[CTA_TIMEOUT]) {
		err = ctnetlink_change_timeout(ct, cda);
		if (err < 0)
			return err;
	}
	if (cda[CTA_STATUS]) {
		err = ctnetlink_change_status(ct, cda);
		if (err < 0)
			return err;
	}
	if (cda[CTA_HELP]) {
		err = ctnetlink_change_helper(ct, cda);
		if (err < 0)
			return err;
	}
#if defined(CONFIG_NF_CONNTRACK_MARK)
	if (cda[CTA_MARK])
		ct->mark = ntohl(nla_get_be32(cda[CTA_MARK]));
#endif
	return 0;
}

static int
ctnetlink_nfqueue_parse(const struct nlattr *attr, struct nf_conn *ct)
{
	struct nlattr *cda[CTA_MAX+1];

	nla_parse_nested(cda, CTA_MAX, attr, ct_nla_policy);

	return ctnetlink_nfqueue_parse_ct((const struct nlattr **)cda, ct);
}

static struct nfq_ct_hook ctnetlink_nfqueue_hook = {
	.build_size	= ctnetlink_nfqueue_build_size,
	.build		= ctnetlink_nfqueue_build,
	.parse		= ctnetlink_nfqueue_parse,
};
#endif /* CONFIG_NETFILTER_NETLINK_QUEUE */

/***********************************************************************
 * EXPECT
 ***********************************************************************/
@@ -2424,7 +2558,11 @@ 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)
	/* setup interaction between nf_queue and nf_conntrack_netlink. */
	RCU_INIT_POINTER(nfq_ct_hook, &ctnetlink_nfqueue_hook);
#endif
	return 0;

err_unreg_exp_subsys:
@@ -2442,6 +2580,10 @@ 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)
	RCU_INIT_POINTER(nfq_ct_hook, NULL);
#endif
}

module_init(ctnetlink_init);
+48 −0
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@
#include <linux/list.h>
#include <net/sock.h>
#include <net/netfilter/nf_queue.h>
#include <net/netfilter/nf_conntrack.h>

#include <linux/atomic.h>

@@ -233,6 +234,9 @@ 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);

	size =    NLMSG_SPACE(sizeof(struct nfgenmsg))
		+ nla_total_size(sizeof(struct nfqnl_msg_packet_hdr))
@@ -266,6 +270,17 @@ 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;
		}
	}

	skb = alloc_skb(size, GFP_ATOMIC);
	if (!skb)
@@ -389,6 +404,24 @@ 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;
	}

	nlh->nlmsg_len = skb->tail - old_tail;
	return skb;

@@ -632,6 +665,7 @@ static const struct nla_policy nfqa_verdict_policy[NFQA_MAX+1] = {
	[NFQA_VERDICT_HDR]	= { .len = sizeof(struct nfqnl_msg_verdict_hdr) },
	[NFQA_MARK]		= { .type = NLA_U32 },
	[NFQA_PAYLOAD]		= { .type = NLA_UNSPEC },
	[NFQA_CT]		= { .type = NLA_UNSPEC },
};

static const struct nla_policy nfqa_verdict_batch_policy[NFQA_MAX+1] = {
@@ -732,6 +766,7 @@ 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;

	queue = instance_lookup(queue_num);
	if (!queue)
@@ -750,6 +785,19 @@ 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 & NFQA_CFG_F_CONNTRACK) && nfqa[NFQA_CT]) {
		enum ip_conntrack_info ctinfo;
		struct nf_conn *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();

	if (nfqa[NFQA_PAYLOAD]) {
		if (nfqnl_mangle(nla_data(nfqa[NFQA_PAYLOAD]),
				 nla_len(nfqa[NFQA_PAYLOAD]), entry) < 0)