aboutsummaryrefslogtreecommitdiff
path: root/pfinet/linux-src/net/ipv6/tcp_ipv6.c
diff options
context:
space:
mode:
authorSamuel Thibault <samuel.thibault@ens-lyon.org>2013-01-13 16:23:35 +0100
committerSamuel Thibault <samuel.thibault@ens-lyon.org>2013-01-13 16:26:51 +0100
commit2b2d7fdc42475019e5ce3eabc9c9673e3c13d89f (patch)
treee7409c870c39a10241875454e345d5ec16517738 /pfinet/linux-src/net/ipv6/tcp_ipv6.c
parent03be85eb11f756414aafedca22f93a3351b727b8 (diff)
downloadhurd-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/tcp_ipv6.c')
-rw-r--r--pfinet/linux-src/net/ipv6/tcp_ipv6.c58
1 files changed, 47 insertions, 11 deletions
diff --git a/pfinet/linux-src/net/ipv6/tcp_ipv6.c b/pfinet/linux-src/net/ipv6/tcp_ipv6.c
index c761e76c..3fba9af6 100644
--- a/pfinet/linux-src/net/ipv6/tcp_ipv6.c
+++ b/pfinet/linux-src/net/ipv6/tcp_ipv6.c
@@ -4,6 +4,9 @@
*
* 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.
*
* $Id: tcp_ipv6.c,v 1.3 2007/10/13 01:43:00 stesie Exp $
*
@@ -79,6 +82,43 @@ static __inline__ int tcp_v6_sk_hashfn(struct sock *sk)
return tcp_v6_hashfn(laddr, lport, faddr, fport);
}
+static inline int ipv6_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(&np->rcv_saddr,
+ (sk2->state != TCP_TIME_WAIT ?
+ &sk2->rcv_saddr :
+ &((struct tcp_tw_bucket *)sk)->v6_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.
* But it doesn't matter, the recalculation is in the rarest path
@@ -134,18 +174,11 @@ static int tcp_v6_get_port(struct sock *sk, unsigned short snum)
for( ; sk2 != NULL; sk2 = sk2->bind_next) {
if (sk->bound_dev_if == sk2->bound_dev_if) {
- if (!sk_reuse ||
+ if ((!sk_reuse ||
!sk2->reuse ||
- sk2->state == TCP_LISTEN) {
- /* NOTE: IPv6 tw bucket have different format */
- if (!sk2->rcv_saddr ||
- addr_type == IPV6_ADDR_ANY ||
- !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr,
- sk2->state != TCP_TIME_WAIT ?
- &sk2->net_pinfo.af_inet6.rcv_saddr :
- &((struct tcp_tw_bucket*)sk)->v6_rcv_saddr))
- break;
- }
+ sk2->state == TCP_LISTEN) &&
+ ipv6_rcv_saddr_equal(sk, sk2))
+ break;
}
}
/* If we found a conflict, fail. */
@@ -460,6 +493,9 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
SOCK_DEBUG(sk, "connect: ipv4 mapped\n");
+ if (__ipv6_only_sock(sk))
+ return -ENETUNREACH;
+
sin.sin_family = AF_INET;
sin.sin_port = usin->sin6_port;
sin.sin_addr.s_addr = usin->sin6_addr.s6_addr32[3];