[NETFILTER]: nf_nat: fix memset error
[linux-2.6-block.git] / net / netfilter / nf_sockopt.c
CommitLineData
f6ebe77f
HW
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>
4a3e2f71 6#include <linux/mutex.h>
f6ebe77f
HW
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. */
4a3e2f71 14static DEFINE_MUTEX(nf_sockopt_mutex);
f6ebe77f
HW
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{
55d84acd 26 struct nf_sockopt_ops *ops;
f6ebe77f
HW
27 int ret = 0;
28
4a3e2f71 29 if (mutex_lock_interruptible(&nf_sockopt_mutex) != 0)
f6ebe77f
HW
30 return -EINTR;
31
55d84acd 32 list_for_each_entry(ops, &nf_sockopts, list) {
f6ebe77f 33 if (ops->pf == reg->pf
601e68e1 34 && (overlap(ops->set_optmin, ops->set_optmax,
f6ebe77f 35 reg->set_optmin, reg->set_optmax)
601e68e1 36 || overlap(ops->get_optmin, ops->get_optmax,
f6ebe77f
HW
37 reg->get_optmin, reg->get_optmax))) {
38 NFDEBUG("nf_sock overlap: %u-%u/%u-%u v %u-%u/%u-%u\n",
601e68e1
YH
39 ops->set_optmin, ops->set_optmax,
40 ops->get_optmin, ops->get_optmax,
f6ebe77f
HW
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(&reg->list, &nf_sockopts);
49out:
4a3e2f71 50 mutex_unlock(&nf_sockopt_mutex);
f6ebe77f
HW
51 return ret;
52}
53EXPORT_SYMBOL(nf_register_sockopt);
54
55void nf_unregister_sockopt(struct nf_sockopt_ops *reg)
56{
4a3e2f71 57 mutex_lock(&nf_sockopt_mutex);
f6ebe77f 58 list_del(&reg->list);
4a3e2f71 59 mutex_unlock(&nf_sockopt_mutex);
f6ebe77f
HW
60}
61EXPORT_SYMBOL(nf_unregister_sockopt);
62
63/* Call get/setsockopt() */
601e68e1 64static int nf_sockopt(struct sock *sk, int pf, int val,
f6ebe77f
HW
65 char __user *opt, int *len, int get)
66{
f6ebe77f
HW
67 struct nf_sockopt_ops *ops;
68 int ret;
69
c48dad7e
EB
70 if (sk->sk_net != &init_net)
71 return -ENOPROTOOPT;
72
4a3e2f71 73 if (mutex_lock_interruptible(&nf_sockopt_mutex) != 0)
f6ebe77f
HW
74 return -EINTR;
75
55d84acd 76 list_for_each_entry(ops, &nf_sockopts, list) {
f6ebe77f 77 if (ops->pf == pf) {
16fcec35
NH
78 if (!try_module_get(ops->owner))
79 goto out_nosup;
f6ebe77f
HW
80 if (get) {
81 if (val >= ops->get_optmin
82 && val < ops->get_optmax) {
4a3e2f71 83 mutex_unlock(&nf_sockopt_mutex);
f6ebe77f
HW
84 ret = ops->get(sk, val, opt, len);
85 goto out;
86 }
87 } else {
88 if (val >= ops->set_optmin
89 && val < ops->set_optmax) {
4a3e2f71 90 mutex_unlock(&nf_sockopt_mutex);
f6ebe77f
HW
91 ret = ops->set(sk, val, opt, *len);
92 goto out;
93 }
94 }
16fcec35 95 module_put(ops->owner);
f6ebe77f
HW
96 }
97 }
16fcec35 98 out_nosup:
4a3e2f71 99 mutex_unlock(&nf_sockopt_mutex);
f6ebe77f 100 return -ENOPROTOOPT;
601e68e1 101
f6ebe77f 102 out:
16fcec35 103 module_put(ops->owner);
f6ebe77f
HW
104 return ret;
105}
106
107int nf_setsockopt(struct sock *sk, int pf, int val, char __user *opt,
108 int len)
109{
110 return nf_sockopt(sk, pf, val, opt, &len, 0);
111}
112EXPORT_SYMBOL(nf_setsockopt);
113
114int nf_getsockopt(struct sock *sk, int pf, int val, char __user *opt, int *len)
115{
116 return nf_sockopt(sk, pf, val, opt, len, 1);
117}
118EXPORT_SYMBOL(nf_getsockopt);
119
3fdadf7d
DM
120#ifdef CONFIG_COMPAT
121static int compat_nf_sockopt(struct sock *sk, int pf, int val,
543d9cfe 122 char __user *opt, int *len, int get)
3fdadf7d 123{
3fdadf7d
DM
124 struct nf_sockopt_ops *ops;
125 int ret;
126
c48dad7e
EB
127 if (sk->sk_net != &init_net)
128 return -ENOPROTOOPT;
129
130
3fdadf7d
DM
131 if (mutex_lock_interruptible(&nf_sockopt_mutex) != 0)
132 return -EINTR;
133
55d84acd 134 list_for_each_entry(ops, &nf_sockopts, list) {
3fdadf7d 135 if (ops->pf == pf) {
16fcec35
NH
136 if (!try_module_get(ops->owner))
137 goto out_nosup;
138
3fdadf7d
DM
139 if (get) {
140 if (val >= ops->get_optmin
141 && val < ops->get_optmax) {
3fdadf7d
DM
142 mutex_unlock(&nf_sockopt_mutex);
143 if (ops->compat_get)
144 ret = ops->compat_get(sk,
145 val, opt, len);
146 else
147 ret = ops->get(sk,
148 val, opt, len);
149 goto out;
150 }
151 } else {
152 if (val >= ops->set_optmin
153 && val < ops->set_optmax) {
3fdadf7d
DM
154 mutex_unlock(&nf_sockopt_mutex);
155 if (ops->compat_set)
156 ret = ops->compat_set(sk,
157 val, opt, *len);
158 else
159 ret = ops->set(sk,
160 val, opt, *len);
161 goto out;
162 }
163 }
16fcec35 164 module_put(ops->owner);
3fdadf7d
DM
165 }
166 }
16fcec35 167 out_nosup:
3fdadf7d
DM
168 mutex_unlock(&nf_sockopt_mutex);
169 return -ENOPROTOOPT;
170
171 out:
16fcec35 172 module_put(ops->owner);
3fdadf7d
DM
173 return ret;
174}
175
176int compat_nf_setsockopt(struct sock *sk, int pf,
177 int val, char __user *opt, int len)
178{
179 return compat_nf_sockopt(sk, pf, val, opt, &len, 0);
180}
181EXPORT_SYMBOL(compat_nf_setsockopt);
182
183int compat_nf_getsockopt(struct sock *sk, int pf,
184 int val, char __user *opt, int *len)
185{
186 return compat_nf_sockopt(sk, pf, val, opt, len, 1);
187}
188EXPORT_SYMBOL(compat_nf_getsockopt);
189#endif