Merge master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
This commit is contained in:
commit
dcbd39a1f1
|
@ -76,6 +76,13 @@ struct atm_dev_stats {
|
|||
/* set interface ESI */
|
||||
#define ATM_SETESIF _IOW('a',ATMIOC_ITF+13,struct atmif_sioc)
|
||||
/* force interface ESI */
|
||||
#define ATM_ADDLECSADDR _IOW('a', ATMIOC_ITF+14, struct atmif_sioc)
|
||||
/* register a LECS address */
|
||||
#define ATM_DELLECSADDR _IOW('a', ATMIOC_ITF+15, struct atmif_sioc)
|
||||
/* unregister a LECS address */
|
||||
#define ATM_GETLECSADDR _IOW('a', ATMIOC_ITF+16, struct atmif_sioc)
|
||||
/* retrieve LECS address(es) */
|
||||
|
||||
#define ATM_GETSTAT _IOW('a',ATMIOC_SARCOM+0,struct atmif_sioc)
|
||||
/* get AAL layer statistics */
|
||||
#define ATM_GETSTATZ _IOW('a',ATMIOC_SARCOM+1,struct atmif_sioc)
|
||||
|
@ -328,6 +335,8 @@ struct atm_dev_addr {
|
|||
struct list_head entry; /* next address */
|
||||
};
|
||||
|
||||
enum atm_addr_type_t { ATM_ADDR_LOCAL, ATM_ADDR_LECS };
|
||||
|
||||
struct atm_dev {
|
||||
const struct atmdev_ops *ops; /* device operations; NULL if unused */
|
||||
const struct atmphy_ops *phy; /* PHY operations, may be undefined */
|
||||
|
@ -338,6 +347,7 @@ struct atm_dev {
|
|||
void *phy_data; /* private PHY date */
|
||||
unsigned long flags; /* device flags (ATM_DF_*) */
|
||||
struct list_head local; /* local ATM addresses */
|
||||
struct list_head lecs; /* LECS ATM addresses learned via ILMI */
|
||||
unsigned char esi[ESI_LEN]; /* ESI ("MAC" addr) */
|
||||
struct atm_cirange ci_range; /* VPI/VCI range */
|
||||
struct k_atm_dev_stats stats; /* statistics */
|
||||
|
|
|
@ -103,16 +103,20 @@ enum sctp_optname {
|
|||
#define SCTP_SOCKOPT_BINDX_REM SCTP_SOCKOPT_BINDX_REM
|
||||
SCTP_SOCKOPT_PEELOFF, /* peel off association. */
|
||||
#define SCTP_SOCKOPT_PEELOFF SCTP_SOCKOPT_PEELOFF
|
||||
SCTP_GET_PEER_ADDRS_NUM, /* Get number of peer addresss. */
|
||||
#define SCTP_GET_PEER_ADDRS_NUM SCTP_GET_PEER_ADDRS_NUM
|
||||
SCTP_GET_PEER_ADDRS, /* Get all peer addresss. */
|
||||
#define SCTP_GET_PEER_ADDRS SCTP_GET_PEER_ADDRS
|
||||
SCTP_GET_LOCAL_ADDRS_NUM, /* Get number of local addresss. */
|
||||
#define SCTP_GET_LOCAL_ADDRS_NUM SCTP_GET_LOCAL_ADDRS_NUM
|
||||
SCTP_GET_LOCAL_ADDRS, /* Get all local addresss. */
|
||||
#define SCTP_GET_LOCAL_ADDRS SCTP_GET_LOCAL_ADDRS
|
||||
SCTP_GET_PEER_ADDRS_NUM_OLD, /* Get number of peer addresss. */
|
||||
#define SCTP_GET_PEER_ADDRS_NUM_OLD SCTP_GET_PEER_ADDRS_NUM_OLD
|
||||
SCTP_GET_PEER_ADDRS_OLD, /* Get all peer addresss. */
|
||||
#define SCTP_GET_PEER_ADDRS_OLD SCTP_GET_PEER_ADDRS_OLD
|
||||
SCTP_GET_LOCAL_ADDRS_NUM_OLD, /* Get number of local addresss. */
|
||||
#define SCTP_GET_LOCAL_ADDRS_NUM_OLD SCTP_GET_LOCAL_ADDRS_NUM_OLD
|
||||
SCTP_GET_LOCAL_ADDRS_OLD, /* Get all local addresss. */
|
||||
#define SCTP_GET_LOCAL_ADDRS_OLD SCTP_GET_LOCAL_ADDRS_OLD
|
||||
SCTP_SOCKOPT_CONNECTX, /* CONNECTX requests. */
|
||||
#define SCTP_SOCKOPT_CONNECTX SCTP_SOCKOPT_CONNECTX
|
||||
SCTP_GET_PEER_ADDRS, /* Get all peer addresss. */
|
||||
#define SCTP_GET_PEER_ADDRS SCTP_GET_PEER_ADDRS
|
||||
SCTP_GET_LOCAL_ADDRS, /* Get all local addresss. */
|
||||
#define SCTP_GET_LOCAL_ADDRS SCTP_GET_LOCAL_ADDRS
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -239,7 +243,7 @@ struct sctp_paddr_change {
|
|||
int spc_state;
|
||||
int spc_error;
|
||||
sctp_assoc_t spc_assoc_id;
|
||||
};
|
||||
} __attribute__((packed, aligned(4)));
|
||||
|
||||
/*
|
||||
* spc_state: 32 bits (signed integer)
|
||||
|
@ -464,7 +468,7 @@ struct sctp_assocparams {
|
|||
struct sctp_setpeerprim {
|
||||
sctp_assoc_t sspp_assoc_id;
|
||||
struct sockaddr_storage sspp_addr;
|
||||
};
|
||||
} __attribute__((packed, aligned(4)));
|
||||
|
||||
/*
|
||||
* 7.1.10 Set Primary Address (SCTP_PRIMARY_ADDR)
|
||||
|
@ -477,7 +481,7 @@ struct sctp_setpeerprim {
|
|||
struct sctp_prim {
|
||||
sctp_assoc_t ssp_assoc_id;
|
||||
struct sockaddr_storage ssp_addr;
|
||||
};
|
||||
} __attribute__((packed, aligned(4)));
|
||||
|
||||
/*
|
||||
* 7.1.11 Set Adaption Layer Indicator (SCTP_ADAPTION_LAYER)
|
||||
|
@ -504,7 +508,7 @@ struct sctp_paddrparams {
|
|||
struct sockaddr_storage spp_address;
|
||||
__u32 spp_hbinterval;
|
||||
__u16 spp_pathmaxrxt;
|
||||
};
|
||||
} __attribute__((packed, aligned(4)));
|
||||
|
||||
/*
|
||||
* 7.2.2 Peer Address Information
|
||||
|
@ -523,7 +527,7 @@ struct sctp_paddrinfo {
|
|||
__u32 spinfo_srtt;
|
||||
__u32 spinfo_rto;
|
||||
__u32 spinfo_mtu;
|
||||
};
|
||||
} __attribute__((packed, aligned(4)));
|
||||
|
||||
/* Peer addresses's state. */
|
||||
enum sctp_spinfo_state {
|
||||
|
@ -559,11 +563,16 @@ struct sctp_status {
|
|||
* SCTP_GET_LOCAL_ADDRS socket options used internally to implement
|
||||
* sctp_getpaddrs() and sctp_getladdrs() API.
|
||||
*/
|
||||
struct sctp_getaddrs {
|
||||
struct sctp_getaddrs_old {
|
||||
sctp_assoc_t assoc_id;
|
||||
int addr_num;
|
||||
struct sockaddr __user *addrs;
|
||||
};
|
||||
struct sctp_getaddrs {
|
||||
sctp_assoc_t assoc_id; /*input*/
|
||||
__u32 addr_num; /*output*/
|
||||
__u8 addrs[0]; /*output, variable size*/
|
||||
};
|
||||
|
||||
/* These are bit fields for msghdr->msg_flags. See section 5.1. */
|
||||
/* On user space Linux, these live in <bits/socket.h> as an enum. */
|
||||
|
|
|
@ -44,31 +44,43 @@ static void notify_sigd(struct atm_dev *dev)
|
|||
sigd_enq(NULL, as_itf_notify, NULL, &pvc, NULL);
|
||||
}
|
||||
|
||||
void atm_reset_addr(struct atm_dev *dev)
|
||||
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);
|
||||
list_for_each_entry_safe(this, p, &dev->local, entry) {
|
||||
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);
|
||||
notify_sigd(dev);
|
||||
if (head == &dev->local)
|
||||
notify_sigd(dev);
|
||||
}
|
||||
|
||||
int atm_add_addr(struct atm_dev *dev, struct sockaddr_atmsvc *addr)
|
||||
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);
|
||||
list_for_each_entry(this, &dev->local, entry) {
|
||||
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;
|
||||
|
@ -80,28 +92,36 @@ int atm_add_addr(struct atm_dev *dev, struct sockaddr_atmsvc *addr)
|
|||
return -ENOMEM;
|
||||
}
|
||||
this->addr = *addr;
|
||||
list_add(&this->entry, &dev->local);
|
||||
list_add(&this->entry, head);
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
notify_sigd(dev);
|
||||
if (head == &dev->local)
|
||||
notify_sigd(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int atm_del_addr(struct atm_dev *dev, struct sockaddr_atmsvc *addr)
|
||||
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);
|
||||
list_for_each_entry(this, &dev->local, entry) {
|
||||
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);
|
||||
notify_sigd(dev);
|
||||
if (head == &dev->local)
|
||||
notify_sigd(dev);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -110,22 +130,27 @@ int atm_del_addr(struct atm_dev *dev, struct sockaddr_atmsvc *addr)
|
|||
}
|
||||
|
||||
int atm_get_addr(struct atm_dev *dev, struct sockaddr_atmsvc __user * buf,
|
||||
size_t size)
|
||||
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);
|
||||
list_for_each_entry(this, &dev->local, entry)
|
||||
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, &dev->local, entry)
|
||||
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;
|
||||
|
|
|
@ -9,10 +9,12 @@
|
|||
#include <linux/atm.h>
|
||||
#include <linux/atmdev.h>
|
||||
|
||||
|
||||
void atm_reset_addr(struct atm_dev *dev);
|
||||
int atm_add_addr(struct atm_dev *dev,struct sockaddr_atmsvc *addr);
|
||||
int atm_del_addr(struct atm_dev *dev,struct sockaddr_atmsvc *addr);
|
||||
int atm_get_addr(struct atm_dev *dev,struct sockaddr_atmsvc __user *buf,size_t size);
|
||||
void atm_reset_addr(struct atm_dev *dev, enum atm_addr_type_t type);
|
||||
int atm_add_addr(struct atm_dev *dev, struct sockaddr_atmsvc *addr,
|
||||
enum atm_addr_type_t type);
|
||||
int atm_del_addr(struct atm_dev *dev, struct sockaddr_atmsvc *addr,
|
||||
enum atm_addr_type_t type);
|
||||
int atm_get_addr(struct atm_dev *dev, struct sockaddr_atmsvc __user *buf,
|
||||
size_t size, enum atm_addr_type_t type);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -220,7 +220,7 @@ static int br2684_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
|||
/* netif_stop_queue(dev); */
|
||||
dev_kfree_skb(skb);
|
||||
read_unlock(&devs_lock);
|
||||
return -EUNATCH;
|
||||
return 0;
|
||||
}
|
||||
if (!br2684_xmit_vcc(skb, brdev, brvcc)) {
|
||||
/*
|
||||
|
|
|
@ -40,6 +40,7 @@ static struct atm_dev *__alloc_atm_dev(const char *type)
|
|||
dev->link_rate = ATM_OC3_PCR;
|
||||
spin_lock_init(&dev->lock);
|
||||
INIT_LIST_HEAD(&dev->local);
|
||||
INIT_LIST_HEAD(&dev->lecs);
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
@ -320,10 +321,12 @@ int atm_dev_ioctl(unsigned int cmd, void __user *arg)
|
|||
error = -EPERM;
|
||||
goto done;
|
||||
}
|
||||
atm_reset_addr(dev);
|
||||
atm_reset_addr(dev, ATM_ADDR_LOCAL);
|
||||
break;
|
||||
case ATM_ADDADDR:
|
||||
case ATM_DELADDR:
|
||||
case ATM_ADDLECSADDR:
|
||||
case ATM_DELLECSADDR:
|
||||
if (!capable(CAP_NET_ADMIN)) {
|
||||
error = -EPERM;
|
||||
goto done;
|
||||
|
@ -335,14 +338,21 @@ int atm_dev_ioctl(unsigned int cmd, void __user *arg)
|
|||
error = -EFAULT;
|
||||
goto done;
|
||||
}
|
||||
if (cmd == ATM_ADDADDR)
|
||||
error = atm_add_addr(dev, &addr);
|
||||
if (cmd == ATM_ADDADDR || cmd == ATM_ADDLECSADDR)
|
||||
error = atm_add_addr(dev, &addr,
|
||||
(cmd == ATM_ADDADDR ?
|
||||
ATM_ADDR_LOCAL : ATM_ADDR_LECS));
|
||||
else
|
||||
error = atm_del_addr(dev, &addr);
|
||||
error = atm_del_addr(dev, &addr,
|
||||
(cmd == ATM_DELADDR ?
|
||||
ATM_ADDR_LOCAL : ATM_ADDR_LECS));
|
||||
goto done;
|
||||
}
|
||||
case ATM_GETADDR:
|
||||
error = atm_get_addr(dev, buf, len);
|
||||
case ATM_GETLECSADDR:
|
||||
error = atm_get_addr(dev, buf, len,
|
||||
(cmd == ATM_GETADDR ?
|
||||
ATM_ADDR_LOCAL : ATM_ADDR_LECS));
|
||||
if (error < 0)
|
||||
goto done;
|
||||
size = error;
|
||||
|
|
|
@ -3159,8 +3159,9 @@ static int sctp_getsockopt_initmsg(struct sock *sk, int len, char __user *optval
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int sctp_getsockopt_peer_addrs_num(struct sock *sk, int len,
|
||||
char __user *optval, int __user *optlen)
|
||||
static int sctp_getsockopt_peer_addrs_num_old(struct sock *sk, int len,
|
||||
char __user *optval,
|
||||
int __user *optlen)
|
||||
{
|
||||
sctp_assoc_t id;
|
||||
struct sctp_association *asoc;
|
||||
|
@ -3185,23 +3186,28 @@ static int sctp_getsockopt_peer_addrs_num(struct sock *sk, int len,
|
|||
return cnt;
|
||||
}
|
||||
|
||||
static int sctp_getsockopt_peer_addrs(struct sock *sk, int len,
|
||||
char __user *optval, int __user *optlen)
|
||||
/*
|
||||
* Old API for getting list of peer addresses. Does not work for 32-bit
|
||||
* programs running on a 64-bit kernel
|
||||
*/
|
||||
static int sctp_getsockopt_peer_addrs_old(struct sock *sk, int len,
|
||||
char __user *optval,
|
||||
int __user *optlen)
|
||||
{
|
||||
struct sctp_association *asoc;
|
||||
struct list_head *pos;
|
||||
int cnt = 0;
|
||||
struct sctp_getaddrs getaddrs;
|
||||
struct sctp_getaddrs_old getaddrs;
|
||||
struct sctp_transport *from;
|
||||
void __user *to;
|
||||
union sctp_addr temp;
|
||||
struct sctp_sock *sp = sctp_sk(sk);
|
||||
int addrlen;
|
||||
|
||||
if (len != sizeof(struct sctp_getaddrs))
|
||||
if (len != sizeof(struct sctp_getaddrs_old))
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_user(&getaddrs, optval, sizeof(struct sctp_getaddrs)))
|
||||
if (copy_from_user(&getaddrs, optval, sizeof(struct sctp_getaddrs_old)))
|
||||
return -EFAULT;
|
||||
|
||||
if (getaddrs.addr_num <= 0) return -EINVAL;
|
||||
|
@ -3225,15 +3231,69 @@ static int sctp_getsockopt_peer_addrs(struct sock *sk, int len,
|
|||
if (cnt >= getaddrs.addr_num) break;
|
||||
}
|
||||
getaddrs.addr_num = cnt;
|
||||
if (copy_to_user(optval, &getaddrs, sizeof(struct sctp_getaddrs)))
|
||||
if (copy_to_user(optval, &getaddrs, sizeof(struct sctp_getaddrs_old)))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sctp_getsockopt_local_addrs_num(struct sock *sk, int len,
|
||||
char __user *optval,
|
||||
int __user *optlen)
|
||||
static int sctp_getsockopt_peer_addrs(struct sock *sk, int len,
|
||||
char __user *optval, int __user *optlen)
|
||||
{
|
||||
struct sctp_association *asoc;
|
||||
struct list_head *pos;
|
||||
int cnt = 0;
|
||||
struct sctp_getaddrs getaddrs;
|
||||
struct sctp_transport *from;
|
||||
void __user *to;
|
||||
union sctp_addr temp;
|
||||
struct sctp_sock *sp = sctp_sk(sk);
|
||||
int addrlen;
|
||||
size_t space_left;
|
||||
int bytes_copied;
|
||||
|
||||
if (len < sizeof(struct sctp_getaddrs))
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_user(&getaddrs, optval, sizeof(struct sctp_getaddrs)))
|
||||
return -EFAULT;
|
||||
|
||||
/* For UDP-style sockets, id specifies the association to query. */
|
||||
asoc = sctp_id2assoc(sk, getaddrs.assoc_id);
|
||||
if (!asoc)
|
||||
return -EINVAL;
|
||||
|
||||
to = optval + offsetof(struct sctp_getaddrs,addrs);
|
||||
space_left = len - sizeof(struct sctp_getaddrs) -
|
||||
offsetof(struct sctp_getaddrs,addrs);
|
||||
|
||||
list_for_each(pos, &asoc->peer.transport_addr_list) {
|
||||
from = list_entry(pos, struct sctp_transport, transports);
|
||||
memcpy(&temp, &from->ipaddr, sizeof(temp));
|
||||
sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp);
|
||||
addrlen = sctp_get_af_specific(sk->sk_family)->sockaddr_len;
|
||||
if(space_left < addrlen)
|
||||
return -ENOMEM;
|
||||
temp.v4.sin_port = htons(temp.v4.sin_port);
|
||||
if (copy_to_user(to, &temp, addrlen))
|
||||
return -EFAULT;
|
||||
to += addrlen;
|
||||
cnt++;
|
||||
space_left -= addrlen;
|
||||
}
|
||||
|
||||
if (put_user(cnt, &((struct sctp_getaddrs __user *)optval)->addr_num))
|
||||
return -EFAULT;
|
||||
bytes_copied = ((char __user *)to) - optval;
|
||||
if (put_user(bytes_copied, optlen))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sctp_getsockopt_local_addrs_num_old(struct sock *sk, int len,
|
||||
char __user *optval,
|
||||
int __user *optlen)
|
||||
{
|
||||
sctp_assoc_t id;
|
||||
struct sctp_bind_addr *bp;
|
||||
|
@ -3306,8 +3366,8 @@ done:
|
|||
/* Helper function that copies local addresses to user and returns the number
|
||||
* of addresses copied.
|
||||
*/
|
||||
static int sctp_copy_laddrs_to_user(struct sock *sk, __u16 port, int max_addrs,
|
||||
void __user *to)
|
||||
static int sctp_copy_laddrs_to_user_old(struct sock *sk, __u16 port, int max_addrs,
|
||||
void __user *to)
|
||||
{
|
||||
struct list_head *pos;
|
||||
struct sctp_sockaddr_entry *addr;
|
||||
|
@ -3341,14 +3401,54 @@ static int sctp_copy_laddrs_to_user(struct sock *sk, __u16 port, int max_addrs,
|
|||
return cnt;
|
||||
}
|
||||
|
||||
static int sctp_getsockopt_local_addrs(struct sock *sk, int len,
|
||||
char __user *optval, int __user *optlen)
|
||||
static int sctp_copy_laddrs_to_user(struct sock *sk, __u16 port,
|
||||
void * __user *to, size_t space_left)
|
||||
{
|
||||
struct list_head *pos;
|
||||
struct sctp_sockaddr_entry *addr;
|
||||
unsigned long flags;
|
||||
union sctp_addr temp;
|
||||
int cnt = 0;
|
||||
int addrlen;
|
||||
|
||||
sctp_spin_lock_irqsave(&sctp_local_addr_lock, flags);
|
||||
list_for_each(pos, &sctp_local_addr_list) {
|
||||
addr = list_entry(pos, struct sctp_sockaddr_entry, list);
|
||||
if ((PF_INET == sk->sk_family) &&
|
||||
(AF_INET6 == addr->a.sa.sa_family))
|
||||
continue;
|
||||
memcpy(&temp, &addr->a, sizeof(temp));
|
||||
sctp_get_pf_specific(sk->sk_family)->addr_v4map(sctp_sk(sk),
|
||||
&temp);
|
||||
addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len;
|
||||
if(space_left<addrlen)
|
||||
return -ENOMEM;
|
||||
temp.v4.sin_port = htons(port);
|
||||
if (copy_to_user(*to, &temp, addrlen)) {
|
||||
sctp_spin_unlock_irqrestore(&sctp_local_addr_lock,
|
||||
flags);
|
||||
return -EFAULT;
|
||||
}
|
||||
*to += addrlen;
|
||||
cnt ++;
|
||||
space_left -= addrlen;
|
||||
}
|
||||
sctp_spin_unlock_irqrestore(&sctp_local_addr_lock, flags);
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
/* Old API for getting list of local addresses. Does not work for 32-bit
|
||||
* programs running on a 64-bit kernel
|
||||
*/
|
||||
static int sctp_getsockopt_local_addrs_old(struct sock *sk, int len,
|
||||
char __user *optval, int __user *optlen)
|
||||
{
|
||||
struct sctp_bind_addr *bp;
|
||||
struct sctp_association *asoc;
|
||||
struct list_head *pos;
|
||||
int cnt = 0;
|
||||
struct sctp_getaddrs getaddrs;
|
||||
struct sctp_getaddrs_old getaddrs;
|
||||
struct sctp_sockaddr_entry *addr;
|
||||
void __user *to;
|
||||
union sctp_addr temp;
|
||||
|
@ -3357,10 +3457,10 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len,
|
|||
rwlock_t *addr_lock;
|
||||
int err = 0;
|
||||
|
||||
if (len != sizeof(struct sctp_getaddrs))
|
||||
if (len != sizeof(struct sctp_getaddrs_old))
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_user(&getaddrs, optval, sizeof(struct sctp_getaddrs)))
|
||||
if (copy_from_user(&getaddrs, optval, sizeof(struct sctp_getaddrs_old)))
|
||||
return -EFAULT;
|
||||
|
||||
if (getaddrs.addr_num <= 0) return -EINVAL;
|
||||
|
@ -3392,8 +3492,9 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len,
|
|||
addr = list_entry(bp->address_list.next,
|
||||
struct sctp_sockaddr_entry, list);
|
||||
if (sctp_is_any(&addr->a)) {
|
||||
cnt = sctp_copy_laddrs_to_user(sk, bp->port,
|
||||
getaddrs.addr_num, to);
|
||||
cnt = sctp_copy_laddrs_to_user_old(sk, bp->port,
|
||||
getaddrs.addr_num,
|
||||
to);
|
||||
if (cnt < 0) {
|
||||
err = cnt;
|
||||
goto unlock;
|
||||
|
@ -3419,7 +3520,7 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len,
|
|||
|
||||
copy_getaddrs:
|
||||
getaddrs.addr_num = cnt;
|
||||
if (copy_to_user(optval, &getaddrs, sizeof(struct sctp_getaddrs)))
|
||||
if (copy_to_user(optval, &getaddrs, sizeof(struct sctp_getaddrs_old)))
|
||||
err = -EFAULT;
|
||||
|
||||
unlock:
|
||||
|
@ -3427,6 +3528,99 @@ unlock:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int sctp_getsockopt_local_addrs(struct sock *sk, int len,
|
||||
char __user *optval, int __user *optlen)
|
||||
{
|
||||
struct sctp_bind_addr *bp;
|
||||
struct sctp_association *asoc;
|
||||
struct list_head *pos;
|
||||
int cnt = 0;
|
||||
struct sctp_getaddrs getaddrs;
|
||||
struct sctp_sockaddr_entry *addr;
|
||||
void __user *to;
|
||||
union sctp_addr temp;
|
||||
struct sctp_sock *sp = sctp_sk(sk);
|
||||
int addrlen;
|
||||
rwlock_t *addr_lock;
|
||||
int err = 0;
|
||||
size_t space_left;
|
||||
int bytes_copied;
|
||||
|
||||
if (len <= sizeof(struct sctp_getaddrs))
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_user(&getaddrs, optval, sizeof(struct sctp_getaddrs)))
|
||||
return -EFAULT;
|
||||
|
||||
/*
|
||||
* For UDP-style sockets, id specifies the association to query.
|
||||
* If the id field is set to the value '0' then the locally bound
|
||||
* addresses are returned without regard to any particular
|
||||
* association.
|
||||
*/
|
||||
if (0 == getaddrs.assoc_id) {
|
||||
bp = &sctp_sk(sk)->ep->base.bind_addr;
|
||||
addr_lock = &sctp_sk(sk)->ep->base.addr_lock;
|
||||
} else {
|
||||
asoc = sctp_id2assoc(sk, getaddrs.assoc_id);
|
||||
if (!asoc)
|
||||
return -EINVAL;
|
||||
bp = &asoc->base.bind_addr;
|
||||
addr_lock = &asoc->base.addr_lock;
|
||||
}
|
||||
|
||||
to = optval + offsetof(struct sctp_getaddrs,addrs);
|
||||
space_left = len - sizeof(struct sctp_getaddrs) -
|
||||
offsetof(struct sctp_getaddrs,addrs);
|
||||
|
||||
sctp_read_lock(addr_lock);
|
||||
|
||||
/* If the endpoint is bound to 0.0.0.0 or ::0, get the valid
|
||||
* addresses from the global local address list.
|
||||
*/
|
||||
if (sctp_list_single_entry(&bp->address_list)) {
|
||||
addr = list_entry(bp->address_list.next,
|
||||
struct sctp_sockaddr_entry, list);
|
||||
if (sctp_is_any(&addr->a)) {
|
||||
cnt = sctp_copy_laddrs_to_user(sk, bp->port,
|
||||
&to, space_left);
|
||||
if (cnt < 0) {
|
||||
err = cnt;
|
||||
goto unlock;
|
||||
}
|
||||
goto copy_getaddrs;
|
||||
}
|
||||
}
|
||||
|
||||
list_for_each(pos, &bp->address_list) {
|
||||
addr = list_entry(pos, struct sctp_sockaddr_entry, list);
|
||||
memcpy(&temp, &addr->a, sizeof(temp));
|
||||
sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp);
|
||||
addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len;
|
||||
if(space_left < addrlen)
|
||||
return -ENOMEM; /*fixme: right error?*/
|
||||
temp.v4.sin_port = htons(temp.v4.sin_port);
|
||||
if (copy_to_user(to, &temp, addrlen)) {
|
||||
err = -EFAULT;
|
||||
goto unlock;
|
||||
}
|
||||
to += addrlen;
|
||||
cnt ++;
|
||||
space_left -= addrlen;
|
||||
}
|
||||
|
||||
copy_getaddrs:
|
||||
if (put_user(cnt, &((struct sctp_getaddrs __user *)optval)->addr_num))
|
||||
return -EFAULT;
|
||||
bytes_copied = ((char __user *)to) - optval;
|
||||
if (put_user(bytes_copied, optlen))
|
||||
return -EFAULT;
|
||||
|
||||
unlock:
|
||||
sctp_read_unlock(addr_lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* 7.1.10 Set Primary Address (SCTP_PRIMARY_ADDR)
|
||||
*
|
||||
* Requests that the local SCTP stack use the enclosed peer address as
|
||||
|
@ -3807,12 +4001,20 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
|
|||
case SCTP_INITMSG:
|
||||
retval = sctp_getsockopt_initmsg(sk, len, optval, optlen);
|
||||
break;
|
||||
case SCTP_GET_PEER_ADDRS_NUM:
|
||||
retval = sctp_getsockopt_peer_addrs_num(sk, len, optval,
|
||||
case SCTP_GET_PEER_ADDRS_NUM_OLD:
|
||||
retval = sctp_getsockopt_peer_addrs_num_old(sk, len, optval,
|
||||
optlen);
|
||||
break;
|
||||
case SCTP_GET_LOCAL_ADDRS_NUM_OLD:
|
||||
retval = sctp_getsockopt_local_addrs_num_old(sk, len, optval,
|
||||
optlen);
|
||||
break;
|
||||
case SCTP_GET_PEER_ADDRS_OLD:
|
||||
retval = sctp_getsockopt_peer_addrs_old(sk, len, optval,
|
||||
optlen);
|
||||
break;
|
||||
case SCTP_GET_LOCAL_ADDRS_NUM:
|
||||
retval = sctp_getsockopt_local_addrs_num(sk, len, optval,
|
||||
case SCTP_GET_LOCAL_ADDRS_OLD:
|
||||
retval = sctp_getsockopt_local_addrs_old(sk, len, optval,
|
||||
optlen);
|
||||
break;
|
||||
case SCTP_GET_PEER_ADDRS:
|
||||
|
|
Loading…
Reference in New Issue