diff --git a/BUILD b/BUILD index 50a4cc23ed7..0be13c0df87 100644 --- a/BUILD +++ b/BUILD @@ -4701,6 +4701,7 @@ grpc_cc_library( "//src/core:ext/transport/chttp2/transport/frame_goaway.cc", "//src/core:ext/transport/chttp2/transport/frame_ping.cc", "//src/core:ext/transport/chttp2/transport/frame_rst_stream.cc", + "//src/core:ext/transport/chttp2/transport/frame_security.cc", "//src/core:ext/transport/chttp2/transport/frame_settings.cc", "//src/core:ext/transport/chttp2/transport/frame_window_update.cc", "//src/core:ext/transport/chttp2/transport/parsing.cc", @@ -4715,6 +4716,7 @@ grpc_cc_library( "//src/core:ext/transport/chttp2/transport/frame_goaway.h", "//src/core:ext/transport/chttp2/transport/frame_ping.h", "//src/core:ext/transport/chttp2/transport/frame_rst_stream.h", + "//src/core:ext/transport/chttp2/transport/frame_security.h", "//src/core:ext/transport/chttp2/transport/frame_settings.h", "//src/core:ext/transport/chttp2/transport/frame_window_update.h", "//src/core:ext/transport/chttp2/transport/internal.h", diff --git a/CMakeLists.txt b/CMakeLists.txt index c6eae5b40c0..f56216d4f82 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2317,6 +2317,7 @@ add_library(grpc src/core/ext/transport/chttp2/transport/frame_goaway.cc src/core/ext/transport/chttp2/transport/frame_ping.cc src/core/ext/transport/chttp2/transport/frame_rst_stream.cc + src/core/ext/transport/chttp2/transport/frame_security.cc src/core/ext/transport/chttp2/transport/frame_settings.cc src/core/ext/transport/chttp2/transport/frame_window_update.cc src/core/ext/transport/chttp2/transport/hpack_encoder.cc @@ -3428,6 +3429,7 @@ add_library(grpc_unsecure src/core/ext/transport/chttp2/transport/frame_goaway.cc src/core/ext/transport/chttp2/transport/frame_ping.cc src/core/ext/transport/chttp2/transport/frame_rst_stream.cc + src/core/ext/transport/chttp2/transport/frame_security.cc src/core/ext/transport/chttp2/transport/frame_settings.cc src/core/ext/transport/chttp2/transport/frame_window_update.cc src/core/ext/transport/chttp2/transport/hpack_encoder.cc diff --git a/Makefile b/Makefile index d72c8ab669c..45e2e7e72c0 100644 --- a/Makefile +++ b/Makefile @@ -720,6 +720,7 @@ LIBGRPC_SRC = \ src/core/ext/transport/chttp2/transport/frame_goaway.cc \ src/core/ext/transport/chttp2/transport/frame_ping.cc \ src/core/ext/transport/chttp2/transport/frame_rst_stream.cc \ + src/core/ext/transport/chttp2/transport/frame_security.cc \ src/core/ext/transport/chttp2/transport/frame_settings.cc \ src/core/ext/transport/chttp2/transport/frame_window_update.cc \ src/core/ext/transport/chttp2/transport/hpack_encoder.cc \ diff --git a/Package.swift b/Package.swift index ceb8a6164dd..1ba620eab9a 100644 --- a/Package.swift +++ b/Package.swift @@ -229,6 +229,8 @@ let package = Package( "src/core/ext/transport/chttp2/transport/frame_ping.h", "src/core/ext/transport/chttp2/transport/frame_rst_stream.cc", "src/core/ext/transport/chttp2/transport/frame_rst_stream.h", + "src/core/ext/transport/chttp2/transport/frame_security.cc", + "src/core/ext/transport/chttp2/transport/frame_security.h", "src/core/ext/transport/chttp2/transport/frame_settings.cc", "src/core/ext/transport/chttp2/transport/frame_settings.h", "src/core/ext/transport/chttp2/transport/frame_window_update.cc", diff --git a/build_autogenerated.yaml b/build_autogenerated.yaml index 077cc43958f..b5f26926fea 100644 --- a/build_autogenerated.yaml +++ b/build_autogenerated.yaml @@ -277,6 +277,7 @@ libs: - src/core/ext/transport/chttp2/transport/frame_goaway.h - src/core/ext/transport/chttp2/transport/frame_ping.h - src/core/ext/transport/chttp2/transport/frame_rst_stream.h + - src/core/ext/transport/chttp2/transport/frame_security.h - src/core/ext/transport/chttp2/transport/frame_settings.h - src/core/ext/transport/chttp2/transport/frame_window_update.h - src/core/ext/transport/chttp2/transport/hpack_constants.h @@ -1332,6 +1333,7 @@ libs: - src/core/ext/transport/chttp2/transport/frame_goaway.cc - src/core/ext/transport/chttp2/transport/frame_ping.cc - src/core/ext/transport/chttp2/transport/frame_rst_stream.cc + - src/core/ext/transport/chttp2/transport/frame_security.cc - src/core/ext/transport/chttp2/transport/frame_settings.cc - src/core/ext/transport/chttp2/transport/frame_window_update.cc - src/core/ext/transport/chttp2/transport/hpack_encoder.cc @@ -2313,6 +2315,7 @@ libs: - src/core/ext/transport/chttp2/transport/frame_goaway.h - src/core/ext/transport/chttp2/transport/frame_ping.h - src/core/ext/transport/chttp2/transport/frame_rst_stream.h + - src/core/ext/transport/chttp2/transport/frame_security.h - src/core/ext/transport/chttp2/transport/frame_settings.h - src/core/ext/transport/chttp2/transport/frame_window_update.h - src/core/ext/transport/chttp2/transport/hpack_constants.h @@ -2796,6 +2799,7 @@ libs: - src/core/ext/transport/chttp2/transport/frame_goaway.cc - src/core/ext/transport/chttp2/transport/frame_ping.cc - src/core/ext/transport/chttp2/transport/frame_rst_stream.cc + - src/core/ext/transport/chttp2/transport/frame_security.cc - src/core/ext/transport/chttp2/transport/frame_settings.cc - src/core/ext/transport/chttp2/transport/frame_window_update.cc - src/core/ext/transport/chttp2/transport/hpack_encoder.cc diff --git a/config.m4 b/config.m4 index 8e8e92c9110..f8b38adf606 100644 --- a/config.m4 +++ b/config.m4 @@ -95,6 +95,7 @@ if test "$PHP_GRPC" != "no"; then src/core/ext/transport/chttp2/transport/frame_goaway.cc \ src/core/ext/transport/chttp2/transport/frame_ping.cc \ src/core/ext/transport/chttp2/transport/frame_rst_stream.cc \ + src/core/ext/transport/chttp2/transport/frame_security.cc \ src/core/ext/transport/chttp2/transport/frame_settings.cc \ src/core/ext/transport/chttp2/transport/frame_window_update.cc \ src/core/ext/transport/chttp2/transport/hpack_encoder.cc \ diff --git a/config.w32 b/config.w32 index 2cda7c51ba4..7a03656de1d 100644 --- a/config.w32 +++ b/config.w32 @@ -60,6 +60,7 @@ if (PHP_GRPC != "no") { "src\\core\\ext\\transport\\chttp2\\transport\\frame_goaway.cc " + "src\\core\\ext\\transport\\chttp2\\transport\\frame_ping.cc " + "src\\core\\ext\\transport\\chttp2\\transport\\frame_rst_stream.cc " + + "src\\core\\ext\\transport\\chttp2\\transport\\frame_security.cc " + "src\\core\\ext\\transport\\chttp2\\transport\\frame_settings.cc " + "src\\core\\ext\\transport\\chttp2\\transport\\frame_window_update.cc " + "src\\core\\ext\\transport\\chttp2\\transport\\hpack_encoder.cc " + diff --git a/gRPC-C++.podspec b/gRPC-C++.podspec index 879779e4e23..72a7240a5e4 100644 --- a/gRPC-C++.podspec +++ b/gRPC-C++.podspec @@ -325,6 +325,7 @@ Pod::Spec.new do |s| 'src/core/ext/transport/chttp2/transport/frame_goaway.h', 'src/core/ext/transport/chttp2/transport/frame_ping.h', 'src/core/ext/transport/chttp2/transport/frame_rst_stream.h', + 'src/core/ext/transport/chttp2/transport/frame_security.h', 'src/core/ext/transport/chttp2/transport/frame_settings.h', 'src/core/ext/transport/chttp2/transport/frame_window_update.h', 'src/core/ext/transport/chttp2/transport/hpack_constants.h', @@ -1615,6 +1616,7 @@ Pod::Spec.new do |s| 'src/core/ext/transport/chttp2/transport/frame_goaway.h', 'src/core/ext/transport/chttp2/transport/frame_ping.h', 'src/core/ext/transport/chttp2/transport/frame_rst_stream.h', + 'src/core/ext/transport/chttp2/transport/frame_security.h', 'src/core/ext/transport/chttp2/transport/frame_settings.h', 'src/core/ext/transport/chttp2/transport/frame_window_update.h', 'src/core/ext/transport/chttp2/transport/hpack_constants.h', diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec index 626c7d9f815..d8a7b33b9bd 100644 --- a/gRPC-Core.podspec +++ b/gRPC-Core.podspec @@ -349,6 +349,8 @@ Pod::Spec.new do |s| 'src/core/ext/transport/chttp2/transport/frame_ping.h', 'src/core/ext/transport/chttp2/transport/frame_rst_stream.cc', 'src/core/ext/transport/chttp2/transport/frame_rst_stream.h', + 'src/core/ext/transport/chttp2/transport/frame_security.cc', + 'src/core/ext/transport/chttp2/transport/frame_security.h', 'src/core/ext/transport/chttp2/transport/frame_settings.cc', 'src/core/ext/transport/chttp2/transport/frame_settings.h', 'src/core/ext/transport/chttp2/transport/frame_window_update.cc', @@ -2467,6 +2469,7 @@ Pod::Spec.new do |s| 'src/core/ext/transport/chttp2/transport/frame_goaway.h', 'src/core/ext/transport/chttp2/transport/frame_ping.h', 'src/core/ext/transport/chttp2/transport/frame_rst_stream.h', + 'src/core/ext/transport/chttp2/transport/frame_security.h', 'src/core/ext/transport/chttp2/transport/frame_settings.h', 'src/core/ext/transport/chttp2/transport/frame_window_update.h', 'src/core/ext/transport/chttp2/transport/hpack_constants.h', diff --git a/grpc.gemspec b/grpc.gemspec index 1f0331818d0..268da42963b 100644 --- a/grpc.gemspec +++ b/grpc.gemspec @@ -235,6 +235,8 @@ Gem::Specification.new do |s| s.files += %w( src/core/ext/transport/chttp2/transport/frame_ping.h ) s.files += %w( src/core/ext/transport/chttp2/transport/frame_rst_stream.cc ) s.files += %w( src/core/ext/transport/chttp2/transport/frame_rst_stream.h ) + s.files += %w( src/core/ext/transport/chttp2/transport/frame_security.cc ) + s.files += %w( src/core/ext/transport/chttp2/transport/frame_security.h ) s.files += %w( src/core/ext/transport/chttp2/transport/frame_settings.cc ) s.files += %w( src/core/ext/transport/chttp2/transport/frame_settings.h ) s.files += %w( src/core/ext/transport/chttp2/transport/frame_window_update.cc ) diff --git a/include/grpc/impl/channel_arg_names.h b/include/grpc/impl/channel_arg_names.h index aa973c21977..94361a9182e 100644 --- a/include/grpc/impl/channel_arg_names.h +++ b/include/grpc/impl/channel_arg_names.h @@ -400,6 +400,8 @@ "grpc.max_allowed_incoming_connections" /** Configure per-channel or per-server stats plugins. */ #define GRPC_ARG_EXPERIMENTAL_STATS_PLUGINS "grpc.experimental.stats_plugins" +/** If non-zero, allow security frames to be sent and received. */ +#define GRPC_ARG_SECURITY_FRAME_ALLOWED "grpc.security_frame_allowed" /** \} */ #endif /* GRPC_IMPL_CHANNEL_ARG_NAMES_H */ diff --git a/package.xml b/package.xml index cac0a8fc360..b19e583a1b9 100644 --- a/package.xml +++ b/package.xml @@ -217,6 +217,8 @@ + + diff --git a/src/core/ext/transport/chttp2/transport/chttp2_transport.cc b/src/core/ext/transport/chttp2/transport/chttp2_transport.cc index 8b204a1d5b1..d4817366fb9 100644 --- a/src/core/ext/transport/chttp2/transport/chttp2_transport.cc +++ b/src/core/ext/transport/chttp2/transport/chttp2_transport.cc @@ -60,6 +60,7 @@ #include "src/core/ext/transport/chttp2/transport/frame_data.h" #include "src/core/ext/transport/chttp2/transport/frame_goaway.h" #include "src/core/ext/transport/chttp2/transport/frame_rst_stream.h" +#include "src/core/ext/transport/chttp2/transport/frame_security.h" #include "src/core/ext/transport/chttp2/transport/hpack_encoder.h" #include "src/core/ext/transport/chttp2/transport/http2_settings.h" #include "src/core/ext/transport/chttp2/transport/internal.h" @@ -549,6 +550,9 @@ static void read_channel_args(grpc_chttp2_transport* t, t->settings.mutable_local().SetPreferredReceiveCryptoMessageSize(INT_MAX); } + t->settings.mutable_local().SetAllowSecurityFrame( + channel_args.GetBool(GRPC_ARG_SECURITY_FRAME_ALLOWED).value_or(false)); + t->ping_on_rst_stream_percent = grpc_core::Clamp( channel_args.GetInt(GRPC_ARG_HTTP2_PING_ON_RST_STREAM_PERCENT) .value_or(1), @@ -592,8 +596,19 @@ void grpc_chttp2_transport::WriteSecurityFrameLocked( if (data == nullptr) { return; } - // TODO(alishananda): create security frame and append to qbuf, initiate write - grpc_core::Crash("unreachable"); + if (!settings.peer().allow_security_frame()) { + close_transport_locked( + this, + grpc_error_set_int( + GRPC_ERROR_CREATE("Unexpected SECURITY frame scheduled for write"), + grpc_core::StatusIntProperty::kRpcStatus, + GRPC_STATUS_FAILED_PRECONDITION)); + } + grpc_core::SliceBuffer security_frame; + grpc_chttp2_security_frame_create(data->c_slice_buffer(), data->Length(), + security_frame.c_slice_buffer()); + grpc_slice_buffer_move_into(security_frame.c_slice_buffer(), &qbuf); + grpc_chttp2_initiate_write(this, GRPC_CHTTP2_INITIATE_WRITE_SEND_MESSAGE); } using grpc_event_engine::experimental::QueryExtension; @@ -638,13 +653,15 @@ grpc_chttp2_transport::grpc_chttp2_transport( } } - transport_framing_endpoint_extension = QueryExtension< - grpc_core::TransportFramingEndpointExtension>( - grpc_event_engine::experimental::grpc_get_wrapped_event_engine_endpoint( - ep.get())); - if (transport_framing_endpoint_extension != nullptr) { - transport_framing_endpoint_extension->SetSendFrameCallback( - [this](grpc_core::SliceBuffer* data) { WriteSecurityFrame(data); }); + if (channel_args.GetBool(GRPC_ARG_SECURITY_FRAME_ALLOWED).value_or(false)) { + transport_framing_endpoint_extension = QueryExtension< + grpc_core::TransportFramingEndpointExtension>( + grpc_event_engine::experimental::grpc_get_wrapped_event_engine_endpoint( + ep.get())); + if (transport_framing_endpoint_extension != nullptr) { + transport_framing_endpoint_extension->SetSendFrameCallback( + [this](grpc_core::SliceBuffer* data) { WriteSecurityFrame(data); }); + } } CHECK(strlen(GRPC_CHTTP2_CLIENT_CONNECT_STRING) == diff --git a/src/core/ext/transport/chttp2/transport/frame.cc b/src/core/ext/transport/chttp2/transport/frame.cc index cd9093f94a9..2545eb356db 100644 --- a/src/core/ext/transport/chttp2/transport/frame.cc +++ b/src/core/ext/transport/chttp2/transport/frame.cc @@ -38,6 +38,7 @@ constexpr uint8_t kFrameTypePing = 6; constexpr uint8_t kFrameTypeGoaway = 7; constexpr uint8_t kFrameTypeWindowUpdate = 8; constexpr uint8_t kFrameTypePushPromise = 5; +constexpr uint8_t kFrameTypeSecurity = 200; constexpr uint8_t kFlagEndStream = 1; constexpr uint8_t kFlagAck = 1; @@ -122,6 +123,7 @@ class SerializeExtraBytesRequired { size_t operator()(const Http2PingFrame&) { return 8; } size_t operator()(const Http2GoawayFrame&) { return 8; } size_t operator()(const Http2WindowUpdateFrame&) { return 4; } + size_t operator()(const Http2SecurityFrame&) { return 0; } size_t operator()(const Http2UnknownFrame&) { Crash("unreachable"); } }; @@ -218,6 +220,15 @@ class SerializeHeaderAndPayload { out_.AppendIndexed(Slice(std::move(hdr_and_payload))); } + void operator()(Http2SecurityFrame& frame) { + auto hdr = extra_bytes_.TakeFirst(kFrameHeaderSize); + Http2FrameHeader{static_cast(frame.payload.Length()), + kFrameTypeSecurity, 0, 0} + .Serialize(hdr.begin()); + out_.AppendIndexed(Slice(std::move(hdr))); + out_.TakeAndAppend(frame.payload); + } + void operator()(Http2UnknownFrame&) { Crash("unreachable"); } private: @@ -415,6 +426,11 @@ absl::StatusOr ParseWindowUpdateFrame( return Http2WindowUpdateFrame{hdr.stream_id, Read4b(buffer)}; } +absl::StatusOr ParseSecurityFrame( + const Http2FrameHeader& /*hdr*/, SliceBuffer& payload) { + return Http2SecurityFrame{std::move(payload)}; +} + } // namespace void Http2FrameHeader::Serialize(uint8_t* output) const { @@ -447,6 +463,8 @@ std::string Http2FrameTypeString(uint8_t frame_type) { return "WINDOW_UPDATE"; case kFrameTypePing: return "PING"; + case kFrameTypeSecurity: + return "SECURITY"; } return absl::StrCat("UNKNOWN(", frame_type, ")"); } @@ -495,6 +513,8 @@ absl::StatusOr ParseFramePayload(const Http2FrameHeader& hdr, return absl::InternalError( "push promise not supported (and SETTINGS_ENABLE_PUSH explicitly " "disabled)."); + case kFrameTypeSecurity: + return ParseSecurityFrame(hdr, payload); default: return Http2UnknownFrame{}; } diff --git a/src/core/ext/transport/chttp2/transport/frame.h b/src/core/ext/transport/chttp2/transport/frame.h index ccefc083f92..8a91d9e1df0 100644 --- a/src/core/ext/transport/chttp2/transport/frame.h +++ b/src/core/ext/transport/chttp2/transport/frame.h @@ -151,6 +151,15 @@ struct Http2WindowUpdateFrame { } }; +// Security-related frame +struct Http2SecurityFrame { + SliceBuffer payload; + + bool operator==(const Http2SecurityFrame& other) const { + return payload.JoinIntoString() == other.payload.JoinIntoString(); + } +}; + // Type of frame was unknown (and should be ignored) struct Http2UnknownFrame { bool operator==(const Http2UnknownFrame&) const { return true; } @@ -164,7 +173,8 @@ struct Http2UnknownFrame { using Http2Frame = absl::variant; + Http2GoawayFrame, Http2WindowUpdateFrame, Http2SecurityFrame, + Http2UnknownFrame>; /////////////////////////////////////////////////////////////////////////////// // Frame header diff --git a/src/core/ext/transport/chttp2/transport/frame_security.cc b/src/core/ext/transport/chttp2/transport/frame_security.cc new file mode 100644 index 00000000000..08a8b800892 --- /dev/null +++ b/src/core/ext/transport/chttp2/transport/frame_security.cc @@ -0,0 +1,82 @@ +// +// Copyright 2024 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include "src/core/ext/transport/chttp2/transport/frame_security.h" + +#include +#include + +#include "absl/status/status.h" +#include "src/core/ext/transport/chttp2/transport/internal.h" +#include "src/core/ext/transport/chttp2/transport/legacy_frame.h" +#include "src/core/lib/iomgr/event_engine_shims/endpoint.h" +#include "src/core/lib/slice/slice.h" +#include "src/core/lib/slice/slice_buffer.h" +#include "src/core/lib/transport/transport_framing_endpoint_extension.h" + +absl::Status grpc_chttp2_security_frame_parser_parse(void* parser, + grpc_chttp2_transport* t, + grpc_chttp2_stream* /*s*/, + const grpc_slice& slice, + int is_last) { + // Ignore frames from non-EventEngine endpoints. + if (t->transport_framing_endpoint_extension == nullptr) { + return absl::OkStatus(); + } + + grpc_chttp2_security_frame_parser* p = + static_cast(parser); + p->payload.Append(grpc_core::Slice(slice)); + + if (is_last) { + // Send security frame payload to endpoint. + t->transport_framing_endpoint_extension->ReceiveFrame( + std::move(p->payload)); + } + + return absl::OkStatus(); +} + +absl::Status grpc_chttp2_security_frame_parser_begin_frame( + grpc_chttp2_security_frame_parser* parser) { + parser->payload.Clear(); + return absl::OkStatus(); +} + +void grpc_chttp2_security_frame_create(grpc_slice_buffer* payload, + uint32_t length, + grpc_slice_buffer* frame) { + // does this frame need padding for security? + // do we need to worry about max frame size? it's 16 bytes + grpc_slice hdr; + uint8_t* p; + static const size_t header_size = 9; + + hdr = GRPC_SLICE_MALLOC(header_size); + p = GRPC_SLICE_START_PTR(hdr); + *p++ = static_cast(length >> 16); + *p++ = static_cast(length >> 8); + *p++ = static_cast(length); + *p++ = GRPC_CHTTP2_FRAME_SECURITY; + *p++ = 0; // no flags + *p++ = 0; + *p++ = 0; + *p++ = 0; + *p++ = 0; + + grpc_slice_buffer_add(frame, hdr); + grpc_slice_buffer_move_first_no_ref(payload, payload->length, frame); +} diff --git a/src/core/ext/transport/chttp2/transport/frame_security.h b/src/core/ext/transport/chttp2/transport/frame_security.h new file mode 100644 index 00000000000..ea6e81f9743 --- /dev/null +++ b/src/core/ext/transport/chttp2/transport/frame_security.h @@ -0,0 +1,44 @@ +// +// Copyright 2024 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef GRPC_SRC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_SECURITY_H +#define GRPC_SRC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_SECURITY_H + +#include +#include +#include + +#include "src/core/ext/transport/chttp2/transport/legacy_frame.h" +#include "src/core/lib/iomgr/error.h" +#include "src/core/lib/slice/slice_buffer.h" +#include "src/core/lib/transport/transport_framing_endpoint_extension.h" + +struct grpc_chttp2_security_frame_parser { + grpc_core::SliceBuffer payload; +}; + +void grpc_chttp2_security_frame_create(grpc_slice_buffer* payload, + uint32_t length, + grpc_slice_buffer* frame); + +absl::Status grpc_chttp2_security_frame_parser_begin_frame( + grpc_chttp2_security_frame_parser* parser); + +grpc_error_handle grpc_chttp2_security_frame_parser_parse( + void* parser, grpc_chttp2_transport* t, grpc_chttp2_stream* s, + const grpc_slice& slice, int is_last); + +#endif // GRPC_SRC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_SECURITY_H diff --git a/src/core/ext/transport/chttp2/transport/http2_settings.cc b/src/core/ext/transport/chttp2/transport/http2_settings.cc index 83bf6e5c5d8..1115fb9dfb5 100644 --- a/src/core/ext/transport/chttp2/transport/http2_settings.cc +++ b/src/core/ext/transport/chttp2/transport/http2_settings.cc @@ -58,6 +58,9 @@ void Http2Settings::Diff( cb(kGrpcPreferredReceiveCryptoFrameSizeWireId, preferred_receive_crypto_message_size_); } + if (allow_security_frame_ != old.allow_security_frame_) { + cb(kGrpcAllowSecurityFrameWireId, allow_security_frame_); + } } std::string Http2Settings::WireIdToName(uint16_t wire_id) { @@ -78,6 +81,8 @@ std::string Http2Settings::WireIdToName(uint16_t wire_id) { return std::string(allow_true_binary_metadata_name()); case kGrpcPreferredReceiveCryptoFrameSizeWireId: return std::string(preferred_receive_crypto_message_size_name()); + case kGrpcAllowSecurityFrameWireId: + return std::string(allow_security_frame_name()); default: return absl::StrCat("UNKNOWN (", wire_id, ")"); } @@ -119,6 +124,10 @@ grpc_http2_error_code Http2Settings::Apply(uint16_t key, uint32_t value) { Clamp(value, min_preferred_receive_crypto_message_size(), max_preferred_receive_crypto_message_size()); break; + case kGrpcAllowSecurityFrameWireId: + if (value > 1) return GRPC_HTTP2_PROTOCOL_ERROR; + allow_security_frame_ = value != 0; + break; } return GRPC_HTTP2_NO_ERROR; } diff --git a/src/core/ext/transport/chttp2/transport/http2_settings.h b/src/core/ext/transport/chttp2/transport/http2_settings.h index 44ed672d123..dc383ed5294 100644 --- a/src/core/ext/transport/chttp2/transport/http2_settings.h +++ b/src/core/ext/transport/chttp2/transport/http2_settings.h @@ -42,6 +42,7 @@ class Http2Settings { kMaxHeaderListSizeWireId = 6, kGrpcAllowTrueBinaryMetadataWireId = 65027, kGrpcPreferredReceiveCryptoFrameSizeWireId = 65028, + kGrpcAllowSecurityFrameWireId = 65029, }; void Diff(bool is_first_send, const Http2Settings& old, @@ -60,6 +61,7 @@ class Http2Settings { bool allow_true_binary_metadata() const { return allow_true_binary_metadata_; } + bool allow_security_frame() const { return allow_security_frame_; } void SetHeaderTableSize(uint32_t x) { header_table_size_ = x; } void SetMaxConcurrentStreams(uint32_t x) { max_concurrent_streams_ = x; } @@ -79,6 +81,7 @@ class Http2Settings { Clamp(x, min_preferred_receive_crypto_message_size(), max_preferred_receive_crypto_message_size()); } + void SetAllowSecurityFrame(bool x) { allow_security_frame_ = x; } static absl::string_view header_table_size_name() { return "HEADER_TABLE_SIZE"; @@ -100,6 +103,9 @@ class Http2Settings { static absl::string_view preferred_receive_crypto_message_size_name() { return "GRPC_PREFERRED_RECEIVE_MESSAGE_SIZE"; } + static absl::string_view allow_security_frame_name() { + return "GRPC_ALLOW_SECURITY_FRAME"; + } static uint32_t max_initial_window_size() { return 2147483647u; } static uint32_t max_max_frame_size() { return 16777215u; } @@ -120,7 +126,8 @@ class Http2Settings { preferred_receive_crypto_message_size_ == rhs.preferred_receive_crypto_message_size_ && enable_push_ == rhs.enable_push_ && - allow_true_binary_metadata_ == rhs.allow_true_binary_metadata_; + allow_true_binary_metadata_ == rhs.allow_true_binary_metadata_ && + allow_security_frame_ == rhs.allow_security_frame_; } bool operator!=(const Http2Settings& rhs) const { return !operator==(rhs); } @@ -134,6 +141,7 @@ class Http2Settings { uint32_t preferred_receive_crypto_message_size_ = 0u; bool enable_push_ = true; bool allow_true_binary_metadata_ = false; + bool allow_security_frame_ = false; }; class Http2SettingsManager { diff --git a/src/core/ext/transport/chttp2/transport/internal.h b/src/core/ext/transport/chttp2/transport/internal.h index 6d9eb84d227..4db6642292a 100644 --- a/src/core/ext/transport/chttp2/transport/internal.h +++ b/src/core/ext/transport/chttp2/transport/internal.h @@ -45,6 +45,7 @@ #include "src/core/ext/transport/chttp2/transport/frame_goaway.h" #include "src/core/ext/transport/chttp2/transport/frame_ping.h" #include "src/core/ext/transport/chttp2/transport/frame_rst_stream.h" +#include "src/core/ext/transport/chttp2/transport/frame_security.h" #include "src/core/ext/transport/chttp2/transport/frame_settings.h" #include "src/core/ext/transport/chttp2/transport/frame_window_update.h" #include "src/core/ext/transport/chttp2/transport/hpack_encoder.h" @@ -405,6 +406,8 @@ struct grpc_chttp2_transport final : public grpc_core::FilterStackTransport, } simple; /// parser for goaway frames grpc_chttp2_goaway_parser goaway_parser; + // parser for secure frames + grpc_chttp2_security_frame_parser security_frame_parser; grpc_core::chttp2::TransportFlowControl flow_control; /// initial window change. This is tracked as we parse settings frames from diff --git a/src/core/ext/transport/chttp2/transport/legacy_frame.h b/src/core/ext/transport/chttp2/transport/legacy_frame.h index f0a6e639d74..14371f49aa5 100644 --- a/src/core/ext/transport/chttp2/transport/legacy_frame.h +++ b/src/core/ext/transport/chttp2/transport/legacy_frame.h @@ -33,6 +33,7 @@ typedef struct grpc_chttp2_transport grpc_chttp2_transport; #define GRPC_CHTTP2_FRAME_PING 6 #define GRPC_CHTTP2_FRAME_GOAWAY 7 #define GRPC_CHTTP2_FRAME_WINDOW_UPDATE 8 +#define GRPC_CHTTP2_FRAME_SECURITY 200 #define GRPC_CHTTP2_DATA_FLAG_END_STREAM 1 #define GRPC_CHTTP2_FLAG_ACK 1 diff --git a/src/core/ext/transport/chttp2/transport/parsing.cc b/src/core/ext/transport/chttp2/transport/parsing.cc index f1c7e07a217..eb024e5b576 100644 --- a/src/core/ext/transport/chttp2/transport/parsing.cc +++ b/src/core/ext/transport/chttp2/transport/parsing.cc @@ -47,6 +47,7 @@ #include "src/core/ext/transport/chttp2/transport/frame_goaway.h" #include "src/core/ext/transport/chttp2/transport/frame_ping.h" #include "src/core/ext/transport/chttp2/transport/frame_rst_stream.h" +#include "src/core/ext/transport/chttp2/transport/frame_security.h" #include "src/core/ext/transport/chttp2/transport/frame_settings.h" #include "src/core/ext/transport/chttp2/transport/frame_window_update.h" #include "src/core/ext/transport/chttp2/transport/hpack_parser.h" @@ -88,6 +89,7 @@ static grpc_error_handle init_window_update_frame_parser( grpc_chttp2_transport* t); static grpc_error_handle init_ping_parser(grpc_chttp2_transport* t); static grpc_error_handle init_goaway_parser(grpc_chttp2_transport* t); +static grpc_error_handle init_security_frame_parser(grpc_chttp2_transport* t); static grpc_error_handle init_non_header_skip_frame_parser( grpc_chttp2_transport* t); @@ -194,6 +196,8 @@ std::string FrameTypeString(uint8_t frame_type, uint8_t flags) { return MakeFrameTypeString("GOAWAY", flags, {}); case GRPC_CHTTP2_FRAME_WINDOW_UPDATE: return MakeFrameTypeString("WINDOW_UPDATE", flags, {}); + case GRPC_CHTTP2_FRAME_SECURITY: + return MakeFrameTypeString("SECURITY", flags, {}); default: return MakeFrameTypeString( absl::StrCat("UNKNOWN_FRAME_TYPE_", static_cast(frame_type)), @@ -451,6 +455,14 @@ static grpc_error_handle init_frame_parser(grpc_chttp2_transport* t, return init_ping_parser(t); case GRPC_CHTTP2_FRAME_GOAWAY: return init_goaway_parser(t); + case GRPC_CHTTP2_FRAME_SECURITY: + if (!t->settings.peer().allow_security_frame()) { + if (GRPC_TRACE_FLAG_ENABLED(http)) { + LOG(ERROR) << "Security frame received but not allowed, ignoring"; + } + return init_non_header_skip_frame_parser(t); + } + return init_security_frame_parser(t); default: GRPC_TRACE_LOG(http, ERROR) << "Unknown frame type " @@ -882,6 +894,16 @@ static grpc_error_handle init_settings_frame_parser(grpc_chttp2_transport* t) { return absl::OkStatus(); } +static grpc_error_handle init_security_frame_parser(grpc_chttp2_transport* t) { + grpc_error_handle err = + grpc_chttp2_security_frame_parser_begin_frame(&t->security_frame_parser); + if (!err.ok()) return err; + t->parser = grpc_chttp2_transport::Parser{ + "security_frame", grpc_chttp2_security_frame_parser_parse, + &t->security_frame_parser}; + return absl::OkStatus(); +} + static grpc_error_handle parse_frame_slice(grpc_chttp2_transport* t, const grpc_slice& slice, int is_last) { diff --git a/src/python/grpcio/grpc_core_dependencies.py b/src/python/grpcio/grpc_core_dependencies.py index 8e5687f3941..b73b62761e7 100644 --- a/src/python/grpcio/grpc_core_dependencies.py +++ b/src/python/grpcio/grpc_core_dependencies.py @@ -69,6 +69,7 @@ CORE_SOURCE_FILES = [ 'src/core/ext/transport/chttp2/transport/frame_goaway.cc', 'src/core/ext/transport/chttp2/transport/frame_ping.cc', 'src/core/ext/transport/chttp2/transport/frame_rst_stream.cc', + 'src/core/ext/transport/chttp2/transport/frame_security.cc', 'src/core/ext/transport/chttp2/transport/frame_settings.cc', 'src/core/ext/transport/chttp2/transport/frame_window_update.cc', 'src/core/ext/transport/chttp2/transport/hpack_encoder.cc', diff --git a/test/core/bad_client/tests/simple_request.cc b/test/core/bad_client/tests/simple_request.cc index 52db06cccb3..3b9b385a943 100644 --- a/test/core/bad_client/tests/simple_request.cc +++ b/test/core/bad_client/tests/simple_request.cc @@ -39,6 +39,26 @@ "\x10\x02te\x08trailers" \ "\x10\x0auser-agent\"bad-client grpc-c/0.12.0.0 (linux)" +#define ONE_SETTING_HDR \ + "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" \ + "\x00\x00\x06\x04\x00\x00\x00\x00\x00" /* settings frame */ + +#define USUAL_HDR \ + "\x00\x00\xc9\x01\x04\x00\x00\x00\x01" /* headers: generated from \ + simple_request.headers in this \ + directory */ \ + "\x10\x05:path\x08/foo/bar" \ + "\x10\x07:scheme\x04http" \ + "\x10\x07:method\x04POST" \ + "\x10\x0a:authority\x09localhost" \ + "\x10\x0c" \ + "content-type\x10" \ + "application/grpc" \ + "\x10\x14grpc-accept-encoding\x15" \ + "deflate,identity,gzip" \ + "\x10\x02te\x08trailers" \ + "\x10\x0auser-agent\"bad-client grpc-c/0.12.0.0 (linux)" + #define PFX_STR_UNUSUAL \ "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" \ "\x00\x00\x00\x04\x00\x00\x00\x00\x00" /* settings frame */ \ @@ -219,7 +239,6 @@ int main(int argc, char** argv) { "\x00\x00\x05\x00\x00\x00\x00\x00\x01" "\x34\x00\x00\x00\x00", 0); - // push a data frame with bad flags GRPC_RUN_BAD_CLIENT_TEST(verifier, nullptr, PFX_STR "\x00\x00\x00\x00\x02\x00\x00\x00\x01", 0); @@ -235,6 +254,22 @@ int main(int argc, char** argv) { "\x00\x00\x04\x08\x00\x00\x00\x00\x01" "\x00\x00\x00\x00", 0); + // push a valid secure frame with payload "hello" and setting + // `allow_security_frame` enabled, frame should be parsed + GRPC_RUN_BAD_CLIENT_TEST( + verifier, nullptr, + ONE_SETTING_HDR + "\xFE\x05\x00\x00\x00\x01" USUAL_HDR + "\x00\x00\x05\xC8\x00\x00\x00\x00\x00\x68\x65\x6C\x6C\x6F", + 0); + // push a valid secure frame with payload "hello" and setting + // `allow_security_frame` disabled, frame should be ignored + GRPC_RUN_BAD_CLIENT_TEST( + VerifyRpcDoesNotGetCanceled, nullptr, + ONE_SETTING_HDR + "\xFE\x05\x00\x00\x00\x00" USUAL_HDR + "\x00\x00\x05\xC8\x00\x00\x00\x00\x00\x68\x65\x6C\x6C\x6F", + 0); // push a short goaway GRPC_RUN_BAD_CLIENT_TEST(failure_verifier, nullptr, PFX_STR "\x00\x00\x04\x07\x00\x00\x00\x00\x00", 0); diff --git a/test/core/end2end/fuzzers/fuzzer_input.proto b/test/core/end2end/fuzzers/fuzzer_input.proto index 32b1c5d4436..0cab21cb4e5 100644 --- a/test/core/end2end/fuzzers/fuzzer_input.proto +++ b/test/core/end2end/fuzzers/fuzzer_input.proto @@ -121,6 +121,10 @@ message H2WindowUpdateFrame { uint32 increment = 2; } +message H2SecurityFrame { + bytes payload = 1; +} + message H2ClientPrefix {} message ChaoticGoodServerFragment { @@ -202,6 +206,7 @@ message InputSegment { uint32 repeated_zeros = 12; ChaoticGoodFrame chaotic_good = 13; FakeTransportFrame fake_transport_frame = 14; + H2SecurityFrame security_frame = 15; } } diff --git a/test/core/end2end/fuzzers/network_input.cc b/test/core/end2end/fuzzers/network_input.cc index 6a91a74d9ca..c86b7c221fb 100644 --- a/test/core/end2end/fuzzers/network_input.cc +++ b/test/core/end2end/fuzzers/network_input.cc @@ -326,6 +326,9 @@ grpc_slice SliceFromSegment(const fuzzer_input::InputSegment& segment) { segment.window_update().stream_id(), segment.window_update().increment(), }); + case fuzzer_input::InputSegment::kSecurityFrame: + return SliceFromH2Frame(Http2SecurityFrame{ + SliceBufferFromBytes(segment.security_frame().payload())}); case fuzzer_input::InputSegment::kClientPrefix: return grpc_slice_from_static_string("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"); case fuzzer_input::InputSegment::kRepeatedZeros: { diff --git a/test/core/transport/chttp2/frame_test.cc b/test/core/transport/chttp2/frame_test.cc index 456d8a01af4..f8761ccf68d 100644 --- a/test/core/transport/chttp2/frame_test.cc +++ b/test/core/transport/chttp2/frame_test.cc @@ -162,6 +162,8 @@ TEST(Frame, Serialization) { 0x0b, 'h', 'e', 'l', 'l', 'o')); EXPECT_EQ(Serialize(Http2WindowUpdateFrame{1, 0x12345678}), ByteVec(0, 0, 4, 8, 0, 0, 0, 0, 1, 0x12, 0x34, 0x56, 0x78)); + EXPECT_EQ(Serialize(Http2SecurityFrame{SliceBufferFromString("hello")}), + ByteVec(0, 0, 5, 200, 0, 0, 0, 0, 0, 'h', 'e', 'l', 'l', 'o')); } TEST(Frame, Parse) { @@ -215,6 +217,8 @@ TEST(Frame, Parse) { Slice::FromCopiedString("hello")})); EXPECT_EQ(ParseFrame(0, 0, 4, 8, 0, 0, 0, 0, 1, 0x12, 0x34, 0x56, 0x78), Http2Frame(Http2WindowUpdateFrame{1, 0x12345678})); + EXPECT_EQ(ParseFrame(0, 0, 5, 200, 0, 0, 0, 0, 0, 'h', 'e', 'l', 'l', 'o'), + Http2Frame(Http2SecurityFrame{SliceBufferFromString("hello")})); } TEST(Frame, ParsePadded) { diff --git a/test/core/transport/chttp2/http2_settings_test.cc b/test/core/transport/chttp2/http2_settings_test.cc index 776a131be4d..d11db9593f9 100644 --- a/test/core/transport/chttp2/http2_settings_test.cc +++ b/test/core/transport/chttp2/http2_settings_test.cc @@ -29,6 +29,7 @@ TEST(Http2SettingsTest, CanSetAndRetrieveSettings) { settings.SetMaxHeaderListSize(6); settings.SetAllowTrueBinaryMetadata(true); settings.SetPreferredReceiveCryptoMessageSize(77777); + settings.SetAllowSecurityFrame(true); EXPECT_EQ(settings.header_table_size(), 1u); EXPECT_EQ(settings.enable_push(), true); EXPECT_EQ(settings.max_concurrent_streams(), 3u); @@ -37,6 +38,7 @@ TEST(Http2SettingsTest, CanSetAndRetrieveSettings) { EXPECT_EQ(settings.max_header_list_size(), 6u); EXPECT_EQ(settings.allow_true_binary_metadata(), true); EXPECT_EQ(settings.preferred_receive_crypto_message_size(), 77777u); + EXPECT_EQ(settings.allow_security_frame(), true); settings.SetHeaderTableSize(10); settings.SetEnablePush(false); settings.SetMaxConcurrentStreams(30); @@ -45,6 +47,7 @@ TEST(Http2SettingsTest, CanSetAndRetrieveSettings) { settings.SetMaxHeaderListSize(60); settings.SetAllowTrueBinaryMetadata(false); settings.SetPreferredReceiveCryptoMessageSize(70000); + settings.SetAllowSecurityFrame(false); EXPECT_EQ(settings.header_table_size(), 10u); EXPECT_EQ(settings.enable_push(), false); EXPECT_EQ(settings.max_concurrent_streams(), 30u); @@ -53,6 +56,7 @@ TEST(Http2SettingsTest, CanSetAndRetrieveSettings) { EXPECT_EQ(settings.max_header_list_size(), 60u); EXPECT_EQ(settings.allow_true_binary_metadata(), false); EXPECT_EQ(settings.preferred_receive_crypto_message_size(), 70000u); + EXPECT_EQ(settings.allow_security_frame(), false); } TEST(Http2SettingsTest, InitialWindowSizeLimits) { @@ -269,6 +273,32 @@ TEST(Http2SettingsTest, DiffOnSettingsWithEightValuesSet) { KeyValue{65027, 1}, KeyValue{65028, 77777})); } +TEST(Http2SettingsTest, DiffOnSettingsWithNineValuesSet) { + Http2Settings settings1; + Http2Settings settings2; + settings1.SetHeaderTableSize(1); + settings1.SetEnablePush(false); + settings1.SetMaxConcurrentStreams(3); + settings1.SetInitialWindowSize(4); + settings1.SetMaxFrameSize(50000); + settings1.SetMaxHeaderListSize(6); + settings1.SetAllowTrueBinaryMetadata(true); + settings1.SetPreferredReceiveCryptoMessageSize(77777); + settings1.SetAllowSecurityFrame(true); + EXPECT_THAT( + Diff(settings1, settings2, false), + ::testing::UnorderedElementsAre( + KeyValue{1, 1}, KeyValue{2, 0}, KeyValue{3, 3}, KeyValue{4, 4}, + KeyValue{5, 50000}, KeyValue{6, 6}, KeyValue{65027, 1}, + KeyValue{65028, 77777}, KeyValue{65029, 1})); + EXPECT_THAT( + Diff(settings1, settings2, true), + ::testing::UnorderedElementsAre( + KeyValue{1, 1}, KeyValue{2, 0}, KeyValue{3, 3}, KeyValue{4, 4}, + KeyValue{5, 50000}, KeyValue{6, 6}, KeyValue{65027, 1}, + KeyValue{65028, 77777}, KeyValue{65029, 1})); +} + TEST(Http2SettingsTest, ChangingHeaderTableSizeChangesEquality) { Http2Settings settings1; Http2Settings settings2; @@ -358,6 +388,17 @@ TEST(Http2SettingsTest, EXPECT_NE(settings1, settings2); } +TEST(Http2SettingsTest, ChangingAllowSecurityFrameChangesEquality) { + Http2Settings settings1; + Http2Settings settings2; + settings1.SetAllowSecurityFrame(true); + EXPECT_NE(settings1, settings2); + settings2.SetAllowSecurityFrame(true); + EXPECT_EQ(settings1, settings2); + settings2.SetAllowSecurityFrame(false); + EXPECT_NE(settings1, settings2); +} + TEST(Http2SettingsTest, WireIdToNameWorks) { EXPECT_EQ(Http2Settings::WireIdToName(1), "HEADER_TABLE_SIZE"); EXPECT_EQ(Http2Settings::WireIdToName(2), "ENABLE_PUSH"); @@ -369,7 +410,8 @@ TEST(Http2SettingsTest, WireIdToNameWorks) { "GRPC_ALLOW_TRUE_BINARY_METADATA"); EXPECT_EQ(Http2Settings::WireIdToName(65028), "GRPC_PREFERRED_RECEIVE_MESSAGE_SIZE"); - EXPECT_EQ(Http2Settings::WireIdToName(65029), "UNKNOWN (65029)"); + EXPECT_EQ(Http2Settings::WireIdToName(65029), "GRPC_ALLOW_SECURITY_FRAME"); + EXPECT_EQ(Http2Settings::WireIdToName(65030), "UNKNOWN (65030)"); } TEST(Http2SettingsTest, ApplyHeaderTableSizeWorks) { @@ -442,6 +484,15 @@ TEST(Http2SettingsTest, ApplyPreferredReceiveCryptoMessageSizeWorks) { EXPECT_EQ(settings.preferred_receive_crypto_message_size(), 0x7fffffffu); } +TEST(Http2SettingsTest, ApplyAllowSecurityFrameWorks) { + Http2Settings settings; + EXPECT_EQ(settings.Apply(65029, 0), GRPC_HTTP2_NO_ERROR); + EXPECT_EQ(settings.allow_security_frame(), false); + EXPECT_EQ(settings.Apply(65029, 1), GRPC_HTTP2_NO_ERROR); + EXPECT_EQ(settings.allow_security_frame(), true); + EXPECT_EQ(settings.Apply(65029, 2), GRPC_HTTP2_PROTOCOL_ERROR); +} + namespace { MATCHER_P(SettingsFrame, settings, "") { if (!arg.has_value()) { diff --git a/tools/doxygen/Doxyfile.c++.internal b/tools/doxygen/Doxyfile.c++.internal index b205ff8cfda..5cce618c9f1 100644 --- a/tools/doxygen/Doxyfile.c++.internal +++ b/tools/doxygen/Doxyfile.c++.internal @@ -1199,6 +1199,8 @@ src/core/ext/transport/chttp2/transport/frame_ping.cc \ src/core/ext/transport/chttp2/transport/frame_ping.h \ src/core/ext/transport/chttp2/transport/frame_rst_stream.cc \ src/core/ext/transport/chttp2/transport/frame_rst_stream.h \ +src/core/ext/transport/chttp2/transport/frame_security.cc \ +src/core/ext/transport/chttp2/transport/frame_security.h \ src/core/ext/transport/chttp2/transport/frame_settings.cc \ src/core/ext/transport/chttp2/transport/frame_settings.h \ src/core/ext/transport/chttp2/transport/frame_window_update.cc \ diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal index ab3e5dc3db6..8f03a2b34df 100644 --- a/tools/doxygen/Doxyfile.core.internal +++ b/tools/doxygen/Doxyfile.core.internal @@ -1006,6 +1006,8 @@ src/core/ext/transport/chttp2/transport/frame_ping.cc \ src/core/ext/transport/chttp2/transport/frame_ping.h \ src/core/ext/transport/chttp2/transport/frame_rst_stream.cc \ src/core/ext/transport/chttp2/transport/frame_rst_stream.h \ +src/core/ext/transport/chttp2/transport/frame_security.cc \ +src/core/ext/transport/chttp2/transport/frame_security.h \ src/core/ext/transport/chttp2/transport/frame_settings.cc \ src/core/ext/transport/chttp2/transport/frame_settings.h \ src/core/ext/transport/chttp2/transport/frame_window_update.cc \