aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@netfilter.org>2006-01-05 15:19:05 -0500
committerDavid S. Miller <davem@davemloft.net>2006-01-05 15:19:05 -0500
commitc1d10adb4a521de5760112853f42aaeefcec96eb (patch)
treec7ba283aa0b9016c8403fa6589b7b3418f71acda /net
parent205d67c7d942c057648148fefb17e46f77e3efd6 (diff)
[NETFILTER]: Add ctnetlink port for nf_conntrack
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org> Signed-off-by: Patrick McHardy <kaber@trash.net> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c47
-rw-r--r--net/ipv4/netfilter/nf_conntrack_proto_icmp.c97
-rw-r--r--net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c47
-rw-r--r--net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c77
-rw-r--r--net/netfilter/Kconfig7
-rw-r--r--net/netfilter/Makefile3
-rw-r--r--net/netfilter/nf_conntrack_core.c232
-rw-r--r--net/netfilter/nf_conntrack_netlink.c1642
-rw-r--r--net/netfilter/nf_conntrack_proto_tcp.c71
-rw-r--r--net/netfilter/nf_conntrack_proto_udp.c10
-rw-r--r--net/netfilter/nf_conntrack_standalone.c42
11 files changed, 2214 insertions, 61 deletions
diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
index 8202c1c0afad..385867efd481 100644
--- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
+++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
@@ -392,6 +392,48 @@ getorigdst(struct sock *sk, int optval, void __user *user, int *len)
392 return -ENOENT; 392 return -ENOENT;
393} 393}
394 394
395#if defined(CONFIG_NF_CT_NETLINK) || \
396 defined(CONFIG_NF_CT_NETLINK_MODULE)
397
398#include <linux/netfilter/nfnetlink.h>
399#include <linux/netfilter/nfnetlink_conntrack.h>
400
401static int ipv4_tuple_to_nfattr(struct sk_buff *skb,
402 const struct nf_conntrack_tuple *tuple)
403{
404 NFA_PUT(skb, CTA_IP_V4_SRC, sizeof(u_int32_t),
405 &tuple->src.u3.ip);
406 NFA_PUT(skb, CTA_IP_V4_DST, sizeof(u_int32_t),
407 &tuple->dst.u3.ip);
408 return 0;
409
410nfattr_failure:
411 return -1;
412}
413
414static const size_t cta_min_ip[CTA_IP_MAX] = {
415 [CTA_IP_V4_SRC-1] = sizeof(u_int32_t),
416 [CTA_IP_V4_DST-1] = sizeof(u_int32_t),
417};
418
419static int ipv4_nfattr_to_tuple(struct nfattr *tb[],
420 struct nf_conntrack_tuple *t)
421{
422 if (!tb[CTA_IP_V4_SRC-1] || !tb[CTA_IP_V4_DST-1])
423 return -EINVAL;
424
425 if (nfattr_bad_size(tb, CTA_IP_MAX, cta_min_ip))
426 return -EINVAL;
427
428 t->src.u3.ip =
429 *(u_int32_t *)NFA_DATA(tb[CTA_IP_V4_SRC-1]);
430 t->dst.u3.ip =
431 *(u_int32_t *)NFA_DATA(tb[CTA_IP_V4_DST-1]);
432
433 return 0;
434}
435#endif
436
395static struct nf_sockopt_ops so_getorigdst = { 437static struct nf_sockopt_ops so_getorigdst = {
396 .pf = PF_INET, 438 .pf = PF_INET,
397 .get_optmin = SO_ORIGINAL_DST, 439 .get_optmin = SO_ORIGINAL_DST,
@@ -408,6 +450,11 @@ struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv4 = {
408 .print_conntrack = ipv4_print_conntrack, 450 .print_conntrack = ipv4_print_conntrack,
409 .prepare = ipv4_prepare, 451 .prepare = ipv4_prepare,
410 .get_features = ipv4_get_features, 452 .get_features = ipv4_get_features,
453#if defined(CONFIG_NF_CT_NETLINK) || \
454 defined(CONFIG_NF_CT_NETLINK_MODULE)
455 .tuple_to_nfattr = ipv4_tuple_to_nfattr,
456 .nfattr_to_tuple = ipv4_nfattr_to_tuple,
457#endif
411 .me = THIS_MODULE, 458 .me = THIS_MODULE,
412}; 459};
413 460
diff --git a/net/ipv4/netfilter/nf_conntrack_proto_icmp.c b/net/ipv4/netfilter/nf_conntrack_proto_icmp.c
index 7ddb5c08f7b8..52dc175be39a 100644
--- a/net/ipv4/netfilter/nf_conntrack_proto_icmp.c
+++ b/net/ipv4/netfilter/nf_conntrack_proto_icmp.c
@@ -50,20 +50,21 @@ static int icmp_pkt_to_tuple(const struct sk_buff *skb,
50 return 1; 50 return 1;
51} 51}
52 52
53/* Add 1; spaces filled with 0. */
54static const u_int8_t invmap[] = {
55 [ICMP_ECHO] = ICMP_ECHOREPLY + 1,
56 [ICMP_ECHOREPLY] = ICMP_ECHO + 1,
57 [ICMP_TIMESTAMP] = ICMP_TIMESTAMPREPLY + 1,
58 [ICMP_TIMESTAMPREPLY] = ICMP_TIMESTAMP + 1,
59 [ICMP_INFO_REQUEST] = ICMP_INFO_REPLY + 1,
60 [ICMP_INFO_REPLY] = ICMP_INFO_REQUEST + 1,
61 [ICMP_ADDRESS] = ICMP_ADDRESSREPLY + 1,
62 [ICMP_ADDRESSREPLY] = ICMP_ADDRESS + 1
63};
64
53static int icmp_invert_tuple(struct nf_conntrack_tuple *tuple, 65static int icmp_invert_tuple(struct nf_conntrack_tuple *tuple,
54 const struct nf_conntrack_tuple *orig) 66 const struct nf_conntrack_tuple *orig)
55{ 67{
56 /* Add 1; spaces filled with 0. */
57 static u_int8_t invmap[]
58 = { [ICMP_ECHO] = ICMP_ECHOREPLY + 1,
59 [ICMP_ECHOREPLY] = ICMP_ECHO + 1,
60 [ICMP_TIMESTAMP] = ICMP_TIMESTAMPREPLY + 1,
61 [ICMP_TIMESTAMPREPLY] = ICMP_TIMESTAMP + 1,
62 [ICMP_INFO_REQUEST] = ICMP_INFO_REPLY + 1,
63 [ICMP_INFO_REPLY] = ICMP_INFO_REQUEST + 1,
64 [ICMP_ADDRESS] = ICMP_ADDRESSREPLY + 1,
65 [ICMP_ADDRESSREPLY] = ICMP_ADDRESS + 1};
66
67 if (orig->dst.u.icmp.type >= sizeof(invmap) 68 if (orig->dst.u.icmp.type >= sizeof(invmap)
68 || !invmap[orig->dst.u.icmp.type]) 69 || !invmap[orig->dst.u.icmp.type])
69 return 0; 70 return 0;
@@ -120,11 +121,12 @@ static int icmp_packet(struct nf_conn *ct,
120static int icmp_new(struct nf_conn *conntrack, 121static int icmp_new(struct nf_conn *conntrack,
121 const struct sk_buff *skb, unsigned int dataoff) 122 const struct sk_buff *skb, unsigned int dataoff)
122{ 123{
123 static u_int8_t valid_new[] 124 static const u_int8_t valid_new[] = {
124 = { [ICMP_ECHO] = 1, 125 [ICMP_ECHO] = 1,
125 [ICMP_TIMESTAMP] = 1, 126 [ICMP_TIMESTAMP] = 1,
126 [ICMP_INFO_REQUEST] = 1, 127 [ICMP_INFO_REQUEST] = 1,
127 [ICMP_ADDRESS] = 1 }; 128 [ICMP_ADDRESS] = 1
129 };
128 130
129 if (conntrack->tuplehash[0].tuple.dst.u.icmp.type >= sizeof(valid_new) 131 if (conntrack->tuplehash[0].tuple.dst.u.icmp.type >= sizeof(valid_new)
130 || !valid_new[conntrack->tuplehash[0].tuple.dst.u.icmp.type]) { 132 || !valid_new[conntrack->tuplehash[0].tuple.dst.u.icmp.type]) {
@@ -168,7 +170,7 @@ icmp_error_message(struct sk_buff *skb,
168 return -NF_ACCEPT; 170 return -NF_ACCEPT;
169 } 171 }
170 172
171 innerproto = nf_ct_find_proto(PF_INET, inside->ip.protocol); 173 innerproto = __nf_ct_proto_find(PF_INET, inside->ip.protocol);
172 dataoff = skb->nh.iph->ihl*4 + sizeof(inside->icmp); 174 dataoff = skb->nh.iph->ihl*4 + sizeof(inside->icmp);
173 /* Are they talking about one of our connections? */ 175 /* Are they talking about one of our connections? */
174 if (!nf_ct_get_tuple(skb, dataoff, dataoff + inside->ip.ihl*4, PF_INET, 176 if (!nf_ct_get_tuple(skb, dataoff, dataoff + inside->ip.ihl*4, PF_INET,
@@ -281,6 +283,60 @@ checksum_skipped:
281 return icmp_error_message(skb, ctinfo, hooknum); 283 return icmp_error_message(skb, ctinfo, hooknum);
282} 284}
283 285
286#if defined(CONFIG_NF_CT_NETLINK) || \
287 defined(CONFIG_NF_CT_NETLINK_MODULE)
288
289#include <linux/netfilter/nfnetlink.h>
290#include <linux/netfilter/nfnetlink_conntrack.h>
291
292static int icmp_tuple_to_nfattr(struct sk_buff *skb,
293 const struct nf_conntrack_tuple *t)
294{
295 NFA_PUT(skb, CTA_PROTO_ICMP_ID, sizeof(u_int16_t),
296 &t->src.u.icmp.id);
297 NFA_PUT(skb, CTA_PROTO_ICMP_TYPE, sizeof(u_int8_t),
298 &t->dst.u.icmp.type);
299 NFA_PUT(skb, CTA_PROTO_ICMP_CODE, sizeof(u_int8_t),
300 &t->dst.u.icmp.code);
301
302 return 0;
303
304nfattr_failure:
305 return -1;
306}
307
308static const size_t cta_min_proto[CTA_PROTO_MAX] = {
309 [CTA_PROTO_ICMP_TYPE-1] = sizeof(u_int8_t),
310 [CTA_PROTO_ICMP_CODE-1] = sizeof(u_int8_t),
311 [CTA_PROTO_ICMP_ID-1] = sizeof(u_int16_t)
312};
313
314static int icmp_nfattr_to_tuple(struct nfattr *tb[],
315 struct nf_conntrack_tuple *tuple)
316{
317 if (!tb[CTA_PROTO_ICMP_TYPE-1]
318 || !tb[CTA_PROTO_ICMP_CODE-1]
319 || !tb[CTA_PROTO_ICMP_ID-1])
320 return -EINVAL;
321
322 if (nfattr_bad_size(tb, CTA_PROTO_MAX, cta_min_proto))
323 return -EINVAL;
324
325 tuple->dst.u.icmp.type =
326 *(u_int8_t *)NFA_DATA(tb[CTA_PROTO_ICMP_TYPE-1]);
327 tuple->dst.u.icmp.code =
328 *(u_int8_t *)NFA_DATA(tb[CTA_PROTO_ICMP_CODE-1]);
329 tuple->src.u.icmp.id =
330 *(u_int16_t *)NFA_DATA(tb[CTA_PROTO_ICMP_ID-1]);
331
332 if (tuple->dst.u.icmp.type >= sizeof(invmap)
333 || !invmap[tuple->dst.u.icmp.type])
334 return -EINVAL;
335
336 return 0;
337}
338#endif
339
284struct nf_conntrack_protocol nf_conntrack_protocol_icmp = 340struct nf_conntrack_protocol nf_conntrack_protocol_icmp =
285{ 341{
286 .list = { NULL, NULL }, 342 .list = { NULL, NULL },
@@ -295,7 +351,12 @@ struct nf_conntrack_protocol nf_conntrack_protocol_icmp =
295 .new = icmp_new, 351 .new = icmp_new,
296 .error = icmp_error, 352 .error = icmp_error,
297 .destroy = NULL, 353 .destroy = NULL,
298 .me = NULL 354 .me = NULL,
355#if defined(CONFIG_NF_CT_NETLINK) || \
356 defined(CONFIG_NF_CT_NETLINK_MODULE)
357 .tuple_to_nfattr = icmp_tuple_to_nfattr,
358 .nfattr_to_tuple = icmp_nfattr_to_tuple,
359#endif
299}; 360};
300 361
301EXPORT_SYMBOL(nf_conntrack_protocol_icmp); 362EXPORT_SYMBOL(nf_conntrack_protocol_icmp);
diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
index 753a3ae8502b..704fbbe74874 100644
--- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
+++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
@@ -401,6 +401,48 @@ static ctl_table nf_ct_net_table[] = {
401}; 401};
402#endif 402#endif
403 403
404#if defined(CONFIG_NF_CT_NETLINK) || \
405 defined(CONFIG_NF_CT_NETLINK_MODULE)
406
407#include <linux/netfilter/nfnetlink.h>
408#include <linux/netfilter/nfnetlink_conntrack.h>
409
410static int ipv6_tuple_to_nfattr(struct sk_buff *skb,
411 const struct nf_conntrack_tuple *tuple)
412{
413 NFA_PUT(skb, CTA_IP_V6_SRC, sizeof(u_int32_t) * 4,
414 &tuple->src.u3.ip6);
415 NFA_PUT(skb, CTA_IP_V6_DST, sizeof(u_int32_t) * 4,
416 &tuple->dst.u3.ip6);
417 return 0;
418
419nfattr_failure:
420 return -1;
421}
422
423static const size_t cta_min_ip[CTA_IP_MAX] = {
424 [CTA_IP_V6_SRC-1] = sizeof(u_int32_t)*4,
425 [CTA_IP_V6_DST-1] = sizeof(u_int32_t)*4,
426};
427
428static int ipv6_nfattr_to_tuple(struct nfattr *tb[],
429 struct nf_conntrack_tuple *t)
430{
431 if (!tb[CTA_IP_V6_SRC-1] || !tb[CTA_IP_V6_DST-1])
432 return -EINVAL;
433
434 if (nfattr_bad_size(tb, CTA_IP_MAX, cta_min_ip))
435 return -EINVAL;
436
437 memcpy(&t->src.u3.ip6, NFA_DATA(tb[CTA_IP_V6_SRC-1]),
438 sizeof(u_int32_t) * 4);
439 memcpy(&t->dst.u3.ip6, NFA_DATA(tb[CTA_IP_V6_DST-1]),
440 sizeof(u_int32_t) * 4);
441
442 return 0;
443}
444#endif
445
404struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv6 = { 446struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv6 = {
405 .l3proto = PF_INET6, 447 .l3proto = PF_INET6,
406 .name = "ipv6", 448 .name = "ipv6",
@@ -409,6 +451,11 @@ struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv6 = {
409 .print_tuple = ipv6_print_tuple, 451 .print_tuple = ipv6_print_tuple,
410 .print_conntrack = ipv6_print_conntrack, 452 .print_conntrack = ipv6_print_conntrack,
411 .prepare = ipv6_prepare, 453 .prepare = ipv6_prepare,
454#if defined(CONFIG_NF_CT_NETLINK) || \
455 defined(CONFIG_NF_CT_NETLINK_MODULE)
456 .tuple_to_nfattr = ipv6_tuple_to_nfattr,
457 .nfattr_to_tuple = ipv6_nfattr_to_tuple,
458#endif
412 .get_features = ipv6_get_features, 459 .get_features = ipv6_get_features,
413 .me = THIS_MODULE, 460 .me = THIS_MODULE,
414}; 461};
diff --git a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c
index a7e03cfacd06..09945c333055 100644
--- a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c
+++ b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c
@@ -57,17 +57,17 @@ static int icmpv6_pkt_to_tuple(const struct sk_buff *skb,
57 return 1; 57 return 1;
58} 58}
59 59
60/* Add 1; spaces filled with 0. */
61static u_int8_t invmap[] = {
62 [ICMPV6_ECHO_REQUEST - 128] = ICMPV6_ECHO_REPLY + 1,
63 [ICMPV6_ECHO_REPLY - 128] = ICMPV6_ECHO_REQUEST + 1,
64 [ICMPV6_NI_QUERY - 128] = ICMPV6_NI_QUERY + 1,
65 [ICMPV6_NI_REPLY - 128] = ICMPV6_NI_REPLY +1
66};
67
60static int icmpv6_invert_tuple(struct nf_conntrack_tuple *tuple, 68static int icmpv6_invert_tuple(struct nf_conntrack_tuple *tuple,
61 const struct nf_conntrack_tuple *orig) 69 const struct nf_conntrack_tuple *orig)
62{ 70{
63 /* Add 1; spaces filled with 0. */
64 static u_int8_t invmap[] = {
65 [ICMPV6_ECHO_REQUEST - 128] = ICMPV6_ECHO_REPLY + 1,
66 [ICMPV6_ECHO_REPLY - 128] = ICMPV6_ECHO_REQUEST + 1,
67 [ICMPV6_NI_QUERY - 128] = ICMPV6_NI_QUERY + 1,
68 [ICMPV6_NI_REPLY - 128] = ICMPV6_NI_REPLY +1
69 };
70
71 int type = orig->dst.u.icmp.type - 128; 71 int type = orig->dst.u.icmp.type - 128;
72 if (type < 0 || type >= sizeof(invmap) || !invmap[type]) 72 if (type < 0 || type >= sizeof(invmap) || !invmap[type])
73 return 0; 73 return 0;
@@ -185,7 +185,7 @@ icmpv6_error_message(struct sk_buff *skb,
185 return -NF_ACCEPT; 185 return -NF_ACCEPT;
186 } 186 }
187 187
188 inproto = nf_ct_find_proto(PF_INET6, inprotonum); 188 inproto = __nf_ct_proto_find(PF_INET6, inprotonum);
189 189
190 /* Are they talking about one of our connections? */ 190 /* Are they talking about one of our connections? */
191 if (!nf_ct_get_tuple(skb, inip6off, inprotoff, PF_INET6, inprotonum, 191 if (!nf_ct_get_tuple(skb, inip6off, inprotoff, PF_INET6, inprotonum,
@@ -255,6 +255,60 @@ skipped:
255 return icmpv6_error_message(skb, dataoff, ctinfo, hooknum); 255 return icmpv6_error_message(skb, dataoff, ctinfo, hooknum);
256} 256}
257 257
258#if defined(CONFIG_NF_CT_NETLINK) || \
259 defined(CONFIG_NF_CT_NETLINK_MODULE)
260
261#include <linux/netfilter/nfnetlink.h>
262#include <linux/netfilter/nfnetlink_conntrack.h>
263static int icmpv6_tuple_to_nfattr(struct sk_buff *skb,
264 const struct nf_conntrack_tuple *t)
265{
266 NFA_PUT(skb, CTA_PROTO_ICMPV6_ID, sizeof(u_int16_t),
267 &t->src.u.icmp.id);
268 NFA_PUT(skb, CTA_PROTO_ICMPV6_TYPE, sizeof(u_int8_t),
269 &t->dst.u.icmp.type);
270 NFA_PUT(skb, CTA_PROTO_ICMPV6_CODE, sizeof(u_int8_t),
271 &t->dst.u.icmp.code);
272
273 return 0;
274
275nfattr_failure:
276 return -1;
277}
278
279static const size_t cta_min_proto[CTA_PROTO_MAX] = {
280 [CTA_PROTO_ICMPV6_TYPE-1] = sizeof(u_int8_t),
281 [CTA_PROTO_ICMPV6_CODE-1] = sizeof(u_int8_t),
282 [CTA_PROTO_ICMPV6_ID-1] = sizeof(u_int16_t)
283};
284
285static int icmpv6_nfattr_to_tuple(struct nfattr *tb[],
286 struct nf_conntrack_tuple *tuple)
287{
288 if (!tb[CTA_PROTO_ICMPV6_TYPE-1]
289 || !tb[CTA_PROTO_ICMPV6_CODE-1]
290 || !tb[CTA_PROTO_ICMPV6_ID-1])
291 return -EINVAL;
292
293 if (nfattr_bad_size(tb, CTA_PROTO_MAX, cta_min_proto))
294 return -EINVAL;
295
296 tuple->dst.u.icmp.type =
297 *(u_int8_t *)NFA_DATA(tb[CTA_PROTO_ICMPV6_TYPE-1]);
298 tuple->dst.u.icmp.code =
299 *(u_int8_t *)NFA_DATA(tb[CTA_PROTO_ICMPV6_CODE-1]);
300 tuple->src.u.icmp.id =
301 *(u_int16_t *)NFA_DATA(tb[CTA_PROTO_ICMPV6_ID-1]);
302
303 if (tuple->dst.u.icmp.type < 128
304 || tuple->dst.u.icmp.type - 128 >= sizeof(invmap)
305 || !invmap[tuple->dst.u.icmp.type - 128])
306 return -EINVAL;
307
308 return 0;
309}
310#endif
311
258struct nf_conntrack_protocol nf_conntrack_protocol_icmpv6 = 312struct nf_conntrack_protocol nf_conntrack_protocol_icmpv6 =
259{ 313{
260 .l3proto = PF_INET6, 314 .l3proto = PF_INET6,
@@ -267,6 +321,11 @@ struct nf_conntrack_protocol nf_conntrack_protocol_icmpv6 =
267 .packet = icmpv6_packet, 321 .packet = icmpv6_packet,
268 .new = icmpv6_new, 322 .new = icmpv6_new,
269 .error = icmpv6_error, 323 .error = icmpv6_error,
324#if defined(CONFIG_NF_CT_NETLINK) || \
325 defined(CONFIG_NF_CT_NETLINK_MODULE)
326 .tuple_to_nfattr = icmpv6_tuple_to_nfattr,
327 .nfattr_to_tuple = icmpv6_nfattr_to_tuple,
328#endif
270}; 329};
271 330
272EXPORT_SYMBOL(nf_conntrack_protocol_icmpv6); 331EXPORT_SYMBOL(nf_conntrack_protocol_icmpv6);
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
index 794c41d19b28..7d55f9cbd853 100644
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
@@ -95,4 +95,11 @@ config NF_CONNTRACK_FTP
95 95
96 To compile it as a module, choose M here. If unsure, say N. 96 To compile it as a module, choose M here. If unsure, say N.
97 97
98config NF_CT_NETLINK
99 tristate 'Connection tracking netlink interface (EXPERIMENTAL)'
100 depends on EXPERIMENTAL && NF_CONNTRACK && NETFILTER_NETLINK
101 depends on NF_CONNTRACK!=y || NETFILTER_NETLINK!=m
102 help
103 This option enables support for a netlink-based userspace interface
104
98endmenu 105endmenu
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile
index 55f019ad2c08..cb2183145c37 100644
--- a/net/netfilter/Makefile
+++ b/net/netfilter/Makefile
@@ -13,3 +13,6 @@ obj-$(CONFIG_NF_CONNTRACK_FTP) += nf_conntrack_ftp.o
13 13
14# SCTP protocol connection tracking 14# SCTP protocol connection tracking
15obj-$(CONFIG_NF_CT_PROTO_SCTP) += nf_conntrack_proto_sctp.o 15obj-$(CONFIG_NF_CT_PROTO_SCTP) += nf_conntrack_proto_sctp.o
16
17# netlink interface for nf_conntrack
18obj-$(CONFIG_NF_CT_NETLINK) += nf_conntrack_netlink.o
diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c
index 0c5b01d732d8..62bb509f05d4 100644
--- a/net/netfilter/nf_conntrack_core.c
+++ b/net/netfilter/nf_conntrack_core.c
@@ -82,6 +82,8 @@ unsigned int nf_ct_log_invalid;
82static LIST_HEAD(unconfirmed); 82static LIST_HEAD(unconfirmed);
83static int nf_conntrack_vmalloc; 83static int nf_conntrack_vmalloc;
84 84
85static unsigned int nf_conntrack_next_id = 1;
86static unsigned int nf_conntrack_expect_next_id = 1;
85#ifdef CONFIG_NF_CONNTRACK_EVENTS 87#ifdef CONFIG_NF_CONNTRACK_EVENTS
86struct notifier_block *nf_conntrack_chain; 88struct notifier_block *nf_conntrack_chain;
87struct notifier_block *nf_conntrack_expect_chain; 89struct notifier_block *nf_conntrack_expect_chain;
@@ -184,7 +186,7 @@ DECLARE_MUTEX(nf_ct_cache_mutex);
184 186
185extern struct nf_conntrack_protocol nf_conntrack_generic_protocol; 187extern struct nf_conntrack_protocol nf_conntrack_generic_protocol;
186struct nf_conntrack_protocol * 188struct nf_conntrack_protocol *
187nf_ct_find_proto(u_int16_t l3proto, u_int8_t protocol) 189__nf_ct_proto_find(u_int16_t l3proto, u_int8_t protocol)
188{ 190{
189 if (unlikely(nf_ct_protos[l3proto] == NULL)) 191 if (unlikely(nf_ct_protos[l3proto] == NULL))
190 return &nf_conntrack_generic_protocol; 192 return &nf_conntrack_generic_protocol;
@@ -192,6 +194,50 @@ nf_ct_find_proto(u_int16_t l3proto, u_int8_t protocol)
192 return nf_ct_protos[l3proto][protocol]; 194 return nf_ct_protos[l3proto][protocol];
193} 195}
194 196
197/* this is guaranteed to always return a valid protocol helper, since
198 * it falls back to generic_protocol */
199struct nf_conntrack_protocol *
200nf_ct_proto_find_get(u_int16_t l3proto, u_int8_t protocol)
201{
202 struct nf_conntrack_protocol *p;
203
204 preempt_disable();
205 p = __nf_ct_proto_find(l3proto, protocol);
206 if (p) {
207 if (!try_module_get(p->me))
208 p = &nf_conntrack_generic_protocol;
209 }
210 preempt_enable();
211
212 return p;
213}
214
215void nf_ct_proto_put(struct nf_conntrack_protocol *p)
216{
217 module_put(p->me);
218}
219
220struct nf_conntrack_l3proto *
221nf_ct_l3proto_find_get(u_int16_t l3proto)
222{
223 struct nf_conntrack_l3proto *p;
224
225 preempt_disable();
226 p = __nf_ct_l3proto_find(l3proto);
227 if (p) {
228 if (!try_module_get(p->me))
229 p = &nf_conntrack_generic_l3proto;
230 }
231 preempt_enable();
232
233 return p;
234}
235
236void nf_ct_l3proto_put(struct nf_conntrack_l3proto *p)
237{
238 module_put(p->me);
239}
240
195static int nf_conntrack_hash_rnd_initted; 241static int nf_conntrack_hash_rnd_initted;
196static unsigned int nf_conntrack_hash_rnd; 242static unsigned int nf_conntrack_hash_rnd;
197 243
@@ -384,7 +430,7 @@ nf_ct_invert_tuple(struct nf_conntrack_tuple *inverse,
384} 430}
385 431
386/* nf_conntrack_expect helper functions */ 432/* nf_conntrack_expect helper functions */
387static void nf_ct_unlink_expect(struct nf_conntrack_expect *exp) 433void nf_ct_unlink_expect(struct nf_conntrack_expect *exp)
388{ 434{
389 ASSERT_WRITE_LOCK(&nf_conntrack_lock); 435 ASSERT_WRITE_LOCK(&nf_conntrack_lock);
390 NF_CT_ASSERT(!timer_pending(&exp->timeout)); 436 NF_CT_ASSERT(!timer_pending(&exp->timeout));
@@ -404,6 +450,33 @@ static void expectation_timed_out(unsigned long ul_expect)
404 nf_conntrack_expect_put(exp); 450 nf_conntrack_expect_put(exp);
405} 451}
406 452
453struct nf_conntrack_expect *
454__nf_conntrack_expect_find(const struct nf_conntrack_tuple *tuple)
455{
456 struct nf_conntrack_expect *i;
457
458 list_for_each_entry(i, &nf_conntrack_expect_list, list) {
459 if (nf_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask)) {
460 atomic_inc(&i->use);
461 return i;
462 }
463 }
464 return NULL;
465}
466
467/* Just find a expectation corresponding to a tuple. */
468struct nf_conntrack_expect *
469nf_conntrack_expect_find(const struct nf_conntrack_tuple *tuple)
470{
471 struct nf_conntrack_expect *i;
472
473 read_lock_bh(&nf_conntrack_lock);
474 i = __nf_conntrack_expect_find(tuple);
475 read_unlock_bh(&nf_conntrack_lock);
476
477 return i;
478}
479
407/* If an expectation for this connection is found, it gets delete from 480/* If an expectation for this connection is found, it gets delete from
408 * global list then returned. */ 481 * global list then returned. */
409static struct nf_conntrack_expect * 482static struct nf_conntrack_expect *
@@ -432,7 +505,7 @@ find_expectation(const struct nf_conntrack_tuple *tuple)
432} 505}
433 506
434/* delete all expectations for this conntrack */ 507/* delete all expectations for this conntrack */
435static void remove_expectations(struct nf_conn *ct) 508void nf_ct_remove_expectations(struct nf_conn *ct)
436{ 509{
437 struct nf_conntrack_expect *i, *tmp; 510 struct nf_conntrack_expect *i, *tmp;
438 511
@@ -462,7 +535,7 @@ clean_from_lists(struct nf_conn *ct)
462 LIST_DELETE(&nf_conntrack_hash[hr], &ct->tuplehash[IP_CT_DIR_REPLY]); 535 LIST_DELETE(&nf_conntrack_hash[hr], &ct->tuplehash[IP_CT_DIR_REPLY]);
463 536
464 /* Destroy all pending expectations */ 537 /* Destroy all pending expectations */
465 remove_expectations(ct); 538 nf_ct_remove_expectations(ct);
466} 539}
467 540
468static void 541static void
@@ -482,12 +555,11 @@ destroy_conntrack(struct nf_conntrack *nfct)
482 /* To make sure we don't get any weird locking issues here: 555 /* To make sure we don't get any weird locking issues here:
483 * destroy_conntrack() MUST NOT be called with a write lock 556 * destroy_conntrack() MUST NOT be called with a write lock
484 * to nf_conntrack_lock!!! -HW */ 557 * to nf_conntrack_lock!!! -HW */
485 l3proto = nf_ct_find_l3proto(ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.l3num); 558 l3proto = __nf_ct_l3proto_find(ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.l3num);
486 if (l3proto && l3proto->destroy) 559 if (l3proto && l3proto->destroy)
487 l3proto->destroy(ct); 560 l3proto->destroy(ct);
488 561
489 proto = nf_ct_find_proto(ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.l3num, 562 proto = __nf_ct_proto_find(ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.l3num, ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.protonum);
490 ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.protonum);
491 if (proto && proto->destroy) 563 if (proto && proto->destroy)
492 proto->destroy(ct); 564 proto->destroy(ct);
493 565
@@ -499,7 +571,7 @@ destroy_conntrack(struct nf_conntrack *nfct)
499 * except TFTP can create an expectation on the first packet, 571 * except TFTP can create an expectation on the first packet,
500 * before connection is in the list, so we need to clean here, 572 * before connection is in the list, so we need to clean here,
501 * too. */ 573 * too. */
502 remove_expectations(ct); 574 nf_ct_remove_expectations(ct);
503 575
504 /* We overload first tuple to link into unconfirmed list. */ 576 /* We overload first tuple to link into unconfirmed list. */
505 if (!nf_ct_is_confirmed(ct)) { 577 if (!nf_ct_is_confirmed(ct)) {
@@ -540,7 +612,7 @@ conntrack_tuple_cmp(const struct nf_conntrack_tuple_hash *i,
540 && nf_ct_tuple_equal(tuple, &i->tuple); 612 && nf_ct_tuple_equal(tuple, &i->tuple);
541} 613}
542 614
543static struct nf_conntrack_tuple_hash * 615struct nf_conntrack_tuple_hash *
544__nf_conntrack_find(const struct nf_conntrack_tuple *tuple, 616__nf_conntrack_find(const struct nf_conntrack_tuple *tuple,
545 const struct nf_conn *ignored_conntrack) 617 const struct nf_conn *ignored_conntrack)
546{ 618{
@@ -575,6 +647,29 @@ nf_conntrack_find_get(const struct nf_conntrack_tuple *tuple,
575 return h; 647 return h;
576} 648}
577 649
650static void __nf_conntrack_hash_insert(struct nf_conn *ct,
651 unsigned int hash,
652 unsigned int repl_hash)
653{
654 ct->id = ++nf_conntrack_next_id;
655 list_prepend(&nf_conntrack_hash[hash],
656 &ct->tuplehash[IP_CT_DIR_ORIGINAL].list);
657 list_prepend(&nf_conntrack_hash[repl_hash],
658 &ct->tuplehash[IP_CT_DIR_REPLY].list);
659}
660
661void nf_conntrack_hash_insert(struct nf_conn *ct)
662{
663 unsigned int hash, repl_hash;
664
665 hash = hash_conntrack(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
666 repl_hash = hash_conntrack(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
667
668 write_lock_bh(&nf_conntrack_lock);
669 __nf_conntrack_hash_insert(ct, hash, repl_hash);
670 write_unlock_bh(&nf_conntrack_lock);
671}
672
578/* Confirm a connection given skb; places it in hash table */ 673/* Confirm a connection given skb; places it in hash table */
579int 674int
580__nf_conntrack_confirm(struct sk_buff **pskb) 675__nf_conntrack_confirm(struct sk_buff **pskb)
@@ -621,10 +716,7 @@ __nf_conntrack_confirm(struct sk_buff **pskb)
621 /* Remove from unconfirmed list */ 716 /* Remove from unconfirmed list */
622 list_del(&ct->tuplehash[IP_CT_DIR_ORIGINAL].list); 717 list_del(&ct->tuplehash[IP_CT_DIR_ORIGINAL].list);
623 718
624 list_prepend(&nf_conntrack_hash[hash], 719 __nf_conntrack_hash_insert(ct, hash, repl_hash);
625 &ct->tuplehash[IP_CT_DIR_ORIGINAL]);
626 list_prepend(&nf_conntrack_hash[repl_hash],
627 &ct->tuplehash[IP_CT_DIR_REPLY]);
628 /* Timer relative to confirmation time, not original 720 /* Timer relative to confirmation time, not original
629 setting time, otherwise we'd get timer wrap in 721 setting time, otherwise we'd get timer wrap in
630 weird delay cases. */ 722 weird delay cases. */
@@ -708,13 +800,41 @@ static inline int helper_cmp(const struct nf_conntrack_helper *i,
708} 800}
709 801
710static struct nf_conntrack_helper * 802static struct nf_conntrack_helper *
711nf_ct_find_helper(const struct nf_conntrack_tuple *tuple) 803__nf_ct_helper_find(const struct nf_conntrack_tuple *tuple)
712{ 804{
713 return LIST_FIND(&helpers, helper_cmp, 805 return LIST_FIND(&helpers, helper_cmp,
714 struct nf_conntrack_helper *, 806 struct nf_conntrack_helper *,
715 tuple); 807 tuple);
716} 808}
717 809
810struct nf_conntrack_helper *
811nf_ct_helper_find_get( const struct nf_conntrack_tuple *tuple)
812{
813 struct nf_conntrack_helper *helper;
814
815 /* need nf_conntrack_lock to assure that helper exists until
816 * try_module_get() is called */
817 read_lock_bh(&nf_conntrack_lock);
818
819 helper = __nf_ct_helper_find(tuple);
820 if (helper) {
821 /* need to increase module usage count to assure helper will
822 * not go away while the caller is e.g. busy putting a
823 * conntrack in the hash that uses the helper */
824 if (!try_module_get(helper->me))
825 helper = NULL;
826 }
827
828 read_unlock_bh(&nf_conntrack_lock);
829
830 return helper;
831}
832
833void nf_ct_helper_put(struct nf_conntrack_helper *helper)
834{
835 module_put(helper->me);
836}
837
718static struct nf_conn * 838static struct nf_conn *
719__nf_conntrack_alloc(const struct nf_conntrack_tuple *orig, 839__nf_conntrack_alloc(const struct nf_conntrack_tuple *orig,
720 const struct nf_conntrack_tuple *repl, 840 const struct nf_conntrack_tuple *repl,
@@ -744,7 +864,7 @@ __nf_conntrack_alloc(const struct nf_conntrack_tuple *orig,
744 /* find features needed by this conntrack. */ 864 /* find features needed by this conntrack. */
745 features = l3proto->get_features(orig); 865 features = l3proto->get_features(orig);
746 read_lock_bh(&nf_conntrack_lock); 866 read_lock_bh(&nf_conntrack_lock);
747 if (nf_ct_find_helper(repl) != NULL) 867 if (__nf_ct_helper_find(repl) != NULL)
748 features |= NF_CT_F_HELP; 868 features |= NF_CT_F_HELP;
749 read_unlock_bh(&nf_conntrack_lock); 869 read_unlock_bh(&nf_conntrack_lock);
750 870
@@ -794,7 +914,7 @@ struct nf_conn *nf_conntrack_alloc(const struct nf_conntrack_tuple *orig,
794{ 914{
795 struct nf_conntrack_l3proto *l3proto; 915 struct nf_conntrack_l3proto *l3proto;
796 916
797 l3proto = nf_ct_find_l3proto(orig->src.l3num); 917 l3proto = __nf_ct_l3proto_find(orig->src.l3num);
798 return __nf_conntrack_alloc(orig, repl, l3proto); 918 return __nf_conntrack_alloc(orig, repl, l3proto);
799} 919}
800 920
@@ -853,7 +973,7 @@ init_conntrack(const struct nf_conntrack_tuple *tuple,
853 nf_conntrack_get(&conntrack->master->ct_general); 973 nf_conntrack_get(&conntrack->master->ct_general);
854 NF_CT_STAT_INC(expect_new); 974 NF_CT_STAT_INC(expect_new);
855 } else { 975 } else {
856 conntrack->helper = nf_ct_find_helper(&repl_tuple); 976 conntrack->helper = __nf_ct_helper_find(&repl_tuple);
857 977
858 NF_CT_STAT_INC(new); 978 NF_CT_STAT_INC(new);
859 } 979 }
@@ -947,13 +1067,13 @@ nf_conntrack_in(int pf, unsigned int hooknum, struct sk_buff **pskb)
947 return NF_ACCEPT; 1067 return NF_ACCEPT;
948 } 1068 }
949 1069
950 l3proto = nf_ct_find_l3proto((u_int16_t)pf); 1070 l3proto = __nf_ct_l3proto_find((u_int16_t)pf);
951 if ((ret = l3proto->prepare(pskb, hooknum, &dataoff, &protonum)) <= 0) { 1071 if ((ret = l3proto->prepare(pskb, hooknum, &dataoff, &protonum)) <= 0) {
952 DEBUGP("not prepared to track yet or error occured\n"); 1072 DEBUGP("not prepared to track yet or error occured\n");
953 return -ret; 1073 return -ret;
954 } 1074 }
955 1075
956 proto = nf_ct_find_proto((u_int16_t)pf, protonum); 1076 proto = __nf_ct_proto_find((u_int16_t)pf, protonum);
957 1077
958 /* It may be an special packet, error, unclean... 1078 /* It may be an special packet, error, unclean...
959 * inverse of the return code tells to the netfilter 1079 * inverse of the return code tells to the netfilter
@@ -1002,9 +1122,9 @@ int nf_ct_invert_tuplepr(struct nf_conntrack_tuple *inverse,
1002 const struct nf_conntrack_tuple *orig) 1122 const struct nf_conntrack_tuple *orig)
1003{ 1123{
1004 return nf_ct_invert_tuple(inverse, orig, 1124 return nf_ct_invert_tuple(inverse, orig,
1005 nf_ct_find_l3proto(orig->src.l3num), 1125 __nf_ct_l3proto_find(orig->src.l3num),
1006 nf_ct_find_proto(orig->src.l3num, 1126 __nf_ct_proto_find(orig->src.l3num,
1007 orig->dst.protonum)); 1127 orig->dst.protonum));
1008} 1128}
1009 1129
1010/* Would two expected things clash? */ 1130/* Would two expected things clash? */
@@ -1096,6 +1216,7 @@ static void nf_conntrack_expect_insert(struct nf_conntrack_expect *exp)
1096 exp->timeout.expires = jiffies + exp->master->helper->timeout * HZ; 1216 exp->timeout.expires = jiffies + exp->master->helper->timeout * HZ;
1097 add_timer(&exp->timeout); 1217 add_timer(&exp->timeout);
1098 1218
1219 exp->id = ++nf_conntrack_expect_next_id;
1099 atomic_inc(&exp->use); 1220 atomic_inc(&exp->use);
1100 NF_CT_STAT_INC(expect_create); 1221 NF_CT_STAT_INC(expect_create);
1101} 1222}
@@ -1176,7 +1297,7 @@ void nf_conntrack_alter_reply(struct nf_conn *conntrack,
1176 1297
1177 conntrack->tuplehash[IP_CT_DIR_REPLY].tuple = *newreply; 1298 conntrack->tuplehash[IP_CT_DIR_REPLY].tuple = *newreply;
1178 if (!conntrack->master && conntrack->expecting == 0) 1299 if (!conntrack->master && conntrack->expecting == 0)
1179 conntrack->helper = nf_ct_find_helper(newreply); 1300 conntrack->helper = __nf_ct_helper_find(newreply);
1180 write_unlock_bh(&nf_conntrack_lock); 1301 write_unlock_bh(&nf_conntrack_lock);
1181} 1302}
1182 1303
@@ -1201,6 +1322,19 @@ int nf_conntrack_helper_register(struct nf_conntrack_helper *me)
1201 return 0; 1322 return 0;
1202} 1323}
1203 1324
1325struct nf_conntrack_helper *
1326__nf_conntrack_helper_find_byname(const char *name)
1327{
1328 struct nf_conntrack_helper *h;
1329
1330 list_for_each_entry(h, &helpers, list) {
1331 if (!strcmp(h->name, name))
1332 return h;
1333 }
1334
1335 return NULL;
1336}
1337
1204static inline int unhelp(struct nf_conntrack_tuple_hash *i, 1338static inline int unhelp(struct nf_conntrack_tuple_hash *i,
1205 const struct nf_conntrack_helper *me) 1339 const struct nf_conntrack_helper *me)
1206{ 1340{
@@ -1284,6 +1418,51 @@ void __nf_ct_refresh_acct(struct nf_conn *ct,
1284 nf_conntrack_event_cache(event, skb); 1418 nf_conntrack_event_cache(event, skb);
1285} 1419}
1286 1420
1421#if defined(CONFIG_NF_CT_NETLINK) || \
1422 defined(CONFIG_NF_CT_NETLINK_MODULE)
1423
1424#include <linux/netfilter/nfnetlink.h>
1425#include <linux/netfilter/nfnetlink_conntrack.h>
1426
1427/* Generic function for tcp/udp/sctp/dccp and alike. This needs to be
1428 * in ip_conntrack_core, since we don't want the protocols to autoload
1429 * or depend on ctnetlink */
1430int nf_ct_port_tuple_to_nfattr(struct sk_buff *skb,
1431 const struct nf_conntrack_tuple *tuple)
1432{
1433 NFA_PUT(skb, CTA_PROTO_SRC_PORT, sizeof(u_int16_t),
1434 &tuple->src.u.tcp.port);
1435 NFA_PUT(skb, CTA_PROTO_DST_PORT, sizeof(u_int16_t),
1436 &tuple->dst.u.tcp.port);
1437 return 0;
1438
1439nfattr_failure:
1440 return -1;
1441}
1442
1443static const size_t cta_min_proto[CTA_PROTO_MAX] = {
1444 [CTA_PROTO_SRC_PORT-1] = sizeof(u_int16_t),
1445 [CTA_PROTO_DST_PORT-1] = sizeof(u_int16_t)
1446};
1447
1448int nf_ct_port_nfattr_to_tuple(struct nfattr *tb[],
1449 struct nf_conntrack_tuple *t)
1450{
1451 if (!tb[CTA_PROTO_SRC_PORT-1] || !tb[CTA_PROTO_DST_PORT-1])
1452 return -EINVAL;
1453
1454 if (nfattr_bad_size(tb, CTA_PROTO_MAX, cta_min_proto))
1455 return -EINVAL;
1456
1457 t->src.u.tcp.port =
1458 *(u_int16_t *)NFA_DATA(tb[CTA_PROTO_SRC_PORT-1]);
1459 t->dst.u.tcp.port =
1460 *(u_int16_t *)NFA_DATA(tb[CTA_PROTO_DST_PORT-1]);
1461
1462 return 0;
1463}
1464#endif
1465
1287/* Used by ipt_REJECT and ip6t_REJECT. */ 1466/* Used by ipt_REJECT and ip6t_REJECT. */
1288void __nf_conntrack_attach(struct sk_buff *nskb, struct sk_buff *skb) 1467void __nf_conntrack_attach(struct sk_buff *nskb, struct sk_buff *skb)
1289{ 1468{
@@ -1366,6 +1545,11 @@ static void free_conntrack_hash(struct list_head *hash, int vmalloced, int size)
1366 get_order(sizeof(struct list_head) * size)); 1545 get_order(sizeof(struct list_head) * size));
1367} 1546}
1368 1547
1548void nf_conntrack_flush()
1549{
1550 nf_ct_iterate_cleanup(kill_all, NULL);
1551}
1552
1369/* Mishearing the voices in his head, our hero wonders how he's 1553/* Mishearing the voices in his head, our hero wonders how he's
1370 supposed to kill the mall. */ 1554 supposed to kill the mall. */
1371void nf_conntrack_cleanup(void) 1555void nf_conntrack_cleanup(void)
@@ -1379,7 +1563,7 @@ void nf_conntrack_cleanup(void)
1379 1563
1380 nf_ct_event_cache_flush(); 1564 nf_ct_event_cache_flush();
1381 i_see_dead_people: 1565 i_see_dead_people:
1382 nf_ct_iterate_cleanup(kill_all, NULL); 1566 nf_conntrack_flush();
1383 if (atomic_read(&nf_conntrack_count) != 0) { 1567 if (atomic_read(&nf_conntrack_count) != 0) {
1384 schedule(); 1568 schedule();
1385 goto i_see_dead_people; 1569 goto i_see_dead_people;
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
new file mode 100644
index 000000000000..4f2e50952a12
--- /dev/null
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -0,0 +1,1642 @@
1/* Connection tracking via netlink socket. Allows for user space
2 * protocol helpers and general trouble making from userspace.
3 *
4 * (C) 2001 by Jay Schulist <jschlst@samba.org>
5 * (C) 2002-2005 by Harald Welte <laforge@gnumonks.org>
6 * (C) 2003 by Patrick Mchardy <kaber@trash.net>
7 * (C) 2005 by Pablo Neira Ayuso <pablo@eurodev.net>
8 *
9 * I've reworked this stuff to use attributes instead of conntrack
10 * structures. 5.44 am. I need more tea. --pablo 05/07/11.
11 *
12 * Initial connection tracking via netlink development funded and
13 * generally made possible by Network Robots, Inc. (www.networkrobots.com)
14 *
15 * Further development of this code funded by Astaro AG (http://www.astaro.com)
16 *
17 * This software may be used and distributed according to the terms
18 * of the GNU General Public License, incorporated herein by reference.
19 *
20 * Derived from ip_conntrack_netlink.c: Port by Pablo Neira Ayuso (05/11/14)
21 */
22
23#include <linux/init.h>
24#include <linux/module.h>
25#include <linux/kernel.h>
26#include <linux/types.h>
27#include <linux/timer.h>
28#include <linux/skbuff.h>
29#include <linux/errno.h>
30#include <linux/netlink.h>
31#include <linux/spinlock.h>
32#include <linux/notifier.h>
33
34#include <linux/netfilter.h>
35#include <net/netfilter/nf_conntrack.h>
36#include <net/netfilter/nf_conntrack_core.h>
37#include <net/netfilter/nf_conntrack_helper.h>
38#include <net/netfilter/nf_conntrack_l3proto.h>
39#include <net/netfilter/nf_conntrack_protocol.h>
40#include <linux/netfilter_ipv4/ip_nat_protocol.h>
41
42#include <linux/netfilter/nfnetlink.h>
43#include <linux/netfilter/nfnetlink_conntrack.h>
44
45MODULE_LICENSE("GPL");
46
47static char __initdata version[] = "0.92";
48
49#if 0
50#define DEBUGP printk
51#else
52#define DEBUGP(format, args...)
53#endif
54
55
56static inline int
57ctnetlink_dump_tuples_proto(struct sk_buff *skb,
58 const struct nf_conntrack_tuple *tuple)
59{
60 struct nf_conntrack_protocol *proto;
61 int ret = 0;
62
63 NFA_PUT(skb, CTA_PROTO_NUM, sizeof(u_int8_t), &tuple->dst.protonum);
64
65 /* If no protocol helper is found, this function will return the
66 * generic protocol helper, so proto won't *ever* be NULL */
67 proto = nf_ct_proto_find_get(tuple->src.l3num, tuple->dst.protonum);
68 if (likely(proto->tuple_to_nfattr))
69 ret = proto->tuple_to_nfattr(skb, tuple);
70
71 nf_ct_proto_put(proto);
72
73 return ret;
74
75nfattr_failure:
76 return -1;
77}
78
79static inline int
80ctnetlink_dump_tuples(struct sk_buff *skb,
81 const struct nf_conntrack_tuple *tuple)
82{
83 struct nfattr *nest_parms;
84 struct nf_conntrack_l3proto *l3proto;
85 int ret = 0;
86
87 l3proto = nf_ct_l3proto_find_get(tuple->src.l3num);
88
89 nest_parms = NFA_NEST(skb, CTA_TUPLE_IP);
90 if (likely(l3proto->tuple_to_nfattr))
91 ret = l3proto->tuple_to_nfattr(skb, tuple);
92 NFA_NEST_END(skb, nest_parms);
93
94 nf_ct_l3proto_put(l3proto);
95
96 if (unlikely(ret < 0))
97 return ret;
98
99 nest_parms = NFA_NEST(skb, CTA_TUPLE_PROTO);
100 ret = ctnetlink_dump_tuples_proto(skb, tuple);
101 NFA_NEST_END(skb, nest_parms);
102
103 return ret;
104
105nfattr_failure:
106 return -1;
107}
108
109static inline int
110ctnetlink_dump_status(struct sk_buff *skb, const struct nf_conn *ct)
111{
112 u_int32_t status = htonl((u_int32_t) ct->status);
113 NFA_PUT(skb, CTA_STATUS, sizeof(status), &status);
114 return 0;
115
116nfattr_failure:
117 return -1;
118}
119
120static inline int
121ctnetlink_dump_timeout(struct sk_buff *skb, const struct nf_conn *ct)
122{
123 long timeout_l = ct->timeout.expires - jiffies;
124 u_int32_t timeout;
125
126 if (timeout_l < 0)
127 timeout = 0;
128 else
129 timeout = htonl(timeout_l / HZ);
130
131 NFA_PUT(skb, CTA_TIMEOUT, sizeof(timeout), &timeout);
132 return 0;
133
134nfattr_failure:
135 return -1;
136}
137
138static inline int
139ctnetlink_dump_protoinfo(struct sk_buff *skb, const struct nf_conn *ct)
140{
141 struct nf_conntrack_protocol *proto = nf_ct_proto_find_get(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num, ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum);
142 struct nfattr *nest_proto;
143 int ret;
144
145 if (!proto->to_nfattr) {
146 nf_ct_proto_put(proto);
147 return 0;
148 }
149
150 nest_proto = NFA_NEST(skb, CTA_PROTOINFO);
151
152 ret = proto->to_nfattr(skb, nest_proto, ct);
153
154 nf_ct_proto_put(proto);
155
156 NFA_NEST_END(skb, nest_proto);
157
158 return ret;
159
160nfattr_failure:
161 return -1;
162}
163
164static inline int
165ctnetlink_dump_helpinfo(struct sk_buff *skb, const struct nf_conn *ct)
166{
167 struct nfattr *nest_helper;
168
169 if (!ct->helper)
170 return 0;
171
172 nest_helper = NFA_NEST(skb, CTA_HELP);
173 NFA_PUT(skb, CTA_HELP_NAME, strlen(ct->helper->name), ct->helper->name);
174
175 if (ct->helper->to_nfattr)
176 ct->helper->to_nfattr(skb, ct);
177
178 NFA_NEST_END(skb, nest_helper);
179
180 return 0;
181
182nfattr_failure:
183 return -1;
184}
185
186#ifdef CONFIG_NF_CT_ACCT
187static inline int
188ctnetlink_dump_counters(struct sk_buff *skb, const struct nf_conn *ct,
189 enum ip_conntrack_dir dir)
190{
191 enum ctattr_type type = dir ? CTA_COUNTERS_REPLY: CTA_COUNTERS_ORIG;
192 struct nfattr *nest_count = NFA_NEST(skb, type);
193 u_int32_t tmp;
194
195 tmp = htonl(ct->counters[dir].packets);
196 NFA_PUT(skb, CTA_COUNTERS32_PACKETS, sizeof(u_int32_t), &tmp);
197
198 tmp = htonl(ct->counters[dir].bytes);
199 NFA_PUT(skb, CTA_COUNTERS32_BYTES, sizeof(u_int32_t), &tmp);
200
201 NFA_NEST_END(skb, nest_count);
202
203 return 0;
204
205nfattr_failure:
206 return -1;
207}
208#else
209#define ctnetlink_dump_counters(a, b, c) (0)
210#endif
211
212#ifdef CONFIG_NF_CONNTRACK_MARK
213static inline int
214ctnetlink_dump_mark(struct sk_buff *skb, const struct nf_conn *ct)
215{
216 u_int32_t mark = htonl(ct->mark);
217
218 NFA_PUT(skb, CTA_MARK, sizeof(u_int32_t), &mark);
219 return 0;
220
221nfattr_failure:
222 return -1;
223}
224#else
225#define ctnetlink_dump_mark(a, b) (0)
226#endif
227
228static inline int
229ctnetlink_dump_id(struct sk_buff *skb, const struct nf_conn *ct)
230{
231 u_int32_t id = htonl(ct->id);
232 NFA_PUT(skb, CTA_ID, sizeof(u_int32_t), &id);
233 return 0;
234
235nfattr_failure:
236 return -1;
237}
238
239static inline int
240ctnetlink_dump_use(struct sk_buff *skb, const struct nf_conn *ct)
241{
242 u_int32_t use = htonl(atomic_read(&ct->ct_general.use));
243
244 NFA_PUT(skb, CTA_USE, sizeof(u_int32_t), &use);
245 return 0;
246
247nfattr_failure:
248 return -1;
249}
250
251#define tuple(ct, dir) (&(ct)->tuplehash[dir].tuple)
252
253static int
254ctnetlink_fill_info(struct sk_buff *skb, u32 pid, u32 seq,
255 int event, int nowait,
256 const struct nf_conn *ct)
257{
258 struct nlmsghdr *nlh;
259 struct nfgenmsg *nfmsg;
260 struct nfattr *nest_parms;
261 unsigned char *b;
262
263 b = skb->tail;
264
265 event |= NFNL_SUBSYS_CTNETLINK << 8;
266 nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(struct nfgenmsg));
267 nfmsg = NLMSG_DATA(nlh);
268
269 nlh->nlmsg_flags = (nowait && pid) ? NLM_F_MULTI : 0;
270 nfmsg->nfgen_family =
271 ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num;
272 nfmsg->version = NFNETLINK_V0;
273 nfmsg->res_id = 0;
274
275 nest_parms = NFA_NEST(skb, CTA_TUPLE_ORIG);
276 if (ctnetlink_dump_tuples(skb, tuple(ct, IP_CT_DIR_ORIGINAL)) < 0)
277 goto nfattr_failure;
278 NFA_NEST_END(skb, nest_parms);
279
280 nest_parms = NFA_NEST(skb, CTA_TUPLE_REPLY);
281 if (ctnetlink_dump_tuples(skb, tuple(ct, IP_CT_DIR_REPLY)) < 0)
282 goto nfattr_failure;
283 NFA_NEST_END(skb, nest_parms);
284
285 if (ctnetlink_dump_status(skb, ct) < 0 ||
286 ctnetlink_dump_timeout(skb, ct) < 0 ||
287 ctnetlink_dump_counters(skb, ct, IP_CT_DIR_ORIGINAL) < 0 ||
288 ctnetlink_dump_counters(skb, ct, IP_CT_DIR_REPLY) < 0 ||
289 ctnetlink_dump_protoinfo(skb, ct) < 0 ||
290 ctnetlink_dump_helpinfo(skb, ct) < 0 ||
291 ctnetlink_dump_mark(skb, ct) < 0 ||
292 ctnetlink_dump_id(skb, ct) < 0 ||
293 ctnetlink_dump_use(skb, ct) < 0)
294 goto nfattr_failure;
295
296 nlh->nlmsg_len = skb->tail - b;
297 return skb->len;
298
299nlmsg_failure:
300nfattr_failure:
301 skb_trim(skb, b - skb->data);
302 return -1;
303}
304
305#ifdef CONFIG_NF_CONNTRACK_EVENTS
306static int ctnetlink_conntrack_event(struct notifier_block *this,
307 unsigned long events, void *ptr)
308{
309 struct nlmsghdr *nlh;
310 struct nfgenmsg *nfmsg;
311 struct nfattr *nest_parms;
312 struct nf_conn *ct = (struct nf_conn *)ptr;
313 struct sk_buff *skb;
314 unsigned int type;
315 unsigned char *b;
316 unsigned int flags = 0, group;
317
318 /* ignore our fake conntrack entry */
319 if (ct == &nf_conntrack_untracked)
320 return NOTIFY_DONE;
321
322 if (events & IPCT_DESTROY) {
323 type = IPCTNL_MSG_CT_DELETE;
324 group = NFNLGRP_CONNTRACK_DESTROY;
325 } else if (events & (IPCT_NEW | IPCT_RELATED)) {
326 type = IPCTNL_MSG_CT_NEW;
327 flags = NLM_F_CREATE|NLM_F_EXCL;
328 /* dump everything */
329 events = ~0UL;
330 group = NFNLGRP_CONNTRACK_NEW;
331 } else if (events & (IPCT_STATUS |
332 IPCT_PROTOINFO |
333 IPCT_HELPER |
334 IPCT_HELPINFO |
335 IPCT_NATINFO)) {
336 type = IPCTNL_MSG_CT_NEW;
337 group = NFNLGRP_CONNTRACK_UPDATE;
338 } else
339 return NOTIFY_DONE;
340
341 /* FIXME: Check if there are any listeners before, don't hurt performance */
342
343 skb = alloc_skb(NLMSG_GOODSIZE, GFP_ATOMIC);
344 if (!skb)
345 return NOTIFY_DONE;
346
347 b = skb->tail;
348
349 type |= NFNL_SUBSYS_CTNETLINK << 8;
350 nlh = NLMSG_PUT(skb, 0, 0, type, sizeof(struct nfgenmsg));
351 nfmsg = NLMSG_DATA(nlh);
352
353 nlh->nlmsg_flags = flags;
354 nfmsg->nfgen_family = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num;
355 nfmsg->version = NFNETLINK_V0;
356 nfmsg->res_id = 0;
357
358 nest_parms = NFA_NEST(skb, CTA_TUPLE_ORIG);
359 if (ctnetlink_dump_tuples(skb, tuple(ct, IP_CT_DIR_ORIGINAL)) < 0)
360 goto nfattr_failure;
361 NFA_NEST_END(skb, nest_parms);
362
363 nest_parms = NFA_NEST(skb, CTA_TUPLE_REPLY);
364 if (ctnetlink_dump_tuples(skb, tuple(ct, IP_CT_DIR_REPLY)) < 0)
365 goto nfattr_failure;
366 NFA_NEST_END(skb, nest_parms);
367
368 /* NAT stuff is now a status flag */
369 if ((events & IPCT_STATUS || events & IPCT_NATINFO)
370 && ctnetlink_dump_status(skb, ct) < 0)
371 goto nfattr_failure;
372 if (events & IPCT_REFRESH
373 && ctnetlink_dump_timeout(skb, ct) < 0)
374 goto nfattr_failure;
375 if (events & IPCT_PROTOINFO
376 && ctnetlink_dump_protoinfo(skb, ct) < 0)
377 goto nfattr_failure;
378 if (events & IPCT_HELPINFO
379 && ctnetlink_dump_helpinfo(skb, ct) < 0)
380 goto nfattr_failure;
381
382 if (ctnetlink_dump_counters(skb, ct, IP_CT_DIR_ORIGINAL) < 0 ||
383 ctnetlink_dump_counters(skb, ct, IP_CT_DIR_REPLY) < 0)
384 goto nfattr_failure;
385
386 nlh->nlmsg_len = skb->tail - b;
387 nfnetlink_send(skb, 0, group, 0);
388 return NOTIFY_DONE;
389
390nlmsg_failure:
391nfattr_failure:
392 kfree_skb(skb);
393 return NOTIFY_DONE;
394}
395#endif /* CONFIG_NF_CONNTRACK_EVENTS */
396
397static int ctnetlink_done(struct netlink_callback *cb)
398{
399 DEBUGP("entered %s\n", __FUNCTION__);
400 return 0;
401}
402
403static int
404ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
405{
406 struct nf_conn *ct = NULL;
407 struct nf_conntrack_tuple_hash *h;
408 struct list_head *i;
409 u_int32_t *id = (u_int32_t *) &cb->args[1];
410
411 DEBUGP("entered %s, last bucket=%lu id=%u\n", __FUNCTION__,
412 cb->args[0], *id);
413
414 read_lock_bh(&nf_conntrack_lock);
415 for (; cb->args[0] < nf_conntrack_htable_size; cb->args[0]++, *id = 0) {
416 list_for_each_prev(i, &nf_conntrack_hash[cb->args[0]]) {
417 h = (struct nf_conntrack_tuple_hash *) i;
418 if (DIRECTION(h) != IP_CT_DIR_ORIGINAL)
419 continue;
420 ct = nf_ct_tuplehash_to_ctrack(h);
421 if (ct->id <= *id)
422 continue;
423 if (ctnetlink_fill_info(skb, NETLINK_CB(cb->skb).pid,
424 cb->nlh->nlmsg_seq,
425 IPCTNL_MSG_CT_NEW,
426 1, ct) < 0)
427 goto out;
428 *id = ct->id;
429 }
430 }
431out:
432 read_unlock_bh(&nf_conntrack_lock);
433
434 DEBUGP("leaving, last bucket=%lu id=%u\n", cb->args[0], *id);
435
436 return skb->len;
437}
438
439#ifdef CONFIG_NF_CT_ACCT
440static int
441ctnetlink_dump_table_w(struct sk_buff *skb, struct netlink_callback *cb)
442{
443 struct nf_conn *ct = NULL;
444 struct nf_conntrack_tuple_hash *h;
445 struct list_head *i;
446 u_int32_t *id = (u_int32_t *) &cb->args[1];
447
448 DEBUGP("entered %s, last bucket=%u id=%u\n", __FUNCTION__,
449 cb->args[0], *id);
450
451 write_lock_bh(&nf_conntrack_lock);
452 for (; cb->args[0] < nf_conntrack_htable_size; cb->args[0]++, *id = 0) {
453 list_for_each_prev(i, &nf_conntrack_hash[cb->args[0]]) {
454 h = (struct nf_conntrack_tuple_hash *) i;
455 if (DIRECTION(h) != IP_CT_DIR_ORIGINAL)
456 continue;
457 ct = nf_ct_tuplehash_to_ctrack(h);
458 if (ct->id <= *id)
459 continue;
460 if (ctnetlink_fill_info(skb, NETLINK_CB(cb->skb).pid,
461 cb->nlh->nlmsg_seq,
462 IPCTNL_MSG_CT_NEW,
463 1, ct) < 0)
464 goto out;
465 *id = ct->id;
466
467 memset(&ct->counters, 0, sizeof(ct->counters));
468 }
469 }
470out:
471 write_unlock_bh(&nf_conntrack_lock);
472
473 DEBUGP("leaving, last bucket=%lu id=%u\n", cb->args[0], *id);
474
475 return skb->len;
476}
477#endif
478
479static inline int
480ctnetlink_parse_tuple_ip(struct nfattr *attr, struct nf_conntrack_tuple *tuple)
481{
482 struct nfattr *tb[CTA_IP_MAX];
483 struct nf_conntrack_l3proto *l3proto;
484 int ret = 0;
485
486 DEBUGP("entered %s\n", __FUNCTION__);
487
488 nfattr_parse_nested(tb, CTA_IP_MAX, attr);
489
490 l3proto = nf_ct_l3proto_find_get(tuple->src.l3num);
491
492 if (likely(l3proto->nfattr_to_tuple))
493 ret = l3proto->nfattr_to_tuple(tb, tuple);
494
495 nf_ct_l3proto_put(l3proto);
496
497 DEBUGP("leaving\n");
498
499 return ret;
500}
501
502static const size_t cta_min_proto[CTA_PROTO_MAX] = {
503 [CTA_PROTO_NUM-1] = sizeof(u_int8_t),
504};
505
506static inline int
507ctnetlink_parse_tuple_proto(struct nfattr *attr,
508 struct nf_conntrack_tuple *tuple)
509{
510 struct nfattr *tb[CTA_PROTO_MAX];
511 struct nf_conntrack_protocol *proto;
512 int ret = 0;
513
514 DEBUGP("entered %s\n", __FUNCTION__);
515
516 nfattr_parse_nested(tb, CTA_PROTO_MAX, attr);
517
518 if (nfattr_bad_size(tb, CTA_PROTO_MAX, cta_min_proto))
519 return -EINVAL;
520
521 if (!tb[CTA_PROTO_NUM-1])
522 return -EINVAL;
523 tuple->dst.protonum = *(u_int8_t *)NFA_DATA(tb[CTA_PROTO_NUM-1]);
524
525 proto = nf_ct_proto_find_get(tuple->src.l3num, tuple->dst.protonum);
526
527 if (likely(proto->nfattr_to_tuple))
528 ret = proto->nfattr_to_tuple(tb, tuple);
529
530 nf_ct_proto_put(proto);
531
532 return ret;
533}
534
535static inline int
536ctnetlink_parse_tuple(struct nfattr *cda[], struct nf_conntrack_tuple *tuple,
537 enum ctattr_tuple type, u_int8_t l3num)
538{
539 struct nfattr *tb[CTA_TUPLE_MAX];
540 int err;
541
542 DEBUGP("entered %s\n", __FUNCTION__);
543
544 memset(tuple, 0, sizeof(*tuple));
545
546 nfattr_parse_nested(tb, CTA_TUPLE_MAX, cda[type-1]);
547
548 if (!tb[CTA_TUPLE_IP-1])
549 return -EINVAL;
550
551 tuple->src.l3num = l3num;
552
553 err = ctnetlink_parse_tuple_ip(tb[CTA_TUPLE_IP-1], tuple);
554 if (err < 0)
555 return err;
556
557 if (!tb[CTA_TUPLE_PROTO-1])
558 return -EINVAL;
559
560 err = ctnetlink_parse_tuple_proto(tb[CTA_TUPLE_PROTO-1], tuple);
561 if (err < 0)
562 return err;
563
564 /* orig and expect tuples get DIR_ORIGINAL */
565 if (type == CTA_TUPLE_REPLY)
566 tuple->dst.dir = IP_CT_DIR_REPLY;
567 else
568 tuple->dst.dir = IP_CT_DIR_ORIGINAL;
569
570 NF_CT_DUMP_TUPLE(tuple);
571
572 DEBUGP("leaving\n");
573
574 return 0;
575}
576
577#ifdef CONFIG_IP_NF_NAT_NEEDED
578static const size_t cta_min_protonat[CTA_PROTONAT_MAX] = {
579 [CTA_PROTONAT_PORT_MIN-1] = sizeof(u_int16_t),
580 [CTA_PROTONAT_PORT_MAX-1] = sizeof(u_int16_t),
581};
582
583static int ctnetlink_parse_nat_proto(struct nfattr *attr,
584 const struct nf_conn *ct,
585 struct ip_nat_range *range)
586{
587 struct nfattr *tb[CTA_PROTONAT_MAX];
588 struct ip_nat_protocol *npt;
589
590 DEBUGP("entered %s\n", __FUNCTION__);
591
592 nfattr_parse_nested(tb, CTA_PROTONAT_MAX, attr);
593
594 if (nfattr_bad_size(tb, CTA_PROTONAT_MAX, cta_min_protonat))
595 return -EINVAL;
596
597 npt = ip_nat_proto_find_get(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum);
598
599 if (!npt->nfattr_to_range) {
600 ip_nat_proto_put(npt);
601 return 0;
602 }
603
604 /* nfattr_to_range returns 1 if it parsed, 0 if not, neg. on error */
605 if (npt->nfattr_to_range(tb, range) > 0)
606 range->flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
607
608 ip_nat_proto_put(npt);
609
610 DEBUGP("leaving\n");
611 return 0;
612}
613
614static const size_t cta_min_nat[CTA_NAT_MAX] = {
615 [CTA_NAT_MINIP-1] = sizeof(u_int32_t),
616 [CTA_NAT_MAXIP-1] = sizeof(u_int32_t),
617};
618
619static inline int
620ctnetlink_parse_nat(struct nfattr *cda[],
621 const struct nf_conn *ct, struct ip_nat_range *range)
622{
623 struct nfattr *tb[CTA_NAT_MAX];
624 int err;
625
626 DEBUGP("entered %s\n", __FUNCTION__);
627
628 memset(range, 0, sizeof(*range));
629
630 nfattr_parse_nested(tb, CTA_NAT_MAX, cda[CTA_NAT-1]);
631
632 if (nfattr_bad_size(tb, CTA_NAT_MAX, cta_min_nat))
633 return -EINVAL;
634
635 if (tb[CTA_NAT_MINIP-1])
636 range->min_ip = *(u_int32_t *)NFA_DATA(tb[CTA_NAT_MINIP-1]);
637
638 if (!tb[CTA_NAT_MAXIP-1])
639 range->max_ip = range->min_ip;
640 else
641 range->max_ip = *(u_int32_t *)NFA_DATA(tb[CTA_NAT_MAXIP-1]);
642
643 if (range->min_ip)
644 range->flags |= IP_NAT_RANGE_MAP_IPS;
645
646 if (!tb[CTA_NAT_PROTO-1])
647 return 0;
648
649 err = ctnetlink_parse_nat_proto(tb[CTA_NAT_PROTO-1], ct, range);
650 if (err < 0)
651 return err;
652
653 DEBUGP("leaving\n");
654 return 0;
655}
656#endif
657
658static inline int
659ctnetlink_parse_help(struct nfattr *attr, char **helper_name)
660{
661 struct nfattr *tb[CTA_HELP_MAX];
662
663 DEBUGP("entered %s\n", __FUNCTION__);
664
665 nfattr_parse_nested(tb, CTA_HELP_MAX, attr);
666
667 if (!tb[CTA_HELP_NAME-1])
668 return -EINVAL;
669
670 *helper_name = NFA_DATA(tb[CTA_HELP_NAME-1]);
671
672 return 0;
673}
674
675static const size_t cta_min[CTA_MAX] = {
676 [CTA_STATUS-1] = sizeof(u_int32_t),
677 [CTA_TIMEOUT-1] = sizeof(u_int32_t),
678 [CTA_MARK-1] = sizeof(u_int32_t),
679 [CTA_USE-1] = sizeof(u_int32_t),
680 [CTA_ID-1] = sizeof(u_int32_t)
681};
682
683static int
684ctnetlink_del_conntrack(struct sock *ctnl, struct sk_buff *skb,
685 struct nlmsghdr *nlh, struct nfattr *cda[], int *errp)
686{
687 struct nf_conntrack_tuple_hash *h;
688 struct nf_conntrack_tuple tuple;
689 struct nf_conn *ct;
690 struct nfgenmsg *nfmsg = NLMSG_DATA(nlh);
691 u_int8_t u3 = nfmsg->nfgen_family;
692 int err = 0;
693
694 DEBUGP("entered %s\n", __FUNCTION__);
695
696 if (nfattr_bad_size(cda, CTA_MAX, cta_min))
697 return -EINVAL;
698
699 if (cda[CTA_TUPLE_ORIG-1])
700 err = ctnetlink_parse_tuple(cda, &tuple, CTA_TUPLE_ORIG, u3);
701 else if (cda[CTA_TUPLE_REPLY-1])
702 err = ctnetlink_parse_tuple(cda, &tuple, CTA_TUPLE_REPLY, u3);
703 else {
704 /* Flush the whole table */
705 nf_conntrack_flush();
706 return 0;
707 }
708
709 if (err < 0)
710 return err;
711
712 h = nf_conntrack_find_get(&tuple, NULL);
713 if (!h) {
714 DEBUGP("tuple not found in conntrack hash\n");
715 return -ENOENT;
716 }
717
718 ct = nf_ct_tuplehash_to_ctrack(h);
719
720 if (cda[CTA_ID-1]) {
721 u_int32_t id = ntohl(*(u_int32_t *)NFA_DATA(cda[CTA_ID-1]));
722 if (ct->id != id) {
723 nf_ct_put(ct);
724 return -ENOENT;
725 }
726 }
727 if (del_timer(&ct->timeout))
728 ct->timeout.function((unsigned long)ct);
729
730 nf_ct_put(ct);
731 DEBUGP("leaving\n");
732
733 return 0;
734}
735
736static int
737ctnetlink_get_conntrack(struct sock *ctnl, struct sk_buff *skb,
738 struct nlmsghdr *nlh, struct nfattr *cda[], int *errp)
739{
740 struct nf_conntrack_tuple_hash *h;
741 struct nf_conntrack_tuple tuple;
742 struct nf_conn *ct;
743 struct sk_buff *skb2 = NULL;
744 struct nfgenmsg *nfmsg = NLMSG_DATA(nlh);
745 u_int8_t u3 = nfmsg->nfgen_family;
746 int err = 0;
747
748 DEBUGP("entered %s\n", __FUNCTION__);
749
750 if (nlh->nlmsg_flags & NLM_F_DUMP) {
751 u32 rlen;
752
753 if (nfmsg->nfgen_family != AF_INET)
754 return -EAFNOSUPPORT;
755
756 if (NFNL_MSG_TYPE(nlh->nlmsg_type) ==
757 IPCTNL_MSG_CT_GET_CTRZERO) {
758#ifdef CONFIG_NF_CT_ACCT
759 if ((*errp = netlink_dump_start(ctnl, skb, nlh,
760 ctnetlink_dump_table_w,
761 ctnetlink_done)) != 0)
762 return -EINVAL;
763#else
764 return -ENOTSUPP;
765#endif
766 } else {
767 if ((*errp = netlink_dump_start(ctnl, skb, nlh,
768 ctnetlink_dump_table,
769 ctnetlink_done)) != 0)
770 return -EINVAL;
771 }
772
773 rlen = NLMSG_ALIGN(nlh->nlmsg_len);
774 if (rlen > skb->len)
775 rlen = skb->len;
776 skb_pull(skb, rlen);
777 return 0;
778 }
779
780 if (nfattr_bad_size(cda, CTA_MAX, cta_min))
781 return -EINVAL;
782
783 if (cda[CTA_TUPLE_ORIG-1])
784 err = ctnetlink_parse_tuple(cda, &tuple, CTA_TUPLE_ORIG, u3);
785 else if (cda[CTA_TUPLE_REPLY-1])
786 err = ctnetlink_parse_tuple(cda, &tuple, CTA_TUPLE_REPLY, u3);
787 else
788 return -EINVAL;
789
790 if (err < 0)
791 return err;
792
793 h = nf_conntrack_find_get(&tuple, NULL);
794 if (!h) {
795 DEBUGP("tuple not found in conntrack hash");
796 return -ENOENT;
797 }
798 DEBUGP("tuple found\n");
799 ct = nf_ct_tuplehash_to_ctrack(h);
800
801 err = -ENOMEM;
802 skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
803 if (!skb2) {
804 nf_ct_put(ct);
805 return -ENOMEM;
806 }
807 NETLINK_CB(skb2).dst_pid = NETLINK_CB(skb).pid;
808
809 err = ctnetlink_fill_info(skb2, NETLINK_CB(skb).pid, nlh->nlmsg_seq,
810 IPCTNL_MSG_CT_NEW, 1, ct);
811 nf_ct_put(ct);
812 if (err <= 0)
813 goto free;
814
815 err = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).pid, MSG_DONTWAIT);
816 if (err < 0)
817 goto out;
818
819 DEBUGP("leaving\n");
820 return 0;
821
822free:
823 kfree_skb(skb2);
824out:
825 return err;
826}
827
828static inline int
829ctnetlink_change_status(struct nf_conn *ct, struct nfattr *cda[])
830{
831 unsigned long d;
832 unsigned status = ntohl(*(u_int32_t *)NFA_DATA(cda[CTA_STATUS-1]));
833 d = ct->status ^ status;
834
835 if (d & (IPS_EXPECTED|IPS_CONFIRMED|IPS_DYING))
836 /* unchangeable */
837 return -EINVAL;
838
839 if (d & IPS_SEEN_REPLY && !(status & IPS_SEEN_REPLY))
840 /* SEEN_REPLY bit can only be set */
841 return -EINVAL;
842
843
844 if (d & IPS_ASSURED && !(status & IPS_ASSURED))
845 /* ASSURED bit can only be set */
846 return -EINVAL;
847
848 if (cda[CTA_NAT-1]) {
849#ifndef CONFIG_IP_NF_NAT_NEEDED
850 return -EINVAL;
851#else
852 unsigned int hooknum;
853 struct ip_nat_range range;
854
855 if (ctnetlink_parse_nat(cda, ct, &range) < 0)
856 return -EINVAL;
857
858 DEBUGP("NAT: %u.%u.%u.%u-%u.%u.%u.%u:%u-%u\n",
859 NIPQUAD(range.min_ip), NIPQUAD(range.max_ip),
860 htons(range.min.all), htons(range.max.all));
861
862 /* This is tricky but it works. ip_nat_setup_info needs the
863 * hook number as parameter, so let's do the correct
864 * conversion and run away */
865 if (status & IPS_SRC_NAT_DONE)
866 hooknum = NF_IP_POST_ROUTING; /* IP_NAT_MANIP_SRC */
867 else if (status & IPS_DST_NAT_DONE)
868 hooknum = NF_IP_PRE_ROUTING; /* IP_NAT_MANIP_DST */
869 else
870 return -EINVAL; /* Missing NAT flags */
871
872 DEBUGP("NAT status: %lu\n",
873 status & (IPS_NAT_MASK | IPS_NAT_DONE_MASK));
874
875 if (ip_nat_initialized(ct, HOOK2MANIP(hooknum)))
876 return -EEXIST;
877 ip_nat_setup_info(ct, &range, hooknum);
878
879 DEBUGP("NAT status after setup_info: %lu\n",
880 ct->status & (IPS_NAT_MASK | IPS_NAT_DONE_MASK));
881#endif
882 }
883
884 /* Be careful here, modifying NAT bits can screw up things,
885 * so don't let users modify them directly if they don't pass
886 * ip_nat_range. */
887 ct->status |= status & ~(IPS_NAT_DONE_MASK | IPS_NAT_MASK);
888 return 0;
889}
890
891
892static inline int
893ctnetlink_change_helper(struct nf_conn *ct, struct nfattr *cda[])
894{
895 struct nf_conntrack_helper *helper;
896 char *helpname;
897 int err;
898
899 DEBUGP("entered %s\n", __FUNCTION__);
900
901 /* don't change helper of sibling connections */
902 if (ct->master)
903 return -EINVAL;
904
905 err = ctnetlink_parse_help(cda[CTA_HELP-1], &helpname);
906 if (err < 0)
907 return err;
908
909 helper = __nf_conntrack_helper_find_byname(helpname);
910 if (!helper) {
911 if (!strcmp(helpname, ""))
912 helper = NULL;
913 else
914 return -EINVAL;
915 }
916
917 if (ct->helper) {
918 if (!helper) {
919 /* we had a helper before ... */
920 nf_ct_remove_expectations(ct);
921 ct->helper = NULL;
922 } else {
923 /* need to zero data of old helper */
924 memset(&ct->help, 0, sizeof(ct->help));
925 }
926 }
927
928 ct->helper = helper;
929
930 return 0;
931}
932
933static inline int
934ctnetlink_change_timeout(struct nf_conn *ct, struct nfattr *cda[])
935{
936 u_int32_t timeout = ntohl(*(u_int32_t *)NFA_DATA(cda[CTA_TIMEOUT-1]));
937
938 if (!del_timer(&ct->timeout))
939 return -ETIME;
940
941 ct->timeout.expires = jiffies + timeout * HZ;
942 add_timer(&ct->timeout);
943
944 return 0;
945}
946
947static inline int
948ctnetlink_change_protoinfo(struct nf_conn *ct, struct nfattr *cda[])
949{
950 struct nfattr *tb[CTA_PROTOINFO_MAX], *attr = cda[CTA_PROTOINFO-1];
951 struct nf_conntrack_protocol *proto;
952 u_int16_t npt = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum;
953 u_int16_t l3num = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num;
954 int err = 0;
955
956 nfattr_parse_nested(tb, CTA_PROTOINFO_MAX, attr);
957
958 proto = nf_ct_proto_find_get(l3num, npt);
959
960 if (proto->from_nfattr)
961 err = proto->from_nfattr(tb, ct);
962 nf_ct_proto_put(proto);
963
964 return err;
965}
966
967static int
968ctnetlink_change_conntrack(struct nf_conn *ct, struct nfattr *cda[])
969{
970 int err;
971
972 DEBUGP("entered %s\n", __FUNCTION__);
973
974 if (cda[CTA_HELP-1]) {
975 err = ctnetlink_change_helper(ct, cda);
976 if (err < 0)
977 return err;
978 }
979
980 if (cda[CTA_TIMEOUT-1]) {
981 err = ctnetlink_change_timeout(ct, cda);
982 if (err < 0)
983 return err;
984 }
985
986 if (cda[CTA_STATUS-1]) {
987 err = ctnetlink_change_status(ct, cda);
988 if (err < 0)
989 return err;
990 }
991
992 if (cda[CTA_PROTOINFO-1]) {
993 err = ctnetlink_change_protoinfo(ct, cda);
994 if (err < 0)
995 return err;
996 }
997
998#if defined(CONFIG_IP_NF_CONNTRACK_MARK)
999 if (cda[CTA_MARK-1])
1000 ct->mark = ntohl(*(u_int32_t *)NFA_DATA(cda[CTA_MARK-1]));
1001#endif
1002
1003 DEBUGP("all done\n");
1004 return 0;
1005}
1006
1007static int
1008ctnetlink_create_conntrack(struct nfattr *cda[],
1009 struct nf_conntrack_tuple *otuple,
1010 struct nf_conntrack_tuple *rtuple)
1011{
1012 struct nf_conn *ct;
1013 int err = -EINVAL;
1014
1015 DEBUGP("entered %s\n", __FUNCTION__);
1016
1017 ct = nf_conntrack_alloc(otuple, rtuple);
1018 if (ct == NULL || IS_ERR(ct))
1019 return -ENOMEM;
1020
1021 if (!cda[CTA_TIMEOUT-1])
1022 goto err;
1023 ct->timeout.expires = ntohl(*(u_int32_t *)NFA_DATA(cda[CTA_TIMEOUT-1]));
1024
1025 ct->timeout.expires = jiffies + ct->timeout.expires * HZ;
1026 ct->status |= IPS_CONFIRMED;
1027
1028 err = ctnetlink_change_status(ct, cda);
1029 if (err < 0)
1030 goto err;
1031
1032 if (cda[CTA_PROTOINFO-1]) {
1033 err = ctnetlink_change_protoinfo(ct, cda);
1034 if (err < 0)
1035 return err;
1036 }
1037
1038#if defined(CONFIG_IP_NF_CONNTRACK_MARK)
1039 if (cda[CTA_MARK-1])
1040 ct->mark = ntohl(*(u_int32_t *)NFA_DATA(cda[CTA_MARK-1]));
1041#endif
1042
1043 ct->helper = nf_ct_helper_find_get(rtuple);
1044
1045 add_timer(&ct->timeout);
1046 nf_conntrack_hash_insert(ct);
1047
1048 if (ct->helper)
1049 nf_ct_helper_put(ct->helper);
1050
1051 DEBUGP("conntrack with id %u inserted\n", ct->id);
1052 return 0;
1053
1054err:
1055 nf_conntrack_free(ct);
1056 return err;
1057}
1058
1059static int
1060ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
1061 struct nlmsghdr *nlh, struct nfattr *cda[], int *errp)
1062{
1063 struct nf_conntrack_tuple otuple, rtuple;
1064 struct nf_conntrack_tuple_hash *h = NULL;
1065 struct nfgenmsg *nfmsg = NLMSG_DATA(nlh);
1066 u_int8_t u3 = nfmsg->nfgen_family;
1067 int err = 0;
1068
1069 DEBUGP("entered %s\n", __FUNCTION__);
1070
1071 if (nfattr_bad_size(cda, CTA_MAX, cta_min))
1072 return -EINVAL;
1073
1074 if (cda[CTA_TUPLE_ORIG-1]) {
1075 err = ctnetlink_parse_tuple(cda, &otuple, CTA_TUPLE_ORIG, u3);
1076 if (err < 0)
1077 return err;
1078 }
1079
1080 if (cda[CTA_TUPLE_REPLY-1]) {
1081 err = ctnetlink_parse_tuple(cda, &rtuple, CTA_TUPLE_REPLY, u3);
1082 if (err < 0)
1083 return err;
1084 }
1085
1086 write_lock_bh(&nf_conntrack_lock);
1087 if (cda[CTA_TUPLE_ORIG-1])
1088 h = __nf_conntrack_find(&otuple, NULL);
1089 else if (cda[CTA_TUPLE_REPLY-1])
1090 h = __nf_conntrack_find(&rtuple, NULL);
1091
1092 if (h == NULL) {
1093 write_unlock_bh(&nf_conntrack_lock);
1094 DEBUGP("no such conntrack, create new\n");
1095 err = -ENOENT;
1096 if (nlh->nlmsg_flags & NLM_F_CREATE)
1097 err = ctnetlink_create_conntrack(cda, &otuple, &rtuple);
1098 return err;
1099 }
1100 /* implicit 'else' */
1101
1102 /* we only allow nat config for new conntracks */
1103 if (cda[CTA_NAT-1]) {
1104 err = -EINVAL;
1105 goto out_unlock;
1106 }
1107
1108 /* We manipulate the conntrack inside the global conntrack table lock,
1109 * so there's no need to increase the refcount */
1110 DEBUGP("conntrack found\n");
1111 err = -EEXIST;
1112 if (!(nlh->nlmsg_flags & NLM_F_EXCL))
1113 err = ctnetlink_change_conntrack(nf_ct_tuplehash_to_ctrack(h), cda);
1114
1115out_unlock:
1116 write_unlock_bh(&nf_conntrack_lock);
1117 return err;
1118}
1119
1120/***********************************************************************
1121 * EXPECT
1122 ***********************************************************************/
1123
1124static inline int
1125ctnetlink_exp_dump_tuple(struct sk_buff *skb,
1126 const struct nf_conntrack_tuple *tuple,
1127 enum ctattr_expect type)
1128{
1129 struct nfattr *nest_parms = NFA_NEST(skb, type);
1130
1131 if (ctnetlink_dump_tuples(skb, tuple) < 0)
1132 goto nfattr_failure;
1133
1134 NFA_NEST_END(skb, nest_parms);
1135
1136 return 0;
1137
1138nfattr_failure:
1139 return -1;
1140}
1141
1142static inline int
1143ctnetlink_exp_dump_expect(struct sk_buff *skb,
1144 const struct nf_conntrack_expect *exp)
1145{
1146 struct nf_conn *master = exp->master;
1147 u_int32_t timeout = htonl((exp->timeout.expires - jiffies) / HZ);
1148 u_int32_t id = htonl(exp->id);
1149
1150 if (ctnetlink_exp_dump_tuple(skb, &exp->tuple, CTA_EXPECT_TUPLE) < 0)
1151 goto nfattr_failure;
1152 if (ctnetlink_exp_dump_tuple(skb, &exp->mask, CTA_EXPECT_MASK) < 0)
1153 goto nfattr_failure;
1154 if (ctnetlink_exp_dump_tuple(skb,
1155 &master->tuplehash[IP_CT_DIR_ORIGINAL].tuple,
1156 CTA_EXPECT_MASTER) < 0)
1157 goto nfattr_failure;
1158
1159 NFA_PUT(skb, CTA_EXPECT_TIMEOUT, sizeof(timeout), &timeout);
1160 NFA_PUT(skb, CTA_EXPECT_ID, sizeof(u_int32_t), &id);
1161
1162 return 0;
1163
1164nfattr_failure:
1165 return -1;
1166}
1167
1168static int
1169ctnetlink_exp_fill_info(struct sk_buff *skb, u32 pid, u32 seq,
1170 int event,
1171 int nowait,
1172 const struct nf_conntrack_expect *exp)
1173{
1174 struct nlmsghdr *nlh;
1175 struct nfgenmsg *nfmsg;
1176 unsigned char *b;
1177
1178 b = skb->tail;
1179
1180 event |= NFNL_SUBSYS_CTNETLINK_EXP << 8;
1181 nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(struct nfgenmsg));
1182 nfmsg = NLMSG_DATA(nlh);
1183
1184 nlh->nlmsg_flags = (nowait && pid) ? NLM_F_MULTI : 0;
1185 nfmsg->nfgen_family = exp->tuple.src.l3num;
1186 nfmsg->version = NFNETLINK_V0;
1187 nfmsg->res_id = 0;
1188
1189 if (ctnetlink_exp_dump_expect(skb, exp) < 0)
1190 goto nfattr_failure;
1191
1192 nlh->nlmsg_len = skb->tail - b;
1193 return skb->len;
1194
1195nlmsg_failure:
1196nfattr_failure:
1197 skb_trim(skb, b - skb->data);
1198 return -1;
1199}
1200
1201#ifdef CONFIG_NF_CONNTRACK_EVENTS
1202static int ctnetlink_expect_event(struct notifier_block *this,
1203 unsigned long events, void *ptr)
1204{
1205 struct nlmsghdr *nlh;
1206 struct nfgenmsg *nfmsg;
1207 struct nf_conntrack_expect *exp = (struct nf_conntrack_expect *)ptr;
1208 struct sk_buff *skb;
1209 unsigned int type;
1210 unsigned char *b;
1211 int flags = 0;
1212
1213 if (events & IPEXP_NEW) {
1214 type = IPCTNL_MSG_EXP_NEW;
1215 flags = NLM_F_CREATE|NLM_F_EXCL;
1216 } else
1217 return NOTIFY_DONE;
1218
1219 skb = alloc_skb(NLMSG_GOODSIZE, GFP_ATOMIC);
1220 if (!skb)
1221 return NOTIFY_DONE;
1222
1223 b = skb->tail;
1224
1225 type |= NFNL_SUBSYS_CTNETLINK << 8;
1226 nlh = NLMSG_PUT(skb, 0, 0, type, sizeof(struct nfgenmsg));
1227 nfmsg = NLMSG_DATA(nlh);
1228
1229 nlh->nlmsg_flags = flags;
1230 nfmsg->nfgen_family = exp->tuple.src.l3num;
1231 nfmsg->version = NFNETLINK_V0;
1232 nfmsg->res_id = 0;
1233
1234 if (ctnetlink_exp_dump_expect(skb, exp) < 0)
1235 goto nfattr_failure;
1236
1237 nlh->nlmsg_len = skb->tail - b;
1238 nfnetlink_send(skb, 0, NFNLGRP_CONNTRACK_EXP_NEW, 0);
1239 return NOTIFY_DONE;
1240
1241nlmsg_failure:
1242nfattr_failure:
1243 kfree_skb(skb);
1244 return NOTIFY_DONE;
1245}
1246#endif
1247
1248static int
1249ctnetlink_exp_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
1250{
1251 struct nf_conntrack_expect *exp = NULL;
1252 struct list_head *i;
1253 u_int32_t *id = (u_int32_t *) &cb->args[0];
1254
1255 DEBUGP("entered %s, last id=%llu\n", __FUNCTION__, *id);
1256
1257 read_lock_bh(&nf_conntrack_lock);
1258 list_for_each_prev(i, &nf_conntrack_expect_list) {
1259 exp = (struct nf_conntrack_expect *) i;
1260 if (exp->id <= *id)
1261 continue;
1262 if (ctnetlink_exp_fill_info(skb, NETLINK_CB(cb->skb).pid,
1263 cb->nlh->nlmsg_seq,
1264 IPCTNL_MSG_EXP_NEW,
1265 1, exp) < 0)
1266 goto out;
1267 *id = exp->id;
1268 }
1269out:
1270 read_unlock_bh(&nf_conntrack_lock);
1271
1272 DEBUGP("leaving, last id=%llu\n", *id);
1273
1274 return skb->len;
1275}
1276
1277static const size_t cta_min_exp[CTA_EXPECT_MAX] = {
1278 [CTA_EXPECT_TIMEOUT-1] = sizeof(u_int32_t),
1279 [CTA_EXPECT_ID-1] = sizeof(u_int32_t)
1280};
1281
1282static int
1283ctnetlink_get_expect(struct sock *ctnl, struct sk_buff *skb,
1284 struct nlmsghdr *nlh, struct nfattr *cda[], int *errp)
1285{
1286 struct nf_conntrack_tuple tuple;
1287 struct nf_conntrack_expect *exp;
1288 struct sk_buff *skb2;
1289 struct nfgenmsg *nfmsg = NLMSG_DATA(nlh);
1290 u_int8_t u3 = nfmsg->nfgen_family;
1291 int err = 0;
1292
1293 DEBUGP("entered %s\n", __FUNCTION__);
1294
1295 if (nfattr_bad_size(cda, CTA_EXPECT_MAX, cta_min_exp))
1296 return -EINVAL;
1297
1298 if (nlh->nlmsg_flags & NLM_F_DUMP) {
1299 u32 rlen;
1300
1301 if (nfmsg->nfgen_family != AF_INET)
1302 return -EAFNOSUPPORT;
1303
1304 if ((*errp = netlink_dump_start(ctnl, skb, nlh,
1305 ctnetlink_exp_dump_table,
1306 ctnetlink_done)) != 0)
1307 return -EINVAL;
1308 rlen = NLMSG_ALIGN(nlh->nlmsg_len);
1309 if (rlen > skb->len)
1310 rlen = skb->len;
1311 skb_pull(skb, rlen);
1312 return 0;
1313 }
1314
1315 if (cda[CTA_EXPECT_MASTER-1])
1316 err = ctnetlink_parse_tuple(cda, &tuple, CTA_EXPECT_MASTER, u3);
1317 else
1318 return -EINVAL;
1319
1320 if (err < 0)
1321 return err;
1322
1323 exp = nf_conntrack_expect_find(&tuple);
1324 if (!exp)
1325 return -ENOENT;
1326
1327 if (cda[CTA_EXPECT_ID-1]) {
1328 u_int32_t id = *(u_int32_t *)NFA_DATA(cda[CTA_EXPECT_ID-1]);
1329 if (exp->id != ntohl(id)) {
1330 nf_conntrack_expect_put(exp);
1331 return -ENOENT;
1332 }
1333 }
1334
1335 err = -ENOMEM;
1336 skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
1337 if (!skb2)
1338 goto out;
1339 NETLINK_CB(skb2).dst_pid = NETLINK_CB(skb).pid;
1340
1341 err = ctnetlink_exp_fill_info(skb2, NETLINK_CB(skb).pid,
1342 nlh->nlmsg_seq, IPCTNL_MSG_EXP_NEW,
1343 1, exp);
1344 if (err <= 0)
1345 goto free;
1346
1347 nf_conntrack_expect_put(exp);
1348
1349 return netlink_unicast(ctnl, skb2, NETLINK_CB(skb).pid, MSG_DONTWAIT);
1350
1351free:
1352 kfree_skb(skb2);
1353out:
1354 nf_conntrack_expect_put(exp);
1355 return err;
1356}
1357
1358static int
1359ctnetlink_del_expect(struct sock *ctnl, struct sk_buff *skb,
1360 struct nlmsghdr *nlh, struct nfattr *cda[], int *errp)
1361{
1362 struct nf_conntrack_expect *exp, *tmp;
1363 struct nf_conntrack_tuple tuple;
1364 struct nf_conntrack_helper *h;
1365 struct nfgenmsg *nfmsg = NLMSG_DATA(nlh);
1366 u_int8_t u3 = nfmsg->nfgen_family;
1367 int err;
1368
1369 if (nfattr_bad_size(cda, CTA_EXPECT_MAX, cta_min_exp))
1370 return -EINVAL;
1371
1372 if (cda[CTA_EXPECT_TUPLE-1]) {
1373 /* delete a single expect by tuple */
1374 err = ctnetlink_parse_tuple(cda, &tuple, CTA_EXPECT_TUPLE, u3);
1375 if (err < 0)
1376 return err;
1377
1378 /* bump usage count to 2 */
1379 exp = nf_conntrack_expect_find(&tuple);
1380 if (!exp)
1381 return -ENOENT;
1382
1383 if (cda[CTA_EXPECT_ID-1]) {
1384 u_int32_t id =
1385 *(u_int32_t *)NFA_DATA(cda[CTA_EXPECT_ID-1]);
1386 if (exp->id != ntohl(id)) {
1387 nf_conntrack_expect_put(exp);
1388 return -ENOENT;
1389 }
1390 }
1391
1392 /* after list removal, usage count == 1 */
1393 nf_conntrack_unexpect_related(exp);
1394 /* have to put what we 'get' above.
1395 * after this line usage count == 0 */
1396 nf_conntrack_expect_put(exp);
1397 } else if (cda[CTA_EXPECT_HELP_NAME-1]) {
1398 char *name = NFA_DATA(cda[CTA_EXPECT_HELP_NAME-1]);
1399
1400 /* delete all expectations for this helper */
1401 write_lock_bh(&nf_conntrack_lock);
1402 h = __nf_conntrack_helper_find_byname(name);
1403 if (!h) {
1404 write_unlock_bh(&nf_conntrack_lock);
1405 return -EINVAL;
1406 }
1407 list_for_each_entry_safe(exp, tmp, &nf_conntrack_expect_list,
1408 list) {
1409 if (exp->master->helper == h
1410 && del_timer(&exp->timeout)) {
1411 nf_ct_unlink_expect(exp);
1412 nf_conntrack_expect_put(exp);
1413 }
1414 }
1415 write_unlock_bh(&nf_conntrack_lock);
1416 } else {
1417 /* This basically means we have to flush everything*/
1418 write_lock_bh(&nf_conntrack_lock);
1419 list_for_each_entry_safe(exp, tmp, &nf_conntrack_expect_list,
1420 list) {
1421 if (del_timer(&exp->timeout)) {
1422 nf_ct_unlink_expect(exp);
1423 nf_conntrack_expect_put(exp);
1424 }
1425 }
1426 write_unlock_bh(&nf_conntrack_lock);
1427 }
1428
1429 return 0;
1430}
1431static int
1432ctnetlink_change_expect(struct nf_conntrack_expect *x, struct nfattr *cda[])
1433{
1434 return -EOPNOTSUPP;
1435}
1436
1437static int
1438ctnetlink_create_expect(struct nfattr *cda[], u_int8_t u3)
1439{
1440 struct nf_conntrack_tuple tuple, mask, master_tuple;
1441 struct nf_conntrack_tuple_hash *h = NULL;
1442 struct nf_conntrack_expect *exp;
1443 struct nf_conn *ct;
1444 int err = 0;
1445
1446 DEBUGP("entered %s\n", __FUNCTION__);
1447
1448 /* caller guarantees that those three CTA_EXPECT_* exist */
1449 err = ctnetlink_parse_tuple(cda, &tuple, CTA_EXPECT_TUPLE, u3);
1450 if (err < 0)
1451 return err;
1452 err = ctnetlink_parse_tuple(cda, &mask, CTA_EXPECT_MASK, u3);
1453 if (err < 0)
1454 return err;
1455 err = ctnetlink_parse_tuple(cda, &master_tuple, CTA_EXPECT_MASTER, u3);
1456 if (err < 0)
1457 return err;
1458
1459 /* Look for master conntrack of this expectation */
1460 h = nf_conntrack_find_get(&master_tuple, NULL);
1461 if (!h)
1462 return -ENOENT;
1463 ct = nf_ct_tuplehash_to_ctrack(h);
1464
1465 if (!ct->helper) {
1466 /* such conntrack hasn't got any helper, abort */
1467 err = -EINVAL;
1468 goto out;
1469 }
1470
1471 exp = nf_conntrack_expect_alloc(ct);
1472 if (!exp) {
1473 err = -ENOMEM;
1474 goto out;
1475 }
1476
1477 exp->expectfn = NULL;
1478 exp->flags = 0;
1479 exp->master = ct;
1480 memcpy(&exp->tuple, &tuple, sizeof(struct nf_conntrack_tuple));
1481 memcpy(&exp->mask, &mask, sizeof(struct nf_conntrack_tuple));
1482
1483 err = nf_conntrack_expect_related(exp);
1484 nf_conntrack_expect_put(exp);
1485
1486out:
1487 nf_ct_put(nf_ct_tuplehash_to_ctrack(h));
1488 return err;
1489}
1490
1491static int
1492ctnetlink_new_expect(struct sock *ctnl, struct sk_buff *skb,
1493 struct nlmsghdr *nlh, struct nfattr *cda[], int *errp)
1494{
1495 struct nf_conntrack_tuple tuple;
1496 struct nf_conntrack_expect *exp;
1497 struct nfgenmsg *nfmsg = NLMSG_DATA(nlh);
1498 u_int8_t u3 = nfmsg->nfgen_family;
1499 int err = 0;
1500
1501 DEBUGP("entered %s\n", __FUNCTION__);
1502
1503 if (nfattr_bad_size(cda, CTA_EXPECT_MAX, cta_min_exp))
1504 return -EINVAL;
1505
1506 if (!cda[CTA_EXPECT_TUPLE-1]
1507 || !cda[CTA_EXPECT_MASK-1]
1508 || !cda[CTA_EXPECT_MASTER-1])
1509 return -EINVAL;
1510
1511 err = ctnetlink_parse_tuple(cda, &tuple, CTA_EXPECT_TUPLE, u3);
1512 if (err < 0)
1513 return err;
1514
1515 write_lock_bh(&nf_conntrack_lock);
1516 exp = __nf_conntrack_expect_find(&tuple);
1517
1518 if (!exp) {
1519 write_unlock_bh(&nf_conntrack_lock);
1520 err = -ENOENT;
1521 if (nlh->nlmsg_flags & NLM_F_CREATE)
1522 err = ctnetlink_create_expect(cda, u3);
1523 return err;
1524 }
1525
1526 err = -EEXIST;
1527 if (!(nlh->nlmsg_flags & NLM_F_EXCL))
1528 err = ctnetlink_change_expect(exp, cda);
1529 write_unlock_bh(&nf_conntrack_lock);
1530
1531 DEBUGP("leaving\n");
1532
1533 return err;
1534}
1535
1536#ifdef CONFIG_NF_CONNTRACK_EVENTS
1537static struct notifier_block ctnl_notifier = {
1538 .notifier_call = ctnetlink_conntrack_event,
1539};
1540
1541static struct notifier_block ctnl_notifier_exp = {
1542 .notifier_call = ctnetlink_expect_event,
1543};
1544#endif
1545
1546static struct nfnl_callback ctnl_cb[IPCTNL_MSG_MAX] = {
1547 [IPCTNL_MSG_CT_NEW] = { .call = ctnetlink_new_conntrack,
1548 .attr_count = CTA_MAX, },
1549 [IPCTNL_MSG_CT_GET] = { .call = ctnetlink_get_conntrack,
1550 .attr_count = CTA_MAX, },
1551 [IPCTNL_MSG_CT_DELETE] = { .call = ctnetlink_del_conntrack,
1552 .attr_count = CTA_MAX, },
1553 [IPCTNL_MSG_CT_GET_CTRZERO] = { .call = ctnetlink_get_conntrack,
1554 .attr_count = CTA_MAX, },
1555};
1556
1557static struct nfnl_callback ctnl_exp_cb[IPCTNL_MSG_EXP_MAX] = {
1558 [IPCTNL_MSG_EXP_GET] = { .call = ctnetlink_get_expect,
1559 .attr_count = CTA_EXPECT_MAX, },
1560 [IPCTNL_MSG_EXP_NEW] = { .call = ctnetlink_new_expect,
1561 .attr_count = CTA_EXPECT_MAX, },
1562 [IPCTNL_MSG_EXP_DELETE] = { .call = ctnetlink_del_expect,
1563 .attr_count = CTA_EXPECT_MAX, },
1564};
1565
1566static struct nfnetlink_subsystem ctnl_subsys = {
1567 .name = "conntrack",
1568 .subsys_id = NFNL_SUBSYS_CTNETLINK,
1569 .cb_count = IPCTNL_MSG_MAX,
1570 .cb = ctnl_cb,
1571};
1572
1573static struct nfnetlink_subsystem ctnl_exp_subsys = {
1574 .name = "conntrack_expect",
1575 .subsys_id = NFNL_SUBSYS_CTNETLINK_EXP,
1576 .cb_count = IPCTNL_MSG_EXP_MAX,
1577 .cb = ctnl_exp_cb,
1578};
1579
1580MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_CTNETLINK);
1581
1582static int __init ctnetlink_init(void)
1583{
1584 int ret;
1585
1586 printk("ctnetlink v%s: registering with nfnetlink.\n", version);
1587 ret = nfnetlink_subsys_register(&ctnl_subsys);
1588 if (ret < 0) {
1589 printk("ctnetlink_init: cannot register with nfnetlink.\n");
1590 goto err_out;
1591 }
1592
1593 ret = nfnetlink_subsys_register(&ctnl_exp_subsys);
1594 if (ret < 0) {
1595 printk("ctnetlink_init: cannot register exp with nfnetlink.\n");
1596 goto err_unreg_subsys;
1597 }
1598
1599#ifdef CONFIG_NF_CONNTRACK_EVENTS
1600 ret = nf_conntrack_register_notifier(&ctnl_notifier);
1601 if (ret < 0) {
1602 printk("ctnetlink_init: cannot register notifier.\n");
1603 goto err_unreg_exp_subsys;
1604 }
1605
1606 ret = nf_conntrack_expect_register_notifier(&ctnl_notifier_exp);
1607 if (ret < 0) {
1608 printk("ctnetlink_init: cannot expect register notifier.\n");
1609 goto err_unreg_notifier;
1610 }
1611#endif
1612
1613 return 0;
1614
1615#ifdef CONFIG_NF_CONNTRACK_EVENTS
1616err_unreg_notifier:
1617 nf_conntrack_unregister_notifier(&ctnl_notifier);
1618err_unreg_exp_subsys:
1619 nfnetlink_subsys_unregister(&ctnl_exp_subsys);
1620#endif
1621err_unreg_subsys:
1622 nfnetlink_subsys_unregister(&ctnl_subsys);
1623err_out:
1624 return ret;
1625}
1626
1627static void __exit ctnetlink_exit(void)
1628{
1629 printk("ctnetlink: unregistering from nfnetlink.\n");
1630
1631#ifdef CONFIG_NF_CONNTRACK_EVENTS
1632 nf_conntrack_unregister_notifier(&ctnl_notifier_exp);
1633 nf_conntrack_unregister_notifier(&ctnl_notifier);
1634#endif
1635
1636 nfnetlink_subsys_unregister(&ctnl_exp_subsys);
1637 nfnetlink_subsys_unregister(&ctnl_subsys);
1638 return;
1639}
1640
1641module_init(ctnetlink_init);
1642module_exit(ctnetlink_exit);
diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c
index 6035633d8225..6167137a5cb5 100644
--- a/net/netfilter/nf_conntrack_proto_tcp.c
+++ b/net/netfilter/nf_conntrack_proto_tcp.c
@@ -1147,6 +1147,63 @@ static int tcp_new(struct nf_conn *conntrack,
1147 receiver->td_scale); 1147 receiver->td_scale);
1148 return 1; 1148 return 1;
1149} 1149}
1150
1151#if defined(CONFIG_NF_CT_NETLINK) || \
1152 defined(CONFIG_NF_CT_NETLINK_MODULE)
1153
1154#include <linux/netfilter/nfnetlink.h>
1155#include <linux/netfilter/nfnetlink_conntrack.h>
1156
1157static int tcp_to_nfattr(struct sk_buff *skb, struct nfattr *nfa,
1158 const struct nf_conn *ct)
1159{
1160 struct nfattr *nest_parms;
1161
1162 read_lock_bh(&tcp_lock);
1163 nest_parms = NFA_NEST(skb, CTA_PROTOINFO_TCP);
1164 NFA_PUT(skb, CTA_PROTOINFO_TCP_STATE, sizeof(u_int8_t),
1165 &ct->proto.tcp.state);
1166 read_unlock_bh(&tcp_lock);
1167
1168 NFA_NEST_END(skb, nest_parms);
1169
1170 return 0;
1171
1172nfattr_failure:
1173 read_unlock_bh(&tcp_lock);
1174 return -1;
1175}
1176
1177static const size_t cta_min_tcp[CTA_PROTOINFO_TCP_MAX] = {
1178 [CTA_PROTOINFO_TCP_STATE-1] = sizeof(u_int8_t),
1179};
1180
1181static int nfattr_to_tcp(struct nfattr *cda[], struct nf_conn *ct)
1182{
1183 struct nfattr *attr = cda[CTA_PROTOINFO_TCP-1];
1184 struct nfattr *tb[CTA_PROTOINFO_TCP_MAX];
1185
1186 /* updates could not contain anything about the private
1187 * protocol info, in that case skip the parsing */
1188 if (!attr)
1189 return 0;
1190
1191 nfattr_parse_nested(tb, CTA_PROTOINFO_TCP_MAX, attr);
1192
1193 if (nfattr_bad_size(tb, CTA_PROTOINFO_TCP_MAX, cta_min_tcp))
1194 return -EINVAL;
1195
1196 if (!tb[CTA_PROTOINFO_TCP_STATE-1])
1197 return -EINVAL;
1198
1199 write_lock_bh(&tcp_lock);
1200 ct->proto.tcp.state =
1201 *(u_int8_t *)NFA_DATA(tb[CTA_PROTOINFO_TCP_STATE-1]);
1202 write_unlock_bh(&tcp_lock);
1203
1204 return 0;
1205}
1206#endif
1150 1207
1151struct nf_conntrack_protocol nf_conntrack_protocol_tcp4 = 1208struct nf_conntrack_protocol nf_conntrack_protocol_tcp4 =
1152{ 1209{
@@ -1160,6 +1217,13 @@ struct nf_conntrack_protocol nf_conntrack_protocol_tcp4 =
1160 .packet = tcp_packet, 1217 .packet = tcp_packet,
1161 .new = tcp_new, 1218 .new = tcp_new,
1162 .error = tcp_error4, 1219 .error = tcp_error4,
1220#if defined(CONFIG_NF_CT_NETLINK) || \
1221 defined(CONFIG_NF_CT_NETLINK_MODULE)
1222 .to_nfattr = tcp_to_nfattr,
1223 .from_nfattr = nfattr_to_tcp,
1224 .tuple_to_nfattr = nf_ct_port_tuple_to_nfattr,
1225 .nfattr_to_tuple = nf_ct_port_nfattr_to_tuple,
1226#endif
1163}; 1227};
1164 1228
1165struct nf_conntrack_protocol nf_conntrack_protocol_tcp6 = 1229struct nf_conntrack_protocol nf_conntrack_protocol_tcp6 =
@@ -1174,6 +1238,13 @@ struct nf_conntrack_protocol nf_conntrack_protocol_tcp6 =
1174 .packet = tcp_packet, 1238 .packet = tcp_packet,
1175 .new = tcp_new, 1239 .new = tcp_new,
1176 .error = tcp_error6, 1240 .error = tcp_error6,
1241#if defined(CONFIG_NF_CT_NETLINK) || \
1242 defined(CONFIG_NF_CT_NETLINK_MODULE)
1243 .to_nfattr = tcp_to_nfattr,
1244 .from_nfattr = nfattr_to_tcp,
1245 .tuple_to_nfattr = nf_ct_port_tuple_to_nfattr,
1246 .nfattr_to_tuple = nf_ct_port_nfattr_to_tuple,
1247#endif
1177}; 1248};
1178 1249
1179EXPORT_SYMBOL(nf_conntrack_protocol_tcp4); 1250EXPORT_SYMBOL(nf_conntrack_protocol_tcp4);
diff --git a/net/netfilter/nf_conntrack_proto_udp.c b/net/netfilter/nf_conntrack_proto_udp.c
index 3cae7ce420dd..1a592a556182 100644
--- a/net/netfilter/nf_conntrack_proto_udp.c
+++ b/net/netfilter/nf_conntrack_proto_udp.c
@@ -196,6 +196,11 @@ struct nf_conntrack_protocol nf_conntrack_protocol_udp4 =
196 .packet = udp_packet, 196 .packet = udp_packet,
197 .new = udp_new, 197 .new = udp_new,
198 .error = udp_error4, 198 .error = udp_error4,
199#if defined(CONFIG_NF_CT_NETLINK) || \
200 defined(CONFIG_NF_CT_NETLINK_MODULE)
201 .tuple_to_nfattr = nf_ct_port_tuple_to_nfattr,
202 .nfattr_to_tuple = nf_ct_port_nfattr_to_tuple,
203#endif
199}; 204};
200 205
201struct nf_conntrack_protocol nf_conntrack_protocol_udp6 = 206struct nf_conntrack_protocol nf_conntrack_protocol_udp6 =
@@ -210,6 +215,11 @@ struct nf_conntrack_protocol nf_conntrack_protocol_udp6 =
210 .packet = udp_packet, 215 .packet = udp_packet,
211 .new = udp_new, 216 .new = udp_new,
212 .error = udp_error6, 217 .error = udp_error6,
218#if defined(CONFIG_NF_CT_NETLINK) || \
219 defined(CONFIG_NF_CT_NETLINK_MODULE)
220 .tuple_to_nfattr = nf_ct_port_tuple_to_nfattr,
221 .nfattr_to_tuple = nf_ct_port_nfattr_to_tuple,
222#endif
213}; 223};
214 224
215EXPORT_SYMBOL(nf_conntrack_protocol_udp4); 225EXPORT_SYMBOL(nf_conntrack_protocol_udp4);
diff --git a/net/netfilter/nf_conntrack_standalone.c b/net/netfilter/nf_conntrack_standalone.c
index 5af381f9fe3d..d17e42b28c79 100644
--- a/net/netfilter/nf_conntrack_standalone.c
+++ b/net/netfilter/nf_conntrack_standalone.c
@@ -161,14 +161,14 @@ static int ct_seq_show(struct seq_file *s, void *v)
161 if (NF_CT_DIRECTION(hash)) 161 if (NF_CT_DIRECTION(hash))
162 return 0; 162 return 0;
163 163
164 l3proto = nf_ct_find_l3proto(conntrack->tuplehash[IP_CT_DIR_ORIGINAL] 164 l3proto = __nf_ct_l3proto_find(conntrack->tuplehash[IP_CT_DIR_ORIGINAL]
165 .tuple.src.l3num); 165 .tuple.src.l3num);
166 166
167 NF_CT_ASSERT(l3proto); 167 NF_CT_ASSERT(l3proto);
168 proto = nf_ct_find_proto(conntrack->tuplehash[IP_CT_DIR_ORIGINAL] 168 proto = __nf_ct_proto_find(conntrack->tuplehash[IP_CT_DIR_ORIGINAL]
169 .tuple.src.l3num, 169 .tuple.src.l3num,
170 conntrack->tuplehash[IP_CT_DIR_ORIGINAL] 170 conntrack->tuplehash[IP_CT_DIR_ORIGINAL]
171 .tuple.dst.protonum); 171 .tuple.dst.protonum);
172 NF_CT_ASSERT(proto); 172 NF_CT_ASSERT(proto);
173 173
174 if (seq_printf(s, "%-8s %u %-8s %u %ld ", 174 if (seq_printf(s, "%-8s %u %-8s %u %ld ",
@@ -307,9 +307,9 @@ static int exp_seq_show(struct seq_file *s, void *v)
307 expect->tuple.src.l3num, 307 expect->tuple.src.l3num,
308 expect->tuple.dst.protonum); 308 expect->tuple.dst.protonum);
309 print_tuple(s, &expect->tuple, 309 print_tuple(s, &expect->tuple,
310 nf_ct_find_l3proto(expect->tuple.src.l3num), 310 __nf_ct_l3proto_find(expect->tuple.src.l3num),
311 nf_ct_find_proto(expect->tuple.src.l3num, 311 __nf_ct_proto_find(expect->tuple.src.l3num,
312 expect->tuple.dst.protonum)); 312 expect->tuple.dst.protonum));
313 return seq_putc(s, '\n'); 313 return seq_putc(s, '\n');
314} 314}
315 315
@@ -847,7 +847,11 @@ EXPORT_SYMBOL(nf_conntrack_helper_unregister);
847EXPORT_SYMBOL(nf_ct_iterate_cleanup); 847EXPORT_SYMBOL(nf_ct_iterate_cleanup);
848EXPORT_SYMBOL(__nf_ct_refresh_acct); 848EXPORT_SYMBOL(__nf_ct_refresh_acct);
849EXPORT_SYMBOL(nf_ct_protos); 849EXPORT_SYMBOL(nf_ct_protos);
850EXPORT_SYMBOL(nf_ct_find_proto); 850EXPORT_SYMBOL(__nf_ct_proto_find);
851EXPORT_SYMBOL(nf_ct_proto_find_get);
852EXPORT_SYMBOL(nf_ct_proto_put);
853EXPORT_SYMBOL(nf_ct_l3proto_find_get);
854EXPORT_SYMBOL(nf_ct_l3proto_put);
851EXPORT_SYMBOL(nf_ct_l3protos); 855EXPORT_SYMBOL(nf_ct_l3protos);
852EXPORT_SYMBOL(nf_conntrack_expect_alloc); 856EXPORT_SYMBOL(nf_conntrack_expect_alloc);
853EXPORT_SYMBOL(nf_conntrack_expect_put); 857EXPORT_SYMBOL(nf_conntrack_expect_put);
@@ -867,3 +871,21 @@ EXPORT_SYMBOL(nf_ct_get_tuple);
867EXPORT_SYMBOL(nf_ct_invert_tuple); 871EXPORT_SYMBOL(nf_ct_invert_tuple);
868EXPORT_SYMBOL(nf_conntrack_in); 872EXPORT_SYMBOL(nf_conntrack_in);
869EXPORT_SYMBOL(__nf_conntrack_attach); 873EXPORT_SYMBOL(__nf_conntrack_attach);
874EXPORT_SYMBOL(nf_conntrack_alloc);
875EXPORT_SYMBOL(nf_conntrack_free);
876EXPORT_SYMBOL(nf_conntrack_flush);
877EXPORT_SYMBOL(nf_ct_remove_expectations);
878EXPORT_SYMBOL(nf_ct_helper_find_get);
879EXPORT_SYMBOL(nf_ct_helper_put);
880EXPORT_SYMBOL(__nf_conntrack_helper_find_byname);
881EXPORT_SYMBOL(__nf_conntrack_find);
882EXPORT_SYMBOL(nf_ct_unlink_expect);
883EXPORT_SYMBOL(nf_conntrack_hash_insert);
884EXPORT_SYMBOL(__nf_conntrack_expect_find);
885EXPORT_SYMBOL(nf_conntrack_expect_find);
886EXPORT_SYMBOL(nf_conntrack_expect_list);
887#if defined(CONFIG_NF_CT_NETLINK) || \
888 defined(CONFIG_NF_CT_NETLINK_MODULE)
889EXPORT_SYMBOL(nf_ct_port_tuple_to_nfattr);
890EXPORT_SYMBOL(nf_ct_port_nfattr_to_tuple);
891#endif