Commit 524354b4 authored by Hideaki Yoshifuji's avatar Hideaki Yoshifuji Committed by David S. Miller

[IPV6]: Add IPV6_V6ONLY socket option support.

parent 4f5b0080
......@@ -462,6 +462,15 @@ ak@muc.de
IPv6 has no global variables such as tcp_*. tcp_* settings under ipv4/ also
apply to IPv6 [XXX?].
bindv6only - BOOLEAN
Default value for IPV6_V6ONLY socket option,
which restricts use of the IPv6 socket to IPv6 communication
only.
TRUE: disable IPv4-mapped address feature
FALSE: enable IPv4-mapped address feature
Default: FALSE (as specified in RFC2553bis)
conf/default/*:
Change the interface-specific default settings.
......
......@@ -156,6 +156,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
......
#ifndef _IPV6_H
#define _IPV6_H
#include <linux/config.h>
#include <linux/in6.h>
#include <asm/byteorder.h>
......@@ -152,7 +153,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;
......@@ -195,6 +197,15 @@ struct tcp6_sock {
#define inet6_sk(__sk) ((struct raw6_sock *)__sk)->pinet6
#define raw6_sk(__sk) (&((struct raw6_sock *)__sk)->raw6)
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
#define __ipv6_only_sock(sk) (inet6_sk(sk)->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
......@@ -357,7 +357,8 @@ enum
enum {
NET_IPV6_CONF=16,
NET_IPV6_NEIGH=17,
NET_IPV6_ROUTE=18
NET_IPV6_ROUTE=18,
NET_IPV6_BINDV6ONLY=20,
};
enum {
......
......@@ -102,6 +102,9 @@ struct frag_hdr {
#include <net/sock.h>
/* sysctls */
extern int sysctl_ipv6_bindv6only;
extern struct ipv6_mib ipv6_statistics[NR_CPUS*2];
#define IP6_INC_STATS(field) SNMP_INC_STATS(ipv6_statistics, field)
#define IP6_INC_STATS_BH(field) SNMP_INC_STATS_BH(ipv6_statistics, field)
......
......@@ -47,9 +47,13 @@
* 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>
......@@ -62,6 +66,7 @@
#include <net/inet_common.h>
#include <linux/inet.h>
#include <linux/ipv6.h>
#include <linux/stddef.h>
#include <linux/ipsec.h>
......@@ -176,7 +181,9 @@ static inline int tcp_bind_conflict(struct sock *sk, struct tcp_bind_bucket *tb)
int sk_reuse = sk->reuse;
for ( ; sk2; sk2 = sk2->bind_next) {
if (sk != sk2 && sk->bound_dev_if == sk2->bound_dev_if) {
if (sk != sk2 &&
!ipv6_only_sock(sk2) &&
sk->bound_dev_if == sk2->bound_dev_if) {
if (!sk_reuse || !sk2->reuse ||
sk2->state == TCP_LISTEN) {
struct inet_opt *inet2 = inet_sk(sk2);
......@@ -412,25 +419,25 @@ static struct sock *__tcp_v4_lookup_listener(struct sock *sk, u32 daddr,
struct sock *result = NULL;
int score, hiscore;
hiscore=0;
hiscore=-1;
for (; sk; sk = sk->next) {
struct inet_opt *inet = inet_sk(sk);
if (inet->num == hnum) {
if (inet->num == hnum && !ipv6_only_sock(sk)) {
__u32 rcv_saddr = inet->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;
......@@ -454,6 +461,7 @@ __inline__ struct sock *tcp_v4_lookup_listener(u32 daddr, unsigned short hnum,
if (inet->num == hnum && !sk->next &&
(!inet->rcv_saddr || inet->rcv_saddr == daddr) &&
(sk->family == PF_INET || !ipv6_only_sock(sk)) &&
!sk->bound_dev_if)
goto sherry_cache;
sk = __tcp_v4_lookup_listener(sk, daddr, hnum, dif);
......
......@@ -66,6 +66,9 @@
* datagrams.
* Hirokazu Takahashi : sendfile() on UDP works now.
* Arnaldo C. Melo : convert /proc/net/udp to seq_file
* 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
......@@ -87,6 +90,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/tcp.h>
......@@ -170,6 +174,7 @@ static int udp_v4_get_port(struct sock *sk, unsigned short snum)
if (inet2->num == snum &&
sk2 != sk &&
!ipv6_only_sock(sk2) &&
sk2->bound_dev_if == sk->bound_dev_if &&
(!inet2->rcv_saddr ||
!inet->rcv_saddr ||
......@@ -228,29 +233,29 @@ struct sock *udp_v4_lookup_longway(u32 saddr, u16 sport, u32 daddr, u16 dport, i
for(sk = udp_hash[hnum & (UDP_HTABLE_SIZE - 1)]; sk != NULL; sk = sk->next) {
struct inet_opt *inet = inet_sk(sk);
if (inet->num == hnum) {
int score = 0;
if (inet->num == hnum && !ipv6_only_sock(sk)) {
int score = (sk->family == PF_INET ? 1 : 0);
if (inet->rcv_saddr) {
if (inet->rcv_saddr != daddr)
continue;
score++;
score+=2;
}
if (inet->daddr) {
if (inet->daddr != saddr)
continue;
score++;
score+=2;
}
if (inet->dport) {
if (inet->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) {
......@@ -288,6 +293,7 @@ static inline struct sock *udp_v4_mcast_next(struct sock *sk,
(inet->daddr && inet->daddr != rmt_addr) ||
(inet->dport != rmt_port && inet->dport) ||
(inet->rcv_saddr && inet->rcv_saddr != loc_addr) ||
ipv6_only_sock(s) ||
(s->bound_dev_if && s->bound_dev_if != dif))
continue;
break;
......
......@@ -88,6 +88,8 @@ extern void ipv6_sysctl_register(void);
extern void ipv6_sysctl_unregister(void);
#endif
int sysctl_ipv6_bindv6only;
#ifdef INET_REFCNT_DEBUG
atomic_t inet6_sock_nr;
#endif
......@@ -218,7 +220,8 @@ static int inet6_create(struct socket *sock, int protocol)
np->mcast_hops = -1;
np->mc_loop = 1;
np->pmtudisc = IPV6_PMTUDISC_WANT;
np->ipv6only = sysctl_ipv6_bindv6only;
/* Init the ipv4 part of the socket since we can have sockets
* using v6 API for ipv4.
*/
......
......@@ -157,7 +157,8 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname, char *optval,
break;
}
if (!(ipv6_addr_type(&np->daddr) & IPV6_ADDR_MAPPED)) {
if (ipv6_only_sock(sk) ||
!(ipv6_addr_type(&np->daddr) & IPV6_ADDR_MAPPED)) {
retv = -EADDRNOTAVAIL;
break;
}
......@@ -203,6 +204,13 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname, char *optval,
}
goto e_inval;
case IPV6_V6ONLY:
if (inet_sk(sk)->num)
goto e_inval;
np->ipv6only = valbool;
retv = 0;
break;
case IPV6_PKTINFO:
np->rxopt.bits.rxinfo = valbool;
retv = 0;
......@@ -475,6 +483,10 @@ int ipv6_getsockopt(struct sock *sk, int level, int optname, char *optval,
break;
}
case IPV6_V6ONLY:
val = np->ipv6only;
break;
case IPV6_PKTINFO:
val = np->rxopt.bits.rxinfo;
break;
......
......@@ -17,6 +17,8 @@ extern ctl_table ipv6_route_table[];
ctl_table ipv6_table[] = {
{NET_IPV6_ROUTE, "route", NULL, 0, 0555, ipv6_route_table},
{NET_IPV6_BINDV6ONLY, "bindv6only",
&sysctl_ipv6_bindv6only, sizeof(int), 0644, NULL, &proc_dointvec},
{0}
};
......
......@@ -14,6 +14,9 @@
*
* Fixes:
* Hideaki YOSHIFUJI : sin6_scope_id support
* 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
* modify it under the terms of the GNU General Public License
......@@ -90,14 +93,35 @@ static inline int ipv6_rcv_saddr_equal(struct sock *sk, struct sock *sk2)
struct ipv6_pinfo *np = inet6_sk(sk);
int addr_type = ipv6_addr_type(&np->rcv_saddr);
return !inet_sk(sk2)->rcv_saddr || addr_type == IPV6_ADDR_ANY ||
(sk2->family == AF_INET6 &&
!ipv6_addr_cmp(&np->rcv_saddr,
sk2->state != TCP_TIME_WAIT ?
&inet6_sk(sk2)->rcv_saddr :
&((struct tcp_tw_bucket *)sk)->v6_rcv_saddr)) ||
(addr_type == IPV6_ADDR_MAPPED && sk2->family == AF_INET &&
inet_sk(sk)->rcv_saddr == inet_sk(sk2)->rcv_saddr);
if (!inet_sk(sk2)->rcv_saddr && !ipv6_only_sock(sk))
return 1;
if (sk2->family == AF_INET6 &&
ipv6_addr_any(&inet6_sk(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(&inet6_sk(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 ?
&inet6_sk(sk2)->rcv_saddr :
&((struct tcp_tw_bucket *)sk)->v6_rcv_saddr)))
return 1;
if (addr_type == IPV6_ADDR_MAPPED &&
!ipv6_only_sock(sk2) &&
(!inet_sk(sk2)->rcv_saddr ||
!inet_sk(sk)->rcv_saddr ||
inet_sk(sk)->rcv_saddr == inet_sk(sk2)->rcv_saddr))
return 1;
return 0;
}
static inline int tcp_v6_bind_conflict(struct sock *sk,
......@@ -612,6 +636,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];
......
......@@ -11,6 +11,9 @@
*
* Fixes:
* Hideaki YOSHIFUJI : sin6_scope_id support
* 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
* modify it under the terms of the GNU General Public License
......@@ -50,16 +53,41 @@
struct udp_mib udp_stats_in6[NR_CPUS*2];
/* XXX This is identical to tcp_ipv6.c:ipv6_rcv_saddr_equal, put
* XXX it somewhere common. -DaveM
*/
static __inline__ int udv6_rcv_saddr_equal(struct sock *sk, struct sock *sk2)
{
struct ipv6_pinfo *np = inet6_sk(sk);
int addr_type = ipv6_addr_type(&np->rcv_saddr);
return !inet_sk(sk2)->rcv_saddr || addr_type == IPV6_ADDR_ANY ||
(sk2->family == AF_INET6 &&
!ipv6_addr_cmp(&np->rcv_saddr, &inet6_sk(sk2)->rcv_saddr)) ||
(addr_type == IPV6_ADDR_MAPPED && sk2->family == AF_INET &&
inet_sk(sk)->rcv_saddr == inet_sk(sk2)->rcv_saddr);
if (!inet_sk(sk2)->rcv_saddr && !ipv6_only_sock(sk))
return 1;
if (sk2->family == AF_INET6 &&
ipv6_addr_any(&inet6_sk(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(&inet6_sk(sk2)->rcv_saddr) == IPV6_ADDR_MAPPED) : 1)))
return 1;
if (sk2->family == AF_INET6 &&
!ipv6_addr_cmp(&inet6_sk(sk)->rcv_saddr,
&inet6_sk(sk2)->rcv_saddr))
return 1;
if (addr_type == IPV6_ADDR_MAPPED &&
!ipv6_only_sock(sk2) &&
(!inet_sk(sk2)->rcv_saddr ||
!inet_sk(sk)->rcv_saddr ||
inet_sk(sk)->rcv_saddr == inet_sk(sk2)->rcv_saddr))
return 1;
return 0;
}
/* Grrr, addr_type already calculated by caller, but I don't want
......@@ -228,6 +256,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;
}
......@@ -263,6 +293,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;
......@@ -793,8 +826,11 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg
fl.oif = 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(iocb, sk, msg, ulen);
}
if (addr_len < SIN6_LEN_RFC2133)
return -EINVAL;
......@@ -841,6 +877,9 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg
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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment