linux/net/netfilter/nf_sockopt.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2#include <linux/kernel.h>
   3#include <linux/init.h>
   4#include <linux/module.h>
   5#include <linux/skbuff.h>
   6#include <linux/netfilter.h>
   7#include <linux/mutex.h>
   8#include <net/sock.h>
   9
  10#include "nf_internals.h"
  11
  12/* Sockopts only registered and called from user context, so
  13   net locking would be overkill.  Also, [gs]etsockopt calls may
  14   sleep. */
  15static DEFINE_MUTEX(nf_sockopt_mutex);
  16static LIST_HEAD(nf_sockopts);
  17
  18/* Do exclusive ranges overlap? */
  19static inline int overlap(int min1, int max1, int min2, int max2)
  20{
  21        return max1 > min2 && min1 < max2;
  22}
  23
  24/* Functions to register sockopt ranges (exclusive). */
  25int nf_register_sockopt(struct nf_sockopt_ops *reg)
  26{
  27        struct nf_sockopt_ops *ops;
  28        int ret = 0;
  29
  30        mutex_lock(&nf_sockopt_mutex);
  31        list_for_each_entry(ops, &nf_sockopts, list) {
  32                if (ops->pf == reg->pf
  33                    && (overlap(ops->set_optmin, ops->set_optmax,
  34                                reg->set_optmin, reg->set_optmax)
  35                        || overlap(ops->get_optmin, ops->get_optmax,
  36                                   reg->get_optmin, reg->get_optmax))) {
  37                        pr_debug("nf_sock overlap: %u-%u/%u-%u v %u-%u/%u-%u\n",
  38                                ops->set_optmin, ops->set_optmax,
  39                                ops->get_optmin, ops->get_optmax,
  40                                reg->set_optmin, reg->set_optmax,
  41                                reg->get_optmin, reg->get_optmax);
  42                        ret = -EBUSY;
  43                        goto out;
  44                }
  45        }
  46
  47        list_add(&reg->list, &nf_sockopts);
  48out:
  49        mutex_unlock(&nf_sockopt_mutex);
  50        return ret;
  51}
  52EXPORT_SYMBOL(nf_register_sockopt);
  53
  54void nf_unregister_sockopt(struct nf_sockopt_ops *reg)
  55{
  56        mutex_lock(&nf_sockopt_mutex);
  57        list_del(&reg->list);
  58        mutex_unlock(&nf_sockopt_mutex);
  59}
  60EXPORT_SYMBOL(nf_unregister_sockopt);
  61
  62static struct nf_sockopt_ops *nf_sockopt_find(struct sock *sk, u_int8_t pf,
  63                int val, int get)
  64{
  65        struct nf_sockopt_ops *ops;
  66
  67        mutex_lock(&nf_sockopt_mutex);
  68        list_for_each_entry(ops, &nf_sockopts, list) {
  69                if (ops->pf == pf) {
  70                        if (!try_module_get(ops->owner))
  71                                goto out_nosup;
  72
  73                        if (get) {
  74                                if (val >= ops->get_optmin &&
  75                                                val < ops->get_optmax)
  76                                        goto out;
  77                        } else {
  78                                if (val >= ops->set_optmin &&
  79                                                val < ops->set_optmax)
  80                                        goto out;
  81                        }
  82                        module_put(ops->owner);
  83                }
  84        }
  85out_nosup:
  86        ops = ERR_PTR(-ENOPROTOOPT);
  87out:
  88        mutex_unlock(&nf_sockopt_mutex);
  89        return ops;
  90}
  91
  92int nf_setsockopt(struct sock *sk, u_int8_t pf, int val, sockptr_t opt,
  93                  unsigned int len)
  94{
  95        struct nf_sockopt_ops *ops;
  96        int ret;
  97
  98        ops = nf_sockopt_find(sk, pf, val, 0);
  99        if (IS_ERR(ops))
 100                return PTR_ERR(ops);
 101        ret = ops->set(sk, val, opt, len);
 102        module_put(ops->owner);
 103        return ret;
 104}
 105EXPORT_SYMBOL(nf_setsockopt);
 106
 107int nf_getsockopt(struct sock *sk, u_int8_t pf, int val, char __user *opt,
 108                  int *len)
 109{
 110        struct nf_sockopt_ops *ops;
 111        int ret;
 112
 113        ops = nf_sockopt_find(sk, pf, val, 1);
 114        if (IS_ERR(ops))
 115                return PTR_ERR(ops);
 116        ret = ops->get(sk, val, opt, len);
 117        module_put(ops->owner);
 118        return ret;
 119}
 120EXPORT_SYMBOL(nf_getsockopt);
 121