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        mutex_lock(&nf_sockopt_mutex);
  30        list_for_each_entry(ops, &nf_sockopts, list) {
  31                if (ops->pf == reg->pf
  32                    && (overlap(ops->set_optmin, ops->set_optmax,
  33                                reg->set_optmin, reg->set_optmax)
  34                        || overlap(ops->get_optmin, ops->get_optmax,
  35                                   reg->get_optmin, reg->get_optmax))) {
  36                        NFDEBUG("nf_sock overlap: %u-%u/%u-%u v %u-%u/%u-%u\n",
  37                                ops->set_optmin, ops->set_optmax,
  38                                ops->get_optmin, ops->get_optmax,
  39                                reg->set_optmin, reg->set_optmax,
  40                                reg->get_optmin, reg->get_optmax);
  41                        ret = -EBUSY;
  42                        goto out;
  43                }
  44        }
  45
  46        list_add(&reg->list, &nf_sockopts);
  47out:
  48        mutex_unlock(&nf_sockopt_mutex);
  49        return ret;
  50}
  51EXPORT_SYMBOL(nf_register_sockopt);
  52
  53void nf_unregister_sockopt(struct nf_sockopt_ops *reg)
  54{
  55        mutex_lock(&nf_sockopt_mutex);
  56        list_del(&reg->list);
  57        mutex_unlock(&nf_sockopt_mutex);
  58}
  59EXPORT_SYMBOL(nf_unregister_sockopt);
  60
  61static struct nf_sockopt_ops *nf_sockopt_find(struct sock *sk, u_int8_t pf,
  62                int val, int get)
  63{
  64        struct nf_sockopt_ops *ops;
  65
  66        mutex_lock(&nf_sockopt_mutex);
  67        list_for_each_entry(ops, &nf_sockopts, list) {
  68                if (ops->pf == pf) {
  69                        if (!try_module_get(ops->owner))
  70                                goto out_nosup;
  71
  72                        if (get) {
  73                                if (val >= ops->get_optmin &&
  74                                                val < ops->get_optmax)
  75                                        goto out;
  76                        } else {
  77                                if (val >= ops->set_optmin &&
  78                                                val < ops->set_optmax)
  79                                        goto out;
  80                        }
  81                        module_put(ops->owner);
  82                }
  83        }
  84out_nosup:
  85        ops = ERR_PTR(-ENOPROTOOPT);
  86out:
  87        mutex_unlock(&nf_sockopt_mutex);
  88        return ops;
  89}
  90
  91/* Call get/setsockopt() */
  92static int nf_sockopt(struct sock *sk, u_int8_t pf, int val,
  93                      char __user *opt, int *len, int get)
  94{
  95        struct nf_sockopt_ops *ops;
  96        int ret;
  97
  98        ops = nf_sockopt_find(sk, pf, val, get);
  99        if (IS_ERR(ops))
 100                return PTR_ERR(ops);
 101
 102        if (get)
 103                ret = ops->get(sk, val, opt, len);
 104        else
 105                ret = ops->set(sk, val, opt, *len);
 106
 107        module_put(ops->owner);
 108        return ret;
 109}
 110
 111int nf_setsockopt(struct sock *sk, u_int8_t pf, int val, char __user *opt,
 112                  unsigned int len)
 113{
 114        return nf_sockopt(sk, pf, val, opt, &len, 0);
 115}
 116EXPORT_SYMBOL(nf_setsockopt);
 117
 118int nf_getsockopt(struct sock *sk, u_int8_t pf, int val, char __user *opt,
 119                  int *len)
 120{
 121        return nf_sockopt(sk, pf, val, opt, len, 1);
 122}
 123EXPORT_SYMBOL(nf_getsockopt);
 124
 125#ifdef CONFIG_COMPAT
 126static int compat_nf_sockopt(struct sock *sk, u_int8_t pf, int val,
 127                             char __user *opt, int *len, int get)
 128{
 129        struct nf_sockopt_ops *ops;
 130        int ret;
 131
 132        ops = nf_sockopt_find(sk, pf, val, get);
 133        if (IS_ERR(ops))
 134                return PTR_ERR(ops);
 135
 136        if (get) {
 137                if (ops->compat_get)
 138                        ret = ops->compat_get(sk, val, opt, len);
 139                else
 140                        ret = ops->get(sk, val, opt, len);
 141        } else {
 142                if (ops->compat_set)
 143                        ret = ops->compat_set(sk, val, opt, *len);
 144                else
 145                        ret = ops->set(sk, val, opt, *len);
 146        }
 147
 148        module_put(ops->owner);
 149        return ret;
 150}
 151
 152int compat_nf_setsockopt(struct sock *sk, u_int8_t pf,
 153                int val, char __user *opt, unsigned int len)
 154{
 155        return compat_nf_sockopt(sk, pf, val, opt, &len, 0);
 156}
 157EXPORT_SYMBOL(compat_nf_setsockopt);
 158
 159int compat_nf_getsockopt(struct sock *sk, u_int8_t pf,
 160                int val, char __user *opt, int *len)
 161{
 162        return compat_nf_sockopt(sk, pf, val, opt, len, 1);
 163}
 164EXPORT_SYMBOL(compat_nf_getsockopt);
 165#endif
 166