bpf: Change bpf_setsockopt(SOL_IPV6) to reuse do_ipv6_setsockopt()
authorMartin KaFai Lau <kafai@fb.com>
Wed, 17 Aug 2022 06:18:34 +0000 (23:18 -0700)
committerAlexei Starovoitov <ast@kernel.org>
Fri, 19 Aug 2022 00:06:13 +0000 (17:06 -0700)
After the prep work in the previous patches,
this patch removes the dup code from bpf_setsockopt(SOL_IPV6)
and reuses the implementation in do_ipv6_setsockopt().

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_setsockopt
to the ipv6_bpf_stub.

The current bpf_setsockopt(IPV6_TCLASS) does not take the
INET_ECN_MASK into the account for tcp.  The
do_ipv6_setsockopt(IPV6_TCLASS) will handle it correctly.

The existing optname white-list is refactored into a new
function sol_ipv6_setsockopt().

After this last SOL_IPV6 dup code removal, the __bpf_setsockopt()
is simplified enough that the extra "{ }" around the if statement
can be removed.

Reviewed-by: Stanislav Fomichev <sdf@google.com>
Signed-off-by: Martin KaFai Lau <kafai@fb.com>
Link: https://lore.kernel.org/r/20220817061834.4181198-1-kafai@fb.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
include/net/ipv6.h
include/net/ipv6_stubs.h
net/core/filter.c
net/ipv6/af_inet6.c
net/ipv6/ipv6_sockglue.c

index de9dcc5652c483c0a656b65504a356be9294f7fe..c110d90320836c2e514477887c5150e89e0faa3b 100644 (file)
@@ -1156,6 +1156,8 @@ struct in6_addr *fl6_update_dst(struct flowi6 *fl6,
  */
 DECLARE_STATIC_KEY_FALSE(ip6_min_hopcount);
 
+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 ipv6_getsockopt(struct sock *sk, int level, int optname,
index 45e0339be6fa4aa8e6c3ca074e00aff1c3b97b2f..8692698b01cfb9ec17296937569fb41248935ab4 100644 (file)
@@ -81,6 +81,8 @@ struct ipv6_bpf_stub {
                                     const struct in6_addr *daddr, __be16 dport,
                                     int dif, int sdif, struct udp_table *tbl,
                                     struct sk_buff *skb);
+       int (*ipv6_setsockopt)(struct sock *sk, int level, int optname,
+                              sockptr_t optval, unsigned int optlen);
 };
 extern const struct ipv6_bpf_stub *ipv6_bpf_stub __read_mostly;
 
index 4d1b42b8f4a894a4c9afb79a31a0b0d954f64222..23282b8cf61e51a188d9cc18f3c1ad8cf4805399 100644 (file)
@@ -5133,45 +5133,41 @@ static int sol_ip_setsockopt(struct sock *sk, int optname,
                                KERNEL_SOCKPTR(optval), optlen);
 }
 
+static int sol_ipv6_setsockopt(struct sock *sk, int optname,
+                              char *optval, int optlen)
+{
+       if (sk->sk_family != AF_INET6)
+               return -EINVAL;
+
+       switch (optname) {
+       case IPV6_TCLASS:
+               if (optlen != sizeof(int))
+                       return -EINVAL;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return ipv6_bpf_stub->ipv6_setsockopt(sk, SOL_IPV6, optname,
+                                             KERNEL_SOCKPTR(optval), optlen);
+}
+
 static int __bpf_setsockopt(struct sock *sk, int level, int optname,
                            char *optval, int optlen)
 {
-       int val, ret = 0;
-
        if (!sk_fullsock(sk))
                return -EINVAL;
 
-       if (level == SOL_SOCKET) {
+       if (level == SOL_SOCKET)
                return sol_socket_setsockopt(sk, optname, optval, optlen);
-       } else if (IS_ENABLED(CONFIG_INET) && level == SOL_IP) {
+       else if (IS_ENABLED(CONFIG_INET) && level == SOL_IP)
                return sol_ip_setsockopt(sk, optname, optval, optlen);
-       } else if (IS_ENABLED(CONFIG_IPV6) && level == SOL_IPV6) {
-               if (optlen != sizeof(int) || sk->sk_family != AF_INET6)
-                       return -EINVAL;
-
-               val = *((int *)optval);
-               /* Only some options are supported */
-               switch (optname) {
-               case IPV6_TCLASS:
-                       if (val < -1 || val > 0xff) {
-                               ret = -EINVAL;
-                       } else {
-                               struct ipv6_pinfo *np = inet6_sk(sk);
-
-                               if (val == -1)
-                                       val = 0;
-                               np->tclass = val;
-                       }
-                       break;
-               default:
-                       ret = -EINVAL;
-               }
-       } else if (IS_ENABLED(CONFIG_INET) && level == SOL_TCP) {
+       else if (IS_ENABLED(CONFIG_IPV6) && level == SOL_IPV6)
+               return sol_ipv6_setsockopt(sk, optname, optval, optlen);
+       else if (IS_ENABLED(CONFIG_INET) && level == SOL_TCP)
                return sol_tcp_setsockopt(sk, optname, optval, optlen);
-       } else {
-               ret = -EINVAL;
-       }
-       return ret;
+
+       return -EINVAL;
 }
 
 static int _bpf_setsockopt(struct sock *sk, int level, int optname,
index 2ce0c44d0081440aaf0d7a0d4de2b14775f72917..cadc97852787d6744550eb5b03069ffab38b8102 100644 (file)
@@ -1057,6 +1057,7 @@ static const struct ipv6_stub ipv6_stub_impl = {
 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,
 };
 
 static int __init inet6_init(void)
index d23de48ff612b60065ba041c8cef5827badb174c..a4535bdbd310c677d48f4f7132000c08f708ee10 100644 (file)
@@ -391,8 +391,8 @@ sticky_done:
        return err;
 }
 
-static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
-                  sockptr_t optval, unsigned int optlen)
+int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
+                      sockptr_t optval, unsigned int optlen)
 {
        struct ipv6_pinfo *np = inet6_sk(sk);
        struct net *net = sock_net(sk);