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

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.com


Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parent fd969f25
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -1160,6 +1160,8 @@ int do_ipv6_setsockopt(struct sock *sk, int level, int optname, sockptr_t optval
		       unsigned int optlen);
int ipv6_setsockopt(struct sock *sk, int level, int optname, sockptr_t optval,
		    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,
		    char __user *optval, int __user *optlen);

+2 −0
Original line number Diff line number Diff line
@@ -83,6 +83,8 @@ struct ipv6_bpf_stub {
				     struct sk_buff *skb);
	int (*ipv6_setsockopt)(struct sock *sk, int level, int optname,
			       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;

+24 −31
Original line number Diff line number Diff line
@@ -5191,8 +5191,9 @@ static int sol_ip_sockopt(struct sock *sk, int optname,
				KERNEL_SOCKPTR(optval), *optlen);
}

static int sol_ipv6_setsockopt(struct sock *sk, int optname,
			       char *optval, int optlen)
static int sol_ipv6_sockopt(struct sock *sk, int optname,
			    char *optval, int *optlen,
			    bool getopt)
{
	if (sk->sk_family != AF_INET6)
		return -EINVAL;
@@ -5200,15 +5201,20 @@ static int sol_ipv6_setsockopt(struct sock *sk, int optname,
	switch (optname) {
	case IPV6_TCLASS:
	case IPV6_AUTOFLOWLABEL:
		if (optlen != sizeof(int))
		if (*optlen != sizeof(int))
			return -EINVAL;
		break;
	default:
		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,
					      KERNEL_SOCKPTR(optval), optlen);
					      KERNEL_SOCKPTR(optval), *optlen);
}

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)
		return sol_ip_sockopt(sk, optname, optval, &optlen, false);
	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)
		return sol_tcp_sockopt(sk, optname, optval, &optlen, false);

@@ -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,
			    char *optval, int optlen)
{
	int err = 0, saved_optlen = optlen;
	int err, saved_optlen = optlen;

	if (!sk_fullsock(sk))
		goto err_clear;
	if (!sk_fullsock(sk)) {
		err = -EINVAL;
		goto done;
	}

	if (level == SOL_SOCKET) {
	if (level == SOL_SOCKET)
		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);
	} 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);
	} else if (IS_ENABLED(CONFIG_IPV6) && level == SOL_IPV6) {
		struct ipv6_pinfo *np = inet6_sk(sk);

		if (optlen != sizeof(int) || sk->sk_family != AF_INET6)
			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;
	}
	else if (IS_ENABLED(CONFIG_IPV6) && level == SOL_IPV6)
		err = sol_ipv6_sockopt(sk, optname, optval, &optlen, true);
	else
		err = -EINVAL;

done:
	if (err)
		optlen = 0;
	if (optlen < saved_optlen)
		memset(optval + optlen, 0, saved_optlen - optlen);
	return err;
err_clear:
	memset(optval, 0, optlen);
	return -EINVAL;
}

static int _bpf_getsockopt(struct sock *sk, int level, int optname,
+1 −0
Original line number Diff line number Diff line
@@ -1058,6 +1058,7 @@ static const struct ipv6_bpf_stub ipv6_bpf_stub_impl = {
	.inet6_bind = __inet6_bind,
	.udp6_lib_lookup = __udp6_lib_lookup,
	.ipv6_setsockopt = do_ipv6_setsockopt,
	.ipv6_getsockopt = do_ipv6_getsockopt,
};

static int __init inet6_init(void)
+2 −2
Original line number Diff line number Diff line
@@ -1131,7 +1131,7 @@ static int compat_ipv6_get_msfilter(struct sock *sk, sockptr_t optval,
	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)
{
	struct ipv6_pinfo *np = inet6_sk(sk);