]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
sctp: implement assign_number for sctp_stream_interleave
authorXin Long <lucien.xin@gmail.com>
Fri, 8 Dec 2017 13:04:02 +0000 (21:04 +0800)
committerDavid S. Miller <davem@davemloft.net>
Mon, 11 Dec 2017 16:23:04 +0000 (11:23 -0500)
assign_number is added as a member of sctp_stream_interleave, used
to assign ssn for data or mid (message id) for idata, called in
sctp_packet_append_data. sctp_chunk_assign_ssn is left as it is,
and sctp_chunk_assign_mid is added for sctp_stream_interleave_1.

This procedure is described in section 2.2.2 of RFC8260.

All sizeof(struct sctp_data_chunk) in tx path is replaced with
sctp_datachk_len, to make it right for idata as well. And also
adjust sctp_chunk_is_data for SCTP_CID_I_DATA.

After this patch, idata can be built and sent in tx path.

Note that if sp strm_interleave is set, it has to wait_connect in
sctp_sendmsg, as asoc intl_enable need to be known after 4 shake-
hands, to decide if it should use data or idata later. data and
idata can't be mixed to send in one asoc.

Signed-off-by: Xin Long <lucien.xin@gmail.com>
Acked-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
Acked-by: Neil Horman <nhorman@tuxdriver.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/sctp/constants.h
include/net/sctp/sctp.h
include/net/sctp/sm.h
include/net/sctp/stream_interleave.h
include/net/sctp/structs.h
net/sctp/output.c
net/sctp/socket.c
net/sctp/stream_interleave.c
net/sctp/ulpevent.c

index deaafa9b09cbea18a438070c0579ababf73dcfba..20ff237c5eb2ff358589b30bbd89d636e41b2d92 100644 (file)
@@ -145,12 +145,13 @@ SCTP_SUBTYPE_CONSTRUCTOR(OTHER,           enum sctp_event_other,  other)
 SCTP_SUBTYPE_CONSTRUCTOR(PRIMITIVE,    enum sctp_event_primitive, primitive)
 
 
-#define sctp_chunk_is_data(a) (a->chunk_hdr->type == SCTP_CID_DATA)
+#define sctp_chunk_is_data(a) (a->chunk_hdr->type == SCTP_CID_DATA || \
+                              a->chunk_hdr->type == SCTP_CID_I_DATA)
 
 /* Calculate the actual data size in a data chunk */
-#define SCTP_DATA_SNDSIZE(c) ((int)((unsigned long)(c->chunk_end)\
-                               - (unsigned long)(c->chunk_hdr)\
-                               - sizeof(struct sctp_data_chunk)))
+#define SCTP_DATA_SNDSIZE(c) ((int)((unsigned long)(c->chunk_end) - \
+                                   (unsigned long)(c->chunk_hdr) - \
+                                   sctp_datachk_len(&c->asoc->stream)))
 
 /* Internal error codes */
 enum sctp_ierror {
index 6f79415f66341932b3376032a9822ec8db8b7ceb..20c0c1be2ca7a50ac4940bd3b9920eed86a69aa7 100644 (file)
@@ -444,13 +444,13 @@ static inline int sctp_frag_point(const struct sctp_association *asoc, int pmtu)
        int frag = pmtu;
 
        frag -= sp->pf->af->net_header_len;
-       frag -= sizeof(struct sctphdr) + sizeof(struct sctp_data_chunk);
+       frag -= sizeof(struct sctphdr) + sctp_datachk_len(&asoc->stream);
 
        if (asoc->user_frag)
                frag = min_t(int, frag, asoc->user_frag);
 
        frag = SCTP_TRUNC4(min_t(int, frag, SCTP_MAX_CHUNK_LEN -
-                                           sizeof(struct sctp_data_chunk)));
+                                           sctp_datachk_len(&asoc->stream)));
 
        return frag;
 }
index f950186aae34d48ee857cb11f090e35c87bde658..ca1db8997e5d8cff6e3729f2eea90e2a487b415e 100644 (file)
@@ -343,7 +343,7 @@ static inline __u16 sctp_data_size(struct sctp_chunk *chunk)
        __u16 size;
 
        size = ntohs(chunk->chunk_hdr->length);
-       size -= sizeof(struct sctp_data_chunk);
+       size -= sctp_datahdr_len(&chunk->asoc->stream);
 
        return size;
 }
index 7b9fa8dbe6206be6a70b05cb047955dc4204fdf1..99f399e7a8710dcd2a450e03da204bdced7fe133 100644 (file)
@@ -37,6 +37,7 @@ struct sctp_stream_interleave {
        struct sctp_chunk *(*make_datafrag)(const struct sctp_association *asoc,
                                            const struct sctp_sndrcvinfo *sinfo,
                                            int len, __u8 flags, gfp_t gfp);
+       void    (*assign_number)(struct sctp_chunk *chunk);
 };
 
 void sctp_stream_interleave_init(struct sctp_stream *stream);
index 96cc898787f16495ca61f32e58cd50d557cecd6d..fd93973bb66767f60e431590fefd2e1fc18c7d48 100644 (file)
@@ -399,6 +399,18 @@ void sctp_stream_update(struct sctp_stream *stream, struct sctp_stream *new);
 #define sctp_ssn_skip(stream, type, sid, ssn) \
        ((stream)->type[sid].ssn = ssn + 1)
 
+/* What is the current MID number for this stream? */
+#define sctp_mid_peek(stream, type, sid) \
+       ((stream)->type[sid].mid)
+
+/* Return the next MID number for this stream.  */
+#define sctp_mid_next(stream, type, sid) \
+       ((stream)->type[sid].mid++)
+
+/* Skip over this mid and all below. */
+#define sctp_mid_skip(stream, type, sid, mid) \
+       ((stream)->type[sid].mid = mid + 1)
+
 /*
  * Pointers to address related SCTP functions.
  * (i.e. things that depend on the address family.)
@@ -623,6 +635,7 @@ struct sctp_chunk {
        __u16   rtt_in_progress:1,      /* This chunk used for RTT calc? */
                has_tsn:1,              /* Does this chunk have a TSN yet? */
                has_ssn:1,              /* Does this chunk have a SSN yet? */
+#define has_mid has_ssn
                singleton:1,            /* Only chunk in the packet? */
                end_of_packet:1,        /* Last chunk in the packet? */
                ecn_ce_done:1,          /* Have we processed the ECN CE bit? */
@@ -1360,7 +1373,10 @@ struct sctp_stream_out_ext {
 };
 
 struct sctp_stream_out {
-       __u16   ssn;
+       union {
+               __u32 mid;
+               __u16 ssn;
+       };
        __u8    state;
        struct sctp_stream_out_ext *ext;
 };
index 4a865cd06d76cd5b2aa417de618da3203f7b53e4..01a26ee051e3878ced4253429b5017708d0c138f 100644 (file)
@@ -313,6 +313,7 @@ static enum sctp_xmit __sctp_packet_append_chunk(struct sctp_packet *packet,
        /* We believe that this chunk is OK to add to the packet */
        switch (chunk->chunk_hdr->type) {
        case SCTP_CID_DATA:
+       case SCTP_CID_I_DATA:
                /* Account for the data being in the packet */
                sctp_packet_append_data(packet, chunk);
                /* Disallow SACK bundling after DATA. */
@@ -724,7 +725,7 @@ static enum sctp_xmit sctp_packet_can_append_data(struct sctp_packet *packet,
         * or delay in hopes of bundling a full sized packet.
         */
        if (chunk->skb->len + q->out_qlen > transport->pathmtu -
-               packet->overhead - sizeof(struct sctp_data_chunk) - 4)
+           packet->overhead - sctp_datachk_len(&chunk->asoc->stream) - 4)
                /* Enough data queued to fill a packet */
                return SCTP_XMIT_OK;
 
@@ -759,7 +760,7 @@ static void sctp_packet_append_data(struct sctp_packet *packet,
 
        asoc->peer.rwnd = rwnd;
        sctp_chunk_assign_tsn(chunk);
-       sctp_chunk_assign_ssn(chunk);
+       asoc->stream.si->assign_number(chunk);
 }
 
 static enum sctp_xmit sctp_packet_will_fit(struct sctp_packet *packet,
index 306c737bde874215acaff7f50bb8e640f90eea86..3654e1ede7168e123b4df6f4a88e0f688a5ec7ba 100644 (file)
@@ -2002,7 +2002,20 @@ static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len)
                if (err < 0)
                        goto out_free;
 
-               wait_connect = true;
+               /* If stream interleave is enabled, wait_connect has to be
+                * done earlier than data enqueue, as it needs to make data
+                * or idata according to asoc->intl_enable which is set
+                * after connection is done.
+                */
+               if (sctp_sk(asoc->base.sk)->strm_interleave) {
+                       timeo = sock_sndtimeo(sk, 0);
+                       err = sctp_wait_for_connect(asoc, &timeo);
+                       if (err)
+                               goto out_unlock;
+               } else {
+                       wait_connect = true;
+               }
+
                pr_debug("%s: we associated primitively\n", __func__);
        }
 
@@ -3180,7 +3193,7 @@ static int sctp_setsockopt_maxseg(struct sock *sk, char __user *optval, unsigned
                if (val == 0) {
                        val = asoc->pathmtu - sp->pf->af->net_header_len;
                        val -= sizeof(struct sctphdr) +
-                              sizeof(struct sctp_data_chunk);
+                              sctp_datachk_len(&asoc->stream);
                }
                asoc->user_frag = val;
                asoc->frag_point = sctp_frag_point(asoc, asoc->pathmtu);
index 397c3c1789b3c831bb13cb610f6a3332cd7b16f5..3ac47e78c01330fdcbaef601a1e4c9e5780a5d4e 100644 (file)
@@ -57,16 +57,53 @@ static struct sctp_chunk *sctp_make_idatafrag_empty(
        return retval;
 }
 
+static void sctp_chunk_assign_mid(struct sctp_chunk *chunk)
+{
+       struct sctp_stream *stream;
+       struct sctp_chunk *lchunk;
+       __u32 cfsn = 0;
+       __u16 sid;
+
+       if (chunk->has_mid)
+               return;
+
+       sid = sctp_chunk_stream_no(chunk);
+       stream = &chunk->asoc->stream;
+
+       list_for_each_entry(lchunk, &chunk->msg->chunks, frag_list) {
+               struct sctp_idatahdr *hdr;
+
+               lchunk->has_mid = 1;
+
+               if (lchunk->chunk_hdr->flags & SCTP_DATA_UNORDERED)
+                       continue;
+
+               hdr = lchunk->subh.idata_hdr;
+
+               if (lchunk->chunk_hdr->flags & SCTP_DATA_FIRST_FRAG)
+                       hdr->ppid = lchunk->sinfo.sinfo_ppid;
+               else
+                       hdr->fsn = htonl(cfsn++);
+
+               if (lchunk->chunk_hdr->flags & SCTP_DATA_LAST_FRAG)
+                       hdr->mid = htonl(sctp_mid_next(stream, out, sid));
+               else
+                       hdr->mid = htonl(sctp_mid_peek(stream, out, sid));
+       }
+}
+
 static struct sctp_stream_interleave sctp_stream_interleave_0 = {
        .data_chunk_len         = sizeof(struct sctp_data_chunk),
        /* DATA process functions */
        .make_datafrag          = sctp_make_datafrag_empty,
+       .assign_number          = sctp_chunk_assign_ssn,
 };
 
 static struct sctp_stream_interleave sctp_stream_interleave_1 = {
        .data_chunk_len         = sizeof(struct sctp_idata_chunk),
        /* I-DATA process functions */
        .make_datafrag          = sctp_make_idatafrag_empty,
+       .assign_number          = sctp_chunk_assign_mid,
 };
 
 void sctp_stream_interleave_init(struct sctp_stream *stream)
index 5447228bf1a014ad6c95b80355bb1c3827800db7..650b634fb2a319e12be685174ea1f2cea80f6982 100644 (file)
@@ -443,8 +443,8 @@ struct sctp_ulpevent *sctp_ulpevent_make_send_failed(
                goto fail;
 
        /* Pull off the common chunk header and DATA header.  */
-       skb_pull(skb, sizeof(struct sctp_data_chunk));
-       len -= sizeof(struct sctp_data_chunk);
+       skb_pull(skb, sctp_datachk_len(&asoc->stream));
+       len -= sctp_datachk_len(&asoc->stream);
 
        /* Embed the event fields inside the cloned skb.  */
        event = sctp_skb2event(skb);