Commit 121d1e0941e05c64ee4223064dd83eb24e871739
Committed by
Pablo Neira Ayuso
1 parent
6229b75d8d
Exists in
smarc-l5.0.0_1.0.0-ga
and in
5 other branches
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>
Showing 3 changed files with 65 additions and 0 deletions Side-by-side Diff
include/uapi/linux/in6.h
include/uapi/linux/netfilter_ipv6/ip6_tables.h
... | ... | @@ -178,6 +178,9 @@ |
178 | 178 | #define IP6T_SO_GET_REVISION_TARGET (IP6T_BASE_CTL + 5) |
179 | 179 | #define IP6T_SO_GET_MAX IP6T_SO_GET_REVISION_TARGET |
180 | 180 | |
181 | +/* obtain original address if REDIRECT'd connection */ | |
182 | +#define IP6T_SO_ORIGINAL_DST 80 | |
183 | + | |
181 | 184 | /* ICMP matching stuff */ |
182 | 185 | struct ip6t_icmp { |
183 | 186 | __u8 type; /* type to match */ |
net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
... | ... | @@ -21,6 +21,7 @@ |
21 | 21 | |
22 | 22 | #include <linux/netfilter_bridge.h> |
23 | 23 | #include <linux/netfilter_ipv6.h> |
24 | +#include <linux/netfilter_ipv6/ip6_tables.h> | |
24 | 25 | #include <net/netfilter/nf_conntrack.h> |
25 | 26 | #include <net/netfilter/nf_conntrack_helper.h> |
26 | 27 | #include <net/netfilter/nf_conntrack_l4proto.h> |
... | ... | @@ -295,6 +296,50 @@ |
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 | 343 | #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) |
299 | 344 | |
300 | 345 | #include <linux/netfilter/nfnetlink.h> |
... | ... | @@ -359,6 +404,14 @@ |
359 | 404 | MODULE_LICENSE("GPL"); |
360 | 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 | 415 | static int ipv6_net_init(struct net *net) |
363 | 416 | { |
364 | 417 | int ret = 0; |
... | ... | @@ -425,6 +478,12 @@ |
425 | 478 | need_conntrack(); |
426 | 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 | 487 | ret = register_pernet_subsys(&ipv6_net_ops); |
429 | 488 | if (ret < 0) |
430 | 489 | goto cleanup_pernet; |
... | ... | @@ -440,6 +499,7 @@ |
440 | 499 | cleanup_ipv6: |
441 | 500 | unregister_pernet_subsys(&ipv6_net_ops); |
442 | 501 | cleanup_pernet: |
502 | + nf_unregister_sockopt(&so_getorigdst6); | |
443 | 503 | return ret; |
444 | 504 | } |
445 | 505 | |
... | ... | @@ -448,6 +508,7 @@ |
448 | 508 | synchronize_net(); |
449 | 509 | nf_unregister_hooks(ipv6_conntrack_ops, ARRAY_SIZE(ipv6_conntrack_ops)); |
450 | 510 | unregister_pernet_subsys(&ipv6_net_ops); |
511 | + nf_unregister_sockopt(&so_getorigdst6); | |
451 | 512 | } |
452 | 513 | |
453 | 514 | module_init(nf_conntrack_l3proto_ipv6_init); |