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
  92/* Call get/setsockopt() */
  93static int nf_sockopt(struct sock *sk, u_int8_t pf, int val,
  94                      char __user *opt, int *len, int get)
  95{
  96        struct nf_sockopt_ops *ops;
  97        int ret;
  98
  99        ops = nf_sockopt_find(sk, pf, val, get);
 100        if (IS_ERR(ops))
 101                return PTR_ERR(ops);
 102
 103        if (get)
 104                ret = ops->get(sk, val, opt, len);
 105        else
 106                ret = ops->set(sk, val, opt, *len);
 107
 108        module_put(ops->owner);
 109        return ret;
 110}
 111
 112int nf_setsockopt(struct sock *sk, u_int8_t pf, int val, char __user *opt,
 113                  unsigned int len)
 114{
 115        return nf_sockopt(sk, pf, val, opt, &len, 0);
 116}
 117EXPORT_SYMBOL(nf_setsockopt);
 118
 119int nf_getsockopt(struct sock *sk, u_int8_t pf, int val, char __user *opt,
 120                  int *len)
 121{
 122        return nf_sockopt(sk, pf, val, opt, len, 1);
 123}
 124EXPORT_SYMBOL(nf_getsockopt);
 125
 126#ifdef CONFIG_COMPAT
 127static int compat_nf_sockopt(struct sock *sk, u_int8_t pf, int val,
 128                             char __user *opt, int *len, int get)
 129{
 130        struct nf_sockopt_ops *ops;
 131        int ret;
 132
 133        ops = nf_sockopt_find(sk, pf, val, get);
 134        if (IS_ERR(ops))
 135                return PTR_ERR(ops);
 136
 137        if (get) {
 138                if (ops->compat_get)
 139                        ret = ops->compat_get(sk, val, opt, len);
 140                else
 141                        ret = ops->get(sk, val, opt, len);
 142        } else {
 143                if (ops->compat_set)
 144                        ret = ops->compat_set(sk, val, opt, *len);
 145                else
 146                        ret = ops->set(sk, val, opt, *len);
 147        }
 148
 149        module_put(ops->owner);
 150        return ret;
 151}
 152
 153int compat_nf_setsockopt(struct sock *sk, u_int8_t pf,
 154                int val, char __user *opt, unsigned int len)
 155{
 156        return compat_nf_sockopt(sk, pf, val, opt, &len, 0);
 157}
 158EXPORT_SYMBOL(compat_nf_setsockopt);
 159
 160int compat_nf_getsockopt(struct sock *sk, u_int8_t pf,
 161                int val, char __user *opt, int *len)
 162{
 163        return compat_nf_sockopt(sk, pf, val, opt, len, 1);
 164}
 165EXPORT_SYMBOL(compat_nf_getsockopt);
 166#endif
 167