162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* net/atm/addr.c - Local ATM address registry */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/atm.h>
762306a36Sopenharmony_ci#include <linux/atmdev.h>
862306a36Sopenharmony_ci#include <linux/slab.h>
962306a36Sopenharmony_ci#include <linux/uaccess.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include "signaling.h"
1262306a36Sopenharmony_ci#include "addr.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cistatic int check_addr(const struct sockaddr_atmsvc *addr)
1562306a36Sopenharmony_ci{
1662306a36Sopenharmony_ci	int i;
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci	if (addr->sas_family != AF_ATMSVC)
1962306a36Sopenharmony_ci		return -EAFNOSUPPORT;
2062306a36Sopenharmony_ci	if (!*addr->sas_addr.pub)
2162306a36Sopenharmony_ci		return *addr->sas_addr.prv ? 0 : -EINVAL;
2262306a36Sopenharmony_ci	for (i = 1; i < ATM_E164_LEN + 1; i++)	/* make sure it's \0-terminated */
2362306a36Sopenharmony_ci		if (!addr->sas_addr.pub[i])
2462306a36Sopenharmony_ci			return 0;
2562306a36Sopenharmony_ci	return -EINVAL;
2662306a36Sopenharmony_ci}
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic int identical(const struct sockaddr_atmsvc *a, const struct sockaddr_atmsvc *b)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	if (*a->sas_addr.prv)
3162306a36Sopenharmony_ci		if (memcmp(a->sas_addr.prv, b->sas_addr.prv, ATM_ESA_LEN))
3262306a36Sopenharmony_ci			return 0;
3362306a36Sopenharmony_ci	if (!*a->sas_addr.pub)
3462306a36Sopenharmony_ci		return !*b->sas_addr.pub;
3562306a36Sopenharmony_ci	if (!*b->sas_addr.pub)
3662306a36Sopenharmony_ci		return 0;
3762306a36Sopenharmony_ci	return !strcmp(a->sas_addr.pub, b->sas_addr.pub);
3862306a36Sopenharmony_ci}
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic void notify_sigd(const struct atm_dev *dev)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	struct sockaddr_atmpvc pvc;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	pvc.sap_addr.itf = dev->number;
4562306a36Sopenharmony_ci	sigd_enq(NULL, as_itf_notify, NULL, &pvc, NULL);
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_civoid atm_reset_addr(struct atm_dev *dev, enum atm_addr_type_t atype)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	unsigned long flags;
5162306a36Sopenharmony_ci	struct atm_dev_addr *this, *p;
5262306a36Sopenharmony_ci	struct list_head *head;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	spin_lock_irqsave(&dev->lock, flags);
5562306a36Sopenharmony_ci	if (atype == ATM_ADDR_LECS)
5662306a36Sopenharmony_ci		head = &dev->lecs;
5762306a36Sopenharmony_ci	else
5862306a36Sopenharmony_ci		head = &dev->local;
5962306a36Sopenharmony_ci	list_for_each_entry_safe(this, p, head, entry) {
6062306a36Sopenharmony_ci		list_del(&this->entry);
6162306a36Sopenharmony_ci		kfree(this);
6262306a36Sopenharmony_ci	}
6362306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->lock, flags);
6462306a36Sopenharmony_ci	if (head == &dev->local)
6562306a36Sopenharmony_ci		notify_sigd(dev);
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ciint atm_add_addr(struct atm_dev *dev, const struct sockaddr_atmsvc *addr,
6962306a36Sopenharmony_ci		 enum atm_addr_type_t atype)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	unsigned long flags;
7262306a36Sopenharmony_ci	struct atm_dev_addr *this;
7362306a36Sopenharmony_ci	struct list_head *head;
7462306a36Sopenharmony_ci	int error;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	error = check_addr(addr);
7762306a36Sopenharmony_ci	if (error)
7862306a36Sopenharmony_ci		return error;
7962306a36Sopenharmony_ci	spin_lock_irqsave(&dev->lock, flags);
8062306a36Sopenharmony_ci	if (atype == ATM_ADDR_LECS)
8162306a36Sopenharmony_ci		head = &dev->lecs;
8262306a36Sopenharmony_ci	else
8362306a36Sopenharmony_ci		head = &dev->local;
8462306a36Sopenharmony_ci	list_for_each_entry(this, head, entry) {
8562306a36Sopenharmony_ci		if (identical(&this->addr, addr)) {
8662306a36Sopenharmony_ci			spin_unlock_irqrestore(&dev->lock, flags);
8762306a36Sopenharmony_ci			return -EEXIST;
8862306a36Sopenharmony_ci		}
8962306a36Sopenharmony_ci	}
9062306a36Sopenharmony_ci	this = kmalloc(sizeof(struct atm_dev_addr), GFP_ATOMIC);
9162306a36Sopenharmony_ci	if (!this) {
9262306a36Sopenharmony_ci		spin_unlock_irqrestore(&dev->lock, flags);
9362306a36Sopenharmony_ci		return -ENOMEM;
9462306a36Sopenharmony_ci	}
9562306a36Sopenharmony_ci	this->addr = *addr;
9662306a36Sopenharmony_ci	list_add(&this->entry, head);
9762306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->lock, flags);
9862306a36Sopenharmony_ci	if (head == &dev->local)
9962306a36Sopenharmony_ci		notify_sigd(dev);
10062306a36Sopenharmony_ci	return 0;
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ciint atm_del_addr(struct atm_dev *dev, const struct sockaddr_atmsvc *addr,
10462306a36Sopenharmony_ci		 enum atm_addr_type_t atype)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	unsigned long flags;
10762306a36Sopenharmony_ci	struct atm_dev_addr *this;
10862306a36Sopenharmony_ci	struct list_head *head;
10962306a36Sopenharmony_ci	int error;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	error = check_addr(addr);
11262306a36Sopenharmony_ci	if (error)
11362306a36Sopenharmony_ci		return error;
11462306a36Sopenharmony_ci	spin_lock_irqsave(&dev->lock, flags);
11562306a36Sopenharmony_ci	if (atype == ATM_ADDR_LECS)
11662306a36Sopenharmony_ci		head = &dev->lecs;
11762306a36Sopenharmony_ci	else
11862306a36Sopenharmony_ci		head = &dev->local;
11962306a36Sopenharmony_ci	list_for_each_entry(this, head, entry) {
12062306a36Sopenharmony_ci		if (identical(&this->addr, addr)) {
12162306a36Sopenharmony_ci			list_del(&this->entry);
12262306a36Sopenharmony_ci			spin_unlock_irqrestore(&dev->lock, flags);
12362306a36Sopenharmony_ci			kfree(this);
12462306a36Sopenharmony_ci			if (head == &dev->local)
12562306a36Sopenharmony_ci				notify_sigd(dev);
12662306a36Sopenharmony_ci			return 0;
12762306a36Sopenharmony_ci		}
12862306a36Sopenharmony_ci	}
12962306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->lock, flags);
13062306a36Sopenharmony_ci	return -ENOENT;
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ciint atm_get_addr(struct atm_dev *dev, struct sockaddr_atmsvc __user * buf,
13462306a36Sopenharmony_ci		 size_t size, enum atm_addr_type_t atype)
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci	unsigned long flags;
13762306a36Sopenharmony_ci	struct atm_dev_addr *this;
13862306a36Sopenharmony_ci	struct list_head *head;
13962306a36Sopenharmony_ci	int total = 0, error;
14062306a36Sopenharmony_ci	struct sockaddr_atmsvc *tmp_buf, *tmp_bufp;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	spin_lock_irqsave(&dev->lock, flags);
14362306a36Sopenharmony_ci	if (atype == ATM_ADDR_LECS)
14462306a36Sopenharmony_ci		head = &dev->lecs;
14562306a36Sopenharmony_ci	else
14662306a36Sopenharmony_ci		head = &dev->local;
14762306a36Sopenharmony_ci	list_for_each_entry(this, head, entry)
14862306a36Sopenharmony_ci	    total += sizeof(struct sockaddr_atmsvc);
14962306a36Sopenharmony_ci	tmp_buf = tmp_bufp = kmalloc(total, GFP_ATOMIC);
15062306a36Sopenharmony_ci	if (!tmp_buf) {
15162306a36Sopenharmony_ci		spin_unlock_irqrestore(&dev->lock, flags);
15262306a36Sopenharmony_ci		return -ENOMEM;
15362306a36Sopenharmony_ci	}
15462306a36Sopenharmony_ci	list_for_each_entry(this, head, entry)
15562306a36Sopenharmony_ci	    memcpy(tmp_bufp++, &this->addr, sizeof(struct sockaddr_atmsvc));
15662306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->lock, flags);
15762306a36Sopenharmony_ci	error = total > size ? -E2BIG : total;
15862306a36Sopenharmony_ci	if (copy_to_user(buf, tmp_buf, total < size ? total : size))
15962306a36Sopenharmony_ci		error = -EFAULT;
16062306a36Sopenharmony_ci	kfree(tmp_buf);
16162306a36Sopenharmony_ci	return error;
16262306a36Sopenharmony_ci}
163