mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-12 21:30:45 +01:00
Propagate ECN information on posix sockets to rtc::ReceivedPacket
Two new socket options are introduced OPT_SEND_ECN used for setting ECN bits. OPT_RECV_ECN used for reading the ECN bits. If ECN bits are set on received IP packets, ECT(1) and CE is propagated via rtc::ReceivedPacket. Bug: webrtc:15368 Change-Id: I3ac335007e2f7d30564569bbc80ce47fa541bef1 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/332380 Reviewed-by: Jonas Oreland <jonaso@google.com> Reviewed-by: Harald Alvestrand <hta@webrtc.org> Commit-Queue: Per Kjellander <perkj@webrtc.org> Cr-Commit-Position: refs/heads/main@{#41885}
This commit is contained in:
parent
329f0ead43
commit
8df31c915a
14 changed files with 244 additions and 25 deletions
|
@ -727,7 +727,7 @@ void DtlsTransport::OnDtlsEvent(rtc::StreamInterface* dtls, int sig, int err) {
|
|||
NotifyPacketReceived(rtc::ReceivedPacket(
|
||||
rtc::MakeArrayView(buf, read), rtc::SocketAddress(),
|
||||
webrtc::Timestamp::Micros(rtc::TimeMicros()),
|
||||
rtc::ReceivedPacket::kDtlsDecrypted));
|
||||
rtc::EcnMarking::kNotEct, rtc::ReceivedPacket::kDtlsDecrypted));
|
||||
} else if (ret == rtc::SR_EOS) {
|
||||
// Remote peer shut down the association with no error.
|
||||
RTC_LOG(LS_INFO) << ToString() << ": DTLS transport closed by remote";
|
||||
|
|
|
@ -34,9 +34,9 @@ TEST(PacketTransportInternal,
|
|||
EXPECT_EQ(packet.decryption_info(),
|
||||
rtc::ReceivedPacket::kDtlsDecrypted);
|
||||
});
|
||||
packet_transport.NotifyPacketReceived(
|
||||
rtc::ReceivedPacket({}, rtc::SocketAddress(), absl::nullopt,
|
||||
rtc::ReceivedPacket::kDtlsDecrypted));
|
||||
packet_transport.NotifyPacketReceived(rtc::ReceivedPacket(
|
||||
{}, rtc::SocketAddress(), absl::nullopt, rtc::EcnMarking::kNotEct,
|
||||
rtc::ReceivedPacket::kDtlsDecrypted));
|
||||
|
||||
packet_transport.DeregisterReceivedPacketCallback(&receiver);
|
||||
}
|
||||
|
|
|
@ -1046,6 +1046,7 @@ rtc_library("threading") {
|
|||
"../api/task_queue:pending_task_safety_flag",
|
||||
"../api/units:time_delta",
|
||||
"../system_wrappers:field_trial",
|
||||
"./network:ecn_marking",
|
||||
"synchronization:mutex",
|
||||
"system:no_unique_address",
|
||||
"system:rtc_export",
|
||||
|
@ -1092,6 +1093,7 @@ rtc_library("socket") {
|
|||
":macromagic",
|
||||
":socket_address",
|
||||
"../api/units:timestamp",
|
||||
"./network:ecn_marking",
|
||||
"system:rtc_export",
|
||||
"third_party/sigslot",
|
||||
]
|
||||
|
|
|
@ -145,9 +145,9 @@ void AsyncUDPSocket::OnReadEvent(Socket* socket) {
|
|||
}
|
||||
*receive_buffer.arrival_time += *socket_time_offset_;
|
||||
}
|
||||
NotifyPacketReceived(ReceivedPacket(receive_buffer.payload,
|
||||
receive_buffer.source_address,
|
||||
receive_buffer.arrival_time));
|
||||
NotifyPacketReceived(
|
||||
ReceivedPacket(receive_buffer.payload, receive_buffer.source_address,
|
||||
receive_buffer.arrival_time, receive_buffer.ecn));
|
||||
}
|
||||
|
||||
void AsyncUDPSocket::OnWriteEvent(Socket* socket) {
|
||||
|
|
|
@ -17,6 +17,11 @@ rtc_library("sent_packet") {
|
|||
absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
|
||||
}
|
||||
|
||||
rtc_source_set("ecn_marking") {
|
||||
visibility = [ "*" ]
|
||||
sources = [ "ecn_marking.h" ]
|
||||
}
|
||||
|
||||
rtc_library("received_packet") {
|
||||
visibility = [ "*" ]
|
||||
sources = [
|
||||
|
@ -24,6 +29,7 @@ rtc_library("received_packet") {
|
|||
"received_packet.h",
|
||||
]
|
||||
deps = [
|
||||
":ecn_marking",
|
||||
"..:socket_address",
|
||||
"../../api:array_view",
|
||||
"../../api/units:timestamp",
|
||||
|
|
42
rtc_base/network/ecn_marking.h
Normal file
42
rtc_base/network/ecn_marking.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright 2024 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef RTC_BASE_NETWORK_ECN_MARKING_H_
|
||||
#define RTC_BASE_NETWORK_ECN_MARKING_H_
|
||||
|
||||
namespace rtc {
|
||||
|
||||
// TODO(https://bugs.webrtc.org/15368): L4S support is slowly being developed.
|
||||
// Help is appreciated.
|
||||
|
||||
// L4S Explicit Congestion Notification (ECN) .
|
||||
// https://www.rfc-editor.org/rfc/rfc9331.html ECT stands for ECN-Capable
|
||||
// Transport and CE stands for Congestion Experienced.
|
||||
|
||||
// RFC-3168, Section 5
|
||||
// +-----+-----+
|
||||
// | ECN FIELD |
|
||||
// +-----+-----+
|
||||
// ECT CE [Obsolete] RFC 2481 names for the ECN bits.
|
||||
// 0 0 Not-ECT
|
||||
// 0 1 ECT(1)
|
||||
// 1 0 ECT(0)
|
||||
// 1 1 CE
|
||||
|
||||
enum class EcnMarking {
|
||||
kNotEct = 0, // Not ECN-Capable Transport
|
||||
kEct1 = 1, // ECN-Capable Transport
|
||||
kEct0 = 2, // Not used by L4s (or webrtc.)
|
||||
kCe = 3, // Congestion experienced
|
||||
};
|
||||
|
||||
} // namespace rtc
|
||||
|
||||
#endif // RTC_BASE_NETWORK_ECN_MARKING_H_
|
|
@ -20,15 +20,17 @@ namespace rtc {
|
|||
ReceivedPacket::ReceivedPacket(rtc::ArrayView<const uint8_t> payload,
|
||||
const SocketAddress& source_address,
|
||||
absl::optional<webrtc::Timestamp> arrival_time,
|
||||
EcnMarking ecn,
|
||||
DecryptionInfo decryption)
|
||||
: payload_(payload),
|
||||
arrival_time_(std::move(arrival_time)),
|
||||
source_address_(source_address),
|
||||
ecn_(ecn),
|
||||
decryption_info_(decryption) {}
|
||||
|
||||
ReceivedPacket ReceivedPacket::CopyAndSet(
|
||||
DecryptionInfo decryption_info) const {
|
||||
return ReceivedPacket(payload_, source_address_, arrival_time_,
|
||||
return ReceivedPacket(payload_, source_address_, arrival_time_, ecn_,
|
||||
decryption_info);
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "absl/types/optional.h"
|
||||
#include "api/array_view.h"
|
||||
#include "api/units/timestamp.h"
|
||||
#include "rtc_base/network/ecn_marking.h"
|
||||
#include "rtc_base/socket_address.h"
|
||||
#include "rtc_base/system/rtc_export.h"
|
||||
|
||||
|
@ -38,6 +39,7 @@ class RTC_EXPORT ReceivedPacket {
|
|||
ReceivedPacket(rtc::ArrayView<const uint8_t> payload,
|
||||
const SocketAddress& source_address,
|
||||
absl::optional<webrtc::Timestamp> arrival_time = absl::nullopt,
|
||||
EcnMarking ecn = EcnMarking::kNotEct,
|
||||
DecryptionInfo decryption = kNotDecrypted);
|
||||
|
||||
ReceivedPacket CopyAndSet(DecryptionInfo decryption_info) const;
|
||||
|
@ -52,6 +54,9 @@ class RTC_EXPORT ReceivedPacket {
|
|||
return arrival_time_;
|
||||
}
|
||||
|
||||
// L4S Explicit Congestion Notification.
|
||||
EcnMarking ecn() const { return ecn_; }
|
||||
|
||||
const DecryptionInfo& decryption_info() const { return decryption_info_; }
|
||||
|
||||
static ReceivedPacket CreateFromLegacy(
|
||||
|
@ -73,6 +78,7 @@ class RTC_EXPORT ReceivedPacket {
|
|||
rtc::ArrayView<const uint8_t> payload_;
|
||||
absl::optional<webrtc::Timestamp> arrival_time_;
|
||||
const SocketAddress& source_address_;
|
||||
EcnMarking ecn_;
|
||||
DecryptionInfo decryption_info_;
|
||||
};
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
#include "rtc_base/event.h"
|
||||
#include "rtc_base/ip_address.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/network/ecn_marking.h"
|
||||
#include "rtc_base/network_monitor.h"
|
||||
#include "rtc_base/synchronization/mutex.h"
|
||||
#include "rtc_base/time_utils.h"
|
||||
|
@ -108,6 +109,33 @@ typedef char* SockOptArg;
|
|||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
// RFC-3168, Section 5. ECN is the two least significant bits.
|
||||
static constexpr uint8_t kEcnMask = 0x03;
|
||||
|
||||
#if defined(WEBRTC_POSIX)
|
||||
|
||||
rtc::EcnMarking EcnFromDs(uint8_t ds) {
|
||||
// RFC-3168, Section 5.
|
||||
constexpr uint8_t ECN_ECT1 = 0x01;
|
||||
constexpr uint8_t ECN_ECT0 = 0x02;
|
||||
constexpr uint8_t ECN_CE = 0x03;
|
||||
const uint8_t ecn = ds & kEcnMask;
|
||||
|
||||
if (ecn == ECN_ECT1) {
|
||||
return rtc::EcnMarking::kEct1;
|
||||
}
|
||||
if (ecn == ECN_ECT0) {
|
||||
return rtc::EcnMarking::kEct0;
|
||||
}
|
||||
if (ecn == ECN_CE) {
|
||||
return rtc::EcnMarking::kCe;
|
||||
}
|
||||
return rtc::EcnMarking::kNotEct;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
class ScopedSetTrue {
|
||||
public:
|
||||
ScopedSetTrue(bool* value) : value_(value) {
|
||||
|
@ -125,6 +153,7 @@ class ScopedSetTrue {
|
|||
bool IsScmTimeStampExperimentDisabled() {
|
||||
return webrtc::field_trial::IsDisabled("WebRTC-SCM-Timestamp");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace rtc {
|
||||
|
@ -314,8 +343,19 @@ int PhysicalSocket::GetOption(Option opt, int* value) {
|
|||
#if defined(WEBRTC_POSIX)
|
||||
// unshift DSCP value to get six most significant bits of IP DiffServ field
|
||||
*value >>= 2;
|
||||
#endif
|
||||
} else if (opt == OPT_SEND_ECN) {
|
||||
#if defined(WEBRTC_POSIX)
|
||||
// Least 2 significant bits.
|
||||
*value = *value & kEcnMask;
|
||||
#endif
|
||||
} else if (opt == OPT_RECV_ECN) {
|
||||
#if defined(WEBRTC_POSIX)
|
||||
// Least 2 significant bits.
|
||||
*value = *value & kEcnMask;
|
||||
#endif
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -329,10 +369,13 @@ int PhysicalSocket::SetOption(Option opt, int value) {
|
|||
value = (value) ? IP_PMTUDISC_DO : IP_PMTUDISC_DONT;
|
||||
#endif
|
||||
} else if (opt == OPT_DSCP) {
|
||||
#if defined(WEBRTC_POSIX)
|
||||
// shift DSCP value to fit six most significant bits of IP DiffServ field
|
||||
value <<= 2;
|
||||
#endif
|
||||
// IP DiffServ consists of DSCP 6 most significant, ECN 2 least
|
||||
// significant.
|
||||
dscp_ = value << 2;
|
||||
value = dscp_ + (ecn_ & kEcnMask);
|
||||
} else if (opt == OPT_SEND_ECN) {
|
||||
ecn_ = value;
|
||||
value = dscp_ + (ecn_ & kEcnMask);
|
||||
}
|
||||
#if defined(WEBRTC_POSIX)
|
||||
if (sopt == IPV6_TCLASS) {
|
||||
|
@ -401,8 +444,8 @@ int PhysicalSocket::SendTo(const void* buffer,
|
|||
}
|
||||
|
||||
int PhysicalSocket::Recv(void* buffer, size_t length, int64_t* timestamp) {
|
||||
int received =
|
||||
DoReadFromSocket(buffer, length, /*out_addr*/ nullptr, timestamp);
|
||||
int received = DoReadFromSocket(buffer, length, /*out_addr*/ nullptr,
|
||||
timestamp, /*ecn=*/nullptr);
|
||||
if ((received == 0) && (length != 0)) {
|
||||
// Note: on graceful shutdown, recv can return 0. In this case, we
|
||||
// pretend it is blocking, and then signal close, so that simplifying
|
||||
|
@ -431,7 +474,7 @@ int PhysicalSocket::RecvFrom(void* buffer,
|
|||
size_t length,
|
||||
SocketAddress* out_addr,
|
||||
int64_t* timestamp) {
|
||||
int received = DoReadFromSocket(buffer, length, out_addr, timestamp);
|
||||
int received = DoReadFromSocket(buffer, length, out_addr, timestamp, nullptr);
|
||||
|
||||
UpdateLastError();
|
||||
int error = GetError();
|
||||
|
@ -450,9 +493,9 @@ int PhysicalSocket::RecvFrom(ReceiveBuffer& buffer) {
|
|||
static constexpr int BUF_SIZE = 64 * 1024;
|
||||
buffer.payload.EnsureCapacity(BUF_SIZE);
|
||||
|
||||
int received =
|
||||
DoReadFromSocket(buffer.payload.data(), buffer.payload.capacity(),
|
||||
&buffer.source_address, ×tamp);
|
||||
int received = DoReadFromSocket(
|
||||
buffer.payload.data(), buffer.payload.capacity(), &buffer.source_address,
|
||||
×tamp, ecn_ ? &buffer.ecn : nullptr);
|
||||
buffer.payload.SetSize(received > 0 ? received : 0);
|
||||
if (received > 0 && timestamp != -1) {
|
||||
buffer.arrival_time = webrtc::Timestamp::Micros(timestamp);
|
||||
|
@ -472,7 +515,8 @@ int PhysicalSocket::RecvFrom(ReceiveBuffer& buffer) {
|
|||
int PhysicalSocket::DoReadFromSocket(void* buffer,
|
||||
size_t length,
|
||||
SocketAddress* out_addr,
|
||||
int64_t* timestamp) {
|
||||
int64_t* timestamp,
|
||||
EcnMarking* ecn) {
|
||||
sockaddr_storage addr_storage;
|
||||
socklen_t addr_len = sizeof(addr_storage);
|
||||
sockaddr* addr = reinterpret_cast<sockaddr*>(&addr_storage);
|
||||
|
@ -487,8 +531,10 @@ int PhysicalSocket::DoReadFromSocket(void* buffer,
|
|||
msg.msg_name = addr;
|
||||
msg.msg_namelen = addr_len;
|
||||
}
|
||||
char control[CMSG_SPACE(sizeof(struct timeval))] = {};
|
||||
if (timestamp) {
|
||||
// TODO(bugs.webrtc.org/15368): What size is needed? IPV6_TCLASS is supposed
|
||||
// to be an int. Why is a larger size needed?
|
||||
char control[CMSG_SPACE(sizeof(struct timeval) + 5 * sizeof(int))] = {};
|
||||
if (timestamp || ecn) {
|
||||
*timestamp = -1;
|
||||
msg.msg_control = &control;
|
||||
msg.msg_controllen = sizeof(control);
|
||||
|
@ -498,17 +544,23 @@ int PhysicalSocket::DoReadFromSocket(void* buffer,
|
|||
// An error occured or shut down.
|
||||
return received;
|
||||
}
|
||||
if (timestamp) {
|
||||
if (timestamp || ecn) {
|
||||
struct cmsghdr* cmsg;
|
||||
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
|
||||
if (ecn) {
|
||||
if ((cmsg->cmsg_type == IPV6_TCLASS &&
|
||||
cmsg->cmsg_level == IPPROTO_IPV6) ||
|
||||
(cmsg->cmsg_type == IP_TOS && cmsg->cmsg_level == IPPROTO_IP)) {
|
||||
*ecn = EcnFromDs(CMSG_DATA(cmsg)[0]);
|
||||
}
|
||||
}
|
||||
if (cmsg->cmsg_level != SOL_SOCKET)
|
||||
continue;
|
||||
if (cmsg->cmsg_type == SCM_TIMESTAMP) {
|
||||
if (timestamp && cmsg->cmsg_type == SCM_TIMESTAMP) {
|
||||
timeval* ts = reinterpret_cast<timeval*>(CMSG_DATA(cmsg));
|
||||
*timestamp =
|
||||
rtc::kNumMicrosecsPerSec * static_cast<int64_t>(ts->tv_sec) +
|
||||
static_cast<int64_t>(ts->tv_usec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -699,6 +751,34 @@ int PhysicalSocket::TranslateOption(Option opt, int* slevel, int* sopt) {
|
|||
#else
|
||||
RTC_LOG(LS_WARNING) << "Socket::OPT_DSCP not supported.";
|
||||
return -1;
|
||||
#endif
|
||||
case OPT_SEND_ECN:
|
||||
#if defined(WEBRTC_POSIX)
|
||||
if (family_ == AF_INET6) {
|
||||
*slevel = IPPROTO_IPV6;
|
||||
*sopt = IPV6_TCLASS;
|
||||
} else {
|
||||
*slevel = IPPROTO_IP;
|
||||
*sopt = IP_TOS;
|
||||
}
|
||||
break;
|
||||
#else
|
||||
RTC_LOG(LS_WARNING) << "Socket::OPT_SEND_ESN not supported.";
|
||||
return -1;
|
||||
#endif
|
||||
case OPT_RECV_ECN:
|
||||
#if defined(WEBRTC_POSIX)
|
||||
if (family_ == AF_INET6) {
|
||||
*slevel = IPPROTO_IPV6;
|
||||
*sopt = IPV6_RECVTCLASS;
|
||||
} else {
|
||||
*slevel = IPPROTO_IP;
|
||||
*sopt = IP_RECVTOS;
|
||||
}
|
||||
break;
|
||||
#else
|
||||
RTC_LOG(LS_WARNING) << "Socket::OPT_RECV_ECN not supported.";
|
||||
return -1;
|
||||
#endif
|
||||
case OPT_RTP_SENDTIME_EXTN_ID:
|
||||
return -1; // No logging is necessary as this not a OS socket option.
|
||||
|
|
|
@ -224,7 +224,8 @@ class PhysicalSocket : public Socket, public sigslot::has_slots<> {
|
|||
int DoReadFromSocket(void* buffer,
|
||||
size_t length,
|
||||
SocketAddress* out_addr,
|
||||
int64_t* timestamp);
|
||||
int64_t* timestamp,
|
||||
EcnMarking* ecn);
|
||||
|
||||
void OnResolveResult(const webrtc::AsyncDnsResolverResult& resolver);
|
||||
|
||||
|
@ -246,6 +247,8 @@ class PhysicalSocket : public Socket, public sigslot::has_slots<> {
|
|||
int error_ RTC_GUARDED_BY(mutex_);
|
||||
ConnState state_;
|
||||
std::unique_ptr<webrtc::AsyncDnsResolverInterface> resolver_;
|
||||
uint8_t dscp_ = 0; // 6bit.
|
||||
uint8_t ecn_ = 0; // 2bits.
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
std::string dbg_addr_;
|
||||
|
|
|
@ -484,6 +484,19 @@ TEST_F(PhysicalSocketTest, TestSocketRecvTimestampIPv6ScmExperimentDisabled) {
|
|||
}
|
||||
#endif
|
||||
|
||||
#if !defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
|
||||
// TODO(bugs.webrtc.org/15368): IpV4 fails on IOS and MAC. IPV6 works.
|
||||
TEST_F(PhysicalSocketTest, TestSocketSendRecvWithEcnIPv4) {
|
||||
MAYBE_SKIP_IPV6;
|
||||
SocketTest::TestSocketSendRecvWithEcnIPV4();
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_F(PhysicalSocketTest, TestSocketSendRecvWithEcnIPv6) {
|
||||
MAYBE_SKIP_IPV6;
|
||||
SocketTest::TestSocketSendRecvWithEcnIPV6();
|
||||
}
|
||||
|
||||
// Verify that if the socket was unable to be bound to a real network interface
|
||||
// (not loopback), Bind will return an error.
|
||||
TEST_F(PhysicalSocketTest,
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
#include "api/units/timestamp.h"
|
||||
#include "rtc_base/buffer.h"
|
||||
#include "rtc_base/network/ecn_marking.h"
|
||||
#include "rtc_base/socket_address.h"
|
||||
#include "rtc_base/system/rtc_export.h"
|
||||
#include "rtc_base/third_party/sigslot/sigslot.h"
|
||||
|
@ -91,6 +92,7 @@ class RTC_EXPORT Socket {
|
|||
|
||||
absl::optional<webrtc::Timestamp> arrival_time;
|
||||
SocketAddress source_address;
|
||||
EcnMarking ecn = EcnMarking::kNotEct;
|
||||
Buffer& payload;
|
||||
};
|
||||
virtual ~Socket() {}
|
||||
|
@ -144,6 +146,8 @@ class RTC_EXPORT Socket {
|
|||
OPT_RTP_SENDTIME_EXTN_ID, // This is a non-traditional socket option param.
|
||||
// This is specific to libjingle and will be used
|
||||
// if SendTime option is needed at socket level.
|
||||
OPT_SEND_ECN, // 2-bit ECN
|
||||
OPT_RECV_ECN
|
||||
};
|
||||
virtual int GetOption(Option opt, int* value) = 0;
|
||||
virtual int SetOption(Option opt, int value) = 0;
|
||||
|
|
|
@ -234,6 +234,15 @@ void SocketTest::TestUdpSocketRecvTimestampUseRtcEpochIPv6() {
|
|||
UdpSocketRecvTimestampUseRtcEpoch(kIPv6Loopback);
|
||||
}
|
||||
|
||||
void SocketTest::TestSocketSendRecvWithEcnIPV4() {
|
||||
SocketSendRecvWithEcn(kIPv4Loopback);
|
||||
}
|
||||
|
||||
void SocketTest::TestSocketSendRecvWithEcnIPV6() {
|
||||
MAYBE_SKIP_IPV6;
|
||||
SocketSendRecvWithEcn(kIPv6Loopback);
|
||||
}
|
||||
|
||||
// For unbound sockets, GetLocalAddress / GetRemoteAddress return AF_UNSPEC
|
||||
// values on Windows, but an empty address of the same family on Linux/MacOS X.
|
||||
bool IsUnspecOrEmptyIP(const IPAddress& address) {
|
||||
|
@ -1079,6 +1088,18 @@ void SocketTest::GetSetOptionsInternal(const IPAddress& loopback) {
|
|||
ASSERT_NE(-1, socket->SetOption(Socket::OPT_DSCP, desired_dscp));
|
||||
ASSERT_NE(-1, socket->GetOption(Socket::OPT_DSCP, ¤t_dscp));
|
||||
ASSERT_EQ(desired_dscp, current_dscp);
|
||||
|
||||
int current_send_esn, desired_send_esn = 1;
|
||||
ASSERT_NE(-1, socket->GetOption(Socket::OPT_SEND_ECN, ¤t_send_esn));
|
||||
ASSERT_NE(-1, socket->SetOption(Socket::OPT_SEND_ECN, desired_send_esn));
|
||||
ASSERT_NE(-1, socket->GetOption(Socket::OPT_SEND_ECN, ¤t_send_esn));
|
||||
ASSERT_EQ(current_send_esn, desired_send_esn);
|
||||
|
||||
int current_recv_esn, desired_recv_esn = 1;
|
||||
ASSERT_NE(-1, socket->GetOption(Socket::OPT_RECV_ECN, ¤t_recv_esn));
|
||||
ASSERT_NE(-1, socket->SetOption(Socket::OPT_RECV_ECN, desired_recv_esn));
|
||||
ASSERT_NE(-1, socket->GetOption(Socket::OPT_RECV_ECN, ¤t_recv_esn));
|
||||
ASSERT_EQ(current_recv_esn, desired_recv_esn);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -1143,4 +1164,41 @@ void SocketTest::UdpSocketRecvTimestampUseRtcEpoch(const IPAddress& loopback) {
|
|||
EXPECT_GT(packet_2->packet_time->us(), packet_1->packet_time->us());
|
||||
EXPECT_NEAR(packet_2->packet_time->us(), rtc::TimeMicros(), 1000'000);
|
||||
}
|
||||
|
||||
void SocketTest::SocketSendRecvWithEcn(const IPAddress& loopback) {
|
||||
StreamSink sink;
|
||||
std::unique_ptr<Socket> socket(
|
||||
socket_factory_->CreateSocket(loopback.family(), SOCK_DGRAM));
|
||||
EXPECT_EQ(0, socket->Bind(SocketAddress(loopback, 0)));
|
||||
SocketAddress address = socket->GetLocalAddress();
|
||||
sink.Monitor(socket.get());
|
||||
rtc::Buffer buffer;
|
||||
Socket::ReceiveBuffer receive_buffer(buffer);
|
||||
|
||||
socket->SendTo("foo", 3, address);
|
||||
EXPECT_TRUE_WAIT(sink.Check(socket.get(), SSE_READ), kTimeout);
|
||||
ASSERT_GT(socket->RecvFrom(receive_buffer), 0);
|
||||
EXPECT_EQ(receive_buffer.ecn, EcnMarking::kNotEct);
|
||||
|
||||
socket->SetOption(Socket::OPT_SEND_ECN, 1); // Ect(1)
|
||||
socket->SetOption(Socket::OPT_RECV_ECN, 1);
|
||||
|
||||
socket->SendTo("bar", 3, address);
|
||||
EXPECT_TRUE_WAIT(sink.Check(socket.get(), SSE_READ), kTimeout);
|
||||
ASSERT_GT(socket->RecvFrom(receive_buffer), 0);
|
||||
EXPECT_EQ(receive_buffer.ecn, EcnMarking::kEct1);
|
||||
|
||||
socket->SetOption(Socket::OPT_SEND_ECN, 2); // Ect(0)
|
||||
socket->SendTo("bar", 3, address);
|
||||
EXPECT_TRUE_WAIT(sink.Check(socket.get(), SSE_READ), kTimeout);
|
||||
ASSERT_GT(socket->RecvFrom(receive_buffer), 0);
|
||||
EXPECT_EQ(receive_buffer.ecn, EcnMarking::kEct0);
|
||||
|
||||
socket->SetOption(Socket::OPT_SEND_ECN, 3); // Ce
|
||||
socket->SendTo("bar", 3, address);
|
||||
EXPECT_TRUE_WAIT(sink.Check(socket.get(), SSE_READ), kTimeout);
|
||||
ASSERT_GT(socket->RecvFrom(receive_buffer), 0);
|
||||
EXPECT_EQ(receive_buffer.ecn, EcnMarking::kCe);
|
||||
}
|
||||
|
||||
} // namespace rtc
|
||||
|
|
|
@ -64,6 +64,8 @@ class SocketTest : public ::testing::Test {
|
|||
void TestSocketRecvTimestampIPv6();
|
||||
void TestUdpSocketRecvTimestampUseRtcEpochIPv4();
|
||||
void TestUdpSocketRecvTimestampUseRtcEpochIPv6();
|
||||
void TestSocketSendRecvWithEcnIPV4();
|
||||
void TestSocketSendRecvWithEcnIPV6();
|
||||
|
||||
static const int kTimeout = 5000; // ms
|
||||
const IPAddress kIPv4Loopback;
|
||||
|
@ -95,6 +97,7 @@ class SocketTest : public ::testing::Test {
|
|||
void GetSetOptionsInternal(const IPAddress& loopback);
|
||||
void SocketRecvTimestamp(const IPAddress& loopback);
|
||||
void UdpSocketRecvTimestampUseRtcEpoch(const IPAddress& loopback);
|
||||
void SocketSendRecvWithEcn(const IPAddress& loopback);
|
||||
|
||||
SocketFactory* socket_factory_;
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue