aboutsummaryrefslogtreecommitdiff
path: root/net/dcsctp/socket/stream_reset_handler.h
blob: dc0ee5e8cc59703dc57caf8ae9b71232ce2c32fe (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
/*
 *  Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */
#ifndef NET_DCSCTP_SOCKET_STREAM_RESET_HANDLER_H_
#define NET_DCSCTP_SOCKET_STREAM_RESET_HANDLER_H_

#include <cstdint>
#include <memory>
#include <string>
#include <unordered_set>
#include <utility>
#include <vector>

#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "api/array_view.h"
#include "net/dcsctp/common/internal_types.h"
#include "net/dcsctp/packet/chunk/reconfig_chunk.h"
#include "net/dcsctp/packet/parameter/incoming_ssn_reset_request_parameter.h"
#include "net/dcsctp/packet/parameter/outgoing_ssn_reset_request_parameter.h"
#include "net/dcsctp/packet/parameter/reconfiguration_response_parameter.h"
#include "net/dcsctp/packet/sctp_packet.h"
#include "net/dcsctp/public/dcsctp_socket.h"
#include "net/dcsctp/rx/data_tracker.h"
#include "net/dcsctp/rx/reassembly_queue.h"
#include "net/dcsctp/socket/context.h"
#include "net/dcsctp/timer/timer.h"
#include "net/dcsctp/tx/retransmission_queue.h"

namespace dcsctp {

// StreamResetHandler handles sending outgoing stream reset requests (to close
// an SCTP stream, which translates to closing a data channel).
//
// It also handles incoming "outgoing stream reset requests", when the peer
// wants to close its data channel.
//
// Resetting streams is an asynchronous operation where the client will request
// a request a stream to be reset, but then it might not be performed exactly at
// this point. First, the sender might need to discard all messages that have
// been enqueued for this stream, or it may select to wait until all have been
// sent. At least, it must wait for the currently sending fragmented message to
// be fully sent, because a stream can't be reset while having received half a
// message. In the stream reset request, the "sender's last assigned TSN" is
// provided, which is simply the TSN for which the receiver should've received
// all messages before this value, before the stream can be reset. Since
// fragments can get lost or sent out-of-order, the receiver of a request may
// not have received all the data just yet, and then it will respond to the
// sender: "In progress". In other words, try again. The sender will then need
// to start a timer and try the very same request again (but with a new sequence
// number) until the receiver successfully performs the operation.
//
// All this can take some time, and may be driven by timers, so the client will
// ultimately be notified using callbacks.
//
// In this implementation, when a stream is reset, the queued but not-yet-sent
// messages will be discarded, but that may change in the future. RFC8831 allows
// both behaviors.
class StreamResetHandler {
 public:
  StreamResetHandler(absl::string_view log_prefix,
                     Context* context,
                     TimerManager* timer_manager,
                     DataTracker* data_tracker,
                     ReassemblyQueue* reassembly_queue,
                     RetransmissionQueue* retransmission_queue)
      : log_prefix_(std::string(log_prefix) + "reset: "),
        ctx_(context),
        data_tracker_(data_tracker),
        reassembly_queue_(reassembly_queue),
        retransmission_queue_(retransmission_queue),
        reconfig_timer_(timer_manager->CreateTimer(
            "re-config",
            [this]() { return OnReconfigTimerExpiry(); },
            TimerOptions(DurationMs(0)))),
        next_outgoing_req_seq_nbr_(ReconfigRequestSN(*ctx_->my_initial_tsn())),
        last_processed_req_seq_nbr_(
            ReconfigRequestSN(*ctx_->peer_initial_tsn() - 1)) {}

  // Initiates reset of the provided streams. While there can only be one
  // ongoing stream reset request at any time, this method can be called at any
  // time and also multiple times. It will enqueue requests that can't be
  // directly fulfilled, and will asynchronously process them when any ongoing
  // request has completed.
  void ResetStreams(rtc::ArrayView<const StreamID> outgoing_streams);

  // Creates a Reset Streams request that must be sent if returned. Will start
  // the reconfig timer. Will return absl::nullopt if there is no need to
  // create a request (no streams to reset) or if there already is an ongoing
  // stream reset request that hasn't completed yet.
  absl::optional<ReConfigChunk> MakeStreamResetRequest();

  // Called when handling and incoming RE-CONFIG chunk.
  void HandleReConfig(ReConfigChunk chunk);

 private:
  // Represents a stream request operation. There can only be one ongoing at
  // any time, and a sent request may either succeed, fail or result in the
  // receiver signaling that it can't process it right now, and then it will be
  // retried.
  class CurrentRequest {
   public:
    CurrentRequest(TSN sender_last_assigned_tsn, std::vector<StreamID> streams)
        : req_seq_nbr_(absl::nullopt),
          sender_last_assigned_tsn_(sender_last_assigned_tsn),
          streams_(std::move(streams)) {}

    // Returns the current request sequence number, if this request has been
    // sent (check `has_been_sent` first). Will return 0 if the request is just
    // prepared (or scheduled for retransmission) but not yet sent.
    ReconfigRequestSN req_seq_nbr() const {
      return req_seq_nbr_.value_or(ReconfigRequestSN(0));
    }

    // The sender's last assigned TSN, from the retransmission queue. The
    // receiver uses this to know when all data up to this TSN has been
    // received, to know when to safely reset the stream.
    TSN sender_last_assigned_tsn() const { return sender_last_assigned_tsn_; }

    // The streams that are to be reset.
    const std::vector<StreamID>& streams() const { return streams_; }

    // If this request has been sent yet. If not, then it's either because it
    // has only been prepared and not yet sent, or because the received couldn't
    // apply the request, and then the exact same request will be retried, but
    // with a new sequence number.
    bool has_been_sent() const { return req_seq_nbr_.has_value(); }

    // If the receiver can't apply the request yet (and answered "In Progress"),
    // this will be called to prepare the request to be retransmitted at a later
    // time.
    void PrepareRetransmission() { req_seq_nbr_ = absl::nullopt; }

    // If the request hasn't been sent yet, this assigns it a request number.
    void PrepareToSend(ReconfigRequestSN new_req_seq_nbr) {
      req_seq_nbr_ = new_req_seq_nbr;
    }

   private:
    // If this is set, this request has been sent. If it's not set, the request
    // has been prepared, but has not yet been sent. This is typically used when
    // the peer responded "in progress" and the same request (but a different
    // request number) must be sent again.
    absl::optional<ReconfigRequestSN> req_seq_nbr_;
    // The sender's (that's us) last assigned TSN, from the retransmission
    // queue.
    TSN sender_last_assigned_tsn_;
    // The streams that are to be reset in this request.
    const std::vector<StreamID> streams_;
  };

  // Called to validate an incoming RE-CONFIG chunk.
  bool Validate(const ReConfigChunk& chunk);

  // Processes a stream stream reconfiguration chunk and may either return
  // absl::nullopt (on protocol errors), or a list of responses - either 0, 1
  // or 2.
  absl::optional<std::vector<ReconfigurationResponseParameter>> Process(
      const ReConfigChunk& chunk);

  // Creates the actual RE-CONFIG chunk. A request (which set `current_request`)
  // must have been created prior.
  ReConfigChunk MakeReconfigChunk();

  // Called to validate the `req_seq_nbr`, that it's the next in sequence. If it
  // fails to validate, and returns false, it will also add a response to
  // `responses`.
  bool ValidateReqSeqNbr(
      ReconfigRequestSN req_seq_nbr,
      std::vector<ReconfigurationResponseParameter>& responses);

  // Called when this socket receives an outgoing stream reset request. It might
  // either be performed straight away, or have to be deferred, and the result
  // of that will be put in `responses`.
  void HandleResetOutgoing(
      const ParameterDescriptor& descriptor,
      std::vector<ReconfigurationResponseParameter>& responses);

  // Called when this socket receives an incoming stream reset request. This
  // isn't really supported, but a successful response is put in `responses`.
  void HandleResetIncoming(
      const ParameterDescriptor& descriptor,
      std::vector<ReconfigurationResponseParameter>& responses);

  // Called when receiving a response to an outgoing stream reset request. It
  // will either commit the stream resetting, if the operation was successful,
  // or will schedule a retry if it was deferred. And if it failed, the
  // operation will be rolled back.
  void HandleResponse(const ParameterDescriptor& descriptor);

  // Expiration handler for the Reconfig timer.
  absl::optional<DurationMs> OnReconfigTimerExpiry();

  const std::string log_prefix_;
  Context* ctx_;
  DataTracker* data_tracker_;
  ReassemblyQueue* reassembly_queue_;
  RetransmissionQueue* retransmission_queue_;
  const std::unique_ptr<Timer> reconfig_timer_;

  // Outgoing streams that have been requested to be reset, but hasn't yet
  // been included in an outgoing request.
  std::unordered_set<StreamID, StreamID::Hasher> streams_to_reset_;

  // The next sequence number for outgoing stream requests.
  ReconfigRequestSN next_outgoing_req_seq_nbr_;

  // The current stream request operation.
  absl::optional<CurrentRequest> current_request_;

  // For incoming requests - last processed request sequence number.
  ReconfigRequestSN last_processed_req_seq_nbr_;
};
}  // namespace dcsctp

#endif  // NET_DCSCTP_SOCKET_STREAM_RESET_HANDLER_H_