import of ftp.dlink.com/GPL/DSMG-600_reB/ppclinux.tar.gz
[linux-2.4.21-pre4.git] / net / atm / addr.c
1 /* net/atm/addr.c - Local ATM address registry */
2
3 /* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
4
5
6 #include <linux/atm.h>
7 #include <linux/atmdev.h>
8 #include <linux/sched.h>
9 #include <asm/uaccess.h>
10
11 #include "signaling.h"
12 #include "addr.h"
13
14
15 static int check_addr(struct sockaddr_atmsvc *addr)
16 {
17         int i;
18
19         if (addr->sas_family != AF_ATMSVC) return -EAFNOSUPPORT;
20         if (!*addr->sas_addr.pub)
21                 return *addr->sas_addr.prv ? 0 : -EINVAL;
22         for (i = 1; i < ATM_E164_LEN+1; i++) /* make sure it's \0-terminated */
23                 if (!addr->sas_addr.pub[i]) return 0;
24         return -EINVAL;
25 }
26
27
28 static int identical(struct sockaddr_atmsvc *a,struct sockaddr_atmsvc *b)
29 {
30         if (*a->sas_addr.prv)
31                 if (memcmp(a->sas_addr.prv,b->sas_addr.prv,ATM_ESA_LEN))
32                         return 0;
33         if (!*a->sas_addr.pub) return !*b->sas_addr.pub;
34         if (!*b->sas_addr.pub) return 0;
35         return !strcmp(a->sas_addr.pub,b->sas_addr.pub);
36 }
37
38
39 /*
40  * Avoid modification of any list of local interfaces while reading it
41  * (which may involve page faults and therefore rescheduling)
42  */
43
44 static DECLARE_MUTEX(local_lock);
45 extern  spinlock_t atm_dev_lock;
46
47 static void notify_sigd(struct atm_dev *dev)
48 {
49         struct sockaddr_atmpvc pvc;
50
51         pvc.sap_addr.itf = dev->number;
52         sigd_enq(NULL,as_itf_notify,NULL,&pvc,NULL);
53 }
54
55 /*
56  *      This is called from atm_ioctl only. You must hold the lock as a caller
57  */
58
59 void atm_reset_addr(struct atm_dev *dev)
60 {
61         struct atm_dev_addr *this;
62
63         down(&local_lock);
64         while (dev->local) {
65                 this = dev->local;
66                 dev->local = this->next;
67                 kfree(this);
68         }
69         up(&local_lock);
70         notify_sigd(dev);
71 }
72
73
74 int atm_add_addr(struct atm_dev *dev,struct sockaddr_atmsvc *addr)
75 {
76         struct atm_dev_addr **walk;
77         int error;
78
79         error = check_addr(addr);
80         if (error) return error;
81         down(&local_lock);
82         for (walk = &dev->local; *walk; walk = &(*walk)->next)
83                 if (identical(&(*walk)->addr,addr)) {
84                         up(&local_lock);
85                         return -EEXIST;
86                 }
87         *walk = kmalloc(sizeof(struct atm_dev_addr),GFP_KERNEL);
88         if (!*walk) {
89                 up(&local_lock);
90                 return -ENOMEM;
91         }
92         (*walk)->addr = *addr;
93         (*walk)->next = NULL;
94         up(&local_lock);
95         notify_sigd(dev);
96         return 0;
97 }
98
99
100 int atm_del_addr(struct atm_dev *dev,struct sockaddr_atmsvc *addr)
101 {
102         struct atm_dev_addr **walk,*this;
103         int error;
104
105         error = check_addr(addr);
106         if (error) return error;
107         down(&local_lock);
108         for (walk = &dev->local; *walk; walk = &(*walk)->next)
109                 if (identical(&(*walk)->addr,addr)) break;
110         if (!*walk) {
111                 up(&local_lock);
112                 return -ENOENT;
113         }
114         this = *walk;
115         *walk = this->next;
116         kfree(this);
117         up(&local_lock);
118         notify_sigd(dev);
119         return 0;
120 }
121
122
123 int atm_get_addr(struct atm_dev *dev,struct sockaddr_atmsvc *u_buf,int size)
124 {
125         struct atm_dev_addr *walk;
126         int total;
127
128         down(&local_lock);
129         total = 0;
130         for (walk = dev->local; walk; walk = walk->next) {
131                 total += sizeof(struct sockaddr_atmsvc);
132                 if (total > size) {
133                         up(&local_lock);
134                         return -E2BIG;
135                 }
136                 if (copy_to_user(u_buf,&walk->addr,
137                     sizeof(struct sockaddr_atmsvc))) {
138                         up(&local_lock);
139                         return -EFAULT;
140                 }
141                 u_buf++;
142         }
143         up(&local_lock);
144         return total;
145 }