]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - net/sctp/sm_statefuns.c
SCTP: Send ABORT chunk with correct tag in response to INIT ACK
[mv-sheeva.git] / net / sctp / sm_statefuns.c
index fd2dfdd7d7fd056ff06ea7422ee90186fc1536aa..385f1757158a3272c50ce20099563b5d78055e9e 100644 (file)
@@ -97,6 +97,13 @@ static sctp_disposition_t sctp_stop_t1_and_abort(sctp_cmd_seq_t *commands,
                                           const struct sctp_association *asoc,
                                           struct sctp_transport *transport);
 
+static sctp_disposition_t sctp_sf_abort_violation(
+                                    const struct sctp_association *asoc,
+                                    void *arg,
+                                    sctp_cmd_seq_t *commands,
+                                    const __u8 *payload,
+                                    const size_t paylen);
+
 static sctp_disposition_t sctp_sf_violation_chunklen(
                                     const struct sctp_endpoint *ep,
                                     const struct sctp_association *asoc,
@@ -104,6 +111,13 @@ static sctp_disposition_t sctp_sf_violation_chunklen(
                                     void *arg,
                                     sctp_cmd_seq_t *commands);
 
+static sctp_disposition_t sctp_sf_violation_ctsn(
+                                    const struct sctp_endpoint *ep,
+                                    const struct sctp_association *asoc,
+                                    const sctp_subtype_t type,
+                                    void *arg,
+                                    sctp_cmd_seq_t *commands);
+
 /* Small helper function that checks if the chunk length
  * is of the appropriate length.  The 'required_length' argument
  * is set to be the size of a specific chunk we are testing.
@@ -250,7 +264,6 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const struct sctp_endpoint *ep,
        struct sctp_chunk *err_chunk;
        struct sctp_packet *packet;
        sctp_unrecognized_param_t *unk_param;
-       struct sock *sk;
        int len;
 
        /* 6.10 Bundling
@@ -271,16 +284,6 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const struct sctp_endpoint *ep,
        if (ep == sctp_sk((sctp_get_ctl_sock()))->ep)
                return sctp_sf_tabort_8_4_8(ep, asoc, type, arg, commands);
 
-       sk = ep->base.sk;
-       /* If the endpoint is not listening or if the number of associations
-        * on the TCP-style socket exceed the max backlog, respond with an
-        * ABORT.
-        */
-       if (!sctp_sstate(sk, LISTENING) ||
-           (sctp_style(sk, TCP) &&
-            sk_acceptq_is_full(sk)))
-               return sctp_sf_tabort_8_4_8(ep, asoc, type, arg, commands);
-
        /* 3.1 A packet containing an INIT chunk MUST have a zero Verification
         * Tag.
         */
@@ -576,6 +579,7 @@ sctp_disposition_t sctp_sf_do_5_1D_ce(const struct sctp_endpoint *ep,
        struct sctp_ulpevent *ev, *ai_ev = NULL;
        int error = 0;
        struct sctp_chunk *err_chk_p;
+       struct sock *sk;
 
        /* If the packet is an OOTB packet which is temporarily on the
         * control endpoint, respond with an ABORT.
@@ -591,6 +595,15 @@ sctp_disposition_t sctp_sf_do_5_1D_ce(const struct sctp_endpoint *ep,
        if (!sctp_chunk_length_valid(chunk, sizeof(sctp_chunkhdr_t)))
                return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
 
+       /* If the endpoint is not listening or if the number of associations
+        * on the TCP-style socket exceed the max backlog, respond with an
+        * ABORT.
+        */
+       sk = ep->base.sk;
+       if (!sctp_sstate(sk, LISTENING) ||
+           (sctp_style(sk, TCP) && sk_acceptq_is_full(sk)))
+               return sctp_sf_tabort_8_4_8(ep, asoc, type, arg, commands);
+
        /* "Decode" the chunk.  We have no optional parameters so we
         * are in good shape.
         */
@@ -1018,19 +1031,21 @@ sctp_disposition_t sctp_sf_backbeat_8_3(const struct sctp_endpoint *ep,
        /* This should never happen, but lets log it if so.  */
        if (unlikely(!link)) {
                if (from_addr.sa.sa_family == AF_INET6) {
-                       printk(KERN_WARNING
-                              "%s association %p could not find address "
-                              NIP6_FMT "\n",
-                              __FUNCTION__,
-                              asoc,
-                              NIP6(from_addr.v6.sin6_addr));
+                       if (net_ratelimit())
+                               printk(KERN_WARNING
+                                   "%s association %p could not find address "
+                                   NIP6_FMT "\n",
+                                   __FUNCTION__,
+                                   asoc,
+                                   NIP6(from_addr.v6.sin6_addr));
                } else {
-                       printk(KERN_WARNING
-                              "%s association %p could not find address "
-                              NIPQUAD_FMT "\n",
-                              __FUNCTION__,
-                              asoc,
-                              NIPQUAD(from_addr.v4.sin_addr.s_addr));
+                       if (net_ratelimit())
+                               printk(KERN_WARNING
+                                   "%s association %p could not find address "
+                                   NIPQUAD_FMT "\n",
+                                   __FUNCTION__,
+                                   asoc,
+                                   NIPQUAD(from_addr.v4.sin_addr.s_addr));
                }
                return SCTP_DISPOSITION_DISCARD;
        }
@@ -2880,6 +2895,13 @@ sctp_disposition_t sctp_sf_eat_sack_6_2(const struct sctp_endpoint *ep,
                return SCTP_DISPOSITION_DISCARD;
        }
 
+       /* If Cumulative TSN Ack beyond the max tsn currently
+        * send, terminating the association and respond to the
+        * sender with an ABORT.
+        */
+       if (!TSN_lt(ctsn, asoc->next_tsn))
+               return sctp_sf_violation_ctsn(ep, asoc, type, arg, commands);
+
        /* Return this SACK for further processing.  */
        sctp_add_cmd_sf(commands, SCTP_CMD_PROCESS_SACK, SCTP_SACKH(sackh));
 
@@ -3341,7 +3363,7 @@ sctp_disposition_t sctp_sf_do_asconf_ack(const struct sctp_endpoint *ep,
                abort = sctp_make_abort(asoc, asconf_ack,
                                        sizeof(sctp_errhdr_t));
                if (abort) {
-                       sctp_init_cause(abort, SCTP_ERROR_ASCONF_ACK, NULL, 0);
+                       sctp_init_cause(abort, SCTP_ERROR_ASCONF_ACK, 0);
                        sctp_add_cmd_sf(commands, SCTP_CMD_REPLY,
                                        SCTP_CHUNK(abort));
                }
@@ -3371,7 +3393,7 @@ sctp_disposition_t sctp_sf_do_asconf_ack(const struct sctp_endpoint *ep,
                abort = sctp_make_abort(asoc, asconf_ack,
                                        sizeof(sctp_errhdr_t));
                if (abort) {
-                       sctp_init_cause(abort, SCTP_ERROR_RSRC_LOW, NULL, 0);
+                       sctp_init_cause(abort, SCTP_ERROR_RSRC_LOW, 0);
                        sctp_add_cmd_sf(commands, SCTP_CMD_REPLY,
                                        SCTP_CHUNK(abort));
                }
@@ -3691,40 +3713,21 @@ sctp_disposition_t sctp_sf_violation(const struct sctp_endpoint *ep,
        return SCTP_DISPOSITION_VIOLATION;
 }
 
-
 /*
- * Handle a protocol violation when the chunk length is invalid.
- * "Invalid" length is identified as smaller then the minimal length a
- * given chunk can be.  For example, a SACK chunk has invalid length
- * if it's length is set to be smaller then the size of sctp_sack_chunk_t.
- *
- * We inform the other end by sending an ABORT with a Protocol Violation
- * error code.
- *
- * Section: Not specified
- * Verification Tag:  Nothing to do
- * Inputs
- * (endpoint, asoc, chunk)
- *
- * Outputs
- * (reply_msg, msg_up, counters)
- *
- * Generate an  ABORT chunk and terminate the association.
+ * Common function to handle a protocol violation.
  */
-static sctp_disposition_t sctp_sf_violation_chunklen(
-                                    const struct sctp_endpoint *ep,
+static sctp_disposition_t sctp_sf_abort_violation(
                                     const struct sctp_association *asoc,
-                                    const sctp_subtype_t type,
                                     void *arg,
-                                    sctp_cmd_seq_t *commands)
+                                    sctp_cmd_seq_t *commands,
+                                    const __u8 *payload,
+                                    const size_t paylen)
 {
        struct sctp_chunk *chunk =  arg;
        struct sctp_chunk *abort = NULL;
-       char               err_str[]="The following chunk had invalid length:";
 
        /* Make the abort chunk. */
-       abort = sctp_make_abort_violation(asoc, chunk, err_str,
-                                         sizeof(err_str));
+       abort = sctp_make_abort_violation(asoc, chunk, payload, paylen);
        if (!abort)
                goto nomem;
 
@@ -3756,6 +3759,57 @@ nomem:
        return SCTP_DISPOSITION_NOMEM;
 }
 
+/*
+ * Handle a protocol violation when the chunk length is invalid.
+ * "Invalid" length is identified as smaller then the minimal length a
+ * given chunk can be.  For example, a SACK chunk has invalid length
+ * if it's length is set to be smaller then the size of sctp_sack_chunk_t.
+ *
+ * We inform the other end by sending an ABORT with a Protocol Violation
+ * error code.
+ *
+ * Section: Not specified
+ * Verification Tag:  Nothing to do
+ * Inputs
+ * (endpoint, asoc, chunk)
+ *
+ * Outputs
+ * (reply_msg, msg_up, counters)
+ *
+ * Generate an  ABORT chunk and terminate the association.
+ */
+static sctp_disposition_t sctp_sf_violation_chunklen(
+                                    const struct sctp_endpoint *ep,
+                                    const struct sctp_association *asoc,
+                                    const sctp_subtype_t type,
+                                    void *arg,
+                                    sctp_cmd_seq_t *commands)
+{
+       char err_str[]="The following chunk had invalid length:";
+
+       return sctp_sf_abort_violation(asoc, arg, commands, err_str,
+                                       sizeof(err_str));
+}
+
+/* Handle a protocol violation when the peer trying to advance the
+ * cumulative tsn ack to a point beyond the max tsn currently sent.
+ *
+ * We inform the other end by sending an ABORT with a Protocol Violation
+ * error code.
+ */
+static sctp_disposition_t sctp_sf_violation_ctsn(
+                                    const struct sctp_endpoint *ep,
+                                    const struct sctp_association *asoc,
+                                    const sctp_subtype_t type,
+                                    void *arg,
+                                    sctp_cmd_seq_t *commands)
+{
+       char err_str[]="The cumulative tsn ack beyond the max tsn currently sent:";
+
+       return sctp_sf_abort_violation(asoc, arg, commands, err_str,
+                                       sizeof(err_str));
+}
+
 /***************************************************************************
  * These are the state functions for handling primitive (Section 10) events.
  ***************************************************************************/
@@ -5122,7 +5176,22 @@ static struct sctp_packet *sctp_ootb_pkt_new(const struct sctp_association *asoc
         * association exists, otherwise, use the peer's vtag.
         */
        if (asoc) {
-               vtag = asoc->peer.i.init_tag;
+               /* Special case the INIT-ACK as there is no peer's vtag
+                * yet.
+                */
+               switch(chunk->chunk_hdr->type) {
+               case SCTP_CID_INIT_ACK:
+               {
+                       sctp_initack_chunk_t *initack;
+
+                       initack = (sctp_initack_chunk_t *)chunk->chunk_hdr;
+                       vtag = ntohl(initack->init_hdr.init_tag);
+                       break;
+               }
+               default:
+                       vtag = asoc->peer.i.init_tag;
+                       break;
+               }
        } else {
                /* Special case the INIT and stale COOKIE_ECHO as there is no
                 * vtag yet.