18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/* net/atm/addr.c - Local ATM address registry */
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/atm.h>
78c2ecf20Sopenharmony_ci#include <linux/atmdev.h>
88c2ecf20Sopenharmony_ci#include <linux/slab.h>
98c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include "signaling.h"
128c2ecf20Sopenharmony_ci#include "addr.h"
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_cistatic int check_addr(const struct sockaddr_atmsvc *addr)
158c2ecf20Sopenharmony_ci{
168c2ecf20Sopenharmony_ci	int i;
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci	if (addr->sas_family != AF_ATMSVC)
198c2ecf20Sopenharmony_ci		return -EAFNOSUPPORT;
208c2ecf20Sopenharmony_ci	if (!*addr->sas_addr.pub)
218c2ecf20Sopenharmony_ci		return *addr->sas_addr.prv ? 0 : -EINVAL;
228c2ecf20Sopenharmony_ci	for (i = 1; i < ATM_E164_LEN + 1; i++)	/* make sure it's \0-terminated */
238c2ecf20Sopenharmony_ci		if (!addr->sas_addr.pub[i])
248c2ecf20Sopenharmony_ci			return 0;
258c2ecf20Sopenharmony_ci	return -EINVAL;
268c2ecf20Sopenharmony_ci}
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistatic int identical(const struct sockaddr_atmsvc *a, const struct sockaddr_atmsvc *b)
298c2ecf20Sopenharmony_ci{
308c2ecf20Sopenharmony_ci	if (*a->sas_addr.prv)
318c2ecf20Sopenharmony_ci		if (memcmp(a->sas_addr.prv, b->sas_addr.prv, ATM_ESA_LEN))
328c2ecf20Sopenharmony_ci			return 0;
338c2ecf20Sopenharmony_ci	if (!*a->sas_addr.pub)
348c2ecf20Sopenharmony_ci		return !*b->sas_addr.pub;
358c2ecf20Sopenharmony_ci	if (!*b->sas_addr.pub)
368c2ecf20Sopenharmony_ci		return 0;
378c2ecf20Sopenharmony_ci	return !strcmp(a->sas_addr.pub, b->sas_addr.pub);
388c2ecf20Sopenharmony_ci}
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic void notify_sigd(const struct atm_dev *dev)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	struct sockaddr_atmpvc pvc;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	pvc.sap_addr.itf = dev->number;
458c2ecf20Sopenharmony_ci	sigd_enq(NULL, as_itf_notify, NULL, &pvc, NULL);
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_civoid atm_reset_addr(struct atm_dev *dev, enum atm_addr_type_t atype)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	unsigned long flags;
518c2ecf20Sopenharmony_ci	struct atm_dev_addr *this, *p;
528c2ecf20Sopenharmony_ci	struct list_head *head;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dev->lock, flags);
558c2ecf20Sopenharmony_ci	if (atype == ATM_ADDR_LECS)
568c2ecf20Sopenharmony_ci		head = &dev->lecs;
578c2ecf20Sopenharmony_ci	else
588c2ecf20Sopenharmony_ci		head = &dev->local;
598c2ecf20Sopenharmony_ci	list_for_each_entry_safe(this, p, head, entry) {
608c2ecf20Sopenharmony_ci		list_del(&this->entry);
618c2ecf20Sopenharmony_ci		kfree(this);
628c2ecf20Sopenharmony_ci	}
638c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dev->lock, flags);
648c2ecf20Sopenharmony_ci	if (head == &dev->local)
658c2ecf20Sopenharmony_ci		notify_sigd(dev);
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ciint atm_add_addr(struct atm_dev *dev, const struct sockaddr_atmsvc *addr,
698c2ecf20Sopenharmony_ci		 enum atm_addr_type_t atype)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	unsigned long flags;
728c2ecf20Sopenharmony_ci	struct atm_dev_addr *this;
738c2ecf20Sopenharmony_ci	struct list_head *head;
748c2ecf20Sopenharmony_ci	int error;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	error = check_addr(addr);
778c2ecf20Sopenharmony_ci	if (error)
788c2ecf20Sopenharmony_ci		return error;
798c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dev->lock, flags);
808c2ecf20Sopenharmony_ci	if (atype == ATM_ADDR_LECS)
818c2ecf20Sopenharmony_ci		head = &dev->lecs;
828c2ecf20Sopenharmony_ci	else
838c2ecf20Sopenharmony_ci		head = &dev->local;
848c2ecf20Sopenharmony_ci	list_for_each_entry(this, head, entry) {
858c2ecf20Sopenharmony_ci		if (identical(&this->addr, addr)) {
868c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&dev->lock, flags);
878c2ecf20Sopenharmony_ci			return -EEXIST;
888c2ecf20Sopenharmony_ci		}
898c2ecf20Sopenharmony_ci	}
908c2ecf20Sopenharmony_ci	this = kmalloc(sizeof(struct atm_dev_addr), GFP_ATOMIC);
918c2ecf20Sopenharmony_ci	if (!this) {
928c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&dev->lock, flags);
938c2ecf20Sopenharmony_ci		return -ENOMEM;
948c2ecf20Sopenharmony_ci	}
958c2ecf20Sopenharmony_ci	this->addr = *addr;
968c2ecf20Sopenharmony_ci	list_add(&this->entry, head);
978c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dev->lock, flags);
988c2ecf20Sopenharmony_ci	if (head == &dev->local)
998c2ecf20Sopenharmony_ci		notify_sigd(dev);
1008c2ecf20Sopenharmony_ci	return 0;
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ciint atm_del_addr(struct atm_dev *dev, const struct sockaddr_atmsvc *addr,
1048c2ecf20Sopenharmony_ci		 enum atm_addr_type_t atype)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	unsigned long flags;
1078c2ecf20Sopenharmony_ci	struct atm_dev_addr *this;
1088c2ecf20Sopenharmony_ci	struct list_head *head;
1098c2ecf20Sopenharmony_ci	int error;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	error = check_addr(addr);
1128c2ecf20Sopenharmony_ci	if (error)
1138c2ecf20Sopenharmony_ci		return error;
1148c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dev->lock, flags);
1158c2ecf20Sopenharmony_ci	if (atype == ATM_ADDR_LECS)
1168c2ecf20Sopenharmony_ci		head = &dev->lecs;
1178c2ecf20Sopenharmony_ci	else
1188c2ecf20Sopenharmony_ci		head = &dev->local;
1198c2ecf20Sopenharmony_ci	list_for_each_entry(this, head, entry) {
1208c2ecf20Sopenharmony_ci		if (identical(&this->addr, addr)) {
1218c2ecf20Sopenharmony_ci			list_del(&this->entry);
1228c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&dev->lock, flags);
1238c2ecf20Sopenharmony_ci			kfree(this);
1248c2ecf20Sopenharmony_ci			if (head == &dev->local)
1258c2ecf20Sopenharmony_ci				notify_sigd(dev);
1268c2ecf20Sopenharmony_ci			return 0;
1278c2ecf20Sopenharmony_ci		}
1288c2ecf20Sopenharmony_ci	}
1298c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dev->lock, flags);
1308c2ecf20Sopenharmony_ci	return -ENOENT;
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ciint atm_get_addr(struct atm_dev *dev, struct sockaddr_atmsvc __user * buf,
1348c2ecf20Sopenharmony_ci		 size_t size, enum atm_addr_type_t atype)
1358c2ecf20Sopenharmony_ci{
1368c2ecf20Sopenharmony_ci	unsigned long flags;
1378c2ecf20Sopenharmony_ci	struct atm_dev_addr *this;
1388c2ecf20Sopenharmony_ci	struct list_head *head;
1398c2ecf20Sopenharmony_ci	int total = 0, error;
1408c2ecf20Sopenharmony_ci	struct sockaddr_atmsvc *tmp_buf, *tmp_bufp;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dev->lock, flags);
1438c2ecf20Sopenharmony_ci	if (atype == ATM_ADDR_LECS)
1448c2ecf20Sopenharmony_ci		head = &dev->lecs;
1458c2ecf20Sopenharmony_ci	else
1468c2ecf20Sopenharmony_ci		head = &dev->local;
1478c2ecf20Sopenharmony_ci	list_for_each_entry(this, head, entry)
1488c2ecf20Sopenharmony_ci	    total += sizeof(struct sockaddr_atmsvc);
1498c2ecf20Sopenharmony_ci	tmp_buf = tmp_bufp = kmalloc(total, GFP_ATOMIC);
1508c2ecf20Sopenharmony_ci	if (!tmp_buf) {
1518c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&dev->lock, flags);
1528c2ecf20Sopenharmony_ci		return -ENOMEM;
1538c2ecf20Sopenharmony_ci	}
1548c2ecf20Sopenharmony_ci	list_for_each_entry(this, head, entry)
1558c2ecf20Sopenharmony_ci	    memcpy(tmp_bufp++, &this->addr, sizeof(struct sockaddr_atmsvc));
1568c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dev->lock, flags);
1578c2ecf20Sopenharmony_ci	error = total > size ? -E2BIG : total;
1588c2ecf20Sopenharmony_ci	if (copy_to_user(buf, tmp_buf, total < size ? total : size))
1598c2ecf20Sopenharmony_ci		error = -EFAULT;
1608c2ecf20Sopenharmony_ci	kfree(tmp_buf);
1618c2ecf20Sopenharmony_ci	return error;
1628c2ecf20Sopenharmony_ci}
163