Blame view
net/netfilter/nf_sockopt.c
3.86 KB
f6ebe77f9
|
1 2 3 4 5 |
#include <linux/kernel.h> #include <linux/init.h> #include <linux/module.h> #include <linux/skbuff.h> #include <linux/netfilter.h> |
4a3e2f711
|
6 |
#include <linux/mutex.h> |
f6ebe77f9
|
7 8 9 10 11 12 13 |
#include <net/sock.h> #include "nf_internals.h" /* Sockopts only registered and called from user context, so net locking would be overkill. Also, [gs]etsockopt calls may sleep. */ |
4a3e2f711
|
14 |
static DEFINE_MUTEX(nf_sockopt_mutex); |
f6ebe77f9
|
15 16 17 18 19 20 21 22 23 24 25 |
static LIST_HEAD(nf_sockopts); /* Do exclusive ranges overlap? */ static inline int overlap(int min1, int max1, int min2, int max2) { return max1 > min2 && min1 < max2; } /* Functions to register sockopt ranges (exclusive). */ int nf_register_sockopt(struct nf_sockopt_ops *reg) { |
55d84acd3
|
26 |
struct nf_sockopt_ops *ops; |
f6ebe77f9
|
27 |
int ret = 0; |
4a3e2f711
|
28 |
if (mutex_lock_interruptible(&nf_sockopt_mutex) != 0) |
f6ebe77f9
|
29 |
return -EINTR; |
55d84acd3
|
30 |
list_for_each_entry(ops, &nf_sockopts, list) { |
f6ebe77f9
|
31 |
if (ops->pf == reg->pf |
601e68e10
|
32 |
&& (overlap(ops->set_optmin, ops->set_optmax, |
f6ebe77f9
|
33 |
reg->set_optmin, reg->set_optmax) |
601e68e10
|
34 |
|| overlap(ops->get_optmin, ops->get_optmax, |
f6ebe77f9
|
35 36 37 |
reg->get_optmin, reg->get_optmax))) { NFDEBUG("nf_sock overlap: %u-%u/%u-%u v %u-%u/%u-%u ", |
601e68e10
|
38 39 |
ops->set_optmin, ops->set_optmax, ops->get_optmin, ops->get_optmax, |
f6ebe77f9
|
40 41 42 43 44 45 46 47 48 |
reg->set_optmin, reg->set_optmax, reg->get_optmin, reg->get_optmax); ret = -EBUSY; goto out; } } list_add(®->list, &nf_sockopts); out: |
4a3e2f711
|
49 |
mutex_unlock(&nf_sockopt_mutex); |
f6ebe77f9
|
50 51 52 53 54 55 |
return ret; } EXPORT_SYMBOL(nf_register_sockopt); void nf_unregister_sockopt(struct nf_sockopt_ops *reg) { |
4a3e2f711
|
56 |
mutex_lock(&nf_sockopt_mutex); |
f6ebe77f9
|
57 |
list_del(®->list); |
4a3e2f711
|
58 |
mutex_unlock(&nf_sockopt_mutex); |
f6ebe77f9
|
59 60 |
} EXPORT_SYMBOL(nf_unregister_sockopt); |
76108cea0
|
61 |
static struct nf_sockopt_ops *nf_sockopt_find(struct sock *sk, u_int8_t pf, |
4ce5ba6ae
|
62 |
int val, int get) |
f6ebe77f9
|
63 |
{ |
f6ebe77f9
|
64 |
struct nf_sockopt_ops *ops; |
f6ebe77f9
|
65 |
|
4a3e2f711
|
66 |
if (mutex_lock_interruptible(&nf_sockopt_mutex) != 0) |
4ce5ba6ae
|
67 |
return ERR_PTR(-EINTR); |
f6ebe77f9
|
68 |
|
55d84acd3
|
69 |
list_for_each_entry(ops, &nf_sockopts, list) { |
f6ebe77f9
|
70 |
if (ops->pf == pf) { |
16fcec35e
|
71 72 |
if (!try_module_get(ops->owner)) goto out_nosup; |
4ce5ba6ae
|
73 |
|
f6ebe77f9
|
74 |
if (get) { |
4ce5ba6ae
|
75 76 |
if (val >= ops->get_optmin && val < ops->get_optmax) |
f6ebe77f9
|
77 |
goto out; |
f6ebe77f9
|
78 |
} else { |
4ce5ba6ae
|
79 80 |
if (val >= ops->set_optmin && val < ops->set_optmax) |
f6ebe77f9
|
81 |
goto out; |
f6ebe77f9
|
82 |
} |
16fcec35e
|
83 |
module_put(ops->owner); |
f6ebe77f9
|
84 85 |
} } |
4ce5ba6ae
|
86 87 88 |
out_nosup: ops = ERR_PTR(-ENOPROTOOPT); out: |
4a3e2f711
|
89 |
mutex_unlock(&nf_sockopt_mutex); |
4ce5ba6ae
|
90 91 92 93 |
return ops; } /* Call get/setsockopt() */ |
76108cea0
|
94 |
static int nf_sockopt(struct sock *sk, u_int8_t pf, int val, |
4ce5ba6ae
|
95 96 97 98 99 100 101 102 103 104 105 106 107 |
char __user *opt, int *len, int get) { struct nf_sockopt_ops *ops; int ret; ops = nf_sockopt_find(sk, pf, val, get); if (IS_ERR(ops)) return PTR_ERR(ops); if (get) ret = ops->get(sk, val, opt, len); else ret = ops->set(sk, val, opt, *len); |
601e68e10
|
108 |
|
16fcec35e
|
109 |
module_put(ops->owner); |
f6ebe77f9
|
110 111 |
return ret; } |
76108cea0
|
112 |
int nf_setsockopt(struct sock *sk, u_int8_t pf, int val, char __user *opt, |
b7058842c
|
113 |
unsigned int len) |
f6ebe77f9
|
114 115 116 117 |
{ return nf_sockopt(sk, pf, val, opt, &len, 0); } EXPORT_SYMBOL(nf_setsockopt); |
76108cea0
|
118 119 |
int nf_getsockopt(struct sock *sk, u_int8_t pf, int val, char __user *opt, int *len) |
f6ebe77f9
|
120 121 122 123 |
{ return nf_sockopt(sk, pf, val, opt, len, 1); } EXPORT_SYMBOL(nf_getsockopt); |
3fdadf7d2
|
124 |
#ifdef CONFIG_COMPAT |
76108cea0
|
125 |
static int compat_nf_sockopt(struct sock *sk, u_int8_t pf, int val, |
543d9cfee
|
126 |
char __user *opt, int *len, int get) |
3fdadf7d2
|
127 |
{ |
3fdadf7d2
|
128 129 |
struct nf_sockopt_ops *ops; int ret; |
4ce5ba6ae
|
130 131 132 133 134 135 136 137 |
ops = nf_sockopt_find(sk, pf, val, get); if (IS_ERR(ops)) return PTR_ERR(ops); if (get) { if (ops->compat_get) ret = ops->compat_get(sk, val, opt, len); else |
6452a5fde
|
138 |
ret = ops->get(sk, val, opt, len); |
4ce5ba6ae
|
139 140 |
} else { if (ops->compat_set) |
6452a5fde
|
141 |
ret = ops->compat_set(sk, val, opt, *len); |
4ce5ba6ae
|
142 |
else |
6452a5fde
|
143 |
ret = ops->set(sk, val, opt, *len); |
3fdadf7d2
|
144 |
} |
3fdadf7d2
|
145 |
|
16fcec35e
|
146 |
module_put(ops->owner); |
3fdadf7d2
|
147 148 |
return ret; } |
76108cea0
|
149 |
int compat_nf_setsockopt(struct sock *sk, u_int8_t pf, |
b7058842c
|
150 |
int val, char __user *opt, unsigned int len) |
3fdadf7d2
|
151 152 153 154 |
{ return compat_nf_sockopt(sk, pf, val, opt, &len, 0); } EXPORT_SYMBOL(compat_nf_setsockopt); |
76108cea0
|
155 |
int compat_nf_getsockopt(struct sock *sk, u_int8_t pf, |
3fdadf7d2
|
156 157 158 159 160 161 |
int val, char __user *opt, int *len) { return compat_nf_sockopt(sk, pf, val, opt, len, 1); } EXPORT_SYMBOL(compat_nf_getsockopt); #endif |