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
30435
    : stats_(stats), max_outbound_frames_(http2_options.max_outbound_frames().value()),
13
739982
      frame_buffer_releasor_([this]() { releaseOutboundFrame(); }),
14
30435
      max_outbound_control_frames_(http2_options.max_outbound_control_frames().value()),
15
47430
      control_frame_buffer_releasor_([this]() { releaseOutboundControlFrame(); }),
16
      max_consecutive_inbound_frames_with_empty_payload_(
17
30435
          http2_options.max_consecutive_inbound_frames_with_empty_payload().value()),
18
      max_inbound_priority_frames_per_stream_(
19
30435
          http2_options.max_inbound_priority_frames_per_stream().value()),
20
      max_inbound_window_update_frames_per_data_frame_sent_(
21
30435
          http2_options.max_inbound_window_update_frames_per_data_frame_sent().value()) {}
22

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

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

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

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

            
54
1765793
  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
1765734
  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
1765710
  return okStatus();
63
1765734
}
64

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

            
87
617381
  status_.Update(checkInboundFrameLimits());
88
617381
  return status_;
89
617383
}
90

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

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

            
103
617358
  if (inbound_priority_frames_ >
104
617358
      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
617345
  if (inbound_window_update_frames_ >
110
617345
      5 + 2 * (opened_streams_ +
111
617345
               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
617338
  return okStatus();
117
617345
}
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