summaryrefslogtreecommitdiff
path: root/pfinet/linux-src
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
parent03be85eb11f756414aafedca22f93a3351b727b8 (diff)
[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')
-rw-r--r--pfinet/linux-src/include/linux/in6.h1
-rw-r--r--pfinet/linux-src/include/linux/ipv6.h9
-rw-r--r--pfinet/linux-src/include/linux/sysctl.h3
-rw-r--r--pfinet/linux-src/include/net/ipv6.h3
-rw-r--r--pfinet/linux-src/include/net/sock.h3
-rw-r--r--pfinet/linux-src/net/ipv4/tcp_ipv4.c20
-rw-r--r--pfinet/linux-src/net/ipv4/udp.c21
-rw-r--r--pfinet/linux-src/net/ipv6/af_inet6.c4
-rw-r--r--pfinet/linux-src/net/ipv6/ipv6_sockglue.c15
-rw-r--r--pfinet/linux-src/net/ipv6/tcp_ipv6.c58
-rw-r--r--pfinet/linux-src/net/ipv6/udp_ipv6.c59
11 files changed, 162 insertions, 34 deletions
diff --git a/pfinet/linux-src/include/linux/in6.h b/pfinet/linux-src/include/linux/in6.h
index ca5e768b..35e018c7 100644
--- a/pfinet/linux-src/include/linux/in6.h
+++ b/pfinet/linux-src/include/linux/in6.h
@@ -179,6 +179,7 @@ struct in6_flowlabel_req
#define IPV6_MTU_DISCOVER 23
#define IPV6_MTU 24
#define IPV6_RECVERR 25
+#define IPV6_V6ONLY 26
/* IPV6_MTU_DISCOVER values */
#define IPV6_PMTUDISC_DONT 0
diff --git a/pfinet/linux-src/include/linux/ipv6.h b/pfinet/linux-src/include/linux/ipv6.h
index 84564bae..299db1a5 100644
--- a/pfinet/linux-src/include/linux/ipv6.h
+++ b/pfinet/linux-src/include/linux/ipv6.h
@@ -1,6 +1,7 @@
#ifndef _IPV6_H
#define _IPV6_H
+#include <linux/config.h>
#include <linux/in6.h>
#include <asm/byteorder.h>
@@ -118,6 +119,14 @@ struct inet6_skb_parm
__u16 dst1;
};
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+#define __ipv6_only_sock(sk) (sk->net_pinfo.af_inet6.ipv6only)
+#define ipv6_only_sock(sk) ((sk)->family == PF_INET6 && __ipv6_only_sock(sk))
+#else
+#define __ipv6_only_sock(sk) 0
+#define ipv6_only_sock(sk) 0
+#endif
+
#endif
#endif
diff --git a/pfinet/linux-src/include/linux/sysctl.h b/pfinet/linux-src/include/linux/sysctl.h
index 00bebabe..1067bfbb 100644
--- a/pfinet/linux-src/include/linux/sysctl.h
+++ b/pfinet/linux-src/include/linux/sysctl.h
@@ -276,7 +276,8 @@ enum
enum {
NET_IPV6_CONF=16,
NET_IPV6_NEIGH=17,
- NET_IPV6_ROUTE=18
+ NET_IPV6_ROUTE=18,
+ NET_IPV6_BINDV6ONLY=20,
};
enum {
diff --git a/pfinet/linux-src/include/net/ipv6.h b/pfinet/linux-src/include/net/ipv6.h
index 50e2299f..77520eeb 100644
--- a/pfinet/linux-src/include/net/ipv6.h
+++ b/pfinet/linux-src/include/net/ipv6.h
@@ -86,6 +86,9 @@ struct frag_hdr {
#include <net/sock.h>
+/* sysctls */
+extern int sysctl_ipv6_bindv6only;
+
extern struct ipv6_mib ipv6_statistics;
extern struct icmpv6_mib icmpv6_statistics;
extern struct udp_mib udp_stats_in6;
diff --git a/pfinet/linux-src/include/net/sock.h b/pfinet/linux-src/include/net/sock.h
index 7789ec40..96fdfe8b 100644
--- a/pfinet/linux-src/include/net/sock.h
+++ b/pfinet/linux-src/include/net/sock.h
@@ -170,7 +170,8 @@ struct ipv6_pinfo {
__u8 mc_loop:1,
recverr:1,
sndflow:1,
- pmtudisc:2;
+ pmtudisc:2,
+ ipv6only:1;
struct ipv6_mc_socklist *ipv6_mc_list;
struct ipv6_fl_socklist *ipv6_fl_list;
diff --git a/pfinet/linux-src/net/ipv4/tcp_ipv4.c b/pfinet/linux-src/net/ipv4/tcp_ipv4.c
index 60d2bdb4..df2c8b7c 100644
--- a/pfinet/linux-src/net/ipv4/tcp_ipv4.c
+++ b/pfinet/linux-src/net/ipv4/tcp_ipv4.c
@@ -45,9 +45,13 @@
* Vitaly E. Lavrov : Transparent proxy revived after year coma.
* Andi Kleen : Fix new listen.
* Andi Kleen : Fix accept error reporting.
+ * 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.
*/
#include <linux/config.h>
+
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/random.h>
@@ -61,6 +65,7 @@
#include <asm/segment.h>
#include <linux/inet.h>
+#include <linux/ipv6.h>
#include <linux/stddef.h>
extern int sysctl_tcp_timestamps;
@@ -258,7 +263,8 @@ static int tcp_v4_get_port(struct sock *sk, unsigned short snum)
int sk_reuse = sk->reuse;
for( ; sk2 != NULL; sk2 = sk2->bind_next) {
- if (sk->bound_dev_if == sk2->bound_dev_if) {
+ if (!ipv6_only_sock(sk2) &&
+ sk->bound_dev_if == sk2->bound_dev_if) {
if (!sk_reuse ||
!sk2->reuse ||
sk2->state == TCP_LISTEN) {
@@ -378,23 +384,23 @@ static struct sock *tcp_v4_lookup_listener(u32 daddr, unsigned short hnum, int d
struct sock *result = NULL;
int score, hiscore;
- hiscore=0;
+ hiscore=-1;
for(sk = tcp_listening_hash[tcp_lhashfn(hnum)]; sk; sk = sk->next) {
- if(sk->num == hnum) {
+ if(sk->num == hnum && !ipv6_only_sock(sk)) {
__u32 rcv_saddr = sk->rcv_saddr;
- score = 1;
+ score = (sk->family == PF_INET ? 1 : 0);
if(rcv_saddr) {
if (rcv_saddr != daddr)
continue;
- score++;
+ score+=2;
}
if (sk->bound_dev_if) {
if (sk->bound_dev_if != dif)
continue;
- score++;
+ score+=2;
}
- if (score == 3)
+ if (score == 5)
return sk;
if (score > hiscore) {
hiscore = score;
diff --git a/pfinet/linux-src/net/ipv4/udp.c b/pfinet/linux-src/net/ipv4/udp.c
index 1ceb43e4..f9be2e04 100644
--- a/pfinet/linux-src/net/ipv4/udp.c
+++ b/pfinet/linux-src/net/ipv4/udp.c
@@ -61,6 +61,9 @@
* return ENOTCONN for unconnected sockets (POSIX)
* Janos Farkas : don't deliver multi/broadcasts to a different
* bound-to-device socket
+ * 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.
*
*
* This program is free software; you can redistribute it and/or
@@ -104,6 +107,7 @@
#include <linux/mm.h>
#include <linux/config.h>
#include <linux/inet.h>
+#include <linux/ipv6.h>
#include <linux/netdevice.h>
#include <net/snmp.h>
#include <net/ip.h>
@@ -178,6 +182,7 @@ gotit:
sk2 = sk2->next) {
if (sk2->num == snum &&
sk2 != sk &&
+ !ipv6_only_sock(sk2) &&
sk2->bound_dev_if == sk->bound_dev_if &&
(!sk2->rcv_saddr ||
!sk->rcv_saddr ||
@@ -236,29 +241,30 @@ struct sock *udp_v4_lookup_longway(u32 saddr, u16 sport, u32 daddr, u16 dport, i
int badness = -1;
for(sk = udp_hash[hnum & (UDP_HTABLE_SIZE - 1)]; sk != NULL; sk = sk->next) {
- if((sk->num == hnum) && !(sk->dead && (sk->state == TCP_CLOSE))) {
- int score = 0;
+ if((sk->num == hnum) && !ipv6_only_sock(sk)
+ && !(sk->dead && (sk->state == TCP_CLOSE))) {
+ int score = (sk->family == PF_INET ? 1 : 0);
if(sk->rcv_saddr) {
if(sk->rcv_saddr != daddr)
continue;
- score++;
+ score+=2;
}
if(sk->daddr) {
if(sk->daddr != saddr)
continue;
- score++;
+ score+=2;
}
if(sk->dport) {
if(sk->dport != sport)
continue;
- score++;
+ score+=2;
}
if(sk->bound_dev_if) {
if(sk->bound_dev_if != dif)
continue;
- score++;
+ score+=2;
}
- if(score == 4) {
+ if(score == 9) {
result = sk;
break;
} else if(score > badness) {
@@ -389,6 +395,7 @@ static inline struct sock *udp_v4_mcast_next(struct sock *sk,
(s->daddr && s->daddr!=raddr) ||
(s->dport != rnum && s->dport != 0) ||
(s->rcv_saddr && s->rcv_saddr != laddr) ||
+ ipv6_only_sock(s) ||
(s->bound_dev_if && s->bound_dev_if != dif))
continue;
break;
diff --git a/pfinet/linux-src/net/ipv6/af_inet6.c b/pfinet/linux-src/net/ipv6/af_inet6.c
index ca428188..f10ff8d7 100644
--- a/pfinet/linux-src/net/ipv6/af_inet6.c
+++ b/pfinet/linux-src/net/ipv6/af_inet6.c
@@ -84,6 +84,8 @@ extern void ipv6_sysctl_register(void);
extern void ipv6_sysctl_unregister(void);
#endif
+int sysctl_ipv6_bindv6only;
+
static int inet6_create(struct socket *sock, int protocol)
{
struct sock *sk;
@@ -137,6 +139,8 @@ static int inet6_create(struct socket *sock, int protocol)
sk->net_pinfo.af_inet6.mc_loop = 1;
sk->net_pinfo.af_inet6.pmtudisc = IPV6_PMTUDISC_WANT;
+ sk->net_pinfo.af_inet6.ipv6only = sysctl_ipv6_bindv6only;
+
/* Init the ipv4 part of the socket since we can have sockets
* using v6 API for ipv4.
*/
diff --git a/pfinet/linux-src/net/ipv6/ipv6_sockglue.c b/pfinet/linux-src/net/ipv6/ipv6_sockglue.c
index 6a48d1be..f1f67811 100644
--- a/pfinet/linux-src/net/ipv6/ipv6_sockglue.c
+++ b/pfinet/linux-src/net/ipv6/ipv6_sockglue.c
@@ -146,7 +146,8 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname, char *optval,
goto addrform_done;
}
- if (!(ipv6_addr_type(&np->daddr) & IPV6_ADDR_MAPPED)) {
+ if (ipv6_only_sock(sk) ||
+ !(ipv6_addr_type(&np->daddr) & IPV6_ADDR_MAPPED)) {
retv = -EADDRNOTAVAIL;
goto addrform_done;
}
@@ -181,6 +182,15 @@ addrform_done:
}
break;
+ case IPV6_V6ONLY:
+ if (sk->num) {
+ retv = -EINVAL;
+ goto out;
+ }
+ np->ipv6only = valbool;
+ retv = 0;
+ break;
+
case IPV6_PKTINFO:
np->rxopt.bits.rxinfo = valbool;
retv = 0;
@@ -395,6 +405,9 @@ int ipv6_getsockopt(struct sock *sk, int level, int optname, char *optval,
if (!val)
return -ENOTCONN;
break;
+ case IPV6_V6ONLY:
+ val = np->ipv6only;
+ break;
default:
return -EINVAL;
}
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];
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;