librats 2.3.3 → 2.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/native-src/CMakeLists.txt +15 -0
- package/native-src/src/librats/bindings/rats.cpp +59 -2
- package/native-src/src/librats/bindings/rats.h +30 -0
- package/native-src/src/librats/bittorrent/client.cpp +221 -26
- package/native-src/src/librats/bittorrent/client.h +73 -6
- package/native-src/src/librats/bittorrent/mse.cpp +626 -0
- package/native-src/src/librats/bittorrent/mse.h +218 -0
- package/native-src/src/librats/bittorrent/peer_connection.cpp +187 -59
- package/native-src/src/librats/bittorrent/peer_connection.h +109 -17
- package/native-src/src/librats/bittorrent/peer_link.cpp +134 -0
- package/native-src/src/librats/bittorrent/peer_link.h +145 -0
- package/native-src/src/librats/bittorrent/peer_list.cpp +75 -12
- package/native-src/src/librats/bittorrent/peer_list.h +130 -18
- package/native-src/src/librats/bittorrent/torrent.cpp +54 -11
- package/native-src/src/librats/bittorrent/torrent.h +19 -3
- package/native-src/src/librats/bittorrent/types.h +27 -0
- package/native-src/src/librats/bittorrent/utp_manager.cpp +234 -0
- package/native-src/src/librats/bittorrent/utp_manager.h +131 -0
- package/native-src/src/librats/bittorrent/utp_packet.h +216 -0
- package/native-src/src/librats/bittorrent/utp_stream.cpp +903 -0
- package/native-src/src/librats/bittorrent/utp_stream.h +400 -0
- package/native-src/src/librats/core/socket.cpp +44 -8
- package/native-src/src/librats/core/socket.h +23 -1
- package/package.json +1 -1
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file utp_stream.h
|
|
5
|
+
* @brief One uTP connection (BEP 29): an ordered, reliable, delay-controlled byte
|
|
6
|
+
* stream over datagrams.
|
|
7
|
+
*
|
|
8
|
+
* uTP is what most of the swarm now speaks. It exists for one reason: a BitTorrent
|
|
9
|
+
* client saturating an uplink with TCP starves everything else on the same line —
|
|
10
|
+
* VoIP, SSH, the web page the user is reading — because TCP only backs off once a
|
|
11
|
+
* router has already filled its queue and started dropping. uTP measures the
|
|
12
|
+
* *one-way delay* instead and backs off as soon as the queue starts growing, so it
|
|
13
|
+
* yields to any TCP flow sharing the path. That is the whole design, and it is why
|
|
14
|
+
* `wnd_size` is in bytes, why every packet carries two timestamps, and why the
|
|
15
|
+
* congestion controller (LEDBAT, RFC 6817) targets 100 ms of queuing delay rather
|
|
16
|
+
* than a loss event.
|
|
17
|
+
*
|
|
18
|
+
* ## What this is not
|
|
19
|
+
*
|
|
20
|
+
* The library already has a reliable-UDP transport (`transport/udp_stream.h`) for
|
|
21
|
+
* the node's own mesh. This is a *second* one, deliberately: that one is ours to
|
|
22
|
+
* design, this one has to be bit-compatible with uTorrent and libtorrent or it is
|
|
23
|
+
* worthless. They share no code, and per the project's layering BitTorrent never
|
|
24
|
+
* reaches into `transport/`.
|
|
25
|
+
*
|
|
26
|
+
* ## Structure
|
|
27
|
+
*
|
|
28
|
+
* A Stream has no socket, no thread and no locks: it is driven entirely by
|
|
29
|
+
* `on_packet()` and `tick()` and emits datagrams through its `Host`, exactly like
|
|
30
|
+
* `dht::Node` is driven by `on_datagram()`. The Host (utp_manager.h) owns the one
|
|
31
|
+
* shared UDP socket and demultiplexes to us. Everything lives on the BitTorrent
|
|
32
|
+
* reactor thread.
|
|
33
|
+
*
|
|
34
|
+
* Reads and writes mirror non-blocking socket semantics, because that is what the
|
|
35
|
+
* layer above (`PeerLink` → `PeerConnection`) is written against: `write()` takes
|
|
36
|
+
* what it can and reports WouldBlock; `read()` drains what has arrived in order and
|
|
37
|
+
* reports Eof once the peer's FIN has been reached.
|
|
38
|
+
*
|
|
39
|
+
* ## Connection ids
|
|
40
|
+
*
|
|
41
|
+
* Each direction has its own 16-bit id, and the pair is always adjacent. The
|
|
42
|
+
* initiator picks `recv_id` at random and uses `send_id = recv_id + 1`; the SYN
|
|
43
|
+
* itself is sent carrying `recv_id` — that is, the id the initiator expects the
|
|
44
|
+
* *answer* on, which is the one genuinely surprising thing about the uTP handshake.
|
|
45
|
+
* The responder reads that id, and takes `send_id = id`, `recv_id = id + 1`.
|
|
46
|
+
*
|
|
47
|
+
* ## What is not implemented (on purpose)
|
|
48
|
+
*
|
|
49
|
+
* - **Path-MTU discovery.** libtorrent probes upwards from a floor. We instead fix
|
|
50
|
+
* the payload at a size that fits inside IPv6's 1280-byte minimum MTU, so no path
|
|
51
|
+
* can fragment us. That costs some throughput on a 1500-byte path and buys
|
|
52
|
+
* immunity to the whole class of black-holed-fragment failures.
|
|
53
|
+
* - **The close-reason extension** (type 3). Parsed past, never acted on: it is a
|
|
54
|
+
* diagnostic uTorrent emits, and nothing downstream of us would use it.
|
|
55
|
+
* - **Delayed-ACK timers.** Acks are deferred only to the end of the current socket
|
|
56
|
+
* drain (see Host::utp_defer_ack), never on a timer, so we never sit on an ack the
|
|
57
|
+
* sender's congestion control is waiting for.
|
|
58
|
+
*/
|
|
59
|
+
|
|
60
|
+
#include "librats/bittorrent/utp_packet.h"
|
|
61
|
+
#include "librats/core/address.h"
|
|
62
|
+
#include "librats/core/bytes.h"
|
|
63
|
+
|
|
64
|
+
#include <chrono>
|
|
65
|
+
#include <cstdint>
|
|
66
|
+
#include <deque>
|
|
67
|
+
#include <string>
|
|
68
|
+
#include <unordered_map>
|
|
69
|
+
|
|
70
|
+
namespace librats::bittorrent::utp {
|
|
71
|
+
|
|
72
|
+
// ---- Tunables, all matching libtorrent's shipping defaults --------------------
|
|
73
|
+
|
|
74
|
+
/// LEDBAT's target queuing delay. Above it we shrink the window, below it we grow:
|
|
75
|
+
/// this single number is what makes uTP yield to TCP rather than compete with it.
|
|
76
|
+
constexpr int kTargetDelayUs = 100 * 1000;
|
|
77
|
+
/// How aggressively the window follows the delay signal (`utp_gain_factor`).
|
|
78
|
+
constexpr int kGainFactor = 3000;
|
|
79
|
+
/// Floor on the retransmit timeout, so a very short RTT can't produce a hair
|
|
80
|
+
/// trigger that mistakes reordering for loss.
|
|
81
|
+
constexpr int kMinTimeoutMs = 500;
|
|
82
|
+
/// Timeout for the SYN, where there is no RTT estimate to base one on.
|
|
83
|
+
constexpr int kConnectTimeoutMs = 3000;
|
|
84
|
+
/// Retransmission budgets before the connection is declared dead.
|
|
85
|
+
constexpr int kSynResends = 2;
|
|
86
|
+
constexpr int kFinResends = 2;
|
|
87
|
+
constexpr int kNumResends = 3;
|
|
88
|
+
/// What fraction (percent) of the window survives a loss event.
|
|
89
|
+
constexpr int kLossMultiplier = 50;
|
|
90
|
+
/// The window is cut at most once per this interval, so a burst of losses inside
|
|
91
|
+
/// one round trip is charged once rather than collapsing the window to nothing.
|
|
92
|
+
constexpr int kCwndReduceTimerMs = 100;
|
|
93
|
+
/// Duplicate acks that trigger a fast retransmit.
|
|
94
|
+
constexpr int kDupAckLimit = 3;
|
|
95
|
+
|
|
96
|
+
/// In-order bytes we will hold for a reader that has not drained us, and therefore
|
|
97
|
+
/// the largest window we ever advertise. Also the ceiling on throughput: this many
|
|
98
|
+
/// bytes per RTT, i.e. ~20 Mbit/s per peer at 100 ms — far more than a single peer
|
|
99
|
+
/// in a swarm ever supplies, and small enough that a few hundred connections cannot
|
|
100
|
+
/// add up to anything alarming (the buffer is grown on demand, not preallocated).
|
|
101
|
+
constexpr std::size_t kRecvBufferCapacity = 256 * 1024;
|
|
102
|
+
|
|
103
|
+
/// Bytes the writer may queue ahead of what the window will pass. Backpressure
|
|
104
|
+
/// above this is reported as WouldBlock and handled by the layer above, which
|
|
105
|
+
/// already has a send queue and a high-water mark of its own.
|
|
106
|
+
constexpr std::size_t kSendHighWater = 256 * 1024;
|
|
107
|
+
|
|
108
|
+
/// How long a closed stream is kept around to retransmit its FIN and absorb the
|
|
109
|
+
/// peer's last packets before the manager reaps it.
|
|
110
|
+
constexpr std::chrono::milliseconds kLinger{3000};
|
|
111
|
+
|
|
112
|
+
// ---- Delay history -----------------------------------------------------------
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* The lowest delay sample seen recently, used as the zero point for the delay
|
|
116
|
+
* measurements LEDBAT runs on.
|
|
117
|
+
*
|
|
118
|
+
* The two endpoints' clocks are unrelated, so a raw one-way delay sample is a
|
|
119
|
+
* meaningless number — but the *difference* between it and the smallest such
|
|
120
|
+
* sample is real queuing delay. The base has to be able to rise as well as fall or
|
|
121
|
+
* clock drift would slowly convince us the path is permanently congested, hence the ring of
|
|
122
|
+
* per-minute minima rather than a single running minimum. Ported from libtorrent's
|
|
123
|
+
* timestamp_history.
|
|
124
|
+
*/
|
|
125
|
+
class DelayHistory {
|
|
126
|
+
public:
|
|
127
|
+
static constexpr int kHistorySize = 20; ///< minutes of history
|
|
128
|
+
|
|
129
|
+
bool initialized() const noexcept { return num_samples_ != kNotInitialized; }
|
|
130
|
+
/// Record a sample and return it relative to the base. @p step advances the
|
|
131
|
+
/// ring (called once a minute).
|
|
132
|
+
std::uint32_t add_sample(std::uint32_t sample, bool step);
|
|
133
|
+
std::uint32_t base() const noexcept { return base_; }
|
|
134
|
+
/// Shift the whole history, to compensate for observed clock drift.
|
|
135
|
+
void adjust_base(int change);
|
|
136
|
+
|
|
137
|
+
private:
|
|
138
|
+
static constexpr std::uint16_t kNotInitialized = 0xffff;
|
|
139
|
+
|
|
140
|
+
std::uint32_t history_[kHistorySize] = {};
|
|
141
|
+
std::uint32_t base_ = 0;
|
|
142
|
+
std::uint16_t index_ = 0;
|
|
143
|
+
std::uint16_t num_samples_ = kNotInitialized;
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
/// Exponential moving average with a companion average deviation, in the fixed-point
|
|
147
|
+
/// form libtorrent uses for its RTT estimator (RFC 6298's SRTT/RTTVAR by another name).
|
|
148
|
+
class SlidingAverage {
|
|
149
|
+
public:
|
|
150
|
+
void add_sample(int s);
|
|
151
|
+
int mean() const noexcept { return num_samples_ > 0 ? (mean_ + 32) / 64 : 0; }
|
|
152
|
+
int avg_deviation() const noexcept { return num_samples_ > 1 ? (deviation_ + 32) / 64 : 0; }
|
|
153
|
+
int num_samples() const noexcept { return num_samples_; }
|
|
154
|
+
|
|
155
|
+
private:
|
|
156
|
+
static constexpr int kInvertedGain = 16;
|
|
157
|
+
int mean_ = 0; ///< fixed point, x64
|
|
158
|
+
int deviation_ = 0; ///< fixed point, x64
|
|
159
|
+
int num_samples_ = 0;
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
// ---- The stream --------------------------------------------------------------
|
|
163
|
+
|
|
164
|
+
class Stream;
|
|
165
|
+
|
|
166
|
+
/// What a Stream needs from whoever owns the socket. Implemented by utp::Manager.
|
|
167
|
+
class Host {
|
|
168
|
+
public:
|
|
169
|
+
virtual ~Host() = default;
|
|
170
|
+
/// Put one datagram on the wire. Failures are the Host's to swallow — a stream
|
|
171
|
+
/// treats a datagram as sent, and its own retransmit timer as the recovery path.
|
|
172
|
+
virtual void utp_send(const Address& to, const std::uint8_t* data, std::size_t len) = 0;
|
|
173
|
+
/// Ask to be called back at Stream::send_deferred_ack() once the socket's
|
|
174
|
+
/// current receive burst has been fully drained, so a burst of N data packets
|
|
175
|
+
/// costs one ack instead of N.
|
|
176
|
+
virtual void utp_defer_ack(Stream& s) = 0;
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
class Stream {
|
|
180
|
+
public:
|
|
181
|
+
using Clock = std::chrono::steady_clock;
|
|
182
|
+
|
|
183
|
+
enum class State : std::uint8_t {
|
|
184
|
+
Idle, ///< created, nothing sent (a responder before its SYN arrives)
|
|
185
|
+
SynSent, ///< initiator waiting for the SYN-ack
|
|
186
|
+
Connected, ///< data may flow
|
|
187
|
+
FinSent, ///< our side is done sending; still acking theirs
|
|
188
|
+
Closed, ///< dead: reaped by the manager
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
enum class Status : std::uint8_t { Ok, WouldBlock, Eof, Error };
|
|
192
|
+
struct IoResult {
|
|
193
|
+
std::size_t bytes = 0;
|
|
194
|
+
Status status = Status::WouldBlock;
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
/// All callbacks fire on the reactor thread, from inside on_packet()/tick().
|
|
198
|
+
/// An observer may close the stream from within one; it must not delete it
|
|
199
|
+
/// (the manager owns streams and reaps them between callbacks).
|
|
200
|
+
struct Observer {
|
|
201
|
+
virtual ~Observer() = default;
|
|
202
|
+
virtual void on_utp_connected() {}
|
|
203
|
+
virtual void on_utp_readable() {}
|
|
204
|
+
virtual void on_utp_writable() {}
|
|
205
|
+
/// The connection died on its own: reset, timeout, protocol error. An
|
|
206
|
+
/// orderly close by the peer is *not* reported here — it surfaces as Eof
|
|
207
|
+
/// from read(), exactly as a TCP recv() of 0 would.
|
|
208
|
+
virtual void on_utp_error(const std::string& why) {}
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
Stream(Host& host, std::uint16_t recv_id, std::uint16_t send_id);
|
|
212
|
+
~Stream() = default;
|
|
213
|
+
|
|
214
|
+
Stream(const Stream&) = delete;
|
|
215
|
+
Stream& operator=(const Stream&) = delete;
|
|
216
|
+
|
|
217
|
+
void set_observer(Observer* o) noexcept { obs_ = o; }
|
|
218
|
+
/// Does a live user object still own this stream? The manager reaps only
|
|
219
|
+
/// streams nobody is holding.
|
|
220
|
+
bool has_observer() const noexcept { return obs_ != nullptr; }
|
|
221
|
+
/// The user object is going away. Stop delivering callbacks; keep enough state
|
|
222
|
+
/// alive to finish the close handshake.
|
|
223
|
+
void detach() noexcept { obs_ = nullptr; }
|
|
224
|
+
|
|
225
|
+
/// Start an outgoing connection: sends the SYN.
|
|
226
|
+
void connect(const Address& to, Clock::time_point now);
|
|
227
|
+
|
|
228
|
+
/// Drain in-order received bytes. Eof once the peer's FIN has been reached and
|
|
229
|
+
/// everything before it delivered.
|
|
230
|
+
IoResult read(ByteSpan into);
|
|
231
|
+
/// Queue bytes for transmission, taking as many as the send buffer will hold.
|
|
232
|
+
IoResult write(const ByteView* slices, std::size_t count, Clock::time_point now);
|
|
233
|
+
/// Orderly shutdown: flush what is queued, then FIN. Idempotent.
|
|
234
|
+
void close(Clock::time_point now);
|
|
235
|
+
/// Abort: tell the peer the connection is gone and stop immediately.
|
|
236
|
+
void reset(Clock::time_point now);
|
|
237
|
+
|
|
238
|
+
/// Feed one datagram addressed to this stream. Returns false if it was
|
|
239
|
+
/// malformed or did not belong here (the caller may then look elsewhere).
|
|
240
|
+
bool on_packet(const std::uint8_t* data, std::size_t len,
|
|
241
|
+
const Address& from, Clock::time_point now);
|
|
242
|
+
/// Periodic timer: retransmissions, timeouts, linger expiry.
|
|
243
|
+
void tick(Clock::time_point now);
|
|
244
|
+
/// Emit the acknowledgement that on_packet() deferred (see Host::utp_defer_ack).
|
|
245
|
+
void send_deferred_ack(Clock::time_point now);
|
|
246
|
+
|
|
247
|
+
// ---- state ----
|
|
248
|
+
std::uint16_t recv_id() const noexcept { return recv_id_; }
|
|
249
|
+
std::uint16_t send_id() const noexcept { return send_id_; }
|
|
250
|
+
const Address& remote() const noexcept { return remote_; }
|
|
251
|
+
State state() const noexcept { return state_; }
|
|
252
|
+
bool connected() const noexcept { return state_ == State::Connected || state_ == State::FinSent; }
|
|
253
|
+
/// True once nothing more will happen here and the manager may destroy it.
|
|
254
|
+
bool reapable(Clock::time_point now) const noexcept;
|
|
255
|
+
/// Does this datagram belong to this stream? Both the id and the sender must
|
|
256
|
+
/// match, so a third party cannot inject into a connection by guessing an id.
|
|
257
|
+
bool matches(const Address& from, std::uint16_t id) const noexcept {
|
|
258
|
+
return id == recv_id_ && from == remote_;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// ---- introspection, for tests and logging ----
|
|
262
|
+
std::size_t bytes_in_flight() const noexcept { return bytes_in_flight_; }
|
|
263
|
+
std::size_t send_queue_bytes() const noexcept { return pending_bytes_ + bytes_in_flight_; }
|
|
264
|
+
std::uint32_t cwnd() const noexcept { return std::uint32_t(cwnd_ >> 16); }
|
|
265
|
+
std::uint16_t seq_nr() const noexcept { return seq_nr_; }
|
|
266
|
+
std::uint16_t ack_nr() const noexcept { return ack_nr_; }
|
|
267
|
+
|
|
268
|
+
private:
|
|
269
|
+
/// One packet we have sent and not yet had acknowledged. The payload is kept
|
|
270
|
+
/// (not the datagram): every transmission rebuilds the header, so a retransmit
|
|
271
|
+
/// carries an up-to-date ack_nr, window and SACK rather than a stale snapshot.
|
|
272
|
+
struct OutPacket {
|
|
273
|
+
std::uint16_t seq = 0;
|
|
274
|
+
PacketType type = PacketType::Data;
|
|
275
|
+
Bytes payload;
|
|
276
|
+
Clock::time_point send_time{};
|
|
277
|
+
std::uint16_t transmissions = 0;
|
|
278
|
+
bool acked = false;
|
|
279
|
+
bool in_flight = false; ///< counted in bytes_in_flight_
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
// ---- sending ----
|
|
283
|
+
void send_syn(Clock::time_point now);
|
|
284
|
+
void send_state(Clock::time_point now); ///< a pure ack
|
|
285
|
+
void send_reset_packet(Clock::time_point now);
|
|
286
|
+
/// Build and transmit one packet from outbuf_, (re)writing its header.
|
|
287
|
+
void transmit(OutPacket& p, Clock::time_point now);
|
|
288
|
+
/// Move as much of pending_ into flight as the windows allow.
|
|
289
|
+
void pump(Clock::time_point now);
|
|
290
|
+
/// Assemble a datagram into scratch_ and hand it to the host.
|
|
291
|
+
void emit(PacketType type, std::uint16_t seq, const std::uint8_t* payload,
|
|
292
|
+
std::size_t payload_len, Clock::time_point now, bool with_sack);
|
|
293
|
+
std::size_t write_sack(std::uint8_t* out) const;
|
|
294
|
+
bool has_sack() const noexcept { return !inbuf_.empty(); }
|
|
295
|
+
/// Register with the host for one acknowledgement at the end of the current
|
|
296
|
+
/// receive burst. Idempotent, so N data packets still cost exactly one ack.
|
|
297
|
+
void defer_ack();
|
|
298
|
+
|
|
299
|
+
// ---- receiving ----
|
|
300
|
+
/// Apply a cumulative ack, popping everything it covers off outbuf_.
|
|
301
|
+
void process_ack(std::uint16_t ack_nr, Clock::time_point now,
|
|
302
|
+
int& acked_bytes, std::uint32_t& min_rtt);
|
|
303
|
+
/// Apply a selective ack bitmap; may trigger fast retransmits.
|
|
304
|
+
void process_sack(std::uint16_t packet_ack, const std::uint8_t* bitmap, std::size_t len,
|
|
305
|
+
Clock::time_point now, int& acked_bytes, std::uint32_t& min_rtt);
|
|
306
|
+
void ack_packet(OutPacket& p, Clock::time_point now, std::uint32_t& min_rtt);
|
|
307
|
+
void pop_acked_front();
|
|
308
|
+
OutPacket* packet_at(std::uint16_t seq);
|
|
309
|
+
/// Deliver payload in order, draining the reorder buffer behind it.
|
|
310
|
+
void consume_data(const Header& h, const std::uint8_t* payload, std::size_t len);
|
|
311
|
+
void deliver(const std::uint8_t* data, std::size_t len);
|
|
312
|
+
|
|
313
|
+
// ---- congestion control ----
|
|
314
|
+
void do_ledbat(int acked_bytes, int delay, int in_flight);
|
|
315
|
+
void experienced_loss(std::uint16_t seq, Clock::time_point now);
|
|
316
|
+
int packet_timeout() const;
|
|
317
|
+
void resend(OutPacket& p, Clock::time_point now, bool fast);
|
|
318
|
+
|
|
319
|
+
void fail(const std::string& why);
|
|
320
|
+
/// Bytes the peer says it can still take, intersected with what we may send.
|
|
321
|
+
std::size_t window_available() const noexcept;
|
|
322
|
+
std::uint32_t advertised_window() const noexcept;
|
|
323
|
+
|
|
324
|
+
Host& host_;
|
|
325
|
+
Observer* obs_ = nullptr;
|
|
326
|
+
|
|
327
|
+
std::uint16_t recv_id_;
|
|
328
|
+
std::uint16_t send_id_;
|
|
329
|
+
Address remote_{};
|
|
330
|
+
State state_ = State::Idle;
|
|
331
|
+
|
|
332
|
+
// ---- sequence state ----
|
|
333
|
+
std::uint16_t seq_nr_ = 0; ///< sequence number of the next packet we send
|
|
334
|
+
std::uint16_t acked_seq_nr_ = 0; ///< highest of ours acknowledged in order
|
|
335
|
+
std::uint16_t ack_nr_ = 0; ///< highest of theirs received in order
|
|
336
|
+
std::uint16_t fast_resend_seq_nr_ = 0; ///< nothing below this is fast-resent again
|
|
337
|
+
std::uint16_t loss_seq_nr_ = 0; ///< a loss past this cuts the window again
|
|
338
|
+
int duplicate_acks_ = 0;
|
|
339
|
+
|
|
340
|
+
bool in_eof_ = false; ///< their FIN arrived
|
|
341
|
+
std::uint16_t in_eof_seq_nr_ = 0; ///< the sequence number it carried
|
|
342
|
+
bool out_eof_ = false; ///< our FIN has been queued
|
|
343
|
+
bool error_ = false;
|
|
344
|
+
/// The writer was told WouldBlock and is waiting to be woken. Without this the
|
|
345
|
+
/// layer above would sit on a full queue after the window reopened.
|
|
346
|
+
bool write_blocked_ = false;
|
|
347
|
+
/// Zero-window persist is in progress: pump() may put one packet on the wire
|
|
348
|
+
/// even though the peer says it has no room. See window_available().
|
|
349
|
+
bool probe_ = false;
|
|
350
|
+
/// Datagrams emitted, ever. Only the *change* across one incoming packet is
|
|
351
|
+
/// used — to tell whether an ack already rode out on a data packet.
|
|
352
|
+
std::uint32_t out_packets_ = 0;
|
|
353
|
+
|
|
354
|
+
// ---- send side ----
|
|
355
|
+
/// Payload chunks not yet given a sequence number. All but the last are full
|
|
356
|
+
/// (kMaxPayload); the last stays open so successive small writes coalesce into
|
|
357
|
+
/// one packet instead of one packet each — Nagle, without a timer.
|
|
358
|
+
std::deque<Bytes> pending_;
|
|
359
|
+
std::size_t pending_bytes_ = 0;
|
|
360
|
+
std::deque<OutPacket> outbuf_; ///< outbuf_[i].seq == acked_seq_nr_ + 1 + i
|
|
361
|
+
std::size_t bytes_in_flight_ = 0;
|
|
362
|
+
|
|
363
|
+
// ---- receive side ----
|
|
364
|
+
std::deque<Bytes> recv_q_; ///< in-order bytes waiting for the reader
|
|
365
|
+
std::size_t recv_head_ = 0; ///< bytes already consumed from recv_q_.front()
|
|
366
|
+
std::size_t recv_bytes_ = 0;
|
|
367
|
+
std::unordered_map<std::uint16_t, Bytes> inbuf_; ///< out-of-order, keyed by seq
|
|
368
|
+
std::size_t inbuf_bytes_ = 0;
|
|
369
|
+
|
|
370
|
+
// ---- congestion control / timing ----
|
|
371
|
+
std::int64_t cwnd_ = 0; ///< bytes, fixed point with 16 fractional bits
|
|
372
|
+
std::int32_t ssthresh_ = 0;
|
|
373
|
+
bool slow_start_ = true;
|
|
374
|
+
std::uint32_t adv_wnd_ = kMaxPayload; ///< the peer's advertised window, bytes
|
|
375
|
+
SlidingAverage rtt_;
|
|
376
|
+
DelayHistory their_delay_hist_; ///< our measurement of their one-way delay
|
|
377
|
+
DelayHistory delay_hist_; ///< the delay they measured, reflected to us
|
|
378
|
+
/// The last few delay readings. LEDBAT uses the smallest of them, because a
|
|
379
|
+
/// single sample is as likely to record a scheduling hiccup at either end as
|
|
380
|
+
/// anything about the path.
|
|
381
|
+
static constexpr int kDelaySampleCount = 3;
|
|
382
|
+
std::uint32_t delay_samples_[kDelaySampleCount] = {0xffffffffu, 0xffffffffu, 0xffffffffu};
|
|
383
|
+
int delay_sample_idx_ = 0;
|
|
384
|
+
std::uint32_t reply_micro_ = 0; ///< what we put in timestamp_difference
|
|
385
|
+
Clock::time_point last_history_step_{};
|
|
386
|
+
Clock::time_point timeout_{}; ///< retransmission deadline
|
|
387
|
+
Clock::time_point next_loss_{}; ///< earliest the window may be cut again
|
|
388
|
+
Clock::time_point closed_at_{}; ///< when linger started
|
|
389
|
+
int num_timeouts_ = 0;
|
|
390
|
+
/// Have we ever heard from this endpoint? A stream that has not is dropped on
|
|
391
|
+
/// its first timeout: the address may simply have been made up by whoever
|
|
392
|
+
/// handed it to us, and there is no reason to spend retransmissions on it.
|
|
393
|
+
bool confirmed_ = false;
|
|
394
|
+
bool deferred_ack_ = false;
|
|
395
|
+
|
|
396
|
+
/// Reused datagram assembly buffer, so a packet costs no allocation.
|
|
397
|
+
std::uint8_t scratch_[kMaxDatagram] = {};
|
|
398
|
+
};
|
|
399
|
+
|
|
400
|
+
} // namespace librats::bittorrent::utp
|
|
@@ -860,7 +860,8 @@ int send_tcp_string(socket_t socket, const std::string& data) {
|
|
|
860
860
|
|
|
861
861
|
// ── UDP Socket Functions ────────────────────────────────────────────────────
|
|
862
862
|
|
|
863
|
-
socket_t create_udp_socket(int port, const std::string& bind_address, AddressFamily af
|
|
863
|
+
socket_t create_udp_socket(int port, const std::string& bind_address, AddressFamily af,
|
|
864
|
+
UdpPortMode mode) {
|
|
864
865
|
if (!validate_port(port)) return RATS_INVALID_SOCKET;
|
|
865
866
|
|
|
866
867
|
const char* af_label = (af == AddressFamily::IPv4) ? "IPv4" :
|
|
@@ -891,8 +892,8 @@ socket_t create_udp_socket(int port, const std::string& bind_address, AddressFam
|
|
|
891
892
|
}
|
|
892
893
|
#endif
|
|
893
894
|
|
|
894
|
-
// Reuse the address — but only for a port we asked for by number,
|
|
895
|
-
//
|
|
895
|
+
// Reuse the address — but only for a port we asked for by number, and only when
|
|
896
|
+
// the caller has not said the port must be its own.
|
|
896
897
|
//
|
|
897
898
|
// Never when the kernel is choosing the port. On a datagram socket the option
|
|
898
899
|
// does not merely relax rebinding, it takes the port out of the set an auto-bind
|
|
@@ -901,7 +902,13 @@ socket_t create_udp_socket(int port, const std::string& bind_address, AddressFam
|
|
|
901
902
|
// here did. The two then share the port and the kernel picks one of them per
|
|
902
903
|
// datagram, so a dial-only socket can land on the port a listener beside it is
|
|
903
904
|
// serving and start eating its traffic. Rare, and invisible from either end.
|
|
904
|
-
|
|
905
|
+
//
|
|
906
|
+
// Exclusive asks for the same guarantee on a port given by number, where the
|
|
907
|
+
// clash is not rare at all: two subsystems can want one number by protocol. Not
|
|
908
|
+
// setting the option is what makes *our* bind fail over somebody else's socket;
|
|
909
|
+
// SO_EXCLUSIVEADDRUSE closes the other direction on Windows, where a later bind
|
|
910
|
+
// that does set SO_REUSEADDR would otherwise take the port out from under us.
|
|
911
|
+
if (port != 0 && mode == UdpPortMode::Shared) {
|
|
905
912
|
int opt = 1;
|
|
906
913
|
if (setsockopt(udp_socket, SOL_SOCKET, SO_REUSEADDR,
|
|
907
914
|
(char*)&opt, sizeof(opt)) == RATS_SOCKET_ERROR) {
|
|
@@ -911,6 +918,19 @@ socket_t create_udp_socket(int port, const std::string& bind_address, AddressFam
|
|
|
911
918
|
return RATS_INVALID_SOCKET;
|
|
912
919
|
}
|
|
913
920
|
}
|
|
921
|
+
#ifdef _WIN32
|
|
922
|
+
if (port != 0 && mode == UdpPortMode::Exclusive) {
|
|
923
|
+
// Best-effort: not having it still leaves the bind itself exclusive, which
|
|
924
|
+
// is the half that matters for a socket opened after the one it clashes with.
|
|
925
|
+
int opt = 1;
|
|
926
|
+
if (setsockopt(udp_socket, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
|
|
927
|
+
(char*)&opt, sizeof(opt)) == RATS_SOCKET_ERROR) {
|
|
928
|
+
LOG_SOCKET_DEBUG("SO_EXCLUSIVEADDRUSE unavailable on " << af_label
|
|
929
|
+
<< " UDP socket (error: "
|
|
930
|
+
<< socket_error_string(get_last_socket_error()) << ")");
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
#endif
|
|
914
934
|
|
|
915
935
|
// For IPv6/DualStack sockets, configure IPV6_V6ONLY
|
|
916
936
|
if (family == AF_INET6) {
|
|
@@ -943,8 +963,16 @@ socket_t create_udp_socket(int port, const std::string& bind_address, AddressFam
|
|
|
943
963
|
|
|
944
964
|
if (bind(udp_socket, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == RATS_SOCKET_ERROR) {
|
|
945
965
|
const int error = get_last_socket_error();
|
|
946
|
-
|
|
947
|
-
|
|
966
|
+
// An Exclusive bind losing a contested port is the question being
|
|
967
|
+
// answered, not a fault: the caller asked precisely so it could move.
|
|
968
|
+
if (mode == UdpPortMode::Exclusive) {
|
|
969
|
+
LOG_SOCKET_DEBUG("Port " << port << " already held, " << af_label
|
|
970
|
+
<< " UDP bind declined (error: "
|
|
971
|
+
<< socket_error_string(error) << ")");
|
|
972
|
+
} else {
|
|
973
|
+
LOG_SOCKET_ERROR("Failed to bind " << af_label << " UDP socket to port " << port
|
|
974
|
+
<< " (error: " << socket_error_string(error) << ")");
|
|
975
|
+
}
|
|
948
976
|
return fail_socket(udp_socket, error);
|
|
949
977
|
}
|
|
950
978
|
} else {
|
|
@@ -965,8 +993,16 @@ socket_t create_udp_socket(int port, const std::string& bind_address, AddressFam
|
|
|
965
993
|
|
|
966
994
|
if (bind(udp_socket, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == RATS_SOCKET_ERROR) {
|
|
967
995
|
const int error = get_last_socket_error();
|
|
968
|
-
|
|
969
|
-
|
|
996
|
+
// An Exclusive bind losing a contested port is the question being
|
|
997
|
+
// answered, not a fault: the caller asked precisely so it could move.
|
|
998
|
+
if (mode == UdpPortMode::Exclusive) {
|
|
999
|
+
LOG_SOCKET_DEBUG("Port " << port << " already held, " << af_label
|
|
1000
|
+
<< " UDP bind declined (error: "
|
|
1001
|
+
<< socket_error_string(error) << ")");
|
|
1002
|
+
} else {
|
|
1003
|
+
LOG_SOCKET_ERROR("Failed to bind " << af_label << " UDP socket to port " << port
|
|
1004
|
+
<< " (error: " << socket_error_string(error) << ")");
|
|
1005
|
+
}
|
|
970
1006
|
return fail_socket(udp_socket, error);
|
|
971
1007
|
}
|
|
972
1008
|
}
|
|
@@ -52,6 +52,26 @@ enum class AddressFamily {
|
|
|
52
52
|
DualStack // IPv6 socket with IPv4 support (default)
|
|
53
53
|
};
|
|
54
54
|
|
|
55
|
+
/**
|
|
56
|
+
* Whether a UDP bind is allowed to land on a port another socket already holds.
|
|
57
|
+
*
|
|
58
|
+
* On a datagram socket SO_REUSEADDR does not mean what it means for TCP. There is
|
|
59
|
+
* no TIME_WAIT to wait out, so what the option actually buys is the right to bind
|
|
60
|
+
* a port somebody else is already serving — and then the kernel hands each arriving
|
|
61
|
+
* datagram to one of the two, by a rule neither of them can see. Both sockets read
|
|
62
|
+
* a fraction of a stream neither of them is meant to share, and nothing reports an
|
|
63
|
+
* error at any point.
|
|
64
|
+
*
|
|
65
|
+
* `Shared` is the historical default and is right for a port only one subsystem
|
|
66
|
+
* ever wants. `Exclusive` is for a port that is *also* plausibly somebody else's —
|
|
67
|
+
* the BitTorrent uTP mux, which by protocol wants the same number the DHT is
|
|
68
|
+
* already serving. There the bind must fail loudly so the caller can move.
|
|
69
|
+
*/
|
|
70
|
+
enum class UdpPortMode {
|
|
71
|
+
Shared, ///< May bind over an existing holder (SO_REUSEADDR).
|
|
72
|
+
Exclusive ///< Bind fails if the port is taken, and refuses later sharers.
|
|
73
|
+
};
|
|
74
|
+
|
|
55
75
|
/**
|
|
56
76
|
* Whether a socket bound with `af` can send to an address of the given family
|
|
57
77
|
* at all.
|
|
@@ -240,10 +260,12 @@ int send_tcp_string(socket_t socket, const std::string& data);
|
|
|
240
260
|
* @param port The port to bind to (0 for any available port)
|
|
241
261
|
* @param bind_address The interface IP address to bind to (empty for all interfaces)
|
|
242
262
|
* @param af Address family (DualStack by default)
|
|
263
|
+
* @param mode Whether the port may be shared with a socket that already holds it
|
|
243
264
|
* @return UDP socket handle, or RATS_INVALID_SOCKET on error
|
|
244
265
|
*/
|
|
245
266
|
socket_t create_udp_socket(int port = 0, const std::string& bind_address = "",
|
|
246
|
-
AddressFamily af = AddressFamily::DualStack
|
|
267
|
+
AddressFamily af = AddressFamily::DualStack,
|
|
268
|
+
UdpPortMode mode = UdpPortMode::Shared);
|
|
247
269
|
|
|
248
270
|
/**
|
|
249
271
|
* Send UDP data to a destination host and port
|