Author: tuexen
Date: Sun Jun 26 16:38:42 2016
New Revision: 302212
URL: https://svnweb.freebsd.org/changeset/base/302212

Log:
  This patch fixes two bugs related to the SCTP message recovery
  for messages which have been put on the send queue:
  * Do not report any DATA or I-DATA chunk padding.
  * Correctly deal with the I-DATA chunk header instead of the DATA
    chunk header when the I-DATA extension is used.
  
  Approved by:  re (kib)
  MFC after:    1 week

Modified:
  head/sys/netinet/sctputil.c

Modified: head/sys/netinet/sctputil.c
==============================================================================
--- head/sys/netinet/sctputil.c Sun Jun 26 14:44:01 2016        (r302211)
+++ head/sys/netinet/sctputil.c Sun Jun 26 16:38:42 2016        (r302212)
@@ -2933,7 +2933,8 @@ sctp_notify_send_failed(struct sctp_tcb 
        struct sctp_send_failed *ssf;
        struct sctp_send_failed_event *ssfe;
        struct sctp_queued_to_read *control;
-       int length;
+       struct sctp_chunkhdr *chkhdr;
+       int notifhdr_len, chk_len, chkhdr_len, padding_len, payload_len;
 
        if ((stcb == NULL) ||
            (sctp_stcb_is_feature_off(stcb->sctp_ep, stcb, 
SCTP_PCB_FLAGS_RECVSENDFAILEVNT) &&
@@ -2942,27 +2943,49 @@ sctp_notify_send_failed(struct sctp_tcb 
                return;
        }
        if (sctp_stcb_is_feature_on(stcb->sctp_ep, stcb, 
SCTP_PCB_FLAGS_RECVNSENDFAILEVNT)) {
-               length = sizeof(struct sctp_send_failed_event);
+               notifhdr_len = sizeof(struct sctp_send_failed_event);
        } else {
-               length = sizeof(struct sctp_send_failed);
+               notifhdr_len = sizeof(struct sctp_send_failed);
        }
-       m_notify = sctp_get_mbuf_for_msg(length, 0, M_NOWAIT, 1, MT_DATA);
+       m_notify = sctp_get_mbuf_for_msg(notifhdr_len, 0, M_NOWAIT, 1, MT_DATA);
        if (m_notify == NULL)
                /* no space left */
                return;
-       SCTP_BUF_LEN(m_notify) = 0;
+       SCTP_BUF_LEN(m_notify) = notifhdr_len;
+       if (stcb->asoc.idata_supported) {
+               chkhdr_len = sizeof(struct sctp_idata_chunk);
+       } else {
+               chkhdr_len = sizeof(struct sctp_data_chunk);
+       }
+       /* Use some defaults in case we can't access the chunk header */
+       if (chk->send_size >= chkhdr_len) {
+               payload_len = chk->send_size - chkhdr_len;
+       } else {
+               payload_len = 0;
+       }
+       padding_len = 0;
+       if (chk->data != NULL) {
+               chkhdr = mtod(chk->data, struct sctp_chunkhdr *);
+               if (chkhdr != NULL) {
+                       chk_len = ntohs(chkhdr->chunk_length);
+                       if ((chk_len >= chkhdr_len) &&
+                           (chk->send_size >= chk_len) &&
+                           (chk->send_size - chk_len < 4)) {
+                               padding_len = chk->send_size - chk_len;
+                               payload_len = chk->send_size - chkhdr_len - 
padding_len;
+                       }
+               }
+       }
        if (sctp_stcb_is_feature_on(stcb->sctp_ep, stcb, 
SCTP_PCB_FLAGS_RECVNSENDFAILEVNT)) {
                ssfe = mtod(m_notify, struct sctp_send_failed_event *);
-               memset(ssfe, 0, length);
+               memset(ssfe, 0, notifhdr_len);
                ssfe->ssfe_type = SCTP_SEND_FAILED_EVENT;
                if (sent) {
                        ssfe->ssfe_flags = SCTP_DATA_SENT;
                } else {
                        ssfe->ssfe_flags = SCTP_DATA_UNSENT;
                }
-               length += chk->send_size;
-               length -= sizeof(struct sctp_data_chunk);
-               ssfe->ssfe_length = length;
+               ssfe->ssfe_length = (uint32_t) (notifhdr_len + payload_len);
                ssfe->ssfe_error = error;
                /* not exactly what the user sent in, but should be close :) */
                ssfe->ssfe_info.snd_sid = chk->rec.data.stream_number;
@@ -2971,39 +2994,33 @@ sctp_notify_send_failed(struct sctp_tcb 
                ssfe->ssfe_info.snd_context = chk->rec.data.context;
                ssfe->ssfe_info.snd_assoc_id = sctp_get_associd(stcb);
                ssfe->ssfe_assoc_id = sctp_get_associd(stcb);
-               SCTP_BUF_LEN(m_notify) = sizeof(struct sctp_send_failed_event);
        } else {
                ssf = mtod(m_notify, struct sctp_send_failed *);
-               memset(ssf, 0, length);
+               memset(ssf, 0, notifhdr_len);
                ssf->ssf_type = SCTP_SEND_FAILED;
                if (sent) {
                        ssf->ssf_flags = SCTP_DATA_SENT;
                } else {
                        ssf->ssf_flags = SCTP_DATA_UNSENT;
                }
-               length += chk->send_size;
-               length -= sizeof(struct sctp_data_chunk);
-               ssf->ssf_length = length;
+               ssf->ssf_length = (uint32_t) (notifhdr_len + payload_len);
                ssf->ssf_error = error;
                /* not exactly what the user sent in, but should be close :) */
-               bzero(&ssf->ssf_info, sizeof(ssf->ssf_info));
                ssf->ssf_info.sinfo_stream = chk->rec.data.stream_number;
-               ssf->ssf_info.sinfo_ssn = chk->rec.data.stream_seq;
+               ssf->ssf_info.sinfo_ssn = (uint16_t) chk->rec.data.stream_seq;
                ssf->ssf_info.sinfo_flags = chk->rec.data.rcv_flags;
                ssf->ssf_info.sinfo_ppid = chk->rec.data.payloadtype;
                ssf->ssf_info.sinfo_context = chk->rec.data.context;
                ssf->ssf_info.sinfo_assoc_id = sctp_get_associd(stcb);
                ssf->ssf_assoc_id = sctp_get_associd(stcb);
-               SCTP_BUF_LEN(m_notify) = sizeof(struct sctp_send_failed);
        }
-       if (chk->data) {
-               /*
-                * trim off the sctp chunk header(it should be there)
-                */
-               if (chk->send_size >= sizeof(struct sctp_data_chunk)) {
-                       m_adj(chk->data, sizeof(struct sctp_data_chunk));
+       if (chk->data != NULL) {
+               /* Trim off the sctp chunk header (it should be there) */
+               if (chk->send_size == chkhdr_len + payload_len + padding_len) {
+                       m_adj(chk->data, chkhdr_len);
+                       m_adj(chk->data, -padding_len);
                        sctp_mbuf_crush(chk->data);
-                       chk->send_size -= sizeof(struct sctp_data_chunk);
+                       chk->send_size -= (chkhdr_len + padding_len);
                }
        }
        SCTP_BUF_NEXT(m_notify) = chk->data;
@@ -3048,7 +3065,7 @@ sctp_notify_send_failed2(struct sctp_tcb
        struct sctp_send_failed *ssf;
        struct sctp_send_failed_event *ssfe;
        struct sctp_queued_to_read *control;
-       int length;
+       int notifhdr_len;
 
        if ((stcb == NULL) ||
            (sctp_stcb_is_feature_off(stcb->sctp_ep, stcb, 
SCTP_PCB_FLAGS_RECVSENDFAILEVNT) &&
@@ -3057,23 +3074,22 @@ sctp_notify_send_failed2(struct sctp_tcb
                return;
        }
        if (sctp_stcb_is_feature_on(stcb->sctp_ep, stcb, 
SCTP_PCB_FLAGS_RECVNSENDFAILEVNT)) {
-               length = sizeof(struct sctp_send_failed_event);
+               notifhdr_len = sizeof(struct sctp_send_failed_event);
        } else {
-               length = sizeof(struct sctp_send_failed);
+               notifhdr_len = sizeof(struct sctp_send_failed);
        }
-       m_notify = sctp_get_mbuf_for_msg(length, 0, M_NOWAIT, 1, MT_DATA);
+       m_notify = sctp_get_mbuf_for_msg(notifhdr_len, 0, M_NOWAIT, 1, MT_DATA);
        if (m_notify == NULL) {
                /* no space left */
                return;
        }
-       SCTP_BUF_LEN(m_notify) = 0;
+       SCTP_BUF_LEN(m_notify) = notifhdr_len;
        if (sctp_stcb_is_feature_on(stcb->sctp_ep, stcb, 
SCTP_PCB_FLAGS_RECVNSENDFAILEVNT)) {
                ssfe = mtod(m_notify, struct sctp_send_failed_event *);
-               memset(ssfe, 0, length);
+               memset(ssfe, 0, notifhdr_len);
                ssfe->ssfe_type = SCTP_SEND_FAILED_EVENT;
                ssfe->ssfe_flags = SCTP_DATA_UNSENT;
-               length += sp->length;
-               ssfe->ssfe_length = length;
+               ssfe->ssfe_length = (uint32_t) (notifhdr_len + sp->length);
                ssfe->ssfe_error = error;
                /* not exactly what the user sent in, but should be close :) */
                ssfe->ssfe_info.snd_sid = sp->stream;
@@ -3086,14 +3102,12 @@ sctp_notify_send_failed2(struct sctp_tcb
                ssfe->ssfe_info.snd_context = sp->context;
                ssfe->ssfe_info.snd_assoc_id = sctp_get_associd(stcb);
                ssfe->ssfe_assoc_id = sctp_get_associd(stcb);
-               SCTP_BUF_LEN(m_notify) = sizeof(struct sctp_send_failed_event);
        } else {
                ssf = mtod(m_notify, struct sctp_send_failed *);
-               memset(ssf, 0, length);
+               memset(ssf, 0, notifhdr_len);
                ssf->ssf_type = SCTP_SEND_FAILED;
                ssf->ssf_flags = SCTP_DATA_UNSENT;
-               length += sp->length;
-               ssf->ssf_length = length;
+               ssf->ssf_length = (uint32_t) (notifhdr_len + sp->length);
                ssf->ssf_error = error;
                /* not exactly what the user sent in, but should be close :) */
                ssf->ssf_info.sinfo_stream = sp->stream;
@@ -3107,7 +3121,6 @@ sctp_notify_send_failed2(struct sctp_tcb
                ssf->ssf_info.sinfo_context = sp->context;
                ssf->ssf_info.sinfo_assoc_id = sctp_get_associd(stcb);
                ssf->ssf_assoc_id = sctp_get_associd(stcb);
-               SCTP_BUF_LEN(m_notify) = sizeof(struct sctp_send_failed);
        }
        SCTP_BUF_NEXT(m_notify) = sp->data;
 
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to