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 list_head *i; 27 int ret = 0; 28 29 if (mutex_lock_interruptible(&nf_sockopt_mutex) != 0) 30 return -EINTR; 31 32 list_for_each(i, &nf_sockopts) { 33 struct nf_sockopt_ops *ops = (struct nf_sockopt_ops *)i; 34 if (ops->pf == reg->pf 35 && (overlap(ops->set_optmin, ops->set_optmax, 36 reg->set_optmin, reg->set_optmax) 37 || overlap(ops->get_optmin, ops->get_optmax, 38 reg->get_optmin, reg->get_optmax))) { 39 NFDEBUG("nf_sock overlap: %u-%u/%u-%u v %u-%u/%u-%u\n", 40 ops->set_optmin, ops->set_optmax, 41 ops->get_optmin, ops->get_optmax, 42 reg->set_optmin, reg->set_optmax, 43 reg->get_optmin, reg->get_optmax); 44 ret = -EBUSY; 45 goto out; 46 } 47 } 48 49 list_add(®->list, &nf_sockopts); 50out: 51 mutex_unlock(&nf_sockopt_mutex); 52 return ret; 53} 54EXPORT_SYMBOL(nf_register_sockopt); 55 56void nf_unregister_sockopt(struct nf_sockopt_ops *reg) 57{ 58 /* No point being interruptible: we're probably in cleanup_module() */ 59 restart: 60 mutex_lock(&nf_sockopt_mutex); 61 if (reg->use != 0) { 62 /* To be woken by nf_sockopt call... */ 63 set_current_state(TASK_UNINTERRUPTIBLE); 64 reg->cleanup_task = current; 65 mutex_unlock(&nf_sockopt_mutex); 66 schedule(); 67 goto restart; 68 } 69 list_del(®->list); 70 mutex_unlock(&nf_sockopt_mutex); 71} 72EXPORT_SYMBOL(nf_unregister_sockopt); 73 74/* Call get/setsockopt() */ 75static int nf_sockopt(struct sock *sk, int pf, int val, 76 char __user *opt, int *len, int get) 77{ 78 struct list_head *i; 79 struct nf_sockopt_ops *ops; 80 int ret; 81 82 if (mutex_lock_interruptible(&nf_sockopt_mutex) != 0) 83 return -EINTR; 84 85 list_for_each(i, &nf_sockopts) { 86 ops = (struct nf_sockopt_ops *)i; 87 if (ops->pf == pf) { 88 if (get) { 89 if (val >= ops->get_optmin 90 && val < ops->get_optmax) { 91 ops->use++; 92 mutex_unlock(&nf_sockopt_mutex); 93 ret = ops->get(sk, val, opt, len); 94 goto out; 95 } 96 } else { 97 if (val >= ops->set_optmin 98 && val < ops->set_optmax) { 99 ops->use++; 100 mutex_unlock(&nf_sockopt_mutex); 101 ret = ops->set(sk, val, opt, *len); 102 goto out; 103 } 104 } 105 } 106 } 107 mutex_unlock(&nf_sockopt_mutex); 108 return -ENOPROTOOPT; 109 110 out: 111 mutex_lock(&nf_sockopt_mutex); 112 ops->use--; 113 if (ops->cleanup_task) 114 wake_up_process(ops->cleanup_task); 115 mutex_unlock(&nf_sockopt_mutex); 116 return ret; 117} 118 119int nf_setsockopt(struct sock *sk, int pf, int val, char __user *opt, 120 int len) 121{ 122 return nf_sockopt(sk, pf, val, opt, &len, 0); 123} 124EXPORT_SYMBOL(nf_setsockopt); 125 126int nf_getsockopt(struct sock *sk, int pf, int val, char __user *opt, int *len) 127{ 128 return nf_sockopt(sk, pf, val, opt, len, 1); 129} 130EXPORT_SYMBOL(nf_getsockopt); 131 132#ifdef CONFIG_COMPAT 133static int compat_nf_sockopt(struct sock *sk, int pf, int val, 134 char __user *opt, int *len, int get) 135{ 136 struct list_head *i; 137 struct nf_sockopt_ops *ops; 138 int ret; 139 140 if (mutex_lock_interruptible(&nf_sockopt_mutex) != 0) 141 return -EINTR; 142 143 list_for_each(i, &nf_sockopts) { 144 ops = (struct nf_sockopt_ops *)i; 145 if (ops->pf == pf) { 146 if (get) { 147 if (val >= ops->get_optmin 148 && val < ops->get_optmax) { 149 ops->use++; 150 mutex_unlock(&nf_sockopt_mutex); 151 if (ops->compat_get) 152 ret = ops->compat_get(sk, 153 val, opt, len); 154 else 155 ret = ops->get(sk, 156 val, opt, len); 157 goto out; 158 } 159 } else { 160 if (val >= ops->set_optmin 161 && val < ops->set_optmax) { 162 ops->use++; 163 mutex_unlock(&nf_sockopt_mutex); 164 if (ops->compat_set) 165 ret = ops->compat_set(sk, 166 val, opt, *len); 167 else 168 ret = ops->set(sk, 169 val, opt, *len); 170 goto out; 171 } 172 } 173 } 174 } 175 mutex_unlock(&nf_sockopt_mutex); 176 return -ENOPROTOOPT; 177 178 out: 179 mutex_lock(&nf_sockopt_mutex); 180 ops->use--; 181 if (ops->cleanup_task) 182 wake_up_process(ops->cleanup_task); 183 mutex_unlock(&nf_sockopt_mutex); 184 return ret; 185} 186 187int compat_nf_setsockopt(struct sock *sk, int pf, 188 int val, char __user *opt, int len) 189{ 190 return compat_nf_sockopt(sk, pf, val, opt, &len, 0); 191} 192EXPORT_SYMBOL(compat_nf_setsockopt); 193 194int compat_nf_getsockopt(struct sock *sk, int pf, 195 int val, char __user *opt, int *len) 196{ 197 return compat_nf_sockopt(sk, pf, val, opt, len, 1); 198} 199EXPORT_SYMBOL(compat_nf_getsockopt); 200#endif 201