Newer
Older
sctp_copy_sock(sock->sk, sk, asoc);
/* Make peeled-off sockets more like 1-1 accepted sockets.
* Set the daddr and initialize id to something more random
*/
af = sctp_get_af_specific(asoc->peer.primary_addr.sa.sa_family);
af->to_sk_daddr(&asoc->peer.primary_addr, sk);
/* Populate the fields of the newsk from the oldsk and migrate the
* asoc to the newsk.
*/
sctp_sock_migrate(sk, sock->sk, asoc, SCTP_SOCKET_UDP_HIGH_BANDWIDTH);
*sockp = sock;
return err;
}
static int sctp_getsockopt_peeloff(struct sock *sk, int len, char __user *optval, int __user *optlen)
{
sctp_peeloff_arg_t peeloff;
struct socket *newsock;
int retval = 0;
struct sctp_association *asoc;
if (len < sizeof(sctp_peeloff_arg_t))
len = sizeof(sctp_peeloff_arg_t);
if (copy_from_user(&peeloff, optval, len))
return -EFAULT;
asoc = sctp_id2assoc(sk, peeloff.associd);
if (!asoc) {
retval = -EINVAL;
goto out;
}
SCTP_DEBUG_PRINTK("%s: sk: %p asoc: %p\n", __func__, sk, asoc);
retval = sctp_do_peeloff(asoc, &newsock);
if (retval < 0)
goto out;
/* Map the socket to an unused fd that can be returned to the user. */
retval = sock_map_fd(newsock, 0);
if (retval < 0) {
sock_release(newsock);
goto out;
}
SCTP_DEBUG_PRINTK("%s: sk: %p asoc: %p newsk: %p sd: %d\n",
__func__, sk, asoc, newsock->sk, retval);
/* Return the fd mapped to the new socket. */
peeloff.sd = retval;
if (put_user(len, optlen))
return -EFAULT;
if (copy_to_user(optval, &peeloff, len))
retval = -EFAULT;
out:
return retval;
}
/* 7.1.13 Peer Address Parameters (SCTP_PEER_ADDR_PARAMS)
*
* Applications can enable or disable heartbeats for any peer address of
* an association, modify an address's heartbeat interval, force a
* heartbeat to be sent immediately, and adjust the address's maximum
* number of retransmissions sent before an address is considered
* unreachable. The following structure is used to access and modify an
* address's parameters:
*
* struct sctp_paddrparams {
* sctp_assoc_t spp_assoc_id;
* struct sockaddr_storage spp_address;
* uint32_t spp_hbinterval;
* uint16_t spp_pathmaxrxt;
* uint32_t spp_pathmtu;
* uint32_t spp_sackdelay;
* uint32_t spp_flags;
* };
*
* spp_assoc_id - (one-to-many style socket) This is filled in the
* application, and identifies the association for
* this query.
* spp_address - This specifies which address is of interest.
* spp_hbinterval - This contains the value of the heartbeat interval,
* in milliseconds. If a value of zero
* is present in this field then no changes are to
* be made to this parameter.
* spp_pathmaxrxt - This contains the maximum number of
* retransmissions before this address shall be
4095
4096
4097
4098
4099
4100
4101
4102
4103
4104
4105
4106
4107
4108
4109
4110
4111
4112
4113
4114
4115
4116
4117
4118
4119
4120
4121
4122
4123
4124
4125
4126
4127
4128
4129
4130
4131
4132
4133
4134
4135
4136
4137
4138
4139
4140
4141
4142
4143
4144
4145
4146
4147
4148
4149
4150
4151
4152
4153
4154
4155
4156
4157
* considered unreachable. If a value of zero
* is present in this field then no changes are to
* be made to this parameter.
* spp_pathmtu - When Path MTU discovery is disabled the value
* specified here will be the "fixed" path mtu.
* Note that if the spp_address field is empty
* then all associations on this address will
* have this fixed path mtu set upon them.
*
* spp_sackdelay - When delayed sack is enabled, this value specifies
* the number of milliseconds that sacks will be delayed
* for. This value will apply to all addresses of an
* association if the spp_address field is empty. Note
* also, that if delayed sack is enabled and this
* value is set to 0, no change is made to the last
* recorded delayed sack timer value.
*
* spp_flags - These flags are used to control various features
* on an association. The flag field may contain
* zero or more of the following options.
*
* SPP_HB_ENABLE - Enable heartbeats on the
* specified address. Note that if the address
* field is empty all addresses for the association
* have heartbeats enabled upon them.
*
* SPP_HB_DISABLE - Disable heartbeats on the
* speicifed address. Note that if the address
* field is empty all addresses for the association
* will have their heartbeats disabled. Note also
* that SPP_HB_ENABLE and SPP_HB_DISABLE are
* mutually exclusive, only one of these two should
* be specified. Enabling both fields will have
* undetermined results.
*
* SPP_HB_DEMAND - Request a user initiated heartbeat
* to be made immediately.
*
* SPP_PMTUD_ENABLE - This field will enable PMTU
* discovery upon the specified address. Note that
* if the address feild is empty then all addresses
* on the association are effected.
*
* SPP_PMTUD_DISABLE - This field will disable PMTU
* discovery upon the specified address. Note that
* if the address feild is empty then all addresses
* on the association are effected. Not also that
* SPP_PMTUD_ENABLE and SPP_PMTUD_DISABLE are mutually
* exclusive. Enabling both will have undetermined
* results.
*
* SPP_SACKDELAY_ENABLE - Setting this flag turns
* on delayed sack. The time specified in spp_sackdelay
* is used to specify the sack delay for this address. Note
* that if spp_address is empty then all addresses will
* enable delayed sack and take on the sack delay
* value specified in spp_sackdelay.
* SPP_SACKDELAY_DISABLE - Setting this flag turns
* off delayed sack. If the spp_address field is blank then
* delayed sack is disabled for the entire association. Note
* also that this field is mutually exclusive to
* SPP_SACKDELAY_ENABLE, setting both will have undefined
* results.
*/
static int sctp_getsockopt_peer_addr_params(struct sock *sk, int len,
char __user *optval, int __user *optlen)
struct sctp_paddrparams params;
struct sctp_transport *trans = NULL;
struct sctp_association *asoc = NULL;
struct sctp_sock *sp = sctp_sk(sk);
if (len < sizeof(struct sctp_paddrparams))
len = sizeof(struct sctp_paddrparams);
if (copy_from_user(¶ms, optval, len))
return -EFAULT;
/* If an address other than INADDR_ANY is specified, and
* no transport is found, then the request is invalid.
if (!sctp_is_any(sk, ( union sctp_addr *)¶ms.spp_address)) {
trans = sctp_addr_id2transport(sk, ¶ms.spp_address,
params.spp_assoc_id);
if (!trans) {
SCTP_DEBUG_PRINTK("Failed no transport\n");
return -EINVAL;
}
/* Get association, if assoc_id != 0 and the socket is a one
* to many style socket, and an association was not found, then
* the id was invalid.
*/
asoc = sctp_id2assoc(sk, params.spp_assoc_id);
if (!asoc && params.spp_assoc_id && sctp_style(sk, UDP)) {
SCTP_DEBUG_PRINTK("Failed no association\n");
}
4195
4196
4197
4198
4199
4200
4201
4202
4203
4204
4205
4206
4207
4208
4209
4210
4211
4212
4213
4214
4215
4216
4217
4218
if (trans) {
/* Fetch transport values. */
params.spp_hbinterval = jiffies_to_msecs(trans->hbinterval);
params.spp_pathmtu = trans->pathmtu;
params.spp_pathmaxrxt = trans->pathmaxrxt;
params.spp_sackdelay = jiffies_to_msecs(trans->sackdelay);
/*draft-11 doesn't say what to return in spp_flags*/
params.spp_flags = trans->param_flags;
} else if (asoc) {
/* Fetch association values. */
params.spp_hbinterval = jiffies_to_msecs(asoc->hbinterval);
params.spp_pathmtu = asoc->pathmtu;
params.spp_pathmaxrxt = asoc->pathmaxrxt;
params.spp_sackdelay = jiffies_to_msecs(asoc->sackdelay);
/*draft-11 doesn't say what to return in spp_flags*/
params.spp_flags = asoc->param_flags;
} else {
/* Fetch socket values. */
params.spp_hbinterval = sp->hbinterval;
params.spp_pathmtu = sp->pathmtu;
params.spp_sackdelay = sp->sackdelay;
params.spp_pathmaxrxt = sp->pathmaxrxt;
/*draft-11 doesn't say what to return in spp_flags*/
params.spp_flags = sp->param_flags;
}
if (copy_to_user(optval, ¶ms, len))
return -EFAULT;
if (put_user(len, optlen))
return -EFAULT;
return 0;
}
/*
* 7.1.23. Get or set delayed ack timer (SCTP_DELAYED_SACK)
*
* This option will effect the way delayed acks are performed. This
* option allows you to get or set the delayed ack time, in
* milliseconds. It also allows changing the delayed ack frequency.
* Changing the frequency to 1 disables the delayed sack algorithm. If
* the assoc_id is 0, then this sets or gets the endpoints default
* values. If the assoc_id field is non-zero, then the set or get
* effects the specified association for the one to many model (the
* assoc_id field is ignored by the one to one model). Note that if
* sack_delay or sack_freq are 0 when setting this option, then the
* current values will remain unchanged.
*
* struct sctp_sack_info {
* sctp_assoc_t sack_assoc_id;
* uint32_t sack_delay;
* uint32_t sack_freq;
* };
* sack_assoc_id - This parameter, indicates which association the user
* is performing an action upon. Note that if this field's value is
* zero then the endpoints default value is changed (effecting future
* associations only).
* sack_delay - This parameter contains the number of milliseconds that
* the user is requesting the delayed ACK timer be set to. Note that
* this value is defined in the standard to be between 200 and 500
* milliseconds.
* sack_freq - This parameter contains the number of packets that must
* be received before a sack is sent without waiting for the delay
* timer to expire. The default value for this is 2, setting this
* value to 1 will disable the delayed sack algorithm.
static int sctp_getsockopt_delayed_ack(struct sock *sk, int len,
char __user *optval,
int __user *optlen)
{
struct sctp_sack_info params;
struct sctp_association *asoc = NULL;
struct sctp_sock *sp = sctp_sk(sk);
if (len >= sizeof(struct sctp_sack_info)) {
len = sizeof(struct sctp_sack_info);
if (copy_from_user(¶ms, optval, len))
return -EFAULT;
} else if (len == sizeof(struct sctp_assoc_value)) {
printk(KERN_WARNING "SCTP: Use of struct sctp_assoc_value "
"in delayed_ack socket option deprecated\n");
printk(KERN_WARNING "SCTP: Use struct sctp_sack_info instead\n");
if (copy_from_user(¶ms, optval, len))
return -EFAULT;
} else
return - EINVAL;
/* Get association, if sack_assoc_id != 0 and the socket is a one
* to many style socket, and an association was not found, then
* the id was invalid.
asoc = sctp_id2assoc(sk, params.sack_assoc_id);
if (!asoc && params.sack_assoc_id && sctp_style(sk, UDP))
return -EINVAL;
if (asoc) {
/* Fetch association values. */
if (asoc->param_flags & SPP_SACKDELAY_ENABLE) {
params.sack_delay = jiffies_to_msecs(
asoc->sackdelay);
params.sack_freq = asoc->sackfreq;
} else {
params.sack_delay = 0;
params.sack_freq = 1;
}
} else {
/* Fetch socket values. */
if (sp->param_flags & SPP_SACKDELAY_ENABLE) {
params.sack_delay = sp->sackdelay;
params.sack_freq = sp->sackfreq;
} else {
params.sack_delay = 0;
params.sack_freq = 1;
}
}
if (copy_to_user(optval, ¶ms, len))
return -EFAULT;
if (put_user(len, optlen))
return -EFAULT;
return 0;
}
/* 7.1.3 Initialization Parameters (SCTP_INITMSG)
*
* Applications can specify protocol parameters for the default association
* initialization. The option name argument to setsockopt() and getsockopt()
* is SCTP_INITMSG.
*
* Setting initialization parameters is effective only on an unconnected
* socket (for UDP-style sockets only future associations are effected
* by the change). With TCP-style sockets, this option is inherited by
* sockets derived from a listener socket.
*/
static int sctp_getsockopt_initmsg(struct sock *sk, int len, char __user *optval, int __user *optlen)
{
if (len < sizeof(struct sctp_initmsg))
len = sizeof(struct sctp_initmsg);
if (put_user(len, optlen))
return -EFAULT;
if (copy_to_user(optval, &sctp_sk(sk)->initmsg, len))
return -EFAULT;
return 0;
}

Ivan Skytte Jørgensen
committed
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;
struct list_head *pos;
int cnt = 0;
if (len < sizeof(sctp_assoc_t))
return -EINVAL;
if (copy_from_user(&id, optval, sizeof(sctp_assoc_t)))
return -EFAULT;
printk(KERN_WARNING "SCTP: Use of SCTP_GET_PEER_ADDRS_NUM_OLD "
"socket option deprecated\n");
/* For UDP-style sockets, id specifies the association to query. */
asoc = sctp_id2assoc(sk, id);
if (!asoc)
return -EINVAL;
list_for_each(pos, &asoc->peer.transport_addr_list) {
cnt ++;
}
return cnt;
}

Ivan Skytte Jørgensen
committed
* 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;
int cnt = 0;

Ivan Skytte Jørgensen
committed
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_old))
len = sizeof(struct sctp_getaddrs_old);
if (copy_from_user(&getaddrs, optval, len))
return -EFAULT;
if (getaddrs.addr_num <= 0) return -EINVAL;
printk(KERN_WARNING "SCTP: Use of SCTP_GET_PEER_ADDRS_OLD "
"socket option deprecated\n");
/* For UDP-style sockets, id specifies the association to query. */
asoc = sctp_id2assoc(sk, getaddrs.assoc_id);
if (!asoc)
return -EINVAL;
to = (void __user *)getaddrs.addrs;
list_for_each_entry(from, &asoc->peer.transport_addr_list,
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 (copy_to_user(to, &temp, addrlen))
return -EFAULT;
to += addrlen ;
cnt ++;
if (cnt >= getaddrs.addr_num) break;
}
getaddrs.addr_num = cnt;
if (put_user(len, optlen))
return -EFAULT;
if (copy_to_user(optval, &getaddrs, len))

Ivan Skytte Jørgensen
committed
4432
4433
4434
4435
4436
4437
4438
4439
4440
4441
4442
4443
4444
4445
4446
4447
4448
4449
4450
4451
4452
4453
4454
4455
4456
4457
4458
4459
4460
4461
4462
return -EFAULT;
return 0;
}
static int sctp_getsockopt_peer_addrs(struct sock *sk, int len,
char __user *optval, int __user *optlen)
{
struct sctp_association *asoc;
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 - offsetof(struct sctp_getaddrs,addrs);

Ivan Skytte Jørgensen
committed
list_for_each_entry(from, &asoc->peer.transport_addr_list,
transports) {
memcpy(&temp, &from->ipaddr, sizeof(temp));

Ivan Skytte Jørgensen
committed
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)

Ivan Skytte Jørgensen
committed
return -ENOMEM;
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))

Ivan Skytte Jørgensen
committed
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;
struct sctp_association *asoc;
struct sctp_sockaddr_entry *addr;
int cnt = 0;
if (len < sizeof(sctp_assoc_t))
return -EINVAL;
if (copy_from_user(&id, optval, sizeof(sctp_assoc_t)))
return -EFAULT;
printk(KERN_WARNING "SCTP: Use of SCTP_GET_LOCAL_ADDRS_NUM_OLD "
"socket option deprecated\n");
4507
4508
4509
4510
4511
4512
4513
4514
4515
4516
4517
4518
4519
4520
4521
4522
4523
4524
4525
4526
4527
/*
* 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 == id) {
bp = &sctp_sk(sk)->ep->base.bind_addr;
} else {
asoc = sctp_id2assoc(sk, id);
if (!asoc)
return -EINVAL;
bp = &asoc->base.bind_addr;
}
/* If the endpoint is bound to 0.0.0.0 or ::0, count 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(sk, &addr->a)) {
rcu_read_lock();
list_for_each_entry_rcu(addr,
&sctp_local_addr_list, list) {
if (!addr->valid)
continue;
if ((PF_INET == sk->sk_family) &&
(AF_INET6 == addr->a.sa.sa_family))
if ((PF_INET6 == sk->sk_family) &&
inet_v6_ipv6only(sk) &&
(AF_INET == addr->a.sa.sa_family))
continue;
rcu_read_unlock();
} else {
cnt = 1;
}
goto done;
}
/* Protection on the bound address list is not needed,
* since in the socket option context we hold the socket lock,
* so there is no way that the bound address list can change.
*/
list_for_each_entry(addr, &bp->address_list, list) {
cnt ++;
}
done:
return cnt;
}
/* Helper function that copies local addresses to user and returns the number
* of addresses copied.
*/
static int sctp_copy_laddrs_old(struct sock *sk, __u16 port,
int max_addrs, void *to,
int *bytes_copied)
{
struct sctp_sockaddr_entry *addr;
union sctp_addr temp;
int cnt = 0;
int addrlen;
rcu_read_lock();
list_for_each_entry_rcu(addr, &sctp_local_addr_list, list) {
if (!addr->valid)
continue;
if ((PF_INET == sk->sk_family) &&
(AF_INET6 == addr->a.sa.sa_family))
if ((PF_INET6 == sk->sk_family) &&
inet_v6_ipv6only(sk) &&
(AF_INET == addr->a.sa.sa_family))
continue;
memcpy(&temp, &addr->a, sizeof(temp));
if (!temp.v4.sin_port)
temp.v4.sin_port = htons(port);
sctp_get_pf_specific(sk->sk_family)->addr_v4map(sctp_sk(sk),
&temp);
addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len;
memcpy(to, &temp, addrlen);
*bytes_copied += addrlen;
rcu_read_unlock();
static int sctp_copy_laddrs(struct sock *sk, __u16 port, void *to,
size_t space_left, int *bytes_copied)

Ivan Skytte Jørgensen
committed
{
struct sctp_sockaddr_entry *addr;
union sctp_addr temp;
int cnt = 0;
int addrlen;
rcu_read_lock();
list_for_each_entry_rcu(addr, &sctp_local_addr_list, list) {
if (!addr->valid)
continue;
if ((PF_INET == sk->sk_family) &&
(AF_INET6 == addr->a.sa.sa_family))

Ivan Skytte Jørgensen
committed
continue;
if ((PF_INET6 == sk->sk_family) &&
inet_v6_ipv6only(sk) &&
(AF_INET == addr->a.sa.sa_family))
continue;
memcpy(&temp, &addr->a, sizeof(temp));
if (!temp.v4.sin_port)
temp.v4.sin_port = htons(port);

Ivan Skytte Jørgensen
committed
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) {
cnt = -ENOMEM;
break;
}
memcpy(to, &temp, addrlen);
to += addrlen;

Ivan Skytte Jørgensen
committed
cnt ++;
space_left -= addrlen;

Ivan Skytte Jørgensen
committed
}
rcu_read_unlock();

Ivan Skytte Jørgensen
committed
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;
int cnt = 0;

Ivan Skytte Jørgensen
committed
struct sctp_getaddrs_old getaddrs;
struct sctp_sockaddr_entry *addr;
void __user *to;
union sctp_addr temp;
struct sctp_sock *sp = sctp_sk(sk);
int addrlen;
int err = 0;
void *addrs;
int bytes_copied = 0;
if (len < sizeof(struct sctp_getaddrs_old))
len = sizeof(struct sctp_getaddrs_old);
if (copy_from_user(&getaddrs, optval, len))
if (getaddrs.addr_num <= 0 ||
getaddrs.addr_num >= (INT_MAX / sizeof(union sctp_addr)))
return -EINVAL;
printk(KERN_WARNING "SCTP: Use of SCTP_GET_LOCAL_ADDRS_OLD "
"socket option deprecated\n");
/*
* 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;
} else {
asoc = sctp_id2assoc(sk, getaddrs.assoc_id);
if (!asoc)
return -EINVAL;
bp = &asoc->base.bind_addr;
}
to = getaddrs.addrs;
/* Allocate space for a local instance of packed array to hold all
* the data. We store addresses here first and then put write them
* to the user in one shot.
*/
addrs = kmalloc(sizeof(union sctp_addr) * getaddrs.addr_num,
GFP_KERNEL);
if (!addrs)
return -ENOMEM;
/* 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(sk, &addr->a)) {
cnt = sctp_copy_laddrs_old(sk, bp->port,
getaddrs.addr_num,
addrs, &bytes_copied);
/* Protection on the bound address list is not needed since
* in the socket option context we hold a socket lock and
* thus the bound address list can't change.
*/
list_for_each_entry(addr, &bp->address_list, 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;
memcpy(buf, &temp, addrlen);
buf += addrlen;
bytes_copied += addrlen;
cnt ++;
if (cnt >= getaddrs.addr_num) break;
}
copy_getaddrs:
/* copy the entire address list into the user provided space */
if (copy_to_user(to, addrs, bytes_copied)) {
err = -EFAULT;
goto error;
}
/* copy the leading structure back to user */
if (copy_to_user(optval, &getaddrs, len))
error:
kfree(addrs);

Ivan Skytte Jørgensen
committed
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;
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;
int err = 0;
size_t space_left;
int bytes_copied = 0;
void *addrs;

Ivan Skytte Jørgensen
committed
if (len < sizeof(struct sctp_getaddrs))

Ivan Skytte Jørgensen
committed
4776
4777
4778
4779
4780
4781
4782
4783
4784
4785
4786
4787
4788
4789
4790
4791
4792
4793
4794
4795
4796
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;
} else {
asoc = sctp_id2assoc(sk, getaddrs.assoc_id);
if (!asoc)
return -EINVAL;
bp = &asoc->base.bind_addr;
}
to = optval + offsetof(struct sctp_getaddrs,addrs);
space_left = len - offsetof(struct sctp_getaddrs,addrs);
addrs = kmalloc(space_left, GFP_KERNEL);
if (!addrs)
return -ENOMEM;

Ivan Skytte Jørgensen
committed
/* 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(sk, &addr->a)) {
cnt = sctp_copy_laddrs(sk, bp->port, addrs,
space_left, &bytes_copied);

Ivan Skytte Jørgensen
committed
if (cnt < 0) {
err = cnt;

Ivan Skytte Jørgensen
committed
}

Ivan Skytte Jørgensen
committed
}
}
/* Protection on the bound address list is not needed since
* in the socket option context we hold a socket lock and
* thus the bound address list can't change.
*/
list_for_each_entry(addr, &bp->address_list, list) {
memcpy(&temp, &addr->a, sizeof(temp));

Ivan Skytte Jørgensen
committed
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) {
err = -ENOMEM; /*fixme: right error?*/

Ivan Skytte Jørgensen
committed
}
memcpy(buf, &temp, addrlen);
buf += addrlen;
bytes_copied += addrlen;

Ivan Skytte Jørgensen
committed
cnt ++;
space_left -= addrlen;
}
copy_getaddrs:
if (copy_to_user(to, addrs, bytes_copied)) {
err = -EFAULT;
if (put_user(cnt, &((struct sctp_getaddrs __user *)optval)->addr_num)) {
err = -EFAULT;
}

Ivan Skytte Jørgensen
committed
if (put_user(bytes_copied, optlen))
err = -EFAULT;
kfree(addrs);

Ivan Skytte Jørgensen
committed
return err;
}
/* 7.1.10 Set Primary Address (SCTP_PRIMARY_ADDR)
*
* Requests that the local SCTP stack use the enclosed peer address as
* the association primary. The enclosed address must be one of the
* association peer's addresses.
*/
static int sctp_getsockopt_primary_addr(struct sock *sk, int len,
char __user *optval, int __user *optlen)
{
struct sctp_prim prim;
struct sctp_association *asoc;
struct sctp_sock *sp = sctp_sk(sk);
if (len < sizeof(struct sctp_prim))
len = sizeof(struct sctp_prim);
if (copy_from_user(&prim, optval, len))
return -EFAULT;
asoc = sctp_id2assoc(sk, prim.ssp_assoc_id);
if (!asoc)
return -EINVAL;
if (!asoc->peer.primary_path)
return -ENOTCONN;
memcpy(&prim.ssp_addr, &asoc->peer.primary_path->ipaddr,
asoc->peer.primary_path->af_specific->sockaddr_len);
sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp,
(union sctp_addr *)&prim.ssp_addr);
if (put_user(len, optlen))
return -EFAULT;
if (copy_to_user(optval, &prim, len))
return -EFAULT;
return 0;
}
/*

Ivan Skytte Jorgensen
committed
* 7.1.11 Set Adaptation Layer Indicator (SCTP_ADAPTATION_LAYER)

Ivan Skytte Jorgensen
committed
* Requests that the local endpoint set the specified Adaptation Layer
* Indication parameter for all future INIT and INIT-ACK exchanges.
*/

Ivan Skytte Jorgensen
committed
static int sctp_getsockopt_adaptation_layer(struct sock *sk, int len,

Ivan Skytte Jorgensen
committed
struct sctp_setadaptation adaptation;
if (len < sizeof(struct sctp_setadaptation))
len = sizeof(struct sctp_setadaptation);

Ivan Skytte Jorgensen
committed
adaptation.ssb_adaptation_ind = sctp_sk(sk)->adaptation_ind;
if (put_user(len, optlen))
return -EFAULT;

Ivan Skytte Jorgensen
committed
if (copy_to_user(optval, &adaptation, len))
4921
4922
4923
4924
4925
4926
4927
4928
4929
4930
4931
4932
4933
4934
4935
4936
4937
4938
4939
4940
4941
4942
4943
4944
4945
4946
4947
4948
4949
4950
return 0;
}
/*
*
* 7.1.14 Set default send parameters (SCTP_DEFAULT_SEND_PARAM)
*
* Applications that wish to use the sendto() system call may wish to
* specify a default set of parameters that would normally be supplied
* through the inclusion of ancillary data. This socket option allows
* such an application to set the default sctp_sndrcvinfo structure.
* The application that wishes to use this socket option simply passes
* in to this call the sctp_sndrcvinfo structure defined in Section
* 5.2.2) The input parameters accepted by this call include
* sinfo_stream, sinfo_flags, sinfo_ppid, sinfo_context,
* sinfo_timetolive. The user must provide the sinfo_assoc_id field in
* to this call if the caller is using the UDP model.
*
* For getsockopt, it get the default sctp_sndrcvinfo structure.
*/
static int sctp_getsockopt_default_send_param(struct sock *sk,
int len, char __user *optval,
int __user *optlen)
{
struct sctp_sndrcvinfo info;
struct sctp_association *asoc;
struct sctp_sock *sp = sctp_sk(sk);
if (len < sizeof(struct sctp_sndrcvinfo))
len = sizeof(struct sctp_sndrcvinfo);
if (copy_from_user(&info, optval, len))
return -EFAULT;
asoc = sctp_id2assoc(sk, info.sinfo_assoc_id);
if (!asoc && info.sinfo_assoc_id && sctp_style(sk, UDP))
return -EINVAL;
if (asoc) {
info.sinfo_stream = asoc->default_stream;
info.sinfo_flags = asoc->default_flags;
info.sinfo_ppid = asoc->default_ppid;
info.sinfo_context = asoc->default_context;
info.sinfo_timetolive = asoc->default_timetolive;
} else {
info.sinfo_stream = sp->default_stream;
info.sinfo_flags = sp->default_flags;
info.sinfo_ppid = sp->default_ppid;
info.sinfo_context = sp->default_context;
info.sinfo_timetolive = sp->default_timetolive;
}
if (put_user(len, optlen))
return -EFAULT;
if (copy_to_user(optval, &info, len))
4980
4981
4982
4983
4984
4985
4986
4987
4988
4989
4990
4991
4992
4993
4994
4995
4996
4997
4998
4999
5000
return -EFAULT;
return 0;
}
/*
*
* 7.1.5 SCTP_NODELAY
*
* Turn on/off any Nagle-like algorithm. This means that packets are
* generally sent as soon as possible and no unnecessary delays are
* introduced, at the cost of more packets in the network. Expects an
* integer boolean flag.
*/
static int sctp_getsockopt_nodelay(struct sock *sk, int len,
char __user *optval, int __user *optlen)
{
int val;
if (len < sizeof(int))