/* ADDIP Section 4.1.1 Congestion Control of ASCONF Chunks
*
- * R1) One and only one ASCONF Chunk MAY be in transit and unacknowledged
+ * R1) One and only one ASCONF Chunk MAY be in transit and unacknowledged
* at any one time. If a sender, after sending an ASCONF chunk, decides
- * it needs to transfer another ASCONF Chunk, it MUST wait until the
+ * it needs to transfer another ASCONF Chunk, it MUST wait until the
* ASCONF-ACK Chunk returns from the previous ASCONF Chunk before sending a
- * subsequent ASCONF. Note this restriction binds each side, so at any
- * time two ASCONF may be in-transit on any given association (one sent
+ * subsequent ASCONF. Note this restriction binds each side, so at any
+ * time two ASCONF may be in-transit on any given association (one sent
* from each endpoint).
*/
static int sctp_send_asconf(struct sctp_association *asoc,
/* If there is an outstanding ASCONF chunk, queue it for later
* transmission.
- */
+ */
if (asoc->addip_last_asconf) {
list_add_tail(&chunk->list, &asoc->addip_chunk_list);
- goto out;
+ goto out;
}
/* Hold the chunk until an ASCONF_ACK is received. */
goto err_bindx_add;
}
- retval = sctp_do_bind(sk, (union sctp_addr *)sa_addr,
+ retval = sctp_do_bind(sk, (union sctp_addr *)sa_addr,
af->sockaddr_len);
addr_buf += af->sockaddr_len;
* associations that are part of the endpoint indicating that a list of local
* addresses are added to the endpoint.
*
- * If any of the addresses is already in the bind address list of the
+ * If any of the addresses is already in the bind address list of the
* association, we do not send the chunk for that association. But it will not
* affect other associations.
*
* Only sctp_setsockopt_bindx() is supposed to call this function.
*/
-static int sctp_send_asconf_add_ip(struct sock *sk,
+static int sctp_send_asconf_add_ip(struct sock *sk,
struct sockaddr *addrs,
int addrcnt)
{
continue;
/* Check if any address in the packed array of addresses is
- * in the bind address list of the association. If so,
- * do not send the asconf chunk to its peer, but continue with
+ * in the bind address list of the association. If so,
+ * do not send the asconf chunk to its peer, but continue with
* other associations.
*/
addr_buf = addrs;
retval = -EINVAL;
goto err_bindx_rem;
}
+
+ if (!af->addr_valid(sa_addr, sp, NULL)) {
+ retval = -EADDRNOTAVAIL;
+ goto err_bindx_rem;
+ }
+
if (sa_addr->v4.sin_port != htons(bp->port)) {
retval = -EINVAL;
goto err_bindx_rem;
* the associations that are part of the endpoint indicating that a list of
* local addresses are removed from the endpoint.
*
- * If any of the addresses is already in the bind address list of the
+ * If any of the addresses is already in the bind address list of the
* association, we do not send the chunk for that association. But it will not
* affect other associations.
*
continue;
/* Check if any address in the packed array of addresses is
- * not present in the bind address list of the association.
+ * not present in the bind address list of the association.
* If so, do not send the asconf chunk to its peer, but
* continue with other associations.
*/
return -EFAULT;
}
- /* Walk through the addrs buffer and count the number of addresses. */
+ /* Walk through the addrs buffer and count the number of addresses. */
addr_buf = kaddrs;
while (walk_size < addrs_size) {
sa_addr = (struct sockaddr *)addr_buf;
/* If the address family is not supported or if this address
* causes the address buffer to overflow return EINVAL.
- */
+ */
if (!af || (walk_size + af->sockaddr_len) > addrs_size) {
kfree(kaddrs);
return -EINVAL;
default:
err = -EINVAL;
break;
- };
+ };
out:
kfree(kaddrs);
}
} else {
/*
- * If an unprivileged user inherits a 1-many
- * style socket with open associations on a
- * privileged port, it MAY be permitted to
- * accept new associations, but it SHOULD NOT
+ * If an unprivileged user inherits a 1-many
+ * style socket with open associations on a
+ * privileged port, it MAY be permitted to
+ * accept new associations, but it SHOULD NOT
* be permitted to open new associations.
*/
if (ep->base.bind_addr.port < PROT_SOCK &&
out_free:
SCTP_DEBUG_PRINTK("About to exit __sctp_connect() free asoc: %p"
- " kaddrs: %p err: %d\n",
- asoc, kaddrs, err);
+ " kaddrs: %p err: %d\n",
+ asoc, kaddrs, err);
if (asoc)
sctp_association_free(asoc);
return err;
* length messages when SCTP_EOF|SCTP_ABORT is not set.
* If SCTP_ABORT is set, the message length could be non zero with
* the msg_iov set to the user abort reason.
- */
+ */
if (((sinfo_flags & SCTP_EOF) && (msg_len > 0)) ||
(!(sinfo_flags & (SCTP_EOF|SCTP_ABORT)) && (msg_len == 0))) {
err = -EINVAL;
= sinit->sinit_max_attempts;
}
if (sinit->sinit_max_init_timeo) {
- asoc->max_init_timeo =
+ asoc->max_init_timeo =
msecs_to_jiffies(sinit->sinit_max_init_timeo);
}
}
/* 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.assoc_id);
if (!asoc && params.assoc_id && sctp_style(sk, UDP))
return -EINVAL;
if (asoc) {
asoc->sackdelay =
msecs_to_jiffies(params.assoc_value);
- asoc->param_flags =
+ asoc->param_flags =
(asoc->param_flags & ~SPP_SACKDELAY) |
SPP_SACKDELAY_ENABLE;
} else {
sp->sackdelay = params.assoc_value;
- sp->param_flags =
+ sp->param_flags =
(sp->param_flags & ~SPP_SACKDELAY) |
SPP_SACKDELAY_ENABLE;
}
} else {
if (asoc) {
- asoc->param_flags =
+ asoc->param_flags =
(asoc->param_flags & ~SPP_SACKDELAY) |
SPP_SACKDELAY_DISABLE;
} else {
- sp->param_flags =
+ sp->param_flags =
(sp->param_flags & ~SPP_SACKDELAY) |
SPP_SACKDELAY_DISABLE;
}
if (params.assoc_value) {
trans->sackdelay =
msecs_to_jiffies(params.assoc_value);
- trans->param_flags =
+ trans->param_flags =
(trans->param_flags & ~SPP_SACKDELAY) |
SPP_SACKDELAY_ENABLE;
} else {
- trans->param_flags =
+ trans->param_flags =
(trans->param_flags & ~SPP_SACKDELAY) |
SPP_SACKDELAY_DISABLE;
}
}
}
-
+
return 0;
}
return -EFAULT;
if (sinit.sinit_num_ostreams)
- sp->initmsg.sinit_num_ostreams = sinit.sinit_num_ostreams;
+ sp->initmsg.sinit_num_ostreams = sinit.sinit_num_ostreams;
if (sinit.sinit_max_instreams)
- sp->initmsg.sinit_max_instreams = sinit.sinit_max_instreams;
+ sp->initmsg.sinit_max_instreams = sinit.sinit_max_instreams;
if (sinit.sinit_max_attempts)
- sp->initmsg.sinit_max_attempts = sinit.sinit_max_attempts;
+ sp->initmsg.sinit_max_attempts = sinit.sinit_max_attempts;
if (sinit.sinit_max_init_timeo)
- sp->initmsg.sinit_max_init_timeo = sinit.sinit_max_init_timeo;
+ sp->initmsg.sinit_max_init_timeo = sinit.sinit_max_init_timeo;
return 0;
}
if (asoc) {
if (rtoinfo.srto_initial != 0)
- asoc->rto_initial =
+ asoc->rto_initial =
msecs_to_jiffies(rtoinfo.srto_initial);
if (rtoinfo.srto_max != 0)
asoc->rto_max = msecs_to_jiffies(rtoinfo.srto_max);
/* Update the frag_point of the existing associations. */
list_for_each(pos, &(sp->ep->asocs)) {
asoc = list_entry(pos, struct sctp_association, asocs);
- asoc->frag_point = sctp_frag_point(sp, asoc->pathmtu);
+ asoc->frag_point = sctp_frag_point(sp, asoc->pathmtu);
}
return 0;
return -EFAULT;
asoc = sctp_id2assoc(sk, prim.sspp_assoc_id);
- if (!asoc)
+ if (!asoc)
return -EINVAL;
if (!asoc->peer.asconf_capable)
out:
sctp_release_sock(sk);
- *err = error;
+ *err = error;
return newsk;
}
sp->assocparams.sasoc_cookie_life = sctp_valid_cookie_life;
/* Initialize default event subscriptions. By default, all the
- * options are off.
+ * options are off.
*/
memset(&sp->subscribe, 0, sizeof(struct sctp_event_subscribe));
sp->pathmtu = 0; // allow default discovery
sp->sackdelay = sctp_sack_timeout;
sp->param_flags = SPP_HB_ENABLE |
- SPP_PMTUD_ENABLE |
- SPP_SACKDELAY_ENABLE;
+ SPP_PMTUD_ENABLE |
+ SPP_SACKDELAY_ENABLE;
/* If enabled no SCTP message fragmentation will be performed.
* Configure through SCTP_DISABLE_FRAGMENTS socket option.
/* 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.assoc_id);
if (!asoc && params.assoc_id && sctp_style(sk, UDP))
return -EINVAL;
return cnt;
}
-/*
+/*
* Old API for getting list of peer addresses. Does not work for 32-bit
* programs running on a 64-bit kernel
*/
return -EINVAL;
to = optval + offsetof(struct sctp_getaddrs,addrs);
- space_left = len - sizeof(struct sctp_getaddrs) -
+ space_left = len - sizeof(struct sctp_getaddrs) -
offsetof(struct sctp_getaddrs,addrs);
list_for_each(pos, &asoc->peer.transport_addr_list) {
addr = list_entry(pos,
struct sctp_sockaddr_entry,
list);
- if ((PF_INET == sk->sk_family) &&
+ if ((PF_INET == sk->sk_family) &&
(AF_INET6 == addr->a.sa.sa_family))
continue;
cnt++;
list_for_each_safe(pos, next, &sctp_local_addr_list) {
addr = list_entry(pos, struct sctp_sockaddr_entry, list);
- if ((PF_INET == sk->sk_family) &&
+ if ((PF_INET == sk->sk_family) &&
(AF_INET6 == addr->a.sa.sa_family))
continue;
memcpy(&temp, &addr->a, sizeof(temp));
list_for_each_safe(pos, next, &sctp_local_addr_list) {
addr = list_entry(pos, struct sctp_sockaddr_entry, list);
- if ((PF_INET == sk->sk_family) &&
+ if ((PF_INET == sk->sk_family) &&
(AF_INET6 == addr->a.sa.sa_family))
continue;
memcpy(&temp, &addr->a, sizeof(temp));
err = cnt;
goto unlock;
}
- goto copy_getaddrs;
+ goto copy_getaddrs;
}
}
err = cnt;
goto unlock;
}
- goto copy_getaddrs;
+ goto copy_getaddrs;
}
}
if (!asoc->peer.primary_path)
return -ENOTCONN;
-
+
memcpy(&prim.ssp_addr, &asoc->peer.primary_path->ipaddr,
asoc->peer.primary_path->af_specific->sockaddr_len);
if (!backlog) {
if (sctp_sstate(sk, CLOSED))
return 0;
-
+
sctp_unhash_endpoint(ep);
sk->sk_state = SCTP_SS_CLOSED;
}
/* Return if we are already listening. */
if (sctp_sstate(sk, LISTENING))
return 0;
-
+
/*
* If a bind() or sctp_bindx() is not called prior to a listen()
* call that allows new associations to be accepted, the system
if (!backlog) {
if (sctp_sstate(sk, CLOSED))
return 0;
-
+
sctp_unhash_endpoint(ep);
sk->sk_state = SCTP_SS_CLOSED;
}
*/
if (sctp_style(sk, TCP) && sctp_sstate(sk, LISTENING))
return (!list_empty(&sp->ep->asocs)) ?
- (POLLIN | POLLRDNORM) : 0;
+ (POLLIN | POLLRDNORM) : 0;
mask = 0;
DEFINE_WAIT(wait);
SCTP_DEBUG_PRINTK("wait_for_sndbuf: asoc=%p, timeo=%ld, msg_len=%zu\n",
- asoc, (long)(*timeo_p), msg_len);
+ asoc, (long)(*timeo_p), msg_len);
/* Increment the association's refcnt. */
sctp_association_hold(asoc);
finish_wait(sk->sk_sleep, &wait);
}
+static void sctp_sock_rfree_frag(struct sk_buff *skb)
+{
+ struct sk_buff *frag;
+
+ if (!skb->data_len)
+ goto done;
+
+ /* Don't forget the fragments. */
+ for (frag = skb_shinfo(skb)->frag_list; frag; frag = frag->next)
+ sctp_sock_rfree_frag(frag);
+
+done:
+ sctp_sock_rfree(skb);
+}
+
+static void sctp_skb_set_owner_r_frag(struct sk_buff *skb, struct sock *sk)
+{
+ struct sk_buff *frag;
+
+ if (!skb->data_len)
+ goto done;
+
+ /* Don't forget the fragments. */
+ for (frag = skb_shinfo(skb)->frag_list; frag; frag = frag->next)
+ sctp_skb_set_owner_r_frag(frag, sk);
+
+done:
+ sctp_skb_set_owner_r(skb, sk);
+}
+
/* Populate the fields of the newsk from the oldsk and migrate the assoc
* and its messages to the newsk.
*/
sctp_skb_for_each(skb, &oldsk->sk_receive_queue, tmp) {
event = sctp_skb2event(skb);
if (event->asoc == assoc) {
- sctp_sock_rfree(skb);
+ sctp_sock_rfree_frag(skb);
__skb_unlink(skb, &oldsk->sk_receive_queue);
__skb_queue_tail(&newsk->sk_receive_queue, skb);
- sctp_skb_set_owner_r(skb, newsk);
+ sctp_skb_set_owner_r_frag(skb, newsk);
}
}
sctp_skb_for_each(skb, &oldsp->pd_lobby, tmp) {
event = sctp_skb2event(skb);
if (event->asoc == assoc) {
- sctp_sock_rfree(skb);
+ sctp_sock_rfree_frag(skb);
__skb_unlink(skb, &oldsp->pd_lobby);
__skb_queue_tail(queue, skb);
- sctp_skb_set_owner_r(skb, newsk);
+ sctp_skb_set_owner_r_frag(skb, newsk);
}
}
}
+ sctp_skb_for_each(skb, &assoc->ulpq.reasm, tmp) {
+ sctp_sock_rfree_frag(skb);
+ sctp_skb_set_owner_r_frag(skb, newsk);
+ }
+
+ sctp_skb_for_each(skb, &assoc->ulpq.lobby, tmp) {
+ sctp_sock_rfree_frag(skb);
+ sctp_skb_set_owner_r_frag(skb, newsk);
+ }
+
/* Set the type of socket to indicate that it is peeled off from the
* original UDP-style socket or created with the accept() call on a
* TCP-style socket..