Merge tag 'pull-18-rc1-work.mount' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-microblaze.git] / net / atm / addr.c
1 // SPDX-License-Identifier: GPL-2.0
2 /* net/atm/addr.c - Local ATM address registry */
3
4 /* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
5
6 #include <linux/atm.h>
7 #include <linux/atmdev.h>
8 #include <linux/slab.h>
9 #include <linux/uaccess.h>
10
11 #include "signaling.h"
12 #include "addr.h"
13
14 static int check_addr(const struct sockaddr_atmsvc *addr)
15 {
16         int i;
17
18         if (addr->sas_family != AF_ATMSVC)
19                 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])
24                         return 0;
25         return -EINVAL;
26 }
27
28 static int identical(const struct sockaddr_atmsvc *a, const 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)
34                 return !*b->sas_addr.pub;
35         if (!*b->sas_addr.pub)
36                 return 0;
37         return !strcmp(a->sas_addr.pub, b->sas_addr.pub);
38 }
39
40 static void notify_sigd(const struct atm_dev *dev)
41 {
42         struct sockaddr_atmpvc pvc;
43
44         pvc.sap_addr.itf = dev->number;
45         sigd_enq(NULL, as_itf_notify, NULL, &pvc, NULL);
46 }
47
48 void atm_reset_addr(struct atm_dev *dev, enum atm_addr_type_t atype)
49 {
50         unsigned long flags;
51         struct atm_dev_addr *this, *p;
52         struct list_head *head;
53
54         spin_lock_irqsave(&dev->lock, flags);
55         if (atype == ATM_ADDR_LECS)
56                 head = &dev->lecs;
57         else
58                 head = &dev->local;
59         list_for_each_entry_safe(this, p, head, entry) {
60                 list_del(&this->entry);
61                 kfree(this);
62         }
63         spin_unlock_irqrestore(&dev->lock, flags);
64         if (head == &dev->local)
65                 notify_sigd(dev);
66 }
67
68 int atm_add_addr(struct atm_dev *dev, const struct sockaddr_atmsvc *addr,
69                  enum atm_addr_type_t atype)
70 {
71         unsigned long flags;
72         struct atm_dev_addr *this;
73         struct list_head *head;
74         int error;
75
76         error = check_addr(addr);
77         if (error)
78                 return error;
79         spin_lock_irqsave(&dev->lock, flags);
80         if (atype == ATM_ADDR_LECS)
81                 head = &dev->lecs;
82         else
83                 head = &dev->local;
84         list_for_each_entry(this, head, entry) {
85                 if (identical(&this->addr, addr)) {
86                         spin_unlock_irqrestore(&dev->lock, flags);
87                         return -EEXIST;
88                 }
89         }
90         this = kmalloc(sizeof(struct atm_dev_addr), GFP_ATOMIC);
91         if (!this) {
92                 spin_unlock_irqrestore(&dev->lock, flags);
93                 return -ENOMEM;
94         }
95         this->addr = *addr;
96         list_add(&this->entry, head);
97         spin_unlock_irqrestore(&dev->lock, flags);
98         if (head == &dev->local)
99                 notify_sigd(dev);
100         return 0;
101 }
102
103 int atm_del_addr(struct atm_dev *dev, const struct sockaddr_atmsvc *addr,
104                  enum atm_addr_type_t atype)
105 {
106         unsigned long flags;
107         struct atm_dev_addr *this;
108         struct list_head *head;
109         int error;
110
111         error = check_addr(addr);
112         if (error)
113                 return error;
114         spin_lock_irqsave(&dev->lock, flags);
115         if (atype == ATM_ADDR_LECS)
116                 head = &dev->lecs;
117         else
118                 head = &dev->local;
119         list_for_each_entry(this, head, entry) {
120                 if (identical(&this->addr, addr)) {
121                         list_del(&this->entry);
122                         spin_unlock_irqrestore(&dev->lock, flags);
123                         kfree(this);
124                         if (head == &dev->local)
125                                 notify_sigd(dev);
126                         return 0;
127                 }
128         }
129         spin_unlock_irqrestore(&dev->lock, flags);
130         return -ENOENT;
131 }
132
133 int atm_get_addr(struct atm_dev *dev, struct sockaddr_atmsvc __user * buf,
134                  size_t size, enum atm_addr_type_t atype)
135 {
136         unsigned long flags;
137         struct atm_dev_addr *this;
138         struct list_head *head;
139         int total = 0, error;
140         struct sockaddr_atmsvc *tmp_buf, *tmp_bufp;
141
142         spin_lock_irqsave(&dev->lock, flags);
143         if (atype == ATM_ADDR_LECS)
144                 head = &dev->lecs;
145         else
146                 head = &dev->local;
147         list_for_each_entry(this, head, entry)
148             total += sizeof(struct sockaddr_atmsvc);
149         tmp_buf = tmp_bufp = kmalloc(total, GFP_ATOMIC);
150         if (!tmp_buf) {
151                 spin_unlock_irqrestore(&dev->lock, flags);
152                 return -ENOMEM;
153         }
154         list_for_each_entry(this, head, entry)
155             memcpy(tmp_bufp++, &this->addr, sizeof(struct sockaddr_atmsvc));
156         spin_unlock_irqrestore(&dev->lock, flags);
157         error = total > size ? -E2BIG : total;
158         if (copy_to_user(buf, tmp_buf, total < size ? total : size))
159                 error = -EFAULT;
160         kfree(tmp_buf);
161         return error;
162 }