linux/net/netfilter/nf_sockopt.c
<<
>>
Prefs
   1#include <linux/kernel.h>
   2#include <linux/init.h>
   3#include <linux/module.h>
   4#include <linux/skbuff.h>
   5#include <linux/netfilter.h>
   6#include <linux/mutex.h>
   7#include <net/sock.h>
   8
   9#include "nf_internals.h"
  10
  11/* Sockopts only registered and called from user context, so
  12   net locking would be overkill.  Also, [gs]etsockopt calls may
  13   sleep. */
  14static DEFINE_MUTEX(nf_sockopt_mutex);
  15static LIST_HEAD(nf_sockopts);
  16
  17/* Do exclusive ranges overlap? */
  18static inline int overlap(int min1, int max1, int min2, int max2)
  19{
  20        return max1 > min2 && min1 < max2;
  21}
  22
  23/* Functions to register sockopt ranges (exclusive). */
  24int nf_register_sockopt(struct nf_sockopt_ops *reg)
  25{
  26        struct nf_sockopt_ops *ops;
  27        int ret = 0;
  28
  29        if (mutex_lock_interruptible(&nf_sockopt_mutex) != 0)
  30                return -EINTR;
  31
  32        list_for_each_entry(ops, &nf_sockopts, list) {
  33                if (ops->pf == reg->pf
  34                    && (overlap(ops->set_optmin, ops->set_optmax,
  35                                reg->set_optmin, reg->set_optmax)
  36                        || overlap(ops->get_optmin, ops->get_optmax,
  37                                   reg->get_optmin, reg->get_optmax))) {
  38                        NFDEBUG("nf_sock overlap: %u-%u/%u-%u v %u-%u/%u-%u\n",
  39                                ops->set_optmin, ops->set_optmax,
  40                                ops->get_optmin, ops->get_optmax,
  41                                reg->set_optmin, reg->set_optmax,
  42                                reg->get_optmin, reg->get_optmax);
  43                        ret = -EBUSY;
  44                        goto out;
  45                }
  46        }
  47
  48        list_add(&reg->list, &nf_sockopts);
  49out:
  50        mutex_unlock(&nf_sockopt_mutex);
  51        return ret;
  52}
  53EXPORT_SYMBOL(nf_register_sockopt);
  54
  55void nf_unregister_sockopt(struct nf_sockopt_ops *reg)
  56{
  57        mutex_lock(&nf_sockopt_mutex);
  58        list_del(&reg->list);
  59        mutex_unlock(&nf_sockopt_mutex);
  60}
  61EXPORT_SYMBOL(nf_unregister_sockopt);
  62
  63static struct nf_sockopt_ops *nf_sockopt_find(struct sock *sk, u_int8_t pf,
  64                int val, int get)
  65{
  66        struct nf_sockopt_ops *ops;
  67
  68        if (mutex_lock_interruptible(&nf_sockopt_mutex) != 0)
  69                return ERR_PTR(-EINTR);
  70
  71        list_for_each_entry(ops, &nf_sockopts, list) {
  72                if (ops->pf == pf) {
  73                        if (!try_module_get(ops->owner))
  74                                goto out_nosup;
  75
  76                        if (get) {
  77                                if (val >= ops->get_optmin &&
  78                                                val < ops->get_optmax)
  79                                        goto out;
  80                        } else {
  81                                if (val >= ops->set_optmin &&
  82                                                val < ops->set_optmax)
  83                                        goto out;
  84                        }
  85                        module_put(ops->owner);
  86                }
  87        }
  88out_nosup:
  89        ops = ERR_PTR(-ENOPROTOOPT);
  90out:
  91        mutex_unlock(&nf_sockopt_mutex);
  92        return ops;
  93}
  94
  95/* Call get/setsockopt() */
  96static int nf_sockopt(struct sock *sk, u_int8_t pf, int val,
  97                      char __user *opt, int *len, int get)
  98{
  99        struct nf_sockopt_ops *ops;
 100        int ret;
 101
 102        ops = nf_sockopt_find(sk, pf, val, get);
 103        if (IS_ERR(ops))
 104                return PTR_ERR(ops);
 105
 106        if (get)
 107                ret = ops->get(sk, val, opt, len);
 108        else
 109                ret = ops->set(sk, val, opt, *len);
 110
 111        module_put(ops->owner);
 112        return ret;
 113}
 114
 115int nf_setsockopt(struct sock *sk, u_int8_t pf, int val, char __user *opt,
 116                  unsigned int len)
 117{
 118        return nf_sockopt(sk, pf, val, opt, &len, 0);
 119}
 120EXPORT_SYMBOL(nf_setsockopt);
 121
 122int nf_getsockopt(struct sock *sk, u_int8_t pf, int val, char __user *opt,
 123                  int *len)
 124{
 125        return nf_sockopt(sk, pf, val, opt, len, 1);
 126}
 127EXPORT_SYMBOL(nf_getsockopt);
 128
 129#ifdef CONFIG_COMPAT
 130static int compat_nf_sockopt(struct sock *sk, u_int8_t pf, int val,
 131                             char __user *opt, int *len, int get)
 132{
 133        struct nf_sockopt_ops *ops;
 134        int ret;
 135
 136        ops = nf_sockopt_find(sk, pf, val, get);
 137        if (IS_ERR(ops))
 138                return PTR_ERR(ops);
 139
 140        if (get) {
 141                if (ops->compat_get)
 142                        ret = ops->compat_get(sk, val, opt, len);
 143                else
 144                        ret = ops->get(sk, val, opt, len);
 145        } else {
 146                if (ops->compat_set)
 147                        ret = ops->compat_set(sk, val, opt, *len);
 148                else
 149                        ret = ops->set(sk, val, opt, *len);
 150        }
 151
 152        module_put(ops->owner);
 153        return ret;
 154}
 155
 156int compat_nf_setsockopt(struct sock *sk, u_int8_t pf,
 157                int val, char __user *opt, unsigned int len)
 158{
 159        return compat_nf_sockopt(sk, pf, val, opt, &len, 0);
 160}
 161EXPORT_SYMBOL(compat_nf_setsockopt);
 162
 163int compat_nf_getsockopt(struct sock *sk, u_int8_t pf,
 164                int val, char __user *opt, int *len)
 165{
 166        return compat_nf_sockopt(sk, pf, val, opt, len, 1);
 167}
 168EXPORT_SYMBOL(compat_nf_getsockopt);
 169#endif
 170