diff options
Diffstat (limited to 'net/atm/ioctl.c')
-rw-r--r-- | net/atm/ioctl.c | 369 |
1 files changed, 271 insertions, 98 deletions
diff --git a/net/atm/ioctl.c b/net/atm/ioctl.c index 4da8892ced5f..62dc8bfe6fe7 100644 --- a/net/atm/ioctl.c +++ b/net/atm/ioctl.c | |||
@@ -3,6 +3,7 @@ | |||
3 | /* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */ | 3 | /* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */ |
4 | /* 2003 John Levon <levon@movementarian.org> */ | 4 | /* 2003 John Levon <levon@movementarian.org> */ |
5 | 5 | ||
6 | #define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__ | ||
6 | 7 | ||
7 | #include <linux/module.h> | 8 | #include <linux/module.h> |
8 | #include <linux/kmod.h> | 9 | #include <linux/kmod.h> |
@@ -36,6 +37,7 @@ void register_atm_ioctl(struct atm_ioctl *ioctl) | |||
36 | list_add_tail(&ioctl->list, &ioctl_list); | 37 | list_add_tail(&ioctl->list, &ioctl_list); |
37 | mutex_unlock(&ioctl_mutex); | 38 | mutex_unlock(&ioctl_mutex); |
38 | } | 39 | } |
40 | EXPORT_SYMBOL(register_atm_ioctl); | ||
39 | 41 | ||
40 | void deregister_atm_ioctl(struct atm_ioctl *ioctl) | 42 | void deregister_atm_ioctl(struct atm_ioctl *ioctl) |
41 | { | 43 | { |
@@ -43,129 +45,128 @@ void deregister_atm_ioctl(struct atm_ioctl *ioctl) | |||
43 | list_del(&ioctl->list); | 45 | list_del(&ioctl->list); |
44 | mutex_unlock(&ioctl_mutex); | 46 | mutex_unlock(&ioctl_mutex); |
45 | } | 47 | } |
46 | |||
47 | EXPORT_SYMBOL(register_atm_ioctl); | ||
48 | EXPORT_SYMBOL(deregister_atm_ioctl); | 48 | EXPORT_SYMBOL(deregister_atm_ioctl); |
49 | 49 | ||
50 | static int do_vcc_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg, int compat) | 50 | static int do_vcc_ioctl(struct socket *sock, unsigned int cmd, |
51 | unsigned long arg, int compat) | ||
51 | { | 52 | { |
52 | struct sock *sk = sock->sk; | 53 | struct sock *sk = sock->sk; |
53 | struct atm_vcc *vcc; | 54 | struct atm_vcc *vcc; |
54 | int error; | 55 | int error; |
55 | struct list_head * pos; | 56 | struct list_head *pos; |
56 | void __user *argp = (void __user *)arg; | 57 | void __user *argp = (void __user *)arg; |
57 | 58 | ||
58 | vcc = ATM_SD(sock); | 59 | vcc = ATM_SD(sock); |
59 | switch (cmd) { | 60 | switch (cmd) { |
60 | case SIOCOUTQ: | 61 | case SIOCOUTQ: |
61 | if (sock->state != SS_CONNECTED || | 62 | if (sock->state != SS_CONNECTED || |
62 | !test_bit(ATM_VF_READY, &vcc->flags)) { | 63 | !test_bit(ATM_VF_READY, &vcc->flags)) { |
63 | error = -EINVAL; | 64 | error = -EINVAL; |
64 | goto done; | 65 | goto done; |
65 | } | 66 | } |
66 | error = put_user(sk->sk_sndbuf - sk_wmem_alloc_get(sk), | 67 | error = put_user(sk->sk_sndbuf - sk_wmem_alloc_get(sk), |
67 | (int __user *) argp) ? -EFAULT : 0; | 68 | (int __user *)argp) ? -EFAULT : 0; |
69 | goto done; | ||
70 | case SIOCINQ: | ||
71 | { | ||
72 | struct sk_buff *skb; | ||
73 | |||
74 | if (sock->state != SS_CONNECTED) { | ||
75 | error = -EINVAL; | ||
68 | goto done; | 76 | goto done; |
69 | case SIOCINQ: | 77 | } |
70 | { | 78 | skb = skb_peek(&sk->sk_receive_queue); |
71 | struct sk_buff *skb; | 79 | error = put_user(skb ? skb->len : 0, |
72 | 80 | (int __user *)argp) ? -EFAULT : 0; | |
73 | if (sock->state != SS_CONNECTED) { | 81 | goto done; |
74 | error = -EINVAL; | 82 | } |
75 | goto done; | 83 | case SIOCGSTAMP: /* borrowed from IP */ |
76 | } | ||
77 | skb = skb_peek(&sk->sk_receive_queue); | ||
78 | error = put_user(skb ? skb->len : 0, | ||
79 | (int __user *)argp) ? -EFAULT : 0; | ||
80 | goto done; | ||
81 | } | ||
82 | case SIOCGSTAMP: /* borrowed from IP */ | ||
83 | #ifdef CONFIG_COMPAT | 84 | #ifdef CONFIG_COMPAT |
84 | if (compat) | 85 | if (compat) |
85 | error = compat_sock_get_timestamp(sk, argp); | 86 | error = compat_sock_get_timestamp(sk, argp); |
86 | else | 87 | else |
87 | #endif | 88 | #endif |
88 | error = sock_get_timestamp(sk, argp); | 89 | error = sock_get_timestamp(sk, argp); |
89 | goto done; | 90 | goto done; |
90 | case SIOCGSTAMPNS: /* borrowed from IP */ | 91 | case SIOCGSTAMPNS: /* borrowed from IP */ |
91 | #ifdef CONFIG_COMPAT | 92 | #ifdef CONFIG_COMPAT |
92 | if (compat) | 93 | if (compat) |
93 | error = compat_sock_get_timestampns(sk, argp); | 94 | error = compat_sock_get_timestampns(sk, argp); |
94 | else | 95 | else |
95 | #endif | 96 | #endif |
96 | error = sock_get_timestampns(sk, argp); | 97 | error = sock_get_timestampns(sk, argp); |
98 | goto done; | ||
99 | case ATM_SETSC: | ||
100 | if (net_ratelimit()) | ||
101 | pr_warning("ATM_SETSC is obsolete; used by %s:%d\n", | ||
102 | current->comm, task_pid_nr(current)); | ||
103 | error = 0; | ||
104 | goto done; | ||
105 | case ATMSIGD_CTRL: | ||
106 | if (!capable(CAP_NET_ADMIN)) { | ||
107 | error = -EPERM; | ||
97 | goto done; | 108 | goto done; |
98 | case ATM_SETSC: | 109 | } |
99 | if (net_ratelimit()) | 110 | /* |
100 | printk(KERN_WARNING "ATM_SETSC is obsolete; used by %s:%d\n", | 111 | * The user/kernel protocol for exchanging signalling |
101 | current->comm, task_pid_nr(current)); | 112 | * info uses kernel pointers as opaque references, |
102 | error = 0; | 113 | * so the holder of the file descriptor can scribble |
114 | * on the kernel... so we should make sure that we | ||
115 | * have the same privileges that /proc/kcore needs | ||
116 | */ | ||
117 | if (!capable(CAP_SYS_RAWIO)) { | ||
118 | error = -EPERM; | ||
103 | goto done; | 119 | goto done; |
104 | case ATMSIGD_CTRL: | 120 | } |
105 | if (!capable(CAP_NET_ADMIN)) { | ||
106 | error = -EPERM; | ||
107 | goto done; | ||
108 | } | ||
109 | /* | ||
110 | * The user/kernel protocol for exchanging signalling | ||
111 | * info uses kernel pointers as opaque references, | ||
112 | * so the holder of the file descriptor can scribble | ||
113 | * on the kernel... so we should make sure that we | ||
114 | * have the same privileges that /proc/kcore needs | ||
115 | */ | ||
116 | if (!capable(CAP_SYS_RAWIO)) { | ||
117 | error = -EPERM; | ||
118 | goto done; | ||
119 | } | ||
120 | #ifdef CONFIG_COMPAT | 121 | #ifdef CONFIG_COMPAT |
121 | /* WTF? I don't even want to _think_ about making this | 122 | /* WTF? I don't even want to _think_ about making this |
122 | work for 32-bit userspace. TBH I don't really want | 123 | work for 32-bit userspace. TBH I don't really want |
123 | to think about it at all. dwmw2. */ | 124 | to think about it at all. dwmw2. */ |
124 | if (compat) { | 125 | if (compat) { |
125 | if (net_ratelimit()) | 126 | if (net_ratelimit()) |
126 | printk(KERN_WARNING "32-bit task cannot be atmsigd\n"); | 127 | pr_warning("32-bit task cannot be atmsigd\n"); |
127 | error = -EINVAL; | 128 | error = -EINVAL; |
128 | goto done; | 129 | goto done; |
129 | } | 130 | } |
130 | #endif | 131 | #endif |
131 | error = sigd_attach(vcc); | 132 | error = sigd_attach(vcc); |
132 | if (!error) | 133 | if (!error) |
133 | sock->state = SS_CONNECTED; | 134 | sock->state = SS_CONNECTED; |
135 | goto done; | ||
136 | case ATM_SETBACKEND: | ||
137 | case ATM_NEWBACKENDIF: | ||
138 | { | ||
139 | atm_backend_t backend; | ||
140 | error = get_user(backend, (atm_backend_t __user *)argp); | ||
141 | if (error) | ||
134 | goto done; | 142 | goto done; |
135 | case ATM_SETBACKEND: | 143 | switch (backend) { |
136 | case ATM_NEWBACKENDIF: | 144 | case ATM_BACKEND_PPP: |
137 | { | 145 | request_module("pppoatm"); |
138 | atm_backend_t backend; | ||
139 | error = get_user(backend, (atm_backend_t __user *) argp); | ||
140 | if (error) | ||
141 | goto done; | ||
142 | switch (backend) { | ||
143 | case ATM_BACKEND_PPP: | ||
144 | request_module("pppoatm"); | ||
145 | break; | ||
146 | case ATM_BACKEND_BR2684: | ||
147 | request_module("br2684"); | ||
148 | break; | ||
149 | } | ||
150 | } | ||
151 | break; | ||
152 | case ATMMPC_CTRL: | ||
153 | case ATMMPC_DATA: | ||
154 | request_module("mpoa"); | ||
155 | break; | 146 | break; |
156 | case ATMARPD_CTRL: | 147 | case ATM_BACKEND_BR2684: |
157 | request_module("clip"); | 148 | request_module("br2684"); |
158 | break; | ||
159 | case ATMLEC_CTRL: | ||
160 | request_module("lec"); | ||
161 | break; | 149 | break; |
150 | } | ||
151 | break; | ||
152 | } | ||
153 | case ATMMPC_CTRL: | ||
154 | case ATMMPC_DATA: | ||
155 | request_module("mpoa"); | ||
156 | break; | ||
157 | case ATMARPD_CTRL: | ||
158 | request_module("clip"); | ||
159 | break; | ||
160 | case ATMLEC_CTRL: | ||
161 | request_module("lec"); | ||
162 | break; | ||
162 | } | 163 | } |
163 | 164 | ||
164 | error = -ENOIOCTLCMD; | 165 | error = -ENOIOCTLCMD; |
165 | 166 | ||
166 | mutex_lock(&ioctl_mutex); | 167 | mutex_lock(&ioctl_mutex); |
167 | list_for_each(pos, &ioctl_list) { | 168 | list_for_each(pos, &ioctl_list) { |
168 | struct atm_ioctl * ic = list_entry(pos, struct atm_ioctl, list); | 169 | struct atm_ioctl *ic = list_entry(pos, struct atm_ioctl, list); |
169 | if (try_module_get(ic->owner)) { | 170 | if (try_module_get(ic->owner)) { |
170 | error = ic->ioctl(sock, cmd, arg); | 171 | error = ic->ioctl(sock, cmd, arg); |
171 | module_put(ic->owner); | 172 | module_put(ic->owner); |
@@ -184,15 +185,187 @@ done: | |||
184 | return error; | 185 | return error; |
185 | } | 186 | } |
186 | 187 | ||
187 | |||
188 | int vcc_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) | 188 | int vcc_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) |
189 | { | 189 | { |
190 | return do_vcc_ioctl(sock, cmd, arg, 0); | 190 | return do_vcc_ioctl(sock, cmd, arg, 0); |
191 | } | 191 | } |
192 | 192 | ||
193 | #ifdef CONFIG_COMPAT | 193 | #ifdef CONFIG_COMPAT |
194 | int vcc_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) | 194 | /* |
195 | * FIXME: | ||
196 | * The compat_ioctl handling is duplicated, using both these conversion | ||
197 | * routines and the compat argument to the actual handlers. Both | ||
198 | * versions are somewhat incomplete and should be merged, e.g. by | ||
199 | * moving the ioctl number translation into the actual handlers and | ||
200 | * killing the conversion code. | ||
201 | * | ||
202 | * -arnd, November 2009 | ||
203 | */ | ||
204 | #define ATM_GETLINKRATE32 _IOW('a', ATMIOC_ITF+1, struct compat_atmif_sioc) | ||
205 | #define ATM_GETNAMES32 _IOW('a', ATMIOC_ITF+3, struct compat_atm_iobuf) | ||
206 | #define ATM_GETTYPE32 _IOW('a', ATMIOC_ITF+4, struct compat_atmif_sioc) | ||
207 | #define ATM_GETESI32 _IOW('a', ATMIOC_ITF+5, struct compat_atmif_sioc) | ||
208 | #define ATM_GETADDR32 _IOW('a', ATMIOC_ITF+6, struct compat_atmif_sioc) | ||
209 | #define ATM_RSTADDR32 _IOW('a', ATMIOC_ITF+7, struct compat_atmif_sioc) | ||
210 | #define ATM_ADDADDR32 _IOW('a', ATMIOC_ITF+8, struct compat_atmif_sioc) | ||
211 | #define ATM_DELADDR32 _IOW('a', ATMIOC_ITF+9, struct compat_atmif_sioc) | ||
212 | #define ATM_GETCIRANGE32 _IOW('a', ATMIOC_ITF+10, struct compat_atmif_sioc) | ||
213 | #define ATM_SETCIRANGE32 _IOW('a', ATMIOC_ITF+11, struct compat_atmif_sioc) | ||
214 | #define ATM_SETESI32 _IOW('a', ATMIOC_ITF+12, struct compat_atmif_sioc) | ||
215 | #define ATM_SETESIF32 _IOW('a', ATMIOC_ITF+13, struct compat_atmif_sioc) | ||
216 | #define ATM_GETSTAT32 _IOW('a', ATMIOC_SARCOM+0, struct compat_atmif_sioc) | ||
217 | #define ATM_GETSTATZ32 _IOW('a', ATMIOC_SARCOM+1, struct compat_atmif_sioc) | ||
218 | #define ATM_GETLOOP32 _IOW('a', ATMIOC_SARCOM+2, struct compat_atmif_sioc) | ||
219 | #define ATM_SETLOOP32 _IOW('a', ATMIOC_SARCOM+3, struct compat_atmif_sioc) | ||
220 | #define ATM_QUERYLOOP32 _IOW('a', ATMIOC_SARCOM+4, struct compat_atmif_sioc) | ||
221 | |||
222 | static struct { | ||
223 | unsigned int cmd32; | ||
224 | unsigned int cmd; | ||
225 | } atm_ioctl_map[] = { | ||
226 | { ATM_GETLINKRATE32, ATM_GETLINKRATE }, | ||
227 | { ATM_GETNAMES32, ATM_GETNAMES }, | ||
228 | { ATM_GETTYPE32, ATM_GETTYPE }, | ||
229 | { ATM_GETESI32, ATM_GETESI }, | ||
230 | { ATM_GETADDR32, ATM_GETADDR }, | ||
231 | { ATM_RSTADDR32, ATM_RSTADDR }, | ||
232 | { ATM_ADDADDR32, ATM_ADDADDR }, | ||
233 | { ATM_DELADDR32, ATM_DELADDR }, | ||
234 | { ATM_GETCIRANGE32, ATM_GETCIRANGE }, | ||
235 | { ATM_SETCIRANGE32, ATM_SETCIRANGE }, | ||
236 | { ATM_SETESI32, ATM_SETESI }, | ||
237 | { ATM_SETESIF32, ATM_SETESIF }, | ||
238 | { ATM_GETSTAT32, ATM_GETSTAT }, | ||
239 | { ATM_GETSTATZ32, ATM_GETSTATZ }, | ||
240 | { ATM_GETLOOP32, ATM_GETLOOP }, | ||
241 | { ATM_SETLOOP32, ATM_SETLOOP }, | ||
242 | { ATM_QUERYLOOP32, ATM_QUERYLOOP }, | ||
243 | }; | ||
244 | |||
245 | #define NR_ATM_IOCTL ARRAY_SIZE(atm_ioctl_map) | ||
246 | |||
247 | static int do_atm_iobuf(struct socket *sock, unsigned int cmd, | ||
248 | unsigned long arg) | ||
249 | { | ||
250 | struct atm_iobuf __user *iobuf; | ||
251 | struct compat_atm_iobuf __user *iobuf32; | ||
252 | u32 data; | ||
253 | void __user *datap; | ||
254 | int len, err; | ||
255 | |||
256 | iobuf = compat_alloc_user_space(sizeof(*iobuf)); | ||
257 | iobuf32 = compat_ptr(arg); | ||
258 | |||
259 | if (get_user(len, &iobuf32->length) || | ||
260 | get_user(data, &iobuf32->buffer)) | ||
261 | return -EFAULT; | ||
262 | datap = compat_ptr(data); | ||
263 | if (put_user(len, &iobuf->length) || | ||
264 | put_user(datap, &iobuf->buffer)) | ||
265 | return -EFAULT; | ||
266 | |||
267 | err = do_vcc_ioctl(sock, cmd, (unsigned long) iobuf, 0); | ||
268 | |||
269 | if (!err) { | ||
270 | if (copy_in_user(&iobuf32->length, &iobuf->length, | ||
271 | sizeof(int))) | ||
272 | err = -EFAULT; | ||
273 | } | ||
274 | |||
275 | return err; | ||
276 | } | ||
277 | |||
278 | static int do_atmif_sioc(struct socket *sock, unsigned int cmd, | ||
279 | unsigned long arg) | ||
280 | { | ||
281 | struct atmif_sioc __user *sioc; | ||
282 | struct compat_atmif_sioc __user *sioc32; | ||
283 | u32 data; | ||
284 | void __user *datap; | ||
285 | int err; | ||
286 | |||
287 | sioc = compat_alloc_user_space(sizeof(*sioc)); | ||
288 | sioc32 = compat_ptr(arg); | ||
289 | |||
290 | if (copy_in_user(&sioc->number, &sioc32->number, 2 * sizeof(int)) || | ||
291 | get_user(data, &sioc32->arg)) | ||
292 | return -EFAULT; | ||
293 | datap = compat_ptr(data); | ||
294 | if (put_user(datap, &sioc->arg)) | ||
295 | return -EFAULT; | ||
296 | |||
297 | err = do_vcc_ioctl(sock, cmd, (unsigned long) sioc, 0); | ||
298 | |||
299 | if (!err) { | ||
300 | if (copy_in_user(&sioc32->length, &sioc->length, | ||
301 | sizeof(int))) | ||
302 | err = -EFAULT; | ||
303 | } | ||
304 | return err; | ||
305 | } | ||
306 | |||
307 | static int do_atm_ioctl(struct socket *sock, unsigned int cmd32, | ||
308 | unsigned long arg) | ||
195 | { | 309 | { |
196 | return do_vcc_ioctl(sock, cmd, arg, 1); | 310 | int i; |
311 | unsigned int cmd = 0; | ||
312 | |||
313 | switch (cmd32) { | ||
314 | case SONET_GETSTAT: | ||
315 | case SONET_GETSTATZ: | ||
316 | case SONET_GETDIAG: | ||
317 | case SONET_SETDIAG: | ||
318 | case SONET_CLRDIAG: | ||
319 | case SONET_SETFRAMING: | ||
320 | case SONET_GETFRAMING: | ||
321 | case SONET_GETFRSENSE: | ||
322 | return do_atmif_sioc(sock, cmd32, arg); | ||
323 | } | ||
324 | |||
325 | for (i = 0; i < NR_ATM_IOCTL; i++) { | ||
326 | if (cmd32 == atm_ioctl_map[i].cmd32) { | ||
327 | cmd = atm_ioctl_map[i].cmd; | ||
328 | break; | ||
329 | } | ||
330 | } | ||
331 | if (i == NR_ATM_IOCTL) | ||
332 | return -EINVAL; | ||
333 | |||
334 | switch (cmd) { | ||
335 | case ATM_GETNAMES: | ||
336 | return do_atm_iobuf(sock, cmd, arg); | ||
337 | |||
338 | case ATM_GETLINKRATE: | ||
339 | case ATM_GETTYPE: | ||
340 | case ATM_GETESI: | ||
341 | case ATM_GETADDR: | ||
342 | case ATM_RSTADDR: | ||
343 | case ATM_ADDADDR: | ||
344 | case ATM_DELADDR: | ||
345 | case ATM_GETCIRANGE: | ||
346 | case ATM_SETCIRANGE: | ||
347 | case ATM_SETESI: | ||
348 | case ATM_SETESIF: | ||
349 | case ATM_GETSTAT: | ||
350 | case ATM_GETSTATZ: | ||
351 | case ATM_GETLOOP: | ||
352 | case ATM_SETLOOP: | ||
353 | case ATM_QUERYLOOP: | ||
354 | return do_atmif_sioc(sock, cmd, arg); | ||
355 | } | ||
356 | |||
357 | return -EINVAL; | ||
358 | } | ||
359 | |||
360 | int vcc_compat_ioctl(struct socket *sock, unsigned int cmd, | ||
361 | unsigned long arg) | ||
362 | { | ||
363 | int ret; | ||
364 | |||
365 | ret = do_vcc_ioctl(sock, cmd, arg, 1); | ||
366 | if (ret != -ENOIOCTLCMD) | ||
367 | return ret; | ||
368 | |||
369 | return do_atm_ioctl(sock, cmd, arg); | ||
197 | } | 370 | } |
198 | #endif | 371 | #endif |