root/net/netfilter/nf_sockopt.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. overlap
  2. nf_register_sockopt
  3. nf_unregister_sockopt
  4. nf_sockopt_find
  5. nf_sockopt
  6. nf_setsockopt
  7. nf_getsockopt
  8. compat_nf_sockopt
  9. compat_nf_setsockopt
  10. compat_nf_getsockopt

   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. */
  15 static DEFINE_MUTEX(nf_sockopt_mutex);
  16 static LIST_HEAD(nf_sockopts);
  17 
  18 /* Do exclusive ranges overlap? */
  19 static 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). */
  25 int 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);
  48 out:
  49         mutex_unlock(&nf_sockopt_mutex);
  50         return ret;
  51 }
  52 EXPORT_SYMBOL(nf_register_sockopt);
  53 
  54 void 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 }
  60 EXPORT_SYMBOL(nf_unregister_sockopt);
  61 
  62 static 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         }
  85 out_nosup:
  86         ops = ERR_PTR(-ENOPROTOOPT);
  87 out:
  88         mutex_unlock(&nf_sockopt_mutex);
  89         return ops;
  90 }
  91 
  92 /* Call get/setsockopt() */
  93 static 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 
 112 int 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 }
 117 EXPORT_SYMBOL(nf_setsockopt);
 118 
 119 int 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 }
 124 EXPORT_SYMBOL(nf_getsockopt);
 125 
 126 #ifdef CONFIG_COMPAT
 127 static 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 
 153 int 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 }
 158 EXPORT_SYMBOL(compat_nf_setsockopt);
 159 
 160 int 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 }
 165 EXPORT_SYMBOL(compat_nf_getsockopt);
 166 #endif

/* [<][>][^][v][top][bottom][index][help] */