]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* drivers/atm/atmtcp.c - ATM over TCP "device" driver */ |
2 | ||
3 | /* Written 1997-2000 by Werner Almesberger, EPFL LRC/ICA */ | |
4 | ||
5 | ||
6 | #include <linux/module.h> | |
7 | #include <linux/wait.h> | |
8 | #include <linux/atmdev.h> | |
9 | #include <linux/atm_tcp.h> | |
10 | #include <linux/bitops.h> | |
11 | #include <linux/init.h> | |
5a0e3ad6 | 12 | #include <linux/slab.h> |
7c0f6ba6 | 13 | #include <linux/uaccess.h> |
60063497 | 14 | #include <linux/atomic.h> |
1da177e4 LT |
15 | |
16 | ||
17 | extern int atm_init_aal5(struct atm_vcc *vcc); /* "raw" AAL5 transport */ | |
18 | ||
19 | ||
20 | #define PRIV(dev) ((struct atmtcp_dev_data *) ((dev)->dev_data)) | |
21 | ||
22 | ||
23 | struct atmtcp_dev_data { | |
24 | struct atm_vcc *vcc; /* control VCC; NULL if detached */ | |
25 | int persist; /* non-zero if persistent */ | |
26 | }; | |
27 | ||
28 | ||
29 | #define DEV_LABEL "atmtcp" | |
30 | ||
31 | #define MAX_VPI_BITS 8 /* simplifies life */ | |
32 | #define MAX_VCI_BITS 16 | |
33 | ||
34 | ||
35 | /* | |
36 | * Hairy code ahead: the control VCC may be closed while we're still | |
37 | * waiting for an answer, so we need to re-validate out_vcc every once | |
38 | * in a while. | |
39 | */ | |
40 | ||
41 | ||
42 | static int atmtcp_send_control(struct atm_vcc *vcc,int type, | |
43 | const struct atmtcp_control *msg,int flag) | |
44 | { | |
45 | DECLARE_WAITQUEUE(wait,current); | |
46 | struct atm_vcc *out_vcc; | |
47 | struct sk_buff *skb; | |
48 | struct atmtcp_control *new_msg; | |
49 | int old_test; | |
50 | int error = 0; | |
51 | ||
52 | out_vcc = PRIV(vcc->dev) ? PRIV(vcc->dev)->vcc : NULL; | |
53 | if (!out_vcc) return -EUNATCH; | |
54 | skb = alloc_skb(sizeof(*msg),GFP_KERNEL); | |
55 | if (!skb) return -ENOMEM; | |
56 | mb(); | |
57 | out_vcc = PRIV(vcc->dev) ? PRIV(vcc->dev)->vcc : NULL; | |
58 | if (!out_vcc) { | |
59 | dev_kfree_skb(skb); | |
60 | return -EUNATCH; | |
61 | } | |
62 | atm_force_charge(out_vcc,skb->truesize); | |
4df864c1 | 63 | new_msg = skb_put(skb, sizeof(*new_msg)); |
1da177e4 LT |
64 | *new_msg = *msg; |
65 | new_msg->hdr.length = ATMTCP_HDR_MAGIC; | |
66 | new_msg->type = type; | |
67 | memset(&new_msg->vcc,0,sizeof(atm_kptr_t)); | |
68 | *(struct atm_vcc **) &new_msg->vcc = vcc; | |
69 | old_test = test_bit(flag,&vcc->flags); | |
70 | out_vcc->push(out_vcc,skb); | |
aa395145 | 71 | add_wait_queue(sk_sleep(sk_atm(vcc)), &wait); |
1da177e4 LT |
72 | while (test_bit(flag,&vcc->flags) == old_test) { |
73 | mb(); | |
74 | out_vcc = PRIV(vcc->dev) ? PRIV(vcc->dev)->vcc : NULL; | |
75 | if (!out_vcc) { | |
76 | error = -EUNATCH; | |
77 | break; | |
78 | } | |
79 | set_current_state(TASK_UNINTERRUPTIBLE); | |
80 | schedule(); | |
81 | } | |
82 | set_current_state(TASK_RUNNING); | |
aa395145 | 83 | remove_wait_queue(sk_sleep(sk_atm(vcc)), &wait); |
1da177e4 LT |
84 | return error; |
85 | } | |
86 | ||
87 | ||
88 | static int atmtcp_recv_control(const struct atmtcp_control *msg) | |
89 | { | |
90 | struct atm_vcc *vcc = *(struct atm_vcc **) &msg->vcc; | |
91 | ||
92 | vcc->vpi = msg->addr.sap_addr.vpi; | |
93 | vcc->vci = msg->addr.sap_addr.vci; | |
94 | vcc->qos = msg->qos; | |
95 | sk_atm(vcc)->sk_err = -msg->result; | |
96 | switch (msg->type) { | |
97 | case ATMTCP_CTRL_OPEN: | |
98 | change_bit(ATM_VF_READY,&vcc->flags); | |
99 | break; | |
100 | case ATMTCP_CTRL_CLOSE: | |
101 | change_bit(ATM_VF_ADDR,&vcc->flags); | |
102 | break; | |
103 | default: | |
104 | printk(KERN_ERR "atmtcp_recv_control: unknown type %d\n", | |
105 | msg->type); | |
106 | return -EINVAL; | |
107 | } | |
aa395145 | 108 | wake_up(sk_sleep(sk_atm(vcc))); |
1da177e4 LT |
109 | return 0; |
110 | } | |
111 | ||
112 | ||
113 | static void atmtcp_v_dev_close(struct atm_dev *dev) | |
114 | { | |
115 | /* Nothing.... Isn't this simple :-) -- REW */ | |
116 | } | |
117 | ||
118 | ||
119 | static int atmtcp_v_open(struct atm_vcc *vcc) | |
120 | { | |
121 | struct atmtcp_control msg; | |
122 | int error; | |
123 | short vpi = vcc->vpi; | |
124 | int vci = vcc->vci; | |
125 | ||
126 | memset(&msg,0,sizeof(msg)); | |
127 | msg.addr.sap_family = AF_ATMPVC; | |
128 | msg.hdr.vpi = htons(vpi); | |
129 | msg.addr.sap_addr.vpi = vpi; | |
130 | msg.hdr.vci = htons(vci); | |
131 | msg.addr.sap_addr.vci = vci; | |
132 | if (vpi == ATM_VPI_UNSPEC || vci == ATM_VCI_UNSPEC) return 0; | |
133 | msg.type = ATMTCP_CTRL_OPEN; | |
134 | msg.qos = vcc->qos; | |
135 | set_bit(ATM_VF_ADDR,&vcc->flags); | |
136 | clear_bit(ATM_VF_READY,&vcc->flags); /* just in case ... */ | |
137 | error = atmtcp_send_control(vcc,ATMTCP_CTRL_OPEN,&msg,ATM_VF_READY); | |
138 | if (error) return error; | |
139 | return -sk_atm(vcc)->sk_err; | |
140 | } | |
141 | ||
142 | ||
143 | static void atmtcp_v_close(struct atm_vcc *vcc) | |
144 | { | |
145 | struct atmtcp_control msg; | |
146 | ||
147 | memset(&msg,0,sizeof(msg)); | |
148 | msg.addr.sap_family = AF_ATMPVC; | |
149 | msg.addr.sap_addr.vpi = vcc->vpi; | |
150 | msg.addr.sap_addr.vci = vcc->vci; | |
151 | clear_bit(ATM_VF_READY,&vcc->flags); | |
152 | (void) atmtcp_send_control(vcc,ATMTCP_CTRL_CLOSE,&msg,ATM_VF_ADDR); | |
153 | } | |
154 | ||
155 | ||
156 | static int atmtcp_v_ioctl(struct atm_dev *dev,unsigned int cmd,void __user *arg) | |
157 | { | |
158 | struct atm_cirange ci; | |
159 | struct atm_vcc *vcc; | |
1da177e4 LT |
160 | struct sock *s; |
161 | int i; | |
162 | ||
163 | if (cmd != ATM_SETCIRANGE) return -ENOIOCTLCMD; | |
164 | if (copy_from_user(&ci, arg,sizeof(ci))) return -EFAULT; | |
165 | if (ci.vpi_bits == ATM_CI_MAX) ci.vpi_bits = MAX_VPI_BITS; | |
166 | if (ci.vci_bits == ATM_CI_MAX) ci.vci_bits = MAX_VCI_BITS; | |
167 | if (ci.vpi_bits > MAX_VPI_BITS || ci.vpi_bits < 0 || | |
168 | ci.vci_bits > MAX_VCI_BITS || ci.vci_bits < 0) return -EINVAL; | |
169 | read_lock(&vcc_sklist_lock); | |
170 | for(i = 0; i < VCC_HTABLE_SIZE; ++i) { | |
171 | struct hlist_head *head = &vcc_hash[i]; | |
172 | ||
b67bfe0d | 173 | sk_for_each(s, head) { |
1da177e4 LT |
174 | vcc = atm_sk(s); |
175 | if (vcc->dev != dev) | |
176 | continue; | |
177 | if ((vcc->vpi >> ci.vpi_bits) || | |
178 | (vcc->vci >> ci.vci_bits)) { | |
179 | read_unlock(&vcc_sklist_lock); | |
180 | return -EBUSY; | |
181 | } | |
182 | } | |
183 | } | |
184 | read_unlock(&vcc_sklist_lock); | |
185 | dev->ci_range = ci; | |
186 | return 0; | |
187 | } | |
188 | ||
189 | ||
190 | static int atmtcp_v_send(struct atm_vcc *vcc,struct sk_buff *skb) | |
191 | { | |
192 | struct atmtcp_dev_data *dev_data; | |
193 | struct atm_vcc *out_vcc=NULL; /* Initializer quietens GCC warning */ | |
194 | struct sk_buff *new_skb; | |
195 | struct atmtcp_hdr *hdr; | |
196 | int size; | |
197 | ||
198 | if (vcc->qos.txtp.traffic_class == ATM_NONE) { | |
199 | if (vcc->pop) vcc->pop(vcc,skb); | |
200 | else dev_kfree_skb(skb); | |
201 | return -EINVAL; | |
202 | } | |
203 | dev_data = PRIV(vcc->dev); | |
204 | if (dev_data) out_vcc = dev_data->vcc; | |
205 | if (!dev_data || !out_vcc) { | |
206 | if (vcc->pop) vcc->pop(vcc,skb); | |
207 | else dev_kfree_skb(skb); | |
208 | if (dev_data) return 0; | |
209 | atomic_inc(&vcc->stats->tx_err); | |
210 | return -ENOLINK; | |
211 | } | |
212 | size = skb->len+sizeof(struct atmtcp_hdr); | |
213 | new_skb = atm_alloc_charge(out_vcc,size,GFP_ATOMIC); | |
214 | if (!new_skb) { | |
215 | if (vcc->pop) vcc->pop(vcc,skb); | |
216 | else dev_kfree_skb(skb); | |
217 | atomic_inc(&vcc->stats->tx_err); | |
218 | return -ENOBUFS; | |
219 | } | |
4df864c1 | 220 | hdr = skb_put(new_skb, sizeof(struct atmtcp_hdr)); |
1da177e4 LT |
221 | hdr->vpi = htons(vcc->vpi); |
222 | hdr->vci = htons(vcc->vci); | |
223 | hdr->length = htonl(skb->len); | |
d626f62b | 224 | skb_copy_from_linear_data(skb, skb_put(new_skb, skb->len), skb->len); |
1da177e4 LT |
225 | if (vcc->pop) vcc->pop(vcc,skb); |
226 | else dev_kfree_skb(skb); | |
227 | out_vcc->push(out_vcc,new_skb); | |
228 | atomic_inc(&vcc->stats->tx); | |
229 | atomic_inc(&out_vcc->stats->rx); | |
230 | return 0; | |
231 | } | |
232 | ||
233 | ||
234 | static int atmtcp_v_proc(struct atm_dev *dev,loff_t *pos,char *page) | |
235 | { | |
236 | struct atmtcp_dev_data *dev_data = PRIV(dev); | |
237 | ||
238 | if (*pos) return 0; | |
239 | if (!dev_data->persist) return sprintf(page,"ephemeral\n"); | |
240 | return sprintf(page,"persistent, %sconnected\n", | |
241 | dev_data->vcc ? "" : "dis"); | |
242 | } | |
243 | ||
244 | ||
245 | static void atmtcp_c_close(struct atm_vcc *vcc) | |
246 | { | |
247 | struct atm_dev *atmtcp_dev; | |
248 | struct atmtcp_dev_data *dev_data; | |
1da177e4 LT |
249 | |
250 | atmtcp_dev = (struct atm_dev *) vcc->dev_data; | |
251 | dev_data = PRIV(atmtcp_dev); | |
252 | dev_data->vcc = NULL; | |
253 | if (dev_data->persist) return; | |
254 | atmtcp_dev->dev_data = NULL; | |
255 | kfree(dev_data); | |
64bf69dd | 256 | atm_dev_deregister(atmtcp_dev); |
1da177e4 | 257 | vcc->dev_data = NULL; |
1da177e4 LT |
258 | module_put(THIS_MODULE); |
259 | } | |
260 | ||
261 | ||
262 | static struct atm_vcc *find_vcc(struct atm_dev *dev, short vpi, int vci) | |
263 | { | |
264 | struct hlist_head *head; | |
265 | struct atm_vcc *vcc; | |
1da177e4 LT |
266 | struct sock *s; |
267 | ||
268 | head = &vcc_hash[vci & (VCC_HTABLE_SIZE -1)]; | |
269 | ||
b67bfe0d | 270 | sk_for_each(s, head) { |
1da177e4 LT |
271 | vcc = atm_sk(s); |
272 | if (vcc->dev == dev && | |
273 | vcc->vci == vci && vcc->vpi == vpi && | |
274 | vcc->qos.rxtp.traffic_class != ATM_NONE) { | |
275 | return vcc; | |
276 | } | |
277 | } | |
278 | return NULL; | |
279 | } | |
280 | ||
281 | ||
282 | static int atmtcp_c_send(struct atm_vcc *vcc,struct sk_buff *skb) | |
283 | { | |
284 | struct atm_dev *dev; | |
285 | struct atmtcp_hdr *hdr; | |
286 | struct atm_vcc *out_vcc; | |
287 | struct sk_buff *new_skb; | |
288 | int result = 0; | |
289 | ||
290 | if (!skb->len) return 0; | |
291 | dev = vcc->dev_data; | |
292 | hdr = (struct atmtcp_hdr *) skb->data; | |
293 | if (hdr->length == ATMTCP_HDR_MAGIC) { | |
294 | result = atmtcp_recv_control( | |
295 | (struct atmtcp_control *) skb->data); | |
296 | goto done; | |
297 | } | |
298 | read_lock(&vcc_sklist_lock); | |
299 | out_vcc = find_vcc(dev, ntohs(hdr->vpi), ntohs(hdr->vci)); | |
300 | read_unlock(&vcc_sklist_lock); | |
301 | if (!out_vcc) { | |
e5a98fc2 | 302 | result = -EUNATCH; |
1da177e4 LT |
303 | atomic_inc(&vcc->stats->tx_err); |
304 | goto done; | |
305 | } | |
306 | skb_pull(skb,sizeof(struct atmtcp_hdr)); | |
307 | new_skb = atm_alloc_charge(out_vcc,skb->len,GFP_KERNEL); | |
308 | if (!new_skb) { | |
309 | result = -ENOBUFS; | |
310 | goto done; | |
311 | } | |
a61bbcf2 | 312 | __net_timestamp(new_skb); |
d626f62b | 313 | skb_copy_from_linear_data(skb, skb_put(new_skb, skb->len), skb->len); |
1da177e4 LT |
314 | out_vcc->push(out_vcc,new_skb); |
315 | atomic_inc(&vcc->stats->tx); | |
316 | atomic_inc(&out_vcc->stats->rx); | |
317 | done: | |
318 | if (vcc->pop) vcc->pop(vcc,skb); | |
319 | else dev_kfree_skb(skb); | |
320 | return result; | |
321 | } | |
322 | ||
323 | ||
324 | /* | |
325 | * Device operations for the virtual ATM devices created by ATMTCP. | |
326 | */ | |
327 | ||
328 | ||
329 | static struct atmdev_ops atmtcp_v_dev_ops = { | |
330 | .dev_close = atmtcp_v_dev_close, | |
331 | .open = atmtcp_v_open, | |
332 | .close = atmtcp_v_close, | |
333 | .ioctl = atmtcp_v_ioctl, | |
334 | .send = atmtcp_v_send, | |
335 | .proc_read = atmtcp_v_proc, | |
336 | .owner = THIS_MODULE | |
337 | }; | |
338 | ||
339 | ||
340 | /* | |
341 | * Device operations for the ATMTCP control device. | |
342 | */ | |
343 | ||
344 | ||
46c4b7a5 | 345 | static const struct atmdev_ops atmtcp_c_dev_ops = { |
1da177e4 LT |
346 | .close = atmtcp_c_close, |
347 | .send = atmtcp_c_send | |
348 | }; | |
349 | ||
350 | ||
351 | static struct atm_dev atmtcp_control_dev = { | |
352 | .ops = &atmtcp_c_dev_ops, | |
353 | .type = "atmtcp", | |
354 | .number = 999, | |
4ef8d0ae | 355 | .lock = __SPIN_LOCK_UNLOCKED(atmtcp_control_dev.lock) |
1da177e4 LT |
356 | }; |
357 | ||
358 | ||
359 | static int atmtcp_create(int itf,int persist,struct atm_dev **result) | |
360 | { | |
361 | struct atmtcp_dev_data *dev_data; | |
362 | struct atm_dev *dev; | |
363 | ||
364 | dev_data = kmalloc(sizeof(*dev_data),GFP_KERNEL); | |
365 | if (!dev_data) | |
366 | return -ENOMEM; | |
367 | ||
d9ca676b | 368 | dev = atm_dev_register(DEV_LABEL,NULL,&atmtcp_v_dev_ops,itf,NULL); |
1da177e4 LT |
369 | if (!dev) { |
370 | kfree(dev_data); | |
371 | return itf == -1 ? -ENOMEM : -EBUSY; | |
372 | } | |
373 | dev->ci_range.vpi_bits = MAX_VPI_BITS; | |
374 | dev->ci_range.vci_bits = MAX_VCI_BITS; | |
375 | dev->dev_data = dev_data; | |
376 | PRIV(dev)->vcc = NULL; | |
377 | PRIV(dev)->persist = persist; | |
378 | if (result) *result = dev; | |
379 | return 0; | |
380 | } | |
381 | ||
382 | ||
383 | static int atmtcp_attach(struct atm_vcc *vcc,int itf) | |
384 | { | |
385 | struct atm_dev *dev; | |
386 | ||
387 | dev = NULL; | |
388 | if (itf != -1) dev = atm_dev_lookup(itf); | |
389 | if (dev) { | |
390 | if (dev->ops != &atmtcp_v_dev_ops) { | |
391 | atm_dev_put(dev); | |
392 | return -EMEDIUMTYPE; | |
393 | } | |
b9556f9a JL |
394 | if (PRIV(dev)->vcc) { |
395 | atm_dev_put(dev); | |
396 | return -EBUSY; | |
397 | } | |
1da177e4 LT |
398 | } |
399 | else { | |
400 | int error; | |
401 | ||
402 | error = atmtcp_create(itf,0,&dev); | |
403 | if (error) return error; | |
404 | } | |
405 | PRIV(dev)->vcc = vcc; | |
406 | vcc->dev = &atmtcp_control_dev; | |
407 | vcc_insert_socket(sk_atm(vcc)); | |
408 | set_bit(ATM_VF_META,&vcc->flags); | |
409 | set_bit(ATM_VF_READY,&vcc->flags); | |
410 | vcc->dev_data = dev; | |
411 | (void) atm_init_aal5(vcc); /* @@@ losing AAL in transit ... */ | |
412 | vcc->stats = &atmtcp_control_dev.stats.aal5; | |
413 | return dev->number; | |
414 | } | |
415 | ||
416 | ||
417 | static int atmtcp_create_persistent(int itf) | |
418 | { | |
419 | return atmtcp_create(itf,1,NULL); | |
420 | } | |
421 | ||
422 | ||
423 | static int atmtcp_remove_persistent(int itf) | |
424 | { | |
425 | struct atm_dev *dev; | |
426 | struct atmtcp_dev_data *dev_data; | |
427 | ||
428 | dev = atm_dev_lookup(itf); | |
429 | if (!dev) return -ENODEV; | |
430 | if (dev->ops != &atmtcp_v_dev_ops) { | |
431 | atm_dev_put(dev); | |
432 | return -EMEDIUMTYPE; | |
433 | } | |
434 | dev_data = PRIV(dev); | |
435 | if (!dev_data->persist) return 0; | |
436 | dev_data->persist = 0; | |
437 | if (PRIV(dev)->vcc) return 0; | |
438 | kfree(dev_data); | |
439 | atm_dev_put(dev); | |
64bf69dd | 440 | atm_dev_deregister(dev); |
1da177e4 LT |
441 | return 0; |
442 | } | |
443 | ||
444 | static int atmtcp_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) | |
445 | { | |
446 | int err = 0; | |
447 | struct atm_vcc *vcc = ATM_SD(sock); | |
448 | ||
449 | if (cmd != SIOCSIFATMTCP && cmd != ATMTCP_CREATE && cmd != ATMTCP_REMOVE) | |
450 | return -ENOIOCTLCMD; | |
451 | ||
452 | if (!capable(CAP_NET_ADMIN)) | |
453 | return -EPERM; | |
454 | ||
455 | switch (cmd) { | |
456 | case SIOCSIFATMTCP: | |
457 | err = atmtcp_attach(vcc, (int) arg); | |
458 | if (err >= 0) { | |
459 | sock->state = SS_CONNECTED; | |
460 | __module_get(THIS_MODULE); | |
461 | } | |
462 | break; | |
463 | case ATMTCP_CREATE: | |
464 | err = atmtcp_create_persistent((int) arg); | |
465 | break; | |
466 | case ATMTCP_REMOVE: | |
467 | err = atmtcp_remove_persistent((int) arg); | |
468 | break; | |
469 | } | |
470 | return err; | |
471 | } | |
472 | ||
473 | static struct atm_ioctl atmtcp_ioctl_ops = { | |
474 | .owner = THIS_MODULE, | |
475 | .ioctl = atmtcp_ioctl, | |
476 | }; | |
477 | ||
478 | static __init int atmtcp_init(void) | |
479 | { | |
480 | register_atm_ioctl(&atmtcp_ioctl_ops); | |
481 | return 0; | |
482 | } | |
483 | ||
484 | ||
485 | static void __exit atmtcp_exit(void) | |
486 | { | |
487 | deregister_atm_ioctl(&atmtcp_ioctl_ops); | |
488 | } | |
489 | ||
490 | MODULE_LICENSE("GPL"); | |
491 | module_init(atmtcp_init); | |
492 | module_exit(atmtcp_exit); |