1 // Copyright 2023 gRPC authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <grpc/support/port_platform.h>
16
17 #include "src/core/ext/transport/chttp2/transport/ping_abuse_policy.h"
18
19 #include <algorithm>
20
21 #include "absl/strings/str_cat.h"
22 #include "absl/types/optional.h"
23
24 #include <grpc/impl/channel_arg_names.h>
25
26 namespace grpc_core {
27
28 namespace {
29 Duration g_default_min_recv_ping_interval_without_data = Duration::Minutes(5);
30 int g_default_max_ping_strikes = 2;
31 } // namespace
32
Chttp2PingAbusePolicy(const ChannelArgs & args)33 Chttp2PingAbusePolicy::Chttp2PingAbusePolicy(const ChannelArgs& args)
34 : min_recv_ping_interval_without_data_(std::max(
35 Duration::Zero(),
36 args.GetDurationFromIntMillis(
37 GRPC_ARG_HTTP2_MIN_RECV_PING_INTERVAL_WITHOUT_DATA_MS)
38 .value_or(g_default_min_recv_ping_interval_without_data))),
39 max_ping_strikes_(
40 std::max(0, args.GetInt(GRPC_ARG_HTTP2_MAX_PING_STRIKES)
41 .value_or(g_default_max_ping_strikes))) {}
42
SetDefaults(const ChannelArgs & args)43 void Chttp2PingAbusePolicy::SetDefaults(const ChannelArgs& args) {
44 g_default_max_ping_strikes =
45 std::max(0, args.GetInt(GRPC_ARG_HTTP2_MAX_PING_STRIKES)
46 .value_or(g_default_max_ping_strikes));
47 g_default_min_recv_ping_interval_without_data =
48 std::max(Duration::Zero(),
49 args.GetDurationFromIntMillis(
50 GRPC_ARG_HTTP2_MIN_RECV_PING_INTERVAL_WITHOUT_DATA_MS)
51 .value_or(g_default_min_recv_ping_interval_without_data));
52 }
53
ReceivedOnePing(bool transport_idle)54 bool Chttp2PingAbusePolicy::ReceivedOnePing(bool transport_idle) {
55 const Timestamp now = Timestamp::Now();
56 const Timestamp next_allowed_ping =
57 last_ping_recv_time_ + RecvPingIntervalWithoutData(transport_idle);
58 last_ping_recv_time_ = now;
59 if (next_allowed_ping <= now) return false;
60 // Received ping too soon: increment strike count.
61 ++ping_strikes_;
62 return ping_strikes_ > max_ping_strikes_ && max_ping_strikes_ != 0;
63 }
64
GetDebugString(bool transport_idle) const65 std::string Chttp2PingAbusePolicy::GetDebugString(bool transport_idle) const {
66 return absl::StrCat(
67 "now=", Timestamp::Now().ToString(), " transport_idle=", transport_idle,
68 " next_allowed_ping=",
69 (last_ping_recv_time_ + RecvPingIntervalWithoutData(transport_idle))
70 .ToString(),
71 " ping_strikes=", ping_strikes_);
72 }
73
RecvPingIntervalWithoutData(bool transport_idle) const74 Duration Chttp2PingAbusePolicy::RecvPingIntervalWithoutData(
75 bool transport_idle) const {
76 if (transport_idle) {
77 // According to RFC1122, the interval of TCP Keep-Alive is default to
78 // no less than two hours. When there is no outstanding streams, we
79 // restrict the number of PINGS equivalent to TCP Keep-Alive.
80 return Duration::Hours(2);
81 }
82 return min_recv_ping_interval_without_data_;
83 }
84
ResetPingStrikes()85 void Chttp2PingAbusePolicy::ResetPingStrikes() {
86 last_ping_recv_time_ = Timestamp::InfPast();
87 ping_strikes_ = 0;
88 }
89
90 } // namespace grpc_core
91