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 ...@@ -462,6 +462,15 @@ ak@muc.de
IPv6 has no global variables such as tcp_*. tcp_* settings under ipv4/ also IPv6 has no global variables such as tcp_*. tcp_* settings under ipv4/ also
apply to IPv6 [XXX?]. 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/*: conf/default/*:
Change the interface-specific default settings. Change the interface-specific default settings.
......
...@@ -156,6 +156,7 @@ struct in6_flowlabel_req ...@@ -156,6 +156,7 @@ struct in6_flowlabel_req
#define IPV6_MTU_DISCOVER 23 #define IPV6_MTU_DISCOVER 23
#define IPV6_MTU 24 #define IPV6_MTU 24
#define IPV6_RECVERR 25 #define IPV6_RECVERR 25
#define IPV6_V6ONLY 26
/* IPV6_MTU_DISCOVER values */ /* IPV6_MTU_DISCOVER values */
#define IPV6_PMTUDISC_DONT 0 #define IPV6_PMTUDISC_DONT 0
......
#ifndef _IPV6_H #ifndef _IPV6_H
#define _IPV6_H #define _IPV6_H
#include <linux/config.h>
#include <linux/in6.h> #include <linux/in6.h>
#include <asm/byteorder.h> #include <asm/byteorder.h>
...@@ -152,7 +153,8 @@ struct ipv6_pinfo { ...@@ -152,7 +153,8 @@ struct ipv6_pinfo {
__u8 mc_loop:1, __u8 mc_loop:1,
recverr:1, recverr:1,
sndflow:1, sndflow:1,
pmtudisc:2; pmtudisc:2,
ipv6only:1;
struct ipv6_mc_socklist *ipv6_mc_list; struct ipv6_mc_socklist *ipv6_mc_list;
struct ipv6_fl_socklist *ipv6_fl_list; struct ipv6_fl_socklist *ipv6_fl_list;
...@@ -195,6 +197,15 @@ struct tcp6_sock { ...@@ -195,6 +197,15 @@ struct tcp6_sock {
#define inet6_sk(__sk) ((struct raw6_sock *)__sk)->pinet6 #define inet6_sk(__sk) ((struct raw6_sock *)__sk)->pinet6
#define raw6_sk(__sk) (&((struct raw6_sock *)__sk)->raw6) #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
#endif #endif
...@@ -357,7 +357,8 @@ enum ...@@ -357,7 +357,8 @@ enum
enum { enum {
NET_IPV6_CONF=16, NET_IPV6_CONF=16,
NET_IPV6_NEIGH=17, NET_IPV6_NEIGH=17,
NET_IPV6_ROUTE=18 NET_IPV6_ROUTE=18,
NET_IPV6_BINDV6ONLY=20,
}; };
enum { enum {
......
...@@ -102,6 +102,9 @@ struct frag_hdr { ...@@ -102,6 +102,9 @@ struct frag_hdr {
#include <net/sock.h> #include <net/sock.h>
/* sysctls */
extern int sysctl_ipv6_bindv6only;
extern struct ipv6_mib ipv6_statistics[NR_CPUS*2]; extern struct ipv6_mib ipv6_statistics[NR_CPUS*2];
#define IP6_INC_STATS(field) SNMP_INC_STATS(ipv6_statistics, field) #define IP6_INC_STATS(field) SNMP_INC_STATS(ipv6_statistics, field)
#define IP6_INC_STATS_BH(field) SNMP_INC_STATS_BH(ipv6_statistics, field) #define IP6_INC_STATS_BH(field) SNMP_INC_STATS_BH(ipv6_statistics, field)
......
...@@ -47,9 +47,13 @@ ...@@ -47,9 +47,13 @@
* coma. * coma.
* Andi Kleen : Fix new listen. * Andi Kleen : Fix new listen.
* Andi Kleen : Fix accept error reporting. * 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/config.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/fcntl.h> #include <linux/fcntl.h>
#include <linux/random.h> #include <linux/random.h>
...@@ -62,6 +66,7 @@ ...@@ -62,6 +66,7 @@
#include <net/inet_common.h> #include <net/inet_common.h>
#include <linux/inet.h> #include <linux/inet.h>
#include <linux/ipv6.h>
#include <linux/stddef.h> #include <linux/stddef.h>
#include <linux/ipsec.h> #include <linux/ipsec.h>
...@@ -176,7 +181,9 @@ static inline int tcp_bind_conflict(struct sock *sk, struct tcp_bind_bucket *tb) ...@@ -176,7 +181,9 @@ static inline int tcp_bind_conflict(struct sock *sk, struct tcp_bind_bucket *tb)
int sk_reuse = sk->reuse; int sk_reuse = sk->reuse;
for ( ; sk2; sk2 = sk2->bind_next) { 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 || if (!sk_reuse || !sk2->reuse ||
sk2->state == TCP_LISTEN) { sk2->state == TCP_LISTEN) {
struct inet_opt *inet2 = inet_sk(sk2); struct inet_opt *inet2 = inet_sk(sk2);
...@@ -412,25 +419,25 @@ static struct sock *__tcp_v4_lookup_listener(struct sock *sk, u32 daddr, ...@@ -412,25 +419,25 @@ static struct sock *__tcp_v4_lookup_listener(struct sock *sk, u32 daddr,
struct sock *result = NULL; struct sock *result = NULL;
int score, hiscore; int score, hiscore;
hiscore=0; hiscore=-1;
for (; sk; sk = sk->next) { for (; sk; sk = sk->next) {
struct inet_opt *inet = inet_sk(sk); 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; __u32 rcv_saddr = inet->rcv_saddr;
score = 1; score = (sk->family == PF_INET ? 1 : 0);
if (rcv_saddr) { if (rcv_saddr) {
if (rcv_saddr != daddr) if (rcv_saddr != daddr)
continue; continue;
score++; score+=2;
} }
if (sk->bound_dev_if) { if (sk->bound_dev_if) {
if (sk->bound_dev_if != dif) if (sk->bound_dev_if != dif)
continue; continue;
score++; score+=2;
} }
if (score == 3) if (score == 5)
return sk; return sk;
if (score > hiscore) { if (score > hiscore) {
hiscore = score; hiscore = score;
...@@ -454,6 +461,7 @@ __inline__ struct sock *tcp_v4_lookup_listener(u32 daddr, unsigned short hnum, ...@@ -454,6 +461,7 @@ __inline__ struct sock *tcp_v4_lookup_listener(u32 daddr, unsigned short hnum,
if (inet->num == hnum && !sk->next && if (inet->num == hnum && !sk->next &&
(!inet->rcv_saddr || inet->rcv_saddr == daddr) && (!inet->rcv_saddr || inet->rcv_saddr == daddr) &&
(sk->family == PF_INET || !ipv6_only_sock(sk)) &&
!sk->bound_dev_if) !sk->bound_dev_if)
goto sherry_cache; goto sherry_cache;
sk = __tcp_v4_lookup_listener(sk, daddr, hnum, dif); sk = __tcp_v4_lookup_listener(sk, daddr, hnum, dif);
......
...@@ -66,6 +66,9 @@ ...@@ -66,6 +66,9 @@
* datagrams. * datagrams.
* Hirokazu Takahashi : sendfile() on UDP works now. * Hirokazu Takahashi : sendfile() on UDP works now.
* Arnaldo C. Melo : convert /proc/net/udp to seq_file * 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 * This program is free software; you can redistribute it and/or
...@@ -87,6 +90,7 @@ ...@@ -87,6 +90,7 @@
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/config.h> #include <linux/config.h>
#include <linux/inet.h> #include <linux/inet.h>
#include <linux/ipv6.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <net/snmp.h> #include <net/snmp.h>
#include <net/tcp.h> #include <net/tcp.h>
...@@ -170,6 +174,7 @@ static int udp_v4_get_port(struct sock *sk, unsigned short snum) ...@@ -170,6 +174,7 @@ static int udp_v4_get_port(struct sock *sk, unsigned short snum)
if (inet2->num == snum && if (inet2->num == snum &&
sk2 != sk && sk2 != sk &&
!ipv6_only_sock(sk2) &&
sk2->bound_dev_if == sk->bound_dev_if && sk2->bound_dev_if == sk->bound_dev_if &&
(!inet2->rcv_saddr || (!inet2->rcv_saddr ||
!inet->rcv_saddr || !inet->rcv_saddr ||
...@@ -228,29 +233,29 @@ struct sock *udp_v4_lookup_longway(u32 saddr, u16 sport, u32 daddr, u16 dport, i ...@@ -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) { for(sk = udp_hash[hnum & (UDP_HTABLE_SIZE - 1)]; sk != NULL; sk = sk->next) {
struct inet_opt *inet = inet_sk(sk); struct inet_opt *inet = inet_sk(sk);
if (inet->num == hnum) { if (inet->num == hnum && !ipv6_only_sock(sk)) {
int score = 0; int score = (sk->family == PF_INET ? 1 : 0);
if (inet->rcv_saddr) { if (inet->rcv_saddr) {
if (inet->rcv_saddr != daddr) if (inet->rcv_saddr != daddr)
continue; continue;
score++; score+=2;
} }
if (inet->daddr) { if (inet->daddr) {
if (inet->daddr != saddr) if (inet->daddr != saddr)
continue; continue;
score++; score+=2;
} }
if (inet->dport) { if (inet->dport) {
if (inet->dport != sport) if (inet->dport != sport)
continue; continue;
score++; score+=2;
} }
if(sk->bound_dev_if) { if(sk->bound_dev_if) {
if(sk->bound_dev_if != dif) if(sk->bound_dev_if != dif)
continue; continue;
score++; score+=2;
} }
if(score == 4) { if(score == 9) {
result = sk; result = sk;
break; break;
} else if(score > badness) { } else if(score > badness) {
...@@ -288,6 +293,7 @@ static inline struct sock *udp_v4_mcast_next(struct sock *sk, ...@@ -288,6 +293,7 @@ static inline struct sock *udp_v4_mcast_next(struct sock *sk,
(inet->daddr && inet->daddr != rmt_addr) || (inet->daddr && inet->daddr != rmt_addr) ||
(inet->dport != rmt_port && inet->dport) || (inet->dport != rmt_port && inet->dport) ||
(inet->rcv_saddr && inet->rcv_saddr != loc_addr) || (inet->rcv_saddr && inet->rcv_saddr != loc_addr) ||
ipv6_only_sock(s) ||
(s->bound_dev_if && s->bound_dev_if != dif)) (s->bound_dev_if && s->bound_dev_if != dif))
continue; continue;
break; break;
......
...@@ -88,6 +88,8 @@ extern void ipv6_sysctl_register(void); ...@@ -88,6 +88,8 @@ extern void ipv6_sysctl_register(void);
extern void ipv6_sysctl_unregister(void); extern void ipv6_sysctl_unregister(void);
#endif #endif
int sysctl_ipv6_bindv6only;
#ifdef INET_REFCNT_DEBUG #ifdef INET_REFCNT_DEBUG
atomic_t inet6_sock_nr; atomic_t inet6_sock_nr;
#endif #endif
...@@ -218,7 +220,8 @@ static int inet6_create(struct socket *sock, int protocol) ...@@ -218,7 +220,8 @@ static int inet6_create(struct socket *sock, int protocol)
np->mcast_hops = -1; np->mcast_hops = -1;
np->mc_loop = 1; np->mc_loop = 1;
np->pmtudisc = IPV6_PMTUDISC_WANT; np->pmtudisc = IPV6_PMTUDISC_WANT;
np->ipv6only = sysctl_ipv6_bindv6only;
/* Init the ipv4 part of the socket since we can have sockets /* Init the ipv4 part of the socket since we can have sockets
* using v6 API for ipv4. * using v6 API for ipv4.
*/ */
......
...@@ -157,7 +157,8 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname, char *optval, ...@@ -157,7 +157,8 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname, char *optval,
break; 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; retv = -EADDRNOTAVAIL;
break; break;
} }
...@@ -203,6 +204,13 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname, char *optval, ...@@ -203,6 +204,13 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname, char *optval,
} }
goto e_inval; goto e_inval;
case IPV6_V6ONLY:
if (inet_sk(sk)->num)
goto e_inval;
np->ipv6only = valbool;
retv = 0;
break;
case IPV6_PKTINFO: case IPV6_PKTINFO:
np->rxopt.bits.rxinfo = valbool; np->rxopt.bits.rxinfo = valbool;
retv = 0; retv = 0;
...@@ -475,6 +483,10 @@ int ipv6_getsockopt(struct sock *sk, int level, int optname, char *optval, ...@@ -475,6 +483,10 @@ int ipv6_getsockopt(struct sock *sk, int level, int optname, char *optval,
break; break;
} }
case IPV6_V6ONLY:
val = np->ipv6only;
break;
case IPV6_PKTINFO: case IPV6_PKTINFO:
val = np->rxopt.bits.rxinfo; val = np->rxopt.bits.rxinfo;
break; break;
......
...@@ -17,6 +17,8 @@ extern ctl_table ipv6_route_table[]; ...@@ -17,6 +17,8 @@ extern ctl_table ipv6_route_table[];
ctl_table ipv6_table[] = { ctl_table ipv6_table[] = {
{NET_IPV6_ROUTE, "route", NULL, 0, 0555, ipv6_route_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} {0}
}; };
......
...@@ -14,6 +14,9 @@ ...@@ -14,6 +14,9 @@
* *
* Fixes: * Fixes:
* Hideaki YOSHIFUJI : sin6_scope_id support * 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * 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) ...@@ -90,14 +93,35 @@ static inline int ipv6_rcv_saddr_equal(struct sock *sk, struct sock *sk2)
struct ipv6_pinfo *np = inet6_sk(sk); struct ipv6_pinfo *np = inet6_sk(sk);
int addr_type = ipv6_addr_type(&np->rcv_saddr); int addr_type = ipv6_addr_type(&np->rcv_saddr);
return !inet_sk(sk2)->rcv_saddr || addr_type == IPV6_ADDR_ANY || if (!inet_sk(sk2)->rcv_saddr && !ipv6_only_sock(sk))
(sk2->family == AF_INET6 && return 1;
!ipv6_addr_cmp(&np->rcv_saddr,
sk2->state != TCP_TIME_WAIT ? if (sk2->family == AF_INET6 &&
&inet6_sk(sk2)->rcv_saddr : ipv6_addr_any(&inet6_sk(sk2)->rcv_saddr) &&
&((struct tcp_tw_bucket *)sk)->v6_rcv_saddr)) || !(ipv6_only_sock(sk2) && addr_type == IPV6_ADDR_MAPPED))
(addr_type == IPV6_ADDR_MAPPED && sk2->family == AF_INET && return 1;
inet_sk(sk)->rcv_saddr == inet_sk(sk2)->rcv_saddr);
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, 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, ...@@ -612,6 +636,9 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
SOCK_DEBUG(sk, "connect: ipv4 mapped\n"); SOCK_DEBUG(sk, "connect: ipv4 mapped\n");
if (__ipv6_only_sock(sk))
return -ENETUNREACH;
sin.sin_family = AF_INET; sin.sin_family = AF_INET;
sin.sin_port = usin->sin6_port; sin.sin_port = usin->sin6_port;
sin.sin_addr.s_addr = usin->sin6_addr.s6_addr32[3]; sin.sin_addr.s_addr = usin->sin6_addr.s6_addr32[3];
......
...@@ -11,6 +11,9 @@ ...@@ -11,6 +11,9 @@
* *
* Fixes: * Fixes:
* Hideaki YOSHIFUJI : sin6_scope_id support * 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
...@@ -50,16 +53,41 @@ ...@@ -50,16 +53,41 @@
struct udp_mib udp_stats_in6[NR_CPUS*2]; 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) static __inline__ int udv6_rcv_saddr_equal(struct sock *sk, struct sock *sk2)
{ {
struct ipv6_pinfo *np = inet6_sk(sk); struct ipv6_pinfo *np = inet6_sk(sk);
int addr_type = ipv6_addr_type(&np->rcv_saddr); int addr_type = ipv6_addr_type(&np->rcv_saddr);
return !inet_sk(sk2)->rcv_saddr || addr_type == IPV6_ADDR_ANY || if (!inet_sk(sk2)->rcv_saddr && !ipv6_only_sock(sk))
(sk2->family == AF_INET6 && return 1;
!ipv6_addr_cmp(&np->rcv_saddr, &inet6_sk(sk2)->rcv_saddr)) ||
(addr_type == IPV6_ADDR_MAPPED && sk2->family == AF_INET && if (sk2->family == AF_INET6 &&
inet_sk(sk)->rcv_saddr == inet_sk(sk2)->rcv_saddr); 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 /* 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) ...@@ -228,6 +256,8 @@ int udpv6_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
int err; int err;
if (usin->sin6_family == AF_INET) { if (usin->sin6_family == AF_INET) {
if (__ipv6_only_sock(sk))
return -EAFNOSUPPORT;
err = udp_connect(sk, uaddr, addr_len); err = udp_connect(sk, uaddr, addr_len);
goto ipv4_connected; goto ipv4_connected;
} }
...@@ -263,6 +293,9 @@ int udpv6_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) ...@@ -263,6 +293,9 @@ int udpv6_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
if (addr_type == IPV6_ADDR_MAPPED) { if (addr_type == IPV6_ADDR_MAPPED) {
struct sockaddr_in sin; struct sockaddr_in sin;
if (__ipv6_only_sock(sk))
return -ENETUNREACH;
sin.sin_family = AF_INET; sin.sin_family = AF_INET;
sin.sin_addr.s_addr = daddr->s6_addr32[3]; sin.sin_addr.s_addr = daddr->s6_addr32[3];
sin.sin_port = usin->sin6_port; sin.sin_port = usin->sin6_port;
...@@ -793,8 +826,11 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg ...@@ -793,8 +826,11 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg
fl.oif = 0; fl.oif = 0;
if (sin6) { 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); return udp_sendmsg(iocb, sk, msg, ulen);
}
if (addr_len < SIN6_LEN_RFC2133) if (addr_len < SIN6_LEN_RFC2133)
return -EINVAL; return -EINVAL;
...@@ -841,6 +877,9 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg ...@@ -841,6 +877,9 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg
if (addr_type == IPV6_ADDR_MAPPED) { if (addr_type == IPV6_ADDR_MAPPED) {
struct sockaddr_in sin; struct sockaddr_in sin;
if (__ipv6_only_sock(sk))
return -ENETUNREACH;
sin.sin_family = AF_INET; sin.sin_family = AF_INET;
sin.sin_addr.s_addr = daddr->s6_addr32[3]; sin.sin_addr.s_addr = daddr->s6_addr32[3];
sin.sin_port = udh.uh.dest; 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