/* Forward declarations for internal helper functions. */
static int sctp_writeable(struct sock *sk);
-static inline int sctp_wspace(struct sctp_association *asoc);
-static inline void sctp_set_owner_w(struct sctp_chunk *chunk);
static void sctp_wfree(struct sk_buff *skb);
static int sctp_wait_for_sndbuf(struct sctp_association *, long *timeo_p,
size_t msg_len);
static int sctp_wait_for_connect(struct sctp_association *, long *timeo_p);
static int sctp_wait_for_accept(struct sock *sk, long timeo);
static void sctp_wait_for_close(struct sock *sk, long timeo);
-static inline int sctp_verify_addr(struct sock *, union sctp_addr *, int);
+static struct sctp_af *sctp_sockaddr_af(struct sctp_sock *opt,
+ union sctp_addr *addr, int len);
static int sctp_bindx_add(struct sock *, struct sockaddr *, int);
static int sctp_bindx_rem(struct sock *, struct sockaddr *, int);
static int sctp_send_asconf_add_ip(struct sock *, struct sockaddr *, int);
static char *sctp_hmac_alg = SCTP_COOKIE_HMAC_ALG;
extern kmem_cache_t *sctp_bucket_cachep;
-extern int sctp_assoc_valid(struct sock *sk, struct sctp_association *asoc);
+
+/* Get the sndbuf space available at the time on the association. */
+static inline int sctp_wspace(struct sctp_association *asoc)
+{
+ struct sock *sk = asoc->base.sk;
+ int amt = 0;
+
+ if (asoc->ep->sndbuf_policy) {
+ /* make sure that no association uses more than sk_sndbuf */
+ amt = sk->sk_sndbuf - asoc->sndbuf_used;
+ } else {
+ /* do socket level accounting */
+ amt = sk->sk_sndbuf - atomic_read(&sk->sk_wmem_alloc);
+ }
+
+ if (amt < 0)
+ amt = 0;
+
+ return amt;
+}
+
+/* Increment the used sndbuf space count of the corresponding association by
+ * the size of the outgoing data chunk.
+ * Also, set the skb destructor for sndbuf accounting later.
+ *
+ * Since it is always 1-1 between chunk and skb, and also a new skb is always
+ * allocated for chunk bundling in sctp_packet_transmit(), we can use the
+ * destructor in the data chunk skb for the purpose of the sndbuf space
+ * tracking.
+ */
+static inline void sctp_set_owner_w(struct sctp_chunk *chunk)
+{
+ struct sctp_association *asoc = chunk->asoc;
+ struct sock *sk = asoc->base.sk;
+
+ /* The sndbuf space is tracked per association. */
+ sctp_association_hold(asoc);
+
+ skb_set_owner_w(chunk->skb, sk);
+
+ chunk->skb->destructor = sctp_wfree;
+ /* Save the chunk pointer in skb for sctp_wfree to use later. */
+ *((struct sctp_chunk **)(chunk->skb->cb)) = chunk;
+
+ asoc->sndbuf_used += SCTP_DATA_SNDSIZE(chunk) +
+ sizeof(struct sk_buff) +
+ sizeof(struct sctp_chunk);
+
+ sk->sk_wmem_queued += SCTP_DATA_SNDSIZE(chunk) +
+ sizeof(struct sk_buff) +
+ sizeof(struct sctp_chunk);
+
+ atomic_add(sizeof(struct sctp_chunk), &sk->sk_wmem_alloc);
+}
+
+/* Verify that this is a valid address. */
+static inline int sctp_verify_addr(struct sock *sk, union sctp_addr *addr,
+ int len)
+{
+ struct sctp_af *af;
+
+ /* Verify basic sockaddr. */
+ af = sctp_sockaddr_af(sctp_sk(sk), addr, len);
+ if (!af)
+ return -EINVAL;
+
+ /* Is this a valid SCTP address? */
+ if (!af->addr_valid(addr, sctp_sk(sk)))
+ return -EINVAL;
+
+ if (!sctp_sk(sk)->pf->send_verify(sctp_sk(sk), (addr)))
+ return -EINVAL;
+
+ return 0;
+}
/* Look up the association by its id. If this is not a UDP-style
* socket, the ID field is always ignored.
* id are specified, the associations matching the address and the id should be
* the same.
*/
-struct sctp_transport *sctp_addr_id2transport(struct sock *sk,
+static struct sctp_transport *sctp_addr_id2transport(struct sock *sk,
struct sockaddr_storage *addr,
sctp_assoc_t id)
{
* sockaddr_in6 [RFC 2553]),
* addr_len - the size of the address structure.
*/
-int sctp_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
+SCTP_STATIC int sctp_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{
int retval = 0;
static long sctp_get_port_local(struct sock *, union sctp_addr *);
/* Verify this is a valid sockaddr. */
-static struct sctp_af *sctp_sockaddr_af(struct sctp_opt *opt,
+static struct sctp_af *sctp_sockaddr_af(struct sctp_sock *opt,
union sctp_addr *addr, int len)
{
struct sctp_af *af;
/* Bind a local address either to an endpoint or to an association. */
SCTP_STATIC int sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len)
{
- struct sctp_opt *sp = sctp_sk(sk);
+ struct sctp_sock *sp = sctp_sk(sk);
struct sctp_endpoint *ep = sp->ep;
struct sctp_bind_addr *bp = &ep->base.bind_addr;
struct sctp_af *af;
}
/* Refresh ephemeral port. */
- if (!snum)
- snum = inet_sk(sk)->num;
+ if (!bp->port)
+ bp->port = inet_sk(sk)->num;
/* Add the address to the bind address list. */
sctp_local_bh_disable();
addr->v4.sin_port = ntohs(addr->v4.sin_port);
ret = sctp_add_bind_addr(bp, addr, GFP_ATOMIC);
addr->v4.sin_port = htons(addr->v4.sin_port);
- if (!ret && !bp->port)
- bp->port = snum;
sctp_write_unlock(&ep->base.addr_lock);
sctp_local_bh_enable();
struct sockaddr *addrs,
int addrcnt)
{
- struct sctp_opt *sp;
+ struct sctp_sock *sp;
struct sctp_endpoint *ep;
struct sctp_association *asoc;
struct sctp_bind_addr *bp;
*/
int sctp_bindx_rem(struct sock *sk, struct sockaddr *addrs, int addrcnt)
{
- struct sctp_opt *sp = sctp_sk(sk);
+ struct sctp_sock *sp = sctp_sk(sk);
struct sctp_endpoint *ep = sp->ep;
int cnt;
struct sctp_bind_addr *bp = &ep->base.bind_addr;
struct sockaddr *addrs,
int addrcnt)
{
- struct sctp_opt *sp;
+ struct sctp_sock *sp;
struct sctp_endpoint *ep;
struct sctp_association *asoc;
struct sctp_bind_addr *bp;
*
* Returns 0 if ok, <0 errno code on error.
*/
-SCTP_STATIC int sctp_setsockopt_bindx(struct sock* sk, struct sockaddr *addrs,
+SCTP_STATIC int sctp_setsockopt_bindx(struct sock* sk,
+ struct sockaddr __user *addrs,
int addrs_size, int op)
{
struct sockaddr *kaddrs;
sctp_local_bh_disable();
sctp_bh_lock_sock(sk);
- /* Hold the sock, since inet_sock_release() will put sock_put()
+ /* Hold the sock, since sk_common_release() will put sock_put()
* and we have just a little more cleanup.
*/
sock_hold(sk);
- inet_sock_release(sk);
+ sk_common_release(sk);
sctp_bh_unlock_sock(sk);
sctp_local_bh_enable();
* Note: This function could use a rewrite especially when explicit
* connect support comes in.
*/
-/* BUG: We do not implement the equivalent of wait_for_tcp_memory(). */
+/* BUG: We do not implement the equivalent of sk_stream_wait_memory(). */
SCTP_STATIC int sctp_msghdr_parse(const struct msghdr *, sctp_cmsgs_t *);
SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
struct msghdr *msg, size_t msg_len)
{
- struct sctp_opt *sp;
+ struct sctp_sock *sp;
struct sctp_endpoint *ep;
struct sctp_association *new_asoc=NULL, *asoc=NULL;
struct sctp_transport *transport, *chunk_tp;
struct sctp_sndrcvinfo default_sinfo = { 0 };
struct sctp_sndrcvinfo *sinfo;
struct sctp_initmsg *sinit;
- sctp_assoc_t associd = NULL;
- sctp_cmsgs_t cmsgs = { 0 };
+ sctp_assoc_t associd = 0;
+ sctp_cmsgs_t cmsgs = { NULL };
int err;
sctp_scope_t scope;
long timeo;
if (!asoc) {
SCTP_DEBUG_PRINTK("There is no association yet.\n");
+ if (sinfo_flags & (MSG_EOF | MSG_ABORT)) {
+ err = -EINVAL;
+ goto out_unlock;
+ }
+
/* Check for invalid stream against the stream counts,
* either the default or the user specified stream counts.
*/
}
if (sinit->sinit_max_init_timeo) {
asoc->max_init_timeo =
- MSECS_TO_JIFFIES(sinit->sinit_max_init_timeo);
+ msecs_to_jiffies(sinit->sinit_max_init_timeo);
}
}
int flags, int *addr_len)
{
struct sctp_ulpevent *event = NULL;
- struct sctp_opt *sp = sctp_sk(sk);
+ struct sctp_sock *sp = sctp_sk(sk);
struct sk_buff *skb;
int copied;
int err = 0;
* instead a error will be indicated to the user.
*/
static int sctp_setsockopt_disable_fragments(struct sock *sk,
- char *optval, int optlen)
+ char __user *optval, int optlen)
{
int val;
if (optlen < sizeof(int))
return -EINVAL;
- if (get_user(val, (int *)optval))
+ if (get_user(val, (int __user *)optval))
return -EFAULT;
sctp_sk(sk)->disable_fragments = (val == 0) ? 0 : 1;
return 0;
}
-static int sctp_setsockopt_events(struct sock *sk, char *optval,
+static int sctp_setsockopt_events(struct sock *sk, char __user *optval,
int optlen)
{
if (optlen != sizeof(struct sctp_event_subscribe))
* integer defining the number of seconds of idle time before an
* association is closed.
*/
-static int sctp_setsockopt_autoclose(struct sock *sk, char *optval,
+static int sctp_setsockopt_autoclose(struct sock *sk, char __user *optval,
int optlen)
{
- struct sctp_opt *sp = sctp_sk(sk);
+ struct sctp_sock *sp = sctp_sk(sk);
/* Applicable to UDP-style socket only */
if (sctp_style(sk, TCP))
* considered unreachable.
*/
static int sctp_setsockopt_peer_addr_params(struct sock *sk,
- char *optval, int optlen)
+ char __user *optval, int optlen)
{
struct sctp_paddrparams params;
struct sctp_transport *trans;
if (copy_from_user(¶ms, optval, optlen))
return -EFAULT;
+ /*
+ * API 7. Socket Options (setting the default value for the endpoint)
+ * All options that support specific settings on an association by
+ * filling in either an association id variable or a sockaddr_storage
+ * SHOULD also support setting of the same value for the entire endpoint
+ * (i.e. future associations). To accomplish this the following logic is
+ * used when setting one of these options:
+
+ * c) If neither the sockaddr_storage or association identification is
+ * set i.e. the sockaddr_storage is set to all 0's (INADDR_ANY) and
+ * the association identification is 0, the settings are a default
+ * and to be applied to the endpoint (all future associations).
+ */
+
+ /* update default value for endpoint (all future associations) */
+ if (!params.spp_assoc_id &&
+ sctp_is_any(( union sctp_addr *)¶ms.spp_address)) {
+ /* Manual heartbeat on an endpoint is invalid. */
+ if (0xffffffff == params.spp_hbinterval)
+ return -EINVAL;
+ else if (params.spp_hbinterval)
+ sctp_sk(sk)->paddrparam.spp_hbinterval =
+ params.spp_hbinterval;
+ if (params.spp_pathmaxrxt)
+ sctp_sk(sk)->paddrparam.spp_pathmaxrxt =
+ params.spp_pathmaxrxt;
+ return 0;
+ }
+
trans = sctp_addr_id2transport(sk, ¶ms.spp_address,
params.spp_assoc_id);
if (!trans)
if (params.spp_hbinterval) {
trans->hb_allowed = 1;
trans->hb_interval =
- MSECS_TO_JIFFIES(params.spp_hbinterval);
+ msecs_to_jiffies(params.spp_hbinterval);
} else
trans->hb_allowed = 0;
}
/* spp_pathmaxrxt contains the maximum number of retransmissions
* before this address shall be considered unreachable.
*/
- trans->error_threshold = params.spp_pathmaxrxt;
+ if (params.spp_pathmaxrxt)
+ trans->max_retrans = params.spp_pathmaxrxt;
return 0;
}
* 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 *optval, int optlen)
+static int sctp_setsockopt_initmsg(struct sock *sk, char __user *optval, int optlen)
{
struct sctp_initmsg sinit;
- struct sctp_opt *sp = sctp_sk(sk);
+ struct sctp_sock *sp = sctp_sk(sk);
if (optlen != sizeof(struct sctp_initmsg))
return -EINVAL;
* to this call if the caller is using the UDP model.
*/
static int sctp_setsockopt_default_send_param(struct sock *sk,
- char *optval, int optlen)
+ char __user *optval, int optlen)
{
struct sctp_sndrcvinfo info;
struct sctp_association *asoc;
- struct sctp_opt *sp = sctp_sk(sk);
+ struct sctp_sock *sp = sctp_sk(sk);
if (optlen != sizeof(struct sctp_sndrcvinfo))
return -EINVAL;
* the association primary. The enclosed address must be one of the
* association peer's addresses.
*/
-static int sctp_setsockopt_primary_addr(struct sock *sk, char *optval,
+static int sctp_setsockopt_primary_addr(struct sock *sk, char __user *optval,
int optlen)
{
struct sctp_prim prim;
* introduced, at the cost of more packets in the network. Expects an
* integer boolean flag.
*/
-static int sctp_setsockopt_nodelay(struct sock *sk, char *optval,
+static int sctp_setsockopt_nodelay(struct sock *sk, char __user *optval,
int optlen)
{
int val;
if (optlen < sizeof(int))
return -EINVAL;
- if (get_user(val, (int *)optval))
+ if (get_user(val, (int __user *)optval))
return -EFAULT;
sctp_sk(sk)->nodelay = (val == 0) ? 0 : 1;
* be changed.
*
*/
-static int sctp_setsockopt_rtoinfo(struct sock *sk, char *optval, int optlen) {
+static int sctp_setsockopt_rtoinfo(struct sock *sk, char __user *optval, int optlen) {
struct sctp_rtoinfo rtoinfo;
struct sctp_association *asoc;
if (asoc) {
if (rtoinfo.srto_initial != 0)
asoc->rto_initial =
- MSECS_TO_JIFFIES(rtoinfo.srto_initial);
+ msecs_to_jiffies(rtoinfo.srto_initial);
if (rtoinfo.srto_max != 0)
- asoc->rto_max = MSECS_TO_JIFFIES(rtoinfo.srto_max);
+ asoc->rto_max = msecs_to_jiffies(rtoinfo.srto_max);
if (rtoinfo.srto_min != 0)
- asoc->rto_min = MSECS_TO_JIFFIES(rtoinfo.srto_min);
+ asoc->rto_min = msecs_to_jiffies(rtoinfo.srto_min);
} else {
/* If there is no association or the association-id = 0
* set the values to the endpoint.
*/
- struct sctp_opt *sp = sctp_sk(sk);
+ struct sctp_sock *sp = sctp_sk(sk);
if (rtoinfo.srto_initial != 0)
sp->rtoinfo.srto_initial = rtoinfo.srto_initial;
* See [SCTP] for more information.
*
*/
-static int sctp_setsockopt_associnfo(struct sock *sk, char *optval, int optlen)
+static int sctp_setsockopt_associnfo(struct sock *sk, char __user *optval, int optlen)
{
struct sctp_assocparams assocparams;
}
} else {
/* Set the values to the endpoint */
- struct sctp_opt *sp = sctp_sk(sk);
+ struct sctp_sock *sp = sctp_sk(sk);
if (assocparams.sasoc_asocmaxrxt != 0)
sp->assocparams.sasoc_asocmaxrxt =
* 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 *optval, int optlen)
+static int sctp_setsockopt_mappedv4(struct sock *sk, char __user *optval, int optlen)
{
int val;
- struct sctp_opt *sp = sctp_sk(sk);
+ struct sctp_sock *sp = sctp_sk(sk);
if (optlen < sizeof(int))
return -EINVAL;
- if (get_user(val, (int *)optval))
+ if (get_user(val, (int __user *)optval))
return -EFAULT;
if (val)
sp->v4mapped = 1;
* PMTU of the underlying association is smaller than the value set by
* the user.
*/
-static int sctp_setsockopt_maxseg(struct sock *sk, char *optval, int optlen)
+static int sctp_setsockopt_maxseg(struct sock *sk, char __user *optval, int optlen)
{
struct sctp_association *asoc;
struct list_head *pos;
- struct sctp_opt *sp = sctp_sk(sk);
+ struct sctp_sock *sp = sctp_sk(sk);
int val;
if (optlen < sizeof(int))
return -EINVAL;
- if (get_user(val, (int *)optval))
+ if (get_user(val, (int __user *)optval))
return -EFAULT;
if ((val < 8) || (val > SCTP_MAX_CHUNK_LEN))
return -EINVAL;
* locally bound addresses. The following structure is used to make a
* set primary request:
*/
-static int sctp_setsockopt_peer_primary_addr(struct sock *sk, char *optval,
+static int sctp_setsockopt_peer_primary_addr(struct sock *sk, char __user *optval,
int optlen)
{
- struct sctp_opt *sp;
+ struct sctp_sock *sp;
struct sctp_endpoint *ep;
struct sctp_association *asoc = NULL;
struct sctp_setpeerprim prim;
return err;
}
+static int sctp_setsockopt_adaption_layer(struct sock *sk, char __user *optval,
+ int optlen)
+{
+ __u32 val;
+
+ if (optlen < sizeof(__u32))
+ return -EINVAL;
+ if (copy_from_user(&val, optval, sizeof(__u32)))
+ return -EFAULT;
+
+ sctp_sk(sk)->adaption_ind = val;
+
+ return 0;
+}
/* API 6.2 setsockopt(), getsockopt()
*
*
* The syntax is:
*
- * ret = getsockopt(int sd, int level, int optname, void *optval,
- * int *optlen);
- * ret = setsockopt(int sd, int level, int optname, const void *optval,
+ * ret = getsockopt(int sd, int level, int optname, void __user *optval,
+ * int __user *optlen);
+ * ret = setsockopt(int sd, int level, int optname, const void __user *optval,
* int optlen);
*
* sd - the socket descript.
* optlen - the size of the buffer.
*/
SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname,
- char *optval, int optlen)
+ char __user *optval, int optlen)
{
int retval = 0;
switch (optname) {
case SCTP_SOCKOPT_BINDX_ADD:
/* 'optlen' is the size of the addresses buffer. */
- retval = sctp_setsockopt_bindx(sk, (struct sockaddr *)optval,
+ retval = sctp_setsockopt_bindx(sk, (struct sockaddr __user *)optval,
optlen, SCTP_BINDX_ADD_ADDR);
break;
case SCTP_SOCKOPT_BINDX_REM:
/* 'optlen' is the size of the addresses buffer. */
- retval = sctp_setsockopt_bindx(sk, (struct sockaddr *)optval,
+ retval = sctp_setsockopt_bindx(sk, (struct sockaddr __user *)optval,
optlen, SCTP_BINDX_REM_ADDR);
break;
case SCTP_MAXSEG:
retval = sctp_setsockopt_maxseg(sk, optval, optlen);
break;
+ case SCTP_ADAPTION_LAYER:
+ retval = sctp_setsockopt_adaption_layer(sk, optval, optlen);
+ break;
+
default:
retval = -ENOPROTOOPT;
break;
SCTP_STATIC int sctp_connect(struct sock *sk, struct sockaddr *uaddr,
int addr_len)
{
- struct sctp_opt *sp;
+ struct sctp_sock *sp;
struct sctp_endpoint *ep;
struct sctp_association *asoc;
struct sctp_transport *transport;
*/
SCTP_STATIC struct sock *sctp_accept(struct sock *sk, int flags, int *err)
{
- struct sctp_opt *sp;
+ struct sctp_sock *sp;
struct sctp_endpoint *ep;
struct sock *newsk = NULL;
struct sctp_association *asoc;
SCTP_STATIC int sctp_init_sock(struct sock *sk)
{
struct sctp_endpoint *ep;
- struct sctp_opt *sp;
+ struct sctp_sock *sp;
SCTP_DEBUG_PRINTK("sctp_init_sock(sk: %p)\n", sk);
sp->initmsg.sinit_num_ostreams = sctp_max_outstreams;
sp->initmsg.sinit_max_instreams = sctp_max_instreams;
sp->initmsg.sinit_max_attempts = sctp_max_retrans_init;
- sp->initmsg.sinit_max_init_timeo = JIFFIES_TO_MSECS(sctp_rto_max);
+ sp->initmsg.sinit_max_init_timeo = jiffies_to_msecs(sctp_rto_max);
/* Initialize default RTO related parameters. These parameters can
* be modified for with the SCTP_RTOINFO socket option.
*/
- sp->rtoinfo.srto_initial = JIFFIES_TO_MSECS(sctp_rto_initial);
- sp->rtoinfo.srto_max = JIFFIES_TO_MSECS(sctp_rto_max);
- sp->rtoinfo.srto_min = JIFFIES_TO_MSECS(sctp_rto_min);
+ sp->rtoinfo.srto_initial = jiffies_to_msecs(sctp_rto_initial);
+ sp->rtoinfo.srto_max = jiffies_to_msecs(sctp_rto_max);
+ sp->rtoinfo.srto_min = jiffies_to_msecs(sctp_rto_min);
/* Initialize default association related parameters. These parameters
* can be modified with the SCTP_ASSOCINFO socket option.
sp->assocparams.sasoc_peer_rwnd = 0;
sp->assocparams.sasoc_local_rwnd = 0;
sp->assocparams.sasoc_cookie_life =
- JIFFIES_TO_MSECS(sctp_valid_cookie_life);
+ jiffies_to_msecs(sctp_valid_cookie_life);
/* Initialize default event subscriptions. By default, all the
* options are off.
/* Default Peer Address Parameters. These defaults can
* be modified via SCTP_PEER_ADDR_PARAMS
*/
- sp->paddrparam.spp_hbinterval = JIFFIES_TO_MSECS(sctp_hb_interval);
+ sp->paddrparam.spp_hbinterval = jiffies_to_msecs(sctp_hb_interval);
sp->paddrparam.spp_pathmaxrxt = sctp_max_retrans_path;
/* If enabled no SCTP message fragmentation will be performed.
/* User specified fragmentation limit. */
sp->user_frag = 0;
+ sp->adaption_ind = 0;
+
sp->pf = sctp_get_pf_specific(sk->sk_family);
/* Control variables for partial data delivery. */
* number of unacked data chunks, and number of data chunks pending
* receipt. This information is read-only.
*/
-static int sctp_getsockopt_sctp_status(struct sock *sk, int len, char *optval,
- int *optlen)
+static int sctp_getsockopt_sctp_status(struct sock *sk, int len,
+ char __user *optval,
+ int __user *optlen)
{
struct sctp_status status;
struct sctp_association *asoc = NULL;
status.sstat_primary.spinfo_state = transport->active;
status.sstat_primary.spinfo_cwnd = transport->cwnd;
status.sstat_primary.spinfo_srtt = transport->srtt;
- status.sstat_primary.spinfo_rto = JIFFIES_TO_MSECS(transport->rto);
+ status.sstat_primary.spinfo_rto = jiffies_to_msecs(transport->rto);
status.sstat_primary.spinfo_mtu = transport->pmtu;
if (put_user(len, optlen)) {
goto out;
}
- SCTP_DEBUG_PRINTK("sctp_getsockopt_sctp_status(%d): %d %d %p\n",
+ SCTP_DEBUG_PRINTK("sctp_getsockopt_sctp_status(%d): %d %d %d\n",
len, status.sstat_state, status.sstat_rwnd,
status.sstat_assoc_id);
* read-only.
*/
static int sctp_getsockopt_peer_addr_info(struct sock *sk, int len,
- char *optval, int *optlen)
+ char __user *optval,
+ int __user *optlen)
{
struct sctp_paddrinfo pinfo;
struct sctp_transport *transport;
pinfo.spinfo_state = transport->active;
pinfo.spinfo_cwnd = transport->cwnd;
pinfo.spinfo_srtt = transport->srtt;
- pinfo.spinfo_rto = JIFFIES_TO_MSECS(transport->rto);
+ pinfo.spinfo_rto = jiffies_to_msecs(transport->rto);
pinfo.spinfo_mtu = transport->pmtu;
if (put_user(len, optlen)) {
* instead a error will be indicated to the user.
*/
static int sctp_getsockopt_disable_fragments(struct sock *sk, int len,
- char *optval, int *optlen)
+ char __user *optval, int __user *optlen)
{
int val;
* This socket option is used to specify various notifications and
* ancillary data the user wishes to receive.
*/
-static int sctp_getsockopt_events(struct sock *sk, int len, char *optval,
- int *optlen)
+static int sctp_getsockopt_events(struct sock *sk, int len, char __user *optval,
+ int __user *optlen)
{
if (len != sizeof(struct sctp_event_subscribe))
return -EINVAL;
* integer defining the number of seconds of idle time before an
* association is closed.
*/
-static int sctp_getsockopt_autoclose(struct sock *sk, int len, char *optval, int *optlen)
+static int sctp_getsockopt_autoclose(struct sock *sk, int len, char __user *optval, int __user *optlen)
{
/* Applicable to UDP-style socket only */
if (sctp_style(sk, TCP))
return err;
}
-static int sctp_getsockopt_peeloff(struct sock *sk, int len, char *optval, int *optlen)
+static int sctp_getsockopt_peeloff(struct sock *sk, int len, char __user *optval, int __user *optlen)
{
sctp_peeloff_arg_t peeloff;
struct socket *newsock;
* considered unreachable.
*/
static int sctp_getsockopt_peer_addr_params(struct sock *sk, int len,
- char *optval, int *optlen)
+ char __user *optval, int __user *optlen)
{
struct sctp_paddrparams params;
struct sctp_transport *trans;
if (len != sizeof(struct sctp_paddrparams))
return -EINVAL;
- if (copy_from_user(¶ms, optval, *optlen))
+ if (copy_from_user(¶ms, optval, len))
return -EFAULT;
+ /* If no association id is specified retrieve the default value
+ * for the endpoint that will be used for all future associations
+ */
+ if (!params.spp_assoc_id &&
+ sctp_is_any(( union sctp_addr *)¶ms.spp_address)) {
+ params.spp_hbinterval = sctp_sk(sk)->paddrparam.spp_hbinterval;
+ params.spp_pathmaxrxt = sctp_sk(sk)->paddrparam.spp_pathmaxrxt;
+
+ goto done;
+ }
+
trans = sctp_addr_id2transport(sk, ¶ms.spp_address,
params.spp_assoc_id);
if (!trans)
if (!trans->hb_allowed)
params.spp_hbinterval = 0;
else
- params.spp_hbinterval = JIFFIES_TO_MSECS(trans->hb_interval);
+ params.spp_hbinterval = jiffies_to_msecs(trans->hb_interval);
/* spp_pathmaxrxt contains the maximum number of retransmissions
* before this address shall be considered unreachable.
*/
- params.spp_pathmaxrxt = trans->error_threshold;
+ params.spp_pathmaxrxt = trans->max_retrans;
+done:
if (copy_to_user(optval, ¶ms, len))
return -EFAULT;
* by the change). With TCP-style sockets, this option is inherited by
* sockets derived from a listener socket.
*/
-static int sctp_getsockopt_initmsg(struct sock *sk, int len, char *optval, int *optlen)
+static int sctp_getsockopt_initmsg(struct sock *sk, int len, char __user *optval, int __user *optlen)
{
if (len != sizeof(struct sctp_initmsg))
return -EINVAL;
}
static int sctp_getsockopt_peer_addrs_num(struct sock *sk, int len,
- char *optval, int *optlen)
+ char __user *optval, int __user *optlen)
{
sctp_assoc_t id;
struct sctp_association *asoc;
}
static int sctp_getsockopt_peer_addrs(struct sock *sk, int len,
- char *optval, int *optlen)
+ 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 *to;
+ void __user *to;
union sctp_addr temp;
- struct sctp_opt *sp = sctp_sk(sk);
+ struct sctp_sock *sp = sctp_sk(sk);
int addrlen;
if (len != sizeof(struct sctp_getaddrs))
if (!asoc)
return -EINVAL;
- to = (void *)getaddrs.addrs;
+ 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));
}
static int sctp_getsockopt_local_addrs_num(struct sock *sk, int len,
- char *optval, int *optlen)
+ char __user *optval,
+ int __user *optlen)
{
sctp_assoc_t id;
struct sctp_bind_addr *bp;
struct sctp_association *asoc;
struct list_head *pos;
+ struct sctp_sockaddr_entry *addr;
+ rwlock_t *addr_lock;
+ unsigned long flags;
int cnt = 0;
if (len != sizeof(sctp_assoc_t))
*/
if (0 == id) {
bp = &sctp_sk(sk)->ep->base.bind_addr;
+ addr_lock = &sctp_sk(sk)->ep->base.addr_lock;
} else {
asoc = sctp_id2assoc(sk, id);
if (!asoc)
return -EINVAL;
bp = &asoc->base.bind_addr;
+ addr_lock = &asoc->base.addr_lock;
+ }
+
+ sctp_read_lock(addr_lock);
+
+ /* 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)) {
+ sctp_spin_lock_irqsave(&sctp_local_addr_lock, flags);
+ list_for_each(pos, &sctp_local_addr_list) {
+ addr = list_entry(pos,
+ struct sctp_sockaddr_entry,
+ list);
+ if ((PF_INET == sk->sk_family) &&
+ (AF_INET6 == addr->a.sa.sa_family))
+ continue;
+ cnt++;
+ }
+ sctp_spin_unlock_irqrestore(&sctp_local_addr_lock,
+ flags);
+ } else {
+ cnt = 1;
+ }
+ goto done;
}
list_for_each(pos, &bp->address_list) {
cnt ++;
}
+done:
+ sctp_read_unlock(addr_lock);
+ return cnt;
+}
+
+/* Helper function that copies local addresses to user and returns the number
+ * of addresses copied.
+ */
+static int sctp_copy_laddrs_to_user(struct sock *sk, __u16 port, int max_addrs,
+ void __user *to)
+{
+ struct list_head *pos;
+ struct sctp_sockaddr_entry *addr;
+ unsigned long flags;
+ union sctp_addr temp;
+ int cnt = 0;
+ int addrlen;
+
+ sctp_spin_lock_irqsave(&sctp_local_addr_lock, flags);
+ list_for_each(pos, &sctp_local_addr_list) {
+ addr = list_entry(pos, struct sctp_sockaddr_entry, list);
+ 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;
+ temp.v4.sin_port = htons(port);
+ if (copy_to_user(to, &temp, addrlen)) {
+ sctp_spin_unlock_irqrestore(&sctp_local_addr_lock,
+ flags);
+ return -EFAULT;
+ }
+ to += addrlen;
+ cnt ++;
+ if (cnt >= max_addrs) break;
+ }
+ sctp_spin_unlock_irqrestore(&sctp_local_addr_lock, flags);
+
return cnt;
}
static int sctp_getsockopt_local_addrs(struct sock *sk, int len,
- char *optval, int *optlen)
+ char __user *optval, int __user *optlen)
{
struct sctp_bind_addr *bp;
struct sctp_association *asoc;
struct list_head *pos;
int cnt = 0;
struct sctp_getaddrs getaddrs;
- struct sctp_sockaddr_entry *from;
- void *to;
+ struct sctp_sockaddr_entry *addr;
+ void __user *to;
union sctp_addr temp;
- struct sctp_opt *sp = sctp_sk(sk);
+ struct sctp_sock *sp = sctp_sk(sk);
int addrlen;
+ rwlock_t *addr_lock;
+ int err = 0;
if (len != sizeof(struct sctp_getaddrs))
return -EINVAL;
*/
if (0 == getaddrs.assoc_id) {
bp = &sctp_sk(sk)->ep->base.bind_addr;
+ addr_lock = &sctp_sk(sk)->ep->base.addr_lock;
} else {
asoc = sctp_id2assoc(sk, getaddrs.assoc_id);
if (!asoc)
return -EINVAL;
bp = &asoc->base.bind_addr;
+ addr_lock = &asoc->base.addr_lock;
+ }
+
+ to = getaddrs.addrs;
+
+ sctp_read_lock(addr_lock);
+
+ /* 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_to_user(sk, bp->port,
+ getaddrs.addr_num, to);
+ if (cnt < 0) {
+ err = cnt;
+ goto unlock;
+ }
+ goto copy_getaddrs;
+ }
}
- to = (void *)getaddrs.addrs;
list_for_each(pos, &bp->address_list) {
- from = list_entry(pos,
- struct sctp_sockaddr_entry,
- list);
- memcpy(&temp, &from->a, sizeof(temp));
+ addr = list_entry(pos, struct sctp_sockaddr_entry, list);
+ memcpy(&temp, &addr->a, sizeof(temp));
sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp);
addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len;
temp.v4.sin_port = htons(temp.v4.sin_port);
- if (copy_to_user(to, &temp, addrlen))
- return -EFAULT;
+ if (copy_to_user(to, &temp, addrlen)) {
+ err = -EFAULT;
+ goto unlock;
+ }
to += addrlen;
cnt ++;
if (cnt >= getaddrs.addr_num) break;
}
+
+copy_getaddrs:
getaddrs.addr_num = cnt;
if (copy_to_user(optval, &getaddrs, sizeof(struct sctp_getaddrs)))
- return -EFAULT;
+ err = -EFAULT;
- return 0;
+unlock:
+ sctp_read_unlock(addr_lock);
+ return err;
}
/* 7.1.10 Set Primary Address (SCTP_PRIMARY_ADDR)
* association peer's addresses.
*/
static int sctp_getsockopt_primary_addr(struct sock *sk, int len,
- char *optval, int *optlen)
+ char __user *optval, int __user *optlen)
{
struct sctp_prim prim;
struct sctp_association *asoc;
- struct sctp_opt *sp = sctp_sk(sk);
+ struct sctp_sock *sp = sctp_sk(sk);
if (len != sizeof(struct sctp_prim))
return -EINVAL;
return 0;
}
+/*
+ * 7.1.11 Set Adaption Layer Indicator (SCTP_ADAPTION_LAYER)
+ *
+ * Requests that the local endpoint set the specified Adaption Layer
+ * Indication parameter for all future INIT and INIT-ACK exchanges.
+ */
+static int sctp_getsockopt_adaption_layer(struct sock *sk, int len,
+ char __user *optval, int __user *optlen)
+{
+ __u32 val;
+
+ if (len < sizeof(__u32))
+ return -EINVAL;
+
+ len = sizeof(__u32);
+ val = sctp_sk(sk)->adaption_ind;
+ if (put_user(len, optlen))
+ return -EFAULT;
+ if (copy_to_user(optval, &val, len))
+ return -EFAULT;
+ return 0;
+}
+
/*
*
* 7.1.14 Set default send parameters (SCTP_DEFAULT_SEND_PARAM)
* For getsockopt, it get the default sctp_sndrcvinfo structure.
*/
static int sctp_getsockopt_default_send_param(struct sock *sk,
- int len, char *optval, int *optlen)
+ int len, char __user *optval,
+ int __user *optlen)
{
struct sctp_sndrcvinfo info;
struct sctp_association *asoc;
- struct sctp_opt *sp = sctp_sk(sk);
+ struct sctp_sock *sp = sctp_sk(sk);
if (len != sizeof(struct sctp_sndrcvinfo))
return -EINVAL;
*/
static int sctp_getsockopt_nodelay(struct sock *sk, int len,
- char *optval, int *optlen)
+ char __user *optval, int __user *optlen)
{
int val;
* be changed.
*
*/
-static int sctp_getsockopt_rtoinfo(struct sock *sk, int len, char *optval,
- int *optlen) {
+static int sctp_getsockopt_rtoinfo(struct sock *sk, int len,
+ char __user *optval,
+ int __user *optlen) {
struct sctp_rtoinfo rtoinfo;
struct sctp_association *asoc;
/* Values corresponding to the specific association. */
if (asoc) {
- rtoinfo.srto_initial = JIFFIES_TO_MSECS(asoc->rto_initial);
- rtoinfo.srto_max = JIFFIES_TO_MSECS(asoc->rto_max);
- rtoinfo.srto_min = JIFFIES_TO_MSECS(asoc->rto_min);
+ rtoinfo.srto_initial = jiffies_to_msecs(asoc->rto_initial);
+ rtoinfo.srto_max = jiffies_to_msecs(asoc->rto_max);
+ rtoinfo.srto_min = jiffies_to_msecs(asoc->rto_min);
} else {
/* Values corresponding to the endpoint. */
- struct sctp_opt *sp = sctp_sk(sk);
+ struct sctp_sock *sp = sctp_sk(sk);
rtoinfo.srto_initial = sp->rtoinfo.srto_initial;
rtoinfo.srto_max = sp->rtoinfo.srto_max;
* See [SCTP] for more information.
*
*/
-static int sctp_getsockopt_associnfo(struct sock *sk, int len, char *optval,
- int *optlen)
+static int sctp_getsockopt_associnfo(struct sock *sk, int len,
+ char __user *optval,
+ int __user *optlen)
{
struct sctp_assocparams assocparams;
return -EINVAL;
/* Values correspoinding to the specific association */
- if (assocparams.sasoc_assoc_id != 0) {
+ if (asoc) {
assocparams.sasoc_asocmaxrxt = asoc->max_retrans;
assocparams.sasoc_peer_rwnd = asoc->peer.rwnd;
assocparams.sasoc_local_rwnd = asoc->a_rwnd;
assocparams.sasoc_number_peer_destinations = cnt;
} else {
/* Values corresponding to the endpoint */
- struct sctp_opt *sp = sctp_sk(sk);
+ struct sctp_sock *sp = sctp_sk(sk);
assocparams.sasoc_asocmaxrxt = sp->assocparams.sasoc_asocmaxrxt;
assocparams.sasoc_peer_rwnd = sp->assocparams.sasoc_peer_rwnd;
* addresses on the socket.
*/
static int sctp_getsockopt_mappedv4(struct sock *sk, int len,
- char *optval, int *optlen)
+ char __user *optval, int __user *optlen)
{
int val;
- struct sctp_opt *sp = sctp_sk(sk);
+ struct sctp_sock *sp = sctp_sk(sk);
if (len < sizeof(int))
return -EINVAL;
* the user.
*/
static int sctp_getsockopt_maxseg(struct sock *sk, int len,
- char *optval, int *optlen)
+ char __user *optval, int __user *optlen)
{
int val;
}
SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
- char *optval, int *optlen)
+ char __user *optval, int __user *optlen)
{
int retval = 0;
int len;
retval = sctp_getsockopt_peer_addr_info(sk, len, optval,
optlen);
break;
+ case SCTP_ADAPTION_LAYER:
+ retval = sctp_getsockopt_adaption_layer(sk, len, optval,
+ optlen);
+ break;
default:
retval = -ENOPROTOOPT;
break;
*/
SCTP_STATIC int sctp_seqpacket_listen(struct sock *sk, int backlog)
{
- struct sctp_opt *sp = sctp_sk(sk);
+ struct sctp_sock *sp = sctp_sk(sk);
struct sctp_endpoint *ep = sp->ep;
/* Only UDP style sockets that are not peeled off are allowed to
*/
SCTP_STATIC int sctp_stream_listen(struct sock *sk, int backlog)
{
- struct sctp_opt *sp = sctp_sk(sk);
+ struct sctp_sock *sp = sctp_sk(sk);
struct sctp_endpoint *ep = sp->ep;
/* If backlog is zero, disable listening. */
unsigned int sctp_poll(struct file *file, struct socket *sock, poll_table *wait)
{
struct sock *sk = sock->sk;
- struct sctp_opt *sp = sctp_sk(sk);
+ struct sctp_sock *sp = sctp_sk(sk);
unsigned int mask;
poll_wait(file, sk->sk_sleep, wait);
for (cmsg = CMSG_FIRSTHDR(msg);
cmsg != NULL;
cmsg = CMSG_NXTHDR((struct msghdr*)msg, cmsg)) {
- /* Check for minimum length. The SCM code has this check. */
- if (cmsg->cmsg_len < sizeof(struct cmsghdr) ||
- (unsigned long)(((char*)cmsg - (char*)msg->msg_control)
- + cmsg->cmsg_len) > msg->msg_controllen) {
+ if (!CMSG_OK(msg, cmsg))
return -EINVAL;
- }
/* Should we parse this header or ignore? */
if (cmsg->cmsg_level != IPPROTO_SCTP)
return NULL;
}
-/* Verify that this is a valid address. */
-static inline int sctp_verify_addr(struct sock *sk, union sctp_addr *addr,
- int len)
-{
- struct sctp_af *af;
-
- /* Verify basic sockaddr. */
- af = sctp_sockaddr_af(sctp_sk(sk), addr, len);
- if (!af)
- return -EINVAL;
-
- /* Is this a valid SCTP address? */
- if (!af->addr_valid(addr, sctp_sk(sk)))
- return -EINVAL;
-
- if (!sctp_sk(sk)->pf->send_verify(sctp_sk(sk), (addr)))
- return -EINVAL;
-
- return 0;
-}
-
-/* Get the sndbuf space available at the time on the association. */
-static inline int sctp_wspace(struct sctp_association *asoc)
-{
- struct sock *sk = asoc->base.sk;
- int amt = 0;
-
- amt = sk->sk_sndbuf - asoc->sndbuf_used;
- if (amt < 0)
- amt = 0;
- return amt;
-}
-
-/* Increment the used sndbuf space count of the corresponding association by
- * the size of the outgoing data chunk.
- * Also, set the skb destructor for sndbuf accounting later.
- *
- * Since it is always 1-1 between chunk and skb, and also a new skb is always
- * allocated for chunk bundling in sctp_packet_transmit(), we can use the
- * destructor in the data chunk skb for the purpose of the sndbuf space
- * tracking.
- */
-static inline void sctp_set_owner_w(struct sctp_chunk *chunk)
-{
- struct sctp_association *asoc = chunk->asoc;
- struct sock *sk = asoc->base.sk;
-
- /* The sndbuf space is tracked per association. */
- sctp_association_hold(asoc);
-
- chunk->skb->destructor = sctp_wfree;
- /* Save the chunk pointer in skb for sctp_wfree to use later. */
- *((struct sctp_chunk **)(chunk->skb->cb)) = chunk;
-
- asoc->sndbuf_used += SCTP_DATA_SNDSIZE(chunk);
- sk->sk_wmem_queued += SCTP_DATA_SNDSIZE(chunk);
-}
-
/* If sndbuf has changed, wake up per association sndbuf waiters. */
static void __sctp_write_space(struct sctp_association *asoc)
{
chunk = *((struct sctp_chunk **)(skb->cb));
asoc = chunk->asoc;
sk = asoc->base.sk;
- asoc->sndbuf_used -= SCTP_DATA_SNDSIZE(chunk);
- sk->sk_wmem_queued -= SCTP_DATA_SNDSIZE(chunk);
+ asoc->sndbuf_used -= SCTP_DATA_SNDSIZE(chunk) +
+ sizeof(struct sk_buff) +
+ sizeof(struct sctp_chunk);
+
+ sk->sk_wmem_queued -= SCTP_DATA_SNDSIZE(chunk) +
+ sizeof(struct sk_buff) +
+ sizeof(struct sctp_chunk);
+
+ atomic_sub(sizeof(struct sctp_chunk), &sk->sk_wmem_alloc);
+
+ sock_wfree(skb);
__sctp_write_space(asoc);
sctp_association_put(asoc);
return err;
do_error:
- err = -ECONNREFUSED;
+ if (asoc->counters[SCTP_COUNTER_INIT_ERROR] + 1 >=
+ asoc->max_init_attempts)
+ err = -ETIMEDOUT;
+ else
+ err = -ECONNREFUSED;
goto out;
do_interrupted:
struct sctp_association *assoc,
sctp_socket_type_t type)
{
- struct sctp_opt *oldsp = sctp_sk(oldsk);
- struct sctp_opt *newsp = sctp_sk(newsk);
+ struct sctp_sock *oldsp = sctp_sk(oldsk);
+ struct sctp_sock *newsp = sctp_sk(newsk);
struct sctp_bind_bucket *pp; /* hash list port iterator */
struct sctp_endpoint *newep = newsp->ep;
struct sk_buff *skb, *tmp;
struct sctp_ulpevent *event;
+ int flags = 0;
/* Migrate socket buffer sizes and all the socket level options to the
* new socket.
newsk->sk_sndbuf = oldsk->sk_sndbuf;
newsk->sk_rcvbuf = oldsk->sk_rcvbuf;
/* Brute force copy old sctp opt. */
- memcpy(newsp, oldsp, sizeof(struct sctp_opt));
+ inet_sk_copy_descendant(newsk, oldsk);
/* Restore the ep value that was overwritten with the above structure
* copy.
sctp_sk(newsk)->bind_hash = pp;
inet_sk(newsk)->num = inet_sk(oldsk)->num;
+ /* Copy the bind_addr list from the original endpoint to the new
+ * endpoint so that we can handle restarts properly
+ */
+ 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);
+
/* Move any messages in the old socket's receive queue that are for the
* peeled off association to the new socket's receive queue.
*/
* delivery. Three cases:
* 1) No partial deliver; no work.
* 2) Peeling off partial delivery; keep pd_lobby in new pd_lobby.
- * 3) Peeling off non-partial delivery; move pd_lobby to recieve_queue.
+ * 3) Peeling off non-partial delivery; move pd_lobby to receive_queue.
*/
skb_queue_head_init(&newsp->pd_lobby);
sctp_sk(newsk)->pd_mode = assoc->ulpq.pd_mode;
/* This proto struct describes the ULP interface for SCTP. */
struct proto sctp_prot = {
.name = "SCTP",
+ .owner = THIS_MODULE,
.close = sctp_close,
.connect = sctp_connect,
.disconnect = sctp_disconnect,
.hash = sctp_hash,
.unhash = sctp_unhash,
.get_port = sctp_get_port,
+ .obj_size = sizeof(struct sctp_sock),
+};
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+struct proto sctpv6_prot = {
+ .name = "SCTPv6",
+ .owner = THIS_MODULE,
+ .close = sctp_close,
+ .connect = sctp_connect,
+ .disconnect = sctp_disconnect,
+ .accept = sctp_accept,
+ .ioctl = sctp_ioctl,
+ .init = sctp_init_sock,
+ .destroy = sctp_destroy_sock,
+ .shutdown = sctp_shutdown,
+ .setsockopt = sctp_setsockopt,
+ .getsockopt = sctp_getsockopt,
+ .sendmsg = sctp_sendmsg,
+ .recvmsg = sctp_recvmsg,
+ .bind = sctp_bind,
+ .backlog_rcv = sctp_backlog_rcv,
+ .hash = sctp_hash,
+ .unhash = sctp_unhash,
+ .get_port = sctp_get_port,
+ .obj_size = sizeof(struct sctp6_sock),
};
+#endif /* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */