Commit 38566ec0 authored by Martin KaFai Lau's avatar Martin KaFai Lau Committed by Alexei Starovoitov

bpf: Change bpf_getsockopt(SOL_IPV6) to reuse do_ipv6_getsockopt()

This patch changes bpf_getsockopt(SOL_IPV6) to reuse
do_ipv6_getsockopt().  It removes the duplicated code from
bpf_getsockopt(SOL_IPV6).

This also makes bpf_getsockopt(SOL_IPV6) supporting the same
set of optnames as in bpf_setsockopt(SOL_IPV6).  In particular,
this adds IPV6_AUTOFLOWLABEL support to bpf_getsockopt(SOL_IPV6).

ipv6 could be compiled as a module.  Like how other code solved it
with stubs in ipv6_stubs.h, this patch adds the do_ipv6_getsockopt
to the ipv6_bpf_stub.
Signed-off-by: default avatarMartin KaFai Lau <martin.lau@kernel.org>
Link: https://lore.kernel.org/r/20220902002931.2896218-1-kafai@fb.comSigned-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parent fd969f25
...@@ -1160,6 +1160,8 @@ int do_ipv6_setsockopt(struct sock *sk, int level, int optname, sockptr_t optval ...@@ -1160,6 +1160,8 @@ int do_ipv6_setsockopt(struct sock *sk, int level, int optname, sockptr_t optval
unsigned int optlen); unsigned int optlen);
int ipv6_setsockopt(struct sock *sk, int level, int optname, sockptr_t optval, int ipv6_setsockopt(struct sock *sk, int level, int optname, sockptr_t optval,
unsigned int optlen); unsigned int optlen);
int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
sockptr_t optval, sockptr_t optlen);
int ipv6_getsockopt(struct sock *sk, int level, int optname, int ipv6_getsockopt(struct sock *sk, int level, int optname,
char __user *optval, int __user *optlen); char __user *optval, int __user *optlen);
......
...@@ -83,6 +83,8 @@ struct ipv6_bpf_stub { ...@@ -83,6 +83,8 @@ struct ipv6_bpf_stub {
struct sk_buff *skb); struct sk_buff *skb);
int (*ipv6_setsockopt)(struct sock *sk, int level, int optname, int (*ipv6_setsockopt)(struct sock *sk, int level, int optname,
sockptr_t optval, unsigned int optlen); sockptr_t optval, unsigned int optlen);
int (*ipv6_getsockopt)(struct sock *sk, int level, int optname,
sockptr_t optval, sockptr_t optlen);
}; };
extern const struct ipv6_bpf_stub *ipv6_bpf_stub __read_mostly; extern const struct ipv6_bpf_stub *ipv6_bpf_stub __read_mostly;
......
...@@ -5191,8 +5191,9 @@ static int sol_ip_sockopt(struct sock *sk, int optname, ...@@ -5191,8 +5191,9 @@ static int sol_ip_sockopt(struct sock *sk, int optname,
KERNEL_SOCKPTR(optval), *optlen); KERNEL_SOCKPTR(optval), *optlen);
} }
static int sol_ipv6_setsockopt(struct sock *sk, int optname, static int sol_ipv6_sockopt(struct sock *sk, int optname,
char *optval, int optlen) char *optval, int *optlen,
bool getopt)
{ {
if (sk->sk_family != AF_INET6) if (sk->sk_family != AF_INET6)
return -EINVAL; return -EINVAL;
...@@ -5200,15 +5201,20 @@ static int sol_ipv6_setsockopt(struct sock *sk, int optname, ...@@ -5200,15 +5201,20 @@ static int sol_ipv6_setsockopt(struct sock *sk, int optname,
switch (optname) { switch (optname) {
case IPV6_TCLASS: case IPV6_TCLASS:
case IPV6_AUTOFLOWLABEL: case IPV6_AUTOFLOWLABEL:
if (optlen != sizeof(int)) if (*optlen != sizeof(int))
return -EINVAL; return -EINVAL;
break; break;
default: default:
return -EINVAL; return -EINVAL;
} }
if (getopt)
return ipv6_bpf_stub->ipv6_getsockopt(sk, SOL_IPV6, optname,
KERNEL_SOCKPTR(optval),
KERNEL_SOCKPTR(optlen));
return ipv6_bpf_stub->ipv6_setsockopt(sk, SOL_IPV6, optname, return ipv6_bpf_stub->ipv6_setsockopt(sk, SOL_IPV6, optname,
KERNEL_SOCKPTR(optval), optlen); KERNEL_SOCKPTR(optval), *optlen);
} }
static int __bpf_setsockopt(struct sock *sk, int level, int optname, static int __bpf_setsockopt(struct sock *sk, int level, int optname,
...@@ -5222,7 +5228,7 @@ static int __bpf_setsockopt(struct sock *sk, int level, int optname, ...@@ -5222,7 +5228,7 @@ static int __bpf_setsockopt(struct sock *sk, int level, int optname,
else if (IS_ENABLED(CONFIG_INET) && level == SOL_IP) else if (IS_ENABLED(CONFIG_INET) && level == SOL_IP)
return sol_ip_sockopt(sk, optname, optval, &optlen, false); return sol_ip_sockopt(sk, optname, optval, &optlen, false);
else if (IS_ENABLED(CONFIG_IPV6) && level == SOL_IPV6) else if (IS_ENABLED(CONFIG_IPV6) && level == SOL_IPV6)
return sol_ipv6_setsockopt(sk, optname, optval, optlen); return sol_ipv6_sockopt(sk, optname, optval, &optlen, false);
else if (IS_ENABLED(CONFIG_INET) && level == SOL_TCP) else if (IS_ENABLED(CONFIG_INET) && level == SOL_TCP)
return sol_tcp_sockopt(sk, optname, optval, &optlen, false); return sol_tcp_sockopt(sk, optname, optval, &optlen, false);
...@@ -5240,43 +5246,30 @@ static int _bpf_setsockopt(struct sock *sk, int level, int optname, ...@@ -5240,43 +5246,30 @@ static int _bpf_setsockopt(struct sock *sk, int level, int optname,
static int __bpf_getsockopt(struct sock *sk, int level, int optname, static int __bpf_getsockopt(struct sock *sk, int level, int optname,
char *optval, int optlen) char *optval, int optlen)
{ {
int err = 0, saved_optlen = optlen; int err, saved_optlen = optlen;
if (!sk_fullsock(sk)) if (!sk_fullsock(sk)) {
goto err_clear; err = -EINVAL;
goto done;
}
if (level == SOL_SOCKET) { if (level == SOL_SOCKET)
err = sol_socket_sockopt(sk, optname, optval, &optlen, true); err = sol_socket_sockopt(sk, optname, optval, &optlen, true);
} else if (IS_ENABLED(CONFIG_INET) && level == SOL_TCP) { else if (IS_ENABLED(CONFIG_INET) && level == SOL_TCP)
err = sol_tcp_sockopt(sk, optname, optval, &optlen, true); err = sol_tcp_sockopt(sk, optname, optval, &optlen, true);
} else if (IS_ENABLED(CONFIG_INET) && level == SOL_IP) { else if (IS_ENABLED(CONFIG_INET) && level == SOL_IP)
err = sol_ip_sockopt(sk, optname, optval, &optlen, true); err = sol_ip_sockopt(sk, optname, optval, &optlen, true);
} else if (IS_ENABLED(CONFIG_IPV6) && level == SOL_IPV6) { else if (IS_ENABLED(CONFIG_IPV6) && level == SOL_IPV6)
struct ipv6_pinfo *np = inet6_sk(sk); err = sol_ipv6_sockopt(sk, optname, optval, &optlen, true);
else
if (optlen != sizeof(int) || sk->sk_family != AF_INET6) err = -EINVAL;
goto err_clear;
/* Only some options are supported */
switch (optname) {
case IPV6_TCLASS:
*((int *)optval) = (int)np->tclass;
break;
default:
goto err_clear;
}
} else {
goto err_clear;
}
done:
if (err) if (err)
optlen = 0; optlen = 0;
if (optlen < saved_optlen) if (optlen < saved_optlen)
memset(optval + optlen, 0, saved_optlen - optlen); memset(optval + optlen, 0, saved_optlen - optlen);
return err; return err;
err_clear:
memset(optval, 0, optlen);
return -EINVAL;
} }
static int _bpf_getsockopt(struct sock *sk, int level, int optname, static int _bpf_getsockopt(struct sock *sk, int level, int optname,
......
...@@ -1058,6 +1058,7 @@ static const struct ipv6_bpf_stub ipv6_bpf_stub_impl = { ...@@ -1058,6 +1058,7 @@ static const struct ipv6_bpf_stub ipv6_bpf_stub_impl = {
.inet6_bind = __inet6_bind, .inet6_bind = __inet6_bind,
.udp6_lib_lookup = __udp6_lib_lookup, .udp6_lib_lookup = __udp6_lib_lookup,
.ipv6_setsockopt = do_ipv6_setsockopt, .ipv6_setsockopt = do_ipv6_setsockopt,
.ipv6_getsockopt = do_ipv6_getsockopt,
}; };
static int __init inet6_init(void) static int __init inet6_init(void)
......
...@@ -1131,8 +1131,8 @@ static int compat_ipv6_get_msfilter(struct sock *sk, sockptr_t optval, ...@@ -1131,8 +1131,8 @@ static int compat_ipv6_get_msfilter(struct sock *sk, sockptr_t optval,
return 0; return 0;
} }
static int do_ipv6_getsockopt(struct sock *sk, int level, int optname, int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
sockptr_t optval, sockptr_t optlen) sockptr_t optval, sockptr_t optlen)
{ {
struct ipv6_pinfo *np = inet6_sk(sk); struct ipv6_pinfo *np = inet6_sk(sk);
int len; int len;
......
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