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