Skip to content

Commit 12f7a50

Browse files
committed
netfilter: add user-space connection tracking helper infrastructure
There are good reasons to supports helpers in user-space instead: * Rapid connection tracking helper development, as developing code in user-space is usually faster. * Reliability: A buggy helper does not crash the kernel. Moreover, we can monitor the helper process and restart it in case of problems. * Security: Avoid complex string matching and mangling in kernel-space running in privileged mode. Going further, we can even think about running user-space helpers as a non-root process. * Extensibility: It allows the development of very specific helpers (most likely non-standard proprietary protocols) that are very likely not to be accepted for mainline inclusion in the form of kernel-space connection tracking helpers. This patch adds the infrastructure to allow the implementation of user-space conntrack helpers by means of the new nfnetlink subsystem `nfnetlink_cthelper' and the existing queueing infrastructure (nfnetlink_queue). I had to add the new hook NF_IP6_PRI_CONNTRACK_HELPER to register ipv[4|6]_helper which results from splitting ipv[4|6]_confirm into two pieces. This change is required not to break NAT sequence adjustment and conntrack confirmation for traffic that is enqueued to our user-space conntrack helpers. Basic operation, in a few steps: 1) Register user-space helper by means of `nfct': nfct helper add ftp inet tcp [ It must be a valid existing helper supported by conntrack-tools ] 2) Add rules to enable the FTP user-space helper which is used to track traffic going to TCP port 21. For locally generated packets: iptables -I OUTPUT -t raw -p tcp --dport 21 -j CT --helper ftp For non-locally generated packets: iptables -I PREROUTING -t raw -p tcp --dport 21 -j CT --helper ftp 3) Run the test conntrackd in helper mode (see example files under doc/helper/conntrackd.conf conntrackd 4) Generate FTP traffic going, if everything is OK, then conntrackd should create expectations (you can check that with `conntrack': conntrack -E expect [NEW] 301 proto=6 src=192.168.1.136 dst=130.89.148.12 sport=0 dport=54037 mask-src=255.255.255.255 mask-dst=255.255.255.255 sport=0 dport=65535 master-src=192.168.1.136 master-dst=130.89.148.12 sport=57127 dport=21 class=0 helper=ftp [DESTROY] 301 proto=6 src=192.168.1.136 dst=130.89.148.12 sport=0 dport=54037 mask-src=255.255.255.255 mask-dst=255.255.255.255 sport=0 dport=65535 master-src=192.168.1.136 master-dst=130.89.148.12 sport=57127 dport=21 class=0 helper=ftp This confirms that our test helper is receiving packets including the conntrack information, and adding expectations in kernel-space. The user-space helper can also store its private tracking information in the conntrack structure in the kernel via the CTA_HELP_INFO. The kernel will consider this a binary blob whose layout is unknown. This information will be included in the information that is transfered to user-space via glue code that integrates nfnetlink_queue and ctnetlink. Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
1 parent ae243be commit 12f7a50

File tree

12 files changed

+839
-26
lines changed

12 files changed

+839
-26
lines changed

include/linux/netfilter/Kbuild

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ header-y += nfnetlink.h
1010
header-y += nfnetlink_acct.h
1111
header-y += nfnetlink_compat.h
1212
header-y += nfnetlink_conntrack.h
13+
header-y += nfnetlink_cthelper.h
1314
header-y += nfnetlink_cttimeout.h
1415
header-y += nfnetlink_log.h
1516
header-y += nfnetlink_queue.h

include/linux/netfilter/nfnetlink.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ struct nfgenmsg {
5050
#define NFNL_SUBSYS_IPSET 6
5151
#define NFNL_SUBSYS_ACCT 7
5252
#define NFNL_SUBSYS_CTNETLINK_TIMEOUT 8
53-
#define NFNL_SUBSYS_COUNT 9
53+
#define NFNL_SUBSYS_CTHELPER 9
54+
#define NFNL_SUBSYS_COUNT 10
5455

5556
#ifdef __KERNEL__
5657

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#ifndef _NFNL_CTHELPER_H_
2+
#define _NFNL_CTHELPER_H_
3+
4+
#define NFCT_HELPER_STATUS_DISABLED 0
5+
#define NFCT_HELPER_STATUS_ENABLED 1
6+
7+
enum nfnl_acct_msg_types {
8+
NFNL_MSG_CTHELPER_NEW,
9+
NFNL_MSG_CTHELPER_GET,
10+
NFNL_MSG_CTHELPER_DEL,
11+
NFNL_MSG_CTHELPER_MAX
12+
};
13+
14+
enum nfnl_cthelper_type {
15+
NFCTH_UNSPEC,
16+
NFCTH_NAME,
17+
NFCTH_TUPLE,
18+
NFCTH_QUEUE_NUM,
19+
NFCTH_POLICY,
20+
NFCTH_PRIV_DATA_LEN,
21+
NFCTH_STATUS,
22+
__NFCTH_MAX
23+
};
24+
#define NFCTH_MAX (__NFCTH_MAX - 1)
25+
26+
enum nfnl_cthelper_policy_type {
27+
NFCTH_POLICY_SET_UNSPEC,
28+
NFCTH_POLICY_SET_NUM,
29+
NFCTH_POLICY_SET,
30+
NFCTH_POLICY_SET1 = NFCTH_POLICY_SET,
31+
NFCTH_POLICY_SET2,
32+
NFCTH_POLICY_SET3,
33+
NFCTH_POLICY_SET4,
34+
__NFCTH_POLICY_SET_MAX
35+
};
36+
#define NFCTH_POLICY_SET_MAX (__NFCTH_POLICY_SET_MAX - 1)
37+
38+
enum nfnl_cthelper_pol_type {
39+
NFCTH_POLICY_UNSPEC,
40+
NFCTH_POLICY_NAME,
41+
NFCTH_POLICY_EXPECT_MAX,
42+
NFCTH_POLICY_EXPECT_TIMEOUT,
43+
__NFCTH_POLICY_MAX
44+
};
45+
#define NFCTH_POLICY_MAX (__NFCTH_POLICY_MAX - 1)
46+
47+
enum nfnl_cthelper_tuple_type {
48+
NFCTH_TUPLE_UNSPEC,
49+
NFCTH_TUPLE_L3PROTONUM,
50+
NFCTH_TUPLE_L4PROTONUM,
51+
__NFCTH_TUPLE_MAX,
52+
};
53+
#define NFCTH_TUPLE_MAX (__NFCTH_TUPLE_MAX - 1)
54+
55+
#endif /* _NFNL_CTHELPER_H */

include/linux/netfilter_ipv4.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ enum nf_ip_hook_priorities {
6666
NF_IP_PRI_SECURITY = 50,
6767
NF_IP_PRI_NAT_SRC = 100,
6868
NF_IP_PRI_SELINUX_LAST = 225,
69+
NF_IP_PRI_CONNTRACK_HELPER = 300,
6970
NF_IP_PRI_CONNTRACK_CONFIRM = INT_MAX,
7071
NF_IP_PRI_LAST = INT_MAX,
7172
};

include/linux/netfilter_ipv6.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ enum nf_ip6_hook_priorities {
7171
NF_IP6_PRI_SECURITY = 50,
7272
NF_IP6_PRI_NAT_SRC = 100,
7373
NF_IP6_PRI_SELINUX_LAST = 225,
74+
NF_IP6_PRI_CONNTRACK_HELPER = 300,
7475
NF_IP6_PRI_LAST = INT_MAX,
7576
};
7677

include/net/netfilter/nf_conntrack_helper.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@
1515

1616
struct module;
1717

18+
enum nf_ct_helper_flags {
19+
NF_CT_HELPER_F_USERSPACE = (1 << 0),
20+
NF_CT_HELPER_F_CONFIGURED = (1 << 1),
21+
};
22+
1823
#define NF_CT_HELPER_NAME_LEN 16
1924

2025
struct nf_conntrack_helper {
@@ -42,6 +47,9 @@ struct nf_conntrack_helper {
4247
int (*from_nlattr)(struct nlattr *attr, struct nf_conn *ct);
4348
int (*to_nlattr)(struct sk_buff *skb, const struct nf_conn *ct);
4449
unsigned int expect_class_max;
50+
51+
unsigned int flags;
52+
unsigned int queue_num; /* For user-space helpers. */
4553
};
4654

4755
extern struct nf_conntrack_helper *
@@ -96,4 +104,7 @@ nf_ct_helper_expectfn_find_by_name(const char *name);
96104
struct nf_ct_helper_expectfn *
97105
nf_ct_helper_expectfn_find_by_symbol(const void *symbol);
98106

107+
extern struct hlist_head *nf_ct_helper_hash;
108+
extern unsigned int nf_ct_helper_hsize;
109+
99110
#endif /*_NF_CONNTRACK_HELPER_H*/

net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -95,11 +95,11 @@ static int ipv4_get_l4proto(const struct sk_buff *skb, unsigned int nhoff,
9595
return NF_ACCEPT;
9696
}
9797

98-
static unsigned int ipv4_confirm(unsigned int hooknum,
99-
struct sk_buff *skb,
100-
const struct net_device *in,
101-
const struct net_device *out,
102-
int (*okfn)(struct sk_buff *))
98+
static unsigned int ipv4_helper(unsigned int hooknum,
99+
struct sk_buff *skb,
100+
const struct net_device *in,
101+
const struct net_device *out,
102+
int (*okfn)(struct sk_buff *))
103103
{
104104
struct nf_conn *ct;
105105
enum ip_conntrack_info ctinfo;
@@ -110,24 +110,38 @@ static unsigned int ipv4_confirm(unsigned int hooknum,
110110
/* This is where we call the helper: as the packet goes out. */
111111
ct = nf_ct_get(skb, &ctinfo);
112112
if (!ct || ctinfo == IP_CT_RELATED_REPLY)
113-
goto out;
113+
return NF_ACCEPT;
114114

115115
help = nfct_help(ct);
116116
if (!help)
117-
goto out;
117+
return NF_ACCEPT;
118118

119119
/* rcu_read_lock()ed by nf_hook_slow */
120120
helper = rcu_dereference(help->helper);
121121
if (!helper)
122-
goto out;
122+
return NF_ACCEPT;
123123

124124
ret = helper->help(skb, skb_network_offset(skb) + ip_hdrlen(skb),
125125
ct, ctinfo);
126-
if (ret != NF_ACCEPT) {
126+
if (ret != NF_ACCEPT && (ret & NF_VERDICT_MASK) != NF_QUEUE) {
127127
nf_log_packet(NFPROTO_IPV4, hooknum, skb, in, out, NULL,
128128
"nf_ct_%s: dropping packet", helper->name);
129-
return ret;
130129
}
130+
return ret;
131+
}
132+
133+
static unsigned int ipv4_confirm(unsigned int hooknum,
134+
struct sk_buff *skb,
135+
const struct net_device *in,
136+
const struct net_device *out,
137+
int (*okfn)(struct sk_buff *))
138+
{
139+
struct nf_conn *ct;
140+
enum ip_conntrack_info ctinfo;
141+
142+
ct = nf_ct_get(skb, &ctinfo);
143+
if (!ct || ctinfo == IP_CT_RELATED_REPLY)
144+
goto out;
131145

132146
/* adjust seqs for loopback traffic only in outgoing direction */
133147
if (test_bit(IPS_SEQ_ADJUST_BIT, &ct->status) &&
@@ -184,13 +198,27 @@ static struct nf_hook_ops ipv4_conntrack_ops[] __read_mostly = {
184198
.hooknum = NF_INET_LOCAL_OUT,
185199
.priority = NF_IP_PRI_CONNTRACK,
186200
},
201+
{
202+
.hook = ipv4_helper,
203+
.owner = THIS_MODULE,
204+
.pf = NFPROTO_IPV4,
205+
.hooknum = NF_INET_POST_ROUTING,
206+
.priority = NF_IP_PRI_CONNTRACK_HELPER,
207+
},
187208
{
188209
.hook = ipv4_confirm,
189210
.owner = THIS_MODULE,
190211
.pf = NFPROTO_IPV4,
191212
.hooknum = NF_INET_POST_ROUTING,
192213
.priority = NF_IP_PRI_CONNTRACK_CONFIRM,
193214
},
215+
{
216+
.hook = ipv4_helper,
217+
.owner = THIS_MODULE,
218+
.pf = NFPROTO_IPV4,
219+
.hooknum = NF_INET_LOCAL_IN,
220+
.priority = NF_IP_PRI_CONNTRACK_HELPER,
221+
},
194222
{
195223
.hook = ipv4_confirm,
196224
.owner = THIS_MODULE,

net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -143,11 +143,11 @@ static int ipv6_get_l4proto(const struct sk_buff *skb, unsigned int nhoff,
143143
return NF_ACCEPT;
144144
}
145145

146-
static unsigned int ipv6_confirm(unsigned int hooknum,
147-
struct sk_buff *skb,
148-
const struct net_device *in,
149-
const struct net_device *out,
150-
int (*okfn)(struct sk_buff *))
146+
static unsigned int ipv6_helper(unsigned int hooknum,
147+
struct sk_buff *skb,
148+
const struct net_device *in,
149+
const struct net_device *out,
150+
int (*okfn)(struct sk_buff *))
151151
{
152152
struct nf_conn *ct;
153153
const struct nf_conn_help *help;
@@ -161,15 +161,15 @@ static unsigned int ipv6_confirm(unsigned int hooknum,
161161
/* This is where we call the helper: as the packet goes out. */
162162
ct = nf_ct_get(skb, &ctinfo);
163163
if (!ct || ctinfo == IP_CT_RELATED_REPLY)
164-
goto out;
164+
return NF_ACCEPT;
165165

166166
help = nfct_help(ct);
167167
if (!help)
168-
goto out;
168+
return NF_ACCEPT;
169169
/* rcu_read_lock()ed by nf_hook_slow */
170170
helper = rcu_dereference(help->helper);
171171
if (!helper)
172-
goto out;
172+
return NF_ACCEPT;
173173

174174
protoff = nf_ct_ipv6_skip_exthdr(skb, extoff, &pnum,
175175
skb->len - extoff);
@@ -179,12 +179,19 @@ static unsigned int ipv6_confirm(unsigned int hooknum,
179179
}
180180

181181
ret = helper->help(skb, protoff, ct, ctinfo);
182-
if (ret != NF_ACCEPT) {
182+
if (ret != NF_ACCEPT && (ret & NF_VERDICT_MASK) != NF_QUEUE) {
183183
nf_log_packet(NFPROTO_IPV6, hooknum, skb, in, out, NULL,
184184
"nf_ct_%s: dropping packet", helper->name);
185-
return ret;
186185
}
187-
out:
186+
return ret;
187+
}
188+
189+
static unsigned int ipv6_confirm(unsigned int hooknum,
190+
struct sk_buff *skb,
191+
const struct net_device *in,
192+
const struct net_device *out,
193+
int (*okfn)(struct sk_buff *))
194+
{
188195
/* We've seen it coming out the other side: confirm it */
189196
return nf_conntrack_confirm(skb);
190197
}
@@ -253,13 +260,27 @@ static struct nf_hook_ops ipv6_conntrack_ops[] __read_mostly = {
253260
.hooknum = NF_INET_LOCAL_OUT,
254261
.priority = NF_IP6_PRI_CONNTRACK,
255262
},
263+
{
264+
.hook = ipv6_helper,
265+
.owner = THIS_MODULE,
266+
.pf = NFPROTO_IPV6,
267+
.hooknum = NF_INET_POST_ROUTING,
268+
.priority = NF_IP6_PRI_CONNTRACK_HELPER,
269+
},
256270
{
257271
.hook = ipv6_confirm,
258272
.owner = THIS_MODULE,
259273
.pf = NFPROTO_IPV6,
260274
.hooknum = NF_INET_POST_ROUTING,
261275
.priority = NF_IP6_PRI_LAST,
262276
},
277+
{
278+
.hook = ipv6_helper,
279+
.owner = THIS_MODULE,
280+
.pf = NFPROTO_IPV6,
281+
.hooknum = NF_INET_LOCAL_IN,
282+
.priority = NF_IP6_PRI_CONNTRACK_HELPER,
283+
},
263284
{
264285
.hook = ipv6_confirm,
265286
.owner = THIS_MODULE,

net/netfilter/Kconfig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@ tristate "Netfilter NFACCT over NFNETLINK interface"
1212
If this option is enabled, the kernel will include support
1313
for extended accounting via NFNETLINK.
1414

15+
config NETFILTER_NETLINK_CTHELPER
16+
tristate "Netfilter CTHELPER over NFNETLINK interface"
17+
depends on NETFILTER_ADVANCED
18+
select NETFILTER_NETLINK
19+
help
20+
If this option is enabled, the kernel will include support
21+
for user-space connection tracking helpers via NFNETLINK.
22+
1523
config NETFILTER_NETLINK_QUEUE
1624
tristate "Netfilter NFQUEUE over NFNETLINK interface"
1725
depends on NETFILTER_ADVANCED

net/netfilter/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ obj-$(CONFIG_NETFILTER) = netfilter.o
99

1010
obj-$(CONFIG_NETFILTER_NETLINK) += nfnetlink.o
1111
obj-$(CONFIG_NETFILTER_NETLINK_ACCT) += nfnetlink_acct.o
12+
obj-$(CONFIG_NETFILTER_NETLINK_CTHELPER) += nfnetlink_cthelper.o
1213
obj-$(CONFIG_NETFILTER_NETLINK_QUEUE) += nfnetlink_queue.o
1314
obj-$(CONFIG_NETFILTER_NETLINK_LOG) += nfnetlink_log.o
1415

0 commit comments

Comments
 (0)