diff options
author | Samuel Thibault <samuel.thibault@ens-lyon.org> | 2013-01-13 16:23:35 +0100 |
---|---|---|
committer | Samuel Thibault <samuel.thibault@ens-lyon.org> | 2013-01-13 16:26:51 +0100 |
commit | 2b2d7fdc42475019e5ce3eabc9c9673e3c13d89f (patch) | |
tree | e7409c870c39a10241875454e345d5ec16517738 /pfinet/linux-src/net/ipv6/udp_ipv6.c | |
parent | 03be85eb11f756414aafedca22f93a3351b727b8 (diff) | |
download | hurd-2b2d7fdc42475019e5ce3eabc9c9673e3c13d89f.tar.gz hurd-2b2d7fdc42475019e5ce3eabc9c9673e3c13d89f.tar.bz2 hurd-2b2d7fdc42475019e5ce3eabc9c9673e3c13d89f.zip |
[IPV6]: Add IPV6_V6ONLY socket option support.
Cherry-picked from Linux 524354b4d086a4f013343d727eaccb7b4c39eb25
* pfinet/glue-include/linux/ipv6.h: Include linux/config.h>
(__ipv6_only_sock, ipv6_only_sock): New macros
* pfinet/linux-src/include/linux/ipv6.h: Likewise.
* pfinet/linux-src/include/linux/in6.h (IPV6_V6ONLY): New macro.
* pfinet/linux-src/include/linux/sysctl.h (NET_IPV6_BINDV6ONLY): New macro.
* pfinet/linux-src/include/net/ipv6.h (sysctl_ipv6_bindv6only): Declare
variable.
* pfinet/linux-src/include/net/sock.h (ipv6_pinfo): Add ipv6only field.
* pfinet/linux-src/net/ipv4/tcp_ipv4.c: Include linux/ipv6.h.
(tcp_v4_get_port, tcp_v4_lookup_listener): Test for ipv6_only_sock.
* pfinet/linux-src/net/ipv4/udp.c: Include linux/ipv6.h.
(udp_v4_get_port, udp_v4_lookup_longway, udp_v4_mcast_next): Test for
ipv6_only_sock.
* pfinet/linux-src/net/ipv6/af_inet6.c (sysctl_ipv6_bindv6only): New
variable.
(inet6_create): Initialize ipv6only field to sysctl_ipv6_bindv6only.
* pfinet/linux-src/net/ipv6/ipv6_sockglue.c (ipv6_setsockopt): Test for
ipv6_only_sock.
(ipv6_setsockopt, ipv6_getsockopt): Support IPV6_V6ONLY case.
* pfinet/linux-src/net/ipv6/tcp_ipv6.c (ipv6_rcv_saddr_equal): New inline
function.
(tcp_v6_get_port): Replace old tests with ipv6_rcv_saddr_equal.
(tcp_v6_connect): Test for __ipv6_only_sock.
* pfinet/linux-src/net/ipv6/udp_ipv6.c (udv6_rcv_saddr_equal): New inline
function.
(udp_v6_get_port): Replace old tests with udv6_rcv_saddr_equal.
(udpv6_connect, udpv6_sendmsg): Test for __ipv6_only_sock.
Diffstat (limited to 'pfinet/linux-src/net/ipv6/udp_ipv6.c')
-rw-r--r-- | pfinet/linux-src/net/ipv6/udp_ipv6.c | 59 |
1 files changed, 53 insertions, 6 deletions
diff --git a/pfinet/linux-src/net/ipv6/udp_ipv6.c b/pfinet/linux-src/net/ipv6/udp_ipv6.c index f6968ae4..bbc4f027 100644 --- a/pfinet/linux-src/net/ipv6/udp_ipv6.c +++ b/pfinet/linux-src/net/ipv6/udp_ipv6.c @@ -5,6 +5,10 @@ * Authors: * Pedro Roque <roque@di.fc.ul.pt> * + * YOSHIFUJI Hideaki @USAGI and: Support IPV6_V6ONLY socket option, which + * Alexey Kuznetsov allow both IPv4 and IPv6 sockets to bind + * a single port at the same time. + * * Based on linux/ipv4/udp.c * * $Id: udp_ipv6.c,v 1.3 2007/10/13 01:43:00 stesie Exp $ @@ -46,6 +50,41 @@ struct udp_mib udp_stats_in6; +static __inline__ int udv6_rcv_saddr_equal(struct sock *sk, struct sock *sk2) +{ + struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6; + int addr_type = ipv6_addr_type(&np->rcv_saddr); + + if (!sk2->rcv_saddr && !ipv6_only_sock(sk)) + return 1; + + if (sk2->family == AF_INET6 && + ipv6_addr_any(&sk2->rcv_saddr) && + !(ipv6_only_sock(sk2) && addr_type == IPV6_ADDR_MAPPED)) + return 1; + + if (addr_type == IPV6_ADDR_ANY && + (!ipv6_only_sock(sk) || + !(sk2->family == AF_INET6 ? + ipv6_addr_type(&sk2->rcv_saddr) == IPV6_ADDR_MAPPED : 1))) + return 1; + + if (sk2->family == AF_INET6 && + !ipv6_addr_cmp(&sk->rcv_saddr, + &sk2->rcv_saddr)) + return 1; + + if (addr_type == IPV6_ADDR_MAPPED && + !ipv6_only_sock(sk2) && + (!sk2->rcv_saddr || + !sk->rcv_saddr || + sk->rcv_saddr == sk2->rcv_saddr)) + return 1; + + return 0; +} + + /* Grrr, addr_type already calculated by caller, but I don't want * to add some silly "cookie" argument to this method just for that. */ @@ -104,11 +143,8 @@ gotit: if (sk2->num == snum && sk2 != sk && sk2->bound_dev_if == sk->bound_dev_if && - (!sk2->rcv_saddr || - addr_type == IPV6_ADDR_ANY || - !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr, - &sk2->net_pinfo.af_inet6.rcv_saddr)) && - (!sk2->reuse || !sk->reuse)) + (!sk2->reuse || !sk->reuse) && + udv6_rcv_saddr_equal(sk, sk2)) goto fail; } } @@ -208,6 +244,8 @@ int udpv6_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) int err; if (usin->sin6_family == AF_INET) { + if (__ipv6_only_sock(sk)) + return -EAFNOSUPPORT; err = udp_connect(sk, uaddr, addr_len); goto ipv4_connected; } @@ -251,6 +289,9 @@ int udpv6_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) if (addr_type == IPV6_ADDR_MAPPED) { struct sockaddr_in sin; + if (__ipv6_only_sock(sk)) + return -ENETUNREACH; + sin.sin_family = AF_INET; sin.sin_addr.s_addr = daddr->s6_addr32[3]; sin.sin_port = usin->sin6_port; @@ -806,8 +847,11 @@ static int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, int ulen) fl.fl6_flowlabel = 0; if (sin6) { - if (sin6->sin6_family == AF_INET) + if (sin6->sin6_family == AF_INET) { + if (__ipv6_only_sock(sk)) + return -ENETUNREACH; return udp_sendmsg(sk, msg, ulen); + } if (addr_len < sizeof(*sin6)) return(-EINVAL); @@ -854,6 +898,9 @@ static int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, int ulen) if (addr_type == IPV6_ADDR_MAPPED) { struct sockaddr_in sin; + if (__ipv6_only_sock(sk)) + return -ENETUNREACH; + sin.sin_family = AF_INET; sin.sin_addr.s_addr = daddr->s6_addr32[3]; sin.sin_port = udh.uh.dest; |