LCOV - code coverage report
Current view: top level - kernel/bpf - reuseport_array.c (source / functions) Hit Total Coverage
Test: Real Lines: 0 102 0.0 %
Date: 2020-10-17 15:46:43 Functions: 0 10 0.0 %
Legend: Neither, QEMU, Real, Both Branches: 0 0 -

           Branch data     Line data    Source code
       1                 :            : // SPDX-License-Identifier: GPL-2.0
       2                 :            : /*
       3                 :            :  * Copyright (c) 2018 Facebook
       4                 :            :  */
       5                 :            : #include <linux/bpf.h>
       6                 :            : #include <linux/err.h>
       7                 :            : #include <linux/sock_diag.h>
       8                 :            : #include <net/sock_reuseport.h>
       9                 :            : 
      10                 :            : struct reuseport_array {
      11                 :            :         struct bpf_map map;
      12                 :            :         struct sock __rcu *ptrs[];
      13                 :            : };
      14                 :            : 
      15                 :            : static struct reuseport_array *reuseport_array(struct bpf_map *map)
      16                 :            : {
      17                 :            :         return (struct reuseport_array *)map;
      18                 :            : }
      19                 :            : 
      20                 :            : /* The caller must hold the reuseport_lock */
      21                 :          0 : void bpf_sk_reuseport_detach(struct sock *sk)
      22                 :            : {
      23                 :            :         struct sock __rcu **socks;
      24                 :            : 
      25                 :          0 :         write_lock_bh(&sk->sk_callback_lock);
      26                 :          0 :         socks = sk->sk_user_data;
      27                 :          0 :         if (socks) {
      28                 :            :                 WRITE_ONCE(sk->sk_user_data, NULL);
      29                 :            :                 /*
      30                 :            :                  * Do not move this NULL assignment outside of
      31                 :            :                  * sk->sk_callback_lock because there is
      32                 :            :                  * a race with reuseport_array_free()
      33                 :            :                  * which does not hold the reuseport_lock.
      34                 :            :                  */
      35                 :            :                 RCU_INIT_POINTER(*socks, NULL);
      36                 :            :         }
      37                 :          0 :         write_unlock_bh(&sk->sk_callback_lock);
      38                 :          0 : }
      39                 :            : 
      40                 :          0 : static int reuseport_array_alloc_check(union bpf_attr *attr)
      41                 :            : {
      42                 :          0 :         if (attr->value_size != sizeof(u32) &&
      43                 :            :             attr->value_size != sizeof(u64))
      44                 :            :                 return -EINVAL;
      45                 :            : 
      46                 :          0 :         return array_map_alloc_check(attr);
      47                 :            : }
      48                 :            : 
      49                 :          0 : static void *reuseport_array_lookup_elem(struct bpf_map *map, void *key)
      50                 :            : {
      51                 :            :         struct reuseport_array *array = reuseport_array(map);
      52                 :          0 :         u32 index = *(u32 *)key;
      53                 :            : 
      54                 :          0 :         if (unlikely(index >= array->map.max_entries))
      55                 :            :                 return NULL;
      56                 :            : 
      57                 :          0 :         return rcu_dereference(array->ptrs[index]);
      58                 :            : }
      59                 :            : 
      60                 :            : /* Called from syscall only */
      61                 :          0 : static int reuseport_array_delete_elem(struct bpf_map *map, void *key)
      62                 :            : {
      63                 :            :         struct reuseport_array *array = reuseport_array(map);
      64                 :          0 :         u32 index = *(u32 *)key;
      65                 :            :         struct sock *sk;
      66                 :            :         int err;
      67                 :            : 
      68                 :          0 :         if (index >= map->max_entries)
      69                 :            :                 return -E2BIG;
      70                 :            : 
      71                 :          0 :         if (!rcu_access_pointer(array->ptrs[index]))
      72                 :            :                 return -ENOENT;
      73                 :            : 
      74                 :            :         spin_lock_bh(&reuseport_lock);
      75                 :            : 
      76                 :          0 :         sk = rcu_dereference_protected(array->ptrs[index],
      77                 :            :                                        lockdep_is_held(&reuseport_lock));
      78                 :          0 :         if (sk) {
      79                 :          0 :                 write_lock_bh(&sk->sk_callback_lock);
      80                 :            :                 WRITE_ONCE(sk->sk_user_data, NULL);
      81                 :            :                 RCU_INIT_POINTER(array->ptrs[index], NULL);
      82                 :          0 :                 write_unlock_bh(&sk->sk_callback_lock);
      83                 :            :                 err = 0;
      84                 :            :         } else {
      85                 :            :                 err = -ENOENT;
      86                 :            :         }
      87                 :            : 
      88                 :            :         spin_unlock_bh(&reuseport_lock);
      89                 :            : 
      90                 :          0 :         return err;
      91                 :            : }
      92                 :            : 
      93                 :          0 : static void reuseport_array_free(struct bpf_map *map)
      94                 :            : {
      95                 :            :         struct reuseport_array *array = reuseport_array(map);
      96                 :            :         struct sock *sk;
      97                 :            :         u32 i;
      98                 :            : 
      99                 :          0 :         synchronize_rcu();
     100                 :            : 
     101                 :            :         /*
     102                 :            :          * ops->map_*_elem() will not be able to access this
     103                 :            :          * array now. Hence, this function only races with
     104                 :            :          * bpf_sk_reuseport_detach() which was triggerred by
     105                 :            :          * close() or disconnect().
     106                 :            :          *
     107                 :            :          * This function and bpf_sk_reuseport_detach() are
     108                 :            :          * both removing sk from "array".  Who removes it
     109                 :            :          * first does not matter.
     110                 :            :          *
     111                 :            :          * The only concern here is bpf_sk_reuseport_detach()
     112                 :            :          * may access "array" which is being freed here.
     113                 :            :          * bpf_sk_reuseport_detach() access this "array"
     114                 :            :          * through sk->sk_user_data _and_ with sk->sk_callback_lock
     115                 :            :          * held which is enough because this "array" is not freed
     116                 :            :          * until all sk->sk_user_data has stopped referencing this "array".
     117                 :            :          *
     118                 :            :          * Hence, due to the above, taking "reuseport_lock" is not
     119                 :            :          * needed here.
     120                 :            :          */
     121                 :            : 
     122                 :            :         /*
     123                 :            :          * Since reuseport_lock is not taken, sk is accessed under
     124                 :            :          * rcu_read_lock()
     125                 :            :          */
     126                 :            :         rcu_read_lock();
     127                 :          0 :         for (i = 0; i < map->max_entries; i++) {
     128                 :          0 :                 sk = rcu_dereference(array->ptrs[i]);
     129                 :          0 :                 if (sk) {
     130                 :          0 :                         write_lock_bh(&sk->sk_callback_lock);
     131                 :            :                         /*
     132                 :            :                          * No need for WRITE_ONCE(). At this point,
     133                 :            :                          * no one is reading it without taking the
     134                 :            :                          * sk->sk_callback_lock.
     135                 :            :                          */
     136                 :          0 :                         sk->sk_user_data = NULL;
     137                 :          0 :                         write_unlock_bh(&sk->sk_callback_lock);
     138                 :            :                         RCU_INIT_POINTER(array->ptrs[i], NULL);
     139                 :            :                 }
     140                 :            :         }
     141                 :            :         rcu_read_unlock();
     142                 :            : 
     143                 :            :         /*
     144                 :            :          * Once reaching here, all sk->sk_user_data is not
     145                 :            :          * referenceing this "array".  "array" can be freed now.
     146                 :            :          */
     147                 :          0 :         bpf_map_area_free(array);
     148                 :          0 : }
     149                 :            : 
     150                 :          0 : static struct bpf_map *reuseport_array_alloc(union bpf_attr *attr)
     151                 :            : {
     152                 :            :         int err, numa_node = bpf_map_attr_numa_node(attr);
     153                 :            :         struct reuseport_array *array;
     154                 :            :         struct bpf_map_memory mem;
     155                 :            :         u64 array_size;
     156                 :            : 
     157                 :          0 :         if (!capable(CAP_SYS_ADMIN))
     158                 :            :                 return ERR_PTR(-EPERM);
     159                 :            : 
     160                 :            :         array_size = sizeof(*array);
     161                 :          0 :         array_size += (u64)attr->max_entries * sizeof(struct sock *);
     162                 :            : 
     163                 :          0 :         err = bpf_map_charge_init(&mem, array_size);
     164                 :          0 :         if (err)
     165                 :          0 :                 return ERR_PTR(err);
     166                 :            : 
     167                 :            :         /* allocate all map elements and zero-initialize them */
     168                 :          0 :         array = bpf_map_area_alloc(array_size, numa_node);
     169                 :          0 :         if (!array) {
     170                 :          0 :                 bpf_map_charge_finish(&mem);
     171                 :          0 :                 return ERR_PTR(-ENOMEM);
     172                 :            :         }
     173                 :            : 
     174                 :            :         /* copy mandatory map attributes */
     175                 :          0 :         bpf_map_init_from_attr(&array->map, attr);
     176                 :          0 :         bpf_map_charge_move(&array->map.memory, &mem);
     177                 :            : 
     178                 :          0 :         return &array->map;
     179                 :            : }
     180                 :            : 
     181                 :          0 : int bpf_fd_reuseport_array_lookup_elem(struct bpf_map *map, void *key,
     182                 :            :                                        void *value)
     183                 :            : {
     184                 :            :         struct sock *sk;
     185                 :            :         int err;
     186                 :            : 
     187                 :          0 :         if (map->value_size != sizeof(u64))
     188                 :            :                 return -ENOSPC;
     189                 :            : 
     190                 :            :         rcu_read_lock();
     191                 :            :         sk = reuseport_array_lookup_elem(map, key);
     192                 :          0 :         if (sk) {
     193                 :          0 :                 *(u64 *)value = sock_gen_cookie(sk);
     194                 :            :                 err = 0;
     195                 :            :         } else {
     196                 :            :                 err = -ENOENT;
     197                 :            :         }
     198                 :            :         rcu_read_unlock();
     199                 :            : 
     200                 :          0 :         return err;
     201                 :            : }
     202                 :            : 
     203                 :            : static int
     204                 :          0 : reuseport_array_update_check(const struct reuseport_array *array,
     205                 :            :                              const struct sock *nsk,
     206                 :            :                              const struct sock *osk,
     207                 :            :                              const struct sock_reuseport *nsk_reuse,
     208                 :            :                              u32 map_flags)
     209                 :            : {
     210                 :          0 :         if (osk && map_flags == BPF_NOEXIST)
     211                 :            :                 return -EEXIST;
     212                 :            : 
     213                 :          0 :         if (!osk && map_flags == BPF_EXIST)
     214                 :            :                 return -ENOENT;
     215                 :            : 
     216                 :          0 :         if (nsk->sk_protocol != IPPROTO_UDP && nsk->sk_protocol != IPPROTO_TCP)
     217                 :            :                 return -ENOTSUPP;
     218                 :            : 
     219                 :          0 :         if (nsk->sk_family != AF_INET && nsk->sk_family != AF_INET6)
     220                 :            :                 return -ENOTSUPP;
     221                 :            : 
     222                 :          0 :         if (nsk->sk_type != SOCK_STREAM && nsk->sk_type != SOCK_DGRAM)
     223                 :            :                 return -ENOTSUPP;
     224                 :            : 
     225                 :            :         /*
     226                 :            :          * sk must be hashed (i.e. listening in the TCP case or binded
     227                 :            :          * in the UDP case) and
     228                 :            :          * it must also be a SO_REUSEPORT sk (i.e. reuse cannot be NULL).
     229                 :            :          *
     230                 :            :          * Also, sk will be used in bpf helper that is protected by
     231                 :            :          * rcu_read_lock().
     232                 :            :          */
     233                 :          0 :         if (!sock_flag(nsk, SOCK_RCU_FREE) || !sk_hashed(nsk) || !nsk_reuse)
     234                 :            :                 return -EINVAL;
     235                 :            : 
     236                 :            :         /* READ_ONCE because the sk->sk_callback_lock may not be held here */
     237                 :          0 :         if (READ_ONCE(nsk->sk_user_data))
     238                 :            :                 return -EBUSY;
     239                 :            : 
     240                 :          0 :         return 0;
     241                 :            : }
     242                 :            : 
     243                 :            : /*
     244                 :            :  * Called from syscall only.
     245                 :            :  * The "nsk" in the fd refcnt.
     246                 :            :  * The "osk" and "reuse" are protected by reuseport_lock.
     247                 :            :  */
     248                 :          0 : int bpf_fd_reuseport_array_update_elem(struct bpf_map *map, void *key,
     249                 :            :                                        void *value, u64 map_flags)
     250                 :            : {
     251                 :            :         struct reuseport_array *array = reuseport_array(map);
     252                 :            :         struct sock *free_osk = NULL, *osk, *nsk;
     253                 :            :         struct sock_reuseport *reuse;
     254                 :          0 :         u32 index = *(u32 *)key;
     255                 :            :         struct socket *socket;
     256                 :            :         int err, fd;
     257                 :            : 
     258                 :          0 :         if (map_flags > BPF_EXIST)
     259                 :            :                 return -EINVAL;
     260                 :            : 
     261                 :          0 :         if (index >= map->max_entries)
     262                 :            :                 return -E2BIG;
     263                 :            : 
     264                 :          0 :         if (map->value_size == sizeof(u64)) {
     265                 :          0 :                 u64 fd64 = *(u64 *)value;
     266                 :            : 
     267                 :          0 :                 if (fd64 > S32_MAX)
     268                 :            :                         return -EINVAL;
     269                 :          0 :                 fd = fd64;
     270                 :            :         } else {
     271                 :          0 :                 fd = *(int *)value;
     272                 :            :         }
     273                 :            : 
     274                 :          0 :         socket = sockfd_lookup(fd, &err);
     275                 :          0 :         if (!socket)
     276                 :          0 :                 return err;
     277                 :            : 
     278                 :          0 :         nsk = socket->sk;
     279                 :          0 :         if (!nsk) {
     280                 :          0 :                 err = -EINVAL;
     281                 :          0 :                 goto put_file;
     282                 :            :         }
     283                 :            : 
     284                 :            :         /* Quick checks before taking reuseport_lock */
     285                 :          0 :         err = reuseport_array_update_check(array, nsk,
     286                 :          0 :                                            rcu_access_pointer(array->ptrs[index]),
     287                 :          0 :                                            rcu_access_pointer(nsk->sk_reuseport_cb),
     288                 :            :                                            map_flags);
     289                 :          0 :         if (err)
     290                 :            :                 goto put_file;
     291                 :            : 
     292                 :            :         spin_lock_bh(&reuseport_lock);
     293                 :            :         /*
     294                 :            :          * Some of the checks only need reuseport_lock
     295                 :            :          * but it is done under sk_callback_lock also
     296                 :            :          * for simplicity reason.
     297                 :            :          */
     298                 :          0 :         write_lock_bh(&nsk->sk_callback_lock);
     299                 :            : 
     300                 :          0 :         osk = rcu_dereference_protected(array->ptrs[index],
     301                 :            :                                         lockdep_is_held(&reuseport_lock));
     302                 :          0 :         reuse = rcu_dereference_protected(nsk->sk_reuseport_cb,
     303                 :            :                                           lockdep_is_held(&reuseport_lock));
     304                 :          0 :         err = reuseport_array_update_check(array, nsk, osk, reuse, map_flags);
     305                 :          0 :         if (err)
     306                 :            :                 goto put_file_unlock;
     307                 :            : 
     308                 :            :         /* Ensure reuse->reuseport_id is set */
     309                 :          0 :         err = reuseport_get_id(reuse);
     310                 :          0 :         if (err < 0)
     311                 :            :                 goto put_file_unlock;
     312                 :            : 
     313                 :            :         WRITE_ONCE(nsk->sk_user_data, &array->ptrs[index]);
     314                 :          0 :         rcu_assign_pointer(array->ptrs[index], nsk);
     315                 :            :         free_osk = osk;
     316                 :          0 :         err = 0;
     317                 :            : 
     318                 :            : put_file_unlock:
     319                 :          0 :         write_unlock_bh(&nsk->sk_callback_lock);
     320                 :            : 
     321                 :          0 :         if (free_osk) {
     322                 :          0 :                 write_lock_bh(&free_osk->sk_callback_lock);
     323                 :            :                 WRITE_ONCE(free_osk->sk_user_data, NULL);
     324                 :          0 :                 write_unlock_bh(&free_osk->sk_callback_lock);
     325                 :            :         }
     326                 :            : 
     327                 :            :         spin_unlock_bh(&reuseport_lock);
     328                 :            : put_file:
     329                 :          0 :         fput(socket->file);
     330                 :          0 :         return err;
     331                 :            : }
     332                 :            : 
     333                 :            : /* Called from syscall */
     334                 :          0 : static int reuseport_array_get_next_key(struct bpf_map *map, void *key,
     335                 :            :                                         void *next_key)
     336                 :            : {
     337                 :            :         struct reuseport_array *array = reuseport_array(map);
     338                 :          0 :         u32 index = key ? *(u32 *)key : U32_MAX;
     339                 :            :         u32 *next = (u32 *)next_key;
     340                 :            : 
     341                 :          0 :         if (index >= array->map.max_entries) {
     342                 :          0 :                 *next = 0;
     343                 :          0 :                 return 0;
     344                 :            :         }
     345                 :            : 
     346                 :          0 :         if (index == array->map.max_entries - 1)
     347                 :            :                 return -ENOENT;
     348                 :            : 
     349                 :          0 :         *next = index + 1;
     350                 :          0 :         return 0;
     351                 :            : }
     352                 :            : 
     353                 :            : const struct bpf_map_ops reuseport_array_ops = {
     354                 :            :         .map_alloc_check = reuseport_array_alloc_check,
     355                 :            :         .map_alloc = reuseport_array_alloc,
     356                 :            :         .map_free = reuseport_array_free,
     357                 :            :         .map_lookup_elem = reuseport_array_lookup_elem,
     358                 :            :         .map_get_next_key = reuseport_array_get_next_key,
     359                 :            :         .map_delete_elem = reuseport_array_delete_elem,
     360                 :            : };
    

Generated by: LCOV version 1.14