| /* net/atm/addr.c - Local ATM address registry */ | |
| /* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */ | |
| #include <linux/atm.h> | |
| #include <linux/atmdev.h> | |
| #include <linux/sched.h> | |
| #include <asm/uaccess.h> | |
| #include "signaling.h" | |
| #include "addr.h" | |
| static int check_addr(struct sockaddr_atmsvc *addr) | |
| { | |
| int i; | |
| if (addr->sas_family != AF_ATMSVC) | |
| return -EAFNOSUPPORT; | |
| if (!*addr->sas_addr.pub) | |
| return *addr->sas_addr.prv ? 0 : -EINVAL; | |
| for (i = 1; i < ATM_E164_LEN + 1; i++) /* make sure it's \0-terminated */ | |
| if (!addr->sas_addr.pub[i]) | |
| return 0; | |
| return -EINVAL; | |
| } | |
| static int identical(struct sockaddr_atmsvc *a, struct sockaddr_atmsvc *b) | |
| { | |
| if (*a->sas_addr.prv) | |
| if (memcmp(a->sas_addr.prv, b->sas_addr.prv, ATM_ESA_LEN)) | |
| return 0; | |
| if (!*a->sas_addr.pub) | |
| return !*b->sas_addr.pub; | |
| if (!*b->sas_addr.pub) | |
| return 0; | |
| return !strcmp(a->sas_addr.pub, b->sas_addr.pub); | |
| } | |
| static void notify_sigd(struct atm_dev *dev) | |
| { | |
| struct sockaddr_atmpvc pvc; | |
| pvc.sap_addr.itf = dev->number; | |
| sigd_enq(NULL, as_itf_notify, NULL, &pvc, NULL); | |
| } | |
| void atm_reset_addr(struct atm_dev *dev, enum atm_addr_type_t atype) | |
| { | |
| unsigned long flags; | |
| struct atm_dev_addr *this, *p; | |
| struct list_head *head; | |
| spin_lock_irqsave(&dev->lock, flags); | |
| if (atype == ATM_ADDR_LECS) | |
| head = &dev->lecs; | |
| else | |
| head = &dev->local; | |
| list_for_each_entry_safe(this, p, head, entry) { | |
| list_del(&this->entry); | |
| kfree(this); | |
| } | |
| spin_unlock_irqrestore(&dev->lock, flags); | |
| if (head == &dev->local) | |
| notify_sigd(dev); | |
| } | |
| int atm_add_addr(struct atm_dev *dev, struct sockaddr_atmsvc *addr, | |
| enum atm_addr_type_t atype) | |
| { | |
| unsigned long flags; | |
| struct atm_dev_addr *this; | |
| struct list_head *head; | |
| int error; | |
| error = check_addr(addr); | |
| if (error) | |
| return error; | |
| spin_lock_irqsave(&dev->lock, flags); | |
| if (atype == ATM_ADDR_LECS) | |
| head = &dev->lecs; | |
| else | |
| head = &dev->local; | |
| list_for_each_entry(this, head, entry) { | |
| if (identical(&this->addr, addr)) { | |
| spin_unlock_irqrestore(&dev->lock, flags); | |
| return -EEXIST; | |
| } | |
| } | |
| this = kmalloc(sizeof(struct atm_dev_addr), GFP_ATOMIC); | |
| if (!this) { | |
| spin_unlock_irqrestore(&dev->lock, flags); | |
| return -ENOMEM; | |
| } | |
| this->addr = *addr; | |
| list_add(&this->entry, head); | |
| spin_unlock_irqrestore(&dev->lock, flags); | |
| if (head == &dev->local) | |
| notify_sigd(dev); | |
| return 0; | |
| } | |
| int atm_del_addr(struct atm_dev *dev, struct sockaddr_atmsvc *addr, | |
| enum atm_addr_type_t atype) | |
| { | |
| unsigned long flags; | |
| struct atm_dev_addr *this; | |
| struct list_head *head; | |
| int error; | |
| error = check_addr(addr); | |
| if (error) | |
| return error; | |
| spin_lock_irqsave(&dev->lock, flags); | |
| if (atype == ATM_ADDR_LECS) | |
| head = &dev->lecs; | |
| else | |
| head = &dev->local; | |
| list_for_each_entry(this, head, entry) { | |
| if (identical(&this->addr, addr)) { | |
| list_del(&this->entry); | |
| spin_unlock_irqrestore(&dev->lock, flags); | |
| kfree(this); | |
| if (head == &dev->local) | |
| notify_sigd(dev); | |
| return 0; | |
| } | |
| } | |
| spin_unlock_irqrestore(&dev->lock, flags); | |
| return -ENOENT; | |
| } | |
| int atm_get_addr(struct atm_dev *dev, struct sockaddr_atmsvc __user * buf, | |
| size_t size, enum atm_addr_type_t atype) | |
| { | |
| unsigned long flags; | |
| struct atm_dev_addr *this; | |
| struct list_head *head; | |
| int total = 0, error; | |
| struct sockaddr_atmsvc *tmp_buf, *tmp_bufp; | |
| spin_lock_irqsave(&dev->lock, flags); | |
| if (atype == ATM_ADDR_LECS) | |
| head = &dev->lecs; | |
| else | |
| head = &dev->local; | |
| list_for_each_entry(this, head, entry) | |
| total += sizeof(struct sockaddr_atmsvc); | |
| tmp_buf = tmp_bufp = kmalloc(total, GFP_ATOMIC); | |
| if (!tmp_buf) { | |
| spin_unlock_irqrestore(&dev->lock, flags); | |
| return -ENOMEM; | |
| } | |
| list_for_each_entry(this, head, entry) | |
| memcpy(tmp_bufp++, &this->addr, sizeof(struct sockaddr_atmsvc)); | |
| spin_unlock_irqrestore(&dev->lock, flags); | |
| error = total > size ? -E2BIG : total; | |
| if (copy_to_user(buf, tmp_buf, total < size ? total : size)) | |
| error = -EFAULT; | |
| kfree(tmp_buf); | |
| return error; | |
| } |