Line data Source code
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 : : stats_(stats), max_outbound_frames_(http2_options.max_outbound_frames().value()), 13 44839 : frame_buffer_releasor_([this]() { releaseOutboundFrame(); }), 14 : max_outbound_control_frames_(http2_options.max_outbound_control_frames().value()), 15 2553 : control_frame_buffer_releasor_([this]() { releaseOutboundControlFrame(); }), 16 : max_consecutive_inbound_frames_with_empty_payload_( 17 : http2_options.max_consecutive_inbound_frames_with_empty_payload().value()), 18 : max_inbound_priority_frames_per_stream_( 19 : http2_options.max_inbound_priority_frames_per_stream().value()), 20 : max_inbound_window_update_frames_per_data_frame_sent_( 21 2860 : http2_options.max_inbound_window_update_frames_per_data_frame_sent().value()) {} 22 : 23 : ProtocolConstraints::ReleasorProc 24 47392 : ProtocolConstraints::incrementOutboundFrameCount(bool is_outbound_flood_monitored_control_frame) { 25 47392 : ++outbound_frames_; 26 47392 : stats_.outbound_frames_active_.set(outbound_frames_); 27 47392 : if (is_outbound_flood_monitored_control_frame) { 28 2553 : ++outbound_control_frames_; 29 2553 : stats_.outbound_control_frames_active_.set(outbound_control_frames_); 30 2553 : } 31 47392 : return is_outbound_flood_monitored_control_frame ? control_frame_buffer_releasor_ 32 47392 : : frame_buffer_releasor_; 33 47392 : } 34 : 35 47392 : void ProtocolConstraints::releaseOutboundFrame() { 36 47392 : ASSERT(outbound_frames_ >= 1); 37 47392 : --outbound_frames_; 38 47392 : stats_.outbound_frames_active_.set(outbound_frames_); 39 47392 : } 40 : 41 2553 : void ProtocolConstraints::releaseOutboundControlFrame() { 42 2553 : ASSERT(outbound_control_frames_ >= 1); 43 2553 : --outbound_control_frames_; 44 2553 : stats_.outbound_control_frames_active_.set(outbound_control_frames_); 45 2553 : releaseOutboundFrame(); 46 2553 : } 47 : 48 96972 : Status ProtocolConstraints::checkOutboundFrameLimits() { 49 : // Stop checking for further violations after the first failure. 50 96972 : if (!status_.ok()) { 51 0 : return status_; 52 0 : } 53 : 54 96972 : if (outbound_frames_ > max_outbound_frames_) { 55 0 : stats_.outbound_flood_.inc(); 56 0 : return status_ = bufferFloodError("Too many frames in the outbound queue."); 57 0 : } 58 96972 : if (outbound_control_frames_ > max_outbound_control_frames_) { 59 0 : stats_.outbound_control_flood_.inc(); 60 0 : return status_ = bufferFloodError("Too many control frames in the outbound queue."); 61 0 : } 62 96972 : return okStatus(); 63 96972 : } 64 : 65 : Status ProtocolConstraints::trackInboundFrames(size_t length, uint8_t type, uint8_t flags, 66 44919 : uint32_t padding_length) { 67 44919 : switch (type) { 68 1764 : case NGHTTP2_HEADERS: 69 1771 : case NGHTTP2_CONTINUATION: 70 23952 : case NGHTTP2_DATA: 71 : // Track frames with an empty payload and no end stream flag. 72 23952 : if (length - padding_length == 0 && !(flags & NGHTTP2_FLAG_END_STREAM)) { 73 1 : consecutive_inbound_frames_with_empty_payload_++; 74 23951 : } else { 75 23951 : consecutive_inbound_frames_with_empty_payload_ = 0; 76 23951 : } 77 23952 : break; 78 21 : case NGHTTP2_PRIORITY: 79 21 : inbound_priority_frames_++; 80 21 : break; 81 16620 : case NGHTTP2_WINDOW_UPDATE: 82 16620 : inbound_window_update_frames_++; 83 16620 : break; 84 4326 : default: 85 4326 : break; 86 44919 : } 87 : 88 44919 : status_.Update(checkInboundFrameLimits()); 89 44919 : return status_; 90 44919 : } 91 : 92 44919 : Status ProtocolConstraints::checkInboundFrameLimits() { 93 : // Stop checking for further violations after the first failure. 94 44919 : if (!status_.ok()) { 95 0 : return status_; 96 0 : } 97 : 98 44919 : if (consecutive_inbound_frames_with_empty_payload_ > 99 44919 : max_consecutive_inbound_frames_with_empty_payload_) { 100 0 : stats_.inbound_empty_frames_flood_.inc(); 101 0 : return inboundFramesWithEmptyPayloadError(); 102 0 : } 103 : 104 44919 : if (inbound_priority_frames_ > 105 44919 : static_cast<uint64_t>(max_inbound_priority_frames_per_stream_) * (1 + opened_streams_)) { 106 0 : stats_.inbound_priority_frames_flood_.inc(); 107 0 : return bufferFloodError("Too many PRIORITY frames"); 108 0 : } 109 : 110 44919 : if (inbound_window_update_frames_ > 111 44919 : 5 + 2 * (opened_streams_ + 112 44919 : max_inbound_window_update_frames_per_data_frame_sent_ * outbound_data_frames_)) { 113 0 : stats_.inbound_window_update_frames_flood_.inc(); 114 0 : return bufferFloodError("Too many WINDOW_UPDATE frames"); 115 0 : } 116 : 117 44919 : return okStatus(); 118 44919 : } 119 : 120 0 : void ProtocolConstraints::dumpState(std::ostream& os, int indent_level) const { 121 0 : const char* spaces = spacesForLevel(indent_level); 122 : 123 0 : os << spaces << "ProtocolConstraints " << this << DUMP_MEMBER(outbound_frames_) 124 0 : << DUMP_MEMBER(max_outbound_frames_) << DUMP_MEMBER(outbound_control_frames_) 125 0 : << DUMP_MEMBER(max_outbound_control_frames_) 126 0 : << DUMP_MEMBER(consecutive_inbound_frames_with_empty_payload_) 127 0 : << DUMP_MEMBER(max_consecutive_inbound_frames_with_empty_payload_) 128 0 : << DUMP_MEMBER(opened_streams_) << DUMP_MEMBER(inbound_priority_frames_) 129 0 : << DUMP_MEMBER(max_inbound_priority_frames_per_stream_) 130 0 : << DUMP_MEMBER(inbound_window_update_frames_) << DUMP_MEMBER(outbound_data_frames_) 131 0 : << DUMP_MEMBER(max_inbound_window_update_frames_per_data_frame_sent_) << '\n'; 132 0 : } 133 : 134 : } // namespace Http2 135 : } // namespace Http 136 : } // namespace Envoy