1 /*
2  * Copyright (c) Facebook, Inc. and its affiliates.
3  *
4  * This source code is licensed under the MIT license found in the
5  * LICENSE file in the root directory of this source tree.
6  *
7  */
8 
9 #pragma once
10 
11 #include <folly/Optional.h>
12 #include <folly/functional/Invoke.h>
13 #include <folly/io/async/EventBase.h>
14 #include <string>
15 
16 #include <quic/QuicConstants.h>
17 #include <quic/QuicException.h>
18 
19 namespace quic {
20 
21 /* Interface for Transport level stats per VIP (server)
22  * Quic Transport expects applications to instantiate this per thread (and
23  * do necessary aggregation at the application level).
24  *
25  * NOTE: since several of these methods are called for every single packets,
26  * and every single connection, it is extremely important to not do any
27  * blocking call in any of the implementation of these methods.
28  */
29 class QuicTransportStatsCallback {
30  public:
31   enum class PacketDropReason : uint8_t {
32     NONE,
33     CONNECTION_NOT_FOUND,
34     DECRYPTION_ERROR,
35     INVALID_PACKET,
36     PARSE_ERROR,
37     PEER_ADDRESS_CHANGE,
38     PROTOCOL_VIOLATION,
39     ROUTING_ERROR_WRONG_HOST,
40     SERVER_STATE_CLOSED,
41     TRANSPORT_PARAMETER_ERROR,
42     WORKER_NOT_INITIALIZED,
43     SERVER_SHUTDOWN,
44     INITIAL_CONNID_SMALL,
45     CANNOT_MAKE_TRANSPORT,
46     UDP_TRUNCATED,
47     CLIENT_STATE_CLOSED,
48     CLIENT_SHUTDOWN,
49     INVALID_SRC_PORT,
50     // NOTE: MAX should always be at the end
51     MAX
52   };
53 
54   enum class SocketErrorType : uint8_t {
55     AGAIN,
56     INVAL,
57     MSGSIZE,
58     NOBUFS,
59     NOMEM,
60     OTHER,
61     MAX
62   };
63 
64   enum class TransportKnobType : uint8_t {
65     ZERO_PMTU_BLACKHOLE,
66     FORCIBLY_SET_UDP_PAYLOAD_SIZE,
67     UNKNOWN,
68     MAX
69   };
70 
71   virtual ~QuicTransportStatsCallback() = default;
72 
73   // packet level metrics
74   virtual void onPacketReceived() = 0;
75 
76   virtual void onDuplicatedPacketReceived() = 0;
77 
78   virtual void onOutOfOrderPacketReceived() = 0;
79 
80   virtual void onPacketProcessed() = 0;
81 
82   virtual void onPacketSent() = 0;
83 
84   virtual void onPacketRetransmission() = 0;
85 
86   virtual void onPacketLoss() = 0;
87 
88   virtual void onPacketSpuriousLoss() = 0;
89 
90   virtual void onPersistentCongestion() = 0;
91 
92   virtual void onPacketDropped(PacketDropReason reason) = 0;
93 
94   virtual void onPacketForwarded() = 0;
95 
96   virtual void onForwardedPacketReceived() = 0;
97 
98   virtual void onForwardedPacketProcessed() = 0;
99 
100   virtual void onClientInitialReceived(QuicVersion version) = 0;
101 
102   virtual void onConnectionRateLimited() = 0;
103 
104   // connection level metrics:
105   virtual void onNewConnection() = 0;
106 
107   virtual void onConnectionClose(
108       folly::Optional<QuicErrorCode> code = folly::none) = 0;
109 
110   // stream level metrics
111   virtual void onNewQuicStream() = 0;
112 
113   virtual void onQuicStreamClosed() = 0;
114 
115   virtual void onQuicStreamReset(QuicErrorCode code) = 0;
116 
117   // flow control / congestion control / loss recovery related metrics
118   virtual void onConnFlowControlUpdate() = 0;
119 
120   virtual void onConnFlowControlBlocked() = 0;
121 
122   virtual void onStatelessReset() = 0;
123 
124   virtual void onStreamFlowControlUpdate() = 0;
125 
126   virtual void onStreamFlowControlBlocked() = 0;
127 
128   virtual void onCwndBlocked() = 0;
129 
130   virtual void onNewCongestionController(CongestionControlType type) = 0;
131 
132   // retransmission timeout counter
133   virtual void onPTO() = 0;
134 
135   // metrics to track bytes read from / written to wire
136   virtual void onRead(size_t bufSize) = 0;
137 
138   virtual void onWrite(size_t bufSize) = 0;
139 
140   virtual void onUDPSocketWriteError(SocketErrorType errorType) = 0;
141 
142   virtual void onConnectionD6DStarted() = 0;
143 
144   virtual void onConnectionPMTURaised() = 0;
145 
146   virtual void onConnectionPMTUBlackholeDetected() = 0;
147 
148   virtual void onConnectionPMTUUpperBoundDetected() = 0;
149 
150   virtual void onTransportKnobApplied(TransportKnobType knobType) = 0;
151 
152   virtual void onTransportKnobError(TransportKnobType knobType) = 0;
153 
154   virtual void onServerUnfinishedHandshake() = 0;
155 
156   virtual void onZeroRttBuffered() = 0;
157 
158   virtual void onZeroRttBufferedPruned() = 0;
159 
160   virtual void onZeroRttAccepted() = 0;
161 
162   virtual void onZeroRttRejected() = 0;
163 
toString(PacketDropReason reason)164   static const char* toString(PacketDropReason reason) {
165     switch (reason) {
166       case PacketDropReason::NONE:
167         return "NONE";
168       case PacketDropReason::CONNECTION_NOT_FOUND:
169         return "CONNECTION_NOT_FOUND";
170       case PacketDropReason::DECRYPTION_ERROR:
171         return "DECRYPTION_ERROR";
172       case PacketDropReason::INVALID_PACKET:
173         return "INVALID_PACKET";
174       case PacketDropReason::PARSE_ERROR:
175         return "PARSE_ERROR";
176       case PacketDropReason::PEER_ADDRESS_CHANGE:
177         return "PEER_ADDRESS_CHANGE";
178       case PacketDropReason::PROTOCOL_VIOLATION:
179         return "PROTOCOL_VIOLATION";
180       case PacketDropReason::ROUTING_ERROR_WRONG_HOST:
181         return "ROUTING_ERROR_WRONG_HOST";
182       case PacketDropReason::SERVER_STATE_CLOSED:
183         return "SERVER_STATE_CLOSED";
184       case PacketDropReason::TRANSPORT_PARAMETER_ERROR:
185         return "TRANSPORT_PARAMETER_ERROR";
186       case PacketDropReason::WORKER_NOT_INITIALIZED:
187         return "WORKER_NOT_INITIALIZED";
188       case PacketDropReason::SERVER_SHUTDOWN:
189         return "SERVER_SHUTDOWN";
190       case PacketDropReason::INITIAL_CONNID_SMALL:
191         return "INITIAL_CONNID_SMALL";
192       case PacketDropReason::CANNOT_MAKE_TRANSPORT:
193         return "CANNOT_MAKE_TRANSPORT";
194       case PacketDropReason::UDP_TRUNCATED:
195         return "UDP_TRUNCATED";
196       case PacketDropReason::CLIENT_STATE_CLOSED:
197         return "CLIENT_STATE_CLOSED";
198       case PacketDropReason::CLIENT_SHUTDOWN:
199         return "CLIENT_SHUTDOWN";
200       case PacketDropReason::INVALID_SRC_PORT:
201         return "INVALID_SRC_PORT";
202       case PacketDropReason::MAX:
203         return "MAX";
204       default:
205         throw std::runtime_error("Undefined PacketDropReason passed");
206     }
207   }
208 
toString(SocketErrorType errorType)209   static const char* toString(SocketErrorType errorType) {
210     switch (errorType) {
211       case SocketErrorType::AGAIN:
212         return "AGAIN";
213       case SocketErrorType::INVAL:
214         return "INVAL";
215       case SocketErrorType::MSGSIZE:
216         return "MSGSIZE";
217       case SocketErrorType::NOBUFS:
218         return "NOBUFS";
219       case SocketErrorType::NOMEM:
220         return "NOMEM";
221       case SocketErrorType::OTHER:
222         return "Other";
223       default:
224         throw std::runtime_error("Undefined SocketErrorType");
225     }
226   }
227 
errnoToSocketErrorType(int err)228   static SocketErrorType errnoToSocketErrorType(int err) {
229     switch (err) {
230       case EAGAIN:
231         return SocketErrorType::AGAIN;
232       case EINVAL:
233         return SocketErrorType::INVAL;
234       case EMSGSIZE:
235         return SocketErrorType::MSGSIZE;
236       case ENOBUFS:
237         return SocketErrorType::NOBUFS;
238       case ENOMEM:
239         return SocketErrorType::NOMEM;
240       default:
241         return SocketErrorType::OTHER;
242     }
243   }
244 
toString(TransportKnobType knobType)245   static const char* toString(TransportKnobType knobType) {
246     switch (knobType) {
247       case TransportKnobType::ZERO_PMTU_BLACKHOLE:
248         return "ZERO_PMTU_BLACKHOLE";
249       case TransportKnobType::FORCIBLY_SET_UDP_PAYLOAD_SIZE:
250         return "FORCIBLY_SET_UDP_PAYLOAD_SIZE";
251       case TransportKnobType::UNKNOWN:
252         return "UNKNOWN";
253       case TransportKnobType::MAX:
254         return "MAX";
255       default:
256         throw std::runtime_error("Undefined TransportKnobType passed");
257     }
258   }
259 
paramIdToTransportKnobType(uint64_t paramId)260   static TransportKnobType paramIdToTransportKnobType(uint64_t paramId) {
261     switch (paramId) {
262       case static_cast<uint64_t>(
263           TransportKnobParamId::ZERO_PMTU_BLACKHOLE_DETECTION):
264         return TransportKnobType::ZERO_PMTU_BLACKHOLE;
265       case static_cast<uint64_t>(
266           TransportKnobParamId::FORCIBLY_SET_UDP_PAYLOAD_SIZE):
267         return TransportKnobType::FORCIBLY_SET_UDP_PAYLOAD_SIZE;
268       default:
269         return TransportKnobType::UNKNOWN;
270     }
271   }
272 };
273 
274 /**
275  * Interface to create QuicTransportStatsCallback instance.
276  * If application supplies the implementation of this factory, the transport
277  * calls 'make' during its initialization _for each worker_.
278  * Further, 'make' is called from the worker's eventbase so that it is
279  * convenient for application to specify actions such as scheduling per thread
280  * aggregation
281  */
282 class QuicTransportStatsCallbackFactory {
283  public:
284   virtual ~QuicTransportStatsCallbackFactory() = default;
285 
286   virtual std::unique_ptr<QuicTransportStatsCallback> make() = 0;
287 };
288 
289 #define QUIC_STATS(statsCallback, method, ...)                              \
290   if (statsCallback) {                                                      \
291     folly::invoke(                                                          \
292         &QuicTransportStatsCallback::method, statsCallback, ##__VA_ARGS__); \
293   }
294 
295 #define QUIC_STATS_FOR_EACH(iterBegin, iterEnd, statsCallback, method, ...)   \
296   if (statsCallback) {                                                        \
297     std::for_each(iterBegin, iterEnd, [&](const auto&) {                      \
298       folly::invoke(                                                          \
299           &QuicTransportStatsCallback::method, statsCallback, ##__VA_ARGS__); \
300     });                                                                       \
301   }
302 } // namespace quic
303