linux/net/atm/addr.c
<<
>>
Prefs
   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
  14static 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
  28static 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
  40static 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
  48void 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
  68int 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
 103int 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
 133int 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}
 163