summaryrefslogtreecommitdiff
path: root/net/sctp/socket.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/sctp/socket.c')
-rw-r--r--net/sctp/socket.c186
1 files changed, 100 insertions, 86 deletions
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index ab54b2b63ed6..de5e9ddfd9b4 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -55,7 +55,6 @@
#include <linux/config.h>
#include <linux/types.h>
-#include <linux/compiler.h>
#include <linux/kernel.h>
#include <linux/wait.h>
#include <linux/time.h>
@@ -63,6 +62,7 @@
#include <linux/fcntl.h>
#include <linux/poll.h>
#include <linux/init.h>
+#include <linux/crypto.h>
#include <net/ip.h>
#include <net/icmp.h>
@@ -96,15 +96,16 @@ static int sctp_do_bind(struct sock *, union sctp_addr *, int);
static int sctp_autobind(struct sock *sk);
static void sctp_sock_migrate(struct sock *, struct sock *,
struct sctp_association *, sctp_socket_type_t);
+static char *sctp_hmac_alg = SCTP_COOKIE_HMAC_ALG;
/* Look up the association by its id. If this is not a UDP-style
* socket, the ID field is always ignored.
*/
-sctp_association_t *sctp_id2assoc(struct sock *sk, sctp_assoc_t id)
+struct sctp_association *sctp_id2assoc(struct sock *sk, sctp_assoc_t id)
{
- sctp_association_t *asoc = NULL;
+ struct sctp_association *asoc = NULL;
- /* If this is not a UDP-style socket, assoc id should be
+ /* If this is not a UDP-style socket, assoc id should be
* ignored.
*/
if (SCTP_SOCKET_UDP != sctp_sk(sk)->type) {
@@ -116,9 +117,9 @@ sctp_association_t *sctp_id2assoc(struct sock *sk, sctp_assoc_t id)
/* First, verify that this is a kernel address. */
if (sctp_is_valid_kaddr((unsigned long) id)) {
- sctp_association_t *temp = (sctp_association_t *) id;
+ struct sctp_association *temp = (sctp_association_t *) id;
- /* Verify that this _is_ an sctp_association_t
+ /* Verify that this _is_ an sctp_association
* data structure and if so, that the socket matches.
*/
if ((SCTP_ASSOC_EYECATCHER == temp->eyecatcher) &&
@@ -188,7 +189,6 @@ static struct sctp_af *sctp_sockaddr_af(struct sctp_opt *opt,
return 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)
{
@@ -637,7 +637,7 @@ SCTP_STATIC int sctp_setsockopt_bindx(struct sock* sk,
/* Alloc space for the address array in kernel memory. */
kaddrs = (struct sockaddr_storage *) kmalloc(addrssize, GFP_KERNEL);
- if (unlikely(NULL == kaddrs))
+ if (unlikely(!kaddrs))
return -ENOMEM;
if (copy_from_user(kaddrs, addrs, addrssize)) {
@@ -1134,8 +1134,9 @@ static int sctp_skb_pull(struct sk_buff *skb, int len)
*/
static struct sk_buff *sctp_skb_recv_datagram(struct sock *, int, int, int *);
-SCTP_STATIC int sctp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
- int len, int noblock, int flags, int *addr_len)
+SCTP_STATIC int sctp_recvmsg(struct kiocb *iocb, struct sock *sk,
+ struct msghdr *msg, int len, int noblock,
+ int flags, int *addr_len)
{
struct sctp_ulpevent *event = NULL;
struct sctp_opt *sp = sctp_sk(sk);
@@ -1156,7 +1157,7 @@ SCTP_STATIC int sctp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr
err = -ENOTCONN;
goto out;
}
-
+
skb = sctp_skb_recv_datagram(sk, flags, noblock, &err);
if (!skb)
goto out;
@@ -1271,7 +1272,7 @@ static int sctp_setsockopt_autoclose(struct sock *sk, char *optval,
}
static int sctp_setsockopt_peer_addr_params(struct sock *sk,
- char *optval, int optlen)
+ char *optval, int optlen)
{
struct sctp_paddrparams params;
sctp_association_t *asoc;
@@ -1329,8 +1330,7 @@ static int sctp_setsockopt_peer_addr_params(struct sock *sk,
return 0;
}
-static int sctp_setsockopt_initmsg(struct sock *sk, char *optval,
- int optlen)
+static int sctp_setsockopt_initmsg(struct sock *sk, char *optval, int optlen)
{
if (optlen != sizeof(struct sctp_initmsg))
return -EINVAL;
@@ -1340,7 +1340,6 @@ static int sctp_setsockopt_initmsg(struct sock *sk, char *optval,
}
/*
- *
* 7.1.15 Set default send parameters (SET_DEFAULT_SEND_PARAM)
*
* Applications that wish to use the sendto() system call may wish to
@@ -1428,12 +1427,10 @@ static int sctp_setsockopt_nodelay(struct sock *sk, char *optval,
if (optlen < sizeof(__u8))
return -EINVAL;
-
if (get_user(val, (__u8 *)optval))
return -EFAULT;
sctp_sk(sk)->nodelay = (val == 0) ? 0 : 1;
-
return 0;
}
@@ -1590,7 +1587,7 @@ SCTP_STATIC int sctp_connect(struct sock *sk, struct sockaddr *uaddr,
/* connect() cannot be done on a socket that is already in ESTABLISHED
* state - UDP-style peeled off socket or a TCP-style socket that
- * is already connected.
+ * is already connected.
* It cannot be done even on a TCP-style listening socket.
*/
if ((SCTP_SS_ESTABLISHED == sk->state) ||
@@ -1690,10 +1687,10 @@ SCTP_STATIC struct sock *sctp_accept(struct sock *sk, int flags, int *err)
struct sctp_opt *sp;
struct sctp_endpoint *ep;
struct sock *newsk = NULL;
- struct sctp_association *assoc;
+ struct sctp_association *asoc;
long timeo;
int error = 0;
-
+
sctp_lock_sock(sk);
sp = sctp_sk(sk);
@@ -1715,21 +1712,21 @@ SCTP_STATIC struct sock *sctp_accept(struct sock *sk, int flags, int *err)
if (error)
goto out;
- /* We treat the list of associations on the endpoint as the accept
- * queue and pick the first association on the list.
+ /* We treat the list of associations on the endpoint as the accept
+ * queue and pick the first association on the list.
*/
- assoc = list_entry(ep->asocs.next, struct sctp_association, asocs);
+ asoc = list_entry(ep->asocs.next, struct sctp_association, asocs);
- newsk = sp->pf->create_accept_sk(sk, assoc);
+ newsk = sp->pf->create_accept_sk(sk, asoc);
if (!newsk) {
error = -ENOMEM;
goto out;
}
/* Populate the fields of the newsk from the oldsk and migrate the
- * assoc to the newsk.
- */
- sctp_sock_migrate(sk, newsk, assoc, SCTP_SOCKET_TCP);
+ * asoc to the newsk.
+ */
+ sctp_sock_migrate(sk, newsk, asoc, SCTP_SOCKET_TCP);
out:
sctp_release_sock(sk);
@@ -1737,10 +1734,10 @@ out:
return newsk;
}
-/* FIXME: Write Comments. */
+/* The SCTP ioctl handler. */
SCTP_STATIC int sctp_ioctl(struct sock *sk, int cmd, unsigned long arg)
{
- return -EOPNOTSUPP; /* STUB */
+ return -ENOIOCTLCMD;
}
/* This is the function which gets called during socket creation to
@@ -1835,11 +1832,12 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk)
* change the data structure relationships, this may still
* be useful for storing pre-connect address information.
*/
- ep = sctp_endpoint_new(proto, sk, GFP_KERNEL);
- if (NULL == ep)
+ ep = sctp_endpoint_new(sk, GFP_KERNEL);
+ if (!ep)
return -ENOMEM;
sp->ep = ep;
+ sp->hmac = NULL;
SCTP_DBG_OBJCNT_INC(sock);
return 0;
@@ -1848,7 +1846,7 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk)
/* Cleanup any SCTP per socket resources. */
SCTP_STATIC int sctp_destroy_sock(struct sock *sk)
{
- sctp_endpoint_t *ep;
+ struct sctp_endpoint *ep;
SCTP_DEBUG_PRINTK("sctp_destroy_sock(sk: %p)\n", sk);
@@ -1877,7 +1875,7 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len, char *optval,
int *optlen)
{
struct sctp_status status;
- sctp_association_t *assoc = NULL;
+ struct sctp_association *asoc = NULL;
struct sctp_transport *transport;
sctp_assoc_t associd;
int retval = 0;
@@ -1893,22 +1891,22 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len, char *optval,
}
associd = status.sstat_assoc_id;
- assoc = sctp_id2assoc(sk, associd);
- if (!assoc) {
+ asoc = sctp_id2assoc(sk, associd);
+ if (!asoc) {
retval = -EINVAL;
goto out;
}
- transport = assoc->peer.primary_path;
+ transport = asoc->peer.primary_path;
- status.sstat_assoc_id = sctp_assoc2id(assoc);
- status.sstat_state = assoc->state;
- status.sstat_rwnd = assoc->peer.rwnd;
- status.sstat_unackdata = assoc->unack_data;
- status.sstat_penddata = assoc->peer.tsn_map.pending_data;
- status.sstat_instrms = assoc->c.sinit_max_instreams;
- status.sstat_outstrms = assoc->c.sinit_num_ostreams;
- status.sstat_fragmentation_point = assoc->frag_point;
+ status.sstat_assoc_id = sctp_assoc2id(asoc);
+ status.sstat_state = asoc->state;
+ status.sstat_rwnd = asoc->peer.rwnd;
+ status.sstat_unackdata = asoc->unack_data;
+ status.sstat_penddata = asoc->peer.tsn_map.pending_data;
+ status.sstat_instrms = asoc->c.sinit_max_instreams;
+ status.sstat_outstrms = asoc->c.sinit_num_ostreams;
+ status.sstat_fragmentation_point = asoc->frag_point;
status.sstat_primary.spinfo_assoc_id = sctp_assoc2id(transport->asoc);
memcpy(&status.sstat_primary.spinfo_address,
&(transport->ipaddr), sizeof(union sctp_addr));
@@ -1975,33 +1973,29 @@ static int sctp_getsockopt_autoclose(struct sock *sk, int len, char *optval, int
}
/* Helper routine to branch off an association to a new socket. */
-SCTP_STATIC int sctp_do_peeloff(sctp_association_t *assoc, struct socket **newsock)
+SCTP_STATIC int sctp_do_peeloff(struct sctp_association *asoc,
+ struct socket **sockp)
{
- struct sock *oldsk = assoc->base.sk;
- struct sock *newsk;
- struct socket *tmpsock;
+ struct sock *sk = asoc->base.sk;
+ struct socket *sock;
int err = 0;
/* An association cannot be branched off from an already peeled-off
* socket, nor is this supported for tcp style sockets.
*/
- if (SCTP_SOCKET_UDP != sctp_sk(oldsk)->type)
- return -EOPNOTSUPP;
+ if (SCTP_SOCKET_UDP != sctp_sk(sk)->type)
+ return -EINVAL;
/* Create a new socket. */
- err = sock_create(oldsk->family, SOCK_SEQPACKET, IPPROTO_SCTP,
- &tmpsock);
+ err = sock_create(sk->family, SOCK_SEQPACKET, IPPROTO_SCTP, &sock);
if (err < 0)
return err;
- newsk = tmpsock->sk;
-
/* Populate the fields of the newsk from the oldsk and migrate the
* assoc to the newsk.
- */
- sctp_sock_migrate(oldsk, newsk, assoc, SCTP_SOCKET_UDP_HIGH_BANDWIDTH);
-
- *newsock = tmpsock;
+ */
+ sctp_sock_migrate(sk, sock->sk, asoc, SCTP_SOCKET_UDP_HIGH_BANDWIDTH);
+ *sockp = sock;
return err;
}
@@ -2019,7 +2013,7 @@ static int sctp_getsockopt_peeloff(struct sock *sk, int len, char *optval, int *
return -EFAULT;
assoc = sctp_id2assoc(sk, peeloff.associd);
- if (NULL == assoc) {
+ if (!assoc) {
retval = -EINVAL;
goto out;
}
@@ -2049,7 +2043,7 @@ out:
return retval;
}
-static int sctp_getsockopt_peer_addr_params(struct sock *sk, int len,
+static int sctp_getsockopt_peer_addr_params(struct sock *sk, int len,
char *optval, int *optlen)
{
struct sctp_paddrparams params;
@@ -2102,7 +2096,7 @@ static int sctp_getsockopt_initmsg(struct sock *sk, int len, char *optval, int *
return 0;
}
-static int sctp_getsockopt_peer_addrs_num(struct sock *sk, int len,
+static int sctp_getsockopt_peer_addrs_num(struct sock *sk, int len,
char *optval, int *optlen)
{
sctp_assoc_t id;
@@ -2350,7 +2344,7 @@ static int sctp_getsockopt_default_send_param(struct sock *sk,
* integer boolean flag.
*/
-static int sctp_getsockopt_nodelay(struct sock *sk, int len,
+static int sctp_getsockopt_nodelay(struct sock *sk, int len,
char *optval, int *optlen)
{
__u8 val;
@@ -2418,7 +2412,7 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
retval = sctp_getsockopt_initmsg(sk, len, optval, optlen);
break;
case SCTP_GET_PEER_ADDRS_NUM:
- retval = sctp_getsockopt_peer_addrs_num(sk, len, optval,
+ retval = sctp_getsockopt_peer_addrs_num(sk, len, optval,
optlen);
break;
case SCTP_GET_LOCAL_ADDRS_NUM:
@@ -2553,7 +2547,7 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
}
- if (pp != NULL && pp->sk != NULL) {
+ if (pp && pp->sk) {
/* We had a port hash table hit - there is an
* available port (pp != NULL) and it is being
* used by other socket (pp->sk != NULL); that other
@@ -2601,18 +2595,17 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
/* If there was a hash table miss, create a new port. */
ret = 1;
- if (pp == NULL && (pp = sctp_bucket_create(head, snum)) == NULL)
+ if (!pp && !(pp = sctp_bucket_create(head, snum)))
goto fail_unlock;
/* In either case (hit or miss), make sure fastreuse is 1 only
* if sk->reuse is too (that is, if the caller requested
* SO_REUSEADDR on this socket -sk-).
*/
- if (pp->sk == NULL) {
+ if (!pp->sk)
pp->fastreuse = sk->reuse ? 1 : 0;
- } else if (pp->fastreuse && sk->reuse == 0) {
+ else if (pp->fastreuse && sk->reuse == 0)
pp->fastreuse = 0;
- }
/* We are set, so fill up all the data in the hash table
* entry, tie the socket list information with the rest of the
@@ -2702,7 +2695,7 @@ SCTP_STATIC int sctp_seqpacket_listen(struct sock *sk, int backlog)
/*
* 4.1.3 listen() - TCP Style Syntax
*
- * Applications uses listen() to ready the SCTP endpoint for accepting
+ * Applications uses listen() to ready the SCTP endpoint for accepting
* inbound associations.
*/
SCTP_STATIC int sctp_stream_listen(struct sock *sk, int backlog)
@@ -2739,15 +2732,25 @@ SCTP_STATIC int sctp_stream_listen(struct sock *sk, int backlog)
int sctp_inet_listen(struct socket *sock, int backlog)
{
struct sock *sk = sock->sk;
- int err;
+ struct crypto_tfm *tfm=NULL;
+ int err = -EINVAL;
+
+ if (unlikely(backlog < 0))
+ goto out;
sctp_lock_sock(sk);
- err = -EINVAL;
if (sock->state != SS_UNCONNECTED)
goto out;
- if (unlikely(backlog < 0))
- goto out;
+
+ /* Allocate HMAC for generating cookie. */
+ if (sctp_hmac_alg) {
+ tfm = crypto_alloc_tfm(sctp_hmac_alg, 0);
+ if (!tfm) {
+ err = -ENOSYS;
+ goto out;
+ }
+ }
switch (sock->type) {
case SOCK_SEQPACKET:
@@ -2756,14 +2759,21 @@ int sctp_inet_listen(struct socket *sock, int backlog)
case SOCK_STREAM:
err = sctp_stream_listen(sk, backlog);
break;
-
default:
- goto out;
+ break;
};
+ if (err)
+ goto cleanup;
+ /* Store away the transform reference. */
+ sctp_sk(sk)->hmac = tfm;
out:
sctp_release_sock(sk);
return err;
+cleanup:
+ if (tfm)
+ crypto_free_tfm(tfm);
+ goto out;
}
/*
@@ -2967,7 +2977,7 @@ SCTP_STATIC int sctp_msghdr_parse(const struct msghdr *msg,
/* Strictly check lengths following example in SCM code. */
switch (cmsg->cmsg_type) {
case SCTP_INIT:
- /* SCTP Socket API Extension (draft 1)
+ /* SCTP Socket API Extension
* 5.2.1 SCTP Initiation Structure (SCTP_INIT)
*
* This cmsghdr structure provides information for
@@ -2987,7 +2997,7 @@ SCTP_STATIC int sctp_msghdr_parse(const struct msghdr *msg,
break;
case SCTP_SNDRCV:
- /* SCTP Socket API Extension (draft 1)
+ /* SCTP Socket API Extension
* 5.2.2 SCTP Header Information Structure(SCTP_SNDRCV)
*
* This cmsghdr structure specifies SCTP options for
@@ -3002,7 +3012,8 @@ SCTP_STATIC int sctp_msghdr_parse(const struct msghdr *msg,
CMSG_LEN(sizeof(struct sctp_sndrcvinfo)))
return -EINVAL;
- cmsgs->info = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
+ cmsgs->info =
+ (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
/* Minimally, validate the sinfo_flags. */
if (cmsgs->info->sinfo_flags &
@@ -3085,13 +3096,14 @@ out:
* Note: This is pretty much the same routine as in core/datagram.c
* with a few changes to make lksctp work.
*/
-static struct sk_buff *sctp_skb_recv_datagram(struct sock *sk, int flags, int noblock, int *err)
+static struct sk_buff *sctp_skb_recv_datagram(struct sock *sk, int flags,
+ int noblock, int *err)
{
int error;
struct sk_buff *skb;
long timeo;
- /* Caller is allowed not to check sk->err before skb_recv_datagram() */
+ /* Caller is allowed not to check sk->err before calling. */
error = sock_error(sk);
if (error)
goto no_packet;
@@ -3140,7 +3152,7 @@ no_packet:
}
/* Verify that this is a valid address. */
-static inline int sctp_verify_addr(struct sock *sk, union sctp_addr *addr,
+static inline int sctp_verify_addr(struct sock *sk, union sctp_addr *addr,
int len)
{
struct sctp_af *af;
@@ -3443,11 +3455,12 @@ static int sctp_wait_for_accept(struct sock *sk, long timeo)
return err;
}
-/* Populate the fields of the newsk from the oldsk and migrate the assoc
+/* Populate the fields of the newsk from the oldsk and migrate the assoc
* and its messages to the newsk.
- */
-void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk,
- struct sctp_association *assoc, sctp_socket_type_t type)
+ */
+static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk,
+ struct sctp_association *assoc,
+ sctp_socket_type_t type)
{
struct sctp_opt *oldsp = sctp_sk(oldsk);
struct sctp_opt *newsp = sctp_sk(newsk);
@@ -3466,6 +3479,7 @@ void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk,
* copy.
*/
newsp->ep = newep;
+ newsp->hmac = NULL;
/* Move any messages in the old socket's receive queue that are for the
* peeled off association to the new socket's receive queue.
@@ -3526,7 +3540,7 @@ void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk,
newsk->state = SCTP_SS_ESTABLISHED;
}
-
+
/* This proto struct describes the ULP interface for SCTP. */
struct proto sctp_prot = {
.name = "SCTP",