sctp: implement event notification SCTP_SENDER_DRY_EVENT
[cascardo/linux.git] / net / sctp / sm_statefuns.c
index 7679208..7f4a4f8 100644 (file)
@@ -393,8 +393,7 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const struct sctp_endpoint *ep,
                goto nomem_init;
 
        /* The call, sctp_process_init(), can fail on memory allocation.  */
-       if (!sctp_process_init(new_asoc, chunk->chunk_hdr->type,
-                              sctp_source(chunk),
+       if (!sctp_process_init(new_asoc, chunk, sctp_source(chunk),
                               (sctp_init_chunk_t *)chunk->chunk_hdr,
                               GFP_ATOMIC))
                goto nomem_init;
@@ -725,7 +724,7 @@ sctp_disposition_t sctp_sf_do_5_1D_ce(const struct sctp_endpoint *ep,
         */
        peer_init = &chunk->subh.cookie_hdr->c.peer_init[0];
 
-       if (!sctp_process_init(new_asoc, chunk->chunk_hdr->type,
+       if (!sctp_process_init(new_asoc, chunk,
                               &chunk->subh.cookie_hdr->c.peer_addr,
                               peer_init, GFP_ATOMIC))
                goto nomem_init;
@@ -942,18 +941,9 @@ static sctp_disposition_t sctp_sf_heartbeat(const struct sctp_endpoint *ep,
 {
        struct sctp_transport *transport = (struct sctp_transport *) arg;
        struct sctp_chunk *reply;
-       sctp_sender_hb_info_t hbinfo;
-       size_t paylen = 0;
-
-       hbinfo.param_hdr.type = SCTP_PARAM_HEARTBEAT_INFO;
-       hbinfo.param_hdr.length = htons(sizeof(sctp_sender_hb_info_t));
-       hbinfo.daddr = transport->ipaddr;
-       hbinfo.sent_at = jiffies;
-       hbinfo.hb_nonce = transport->hb_nonce;
 
        /* Send a heartbeat to our peer.  */
-       paylen = sizeof(sctp_sender_hb_info_t);
-       reply = sctp_make_heartbeat(asoc, transport, &hbinfo, paylen);
+       reply = sctp_make_heartbeat(asoc, transport);
        if (!reply)
                return SCTP_DISPOSITION_NOMEM;
 
@@ -1464,8 +1454,7 @@ static sctp_disposition_t sctp_sf_do_unexpected_init(
         * Verification Tag and Peers Verification tag into a reserved
         * place (local tie-tag and per tie-tag) within the state cookie.
         */
-       if (!sctp_process_init(new_asoc, chunk->chunk_hdr->type,
-                              sctp_source(chunk),
+       if (!sctp_process_init(new_asoc, chunk, sctp_source(chunk),
                               (sctp_init_chunk_t *)chunk->chunk_hdr,
                               GFP_ATOMIC))
                goto nomem;
@@ -1694,8 +1683,7 @@ static sctp_disposition_t sctp_sf_do_dupcook_a(const struct sctp_endpoint *ep,
         */
        peer_init = &chunk->subh.cookie_hdr->c.peer_init[0];
 
-       if (!sctp_process_init(new_asoc, chunk->chunk_hdr->type,
-                              sctp_source(chunk), peer_init,
+       if (!sctp_process_init(new_asoc, chunk, sctp_source(chunk), peer_init,
                               GFP_ATOMIC))
                goto nomem;
 
@@ -1780,8 +1768,7 @@ static sctp_disposition_t sctp_sf_do_dupcook_b(const struct sctp_endpoint *ep,
         * side effects--it is safe to run them here.
         */
        peer_init = &chunk->subh.cookie_hdr->c.peer_init[0];
-       if (!sctp_process_init(new_asoc, chunk->chunk_hdr->type,
-                              sctp_source(chunk), peer_init,
+       if (!sctp_process_init(new_asoc, chunk, sctp_source(chunk), peer_init,
                               GFP_ATOMIC))
                goto nomem;
 
@@ -2412,8 +2399,15 @@ static sctp_disposition_t __sctp_sf_do_9_1_abort(const struct sctp_endpoint *ep,
 
        /* See if we have an error cause code in the chunk.  */
        len = ntohs(chunk->chunk_hdr->length);
-       if (len >= sizeof(struct sctp_chunkhdr) + sizeof(struct sctp_errhdr))
+       if (len >= sizeof(struct sctp_chunkhdr) + sizeof(struct sctp_errhdr)) {
+
+               sctp_errhdr_t *err;
+               sctp_walk_errors(err, chunk->chunk_hdr);
+               if ((void *)err != (void *)chunk->chunk_end)
+                       return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+
                error = ((sctp_errhdr_t *)chunk->skb->data)->cause;
+       }
 
        sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR, SCTP_ERROR(ECONNRESET));
        /* ASSOC_FAILED will DELETE_TCB. */
@@ -3204,6 +3198,7 @@ sctp_disposition_t sctp_sf_operr_notify(const struct sctp_endpoint *ep,
                                        sctp_cmd_seq_t *commands)
 {
        struct sctp_chunk *chunk = arg;
+       sctp_errhdr_t *err;
 
        if (!sctp_vtag_verify(chunk, asoc))
                return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
@@ -3212,6 +3207,10 @@ sctp_disposition_t sctp_sf_operr_notify(const struct sctp_endpoint *ep,
        if (!sctp_chunk_length_valid(chunk, sizeof(sctp_operr_chunk_t)))
                return sctp_sf_violation_chunklen(ep, asoc, type, arg,
                                                  commands);
+       sctp_walk_errors(err, chunk->chunk_hdr);
+       if ((void *)err != (void *)chunk->chunk_end)
+               return sctp_sf_violation_paramlen(ep, asoc, type, arg,
+                                                 (void *)err, commands);
 
        sctp_add_cmd_sf(commands, SCTP_CMD_PROCESS_OPERR,
                        SCTP_CHUNK(chunk));
@@ -3320,8 +3319,10 @@ sctp_disposition_t sctp_sf_ootb(const struct sctp_endpoint *ep,
        struct sctp_chunk *chunk = arg;
        struct sk_buff *skb = chunk->skb;
        sctp_chunkhdr_t *ch;
+       sctp_errhdr_t *err;
        __u8 *ch_end;
        int ootb_shut_ack = 0;
+       int ootb_cookie_ack = 0;
 
        SCTP_INC_STATS(SCTP_MIB_OUTOFBLUES);
 
@@ -3346,6 +3347,23 @@ sctp_disposition_t sctp_sf_ootb(const struct sctp_endpoint *ep,
                if (SCTP_CID_ABORT == ch->type)
                        return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
 
+               /* RFC 8.4, 7) If the packet contains a "Stale cookie" ERROR
+                * or a COOKIE ACK the SCTP Packet should be silently
+                * discarded.
+                */
+
+               if (SCTP_CID_COOKIE_ACK == ch->type)
+                       ootb_cookie_ack = 1;
+
+               if (SCTP_CID_ERROR == ch->type) {
+                       sctp_walk_errors(err, ch) {
+                               if (SCTP_ERROR_STALE_COOKIE == err->cause) {
+                                       ootb_cookie_ack = 1;
+                                       break;
+                               }
+                       }
+               }
+
                /* Report violation if chunk len overflows */
                ch_end = ((__u8 *)ch) + WORD_ROUND(ntohs(ch->length));
                if (ch_end > skb_tail_pointer(skb))
@@ -3357,6 +3375,8 @@ sctp_disposition_t sctp_sf_ootb(const struct sctp_endpoint *ep,
 
        if (ootb_shut_ack)
                return sctp_sf_shut_8_4_5(ep, asoc, type, arg, commands);
+       else if (ootb_cookie_ack)
+               return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
        else
                return sctp_sf_tabort_8_4_8(ep, asoc, type, arg, commands);
 }
@@ -4343,8 +4363,9 @@ static sctp_disposition_t sctp_sf_violation_chunklen(
 
 /*
  * Handle a protocol violation when the parameter length is invalid.
- * "Invalid" length is identified as smaller than the minimal length a
- * given parameter can be.
+ * If the length is smaller than the minimum length of a given parameter,
+ * or accumulated length in multi parameters exceeds the end of the chunk,
+ * the length is considered as invalid.
  */
 static sctp_disposition_t sctp_sf_violation_paramlen(
                                     const struct sctp_endpoint *ep,
@@ -5055,6 +5076,30 @@ sctp_disposition_t sctp_sf_ignore_primitive(
  * These are the state functions for the OTHER events.
  ***************************************************************************/
 
+/*
+ * When the SCTP stack has no more user data to send or retransmit, this
+ * notification is given to the user. Also, at the time when a user app
+ * subscribes to this event, if there is no data to be sent or
+ * retransmit, the stack will immediately send up this notification.
+ */
+sctp_disposition_t sctp_sf_do_no_pending_tsn(
+       const struct sctp_endpoint *ep,
+       const struct sctp_association *asoc,
+       const sctp_subtype_t type,
+       void *arg,
+       sctp_cmd_seq_t *commands)
+{
+       struct sctp_ulpevent *event;
+
+       event = sctp_ulpevent_make_sender_dry_event(asoc, GFP_ATOMIC);
+       if (!event)
+               return SCTP_DISPOSITION_NOMEM;
+
+       sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, SCTP_ULPEVENT(event));
+
+       return SCTP_DISPOSITION_CONSUME;
+}
+
 /*
  * Start the shutdown negotiation.
  *