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(®->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(®->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