diff options
author | Florian Westphal <fw@strlen.de> | 2012-10-29 21:08:49 -0400 |
---|---|---|
committer | Pablo Neira Ayuso <pablo@netfilter.org> | 2012-11-02 07:26:32 -0400 |
commit | 121d1e0941e05c64ee4223064dd83eb24e871739 (patch) | |
tree | a5b841774451210d2d53db14b7cd907cd47f12d4 /net | |
parent | 6229b75d8da5a4eed7bb668de757e252986c2305 (diff) |
netfilter: ipv6: add getsockopt to retrieve origdst
userspace can query the original ipv4 destination address of a REDIRECTed
connection via
getsockopt(m_sock, SOL_IP, SO_ORIGINAL_DST, &m_server_addr, &addrsize)
but for ipv6 no such option existed.
This adds getsockopt(..., IPPROTO_IPV6, IP6T_SO_ORIGINAL_DST, ...).
Without this, userspace needs to parse /proc or use ctnetlink, which
appears to be overkill.
This uses option number 80 for IP6T_SO_ORIGINAL_DST, which is spare,
to use the same number we use in the IPv4 socket option SO_ORIGINAL_DST.
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | 61 |
1 files changed, 61 insertions, 0 deletions
diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c index 8860d23e61cf..02dcafdc7a95 100644 --- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c +++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | |||
@@ -21,6 +21,7 @@ | |||
21 | 21 | ||
22 | #include <linux/netfilter_bridge.h> | 22 | #include <linux/netfilter_bridge.h> |
23 | #include <linux/netfilter_ipv6.h> | 23 | #include <linux/netfilter_ipv6.h> |
24 | #include <linux/netfilter_ipv6/ip6_tables.h> | ||
24 | #include <net/netfilter/nf_conntrack.h> | 25 | #include <net/netfilter/nf_conntrack.h> |
25 | #include <net/netfilter/nf_conntrack_helper.h> | 26 | #include <net/netfilter/nf_conntrack_helper.h> |
26 | #include <net/netfilter/nf_conntrack_l4proto.h> | 27 | #include <net/netfilter/nf_conntrack_l4proto.h> |
@@ -295,6 +296,50 @@ static struct nf_hook_ops ipv6_conntrack_ops[] __read_mostly = { | |||
295 | }, | 296 | }, |
296 | }; | 297 | }; |
297 | 298 | ||
299 | static int | ||
300 | ipv6_getorigdst(struct sock *sk, int optval, void __user *user, int *len) | ||
301 | { | ||
302 | const struct inet_sock *inet = inet_sk(sk); | ||
303 | const struct ipv6_pinfo *inet6 = inet6_sk(sk); | ||
304 | const struct nf_conntrack_tuple_hash *h; | ||
305 | struct sockaddr_in6 sin6; | ||
306 | struct nf_conntrack_tuple tuple = { .src.l3num = NFPROTO_IPV6 }; | ||
307 | struct nf_conn *ct; | ||
308 | |||
309 | tuple.src.u3.in6 = inet6->rcv_saddr; | ||
310 | tuple.src.u.tcp.port = inet->inet_sport; | ||
311 | tuple.dst.u3.in6 = inet6->daddr; | ||
312 | tuple.dst.u.tcp.port = inet->inet_dport; | ||
313 | tuple.dst.protonum = sk->sk_protocol; | ||
314 | |||
315 | if (sk->sk_protocol != IPPROTO_TCP && sk->sk_protocol != IPPROTO_SCTP) | ||
316 | return -ENOPROTOOPT; | ||
317 | |||
318 | if (*len < 0 || (unsigned int) *len < sizeof(sin6)) | ||
319 | return -EINVAL; | ||
320 | |||
321 | h = nf_conntrack_find_get(sock_net(sk), NF_CT_DEFAULT_ZONE, &tuple); | ||
322 | if (!h) { | ||
323 | pr_debug("IP6T_SO_ORIGINAL_DST: Can't find %pI6c/%u-%pI6c/%u.\n", | ||
324 | &tuple.src.u3.ip6, ntohs(tuple.src.u.tcp.port), | ||
325 | &tuple.dst.u3.ip6, ntohs(tuple.dst.u.tcp.port)); | ||
326 | return -ENOENT; | ||
327 | } | ||
328 | |||
329 | ct = nf_ct_tuplehash_to_ctrack(h); | ||
330 | |||
331 | sin6.sin6_family = AF_INET6; | ||
332 | sin6.sin6_port = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.tcp.port; | ||
333 | sin6.sin6_flowinfo = inet6->flow_label & IPV6_FLOWINFO_MASK; | ||
334 | memcpy(&sin6.sin6_addr, | ||
335 | &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u3.in6, | ||
336 | sizeof(sin6.sin6_addr)); | ||
337 | sin6.sin6_scope_id = sk->sk_bound_dev_if; | ||
338 | |||
339 | nf_ct_put(ct); | ||
340 | return copy_to_user(user, &sin6, sizeof(sin6)) ? -EFAULT : 0; | ||
341 | } | ||
342 | |||
298 | #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) | 343 | #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) |
299 | 344 | ||
300 | #include <linux/netfilter/nfnetlink.h> | 345 | #include <linux/netfilter/nfnetlink.h> |
@@ -359,6 +404,14 @@ MODULE_ALIAS("nf_conntrack-" __stringify(AF_INET6)); | |||
359 | MODULE_LICENSE("GPL"); | 404 | MODULE_LICENSE("GPL"); |
360 | MODULE_AUTHOR("Yasuyuki KOZAKAI @USAGI <yasuyuki.kozakai@toshiba.co.jp>"); | 405 | MODULE_AUTHOR("Yasuyuki KOZAKAI @USAGI <yasuyuki.kozakai@toshiba.co.jp>"); |
361 | 406 | ||
407 | static struct nf_sockopt_ops so_getorigdst6 = { | ||
408 | .pf = NFPROTO_IPV6, | ||
409 | .get_optmin = IP6T_SO_ORIGINAL_DST, | ||
410 | .get_optmax = IP6T_SO_ORIGINAL_DST + 1, | ||
411 | .get = ipv6_getorigdst, | ||
412 | .owner = THIS_MODULE, | ||
413 | }; | ||
414 | |||
362 | static int ipv6_net_init(struct net *net) | 415 | static int ipv6_net_init(struct net *net) |
363 | { | 416 | { |
364 | int ret = 0; | 417 | int ret = 0; |
@@ -425,6 +478,12 @@ static int __init nf_conntrack_l3proto_ipv6_init(void) | |||
425 | need_conntrack(); | 478 | need_conntrack(); |
426 | nf_defrag_ipv6_enable(); | 479 | nf_defrag_ipv6_enable(); |
427 | 480 | ||
481 | ret = nf_register_sockopt(&so_getorigdst6); | ||
482 | if (ret < 0) { | ||
483 | pr_err("Unable to register netfilter socket option\n"); | ||
484 | return ret; | ||
485 | } | ||
486 | |||
428 | ret = register_pernet_subsys(&ipv6_net_ops); | 487 | ret = register_pernet_subsys(&ipv6_net_ops); |
429 | if (ret < 0) | 488 | if (ret < 0) |
430 | goto cleanup_pernet; | 489 | goto cleanup_pernet; |
@@ -440,6 +499,7 @@ static int __init nf_conntrack_l3proto_ipv6_init(void) | |||
440 | cleanup_ipv6: | 499 | cleanup_ipv6: |
441 | unregister_pernet_subsys(&ipv6_net_ops); | 500 | unregister_pernet_subsys(&ipv6_net_ops); |
442 | cleanup_pernet: | 501 | cleanup_pernet: |
502 | nf_unregister_sockopt(&so_getorigdst6); | ||
443 | return ret; | 503 | return ret; |
444 | } | 504 | } |
445 | 505 | ||
@@ -448,6 +508,7 @@ static void __exit nf_conntrack_l3proto_ipv6_fini(void) | |||
448 | synchronize_net(); | 508 | synchronize_net(); |
449 | nf_unregister_hooks(ipv6_conntrack_ops, ARRAY_SIZE(ipv6_conntrack_ops)); | 509 | nf_unregister_hooks(ipv6_conntrack_ops, ARRAY_SIZE(ipv6_conntrack_ops)); |
450 | unregister_pernet_subsys(&ipv6_net_ops); | 510 | unregister_pernet_subsys(&ipv6_net_ops); |
511 | nf_unregister_sockopt(&so_getorigdst6); | ||
451 | } | 512 | } |
452 | 513 | ||
453 | module_init(nf_conntrack_l3proto_ipv6_init); | 514 | module_init(nf_conntrack_l3proto_ipv6_init); |