Skip to content

Commit

Permalink
netfilter: add glue code to integrate nfnetlink_queue and ctnetlink
Browse files Browse the repository at this point in the history
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: Pablo Neira Ayuso <pablo@netfilter.org>
  • Loading branch information
Pablo Neira Ayuso committed Jun 16, 2012
1 parent 1afc567 commit 9cb0176
Show file tree
Hide file tree
Showing 5 changed files with 208 additions and 1 deletion.
10 changes: 10 additions & 0 deletions include/linux/netfilter.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions include/linux/netfilter/nfnetlink_queue.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
};
Expand Down Expand Up @@ -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 changes: 4 additions & 0 deletions net/netfilter/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
144 changes: 143 additions & 1 deletion net/netfilter/nf_conntrack_netlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
***********************************************************************/
Expand Down Expand Up @@ -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:
Expand All @@ -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);
Expand Down
48 changes: 48 additions & 0 deletions net/netfilter/nfnetlink_queue.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>

Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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] = {
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand Down

0 comments on commit 9cb0176

Please sign in to comment.