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