ipv6: add ip6_sock_set_addr_preferences

Add a helper to directly set the IPV6_ADD_PREFERENCES sockopt from kernel
space without going through a fake uaccess.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Christoph Hellwig 2020-05-28 07:12:33 +02:00 committed by David S. Miller
parent fce934949c
commit 18d5ad6232
3 changed files with 72 additions and 61 deletions

View File

@ -1195,4 +1195,71 @@ static inline void ip6_sock_set_recverr(struct sock *sk)
release_sock(sk);
}
static inline int __ip6_sock_set_addr_preferences(struct sock *sk, int val)
{
unsigned int pref = 0;
unsigned int prefmask = ~0;
/* check PUBLIC/TMP/PUBTMP_DEFAULT conflicts */
switch (val & (IPV6_PREFER_SRC_PUBLIC |
IPV6_PREFER_SRC_TMP |
IPV6_PREFER_SRC_PUBTMP_DEFAULT)) {
case IPV6_PREFER_SRC_PUBLIC:
pref |= IPV6_PREFER_SRC_PUBLIC;
prefmask &= ~(IPV6_PREFER_SRC_PUBLIC |
IPV6_PREFER_SRC_TMP);
break;
case IPV6_PREFER_SRC_TMP:
pref |= IPV6_PREFER_SRC_TMP;
prefmask &= ~(IPV6_PREFER_SRC_PUBLIC |
IPV6_PREFER_SRC_TMP);
break;
case IPV6_PREFER_SRC_PUBTMP_DEFAULT:
prefmask &= ~(IPV6_PREFER_SRC_PUBLIC |
IPV6_PREFER_SRC_TMP);
break;
case 0:
break;
default:
return -EINVAL;
}
/* check HOME/COA conflicts */
switch (val & (IPV6_PREFER_SRC_HOME | IPV6_PREFER_SRC_COA)) {
case IPV6_PREFER_SRC_HOME:
prefmask &= ~IPV6_PREFER_SRC_COA;
break;
case IPV6_PREFER_SRC_COA:
pref |= IPV6_PREFER_SRC_COA;
break;
case 0:
break;
default:
return -EINVAL;
}
/* check CGA/NONCGA conflicts */
switch (val & (IPV6_PREFER_SRC_CGA|IPV6_PREFER_SRC_NONCGA)) {
case IPV6_PREFER_SRC_CGA:
case IPV6_PREFER_SRC_NONCGA:
case 0:
break;
default:
return -EINVAL;
}
inet6_sk(sk)->srcprefs = (inet6_sk(sk)->srcprefs & prefmask) | pref;
return 0;
}
static inline int ip6_sock_set_addr_preferences(struct sock *sk, bool val)
{
int ret;
lock_sock(sk);
ret = __ip6_sock_set_addr_preferences(sk, val);
release_sock(sk);
return ret;
}
#endif /* _NET_IPV6_H */

View File

@ -845,67 +845,10 @@ done:
break;
case IPV6_ADDR_PREFERENCES:
{
unsigned int pref = 0;
unsigned int prefmask = ~0;
if (optlen < sizeof(int))
goto e_inval;
retv = -EINVAL;
/* check PUBLIC/TMP/PUBTMP_DEFAULT conflicts */
switch (val & (IPV6_PREFER_SRC_PUBLIC|
IPV6_PREFER_SRC_TMP|
IPV6_PREFER_SRC_PUBTMP_DEFAULT)) {
case IPV6_PREFER_SRC_PUBLIC:
pref |= IPV6_PREFER_SRC_PUBLIC;
break;
case IPV6_PREFER_SRC_TMP:
pref |= IPV6_PREFER_SRC_TMP;
break;
case IPV6_PREFER_SRC_PUBTMP_DEFAULT:
break;
case 0:
goto pref_skip_pubtmp;
default:
goto e_inval;
}
prefmask &= ~(IPV6_PREFER_SRC_PUBLIC|
IPV6_PREFER_SRC_TMP);
pref_skip_pubtmp:
/* check HOME/COA conflicts */
switch (val & (IPV6_PREFER_SRC_HOME|IPV6_PREFER_SRC_COA)) {
case IPV6_PREFER_SRC_HOME:
break;
case IPV6_PREFER_SRC_COA:
pref |= IPV6_PREFER_SRC_COA;
case 0:
goto pref_skip_coa;
default:
goto e_inval;
}
prefmask &= ~IPV6_PREFER_SRC_COA;
pref_skip_coa:
/* check CGA/NONCGA conflicts */
switch (val & (IPV6_PREFER_SRC_CGA|IPV6_PREFER_SRC_NONCGA)) {
case IPV6_PREFER_SRC_CGA:
case IPV6_PREFER_SRC_NONCGA:
case 0:
break;
default:
goto e_inval;
}
np->srcprefs = (np->srcprefs & prefmask) | pref;
retv = 0;
retv = __ip6_sock_set_addr_preferences(sk, val);
break;
}
case IPV6_MINHOPCOUNT:
if (optlen < sizeof(int))
goto e_inval;

View File

@ -2150,7 +2150,6 @@ static int xs_tcp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock)
if (!transport->inet) {
struct sock *sk = sock->sk;
unsigned int addr_pref = IPV6_PREFER_SRC_PUBLIC;
/* Avoid temporary address, they are bad for long-lived
* connections such as NFS mounts.
@ -2159,8 +2158,10 @@ static int xs_tcp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock)
* knowledge about the normal duration of connections,
* MAY override this as appropriate.
*/
kernel_setsockopt(sock, SOL_IPV6, IPV6_ADDR_PREFERENCES,
(char *)&addr_pref, sizeof(addr_pref));
if (xs_addr(xprt)->sa_family == PF_INET6) {
ip6_sock_set_addr_preferences(sk,
IPV6_PREFER_SRC_PUBLIC);
}
xs_tcp_set_socket_timeouts(xprt, sock);