diff options
Diffstat (limited to 'net/sctp/socket.c')
-rw-r--r-- | net/sctp/socket.c | 82 |
1 files changed, 64 insertions, 18 deletions
diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 3951a10605bc..f694ee116746 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -658,11 +658,15 @@ static int sctp_bindx_rem(struct sock *sk, struct sockaddr *addrs, int addrcnt) goto err_bindx_rem; } - if (sa_addr->v4.sin_port != htons(bp->port)) { + if (sa_addr->v4.sin_port && + sa_addr->v4.sin_port != htons(bp->port)) { retval = -EINVAL; goto err_bindx_rem; } + if (!sa_addr->v4.sin_port) + sa_addr->v4.sin_port = htons(bp->port); + /* FIXME - There is probably a need to check if sk->sk_saddr and * sk->sk_rcv_addr are currently set to one of the addresses to * be removed. This is something which needs to be looked into @@ -1193,7 +1197,7 @@ out_free: * an endpoint that is multi-homed. Much like sctp_bindx() this call * allows a caller to specify multiple addresses at which a peer can be * reached. The way the SCTP stack uses the list of addresses to set up - * the association is implementation dependant. This function only + * the association is implementation dependent. This function only * specifies that the stack will try to make use of all the addresses in * the list when needed. * @@ -3215,14 +3219,9 @@ static int sctp_setsockopt_hmac_ident(struct sock *sk, if (optlen < sizeof(struct sctp_hmacalgo)) return -EINVAL; - hmacs = kmalloc(optlen, GFP_KERNEL); - if (!hmacs) - return -ENOMEM; - - if (copy_from_user(hmacs, optval, optlen)) { - err = -EFAULT; - goto out; - } + hmacs= memdup_user(optval, optlen); + if (IS_ERR(hmacs)) + return PTR_ERR(hmacs); idents = hmacs->shmac_num_idents; if (idents == 0 || idents > SCTP_AUTH_NUM_HMACS || @@ -3257,14 +3256,9 @@ static int sctp_setsockopt_auth_key(struct sock *sk, if (optlen <= sizeof(struct sctp_authkey)) return -EINVAL; - authkey = kmalloc(optlen, GFP_KERNEL); - if (!authkey) - return -ENOMEM; - - if (copy_from_user(authkey, optval, optlen)) { - ret = -EFAULT; - goto out; - } + authkey= memdup_user(optval, optlen); + if (IS_ERR(authkey)) + return PTR_ERR(authkey); if (authkey->sca_keylength > optlen - sizeof(struct sctp_authkey)) { ret = -EINVAL; @@ -5283,6 +5277,55 @@ static int sctp_getsockopt_assoc_number(struct sock *sk, int len, return 0; } +/* + * 8.2.6. Get the Current Identifiers of Associations + * (SCTP_GET_ASSOC_ID_LIST) + * + * This option gets the current list of SCTP association identifiers of + * the SCTP associations handled by a one-to-many style socket. + */ +static int sctp_getsockopt_assoc_ids(struct sock *sk, int len, + char __user *optval, int __user *optlen) +{ + struct sctp_sock *sp = sctp_sk(sk); + struct sctp_association *asoc; + struct sctp_assoc_ids *ids; + u32 num = 0; + + if (sctp_style(sk, TCP)) + return -EOPNOTSUPP; + + if (len < sizeof(struct sctp_assoc_ids)) + return -EINVAL; + + list_for_each_entry(asoc, &(sp->ep->asocs), asocs) { + num++; + } + + if (len < sizeof(struct sctp_assoc_ids) + sizeof(sctp_assoc_t) * num) + return -EINVAL; + + len = sizeof(struct sctp_assoc_ids) + sizeof(sctp_assoc_t) * num; + + ids = kmalloc(len, GFP_KERNEL); + if (unlikely(!ids)) + return -ENOMEM; + + ids->gaids_number_of_ids = num; + num = 0; + list_for_each_entry(asoc, &(sp->ep->asocs), asocs) { + ids->gaids_assoc_id[num++] = asoc->assoc_id; + } + + if (put_user(len, optlen) || copy_to_user(optval, ids, len)) { + kfree(ids); + return -EFAULT; + } + + kfree(ids); + return 0; +} + SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname, char __user *optval, int __user *optlen) { @@ -5415,6 +5458,9 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname, case SCTP_GET_ASSOC_NUMBER: retval = sctp_getsockopt_assoc_number(sk, len, optval, optlen); break; + case SCTP_GET_ASSOC_ID_LIST: + retval = sctp_getsockopt_assoc_ids(sk, len, optval, optlen); + break; default: retval = -ENOPROTOOPT; break; |