sctp: add SCTP_SEND_FAILED_EVENT event
authorXin Long <lucien.xin@gmail.com>
Tue, 8 Oct 2019 11:27:36 +0000 (19:27 +0800)
committerJakub Kicinski <jakub.kicinski@netronome.com>
Thu, 10 Oct 2019 00:06:58 +0000 (17:06 -0700)
This patch is to add a new event SCTP_SEND_FAILED_EVENT described in
rfc6458#section-6.1.11. It's a update of SCTP_SEND_FAILED event:

  struct sctp_sndrcvinfo ssf_info is replaced with
  struct sctp_sndinfo ssfe_info in struct sctp_send_failed_event.

SCTP_SEND_FAILED is being deprecated, but we don't remove it in this
patch. Both are being processed in sctp_datamsg_destroy() when the
corresp event flag is set.

Signed-off-by: Xin Long <lucien.xin@gmail.com>
Acked-by: Neil Horman <nhorman@tuxdriver.com>
Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
include/net/sctp/ulpevent.h
include/uapi/linux/sctp.h
net/sctp/chunk.c
net/sctp/ulpevent.c

index e6ead1e..0b032b9 100644 (file)
@@ -95,6 +95,13 @@ struct sctp_ulpevent *sctp_ulpevent_make_send_failed(
        __u32 error,
        gfp_t gfp);
 
+struct sctp_ulpevent *sctp_ulpevent_make_send_failed_event(
+       const struct sctp_association *asoc,
+       struct sctp_chunk *chunk,
+       __u16 flags,
+       __u32 error,
+       gfp_t gfp);
+
 struct sctp_ulpevent *sctp_ulpevent_make_shutdown_event(
        const struct sctp_association *asoc,
        __u16 flags,
index 6d5b164..6bce7f9 100644 (file)
@@ -449,6 +449,16 @@ struct sctp_send_failed {
        __u8 ssf_data[0];
 };
 
+struct sctp_send_failed_event {
+       __u16 ssf_type;
+       __u16 ssf_flags;
+       __u32 ssf_length;
+       __u32 ssf_error;
+       struct sctp_sndinfo ssfe_info;
+       sctp_assoc_t ssf_assoc_id;
+       __u8 ssf_data[0];
+};
+
 /*
  *   ssf_flags: 16 bits (unsigned integer)
  *
@@ -605,6 +615,7 @@ struct sctp_event_subscribe {
        __u8 sctp_stream_reset_event;
        __u8 sctp_assoc_reset_event;
        __u8 sctp_stream_change_event;
+       __u8 sctp_send_failure_event_event;
 };
 
 /*
@@ -632,6 +643,7 @@ union sctp_notification {
        struct sctp_stream_reset_event sn_strreset_event;
        struct sctp_assoc_reset_event sn_assocreset_event;
        struct sctp_stream_change_event sn_strchange_event;
+       struct sctp_send_failed_event sn_send_failed_event;
 };
 
 /* Section 5.3.1
@@ -667,7 +679,9 @@ enum sctp_sn_type {
 #define SCTP_ASSOC_RESET_EVENT         SCTP_ASSOC_RESET_EVENT
        SCTP_STREAM_CHANGE_EVENT,
 #define SCTP_STREAM_CHANGE_EVENT       SCTP_STREAM_CHANGE_EVENT
-       SCTP_SN_TYPE_MAX        = SCTP_STREAM_CHANGE_EVENT,
+       SCTP_SEND_FAILED_EVENT,
+#define SCTP_SEND_FAILED_EVENT         SCTP_SEND_FAILED_EVENT
+       SCTP_SN_TYPE_MAX        = SCTP_SEND_FAILED_EVENT,
 #define SCTP_SN_TYPE_MAX               SCTP_SN_TYPE_MAX
 };
 
index cc0405c..cc3ce5d 100644 (file)
@@ -75,41 +75,39 @@ static void sctp_datamsg_destroy(struct sctp_datamsg *msg)
        struct list_head *pos, *temp;
        struct sctp_chunk *chunk;
        struct sctp_ulpevent *ev;
-       int error = 0, notify;
-
-       /* If we failed, we may need to notify. */
-       notify = msg->send_failed ? -1 : 0;
+       int error, sent;
 
        /* Release all references. */
        list_for_each_safe(pos, temp, &msg->chunks) {
                list_del_init(pos);
                chunk = list_entry(pos, struct sctp_chunk, frag_list);
-               /* Check whether we _really_ need to notify. */
-               if (notify < 0) {
-                       asoc = chunk->asoc;
-                       if (msg->send_error)
-                               error = msg->send_error;
-                       else
-                               error = asoc->outqueue.error;
-
-                       notify = sctp_ulpevent_type_enabled(asoc->subscribe,
-                                                           SCTP_SEND_FAILED);
+
+               if (!msg->send_failed) {
+                       sctp_chunk_put(chunk);
+                       continue;
                }
 
-               /* Generate a SEND FAILED event only if enabled. */
-               if (notify > 0) {
-                       int sent;
-                       if (chunk->has_tsn)
-                               sent = SCTP_DATA_SENT;
-                       else
-                               sent = SCTP_DATA_UNSENT;
+               asoc = chunk->asoc;
+               error = msg->send_error ?: asoc->outqueue.error;
+               sent = chunk->has_tsn ? SCTP_DATA_SENT : SCTP_DATA_UNSENT;
 
+               if (sctp_ulpevent_type_enabled(asoc->subscribe,
+                                              SCTP_SEND_FAILED)) {
                        ev = sctp_ulpevent_make_send_failed(asoc, chunk, sent,
                                                            error, GFP_ATOMIC);
                        if (ev)
                                asoc->stream.si->enqueue_event(&asoc->ulpq, ev);
                }
 
+               if (sctp_ulpevent_type_enabled(asoc->subscribe,
+                                              SCTP_SEND_FAILED_EVENT)) {
+                       ev = sctp_ulpevent_make_send_failed_event(asoc, chunk,
+                                                                 sent, error,
+                                                                 GFP_ATOMIC);
+                       if (ev)
+                               asoc->stream.si->enqueue_event(&asoc->ulpq, ev);
+               }
+
                sctp_chunk_put(chunk);
        }
 
index f07b986..c82dbdc 100644 (file)
@@ -527,6 +527,45 @@ fail:
        return NULL;
 }
 
+struct sctp_ulpevent *sctp_ulpevent_make_send_failed_event(
+       const struct sctp_association *asoc, struct sctp_chunk *chunk,
+       __u16 flags, __u32 error, gfp_t gfp)
+{
+       struct sctp_send_failed_event *ssf;
+       struct sctp_ulpevent *event;
+       struct sk_buff *skb;
+       int len;
+
+       skb = skb_copy_expand(chunk->skb, sizeof(*ssf), 0, gfp);
+       if (!skb)
+               return NULL;
+
+       len = ntohs(chunk->chunk_hdr->length);
+       len -= sctp_datachk_len(&asoc->stream);
+
+       skb_pull(skb, sctp_datachk_len(&asoc->stream));
+       event = sctp_skb2event(skb);
+       sctp_ulpevent_init(event, MSG_NOTIFICATION, skb->truesize);
+
+       ssf = skb_push(skb, sizeof(*ssf));
+       ssf->ssf_type = SCTP_SEND_FAILED_EVENT;
+       ssf->ssf_flags = flags;
+       ssf->ssf_length = sizeof(*ssf) + len;
+       skb_trim(skb, ssf->ssf_length);
+       ssf->ssf_error = error;
+
+       ssf->ssfe_info.snd_sid = chunk->sinfo.sinfo_stream;
+       ssf->ssfe_info.snd_ppid = chunk->sinfo.sinfo_ppid;
+       ssf->ssfe_info.snd_context = chunk->sinfo.sinfo_context;
+       ssf->ssfe_info.snd_assoc_id = chunk->sinfo.sinfo_assoc_id;
+       ssf->ssfe_info.snd_flags = chunk->chunk_hdr->flags;
+
+       sctp_ulpevent_set_owner(event, asoc);
+       ssf->ssf_assoc_id = sctp_assoc2id(asoc);
+
+       return event;
+}
+
 /* Create and initialize a SCTP_SHUTDOWN_EVENT notification.
  *
  * Socket Extensions for SCTP - draft-01