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. */ 14 static DEFINE_MUTEX(nf_sockopt_mutex); 15 static LIST_HEAD(nf_sockopts); 16 17 /* Do exclusive ranges overlap? */ 18 static 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). */ 24 int 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); 49 out: 50 mutex_unlock(&nf_sockopt_mutex); 51 return ret; 52 } 53 EXPORT_SYMBOL(nf_register_sockopt); 54 55 void 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 } 61 EXPORT_SYMBOL(nf_unregister_sockopt); 62 63 static struct nf_sockopt_ops *nf_sockopt_find(struct sock *sk, int pf, 64 int val, int get) 65 { 66 struct nf_sockopt_ops *ops; 67 68 if (sock_net(sk) != &init_net) 69 return ERR_PTR(-ENOPROTOOPT); 70 71 if (mutex_lock_interruptible(&nf_sockopt_mutex) != 0) 72 return ERR_PTR(-EINTR); 73 74 list_for_each_entry(ops, &nf_sockopts, list) { 75 if (ops->pf == pf) { 76 if (!try_module_get(ops->owner)) 77 goto out_nosup; 78 79 if (get) { 80 if (val >= ops->get_optmin && 81 val < ops->get_optmax) 82 goto out; 83 } else { 84 if (val >= ops->set_optmin && 85 val < ops->set_optmax) 86 goto out; 87 } 88 module_put(ops->owner); 89 } 90 } 91 out_nosup: 92 ops = ERR_PTR(-ENOPROTOOPT); 93 out: 94 mutex_unlock(&nf_sockopt_mutex); 95 return ops; 96 } 97 98 /* Call get/setsockopt() */ 99 static int nf_sockopt(struct sock *sk, int pf, int val, 100 char __user *opt, int *len, int get) 101 { 102 struct nf_sockopt_ops *ops; 103 int ret; 104 105 ops = nf_sockopt_find(sk, pf, val, get); 106 if (IS_ERR(ops)) 107 return PTR_ERR(ops); 108 109 if (get) 110 ret = ops->get(sk, val, opt, len); 111 else 112 ret = ops->set(sk, val, opt, *len); 113 114 module_put(ops->owner); 115 return ret; 116 } 117 118 int nf_setsockopt(struct sock *sk, int pf, int val, char __user *opt, 119 int len) 120 { 121 return nf_sockopt(sk, pf, val, opt, &len, 0); 122 } 123 EXPORT_SYMBOL(nf_setsockopt); 124 125 int nf_getsockopt(struct sock *sk, int pf, int val, char __user *opt, int *len) 126 { 127 return nf_sockopt(sk, pf, val, opt, len, 1); 128 } 129 EXPORT_SYMBOL(nf_getsockopt); 130 131 #ifdef CONFIG_COMPAT 132 static int compat_nf_sockopt(struct sock *sk, int pf, int val, 133 char __user *opt, int *len, int get) 134 { 135 struct nf_sockopt_ops *ops; 136 int ret; 137 138 ops = nf_sockopt_find(sk, pf, val, get); 139 if (IS_ERR(ops)) 140 return PTR_ERR(ops); 141 142 if (get) { 143 if (ops->compat_get) 144 ret = ops->compat_get(sk, val, opt, len); 145 else 146 ret = ops->get(sk, val, opt, len); 147 } else { 148 if (ops->compat_set) 149 ret = ops->compat_set(sk, val, opt, *len); 150 else 151 ret = ops->set(sk, val, opt, *len); 152 } 153 154 module_put(ops->owner); 155 return ret; 156 } 157 158 int compat_nf_setsockopt(struct sock *sk, int pf, 159 int val, char __user *opt, int len) 160 { 161 return compat_nf_sockopt(sk, pf, val, opt, &len, 0); 162 } 163 EXPORT_SYMBOL(compat_nf_setsockopt); 164 165 int compat_nf_getsockopt(struct sock *sk, int pf, 166 int val, char __user *opt, int *len) 167 { 168 return compat_nf_sockopt(sk, pf, val, opt, len, 1); 169 } 170 EXPORT_SYMBOL(compat_nf_getsockopt); 171 #endif 172