static int sctp_memory_pressure;
static atomic_t sctp_memory_allocated;
-static atomic_t sctp_sockets_allocated;
+struct percpu_counter sctp_sockets_allocated;
static void sctp_enter_memory_pressure(struct sock *sk)
{
if (asoc->ep->sndbuf_policy)
amt = asoc->sndbuf_used;
else
- amt = atomic_read(&asoc->base.sk->sk_wmem_alloc);
+ amt = sk_wmem_alloc_get(asoc->base.sk);
if (amt >= asoc->base.sk->sk_sndbuf) {
if (asoc->base.sk->sk_userlocks & SOCK_SNDBUF_LOCK)
/* Refresh ephemeral port. */
if (!bp->port)
- bp->port = inet_sk(sk)->num;
+ bp->port = inet_sk(sk)->inet_num;
/* Add the address to the bind address list.
* Use GFP_ATOMIC since BHs will be disabled.
/* Copy back into socket for getsockname() use. */
if (!ret) {
- inet_sk(sk)->sport = htons(inet_sk(sk)->num);
+ inet_sk(sk)->inet_sport = htons(inet_sk(sk)->inet_num);
af->to_sk_saddr(addr, sk);
}
goto out_free;
}
+ /* In case the user of sctp_connectx() wants an association
+ * id back, assign one now.
+ */
+ if (assoc_id) {
+ err = sctp_assoc_set_id(asoc, GFP_KERNEL);
+ if (err < 0)
+ goto out_free;
+ }
+
err = sctp_primitive_ASSOCIATE(asoc, NULL);
if (err < 0) {
goto out_free;
}
/* Initialize sk's dport and daddr for getpeername() */
- inet_sk(sk)->dport = htons(asoc->peer.port);
+ inet_sk(sk)->inet_dport = htons(asoc->peer.port);
af = sctp_get_af_specific(sa_addr->sa.sa_family);
af->to_sk_daddr(sa_addr, sk);
sk->sk_err = 0;
timeo = sock_sndtimeo(sk, f_flags & O_NONBLOCK);
err = sctp_wait_for_connect(asoc, &timeo);
- if (!err && assoc_id)
+ if ((err == 0 || err == -EINPROGRESS) && assoc_id)
*assoc_id = asoc->assoc_id;
/* Don't free association on exit. */
return assoc_id;
}
+/*
+ * New (hopefully final) interface for the API. The option buffer is used
+ * both for the returned association id and the addresses.
+ */
+SCTP_STATIC int sctp_getsockopt_connectx3(struct sock* sk, int len,
+ char __user *optval,
+ int __user *optlen)
+{
+ sctp_assoc_t assoc_id = 0;
+ int err = 0;
+
+ if (len < sizeof(assoc_id))
+ return -EINVAL;
+
+ err = __sctp_setsockopt_connectx(sk,
+ (struct sockaddr __user *)(optval + sizeof(assoc_id)),
+ len - sizeof(assoc_id), &assoc_id);
+
+ if (err == 0 || err == -EINPROGRESS) {
+ if (copy_to_user(optval, &assoc_id, sizeof(assoc_id)))
+ return -EFAULT;
+ if (put_user(sizeof(assoc_id), optlen))
+ return -EFAULT;
+ }
+
+ return err;
+}
+
/* API 3.1.4 close() - UDP Style Syntax
* Applications use close() to perform graceful shutdown (as described in
* Section 10.1 of [SCTP]) on ALL the associations currently represented
sctp_lock_sock(sk);
sk->sk_shutdown = SHUTDOWN_MASK;
+ sk->sk_state = SCTP_SS_CLOSING;
ep = sctp_sk(sk)->ep;
sctp_set_owner_w(chunk);
chunk->transport = chunk_tp;
-
- /* Send it to the lower layers. Note: all chunks
- * must either fail or succeed. The lower layer
- * works that way today. Keep it that way or this
- * breaks.
- */
- err = sctp_primitive_SEND(asoc, chunk);
- /* Did the lower layer accept the chunk? */
- if (err)
- sctp_chunk_free(chunk);
- SCTP_DEBUG_PRINTK("We sent primitively.\n");
}
- sctp_datamsg_put(datamsg);
+ /* Send it to the lower layers. Note: all chunks
+ * must either fail or succeed. The lower layer
+ * works that way today. Keep it that way or this
+ * breaks.
+ */
+ err = sctp_primitive_SEND(asoc, datamsg);
+ /* Did the lower layer accept the chunk? */
+ if (err)
+ sctp_datamsg_free(datamsg);
+ else
+ sctp_datamsg_put(datamsg);
+
+ SCTP_DEBUG_PRINTK("We sent primitively.\n");
+
if (err)
goto out_free;
else
len -= skb_len;
__skb_pull(skb, skb_len);
- for (list = skb_shinfo(skb)->frag_list; list; list = list->next) {
+ skb_walk_frags(skb, list) {
rlen = sctp_skb_pull(list, len);
skb->len -= (len-rlen);
skb->data_len -= (len-rlen);
if (err)
goto out_free;
- sock_recv_timestamp(msg, sk, skb);
+ sock_recv_ts_and_drops(msg, sk, skb);
if (sctp_ulpevent_is_notification(event)) {
msg->msg_flags |= MSG_NOTIFICATION;
sp->pf->event_msgname(event, msg->msg_name, addr_len);
* instead a error will be indicated to the user.
*/
static int sctp_setsockopt_disable_fragments(struct sock *sk,
- char __user *optval, int optlen)
+ char __user *optval,
+ unsigned int optlen)
{
int val;
}
static int sctp_setsockopt_events(struct sock *sk, char __user *optval,
- int optlen)
+ unsigned int optlen)
{
if (optlen > sizeof(struct sctp_event_subscribe))
return -EINVAL;
* association is closed.
*/
static int sctp_setsockopt_autoclose(struct sock *sk, char __user *optval,
- int optlen)
+ unsigned int optlen)
{
struct sctp_sock *sp = sctp_sk(sk);
sctp_assoc_sync_pmtu(asoc);
} else if (asoc) {
asoc->pathmtu = params->spp_pathmtu;
- sctp_frag_point(sp, params->spp_pathmtu);
+ sctp_frag_point(asoc, params->spp_pathmtu);
} else {
sp->pathmtu = params->spp_pathmtu;
}
}
static int sctp_setsockopt_peer_addr_params(struct sock *sk,
- char __user *optval, int optlen)
+ char __user *optval,
+ unsigned int optlen)
{
struct sctp_paddrparams params;
struct sctp_transport *trans = NULL;
*/
static int sctp_setsockopt_delayed_ack(struct sock *sk,
- char __user *optval, int optlen)
+ char __user *optval, unsigned int optlen)
{
struct sctp_sack_info params;
struct sctp_transport *trans = NULL;
if (params.sack_delay == 0 && params.sack_freq == 0)
return 0;
} else if (optlen == sizeof(struct sctp_assoc_value)) {
- printk(KERN_WARNING "SCTP: Use of struct sctp_sack_info "
+ printk(KERN_WARNING "SCTP: Use of struct sctp_assoc_value "
"in delayed_ack socket option deprecated\n");
- printk(KERN_WARNING "SCTP: struct sctp_sack_info instead\n");
+ printk(KERN_WARNING "SCTP: Use struct sctp_sack_info instead\n");
if (copy_from_user(¶ms, optval, optlen))
return -EFAULT;
* by the change). With TCP-style sockets, this option is inherited by
* sockets derived from a listener socket.
*/
-static int sctp_setsockopt_initmsg(struct sock *sk, char __user *optval, int optlen)
+static int sctp_setsockopt_initmsg(struct sock *sk, char __user *optval, unsigned int optlen)
{
struct sctp_initmsg sinit;
struct sctp_sock *sp = sctp_sk(sk);
* to this call if the caller is using the UDP model.
*/
static int sctp_setsockopt_default_send_param(struct sock *sk,
- char __user *optval, int optlen)
+ char __user *optval,
+ unsigned int optlen)
{
struct sctp_sndrcvinfo info;
struct sctp_association *asoc;
* association peer's addresses.
*/
static int sctp_setsockopt_primary_addr(struct sock *sk, char __user *optval,
- int optlen)
+ unsigned int optlen)
{
struct sctp_prim prim;
struct sctp_transport *trans;
* integer boolean flag.
*/
static int sctp_setsockopt_nodelay(struct sock *sk, char __user *optval,
- int optlen)
+ unsigned int optlen)
{
int val;
* be changed.
*
*/
-static int sctp_setsockopt_rtoinfo(struct sock *sk, char __user *optval, int optlen) {
+static int sctp_setsockopt_rtoinfo(struct sock *sk, char __user *optval, unsigned int optlen)
+{
struct sctp_rtoinfo rtoinfo;
struct sctp_association *asoc;
* See [SCTP] for more information.
*
*/
-static int sctp_setsockopt_associnfo(struct sock *sk, char __user *optval, int optlen)
+static int sctp_setsockopt_associnfo(struct sock *sk, char __user *optval, unsigned int optlen)
{
struct sctp_assocparams assocparams;
paths++;
}
- /* Only validate asocmaxrxt if we have more then
+ /* Only validate asocmaxrxt if we have more than
* one path/transport. We do this because path
* retransmissions are only counted when we have more
* then one path.
* addresses and a user will receive both PF_INET6 and PF_INET type
* addresses on the socket.
*/
-static int sctp_setsockopt_mappedv4(struct sock *sk, char __user *optval, int optlen)
+static int sctp_setsockopt_mappedv4(struct sock *sk, char __user *optval, unsigned int optlen)
{
int val;
struct sctp_sock *sp = sctp_sk(sk);
}
/*
- * 7.1.17 Set the maximum fragrmentation size (SCTP_MAXSEG)
- *
- * This socket option specifies the maximum size to put in any outgoing
- * SCTP chunk. If a message is larger than this size it will be
+ * 8.1.16. Get or Set the Maximum Fragmentation Size (SCTP_MAXSEG)
+ * This option will get or set the maximum size to put in any outgoing
+ * SCTP DATA chunk. If a message is larger than this size it will be
* fragmented by SCTP into the specified size. Note that the underlying
* SCTP implementation may fragment into smaller sized chunks when the
* PMTU of the underlying association is smaller than the value set by
- * the user.
+ * the user. The default value for this option is '0' which indicates
+ * the user is NOT limiting fragmentation and only the PMTU will effect
+ * SCTP's choice of DATA chunk size. Note also that values set larger
+ * than the maximum size of an IP datagram will effectively let SCTP
+ * control fragmentation (i.e. the same as setting this option to 0).
+ *
+ * The following structure is used to access and modify this parameter:
+ *
+ * struct sctp_assoc_value {
+ * sctp_assoc_t assoc_id;
+ * uint32_t assoc_value;
+ * };
+ *
+ * assoc_id: This parameter is ignored for one-to-one style sockets.
+ * For one-to-many style sockets 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).
+ * assoc_value: This parameter specifies the maximum size in bytes.
*/
-static int sctp_setsockopt_maxseg(struct sock *sk, char __user *optval, int optlen)
+static int sctp_setsockopt_maxseg(struct sock *sk, char __user *optval, unsigned int optlen)
{
+ struct sctp_assoc_value params;
struct sctp_association *asoc;
struct sctp_sock *sp = sctp_sk(sk);
int val;
- if (optlen < sizeof(int))
+ if (optlen == sizeof(int)) {
+ printk(KERN_WARNING
+ "SCTP: Use of int in maxseg socket option deprecated\n");
+ printk(KERN_WARNING
+ "SCTP: Use struct sctp_assoc_value instead\n");
+ if (copy_from_user(&val, optval, optlen))
+ return -EFAULT;
+ params.assoc_id = 0;
+ } else if (optlen == sizeof(struct sctp_assoc_value)) {
+ if (copy_from_user(¶ms, optval, optlen))
+ return -EFAULT;
+ val = params.assoc_value;
+ } else
return -EINVAL;
- if (get_user(val, (int __user *)optval))
- return -EFAULT;
+
if ((val != 0) && ((val < 8) || (val > SCTP_MAX_CHUNK_LEN)))
return -EINVAL;
- sp->user_frag = val;
- /* Update the frag_point of the existing associations. */
- list_for_each_entry(asoc, &(sp->ep->asocs), asocs) {
- asoc->frag_point = sctp_frag_point(sp, asoc->pathmtu);
+ asoc = sctp_id2assoc(sk, params.assoc_id);
+ if (!asoc && params.assoc_id && sctp_style(sk, UDP))
+ return -EINVAL;
+
+ if (asoc) {
+ if (val == 0) {
+ val = asoc->pathmtu;
+ val -= sp->pf->af->net_header_len;
+ val -= sizeof(struct sctphdr) +
+ sizeof(struct sctp_data_chunk);
+ }
+ asoc->user_frag = val;
+ asoc->frag_point = sctp_frag_point(asoc, asoc->pathmtu);
+ } else {
+ sp->user_frag = val;
}
return 0;
* set primary request:
*/
static int sctp_setsockopt_peer_primary_addr(struct sock *sk, char __user *optval,
- int optlen)
+ unsigned int optlen)
{
struct sctp_sock *sp;
struct sctp_endpoint *ep;
}
static int sctp_setsockopt_adaptation_layer(struct sock *sk, char __user *optval,
- int optlen)
+ unsigned int optlen)
{
struct sctp_setadaptation adaptation;
* saved with outbound messages.
*/
static int sctp_setsockopt_context(struct sock *sk, char __user *optval,
- int optlen)
+ unsigned int optlen)
{
struct sctp_assoc_value params;
struct sctp_sock *sp;
*/
static int sctp_setsockopt_fragment_interleave(struct sock *sk,
char __user *optval,
- int optlen)
+ unsigned int optlen)
{
int val;
}
/*
- * 7.1.25. Set or Get the sctp partial delivery point
+ * 8.1.21. Set or Get the SCTP Partial Delivery Point
* (SCTP_PARTIAL_DELIVERY_POINT)
+ *
* This option will set or get the SCTP partial delivery point. This
* point is the size of a message where the partial delivery API will be
* invoked to help free up rwnd space for the peer. Setting this to a
- * lower value will cause partial delivery's to happen more often. The
+ * lower value will cause partial deliveries to happen more often. The
* calls argument is an integer that sets or gets the partial delivery
- * point.
+ * point. Note also that the call will fail if the user attempts to set
+ * this value larger than the socket receive buffer size.
+ *
+ * Note that any single message having a length smaller than or equal to
+ * the SCTP partial delivery point will be delivered in one single read
+ * call as long as the user provided buffer is large enough to hold the
+ * message.
*/
static int sctp_setsockopt_partial_delivery_point(struct sock *sk,
char __user *optval,
- int optlen)
+ unsigned int optlen)
{
u32 val;
if (get_user(val, (int __user *)optval))
return -EFAULT;
+ /* Note: We double the receive buffer from what the user sets
+ * it to be, also initial rwnd is based on rcvbuf/2.
+ */
+ if (val > (sk->sk_rcvbuf >> 1))
+ return -EINVAL;
+
sctp_sk(sk)->pd_point = val;
return 0; /* is this the right error code? */
*/
static int sctp_setsockopt_maxburst(struct sock *sk,
char __user *optval,
- int optlen)
+ unsigned int optlen)
{
struct sctp_assoc_value params;
struct sctp_sock *sp;
int val;
int assoc_id = 0;
- if (optlen < sizeof(int))
- return -EINVAL;
-
if (optlen == sizeof(int)) {
printk(KERN_WARNING
"SCTP: Use of int in max_burst socket option deprecated\n");
* will only effect future associations on the socket.
*/
static int sctp_setsockopt_auth_chunk(struct sock *sk,
- char __user *optval,
- int optlen)
+ char __user *optval,
+ unsigned int optlen)
{
struct sctp_authchunk val;
* endpoint requires the peer to use.
*/
static int sctp_setsockopt_hmac_ident(struct sock *sk,
- char __user *optval,
- int optlen)
+ char __user *optval,
+ unsigned int optlen)
{
struct sctp_hmacalgo *hmacs;
u32 idents;
*/
static int sctp_setsockopt_auth_key(struct sock *sk,
char __user *optval,
- int optlen)
+ unsigned int optlen)
{
struct sctp_authkey *authkey;
struct sctp_association *asoc;
* the association shared key.
*/
static int sctp_setsockopt_active_key(struct sock *sk,
- char __user *optval,
- int optlen)
+ char __user *optval,
+ unsigned int optlen)
{
struct sctp_authkeyid val;
struct sctp_association *asoc;
* This set option will delete a shared secret key from use.
*/
static int sctp_setsockopt_del_key(struct sock *sk,
- char __user *optval,
- int optlen)
+ char __user *optval,
+ unsigned int optlen)
{
struct sctp_authkeyid val;
struct sctp_association *asoc;
* optlen - the size of the buffer.
*/
SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname,
- char __user *optval, int optlen)
+ char __user *optval, unsigned int optlen)
{
int retval = 0;
sp->hmac = NULL;
SCTP_DBG_OBJCNT_INC(sock);
- atomic_inc(&sctp_sockets_allocated);
+ percpu_counter_inc(&sctp_sockets_allocated);
local_bh_disable();
sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
/* Release our hold on the endpoint. */
ep = sctp_sk(sk)->ep;
sctp_endpoint_free(ep);
- atomic_dec(&sctp_sockets_allocated);
+ percpu_counter_dec(&sctp_sockets_allocated);
+ local_bh_disable();
sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
+ local_bh_enable();
}
/* API 4.1.7 shutdown() - TCP Style Syntax
{
struct sock *sk = asoc->base.sk;
struct socket *sock;
- struct inet_sock *inetsk;
struct sctp_af *af;
int err = 0;
if (err < 0)
return err;
- /* 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);
+ 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);
- inetsk = inet_sk(sock->sk);
- inetsk->id = asoc->next_tsn ^ jiffies;
+
+ /* 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;
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_sack_info "
+ printk(KERN_WARNING "SCTP: Use of struct sctp_assoc_value "
"in delayed_ack socket option deprecated\n");
- printk(KERN_WARNING "SCTP: struct sctp_sack_info instead\n");
+ printk(KERN_WARNING "SCTP: Use struct sctp_sack_info instead\n");
if (copy_from_user(¶ms, optval, len))
return -EFAULT;
} else
}
/*
- * 7.1.17 Set the maximum fragrmentation size (SCTP_MAXSEG)
- *
- * This socket option specifies the maximum size to put in any outgoing
- * SCTP chunk. If a message is larger than this size it will be
+ * 8.1.16. Get or Set the Maximum Fragmentation Size (SCTP_MAXSEG)
+ * This option will get or set the maximum size to put in any outgoing
+ * SCTP DATA chunk. If a message is larger than this size it will be
* fragmented by SCTP into the specified size. Note that the underlying
* SCTP implementation may fragment into smaller sized chunks when the
* PMTU of the underlying association is smaller than the value set by
- * the user.
+ * the user. The default value for this option is '0' which indicates
+ * the user is NOT limiting fragmentation and only the PMTU will effect
+ * SCTP's choice of DATA chunk size. Note also that values set larger
+ * than the maximum size of an IP datagram will effectively let SCTP
+ * control fragmentation (i.e. the same as setting this option to 0).
+ *
+ * The following structure is used to access and modify this parameter:
+ *
+ * struct sctp_assoc_value {
+ * sctp_assoc_t assoc_id;
+ * uint32_t assoc_value;
+ * };
+ *
+ * assoc_id: This parameter is ignored for one-to-one style sockets.
+ * For one-to-many style sockets 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).
+ * assoc_value: This parameter specifies the maximum size in bytes.
*/
static int sctp_getsockopt_maxseg(struct sock *sk, int len,
char __user *optval, int __user *optlen)
{
- int val;
+ struct sctp_assoc_value params;
+ struct sctp_association *asoc;
- if (len < sizeof(int))
+ if (len == sizeof(int)) {
+ printk(KERN_WARNING
+ "SCTP: Use of int in maxseg socket option deprecated\n");
+ printk(KERN_WARNING
+ "SCTP: Use struct sctp_assoc_value instead\n");
+ params.assoc_id = 0;
+ } else if (len >= sizeof(struct sctp_assoc_value)) {
+ len = sizeof(struct sctp_assoc_value);
+ if (copy_from_user(¶ms, optval, sizeof(params)))
+ return -EFAULT;
+ } else
return -EINVAL;
- len = sizeof(int);
+ asoc = sctp_id2assoc(sk, params.assoc_id);
+ if (!asoc && params.assoc_id && sctp_style(sk, UDP))
+ return -EINVAL;
+
+ if (asoc)
+ params.assoc_value = asoc->frag_point;
+ else
+ params.assoc_value = sctp_sk(sk)->user_frag;
- val = sctp_sk(sk)->user_frag;
if (put_user(len, optlen))
return -EFAULT;
- if (copy_to_user(optval, &val, len))
- return -EFAULT;
+ if (len == sizeof(int)) {
+ if (copy_to_user(optval, ¶ms.assoc_value, len))
+ return -EFAULT;
+ } else {
+ if (copy_to_user(optval, ¶ms, len))
+ return -EFAULT;
+ }
return 0;
}
struct sctp_sock *sp;
struct sctp_association *asoc;
- if (len < sizeof(int))
- return -EINVAL;
-
if (len == sizeof(int)) {
printk(KERN_WARNING
"SCTP: Use of int in max_burst socket option deprecated\n");
printk(KERN_WARNING
"SCTP: Use struct sctp_assoc_value instead\n");
params.assoc_id = 0;
- } else if (len == sizeof (struct sctp_assoc_value)) {
+ } else if (len >= sizeof(struct sctp_assoc_value)) {
+ len = sizeof(struct sctp_assoc_value);
if (copy_from_user(¶ms, optval, len))
return -EFAULT;
} else
return 0;
}
+/*
+ * 8.2.5. Get the Current Number of Associations (SCTP_GET_ASSOC_NUMBER)
+ * This option gets the current number of associations that are attached
+ * to a one-to-many style socket. The option value is an uint32_t.
+ */
+static int sctp_getsockopt_assoc_number(struct sock *sk, int len,
+ char __user *optval, int __user *optlen)
+{
+ struct sctp_sock *sp = sctp_sk(sk);
+ struct sctp_association *asoc;
+ u32 val = 0;
+
+ if (sctp_style(sk, TCP))
+ return -EOPNOTSUPP;
+
+ if (len < sizeof(u32))
+ return -EINVAL;
+
+ len = sizeof(u32);
+
+ list_for_each_entry(asoc, &(sp->ep->asocs), asocs) {
+ val++;
+ }
+
+ if (put_user(len, optlen))
+ return -EFAULT;
+ if (copy_to_user(optval, &val, len))
+ return -EFAULT;
+
+ return 0;
+}
+
SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
char __user *optval, int __user *optlen)
{
retval = sctp_getsockopt_local_addrs(sk, len, optval,
optlen);
break;
+ case SCTP_SOCKOPT_CONNECTX3:
+ retval = sctp_getsockopt_connectx3(sk, len, optval, optlen);
+ break;
case SCTP_DEFAULT_SEND_PARAM:
retval = sctp_getsockopt_default_send_param(sk, len,
optval, optlen);
retval = sctp_getsockopt_local_auth_chunks(sk, len, optval,
optlen);
break;
+ case SCTP_GET_ASSOC_NUMBER:
+ retval = sctp_getsockopt_assoc_number(sk, len, optval, optlen);
+ break;
default:
retval = -ENOPROTOOPT;
break;
*/
success:
if (!sctp_sk(sk)->bind_hash) {
- inet_sk(sk)->num = snum;
+ inet_sk(sk)->inet_num = snum;
sk_add_bind_node(sk, &pp->owner);
sctp_sk(sk)->bind_hash = pp;
}
}
/*
- * 3.1.3 listen() - UDP Style Syntax
- *
- * By default, new associations are not accepted for UDP style sockets.
- * An application uses listen() to mark a socket as being able to
- * accept new associations.
+ * Move a socket to LISTENING state.
*/
-SCTP_STATIC int sctp_seqpacket_listen(struct sock *sk, int backlog)
+SCTP_STATIC int sctp_listen_start(struct sock *sk, int backlog)
{
struct sctp_sock *sp = sctp_sk(sk);
struct sctp_endpoint *ep = sp->ep;
+ struct crypto_hash *tfm = NULL;
- /* Only UDP style sockets that are not peeled off are allowed to
- * listen().
- */
- if (!sctp_style(sk, UDP))
- return -EINVAL;
-
- /* If backlog is zero, disable listening. */
- if (!backlog) {
- if (sctp_sstate(sk, CLOSED))
- return 0;
-
- sctp_unhash_endpoint(ep);
- sk->sk_state = SCTP_SS_CLOSED;
- return 0;
+ /* Allocate HMAC for generating cookie. */
+ if (!sctp_sk(sk)->hmac && sctp_hmac_alg) {
+ tfm = crypto_alloc_hash(sctp_hmac_alg, 0, CRYPTO_ALG_ASYNC);
+ 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));
+ }
+ return -ENOSYS;
+ }
+ sctp_sk(sk)->hmac = tfm;
}
- /* 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
* extensions draft, but follows the practice as seen in TCP
* sockets.
*
- * Additionally, turn off fastreuse flag since we are not listening
*/
sk->sk_state = SCTP_SS_LISTENING;
if (!ep->base.bind_addr.port) {
if (sctp_autobind(sk))
return -EAGAIN;
} else {
- if (sctp_get_port(sk, inet_sk(sk)->num)) {
+ if (sctp_get_port(sk, inet_sk(sk)->inet_num)) {
sk->sk_state = SCTP_SS_CLOSED;
return -EADDRINUSE;
}
- sctp_sk(sk)->bind_hash->fastreuse = 0;
}
- sctp_hash_endpoint(ep);
- return 0;
-}
-
-/*
- * 4.1.3 listen() - TCP Style Syntax
- *
- * Applications uses listen() to ready the SCTP endpoint for accepting
- * inbound associations.
- */
-SCTP_STATIC int sctp_stream_listen(struct sock *sk, int backlog)
-{
- struct sctp_sock *sp = sctp_sk(sk);
- struct sctp_endpoint *ep = sp->ep;
-
- /* If backlog is zero, disable listening. */
- if (!backlog) {
- if (sctp_sstate(sk, CLOSED))
- return 0;
-
- sctp_unhash_endpoint(ep);
- sk->sk_state = SCTP_SS_CLOSED;
- return 0;
- }
-
- 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
- * picks an ephemeral port and will choose an address set equivalent
- * to binding with a wildcard address.
- *
- * This is not currently spelled out in the SCTP sockets
- * extensions draft, but follows the practice as seen in TCP
- * sockets.
- */
- sk->sk_state = SCTP_SS_LISTENING;
- if (!ep->base.bind_addr.port) {
- if (sctp_autobind(sk))
- return -EAGAIN;
- } else
- sctp_sk(sk)->bind_hash->fastreuse = 0;
-
sk->sk_max_ack_backlog = backlog;
sctp_hash_endpoint(ep);
return 0;
}
/*
+ * 4.1.3 / 5.1.3 listen()
+ *
+ * By default, new associations are not accepted for UDP style sockets.
+ * An application uses listen() to mark a socket as being able to
+ * accept new associations.
+ *
+ * On TCP style sockets, applications use listen() to ready the SCTP
+ * endpoint for accepting inbound associations.
+ *
+ * On both types of endpoints a backlog of '0' disables listening.
+ *
* Move a socket to LISTENING state.
*/
int sctp_inet_listen(struct socket *sock, int backlog)
{
struct sock *sk = sock->sk;
- struct crypto_hash *tfm = NULL;
+ struct sctp_endpoint *ep = sctp_sk(sk)->ep;
int err = -EINVAL;
if (unlikely(backlog < 0))
- goto out;
+ return err;
sctp_lock_sock(sk);
+ /* Peeled-off sockets are not allowed to listen(). */
+ if (sctp_style(sk, UDP_HIGH_BANDWIDTH))
+ goto out;
+
if (sock->state != SS_UNCONNECTED)
goto out;
- /* Allocate HMAC for generating cookie. */
- if (!sctp_sk(sk)->hmac && sctp_hmac_alg) {
- tfm = crypto_alloc_hash(sctp_hmac_alg, 0, CRYPTO_ALG_ASYNC);
- 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;
+ /* If backlog is zero, disable listening. */
+ if (!backlog) {
+ if (sctp_sstate(sk, CLOSED))
goto out;
- }
- }
- switch (sock->type) {
- case SOCK_SEQPACKET:
- err = sctp_seqpacket_listen(sk, backlog);
- break;
- case SOCK_STREAM:
- err = sctp_stream_listen(sk, backlog);
- break;
- default:
- break;
+ err = 0;
+ sctp_unhash_endpoint(ep);
+ sk->sk_state = SCTP_SS_CLOSED;
+ if (sk->sk_reuse)
+ sctp_sk(sk)->bind_hash->fastreuse = 1;
+ goto out;
}
- if (err)
- goto cleanup;
+ /* If we are already listening, just update the backlog */
+ if (sctp_sstate(sk, LISTENING))
+ sk->sk_max_ack_backlog = backlog;
+ else {
+ err = sctp_listen_start(sk, backlog);
+ if (err)
+ goto out;
+ }
- /* Store away the transform reference. */
- if (!sctp_sk(sk)->hmac)
- sctp_sk(sk)->hmac = tfm;
+ err = 0;
out:
sctp_release_sock(sk);
return err;
-cleanup:
- crypto_free_hash(tfm);
- goto out;
}
/*
static inline void __sctp_put_port(struct sock *sk)
{
struct sctp_bind_hashbucket *head =
- &sctp_port_hashtable[sctp_phashfn(inet_sk(sk)->num)];
+ &sctp_port_hashtable[sctp_phashfn(inet_sk(sk)->inet_num)];
struct sctp_bind_bucket *pp;
sctp_spin_lock(&head->lock);
pp = sctp_sk(sk)->bind_hash;
__sk_del_bind_node(sk);
sctp_sk(sk)->bind_hash = NULL;
- inet_sk(sk)->num = 0;
+ inet_sk(sk)->inet_num = 0;
sctp_bucket_destroy(pp);
sctp_spin_unlock(&head->lock);
}
/* Initialize a local sockaddr structure to INADDR_ANY. */
af = sctp_sk(sk)->pf->af;
- port = htons(inet_sk(sk)->num);
+ port = htons(inet_sk(sk)->inet_num);
af->inaddr_any(&autoaddr, port);
return sctp_do_bind(sk, &autoaddr, af->sockaddr_len);
{
int amt = 0;
- amt = sk->sk_sndbuf - atomic_read(&sk->sk_wmem_alloc);
+ amt = sk->sk_sndbuf - sk_wmem_alloc_get(sk);
if (amt < 0)
amt = 0;
return amt;
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;
goto done;
/* Don't forget the fragments. */
- for (frag = skb_shinfo(skb)->frag_list; frag; frag = frag->next)
+ skb_walk_frags(skb, frag)
sctp_skb_set_owner_r_frag(frag, sk);
done:
sctp_skb_set_owner_r(skb, sk);
}
+void sctp_copy_sock(struct sock *newsk, struct sock *sk,
+ struct sctp_association *asoc)
+{
+ struct inet_sock *inet = inet_sk(sk);
+ struct inet_sock *newinet = inet_sk(newsk);
+
+ newsk->sk_type = sk->sk_type;
+ newsk->sk_bound_dev_if = sk->sk_bound_dev_if;
+ newsk->sk_flags = sk->sk_flags;
+ newsk->sk_no_check = sk->sk_no_check;
+ newsk->sk_reuse = sk->sk_reuse;
+
+ newsk->sk_shutdown = sk->sk_shutdown;
+ newsk->sk_destruct = inet_sock_destruct;
+ newsk->sk_family = sk->sk_family;
+ newsk->sk_protocol = IPPROTO_SCTP;
+ newsk->sk_backlog_rcv = sk->sk_prot->backlog_rcv;
+ newsk->sk_sndbuf = sk->sk_sndbuf;
+ newsk->sk_rcvbuf = sk->sk_rcvbuf;
+ newsk->sk_lingertime = sk->sk_lingertime;
+ newsk->sk_rcvtimeo = sk->sk_rcvtimeo;
+ newsk->sk_sndtimeo = sk->sk_sndtimeo;
+
+ newinet = inet_sk(newsk);
+
+ /* Initialize sk's sport, dport, rcv_saddr and daddr for
+ * getsockname() and getpeername()
+ */
+ newinet->inet_sport = inet->inet_sport;
+ newinet->inet_saddr = inet->inet_saddr;
+ newinet->inet_rcv_saddr = inet->inet_rcv_saddr;
+ newinet->inet_dport = htons(asoc->peer.port);
+ newinet->pmtudisc = inet->pmtudisc;
+ newinet->inet_id = asoc->next_tsn ^ jiffies;
+
+ newinet->uc_ttl = inet->uc_ttl;
+ newinet->mc_loop = 1;
+ newinet->mc_ttl = 1;
+ newinet->mc_index = 0;
+ newinet->mc_list = NULL;
+}
+
/* Populate the fields of the newsk from the oldsk and migrate the assoc
* and its messages to the newsk.
*/
newsp->hmac = NULL;
/* Hook this new socket in to the bind_hash list. */
- head = &sctp_port_hashtable[sctp_phashfn(inet_sk(oldsk)->num)];
+ head = &sctp_port_hashtable[sctp_phashfn(inet_sk(oldsk)->inet_num)];
sctp_local_bh_disable();
sctp_spin_lock(&head->lock);
pp = sctp_sk(oldsk)->bind_hash;
sk_add_bind_node(newsk, &pp->owner);
sctp_sk(newsk)->bind_hash = pp;
- inet_sk(newsk)->num = inet_sk(oldsk)->num;
+ inet_sk(newsk)->inet_num = inet_sk(oldsk)->inet_num;
sctp_spin_unlock(&head->lock);
sctp_local_bh_enable();
sctp_skb_for_each(skb, &oldsk->sk_receive_queue, tmp) {
event = sctp_skb2event(skb);
if (event->asoc == assoc) {
- 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_frag(skb, newsk);
sctp_skb_for_each(skb, &oldsp->pd_lobby, tmp) {
event = sctp_skb2event(skb);
if (event->asoc == assoc) {
- sctp_sock_rfree_frag(skb);
__skb_unlink(skb, &oldsp->pd_lobby);
__skb_queue_tail(queue, skb);
sctp_skb_set_owner_r_frag(skb, newsk);
}
- sctp_skb_for_each(skb, &assoc->ulpq.reasm, tmp) {
- sctp_sock_rfree_frag(skb);
+ sctp_skb_for_each(skb, &assoc->ulpq.reasm, tmp)
sctp_skb_set_owner_r_frag(skb, newsk);
- }
- sctp_skb_for_each(skb, &assoc->ulpq.lobby, tmp) {
- sctp_sock_rfree_frag(skb);
+ sctp_skb_for_each(skb, &assoc->ulpq.lobby, tmp)
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