diff options
Diffstat (limited to 'drivers/atm/atmtcp.c')
-rw-r--r-- | drivers/atm/atmtcp.c | 505 |
1 files changed, 505 insertions, 0 deletions
diff --git a/drivers/atm/atmtcp.c b/drivers/atm/atmtcp.c new file mode 100644 index 000000000000..f2f01cb82cb4 --- /dev/null +++ b/drivers/atm/atmtcp.c | |||
@@ -0,0 +1,505 @@ | |||
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> | ||
12 | #include <asm/uaccess.h> | ||
13 | #include <asm/atomic.h> | ||
14 | |||
15 | |||
16 | extern int atm_init_aal5(struct atm_vcc *vcc); /* "raw" AAL5 transport */ | ||
17 | |||
18 | |||
19 | #define PRIV(dev) ((struct atmtcp_dev_data *) ((dev)->dev_data)) | ||
20 | |||
21 | |||
22 | struct atmtcp_dev_data { | ||
23 | struct atm_vcc *vcc; /* control VCC; NULL if detached */ | ||
24 | int persist; /* non-zero if persistent */ | ||
25 | }; | ||
26 | |||
27 | |||
28 | #define DEV_LABEL "atmtcp" | ||
29 | |||
30 | #define MAX_VPI_BITS 8 /* simplifies life */ | ||
31 | #define MAX_VCI_BITS 16 | ||
32 | |||
33 | |||
34 | /* | ||
35 | * Hairy code ahead: the control VCC may be closed while we're still | ||
36 | * waiting for an answer, so we need to re-validate out_vcc every once | ||
37 | * in a while. | ||
38 | */ | ||
39 | |||
40 | |||
41 | static int atmtcp_send_control(struct atm_vcc *vcc,int type, | ||
42 | const struct atmtcp_control *msg,int flag) | ||
43 | { | ||
44 | DECLARE_WAITQUEUE(wait,current); | ||
45 | struct atm_vcc *out_vcc; | ||
46 | struct sk_buff *skb; | ||
47 | struct atmtcp_control *new_msg; | ||
48 | int old_test; | ||
49 | int error = 0; | ||
50 | |||
51 | out_vcc = PRIV(vcc->dev) ? PRIV(vcc->dev)->vcc : NULL; | ||
52 | if (!out_vcc) return -EUNATCH; | ||
53 | skb = alloc_skb(sizeof(*msg),GFP_KERNEL); | ||
54 | if (!skb) return -ENOMEM; | ||
55 | mb(); | ||
56 | out_vcc = PRIV(vcc->dev) ? PRIV(vcc->dev)->vcc : NULL; | ||
57 | if (!out_vcc) { | ||
58 | dev_kfree_skb(skb); | ||
59 | return -EUNATCH; | ||
60 | } | ||
61 | atm_force_charge(out_vcc,skb->truesize); | ||
62 | new_msg = (struct atmtcp_control *) skb_put(skb,sizeof(*new_msg)); | ||
63 | *new_msg = *msg; | ||
64 | new_msg->hdr.length = ATMTCP_HDR_MAGIC; | ||
65 | new_msg->type = type; | ||
66 | memset(&new_msg->vcc,0,sizeof(atm_kptr_t)); | ||
67 | *(struct atm_vcc **) &new_msg->vcc = vcc; | ||
68 | old_test = test_bit(flag,&vcc->flags); | ||
69 | out_vcc->push(out_vcc,skb); | ||
70 | add_wait_queue(sk_atm(vcc)->sk_sleep, &wait); | ||
71 | while (test_bit(flag,&vcc->flags) == old_test) { | ||
72 | mb(); | ||
73 | out_vcc = PRIV(vcc->dev) ? PRIV(vcc->dev)->vcc : NULL; | ||
74 | if (!out_vcc) { | ||
75 | error = -EUNATCH; | ||
76 | break; | ||
77 | } | ||
78 | set_current_state(TASK_UNINTERRUPTIBLE); | ||
79 | schedule(); | ||
80 | } | ||
81 | set_current_state(TASK_RUNNING); | ||
82 | remove_wait_queue(sk_atm(vcc)->sk_sleep, &wait); | ||
83 | return error; | ||
84 | } | ||
85 | |||
86 | |||
87 | static int atmtcp_recv_control(const struct atmtcp_control *msg) | ||
88 | { | ||
89 | struct atm_vcc *vcc = *(struct atm_vcc **) &msg->vcc; | ||
90 | |||
91 | vcc->vpi = msg->addr.sap_addr.vpi; | ||
92 | vcc->vci = msg->addr.sap_addr.vci; | ||
93 | vcc->qos = msg->qos; | ||
94 | sk_atm(vcc)->sk_err = -msg->result; | ||
95 | switch (msg->type) { | ||
96 | case ATMTCP_CTRL_OPEN: | ||
97 | change_bit(ATM_VF_READY,&vcc->flags); | ||
98 | break; | ||
99 | case ATMTCP_CTRL_CLOSE: | ||
100 | change_bit(ATM_VF_ADDR,&vcc->flags); | ||
101 | break; | ||
102 | default: | ||
103 | printk(KERN_ERR "atmtcp_recv_control: unknown type %d\n", | ||
104 | msg->type); | ||
105 | return -EINVAL; | ||
106 | } | ||
107 | wake_up(sk_atm(vcc)->sk_sleep); | ||
108 | return 0; | ||
109 | } | ||
110 | |||
111 | |||
112 | static void atmtcp_v_dev_close(struct atm_dev *dev) | ||
113 | { | ||
114 | /* Nothing.... Isn't this simple :-) -- REW */ | ||
115 | } | ||
116 | |||
117 | |||
118 | static int atmtcp_v_open(struct atm_vcc *vcc) | ||
119 | { | ||
120 | struct atmtcp_control msg; | ||
121 | int error; | ||
122 | short vpi = vcc->vpi; | ||
123 | int vci = vcc->vci; | ||
124 | |||
125 | memset(&msg,0,sizeof(msg)); | ||
126 | msg.addr.sap_family = AF_ATMPVC; | ||
127 | msg.hdr.vpi = htons(vpi); | ||
128 | msg.addr.sap_addr.vpi = vpi; | ||
129 | msg.hdr.vci = htons(vci); | ||
130 | msg.addr.sap_addr.vci = vci; | ||
131 | if (vpi == ATM_VPI_UNSPEC || vci == ATM_VCI_UNSPEC) return 0; | ||
132 | msg.type = ATMTCP_CTRL_OPEN; | ||
133 | msg.qos = vcc->qos; | ||
134 | set_bit(ATM_VF_ADDR,&vcc->flags); | ||
135 | clear_bit(ATM_VF_READY,&vcc->flags); /* just in case ... */ | ||
136 | error = atmtcp_send_control(vcc,ATMTCP_CTRL_OPEN,&msg,ATM_VF_READY); | ||
137 | if (error) return error; | ||
138 | return -sk_atm(vcc)->sk_err; | ||
139 | } | ||
140 | |||
141 | |||
142 | static void atmtcp_v_close(struct atm_vcc *vcc) | ||
143 | { | ||
144 | struct atmtcp_control msg; | ||
145 | |||
146 | memset(&msg,0,sizeof(msg)); | ||
147 | msg.addr.sap_family = AF_ATMPVC; | ||
148 | msg.addr.sap_addr.vpi = vcc->vpi; | ||
149 | msg.addr.sap_addr.vci = vcc->vci; | ||
150 | clear_bit(ATM_VF_READY,&vcc->flags); | ||
151 | (void) atmtcp_send_control(vcc,ATMTCP_CTRL_CLOSE,&msg,ATM_VF_ADDR); | ||
152 | } | ||
153 | |||
154 | |||
155 | static int atmtcp_v_ioctl(struct atm_dev *dev,unsigned int cmd,void __user *arg) | ||
156 | { | ||
157 | struct atm_cirange ci; | ||
158 | struct atm_vcc *vcc; | ||
159 | struct hlist_node *node; | ||
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 | |||
173 | sk_for_each(s, node, head) { | ||
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 | } | ||
220 | hdr = (void *) skb_put(new_skb,sizeof(struct atmtcp_hdr)); | ||
221 | hdr->vpi = htons(vcc->vpi); | ||
222 | hdr->vci = htons(vcc->vci); | ||
223 | hdr->length = htonl(skb->len); | ||
224 | memcpy(skb_put(new_skb,skb->len),skb->data,skb->len); | ||
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; | ||
249 | struct sock *s; | ||
250 | struct hlist_node *node; | ||
251 | struct atm_vcc *walk; | ||
252 | int i; | ||
253 | |||
254 | atmtcp_dev = (struct atm_dev *) vcc->dev_data; | ||
255 | dev_data = PRIV(atmtcp_dev); | ||
256 | dev_data->vcc = NULL; | ||
257 | if (dev_data->persist) return; | ||
258 | atmtcp_dev->dev_data = NULL; | ||
259 | kfree(dev_data); | ||
260 | shutdown_atm_dev(atmtcp_dev); | ||
261 | vcc->dev_data = NULL; | ||
262 | read_lock(&vcc_sklist_lock); | ||
263 | for(i = 0; i < VCC_HTABLE_SIZE; ++i) { | ||
264 | struct hlist_head *head = &vcc_hash[i]; | ||
265 | |||
266 | sk_for_each(s, node, head) { | ||
267 | walk = atm_sk(s); | ||
268 | if (walk->dev != atmtcp_dev) | ||
269 | continue; | ||
270 | wake_up(s->sk_sleep); | ||
271 | } | ||
272 | } | ||
273 | read_unlock(&vcc_sklist_lock); | ||
274 | module_put(THIS_MODULE); | ||
275 | } | ||
276 | |||
277 | |||
278 | static struct atm_vcc *find_vcc(struct atm_dev *dev, short vpi, int vci) | ||
279 | { | ||
280 | struct hlist_head *head; | ||
281 | struct atm_vcc *vcc; | ||
282 | struct hlist_node *node; | ||
283 | struct sock *s; | ||
284 | |||
285 | head = &vcc_hash[vci & (VCC_HTABLE_SIZE -1)]; | ||
286 | |||
287 | sk_for_each(s, node, head) { | ||
288 | vcc = atm_sk(s); | ||
289 | if (vcc->dev == dev && | ||
290 | vcc->vci == vci && vcc->vpi == vpi && | ||
291 | vcc->qos.rxtp.traffic_class != ATM_NONE) { | ||
292 | return vcc; | ||
293 | } | ||
294 | } | ||
295 | return NULL; | ||
296 | } | ||
297 | |||
298 | |||
299 | static int atmtcp_c_send(struct atm_vcc *vcc,struct sk_buff *skb) | ||
300 | { | ||
301 | struct atm_dev *dev; | ||
302 | struct atmtcp_hdr *hdr; | ||
303 | struct atm_vcc *out_vcc; | ||
304 | struct sk_buff *new_skb; | ||
305 | int result = 0; | ||
306 | |||
307 | if (!skb->len) return 0; | ||
308 | dev = vcc->dev_data; | ||
309 | hdr = (struct atmtcp_hdr *) skb->data; | ||
310 | if (hdr->length == ATMTCP_HDR_MAGIC) { | ||
311 | result = atmtcp_recv_control( | ||
312 | (struct atmtcp_control *) skb->data); | ||
313 | goto done; | ||
314 | } | ||
315 | read_lock(&vcc_sklist_lock); | ||
316 | out_vcc = find_vcc(dev, ntohs(hdr->vpi), ntohs(hdr->vci)); | ||
317 | read_unlock(&vcc_sklist_lock); | ||
318 | if (!out_vcc) { | ||
319 | atomic_inc(&vcc->stats->tx_err); | ||
320 | goto done; | ||
321 | } | ||
322 | skb_pull(skb,sizeof(struct atmtcp_hdr)); | ||
323 | new_skb = atm_alloc_charge(out_vcc,skb->len,GFP_KERNEL); | ||
324 | if (!new_skb) { | ||
325 | result = -ENOBUFS; | ||
326 | goto done; | ||
327 | } | ||
328 | do_gettimeofday(&new_skb->stamp); | ||
329 | memcpy(skb_put(new_skb,skb->len),skb->data,skb->len); | ||
330 | out_vcc->push(out_vcc,new_skb); | ||
331 | atomic_inc(&vcc->stats->tx); | ||
332 | atomic_inc(&out_vcc->stats->rx); | ||
333 | done: | ||
334 | if (vcc->pop) vcc->pop(vcc,skb); | ||
335 | else dev_kfree_skb(skb); | ||
336 | return result; | ||
337 | } | ||
338 | |||
339 | |||
340 | /* | ||
341 | * Device operations for the virtual ATM devices created by ATMTCP. | ||
342 | */ | ||
343 | |||
344 | |||
345 | static struct atmdev_ops atmtcp_v_dev_ops = { | ||
346 | .dev_close = atmtcp_v_dev_close, | ||
347 | .open = atmtcp_v_open, | ||
348 | .close = atmtcp_v_close, | ||
349 | .ioctl = atmtcp_v_ioctl, | ||
350 | .send = atmtcp_v_send, | ||
351 | .proc_read = atmtcp_v_proc, | ||
352 | .owner = THIS_MODULE | ||
353 | }; | ||
354 | |||
355 | |||
356 | /* | ||
357 | * Device operations for the ATMTCP control device. | ||
358 | */ | ||
359 | |||
360 | |||
361 | static struct atmdev_ops atmtcp_c_dev_ops = { | ||
362 | .close = atmtcp_c_close, | ||
363 | .send = atmtcp_c_send | ||
364 | }; | ||
365 | |||
366 | |||
367 | static struct atm_dev atmtcp_control_dev = { | ||
368 | .ops = &atmtcp_c_dev_ops, | ||
369 | .type = "atmtcp", | ||
370 | .number = 999, | ||
371 | .lock = SPIN_LOCK_UNLOCKED | ||
372 | }; | ||
373 | |||
374 | |||
375 | static int atmtcp_create(int itf,int persist,struct atm_dev **result) | ||
376 | { | ||
377 | struct atmtcp_dev_data *dev_data; | ||
378 | struct atm_dev *dev; | ||
379 | |||
380 | dev_data = kmalloc(sizeof(*dev_data),GFP_KERNEL); | ||
381 | if (!dev_data) | ||
382 | return -ENOMEM; | ||
383 | |||
384 | dev = atm_dev_register(DEV_LABEL,&atmtcp_v_dev_ops,itf,NULL); | ||
385 | if (!dev) { | ||
386 | kfree(dev_data); | ||
387 | return itf == -1 ? -ENOMEM : -EBUSY; | ||
388 | } | ||
389 | dev->ci_range.vpi_bits = MAX_VPI_BITS; | ||
390 | dev->ci_range.vci_bits = MAX_VCI_BITS; | ||
391 | dev->dev_data = dev_data; | ||
392 | PRIV(dev)->vcc = NULL; | ||
393 | PRIV(dev)->persist = persist; | ||
394 | if (result) *result = dev; | ||
395 | return 0; | ||
396 | } | ||
397 | |||
398 | |||
399 | static int atmtcp_attach(struct atm_vcc *vcc,int itf) | ||
400 | { | ||
401 | struct atm_dev *dev; | ||
402 | |||
403 | dev = NULL; | ||
404 | if (itf != -1) dev = atm_dev_lookup(itf); | ||
405 | if (dev) { | ||
406 | if (dev->ops != &atmtcp_v_dev_ops) { | ||
407 | atm_dev_put(dev); | ||
408 | return -EMEDIUMTYPE; | ||
409 | } | ||
410 | if (PRIV(dev)->vcc) return -EBUSY; | ||
411 | } | ||
412 | else { | ||
413 | int error; | ||
414 | |||
415 | error = atmtcp_create(itf,0,&dev); | ||
416 | if (error) return error; | ||
417 | } | ||
418 | PRIV(dev)->vcc = vcc; | ||
419 | vcc->dev = &atmtcp_control_dev; | ||
420 | vcc_insert_socket(sk_atm(vcc)); | ||
421 | set_bit(ATM_VF_META,&vcc->flags); | ||
422 | set_bit(ATM_VF_READY,&vcc->flags); | ||
423 | vcc->dev_data = dev; | ||
424 | (void) atm_init_aal5(vcc); /* @@@ losing AAL in transit ... */ | ||
425 | vcc->stats = &atmtcp_control_dev.stats.aal5; | ||
426 | return dev->number; | ||
427 | } | ||
428 | |||
429 | |||
430 | static int atmtcp_create_persistent(int itf) | ||
431 | { | ||
432 | return atmtcp_create(itf,1,NULL); | ||
433 | } | ||
434 | |||
435 | |||
436 | static int atmtcp_remove_persistent(int itf) | ||
437 | { | ||
438 | struct atm_dev *dev; | ||
439 | struct atmtcp_dev_data *dev_data; | ||
440 | |||
441 | dev = atm_dev_lookup(itf); | ||
442 | if (!dev) return -ENODEV; | ||
443 | if (dev->ops != &atmtcp_v_dev_ops) { | ||
444 | atm_dev_put(dev); | ||
445 | return -EMEDIUMTYPE; | ||
446 | } | ||
447 | dev_data = PRIV(dev); | ||
448 | if (!dev_data->persist) return 0; | ||
449 | dev_data->persist = 0; | ||
450 | if (PRIV(dev)->vcc) return 0; | ||
451 | kfree(dev_data); | ||
452 | atm_dev_put(dev); | ||
453 | shutdown_atm_dev(dev); | ||
454 | return 0; | ||
455 | } | ||
456 | |||
457 | static int atmtcp_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) | ||
458 | { | ||
459 | int err = 0; | ||
460 | struct atm_vcc *vcc = ATM_SD(sock); | ||
461 | |||
462 | if (cmd != SIOCSIFATMTCP && cmd != ATMTCP_CREATE && cmd != ATMTCP_REMOVE) | ||
463 | return -ENOIOCTLCMD; | ||
464 | |||
465 | if (!capable(CAP_NET_ADMIN)) | ||
466 | return -EPERM; | ||
467 | |||
468 | switch (cmd) { | ||
469 | case SIOCSIFATMTCP: | ||
470 | err = atmtcp_attach(vcc, (int) arg); | ||
471 | if (err >= 0) { | ||
472 | sock->state = SS_CONNECTED; | ||
473 | __module_get(THIS_MODULE); | ||
474 | } | ||
475 | break; | ||
476 | case ATMTCP_CREATE: | ||
477 | err = atmtcp_create_persistent((int) arg); | ||
478 | break; | ||
479 | case ATMTCP_REMOVE: | ||
480 | err = atmtcp_remove_persistent((int) arg); | ||
481 | break; | ||
482 | } | ||
483 | return err; | ||
484 | } | ||
485 | |||
486 | static struct atm_ioctl atmtcp_ioctl_ops = { | ||
487 | .owner = THIS_MODULE, | ||
488 | .ioctl = atmtcp_ioctl, | ||
489 | }; | ||
490 | |||
491 | static __init int atmtcp_init(void) | ||
492 | { | ||
493 | register_atm_ioctl(&atmtcp_ioctl_ops); | ||
494 | return 0; | ||
495 | } | ||
496 | |||
497 | |||
498 | static void __exit atmtcp_exit(void) | ||
499 | { | ||
500 | deregister_atm_ioctl(&atmtcp_ioctl_ops); | ||
501 | } | ||
502 | |||
503 | MODULE_LICENSE("GPL"); | ||
504 | module_init(atmtcp_init); | ||
505 | module_exit(atmtcp_exit); | ||