1
#include "source/common/http/http2/protocol_constraints.h"
2

            
3
#include "source/common/common/assert.h"
4
#include "source/common/common/dump_state_utils.h"
5

            
6
namespace Envoy {
7
namespace Http {
8
namespace Http2 {
9

            
10
ProtocolConstraints::ProtocolConstraints(
11
    CodecStats& stats, const envoy::config::core::v3::Http2ProtocolOptions& http2_options)
12
30695
    : stats_(stats), max_outbound_frames_(http2_options.max_outbound_frames().value()),
13
740748
      frame_buffer_releasor_([this]() { releaseOutboundFrame(); }),
14
30695
      max_outbound_control_frames_(http2_options.max_outbound_control_frames().value()),
15
47715
      control_frame_buffer_releasor_([this]() { releaseOutboundControlFrame(); }),
16
      max_consecutive_inbound_frames_with_empty_payload_(
17
30695
          http2_options.max_consecutive_inbound_frames_with_empty_payload().value()),
18
      max_inbound_priority_frames_per_stream_(
19
30695
          http2_options.max_inbound_priority_frames_per_stream().value()),
20
      max_inbound_window_update_frames_per_data_frame_sent_(
21
30695
          http2_options.max_inbound_window_update_frames_per_data_frame_sent().value()) {}
22

            
23
ProtocolConstraints::ReleasorProc
24
787440
ProtocolConstraints::incrementOutboundFrameCount(bool is_outbound_flood_monitored_control_frame) {
25
787440
  ++outbound_frames_;
26
787440
  stats_.outbound_frames_active_.set(outbound_frames_);
27
787440
  if (is_outbound_flood_monitored_control_frame) {
28
46699
    ++outbound_control_frames_;
29
46699
    stats_.outbound_control_frames_active_.set(outbound_control_frames_);
30
46699
  }
31
787440
  return is_outbound_flood_monitored_control_frame ? control_frame_buffer_releasor_
32
787440
                                                   : frame_buffer_releasor_;
33
787440
}
34

            
35
787423
void ProtocolConstraints::releaseOutboundFrame() {
36
787423
  ASSERT(outbound_frames_ >= 1);
37
787423
  --outbound_frames_;
38
787423
  stats_.outbound_frames_active_.set(outbound_frames_);
39
787423
}
40

            
41
46689
void ProtocolConstraints::releaseOutboundControlFrame() {
42
46689
  ASSERT(outbound_control_frames_ >= 1);
43
46689
  --outbound_control_frames_;
44
46689
  stats_.outbound_control_frames_active_.set(outbound_control_frames_);
45
46689
  releaseOutboundFrame();
46
46689
}
47

            
48
1761714
Status ProtocolConstraints::checkOutboundFrameLimits() {
49
  // Stop checking for further violations after the first failure.
50
1761714
  if (!status_.ok()) {
51
268
    return status_;
52
268
  }
53

            
54
1761446
  if (outbound_frames_ > max_outbound_frames_) {
55
59
    stats_.outbound_flood_.inc();
56
59
    return status_ = bufferFloodError("Too many frames in the outbound queue.");
57
59
  }
58
1761387
  if (outbound_control_frames_ > max_outbound_control_frames_) {
59
24
    stats_.outbound_control_flood_.inc();
60
24
    return status_ = bufferFloodError("Too many control frames in the outbound queue.");
61
24
  }
62
1761363
  return okStatus();
63
1761387
}
64

            
65
617409
Status ProtocolConstraints::trackInboundFrame(uint8_t type, bool end_stream, bool is_empty) {
66
617409
  switch (type) {
67
139841
  case OGHTTP2_HEADERS_FRAME_TYPE:
68
148437
  case OGHTTP2_CONTINUATION_FRAME_TYPE:
69
499397
  case OGHTTP2_DATA_FRAME_TYPE:
70
    // Track frames with an empty payload and no end stream flag.
71
499397
    if (is_empty && !end_stream) {
72
42
      consecutive_inbound_frames_with_empty_payload_++;
73
499368
    } else {
74
499355
      consecutive_inbound_frames_with_empty_payload_ = 0;
75
499355
    }
76
499397
    break;
77
2418
  case OGHTTP2_PRIORITY_FRAME_TYPE:
78
2418
    inbound_priority_frames_++;
79
2418
    break;
80
40305
  case OGHTTP2_WINDOW_UPDATE_FRAME_TYPE:
81
40305
    inbound_window_update_frames_++;
82
40305
    break;
83
75302
  default:
84
75302
    break;
85
617409
  }
86

            
87
617408
  status_.Update(checkInboundFrameLimits());
88
617408
  return status_;
89
617409
}
90

            
91
617402
Status ProtocolConstraints::checkInboundFrameLimits() {
92
  // Stop checking for further violations after the first failure.
93
617402
  if (!status_.ok()) {
94
    return status_;
95
  }
96

            
97
617402
  if (consecutive_inbound_frames_with_empty_payload_ >
98
617402
      max_consecutive_inbound_frames_with_empty_payload_) {
99
18
    stats_.inbound_empty_frames_flood_.inc();
100
18
    return inboundFramesWithEmptyPayloadError();
101
18
  }
102

            
103
617384
  if (inbound_priority_frames_ >
104
617384
      static_cast<uint64_t>(max_inbound_priority_frames_per_stream_) * (1 + opened_streams_)) {
105
13
    stats_.inbound_priority_frames_flood_.inc();
106
13
    return bufferFloodError("Too many PRIORITY frames");
107
13
  }
108

            
109
617371
  if (inbound_window_update_frames_ >
110
617371
      5 + 2 * (opened_streams_ +
111
617371
               max_inbound_window_update_frames_per_data_frame_sent_ * outbound_data_frames_)) {
112
7
    stats_.inbound_window_update_frames_flood_.inc();
113
7
    return bufferFloodError("Too many WINDOW_UPDATE frames");
114
7
  }
115

            
116
617364
  return okStatus();
117
617371
}
118

            
119
13
void ProtocolConstraints::dumpState(std::ostream& os, int indent_level) const {
120
13
  const char* spaces = spacesForLevel(indent_level);
121

            
122
13
  os << spaces << "ProtocolConstraints " << this << DUMP_MEMBER(outbound_frames_)
123
13
     << DUMP_MEMBER(max_outbound_frames_) << DUMP_MEMBER(outbound_control_frames_)
124
13
     << DUMP_MEMBER(max_outbound_control_frames_)
125
13
     << DUMP_MEMBER(consecutive_inbound_frames_with_empty_payload_)
126
13
     << DUMP_MEMBER(max_consecutive_inbound_frames_with_empty_payload_)
127
13
     << DUMP_MEMBER(opened_streams_) << DUMP_MEMBER(inbound_priority_frames_)
128
13
     << DUMP_MEMBER(max_inbound_priority_frames_per_stream_)
129
13
     << DUMP_MEMBER(inbound_window_update_frames_) << DUMP_MEMBER(outbound_data_frames_)
130
13
     << DUMP_MEMBER(max_inbound_window_update_frames_per_data_frame_sent_) << '\n';
131
13
}
132

            
133
} // namespace Http2
134
} // namespace Http
135
} // namespace Envoy