[NET]: Disable netfilter sockopts when not in the initial network namespace
[pandora-kernel.git] / net / netfilter / nf_sockopt.c
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 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(&reg->list, &nf_sockopts);
50 out:
51         mutex_unlock(&nf_sockopt_mutex);
52         return ret;
53 }
54 EXPORT_SYMBOL(nf_register_sockopt);
55
56 void nf_unregister_sockopt(struct nf_sockopt_ops *reg)
57 {
58         mutex_lock(&nf_sockopt_mutex);
59         list_del(&reg->list);
60         mutex_unlock(&nf_sockopt_mutex);
61 }
62 EXPORT_SYMBOL(nf_unregister_sockopt);
63
64 /* Call get/setsockopt() */
65 static int nf_sockopt(struct sock *sk, int pf, int val,
66                       char __user *opt, int *len, int get)
67 {
68         struct list_head *i;
69         struct nf_sockopt_ops *ops;
70         int ret;
71
72         if (sk->sk_net != &init_net)
73                 return -ENOPROTOOPT;
74
75         if (mutex_lock_interruptible(&nf_sockopt_mutex) != 0)
76                 return -EINTR;
77
78         list_for_each(i, &nf_sockopts) {
79                 ops = (struct nf_sockopt_ops *)i;
80                 if (ops->pf == pf) {
81                         if (!try_module_get(ops->owner))
82                                 goto out_nosup;
83                         if (get) {
84                                 if (val >= ops->get_optmin
85                                     && val < ops->get_optmax) {
86                                         mutex_unlock(&nf_sockopt_mutex);
87                                         ret = ops->get(sk, val, opt, len);
88                                         goto out;
89                                 }
90                         } else {
91                                 if (val >= ops->set_optmin
92                                     && val < ops->set_optmax) {
93                                         mutex_unlock(&nf_sockopt_mutex);
94                                         ret = ops->set(sk, val, opt, *len);
95                                         goto out;
96                                 }
97                         }
98                         module_put(ops->owner);
99                 }
100         }
101  out_nosup:
102         mutex_unlock(&nf_sockopt_mutex);
103         return -ENOPROTOOPT;
104
105  out:
106         module_put(ops->owner);
107         return ret;
108 }
109
110 int nf_setsockopt(struct sock *sk, int pf, int val, char __user *opt,
111                   int len)
112 {
113         return nf_sockopt(sk, pf, val, opt, &len, 0);
114 }
115 EXPORT_SYMBOL(nf_setsockopt);
116
117 int nf_getsockopt(struct sock *sk, int pf, int val, char __user *opt, int *len)
118 {
119         return nf_sockopt(sk, pf, val, opt, len, 1);
120 }
121 EXPORT_SYMBOL(nf_getsockopt);
122
123 #ifdef CONFIG_COMPAT
124 static int compat_nf_sockopt(struct sock *sk, int pf, int val,
125                              char __user *opt, int *len, int get)
126 {
127         struct list_head *i;
128         struct nf_sockopt_ops *ops;
129         int ret;
130
131         if (sk->sk_net != &init_net)
132                 return -ENOPROTOOPT;
133
134
135         if (mutex_lock_interruptible(&nf_sockopt_mutex) != 0)
136                 return -EINTR;
137
138         list_for_each(i, &nf_sockopts) {
139                 ops = (struct nf_sockopt_ops *)i;
140                 if (ops->pf == pf) {
141                         if (!try_module_get(ops->owner))
142                                 goto out_nosup;
143
144                         if (get) {
145                                 if (val >= ops->get_optmin
146                                     && val < ops->get_optmax) {
147                                         mutex_unlock(&nf_sockopt_mutex);
148                                         if (ops->compat_get)
149                                                 ret = ops->compat_get(sk,
150                                                         val, opt, len);
151                                         else
152                                                 ret = ops->get(sk,
153                                                         val, opt, len);
154                                         goto out;
155                                 }
156                         } else {
157                                 if (val >= ops->set_optmin
158                                     && val < ops->set_optmax) {
159                                         mutex_unlock(&nf_sockopt_mutex);
160                                         if (ops->compat_set)
161                                                 ret = ops->compat_set(sk,
162                                                         val, opt, *len);
163                                         else
164                                                 ret = ops->set(sk,
165                                                         val, opt, *len);
166                                         goto out;
167                                 }
168                         }
169                         module_put(ops->owner);
170                 }
171         }
172  out_nosup:
173         mutex_unlock(&nf_sockopt_mutex);
174         return -ENOPROTOOPT;
175
176  out:
177         module_put(ops->owner);
178         return ret;
179 }
180
181 int compat_nf_setsockopt(struct sock *sk, int pf,
182                 int val, char __user *opt, int len)
183 {
184         return compat_nf_sockopt(sk, pf, val, opt, &len, 0);
185 }
186 EXPORT_SYMBOL(compat_nf_setsockopt);
187
188 int compat_nf_getsockopt(struct sock *sk, int pf,
189                 int val, char __user *opt, int *len)
190 {
191         return compat_nf_sockopt(sk, pf, val, opt, len, 1);
192 }
193 EXPORT_SYMBOL(compat_nf_getsockopt);
194 #endif