if (!sp->pf->bind_verify(sp, addr))
return -EADDRNOTAVAIL;
- /* We must either be unbound, or bind to the same port. */
- if (bp->port && (snum != bp->port)) {
- SCTP_DEBUG_PRINTK("sctp_do_bind:"
+ /* We must either be unbound, or bind to the same port.
+ * It's OK to allow 0 ports if we are already bound.
+ * We'll just inhert an already bound port in this case
+ */
+ if (bp->port) {
+ if (!snum)
+ snum = bp->port;
+ else if (snum != bp->port) {
+ SCTP_DEBUG_PRINTK("sctp_do_bind:"
" New port %d does not match existing port "
"%d.\n", snum, bp->port);
- return -EINVAL;
+ return -EINVAL;
+ }
}
if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))
goto out_free;
}
+ if (asoc->pmtu_pending)
+ sctp_assoc_pending_pmtu(asoc);
+
/* If fragmentation is disabled and the message length exceeds the
* association fragmentation point, return EMSGSIZE. The I-D
* does not specify what this error is, but this looks like
*
* 7.1.2 SCTP_ASSOCINFO
*
- * This option is used to tune the the maximum retransmission attempts
+ * This option is used to tune the maximum retransmission attempts
* of the association.
* Returns an error if the new association retransmission value is
* greater than the sum of the retransmission value of the peer.
sctp_assoc_t associd;
int retval = 0;
- if (len != sizeof(status)) {
+ if (len < sizeof(status)) {
retval = -EINVAL;
goto out;
}
- if (copy_from_user(&status, optval, sizeof(status))) {
+ len = sizeof(status);
+ if (copy_from_user(&status, optval, len)) {
retval = -EFAULT;
goto out;
}
struct sctp_transport *transport;
int retval = 0;
- if (len != sizeof(pinfo)) {
+ if (len < sizeof(pinfo)) {
retval = -EINVAL;
goto out;
}
- if (copy_from_user(&pinfo, optval, sizeof(pinfo))) {
+ len = sizeof(pinfo);
+ if (copy_from_user(&pinfo, optval, len)) {
retval = -EFAULT;
goto out;
}
static int sctp_getsockopt_events(struct sock *sk, int len, char __user *optval,
int __user *optlen)
{
- if (len != sizeof(struct sctp_event_subscribe))
+ if (len < sizeof(struct sctp_event_subscribe))
return -EINVAL;
+ len = sizeof(struct sctp_event_subscribe);
+ if (put_user(len, optlen))
+ return -EFAULT;
if (copy_to_user(optval, &sctp_sk(sk)->subscribe, len))
return -EFAULT;
return 0;
/* Applicable to UDP-style socket only */
if (sctp_style(sk, TCP))
return -EOPNOTSUPP;
- if (len != sizeof(int))
+ if (len < sizeof(int))
return -EINVAL;
- if (copy_to_user(optval, &sctp_sk(sk)->autoclose, len))
+ len = sizeof(int);
+ if (put_user(len, optlen))
+ return -EFAULT;
+ if (copy_to_user(optval, &sctp_sk(sk)->autoclose, sizeof(int)))
return -EFAULT;
return 0;
}
struct sock *sk = asoc->base.sk;
struct socket *sock;
struct inet_sock *inetsk;
+ struct sctp_af *af;
int err = 0;
/* An association cannot be branched off from an already peeled-off
/* 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);
inetsk = inet_sk(sock->sk);
- inetsk->daddr = asoc->peer.primary_addr.v4.sin_addr.s_addr;
inetsk->id = asoc->next_tsn ^ jiffies;
*sockp = sock;
int retval = 0;
struct sctp_association *asoc;
- if (len != sizeof(sctp_peeloff_arg_t))
+ if (len < sizeof(sctp_peeloff_arg_t))
return -EINVAL;
+ len = sizeof(sctp_peeloff_arg_t);
if (copy_from_user(&peeloff, optval, len))
return -EFAULT;
/* 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;
struct sctp_association *asoc = NULL;
struct sctp_sock *sp = sctp_sk(sk);
- if (len != sizeof(struct sctp_paddrparams))
+ if (len < sizeof(struct sctp_paddrparams))
return -EINVAL;
-
+ len = sizeof(struct sctp_paddrparams);
if (copy_from_user(¶ms, optval, len))
return -EFAULT;
struct sctp_association *asoc = NULL;
struct sctp_sock *sp = sctp_sk(sk);
- if (len != sizeof(struct sctp_assoc_value))
+ if (len < sizeof(struct sctp_assoc_value))
return - EINVAL;
+ len = sizeof(struct sctp_assoc_value);
+
if (copy_from_user(¶ms, optval, len))
return -EFAULT;
*/
static int sctp_getsockopt_initmsg(struct sock *sk, int len, char __user *optval, int __user *optlen)
{
- if (len != sizeof(struct sctp_initmsg))
+ if (len < sizeof(struct sctp_initmsg))
return -EINVAL;
+ 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;
struct list_head *pos;
int cnt = 0;
- if (len != sizeof(sctp_assoc_t))
+ if (len < sizeof(sctp_assoc_t))
return -EINVAL;
if (copy_from_user(&id, optval, sizeof(sctp_assoc_t)))
struct sctp_sock *sp = sctp_sk(sk);
int addrlen;
- if (len != sizeof(struct sctp_getaddrs_old))
+ if (len < sizeof(struct sctp_getaddrs_old))
return -EINVAL;
- if (copy_from_user(&getaddrs, optval, 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;
if (cnt >= getaddrs.addr_num) break;
}
getaddrs.addr_num = cnt;
- if (copy_to_user(optval, &getaddrs, sizeof(struct sctp_getaddrs_old)))
+ if (put_user(len, optlen))
+ return -EFAULT;
+ if (copy_to_user(optval, &getaddrs, len))
return -EFAULT;
return 0;
return -EINVAL;
to = optval + offsetof(struct sctp_getaddrs,addrs);
- space_left = len - sizeof(struct sctp_getaddrs) -
- offsetof(struct sctp_getaddrs,addrs);
+ space_left = len - offsetof(struct sctp_getaddrs,addrs);
list_for_each(pos, &asoc->peer.transport_addr_list) {
from = list_entry(pos, struct sctp_transport, transports);
rwlock_t *addr_lock;
int cnt = 0;
- if (len != sizeof(sctp_assoc_t))
+ if (len < sizeof(sctp_assoc_t))
return -EINVAL;
if (copy_from_user(&id, optval, sizeof(sctp_assoc_t)))
rwlock_t *addr_lock;
int err = 0;
void *addrs;
+ void *buf;
int bytes_copied = 0;
- if (len != sizeof(struct sctp_getaddrs_old))
+ if (len < sizeof(struct sctp_getaddrs_old))
return -EINVAL;
- if (copy_from_user(&getaddrs, optval, 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;
}
}
+ buf = addrs;
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;
- memcpy(addrs, &temp, addrlen);
- to += addrlen;
+ memcpy(buf, &temp, addrlen);
+ buf += addrlen;
bytes_copied += addrlen;
cnt ++;
if (cnt >= getaddrs.addr_num) break;
/* copy the leading structure back to user */
getaddrs.addr_num = cnt;
- if (copy_to_user(optval, &getaddrs, sizeof(struct sctp_getaddrs_old)))
+ if (copy_to_user(optval, &getaddrs, len))
err = -EFAULT;
error:
size_t space_left;
int bytes_copied = 0;
void *addrs;
+ void *buf;
- if (len <= sizeof(struct sctp_getaddrs))
+ if (len < sizeof(struct sctp_getaddrs))
return -EINVAL;
if (copy_from_user(&getaddrs, optval, sizeof(struct sctp_getaddrs)))
}
to = optval + offsetof(struct sctp_getaddrs,addrs);
- space_left = len - sizeof(struct sctp_getaddrs) -
- offsetof(struct sctp_getaddrs,addrs);
+ space_left = len - offsetof(struct sctp_getaddrs,addrs);
+
addrs = kmalloc(space_left, GFP_KERNEL);
if (!addrs)
return -ENOMEM;
}
}
+ buf = addrs;
list_for_each(pos, &bp->address_list) {
addr = list_entry(pos, struct sctp_sockaddr_entry, list);
memcpy(&temp, &addr->a, sizeof(temp));
err = -ENOMEM; /*fixme: right error?*/
goto error;
}
- memcpy(addrs, &temp, addrlen);
- to += addrlen;
+ memcpy(buf, &temp, addrlen);
+ buf += addrlen;
bytes_copied += addrlen;
cnt ++;
space_left -= addrlen;
err = -EFAULT;
goto error;
}
- if (put_user(cnt, &((struct sctp_getaddrs __user *)optval)->addr_num))
- return -EFAULT;
+ if (put_user(cnt, &((struct sctp_getaddrs __user *)optval)->addr_num)) {
+ err = -EFAULT;
+ goto error;
+ }
if (put_user(bytes_copied, optlen))
- return -EFAULT;
-
+ err = -EFAULT;
error:
kfree(addrs);
return err;
struct sctp_association *asoc;
struct sctp_sock *sp = sctp_sk(sk);
- if (len != sizeof(struct sctp_prim))
+ if (len < sizeof(struct sctp_prim))
return -EINVAL;
- if (copy_from_user(&prim, optval, 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);
sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp,
(union sctp_addr *)&prim.ssp_addr);
- if (copy_to_user(optval, &prim, sizeof(struct sctp_prim)))
+ if (put_user(len, optlen))
+ return -EFAULT;
+ if (copy_to_user(optval, &prim, len))
return -EFAULT;
return 0;
{
struct sctp_setadaptation adaptation;
- if (len != sizeof(struct sctp_setadaptation))
+ if (len < sizeof(struct sctp_setadaptation))
return -EINVAL;
+ len = sizeof(struct sctp_setadaptation);
+
adaptation.ssb_adaptation_ind = sctp_sk(sk)->adaptation_ind;
+
+ if (put_user(len, optlen))
+ return -EFAULT;
if (copy_to_user(optval, &adaptation, len))
return -EFAULT;
struct sctp_association *asoc;
struct sctp_sock *sp = sctp_sk(sk);
- if (len != sizeof(struct sctp_sndrcvinfo))
+ if (len < sizeof(struct sctp_sndrcvinfo))
return -EINVAL;
- if (copy_from_user(&info, optval, 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);
info.sinfo_timetolive = sp->default_timetolive;
}
- if (copy_to_user(optval, &info, sizeof(struct sctp_sndrcvinfo)))
+ if (put_user(len, optlen))
+ return -EFAULT;
+ if (copy_to_user(optval, &info, len))
return -EFAULT;
return 0;
struct sctp_rtoinfo rtoinfo;
struct sctp_association *asoc;
- if (len != sizeof (struct sctp_rtoinfo))
+ if (len < sizeof (struct sctp_rtoinfo))
return -EINVAL;
- if (copy_from_user(&rtoinfo, optval, sizeof (struct sctp_rtoinfo)))
+ len = sizeof(struct sctp_rtoinfo);
+
+ if (copy_from_user(&rtoinfo, optval, len))
return -EFAULT;
asoc = sctp_id2assoc(sk, rtoinfo.srto_assoc_id);
*
* 7.1.2 SCTP_ASSOCINFO
*
- * This option is used to tune the the maximum retransmission attempts
+ * This option is used to tune the maximum retransmission attempts
* of the association.
* Returns an error if the new association retransmission value is
* greater than the sum of the retransmission value of the peer.
struct list_head *pos;
int cnt = 0;
- if (len != sizeof (struct sctp_assocparams))
+ if (len < sizeof (struct sctp_assocparams))
return -EINVAL;
- if (copy_from_user(&assocparams, optval,
- sizeof (struct sctp_assocparams)))
+ len = sizeof(struct sctp_assocparams);
+
+ if (copy_from_user(&assocparams, optval, len))
return -EFAULT;
asoc = sctp_id2assoc(sk, assocparams.sasoc_assoc_id);
struct sctp_sock *sp;
struct sctp_association *asoc;
- if (len != sizeof(struct sctp_assoc_value))
+ if (len < sizeof(struct sctp_assoc_value))
return -EINVAL;
+ len = sizeof(struct sctp_assoc_value);
+
if (copy_from_user(¶ms, optval, len))
return -EFAULT;
/* Allocate HMAC for generating cookie. */
if (sctp_hmac_alg) {
tfm = crypto_alloc_hash(sctp_hmac_alg, 0, CRYPTO_ALG_ASYNC);
- if (!tfm) {
+ if (IS_ERR(tfm)) {
+ if (net_ratelimit()) {
+ printk(KERN_INFO
+ "SCTP: failed to load transform for %s: %ld\n",
+ sctp_hmac_alg, PTR_ERR(tfm));
+ }
err = -ENOSYS;
goto out;
}