summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/net/sctp/constants.h2
-rw-r--r--include/net/sctp/structs.h3
-rw-r--r--net/sctp/sm_make_chunk.c1
-rw-r--r--net/sctp/sm_sideeffect.c21
-rw-r--r--net/sctp/socket.c43
5 files changed, 65 insertions, 5 deletions
diff --git a/include/net/sctp/constants.h b/include/net/sctp/constants.h
index 60fe2147486f..9cdd0e551c19 100644
--- a/include/net/sctp/constants.h
+++ b/include/net/sctp/constants.h
@@ -216,7 +216,7 @@ typedef enum {
* - A socket in SCTP_SS_LISTENING state indicates that it is willing to
* accept new associations, but cannot initiate the creation of new ones.
* - A socket in SCTP_SS_ESTABLISHED state indicates that it has a single
- * association in ESTABLISHED state.
+ * association.
*/
typedef enum {
SCTP_SS_CLOSED = TCP_CLOSE,
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h
index 719aa2cbb28c..a4fcb9e14d01 100644
--- a/include/net/sctp/structs.h
+++ b/include/net/sctp/structs.h
@@ -1545,6 +1545,9 @@ struct sctp_association {
* after reaching 4294967295.
*/
__u32 addip_serial;
+
+ /* Is it a temporary association? */
+ __u8 temp;
};
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c
index 861f4f9da48d..5770f34706ae 100644
--- a/net/sctp/sm_make_chunk.c
+++ b/net/sctp/sm_make_chunk.c
@@ -1308,6 +1308,7 @@ struct sctp_association *sctp_make_temp_asoc(const struct sctp_endpoint *ep,
asoc = sctp_association_new(ep, ep->base.sk, scope, gfp);
if (!asoc)
goto nodata;
+ asoc->temp = 1;
skb = chunk->skb;
/* Create an entry for the source address of the packet. */
/* FIXME: Use the af specific helpers. */
diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c
index 46c50dbab871..821b8fd3dc67 100644
--- a/net/sctp/sm_sideeffect.c
+++ b/net/sctp/sm_sideeffect.c
@@ -651,6 +651,24 @@ static void sctp_cmd_new_state(sctp_cmd_seq_t *cmds, struct sctp_association *as
}
}
+/* Helper function to delete an association. */
+static void sctp_cmd_delete_tcb(sctp_cmd_seq_t *cmds,
+ struct sctp_association *asoc)
+{
+ struct sock *sk = asoc->base.sk;
+
+ /* If it is a non-temporary association belonging to a TCP-style
+ * listening socket, do not free it so that accept() can pick it
+ * up later.
+ */
+ if ((SCTP_SOCKET_TCP == sctp_sk(sk)->type) &&
+ (SCTP_SS_LISTENING == sk->state) && (!asoc->temp))
+ return;
+
+ sctp_unhash_established(asoc);
+ sctp_association_free(asoc);
+}
+
/* These three macros allow us to pull the debugging code out of the
* main flow of sctp_do_sm() to keep attention focused on the real
* functionality there.
@@ -861,8 +879,7 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
case SCTP_CMD_DELETE_TCB:
/* Delete the current association. */
- sctp_unhash_established(asoc);
- sctp_association_free(asoc);
+ sctp_cmd_delete_tcb(commands, asoc);
asoc = NULL;
break;
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index 446cd9c23658..01e8a1e4ce75 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -688,7 +688,17 @@ SCTP_STATIC void sctp_close(struct sock *sk, long timeout)
/* Walk all associations on a socket, not on an endpoint. */
list_for_each_safe(pos, temp, &ep->asocs) {
asoc = list_entry(pos, struct sctp_association, asocs);
- sctp_primitive_SHUTDOWN(asoc, NULL);
+
+ /* A closed association can still be in the list if it
+ * belongs to a TCP-style listening socket that is not
+ * yet accepted.
+ */
+ if ((SCTP_SOCKET_TCP == sctp_sk(sk)->type) &&
+ (SCTP_STATE_CLOSED == asoc->state)) {
+ sctp_unhash_established(asoc);
+ sctp_association_free(asoc);
+ } else
+ sctp_primitive_SHUTDOWN(asoc, NULL);
}
/* Clean up any skbs sitting on the receive queue. */
@@ -718,6 +728,16 @@ SCTP_STATIC void sctp_close(struct sock *sk, long timeout)
SCTP_DBG_OBJCNT_DEC(sock);
}
+/* Handle EPIPE error. */
+static int sctp_error(struct sock *sk, int flags, int err)
+{
+ if (err == -EPIPE)
+ err = sock_error(sk) ? : -EPIPE;
+ if (err == -EPIPE && !(flags & MSG_NOSIGNAL))
+ send_sig(SIGPIPE, current, 0);
+ return err;
+}
+
/* API 3.1.3 sendmsg() - UDP Style Syntax
*
* An application uses sendmsg() and recvmsg() calls to transmit data to
@@ -763,6 +783,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
long timeo;
__u16 sinfo_flags = 0;
struct sk_buff_head chunks;
+ int msg_flags = msg->msg_flags;
SCTP_DEBUG_PRINTK("sctp_sendmsg(sk: %p, msg: %p, msg_len: %d)\n",
sk, msg, msg_len);
@@ -773,6 +794,12 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
SCTP_DEBUG_PRINTK("Using endpoint: %s.\n", ep->debug_name);
+ if ((SCTP_SOCKET_TCP == sp->type) &&
+ (SCTP_SS_ESTABLISHED != sk->state)) {
+ err = -EPIPE;
+ goto out_nounlock;
+ }
+
/* Parse out the SCTP CMSGs. */
err = sctp_msghdr_parse(msg, &cmsgs);
@@ -859,6 +886,18 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
if (asoc) {
SCTP_DEBUG_PRINTK("Just looked up association: "
"%s. \n", asoc->debug_name);
+
+ /* We cannot send a message on a TCP-style SCTP_SS_ESTABLISHED
+ * socket that has an association in CLOSED state. This can
+ * happen when an accepted socket has an association that is
+ * already CLOSED.
+ */
+ if ((SCTP_STATE_CLOSED == asoc->state) &&
+ (SCTP_SOCKET_TCP == sp->type)) {
+ err = -EPIPE;
+ goto out_unlock;
+ }
+
if (sinfo_flags & MSG_EOF) {
SCTP_DEBUG_PRINTK("Shutting down association: %p\n",
asoc);
@@ -1067,7 +1106,7 @@ out_unlock:
sctp_release_sock(sk);
out_nounlock:
- return err;
+ return sctp_error(sk, msg_flags, err);
#if 0
do_sock_err: