aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6
diff options
context:
space:
mode:
authorFlorian Westphal <fw@strlen.de>2012-10-29 21:08:49 -0400
committerPablo Neira Ayuso <pablo@netfilter.org>2012-11-02 07:26:32 -0400
commit121d1e0941e05c64ee4223064dd83eb24e871739 (patch)
treea5b841774451210d2d53db14b7cd907cd47f12d4 /net/ipv6
parent6229b75d8da5a4eed7bb668de757e252986c2305 (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/ipv6')
-rw-r--r--net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c61
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
299static int
300ipv6_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));
359MODULE_LICENSE("GPL"); 404MODULE_LICENSE("GPL");
360MODULE_AUTHOR("Yasuyuki KOZAKAI @USAGI <yasuyuki.kozakai@toshiba.co.jp>"); 405MODULE_AUTHOR("Yasuyuki KOZAKAI @USAGI <yasuyuki.kozakai@toshiba.co.jp>");
361 406
407static 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
362static int ipv6_net_init(struct net *net) 415static 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
453module_init(nf_conntrack_l3proto_ipv6_init); 514module_init(nf_conntrack_l3proto_ipv6_init);