-/* SCTP kernel reference Implementation
+/* SCTP kernel implementation
* (C) Copyright IBM Corp. 2001, 2004
* Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001-2002 Nokia, Inc.
* Copyright (c) 2001 La Monte H.P. Yarroll
*
- * This file is part of the SCTP kernel reference Implementation
+ * This file is part of the SCTP kernel implementation
*
* These functions interface with the sockets layer to implement the
* SCTP Extensions for the Sockets API.
* functions--this file is the functions which populate the struct proto
* for SCTP which is the BOTTOM of the sockets interface.
*
- * The SCTP reference implementation is free software;
+ * This SCTP implementation is free software;
* you can redistribute it and/or modify it under the terms of
* the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
- * The SCTP reference implementation is distributed in the hope that it
+ * This SCTP implementation is distributed in the hope that it
* will be useful, but WITHOUT ANY WARRANTY; without even the implied
* ************************
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#include <linux/poll.h>
#include <linux/init.h>
#include <linux/crypto.h>
+#include <linux/slab.h>
#include <net/ip.h>
#include <net/icmp.h>
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(void)
+static void sctp_enter_memory_pressure(struct sock *sk)
{
sctp_memory_pressure = 1;
}
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)
sizeof(struct sctp_chunk);
atomic_add(sizeof(struct sctp_chunk), &sk->sk_wmem_alloc);
- sk_charge_skb(sk, chunk->skb);
+ sk->sk_wmem_queued += chunk->skb->truesize;
+ sk_mem_charge(sk, chunk->skb->truesize);
}
/* Verify that this is a valid address. */
if (len < sizeof (struct sockaddr))
return NULL;
- /* Does this PF support this AF? */
- if (!opt->pf->af_supported(addr->sa.sa_family, opt))
- return NULL;
+ /* V4 mapped address are really of AF_INET family */
+ if (addr->sa.sa_family == AF_INET6 &&
+ ipv6_addr_v4mapped(&addr->v6.sin6_addr)) {
+ if (!opt->pf->af_supported(AF_INET, opt))
+ return NULL;
+ } else {
+ /* Does this PF support this AF? */
+ if (!opt->pf->af_supported(addr->sa.sa_family, opt))
+ return NULL;
+ }
/* If we get this far, af is valid. */
af = sctp_get_af_specific(addr->sa.sa_family);
if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))
return -EACCES;
+ /* See if the address matches any of the addresses we may have
+ * already bound before checking against other endpoints.
+ */
+ if (sctp_bind_addr_match(bp, addr, sp))
+ return -EINVAL;
+
/* Make sure we are allowed to bind here.
* The function sctp_get_port_local() does duplicate address
* detection.
*/
addr->v4.sin_port = htons(snum);
if ((ret = sctp_get_port_local(sk, addr))) {
- if (ret == (long) sk) {
- /* This endpoint has a conflicting address. */
- return -EINVAL;
- } else {
- return -EADDRINUSE;
- }
+ return -EADDRINUSE;
}
/* 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.
*/
- ret = sctp_add_bind_addr(bp, addr, 1, GFP_ATOMIC);
+ ret = sctp_add_bind_addr(bp, addr, SCTP_ADDR_SRC, GFP_ATOMIC);
/* 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);
}
union sctp_addr saveaddr;
void *addr_buf;
struct sctp_af *af;
- struct list_head *pos;
struct list_head *p;
int i;
int retval = 0;
ep = sp->ep;
SCTP_DEBUG_PRINTK("%s: (sk: %p, addrs: %p, addrcnt: %d)\n",
- __FUNCTION__, sk, addrs, addrcnt);
+ __func__, sk, addrs, addrcnt);
- list_for_each(pos, &ep->asocs) {
- asoc = list_entry(pos, struct sctp_association, asocs);
+ list_for_each_entry(asoc, &ep->asocs, asocs) {
if (!asoc->peer.asconf_capable)
continue;
addr = (union sctp_addr *)addr_buf;
af = sctp_get_af_specific(addr->v4.sin_family);
memcpy(&saveaddr, addr, af->sockaddr_len);
- retval = sctp_add_bind_addr(bp, &saveaddr, 0,
- GFP_ATOMIC);
+ retval = sctp_add_bind_addr(bp, &saveaddr,
+ SCTP_ADDR_NEW, GFP_ATOMIC);
addr_buf += af->sockaddr_len;
}
}
* socket routing and failover schemes. Refer to comments in
* sctp_do_bind(). -daisy
*/
- retval = sctp_del_bind_addr(bp, sa_addr, call_rcu);
+ retval = sctp_del_bind_addr(bp, sa_addr);
addr_buf += af->sockaddr_len;
err_bindx_rem:
union sctp_addr *laddr;
void *addr_buf;
struct sctp_af *af;
- struct list_head *pos, *pos1;
struct sctp_sockaddr_entry *saddr;
int i;
int retval = 0;
ep = sp->ep;
SCTP_DEBUG_PRINTK("%s: (sk: %p, addrs: %p, addrcnt: %d)\n",
- __FUNCTION__, sk, addrs, addrcnt);
+ __func__, sk, addrs, addrcnt);
- list_for_each(pos, &ep->asocs) {
- asoc = list_entry(pos, struct sctp_association, asocs);
+ list_for_each_entry(asoc, &ep->asocs, asocs) {
if (!asoc->peer.asconf_capable)
continue;
af = sctp_get_af_specific(laddr->v4.sin_family);
list_for_each_entry(saddr, &bp->address_list, list) {
if (sctp_cmp_addr_exact(&saddr->a, laddr))
- saddr->use_as_src = 0;
+ saddr->state = SCTP_ADDR_DEL;
}
addr_buf += af->sockaddr_len;
}
* as some of the addresses in the bind address list are
* about to be deleted and cannot be used as source addresses.
*/
- list_for_each(pos1, &asoc->peer.transport_addr_list) {
- transport = list_entry(pos1, struct sctp_transport,
- transports);
+ list_for_each_entry(transport, &asoc->peer.transport_addr_list,
+ transports) {
dst_release(transport->dst);
sctp_transport_route(transport, NULL,
sctp_sk(asoc->base.sk));
*/
static int __sctp_connect(struct sock* sk,
struct sockaddr *kaddrs,
- int addrs_size)
+ int addrs_size,
+ sctp_assoc_t *assoc_id)
{
struct sctp_sock *sp;
struct sctp_endpoint *ep;
err = -ENOMEM;
goto out_free;
}
+
+ err = sctp_assoc_set_bind_addr_from_ep(asoc, scope,
+ GFP_KERNEL);
+ if (err < 0) {
+ goto out_free;
+ }
+
}
/* Prime the peer's transport structures. */
walk_size += af->sockaddr_len;
}
- err = sctp_assoc_set_bind_addr_from_ep(asoc, GFP_KERNEL);
- if (err < 0) {
- 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);
}
/* 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 == 0 || err == -EINPROGRESS) && assoc_id)
+ *assoc_id = asoc->assoc_id;
/* Don't free association on exit. */
asoc = NULL;
/* Helper for tunneling sctp_connectx() requests through sctp_setsockopt()
*
* API 8.9
- * int sctp_connectx(int sd, struct sockaddr *addrs, int addrcnt);
+ * int sctp_connectx(int sd, struct sockaddr *addrs, int addrcnt,
+ * sctp_assoc_t *asoc);
*
* If sd is an IPv4 socket, the addresses passed must be IPv4 addresses.
* If the sd is an IPv6 socket, the addresses passed can either be IPv4
* representation is termed a "packed array" of addresses). The caller
* specifies the number of addresses in the array with addrcnt.
*
- * On success, sctp_connectx() returns 0. On failure, sctp_connectx() returns
- * -1, and sets errno to the appropriate error code.
+ * On success, sctp_connectx() returns 0. It also sets the assoc_id to
+ * the association id of the new association. On failure, sctp_connectx()
+ * returns -1, and sets errno to the appropriate error code. The assoc_id
+ * is not touched by the kernel.
*
* For SCTP, the port given in each socket address must be the same, or
* sctp_connectx() will fail, setting errno to EINVAL.
* addrs The pointer to the addresses in user land
* addrssize Size of the addrs buffer
*
- * Returns 0 if ok, <0 errno code on error.
+ * Returns >=0 if ok, <0 errno code on error.
*/
-SCTP_STATIC int sctp_setsockopt_connectx(struct sock* sk,
+SCTP_STATIC int __sctp_setsockopt_connectx(struct sock* sk,
struct sockaddr __user *addrs,
- int addrs_size)
+ int addrs_size,
+ sctp_assoc_t *assoc_id)
{
int err = 0;
struct sockaddr *kaddrs;
SCTP_DEBUG_PRINTK("%s - sk %p addrs %p addrs_size %d\n",
- __FUNCTION__, sk, addrs, addrs_size);
+ __func__, sk, addrs, addrs_size);
if (unlikely(addrs_size <= 0))
return -EINVAL;
if (__copy_from_user(kaddrs, addrs, addrs_size)) {
err = -EFAULT;
} else {
- err = __sctp_connect(sk, kaddrs, addrs_size);
+ err = __sctp_connect(sk, kaddrs, addrs_size, assoc_id);
}
kfree(kaddrs);
+
+ return err;
+}
+
+/*
+ * This is an older interface. It's kept for backward compatibility
+ * to the option that doesn't provide association id.
+ */
+SCTP_STATIC int sctp_setsockopt_connectx_old(struct sock* sk,
+ struct sockaddr __user *addrs,
+ int addrs_size)
+{
+ return __sctp_setsockopt_connectx(sk, addrs, addrs_size, NULL);
+}
+
+/*
+ * New interface for the API. The since the API is done with a socket
+ * option, to make it simple we feed back the association id is as a return
+ * indication to the call. Error is always negative and association id is
+ * always positive.
+ */
+SCTP_STATIC int sctp_setsockopt_connectx(struct sock* sk,
+ struct sockaddr __user *addrs,
+ int addrs_size)
+{
+ sctp_assoc_t assoc_id = 0;
+ int err = 0;
+
+ err = __sctp_setsockopt_connectx(sk, addrs, addrs_size, &assoc_id);
+
+ if (err)
+ return err;
+ else
+ return assoc_id;
+}
+
+/*
+ * New (hopefully final) interface for the API.
+ * We use the sctp_getaddrs_old structure so that use-space library
+ * can avoid any unnecessary allocations. The only defferent part
+ * is that we store the actual length of the address buffer into the
+ * addrs_num structure member. That way we can re-use the existing
+ * code.
+ */
+SCTP_STATIC int sctp_getsockopt_connectx3(struct sock* sk, int len,
+ char __user *optval,
+ int __user *optlen)
+{
+ struct sctp_getaddrs_old param;
+ sctp_assoc_t assoc_id = 0;
+ int err = 0;
+
+ if (len < sizeof(param))
+ return -EINVAL;
+
+ if (copy_from_user(¶m, optval, sizeof(param)))
+ return -EFAULT;
+
+ err = __sctp_setsockopt_connectx(sk,
+ (struct sockaddr __user *)param.addrs,
+ param.addr_num, &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;
}
sctp_lock_sock(sk);
sk->sk_shutdown = SHUTDOWN_MASK;
+ sk->sk_state = SCTP_SS_CLOSING;
ep = sctp_sk(sk)->ep;
long timeo;
__u16 sinfo_flags = 0;
struct sctp_datamsg *datamsg;
- struct list_head *pos;
int msg_flags = msg->msg_flags;
SCTP_DEBUG_PRINTK("sctp_sendmsg(sk: %p, msg: %p, msg_len: %zu)\n",
goto out_unlock;
}
asoc = new_asoc;
+ err = sctp_assoc_set_bind_addr_from_ep(asoc, scope, GFP_KERNEL);
+ if (err < 0) {
+ err = -ENOMEM;
+ goto out_free;
+ }
/* If the SCTP_INIT ancillary data is specified, set all
* the association init values accordingly.
err = -ENOMEM;
goto out_free;
}
- err = sctp_assoc_set_bind_addr_from_ep(asoc, GFP_KERNEL);
- if (err < 0) {
- err = -ENOMEM;
- goto out_free;
- }
}
/* ASSERT: we have a valid association at this point. */
}
/* Now send the (possibly) fragmented message. */
- list_for_each(pos, &datamsg->chunks) {
- chunk = list_entry(pos, struct sctp_chunk, frag_list);
- sctp_datamsg_track(chunk);
+ list_for_each_entry(chunk, &datamsg->chunks, frag_list) {
+ sctp_chunk_hold(chunk);
/* Do accounting for the write space. */
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_free(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);
* rwnd by that amount. If all the data in the skb is read,
* rwnd is updated when the event is freed.
*/
- sctp_assoc_rwnd_increase(event->asoc, copied);
+ if (!sctp_ulpevent_is_notification(event))
+ sctp_assoc_rwnd_increase(event->asoc, copied);
goto out;
} else if ((event->msg_flags & MSG_NOTIFICATION) ||
(event->msg_flags & MSG_EOR))
* 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))
+ if (optlen > sizeof(struct sctp_event_subscribe))
return -EINVAL;
if (copy_from_user(&sctp_sk(sk)->subscribe, optval, optlen))
return -EFAULT;
* 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);
return -EINVAL;
if (copy_from_user(&sp->autoclose, optval, optlen))
return -EFAULT;
+ /* make sure it won't exceed MAX_SCHEDULE_TIMEOUT */
+ sp->autoclose = min_t(long, sp->autoclose, MAX_SCHEDULE_TIMEOUT / HZ);
return 0;
}
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;
}
}
}
- /* Note that unless the spp_flag is set to SPP_PMTUD_ENABLE the value
- * of this field is ignored. Note also that a value of zero
- * indicates the current setting should be left unchanged.
+ /* Note that a value of zero indicates the current setting should be
+ left unchanged.
*/
- if ((params->spp_flags & SPP_PMTUD_ENABLE) && params->spp_pathmaxrxt) {
+ if (params->spp_pathmaxrxt) {
if (trans) {
trans->pathmaxrxt = params->spp_pathmaxrxt;
} else if (asoc) {
}
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;
pmtud_change == SPP_PMTUD ||
sackdelay_change == SPP_SACKDELAY ||
params.spp_sackdelay > 500 ||
- (params.spp_pathmtu
- && params.spp_pathmtu < SCTP_DEFAULT_MINSEGMENT))
+ (params.spp_pathmtu &&
+ params.spp_pathmtu < SCTP_DEFAULT_MINSEGMENT))
return -EINVAL;
/* If an address other than INADDR_ANY is specified, and
* no transport is found, then the request is invalid.
*/
- if (!sctp_is_any(( union sctp_addr *)¶ms.spp_address)) {
+ 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)
* transport.
*/
if (!trans && asoc) {
- struct list_head *pos;
-
- list_for_each(pos, &asoc->peer.transport_addr_list) {
- trans = list_entry(pos, struct sctp_transport,
- transports);
+ list_for_each_entry(trans, &asoc->peer.transport_addr_list,
+ transports) {
sctp_apply_peer_addr_params(¶ms, trans, asoc, sp,
hb_change, pmtud_change,
sackdelay_change);
return 0;
}
-/* 7.1.23. Delayed Ack Timer (SCTP_DELAYED_ACK_TIME)
- *
- * This options will get or set the delayed ack timer. The time is set
- * in milliseconds. If the assoc_id is 0, then this sets or gets the
- * endpoints default delayed ack timer value. If the assoc_id field is
- * non-zero, then the set or get effects the specified association.
- *
- * struct sctp_assoc_value {
- * sctp_assoc_t assoc_id;
- * uint32_t assoc_value;
- * };
+/*
+ * 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;
+ * };
*
- * assoc_id - This parameter, indicates which association the
- * user is preforming an action upon. Note that if
- * this field's value is zero then the endpoints
- * default value is changed (effecting future
- * associations only).
+ * 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).
*
- * assoc_value - 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_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.
*
- * Note: a value of zero will leave the value alone,
- * but disable SACK delay. A non-zero value will also
- * enable SACK delay.
+ * 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_setsockopt_delayed_ack_time(struct sock *sk,
- char __user *optval, int optlen)
+static int sctp_setsockopt_delayed_ack(struct sock *sk,
+ char __user *optval, unsigned int optlen)
{
- struct sctp_assoc_value params;
+ struct sctp_sack_info params;
struct sctp_transport *trans = NULL;
struct sctp_association *asoc = NULL;
struct sctp_sock *sp = sctp_sk(sk);
- if (optlen != sizeof(struct sctp_assoc_value))
- return - EINVAL;
+ if (optlen == sizeof(struct sctp_sack_info)) {
+ if (copy_from_user(¶ms, optval, optlen))
+ return -EFAULT;
- if (copy_from_user(¶ms, optval, optlen))
- return -EFAULT;
+ 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_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, optlen))
+ return -EFAULT;
+
+ if (params.sack_delay == 0)
+ params.sack_freq = 1;
+ else
+ params.sack_freq = 0;
+ } else
+ return - EINVAL;
/* Validate value parameter. */
- if (params.assoc_value > 500)
+ if (params.sack_delay > 500)
return -EINVAL;
- /* Get association, if assoc_id != 0 and the socket is a one
+ /* 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.assoc_id);
- if (!asoc && params.assoc_id && sctp_style(sk, UDP))
+ asoc = sctp_id2assoc(sk, params.sack_assoc_id);
+ if (!asoc && params.sack_assoc_id && sctp_style(sk, UDP))
return -EINVAL;
- if (params.assoc_value) {
+ if (params.sack_delay) {
if (asoc) {
asoc->sackdelay =
- msecs_to_jiffies(params.assoc_value);
+ msecs_to_jiffies(params.sack_delay);
asoc->param_flags =
(asoc->param_flags & ~SPP_SACKDELAY) |
SPP_SACKDELAY_ENABLE;
} else {
- sp->sackdelay = params.assoc_value;
+ sp->sackdelay = params.sack_delay;
sp->param_flags =
(sp->param_flags & ~SPP_SACKDELAY) |
SPP_SACKDELAY_ENABLE;
}
- } else {
+ }
+
+ if (params.sack_freq == 1) {
if (asoc) {
asoc->param_flags =
(asoc->param_flags & ~SPP_SACKDELAY) |
(sp->param_flags & ~SPP_SACKDELAY) |
SPP_SACKDELAY_DISABLE;
}
+ } else if (params.sack_freq > 1) {
+ if (asoc) {
+ asoc->sackfreq = params.sack_freq;
+ asoc->param_flags =
+ (asoc->param_flags & ~SPP_SACKDELAY) |
+ SPP_SACKDELAY_ENABLE;
+ } else {
+ sp->sackfreq = params.sack_freq;
+ sp->param_flags =
+ (sp->param_flags & ~SPP_SACKDELAY) |
+ SPP_SACKDELAY_ENABLE;
+ }
}
/* If change is for association, also apply to each transport. */
if (asoc) {
- struct list_head *pos;
-
- list_for_each(pos, &asoc->peer.transport_addr_list) {
- trans = list_entry(pos, struct sctp_transport,
- transports);
- if (params.assoc_value) {
+ list_for_each_entry(trans, &asoc->peer.transport_addr_list,
+ transports) {
+ if (params.sack_delay) {
trans->sackdelay =
- msecs_to_jiffies(params.assoc_value);
+ msecs_to_jiffies(params.sack_delay);
trans->param_flags =
(trans->param_flags & ~SPP_SACKDELAY) |
SPP_SACKDELAY_ENABLE;
- } else {
+ }
+ if (params.sack_freq == 1) {
trans->param_flags =
(trans->param_flags & ~SPP_SACKDELAY) |
SPP_SACKDELAY_DISABLE;
+ } else if (params.sack_freq > 1) {
+ trans->sackfreq = params.sack_freq;
+ trans->param_flags =
+ (trans->param_flags & ~SPP_SACKDELAY) |
+ SPP_SACKDELAY_ENABLE;
}
}
}
* 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;
if (assocparams.sasoc_asocmaxrxt != 0) {
__u32 path_sum = 0;
int paths = 0;
- struct list_head *pos;
struct sctp_transport *peer_addr;
- list_for_each(pos, &asoc->peer.transport_addr_list) {
- peer_addr = list_entry(pos,
- struct sctp_transport,
- transports);
+ list_for_each_entry(peer_addr, &asoc->peer.transport_addr_list,
+ transports) {
path_sum += peer_addr->pathmaxrxt;
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 list_head *pos;
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(pos, &(sp->ep->asocs)) {
- asoc = list_entry(pos, struct sctp_association, 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;
+ struct sctp_association *asoc;
int val;
-
- if (optlen != sizeof(int))
+ int assoc_id = 0;
+
+ if (optlen == 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");
+ if (copy_from_user(&val, optval, optlen))
+ return -EFAULT;
+ } else if (optlen == sizeof(struct sctp_assoc_value)) {
+ if (copy_from_user(¶ms, optval, optlen))
+ return -EFAULT;
+ val = params.assoc_value;
+ assoc_id = params.assoc_id;
+ } else
return -EINVAL;
- if (get_user(val, (int __user *)optval))
- return -EFAULT;
- if (val < 0)
- return -EINVAL;
+ sp = sctp_sk(sk);
- sctp_sk(sk)->max_burst = val;
+ if (assoc_id != 0) {
+ asoc = sctp_id2assoc(sk, assoc_id);
+ if (!asoc)
+ return -EINVAL;
+ asoc->max_burst = val;
+ } else
+ sp->max_burst = val;
return 0;
}
* 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;
+ if (!sctp_auth_enable)
+ return -EACCES;
+
if (optlen != sizeof(struct sctp_authchunk))
return -EINVAL;
if (copy_from_user(&val, optval, optlen))
* 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;
int err;
+ if (!sctp_auth_enable)
+ return -EACCES;
+
if (optlen < sizeof(struct sctp_hmacalgo))
return -EINVAL;
goto out;
}
- if (hmacs->shmac_num_idents == 0 ||
- hmacs->shmac_num_idents > SCTP_AUTH_NUM_HMACS) {
+ idents = hmacs->shmac_num_idents;
+ if (idents == 0 || idents > SCTP_AUTH_NUM_HMACS ||
+ (idents * sizeof(u16)) > (optlen - sizeof(struct sctp_hmacalgo))) {
err = -EINVAL;
goto out;
}
*/
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;
int ret;
+ if (!sctp_auth_enable)
+ return -EACCES;
+
if (optlen <= sizeof(struct sctp_authkey))
return -EINVAL;
goto out;
}
+ if (authkey->sca_keylength > optlen - sizeof(struct sctp_authkey)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
asoc = sctp_id2assoc(sk, authkey->sca_assoc_id);
if (!asoc && authkey->sca_assoc_id && sctp_style(sk, UDP)) {
ret = -EINVAL;
* 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;
+ if (!sctp_auth_enable)
+ return -EACCES;
+
if (optlen != sizeof(struct sctp_authkeyid))
return -EINVAL;
if (copy_from_user(&val, optval, optlen))
* 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;
+ if (!sctp_auth_enable)
+ return -EACCES;
+
if (optlen != sizeof(struct sctp_authkeyid))
return -EINVAL;
if (copy_from_user(&val, optval, optlen))
* 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;
optlen, SCTP_BINDX_REM_ADDR);
break;
+ case SCTP_SOCKOPT_CONNECTX_OLD:
+ /* 'optlen' is the size of the addresses buffer. */
+ retval = sctp_setsockopt_connectx_old(sk,
+ (struct sockaddr __user *)optval,
+ optlen);
+ break;
+
case SCTP_SOCKOPT_CONNECTX:
/* 'optlen' is the size of the addresses buffer. */
- retval = sctp_setsockopt_connectx(sk, (struct sockaddr __user *)optval,
- optlen);
+ retval = sctp_setsockopt_connectx(sk,
+ (struct sockaddr __user *)optval,
+ optlen);
break;
case SCTP_DISABLE_FRAGMENTS:
retval = sctp_setsockopt_peer_addr_params(sk, optval, optlen);
break;
- case SCTP_DELAYED_ACK_TIME:
- retval = sctp_setsockopt_delayed_ack_time(sk, optval, optlen);
+ case SCTP_DELAYED_ACK:
+ retval = sctp_setsockopt_delayed_ack(sk, optval, optlen);
break;
case SCTP_PARTIAL_DELIVERY_POINT:
retval = sctp_setsockopt_partial_delivery_point(sk, optval, optlen);
sctp_lock_sock(sk);
SCTP_DEBUG_PRINTK("%s - sk: %p, sockaddr: %p, addr_len: %d\n",
- __FUNCTION__, sk, addr, addr_len);
+ __func__, sk, addr, addr_len);
/* Validate addr_len before calling common connect/connectx routine. */
af = sctp_get_af_specific(addr->sa_family);
/* Pass correct addr len to common routine (so it knows there
* is only one address being passed.
*/
- err = __sctp_connect(sk, addr, af->sockaddr_len);
+ err = __sctp_connect(sk, addr, af->sockaddr_len, NULL);
}
sctp_release_sock(sk);
sp->pathmaxrxt = sctp_max_retrans_path;
sp->pathmtu = 0; // allow default discovery
sp->sackdelay = sctp_sack_timeout;
+ sp->sackfreq = 2;
sp->param_flags = SPP_HB_ENABLE |
SPP_PMTUD_ENABLE |
SPP_SACKDELAY_ENABLE;
sp->hmac = NULL;
SCTP_DBG_OBJCNT_INC(sock);
- atomic_inc(&sctp_sockets_allocated);
+
+ local_bh_disable();
+ percpu_counter_inc(&sctp_sockets_allocated);
+ sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
+ local_bh_enable();
+
return 0;
}
/* Cleanup any SCTP per socket resources. */
-SCTP_STATIC int sctp_destroy_sock(struct sock *sk)
+SCTP_STATIC void sctp_destroy_sock(struct sock *sk)
{
struct sctp_endpoint *ep;
/* Release our hold on the endpoint. */
ep = sctp_sk(sk)->ep;
sctp_endpoint_free(ep);
- atomic_dec(&sctp_sockets_allocated);
- return 0;
+ local_bh_disable();
+ percpu_counter_dec(&sctp_sockets_allocated);
+ 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;
goto out;
}
- SCTP_DEBUG_PRINTK("%s: sk: %p asoc: %p\n", __FUNCTION__, sk, asoc);
+ 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);
+ 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",
- __FUNCTION__, sk, asoc, newsock->sk, retval);
+ __func__, sk, asoc, newsock->sk, retval);
/* Return the fd mapped to the new socket. */
peeloff.sd = retval;
/* If an address other than INADDR_ANY is specified, and
* no transport is found, then the request is invalid.
*/
- if (!sctp_is_any(( union sctp_addr *)¶ms.spp_address)) {
+ 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) {
return 0;
}
-/* 7.1.23. Delayed Ack Timer (SCTP_DELAYED_ACK_TIME)
- *
- * This options will get or set the delayed ack timer. The time is set
- * in milliseconds. If the assoc_id is 0, then this sets or gets the
- * endpoints default delayed ack timer value. If the assoc_id field is
- * non-zero, then the set or get effects the specified association.
- *
- * struct sctp_assoc_value {
- * sctp_assoc_t assoc_id;
- * uint32_t assoc_value;
- * };
+/*
+ * 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;
+ * };
*
- * assoc_id - This parameter, indicates which association the
- * user is preforming an action upon. Note that if
- * this field's value is zero then the endpoints
- * default value is changed (effecting future
- * associations only).
+ * 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).
*
- * assoc_value - 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_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.
*
- * Note: a value of zero will leave the value alone,
- * but disable SACK delay. A non-zero value will also
- * enable SACK delay.
+ * 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_time(struct sock *sk, int len,
+static int sctp_getsockopt_delayed_ack(struct sock *sk, int len,
char __user *optval,
int __user *optlen)
{
- struct sctp_assoc_value params;
+ struct sctp_sack_info params;
struct sctp_association *asoc = NULL;
struct sctp_sock *sp = sctp_sk(sk);
- if (len < sizeof(struct sctp_assoc_value))
- return - EINVAL;
-
- len = sizeof(struct sctp_assoc_value);
+ if (len >= sizeof(struct sctp_sack_info)) {
+ len = sizeof(struct sctp_sack_info);
- if (copy_from_user(¶ms, optval, len))
- return -EFAULT;
+ 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 assoc_id != 0 and the socket is a one
+ /* 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.assoc_id);
- if (!asoc && params.assoc_id && sctp_style(sk, UDP))
+ 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.assoc_value = jiffies_to_msecs(
+ if (asoc->param_flags & SPP_SACKDELAY_ENABLE) {
+ params.sack_delay = jiffies_to_msecs(
asoc->sackdelay);
- else
- params.assoc_value = 0;
+ 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.assoc_value = sp->sackdelay;
- else
- params.assoc_value = 0;
+ 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 0;
}
-static int sctp_getsockopt_peer_addrs_num_old(struct sock *sk, int len,
- char __user *optval,
- int __user *optlen)
+
+static int sctp_getsockopt_peer_addrs(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;
+ 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(sctp_assoc_t))
+ if (len < sizeof(struct sctp_getaddrs))
return -EINVAL;
- if (copy_from_user(&id, optval, sizeof(sctp_assoc_t)))
+ 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, id);
+ asoc = sctp_id2assoc(sk, getaddrs.assoc_id);
if (!asoc)
return -EINVAL;
- list_for_each(pos, &asoc->peer.transport_addr_list) {
- cnt ++;
- }
-
- return cnt;
-}
+ to = optval + offsetof(struct sctp_getaddrs,addrs);
+ space_left = len - offsetof(struct sctp_getaddrs,addrs);
-/*
- * 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;
- struct list_head *pos;
- int cnt = 0;
- 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))
- return -EINVAL;
-
- len = sizeof(struct sctp_getaddrs_old);
-
- if (copy_from_user(&getaddrs, optval, len))
- return -EFAULT;
-
- if (getaddrs.addr_num <= 0) return -EINVAL;
-
- /* 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(pos, &asoc->peer.transport_addr_list) {
- from = list_entry(pos, struct sctp_transport, 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))
- 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;
- struct list_head *pos;
- 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);
-
- list_for_each(pos, &asoc->peer.transport_addr_list) {
- from = list_entry(pos, struct sctp_transport, transports);
+ 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;
+ addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len;
if (space_left < addrlen)
return -ENOMEM;
if (copy_to_user(to, &temp, addrlen))
return 0;
}
-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;
-
- /*
- * 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(&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))
- continue;
-
- cnt++;
- }
- 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))
- continue;
- memcpy(&temp, &addr->a, sizeof(temp));
- 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);
-
- to += addrlen;
- *bytes_copied += addrlen;
- cnt ++;
- if (cnt >= max_addrs) break;
- }
- rcu_read_unlock();
-
- return cnt;
-}
-
static int sctp_copy_laddrs(struct sock *sk, __u16 port, void *to,
size_t space_left, int *bytes_copied)
{
if ((PF_INET == sk->sk_family) &&
(AF_INET6 == addr->a.sa.sa_family))
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);
+
sctp_get_pf_specific(sk->sk_family)->addr_v4map(sctp_sk(sk),
&temp);
addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len;
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;
- 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;
- void *buf;
- int bytes_copied = 0;
-
- if (len < sizeof(struct sctp_getaddrs_old))
- return -EINVAL;
-
- len = sizeof(struct sctp_getaddrs_old);
- if (copy_from_user(&getaddrs, optval, len))
- return -EFAULT;
-
- if (getaddrs.addr_num <= 0) return -EINVAL;
- /*
- * 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(&addr->a)) {
- cnt = sctp_copy_laddrs_old(sk, bp->port,
- getaddrs.addr_num,
- addrs, &bytes_copied);
- goto copy_getaddrs;
- }
- }
-
- buf = addrs;
- /* 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 */
- getaddrs.addr_num = cnt;
- if (copy_to_user(optval, &getaddrs, len))
- err = -EFAULT;
-
-error:
- kfree(addrs);
- return err;
-}
static int sctp_getsockopt_local_addrs(struct sock *sk, int len,
char __user *optval, int __user *optlen)
if (sctp_list_single_entry(&bp->address_list)) {
addr = list_entry(bp->address_list.next,
struct sctp_sockaddr_entry, list);
- if (sctp_is_any(&addr->a)) {
+ if (sctp_is_any(sk, &addr->a)) {
cnt = sctp_copy_laddrs(sk, bp->port, addrs,
space_left, &bytes_copied);
if (cnt < 0) {
}
/*
- * 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;
}
char __user *optval,
int __user *optlen)
{
- int val;
+ struct sctp_assoc_value params;
+ struct sctp_sock *sp;
+ struct sctp_association *asoc;
- if (len < sizeof(int))
+ 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)) {
+ len = sizeof(struct sctp_assoc_value);
+ if (copy_from_user(¶ms, optval, len))
+ return -EFAULT;
+ } else
return -EINVAL;
- len = sizeof(int);
+ sp = sctp_sk(sk);
- val = sctp_sk(sk)->max_burst;
- if (put_user(len, optlen))
- return -EFAULT;
- if (copy_to_user(optval, &val, len))
- return -EFAULT;
+ if (params.assoc_id != 0) {
+ asoc = sctp_id2assoc(sk, params.assoc_id);
+ if (!asoc)
+ return -EINVAL;
+ params.assoc_value = asoc->max_burst;
+ } else
+ params.assoc_value = sp->max_burst;
+
+ 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;
- return -ENOTSUPP;
}
static int sctp_getsockopt_hmac_ident(struct sock *sk, int len,
char __user *optval, int __user *optlen)
{
+ struct sctp_hmacalgo __user *p = (void __user *)optval;
struct sctp_hmac_algo_param *hmacs;
- __u16 param_len;
+ __u16 data_len = 0;
+ u32 num_idents;
+
+ if (!sctp_auth_enable)
+ return -EACCES;
hmacs = sctp_sk(sk)->ep->auth_hmacs_list;
- param_len = ntohs(hmacs->param_hdr.length);
+ data_len = ntohs(hmacs->param_hdr.length) - sizeof(sctp_paramhdr_t);
- if (len < param_len)
+ if (len < sizeof(struct sctp_hmacalgo) + data_len)
return -EINVAL;
+
+ len = sizeof(struct sctp_hmacalgo) + data_len;
+ num_idents = data_len / sizeof(u16);
+
if (put_user(len, optlen))
return -EFAULT;
- if (copy_to_user(optval, hmacs->hmac_ids, len))
+ if (put_user(num_idents, &p->shmac_num_idents))
+ return -EFAULT;
+ if (copy_to_user(p->shmac_idents, hmacs->hmac_ids, data_len))
return -EFAULT;
-
return 0;
}
struct sctp_authkeyid val;
struct sctp_association *asoc;
+ if (!sctp_auth_enable)
+ return -EACCES;
+
if (len < sizeof(struct sctp_authkeyid))
return -EINVAL;
if (copy_from_user(&val, optval, sizeof(struct sctp_authkeyid)))
else
val.scact_keynumber = sctp_sk(sk)->ep->active_key_id;
+ len = sizeof(struct sctp_authkeyid);
+ if (put_user(len, optlen))
+ return -EFAULT;
+ if (copy_to_user(optval, &val, len))
+ return -EFAULT;
+
return 0;
}
static int sctp_getsockopt_peer_auth_chunks(struct sock *sk, int len,
char __user *optval, int __user *optlen)
{
+ struct sctp_authchunks __user *p = (void __user *)optval;
struct sctp_authchunks val;
struct sctp_association *asoc;
struct sctp_chunks_param *ch;
+ u32 num_chunks = 0;
char __user *to;
- if (len <= sizeof(struct sctp_authchunks))
+ if (!sctp_auth_enable)
+ return -EACCES;
+
+ if (len < sizeof(struct sctp_authchunks))
return -EINVAL;
if (copy_from_user(&val, optval, sizeof(struct sctp_authchunks)))
return -EFAULT;
- to = val.gauth_chunks;
+ to = p->gauth_chunks;
asoc = sctp_id2assoc(sk, val.gauth_assoc_id);
if (!asoc)
return -EINVAL;
ch = asoc->peer.peer_chunks;
+ if (!ch)
+ goto num;
/* See if the user provided enough room for all the data */
- if (len < ntohs(ch->param_hdr.length))
+ num_chunks = ntohs(ch->param_hdr.length) - sizeof(sctp_paramhdr_t);
+ if (len < num_chunks)
return -EINVAL;
- len = ntohs(ch->param_hdr.length);
- if (put_user(len, optlen))
+ if (copy_to_user(to, ch->chunks, num_chunks))
return -EFAULT;
- if (copy_to_user(to, ch->chunks, len))
+num:
+ len = sizeof(struct sctp_authchunks) + num_chunks;
+ if (put_user(len, optlen)) return -EFAULT;
+ if (put_user(num_chunks, &p->gauth_number_of_chunks))
return -EFAULT;
-
return 0;
}
static int sctp_getsockopt_local_auth_chunks(struct sock *sk, int len,
char __user *optval, int __user *optlen)
{
+ struct sctp_authchunks __user *p = (void __user *)optval;
struct sctp_authchunks val;
struct sctp_association *asoc;
struct sctp_chunks_param *ch;
+ u32 num_chunks = 0;
char __user *to;
- if (len <= sizeof(struct sctp_authchunks))
+ if (!sctp_auth_enable)
+ return -EACCES;
+
+ if (len < sizeof(struct sctp_authchunks))
return -EINVAL;
if (copy_from_user(&val, optval, sizeof(struct sctp_authchunks)))
return -EFAULT;
- to = val.gauth_chunks;
+ to = p->gauth_chunks;
asoc = sctp_id2assoc(sk, val.gauth_assoc_id);
if (!asoc && val.gauth_assoc_id && sctp_style(sk, UDP))
return -EINVAL;
else
ch = sctp_sk(sk)->ep->auth_chunk_list;
- if (len < ntohs(ch->param_hdr.length))
+ if (!ch)
+ goto num;
+
+ num_chunks = ntohs(ch->param_hdr.length) - sizeof(sctp_paramhdr_t);
+ if (len < sizeof(struct sctp_authchunks) + num_chunks)
+ return -EINVAL;
+
+ if (copy_to_user(to, ch->chunks, num_chunks))
+ return -EFAULT;
+num:
+ len = sizeof(struct sctp_authchunks) + num_chunks;
+ if (put_user(len, optlen))
+ return -EFAULT;
+ if (put_user(num_chunks, &p->gauth_number_of_chunks))
+ return -EFAULT;
+
+ 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 = ntohs(ch->param_hdr.length);
+ len = sizeof(u32);
+
+ list_for_each_entry(asoc, &(sp->ep->asocs), asocs) {
+ val++;
+ }
+
if (put_user(len, optlen))
return -EFAULT;
- if (copy_to_user(to, ch->chunks, len))
+ if (copy_to_user(optval, &val, len))
return -EFAULT;
return 0;
retval = sctp_getsockopt_peer_addr_params(sk, len, optval,
optlen);
break;
- case SCTP_DELAYED_ACK_TIME:
- retval = sctp_getsockopt_delayed_ack_time(sk, len, optval,
+ case SCTP_DELAYED_ACK:
+ retval = sctp_getsockopt_delayed_ack(sk, len, optval,
optlen);
break;
case SCTP_INITMSG:
retval = sctp_getsockopt_initmsg(sk, len, optval, optlen);
break;
- case SCTP_GET_PEER_ADDRS_NUM_OLD:
- retval = sctp_getsockopt_peer_addrs_num_old(sk, len, optval,
- optlen);
- break;
- case SCTP_GET_LOCAL_ADDRS_NUM_OLD:
- retval = sctp_getsockopt_local_addrs_num_old(sk, len, optval,
- optlen);
- break;
- case SCTP_GET_PEER_ADDRS_OLD:
- retval = sctp_getsockopt_peer_addrs_old(sk, len, optval,
- optlen);
- break;
- case SCTP_GET_LOCAL_ADDRS_OLD:
- retval = sctp_getsockopt_local_addrs_old(sk, len, optval,
- optlen);
- break;
case SCTP_GET_PEER_ADDRS:
retval = sctp_getsockopt_peer_addrs(sk, len, optval,
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;
{
struct sctp_bind_hashbucket *head; /* hash list */
struct sctp_bind_bucket *pp; /* hash list port iterator */
+ struct hlist_node *node;
unsigned short snum;
int ret;
if (snum == 0) {
/* Search for an available port. */
- unsigned int low = sysctl_local_port_range[0];
- unsigned int high = sysctl_local_port_range[1];
- unsigned int remaining = (high - low) + 1;
- unsigned int rover = net_random() % remaining + low;
- int index;
+ int low, high, remaining, index;
+ unsigned int rover;
+
+ inet_get_local_port_range(&low, &high);
+ remaining = (high - low) + 1;
+ rover = net_random() % remaining + low;
do {
rover++;
if ((rover < low) || (rover > high))
rover = low;
+ if (inet_is_reserved_local_port(rover))
+ continue;
index = sctp_phashfn(rover);
head = &sctp_port_hashtable[index];
sctp_spin_lock(&head->lock);
- for (pp = head->chain; pp; pp = pp->next)
+ sctp_for_each_hentry(pp, node, &head->chain)
if (pp->port == rover)
goto next;
break;
*/
head = &sctp_port_hashtable[sctp_phashfn(snum)];
sctp_spin_lock(&head->lock);
- for (pp = head->chain; pp; pp = pp->next) {
+ sctp_for_each_hentry(pp, node, &head->chain) {
if (pp->port == snum)
goto pp_found;
}
*/
int reuse = sk->sk_reuse;
struct sock *sk2;
- struct hlist_node *node;
SCTP_DEBUG_PRINTK("sctp_get_port() found a possible match\n");
if (pp->fastreuse && sk->sk_reuse &&
struct sctp_endpoint *ep2;
ep2 = sctp_sk(sk2)->ep;
- if (reuse && sk2->sk_reuse &&
- sk2->sk_state != SCTP_SS_LISTENING)
+ if (sk == sk2 ||
+ (reuse && sk2->sk_reuse &&
+ sk2->sk_state != SCTP_SS_LISTENING))
continue;
- if (sctp_bind_addr_match(&ep2->base.bind_addr, addr,
- sctp_sk(sk))) {
+ if (sctp_bind_addr_conflict(&ep2->base.bind_addr, addr,
+ sctp_sk(sk2), sctp_sk(sk))) {
ret = (long)sk2;
goto fail_unlock;
}
*/
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
- 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;
+ } else {
+ if (sctp_get_port(sk, inet_sk(sk)->inet_num)) {
+ sk->sk_state = SCTP_SS_CLOSED;
+ return -EADDRINUSE;
+ }
}
- 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_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. */
- sctp_sk(sk)->hmac = tfm;
+ err = 0;
out:
sctp_release_sock(sk);
return err;
-cleanup:
- crypto_free_hash(tfm);
- goto out;
}
/*
struct sctp_sock *sp = sctp_sk(sk);
unsigned int mask;
- poll_wait(file, sk->sk_sleep, wait);
+ poll_wait(file, sk_sleep(sk), wait);
/* A TCP-style listening socket becomes readable when the accept queue
* is not empty.
struct sctp_bind_bucket *pp;
pp = kmem_cache_alloc(sctp_bucket_cachep, GFP_ATOMIC);
- SCTP_DBG_OBJCNT_INC(bind_bucket);
if (pp) {
+ SCTP_DBG_OBJCNT_INC(bind_bucket);
pp->port = snum;
pp->fastreuse = 0;
INIT_HLIST_HEAD(&pp->owner);
- if ((pp->next = head->chain) != NULL)
- pp->next->pprev = &pp->next;
- head->chain = pp;
- pp->pprev = &head->chain;
+ hlist_add_head(&pp->node, &head->chain);
}
return pp;
}
static void sctp_bucket_destroy(struct sctp_bind_bucket *pp)
{
if (pp && hlist_empty(&pp->owner)) {
- if (pp->next)
- pp->next->pprev = pp->pprev;
- *(pp->pprev) = pp->next;
+ __hlist_del(&pp->node);
kmem_cache_free(sctp_bucket_cachep, pp);
SCTP_DBG_OBJCNT_DEC(bind_bucket);
}
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);
sctp_cmsgs_t *cmsgs)
{
struct cmsghdr *cmsg;
+ struct msghdr *my_msg = (struct msghdr *)msg;
for (cmsg = CMSG_FIRSTHDR(msg);
cmsg != NULL;
- cmsg = CMSG_NXTHDR((struct msghdr*)msg, cmsg)) {
- if (!CMSG_OK(msg, cmsg))
+ cmsg = CMSG_NXTHDR(my_msg, cmsg)) {
+ if (!CMSG_OK(my_msg, cmsg))
return -EINVAL;
/* Should we parse this header or ignore? */
int error;
DEFINE_WAIT(wait);
- prepare_to_wait_exclusive(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE);
+ prepare_to_wait_exclusive(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
/* Socket errors? */
error = sock_error(sk);
sctp_lock_sock(sk);
ready:
- finish_wait(sk->sk_sleep, &wait);
+ finish_wait(sk_sleep(sk), &wait);
return 0;
interrupted:
error = sock_intr_errno(*timeo_p);
out:
- finish_wait(sk->sk_sleep, &wait);
+ finish_wait(sk_sleep(sk), &wait);
*err = error;
return error;
}
wake_up_interruptible(&asoc->wait);
if (sctp_writeable(sk)) {
- if (sk->sk_sleep && waitqueue_active(sk->sk_sleep))
- wake_up_interruptible(sk->sk_sleep);
+ if (sk_sleep(sk) && waitqueue_active(sk_sleep(sk)))
+ wake_up_interruptible(sk_sleep(sk));
/* Note that we try to include the Async I/O support
* here by modeling from the current TCP/UDP code.
* We have not tested with it yet.
*/
- if (sock->fasync_list &&
+ if (sock->wq->fasync_list &&
!(sk->sk_shutdown & SEND_SHUTDOWN))
- sock_wake_async(sock, 2, POLL_OUT);
+ sock_wake_async(sock,
+ SOCK_WAKE_SPACE, POLL_OUT);
}
}
}
atomic_sub(sizeof(struct sctp_chunk), &sk->sk_wmem_alloc);
/*
- * This undoes what is done via sk_charge_skb
+ * This undoes what is done via sctp_set_owner_w and sk_mem_charge
*/
sk->sk_wmem_queued -= skb->truesize;
- sk->sk_forward_alloc += skb->truesize;
+ sk_mem_uncharge(sk, skb->truesize);
sock_wfree(skb);
__sctp_write_space(asoc);
atomic_sub(event->rmem_len, &sk->sk_rmem_alloc);
/*
- * Mimic the behavior of sk_stream_rfree
+ * Mimic the behavior of sock_rfree
*/
- sk->sk_forward_alloc += event->rmem_len;
+ sk_mem_uncharge(sk, event->rmem_len);
}
goto out;
}
+void sctp_data_ready(struct sock *sk, int len)
+{
+ struct socket_wq *wq;
+
+ rcu_read_lock();
+ wq = rcu_dereference(sk->sk_wq);
+ if (wq_has_sleeper(wq))
+ wake_up_interruptible_sync_poll(&wq->wait, POLLIN |
+ POLLRDNORM | POLLRDBAND);
+ sk_wake_async(sk, SOCK_WAKE_WAITD, POLL_IN);
+ rcu_read_unlock();
+}
+
/* If socket sndbuf has changed, wake up all per association waiters. */
void sctp_write_space(struct sock *sk)
{
struct sctp_association *asoc;
- struct list_head *pos;
/* Wake up the tasks in each wait queue. */
- list_for_each(pos, &((sctp_sk(sk))->ep->asocs)) {
- asoc = list_entry(pos, struct sctp_association, asocs);
+ list_for_each_entry(asoc, &((sctp_sk(sk))->ep->asocs), asocs) {
__sctp_write_space(asoc);
}
}
{
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;
long current_timeo = *timeo_p;
DEFINE_WAIT(wait);
- SCTP_DEBUG_PRINTK("%s: asoc=%p, timeo=%ld\n", __FUNCTION__, asoc,
+ SCTP_DEBUG_PRINTK("%s: asoc=%p, timeo=%ld\n", __func__, asoc,
(long)(*timeo_p));
/* Increment the association's refcnt. */
for (;;) {
- prepare_to_wait_exclusive(sk->sk_sleep, &wait,
+ prepare_to_wait_exclusive(sk_sleep(sk), &wait,
TASK_INTERRUPTIBLE);
if (list_empty(&ep->asocs)) {
break;
}
- finish_wait(sk->sk_sleep, &wait);
+ finish_wait(sk_sleep(sk), &wait);
return err;
}
DEFINE_WAIT(wait);
do {
- prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE);
+ prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
if (list_empty(&sctp_sk(sk)->ep->asocs))
break;
sctp_release_sock(sk);
sctp_lock_sock(sk);
} while (!signal_pending(current) && timeout);
- 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);
+ finish_wait(sk_sleep(sk), &wait);
}
static void sctp_skb_set_owner_r_frag(struct sk_buff *skb, struct sock *sk)
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;
+
+ 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.
*/
struct sctp_endpoint *newep = newsp->ep;
struct sk_buff *skb, *tmp;
struct sctp_ulpevent *event;
- int flags = 0;
+ struct sctp_bind_hashbucket *head;
/* Migrate socket buffer sizes and all the socket level options to the
* new socket.
newsp->hmac = NULL;
/* Hook this new socket in to the bind_hash list. */
+ 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();
/* Copy the bind_addr list from the original endpoint to the new
* endpoint so that we can handle restarts properly
*/
- if (PF_INET6 == assoc->base.sk->sk_family)
- flags = SCTP_ADDR6_ALLOWED;
- if (assoc->peer.ipv4_address)
- flags |= SCTP_ADDR4_PEERSUPP;
- if (assoc->peer.ipv6_address)
- flags |= SCTP_ADDR6_PEERSUPP;
- sctp_bind_addr_copy(&newsp->ep->base.bind_addr,
- &oldsp->ep->base.bind_addr,
- SCTP_SCOPE_GLOBAL, GFP_KERNEL, flags);
+ sctp_bind_addr_dup(&newsp->ep->base.bind_addr,
+ &oldsp->ep->base.bind_addr, GFP_KERNEL);
/* Move any messages in the old socket's receive queue that are for the
* peeled off association to the new socket's receive queue.
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
.memory_pressure = &sctp_memory_pressure,
.enter_memory_pressure = sctp_enter_memory_pressure,
.memory_allocated = &sctp_memory_allocated,
+ .sockets_allocated = &sctp_sockets_allocated,
};
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+
struct proto sctpv6_prot = {
.name = "SCTPv6",
.owner = THIS_MODULE,
.memory_pressure = &sctp_memory_pressure,
.enter_memory_pressure = sctp_enter_memory_pressure,
.memory_allocated = &sctp_memory_allocated,
+ .sockets_allocated = &sctp_sockets_allocated,
};
#endif /* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */