1
#pragma once
2

            
3
#include <cstdint>
4
#include <functional>
5

            
6
#include "envoy/config/core/v3/protocol.pb.h"
7
#include "envoy/network/connection.h"
8

            
9
#include "source/common/http/http2/codec_stats.h"
10
#include "source/common/http/status.h"
11

            
12
#ifdef ENVOY_NGHTTP2
13
#include "nghttp2/nghttp2.h"
14
#endif
15

            
16
namespace Envoy {
17
namespace Http {
18
namespace Http2 {
19

            
20
// Frame types as inherited from nghttp2 and preserved for oghttp2
21
enum FrameType {
22
  OGHTTP2_DATA_FRAME_TYPE,
23
  OGHTTP2_HEADERS_FRAME_TYPE,
24
  OGHTTP2_PRIORITY_FRAME_TYPE,
25
  OGHTTP2_RST_STREAM_FRAME_TYPE,
26
  OGHTTP2_SETTINGS_FRAME_TYPE,
27
  OGHTTP2_PUSH_PROMISE_FRAME_TYPE,
28
  OGHTTP2_PING_FRAME_TYPE,
29
  OGHTTP2_GOAWAY_FRAME_TYPE,
30
  OGHTTP2_WINDOW_UPDATE_FRAME_TYPE,
31
  OGHTTP2_CONTINUATION_FRAME_TYPE,
32
};
33

            
34
//  Class for detecting abusive peers and validating additional constraints imposed by Envoy.
35
//  This class does not check protocol compliance with the H/2 standard, as this is checked by
36
//  protocol framer/codec. Currently implemented constraints:
37
//  1. detection of control frame (i.e. PING) initiated floods.
38
//  2. detection of outbound DATA or HEADER frame floods.
39
//  4. zero length, PRIORITY and WINDOW_UPDATE floods.
40

            
41
class ProtocolConstraints : public ScopeTrackedObject {
42
public:
43
  using ReleasorProc = std::function<void()>;
44

            
45
  explicit ProtocolConstraints(CodecStats& stats,
46
                               const envoy::config::core::v3::Http2ProtocolOptions& http2_options);
47

            
48
  // Return ok status if no protocol constraints were violated.
49
  // Return error status of the first detected violation. Subsequent violations of constraints
50
  // do not reset the error status or increment stat counters.
51
15
  const Status& status() const { return status_; }
52

            
53
  // Increment counters of pending (buffered for sending to the peer) outbound frames.
54
  // If the `is_outbound_flood_monitored_control_frame` is false only the counter for all frame
55
  // types is incremented. If the `is_outbound_flood_monitored_control_frame` is true, both the
56
  // control frame and all frame types counters are incremented.
57
  // Returns callable for decrementing frame counters when frames was successfully written to
58
  // the underlying transport socket object.
59
  // To check if outbound frame constraints were violated call the `status()` method.
60
  // TODO(yanavlasov): return StatusOr<ReleasorProc> when flood checks are implemented for both
61
  // directions.
62
  ReleasorProc incrementOutboundFrameCount(bool is_outbound_flood_monitored_control_frame);
63

            
64
  // Track received frames of various types.
65
  // Return an error status if inbound frame constraints were violated.
66
  Status trackInboundFrame(uint8_t type, bool end_stream, bool is_empty);
67
  // Increment the number of DATA frames sent to the peer.
68
528509
  void incrementOutboundDataFrameCount() { ++outbound_data_frames_; }
69
148842
  void incrementOpenedStreamCount() { ++opened_streams_; }
70

            
71
  Status checkOutboundFrameLimits();
72

            
73
  // ScopeTrackedObject
74
  void dumpState(std::ostream& os, int indent_level) const override;
75

            
76
private:
77
  void releaseOutboundFrame();
78
  void releaseOutboundControlFrame();
79
  Status checkInboundFrameLimits();
80

            
81
  Status status_;
82
  CodecStats& stats_;
83
  // This counter keeps track of the number of outbound frames of all types (these that were
84
  // buffered in the underlying connection but not yet written into the socket). If this counter
85
  // exceeds the `max_outbound_frames_' value the connection is terminated.
86
  uint32_t outbound_frames_ = 0;
87
  // Maximum number of outbound frames. Initialized from corresponding http2_protocol_options.
88
  // Default value is 10000.
89
  const uint32_t max_outbound_frames_;
90
  ReleasorProc frame_buffer_releasor_;
91

            
92
  // This counter keeps track of the number of outbound frames of types PING, SETTINGS and
93
  // RST_STREAM (these that were buffered in the underlying connection but not yet written into the
94
  // socket). If this counter exceeds the `max_outbound_control_frames_' value the connection is
95
  // terminated.
96
  uint32_t outbound_control_frames_ = 0;
97
  // Maximum number of outbound frames of types PING, SETTINGS and RST_STREAM. Initialized from
98
  // corresponding http2_protocol_options. Default value is 1000.
99
  const uint32_t max_outbound_control_frames_;
100
  ReleasorProc control_frame_buffer_releasor_;
101

            
102
  // This counter keeps track of the number of consecutive inbound frames of types HEADERS,
103
  // CONTINUATION and DATA with an empty payload and no end stream flag. If this counter exceeds
104
  // the `max_consecutive_inbound_frames_with_empty_payload_` value the connection is terminated.
105
  uint32_t consecutive_inbound_frames_with_empty_payload_ = 0;
106
  // Maximum number of consecutive inbound frames of types HEADERS, CONTINUATION and DATA without
107
  // a payload. Initialized from corresponding http2_protocol_options. Default value is 1.
108
  const uint32_t max_consecutive_inbound_frames_with_empty_payload_;
109

            
110
  // This counter keeps track of the number of opened streams.
111
  // For downstream connection this is incremented when the first HEADERS frame with the new
112
  // stream ID is received from the client.
113
  // For upstream connections this is incremented when the first HEADERS frame with the new
114
  // stream ID is sent to the upstream server.
115
  uint32_t opened_streams_ = 0;
116
  // This counter keeps track of the number of inbound PRIORITY frames. If this counter exceeds
117
  // the value calculated using this formula:
118
  //
119
  //     max_inbound_priority_frames_per_stream_ * (1 + inbound_streams_)
120
  //
121
  // the connection is terminated.
122
  uint64_t inbound_priority_frames_ = 0;
123
  // Maximum number of inbound PRIORITY frames per stream. Initialized from corresponding
124
  // http2_protocol_options. Default value is 100.
125
  const uint32_t max_inbound_priority_frames_per_stream_;
126

            
127
  // This counter keeps track of the number of inbound WINDOW_UPDATE frames. If this counter exceeds
128
  // the value calculated using this formula:
129
  //
130
  //     1 + 2 * (inbound_streams_ +
131
  //              max_inbound_window_update_frames_per_data_frame_sent_ * outbound_data_frames_)
132
  //
133
  // the connection is terminated.
134
  uint64_t inbound_window_update_frames_ = 0;
135
  // This counter keeps track of the number of outbound DATA frames.
136
  uint64_t outbound_data_frames_ = 0;
137
  // Maximum number of inbound WINDOW_UPDATE frames per outbound DATA frame sent. Initialized
138
  // from corresponding http2_protocol_options. Default value is 10.
139
  const uint32_t max_inbound_window_update_frames_per_data_frame_sent_;
140
};
141

            
142
} // namespace Http2
143
} // namespace Http
144
} // namespace Envoy