mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-13 05:40:42 +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(
|
NotifyPacketReceived(rtc::ReceivedPacket(
|
||||||
rtc::MakeArrayView(buf, read), rtc::SocketAddress(),
|
rtc::MakeArrayView(buf, read), rtc::SocketAddress(),
|
||||||
webrtc::Timestamp::Micros(rtc::TimeMicros()),
|
webrtc::Timestamp::Micros(rtc::TimeMicros()),
|
||||||
rtc::ReceivedPacket::kDtlsDecrypted));
|
rtc::EcnMarking::kNotEct, rtc::ReceivedPacket::kDtlsDecrypted));
|
||||||
} else if (ret == rtc::SR_EOS) {
|
} else if (ret == rtc::SR_EOS) {
|
||||||
// Remote peer shut down the association with no error.
|
// Remote peer shut down the association with no error.
|
||||||
RTC_LOG(LS_INFO) << ToString() << ": DTLS transport closed by remote";
|
RTC_LOG(LS_INFO) << ToString() << ": DTLS transport closed by remote";
|
||||||
|
|
|
@ -34,8 +34,8 @@ TEST(PacketTransportInternal,
|
||||||
EXPECT_EQ(packet.decryption_info(),
|
EXPECT_EQ(packet.decryption_info(),
|
||||||
rtc::ReceivedPacket::kDtlsDecrypted);
|
rtc::ReceivedPacket::kDtlsDecrypted);
|
||||||
});
|
});
|
||||||
packet_transport.NotifyPacketReceived(
|
packet_transport.NotifyPacketReceived(rtc::ReceivedPacket(
|
||||||
rtc::ReceivedPacket({}, rtc::SocketAddress(), absl::nullopt,
|
{}, rtc::SocketAddress(), absl::nullopt, rtc::EcnMarking::kNotEct,
|
||||||
rtc::ReceivedPacket::kDtlsDecrypted));
|
rtc::ReceivedPacket::kDtlsDecrypted));
|
||||||
|
|
||||||
packet_transport.DeregisterReceivedPacketCallback(&receiver);
|
packet_transport.DeregisterReceivedPacketCallback(&receiver);
|
||||||
|
|
|
@ -1046,6 +1046,7 @@ rtc_library("threading") {
|
||||||
"../api/task_queue:pending_task_safety_flag",
|
"../api/task_queue:pending_task_safety_flag",
|
||||||
"../api/units:time_delta",
|
"../api/units:time_delta",
|
||||||
"../system_wrappers:field_trial",
|
"../system_wrappers:field_trial",
|
||||||
|
"./network:ecn_marking",
|
||||||
"synchronization:mutex",
|
"synchronization:mutex",
|
||||||
"system:no_unique_address",
|
"system:no_unique_address",
|
||||||
"system:rtc_export",
|
"system:rtc_export",
|
||||||
|
@ -1092,6 +1093,7 @@ rtc_library("socket") {
|
||||||
":macromagic",
|
":macromagic",
|
||||||
":socket_address",
|
":socket_address",
|
||||||
"../api/units:timestamp",
|
"../api/units:timestamp",
|
||||||
|
"./network:ecn_marking",
|
||||||
"system:rtc_export",
|
"system:rtc_export",
|
||||||
"third_party/sigslot",
|
"third_party/sigslot",
|
||||||
]
|
]
|
||||||
|
|
|
@ -145,9 +145,9 @@ void AsyncUDPSocket::OnReadEvent(Socket* socket) {
|
||||||
}
|
}
|
||||||
*receive_buffer.arrival_time += *socket_time_offset_;
|
*receive_buffer.arrival_time += *socket_time_offset_;
|
||||||
}
|
}
|
||||||
NotifyPacketReceived(ReceivedPacket(receive_buffer.payload,
|
NotifyPacketReceived(
|
||||||
receive_buffer.source_address,
|
ReceivedPacket(receive_buffer.payload, receive_buffer.source_address,
|
||||||
receive_buffer.arrival_time));
|
receive_buffer.arrival_time, receive_buffer.ecn));
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncUDPSocket::OnWriteEvent(Socket* socket) {
|
void AsyncUDPSocket::OnWriteEvent(Socket* socket) {
|
||||||
|
|
|
@ -17,6 +17,11 @@ rtc_library("sent_packet") {
|
||||||
absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
|
absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rtc_source_set("ecn_marking") {
|
||||||
|
visibility = [ "*" ]
|
||||||
|
sources = [ "ecn_marking.h" ]
|
||||||
|
}
|
||||||
|
|
||||||
rtc_library("received_packet") {
|
rtc_library("received_packet") {
|
||||||
visibility = [ "*" ]
|
visibility = [ "*" ]
|
||||||
sources = [
|
sources = [
|
||||||
|
@ -24,6 +29,7 @@ rtc_library("received_packet") {
|
||||||
"received_packet.h",
|
"received_packet.h",
|
||||||
]
|
]
|
||||||
deps = [
|
deps = [
|
||||||
|
":ecn_marking",
|
||||||
"..:socket_address",
|
"..:socket_address",
|
||||||
"../../api:array_view",
|
"../../api:array_view",
|
||||||
"../../api/units:timestamp",
|
"../../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,
|
ReceivedPacket::ReceivedPacket(rtc::ArrayView<const uint8_t> payload,
|
||||||
const SocketAddress& source_address,
|
const SocketAddress& source_address,
|
||||||
absl::optional<webrtc::Timestamp> arrival_time,
|
absl::optional<webrtc::Timestamp> arrival_time,
|
||||||
|
EcnMarking ecn,
|
||||||
DecryptionInfo decryption)
|
DecryptionInfo decryption)
|
||||||
: payload_(payload),
|
: payload_(payload),
|
||||||
arrival_time_(std::move(arrival_time)),
|
arrival_time_(std::move(arrival_time)),
|
||||||
source_address_(source_address),
|
source_address_(source_address),
|
||||||
|
ecn_(ecn),
|
||||||
decryption_info_(decryption) {}
|
decryption_info_(decryption) {}
|
||||||
|
|
||||||
ReceivedPacket ReceivedPacket::CopyAndSet(
|
ReceivedPacket ReceivedPacket::CopyAndSet(
|
||||||
DecryptionInfo decryption_info) const {
|
DecryptionInfo decryption_info) const {
|
||||||
return ReceivedPacket(payload_, source_address_, arrival_time_,
|
return ReceivedPacket(payload_, source_address_, arrival_time_, ecn_,
|
||||||
decryption_info);
|
decryption_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include "absl/types/optional.h"
|
#include "absl/types/optional.h"
|
||||||
#include "api/array_view.h"
|
#include "api/array_view.h"
|
||||||
#include "api/units/timestamp.h"
|
#include "api/units/timestamp.h"
|
||||||
|
#include "rtc_base/network/ecn_marking.h"
|
||||||
#include "rtc_base/socket_address.h"
|
#include "rtc_base/socket_address.h"
|
||||||
#include "rtc_base/system/rtc_export.h"
|
#include "rtc_base/system/rtc_export.h"
|
||||||
|
|
||||||
|
@ -38,6 +39,7 @@ class RTC_EXPORT ReceivedPacket {
|
||||||
ReceivedPacket(rtc::ArrayView<const uint8_t> payload,
|
ReceivedPacket(rtc::ArrayView<const uint8_t> payload,
|
||||||
const SocketAddress& source_address,
|
const SocketAddress& source_address,
|
||||||
absl::optional<webrtc::Timestamp> arrival_time = absl::nullopt,
|
absl::optional<webrtc::Timestamp> arrival_time = absl::nullopt,
|
||||||
|
EcnMarking ecn = EcnMarking::kNotEct,
|
||||||
DecryptionInfo decryption = kNotDecrypted);
|
DecryptionInfo decryption = kNotDecrypted);
|
||||||
|
|
||||||
ReceivedPacket CopyAndSet(DecryptionInfo decryption_info) const;
|
ReceivedPacket CopyAndSet(DecryptionInfo decryption_info) const;
|
||||||
|
@ -52,6 +54,9 @@ class RTC_EXPORT ReceivedPacket {
|
||||||
return arrival_time_;
|
return arrival_time_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// L4S Explicit Congestion Notification.
|
||||||
|
EcnMarking ecn() const { return ecn_; }
|
||||||
|
|
||||||
const DecryptionInfo& decryption_info() const { return decryption_info_; }
|
const DecryptionInfo& decryption_info() const { return decryption_info_; }
|
||||||
|
|
||||||
static ReceivedPacket CreateFromLegacy(
|
static ReceivedPacket CreateFromLegacy(
|
||||||
|
@ -73,6 +78,7 @@ class RTC_EXPORT ReceivedPacket {
|
||||||
rtc::ArrayView<const uint8_t> payload_;
|
rtc::ArrayView<const uint8_t> payload_;
|
||||||
absl::optional<webrtc::Timestamp> arrival_time_;
|
absl::optional<webrtc::Timestamp> arrival_time_;
|
||||||
const SocketAddress& source_address_;
|
const SocketAddress& source_address_;
|
||||||
|
EcnMarking ecn_;
|
||||||
DecryptionInfo decryption_info_;
|
DecryptionInfo decryption_info_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -48,6 +48,7 @@
|
||||||
#include "rtc_base/event.h"
|
#include "rtc_base/event.h"
|
||||||
#include "rtc_base/ip_address.h"
|
#include "rtc_base/ip_address.h"
|
||||||
#include "rtc_base/logging.h"
|
#include "rtc_base/logging.h"
|
||||||
|
#include "rtc_base/network/ecn_marking.h"
|
||||||
#include "rtc_base/network_monitor.h"
|
#include "rtc_base/network_monitor.h"
|
||||||
#include "rtc_base/synchronization/mutex.h"
|
#include "rtc_base/synchronization/mutex.h"
|
||||||
#include "rtc_base/time_utils.h"
|
#include "rtc_base/time_utils.h"
|
||||||
|
@ -108,6 +109,33 @@ typedef char* SockOptArg;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace {
|
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 {
|
class ScopedSetTrue {
|
||||||
public:
|
public:
|
||||||
ScopedSetTrue(bool* value) : value_(value) {
|
ScopedSetTrue(bool* value) : value_(value) {
|
||||||
|
@ -125,6 +153,7 @@ class ScopedSetTrue {
|
||||||
bool IsScmTimeStampExperimentDisabled() {
|
bool IsScmTimeStampExperimentDisabled() {
|
||||||
return webrtc::field_trial::IsDisabled("WebRTC-SCM-Timestamp");
|
return webrtc::field_trial::IsDisabled("WebRTC-SCM-Timestamp");
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace rtc {
|
namespace rtc {
|
||||||
|
@ -314,8 +343,19 @@ int PhysicalSocket::GetOption(Option opt, int* value) {
|
||||||
#if defined(WEBRTC_POSIX)
|
#if defined(WEBRTC_POSIX)
|
||||||
// unshift DSCP value to get six most significant bits of IP DiffServ field
|
// unshift DSCP value to get six most significant bits of IP DiffServ field
|
||||||
*value >>= 2;
|
*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
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -329,10 +369,13 @@ int PhysicalSocket::SetOption(Option opt, int value) {
|
||||||
value = (value) ? IP_PMTUDISC_DO : IP_PMTUDISC_DONT;
|
value = (value) ? IP_PMTUDISC_DO : IP_PMTUDISC_DONT;
|
||||||
#endif
|
#endif
|
||||||
} else if (opt == OPT_DSCP) {
|
} else if (opt == OPT_DSCP) {
|
||||||
#if defined(WEBRTC_POSIX)
|
// IP DiffServ consists of DSCP 6 most significant, ECN 2 least
|
||||||
// shift DSCP value to fit six most significant bits of IP DiffServ field
|
// significant.
|
||||||
value <<= 2;
|
dscp_ = value << 2;
|
||||||
#endif
|
value = dscp_ + (ecn_ & kEcnMask);
|
||||||
|
} else if (opt == OPT_SEND_ECN) {
|
||||||
|
ecn_ = value;
|
||||||
|
value = dscp_ + (ecn_ & kEcnMask);
|
||||||
}
|
}
|
||||||
#if defined(WEBRTC_POSIX)
|
#if defined(WEBRTC_POSIX)
|
||||||
if (sopt == IPV6_TCLASS) {
|
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 PhysicalSocket::Recv(void* buffer, size_t length, int64_t* timestamp) {
|
||||||
int received =
|
int received = DoReadFromSocket(buffer, length, /*out_addr*/ nullptr,
|
||||||
DoReadFromSocket(buffer, length, /*out_addr*/ nullptr, timestamp);
|
timestamp, /*ecn=*/nullptr);
|
||||||
if ((received == 0) && (length != 0)) {
|
if ((received == 0) && (length != 0)) {
|
||||||
// Note: on graceful shutdown, recv can return 0. In this case, we
|
// Note: on graceful shutdown, recv can return 0. In this case, we
|
||||||
// pretend it is blocking, and then signal close, so that simplifying
|
// pretend it is blocking, and then signal close, so that simplifying
|
||||||
|
@ -431,7 +474,7 @@ int PhysicalSocket::RecvFrom(void* buffer,
|
||||||
size_t length,
|
size_t length,
|
||||||
SocketAddress* out_addr,
|
SocketAddress* out_addr,
|
||||||
int64_t* timestamp) {
|
int64_t* timestamp) {
|
||||||
int received = DoReadFromSocket(buffer, length, out_addr, timestamp);
|
int received = DoReadFromSocket(buffer, length, out_addr, timestamp, nullptr);
|
||||||
|
|
||||||
UpdateLastError();
|
UpdateLastError();
|
||||||
int error = GetError();
|
int error = GetError();
|
||||||
|
@ -450,9 +493,9 @@ int PhysicalSocket::RecvFrom(ReceiveBuffer& buffer) {
|
||||||
static constexpr int BUF_SIZE = 64 * 1024;
|
static constexpr int BUF_SIZE = 64 * 1024;
|
||||||
buffer.payload.EnsureCapacity(BUF_SIZE);
|
buffer.payload.EnsureCapacity(BUF_SIZE);
|
||||||
|
|
||||||
int received =
|
int received = DoReadFromSocket(
|
||||||
DoReadFromSocket(buffer.payload.data(), buffer.payload.capacity(),
|
buffer.payload.data(), buffer.payload.capacity(), &buffer.source_address,
|
||||||
&buffer.source_address, ×tamp);
|
×tamp, ecn_ ? &buffer.ecn : nullptr);
|
||||||
buffer.payload.SetSize(received > 0 ? received : 0);
|
buffer.payload.SetSize(received > 0 ? received : 0);
|
||||||
if (received > 0 && timestamp != -1) {
|
if (received > 0 && timestamp != -1) {
|
||||||
buffer.arrival_time = webrtc::Timestamp::Micros(timestamp);
|
buffer.arrival_time = webrtc::Timestamp::Micros(timestamp);
|
||||||
|
@ -472,7 +515,8 @@ int PhysicalSocket::RecvFrom(ReceiveBuffer& buffer) {
|
||||||
int PhysicalSocket::DoReadFromSocket(void* buffer,
|
int PhysicalSocket::DoReadFromSocket(void* buffer,
|
||||||
size_t length,
|
size_t length,
|
||||||
SocketAddress* out_addr,
|
SocketAddress* out_addr,
|
||||||
int64_t* timestamp) {
|
int64_t* timestamp,
|
||||||
|
EcnMarking* ecn) {
|
||||||
sockaddr_storage addr_storage;
|
sockaddr_storage addr_storage;
|
||||||
socklen_t addr_len = sizeof(addr_storage);
|
socklen_t addr_len = sizeof(addr_storage);
|
||||||
sockaddr* addr = reinterpret_cast<sockaddr*>(&addr_storage);
|
sockaddr* addr = reinterpret_cast<sockaddr*>(&addr_storage);
|
||||||
|
@ -487,8 +531,10 @@ int PhysicalSocket::DoReadFromSocket(void* buffer,
|
||||||
msg.msg_name = addr;
|
msg.msg_name = addr;
|
||||||
msg.msg_namelen = addr_len;
|
msg.msg_namelen = addr_len;
|
||||||
}
|
}
|
||||||
char control[CMSG_SPACE(sizeof(struct timeval))] = {};
|
// TODO(bugs.webrtc.org/15368): What size is needed? IPV6_TCLASS is supposed
|
||||||
if (timestamp) {
|
// 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;
|
*timestamp = -1;
|
||||||
msg.msg_control = &control;
|
msg.msg_control = &control;
|
||||||
msg.msg_controllen = sizeof(control);
|
msg.msg_controllen = sizeof(control);
|
||||||
|
@ -498,17 +544,23 @@ int PhysicalSocket::DoReadFromSocket(void* buffer,
|
||||||
// An error occured or shut down.
|
// An error occured or shut down.
|
||||||
return received;
|
return received;
|
||||||
}
|
}
|
||||||
if (timestamp) {
|
if (timestamp || ecn) {
|
||||||
struct cmsghdr* cmsg;
|
struct cmsghdr* cmsg;
|
||||||
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, 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)
|
if (cmsg->cmsg_level != SOL_SOCKET)
|
||||||
continue;
|
continue;
|
||||||
if (cmsg->cmsg_type == SCM_TIMESTAMP) {
|
if (timestamp && cmsg->cmsg_type == SCM_TIMESTAMP) {
|
||||||
timeval* ts = reinterpret_cast<timeval*>(CMSG_DATA(cmsg));
|
timeval* ts = reinterpret_cast<timeval*>(CMSG_DATA(cmsg));
|
||||||
*timestamp =
|
*timestamp =
|
||||||
rtc::kNumMicrosecsPerSec * static_cast<int64_t>(ts->tv_sec) +
|
rtc::kNumMicrosecsPerSec * static_cast<int64_t>(ts->tv_sec) +
|
||||||
static_cast<int64_t>(ts->tv_usec);
|
static_cast<int64_t>(ts->tv_usec);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -699,6 +751,34 @@ int PhysicalSocket::TranslateOption(Option opt, int* slevel, int* sopt) {
|
||||||
#else
|
#else
|
||||||
RTC_LOG(LS_WARNING) << "Socket::OPT_DSCP not supported.";
|
RTC_LOG(LS_WARNING) << "Socket::OPT_DSCP not supported.";
|
||||||
return -1;
|
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
|
#endif
|
||||||
case OPT_RTP_SENDTIME_EXTN_ID:
|
case OPT_RTP_SENDTIME_EXTN_ID:
|
||||||
return -1; // No logging is necessary as this not a OS socket option.
|
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,
|
int DoReadFromSocket(void* buffer,
|
||||||
size_t length,
|
size_t length,
|
||||||
SocketAddress* out_addr,
|
SocketAddress* out_addr,
|
||||||
int64_t* timestamp);
|
int64_t* timestamp,
|
||||||
|
EcnMarking* ecn);
|
||||||
|
|
||||||
void OnResolveResult(const webrtc::AsyncDnsResolverResult& resolver);
|
void OnResolveResult(const webrtc::AsyncDnsResolverResult& resolver);
|
||||||
|
|
||||||
|
@ -246,6 +247,8 @@ class PhysicalSocket : public Socket, public sigslot::has_slots<> {
|
||||||
int error_ RTC_GUARDED_BY(mutex_);
|
int error_ RTC_GUARDED_BY(mutex_);
|
||||||
ConnState state_;
|
ConnState state_;
|
||||||
std::unique_ptr<webrtc::AsyncDnsResolverInterface> resolver_;
|
std::unique_ptr<webrtc::AsyncDnsResolverInterface> resolver_;
|
||||||
|
uint8_t dscp_ = 0; // 6bit.
|
||||||
|
uint8_t ecn_ = 0; // 2bits.
|
||||||
|
|
||||||
#if !defined(NDEBUG)
|
#if !defined(NDEBUG)
|
||||||
std::string dbg_addr_;
|
std::string dbg_addr_;
|
||||||
|
|
|
@ -484,6 +484,19 @@ TEST_F(PhysicalSocketTest, TestSocketRecvTimestampIPv6ScmExperimentDisabled) {
|
||||||
}
|
}
|
||||||
#endif
|
#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
|
// Verify that if the socket was unable to be bound to a real network interface
|
||||||
// (not loopback), Bind will return an error.
|
// (not loopback), Bind will return an error.
|
||||||
TEST_F(PhysicalSocketTest,
|
TEST_F(PhysicalSocketTest,
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
|
|
||||||
#include "api/units/timestamp.h"
|
#include "api/units/timestamp.h"
|
||||||
#include "rtc_base/buffer.h"
|
#include "rtc_base/buffer.h"
|
||||||
|
#include "rtc_base/network/ecn_marking.h"
|
||||||
#include "rtc_base/socket_address.h"
|
#include "rtc_base/socket_address.h"
|
||||||
#include "rtc_base/system/rtc_export.h"
|
#include "rtc_base/system/rtc_export.h"
|
||||||
#include "rtc_base/third_party/sigslot/sigslot.h"
|
#include "rtc_base/third_party/sigslot/sigslot.h"
|
||||||
|
@ -91,6 +92,7 @@ class RTC_EXPORT Socket {
|
||||||
|
|
||||||
absl::optional<webrtc::Timestamp> arrival_time;
|
absl::optional<webrtc::Timestamp> arrival_time;
|
||||||
SocketAddress source_address;
|
SocketAddress source_address;
|
||||||
|
EcnMarking ecn = EcnMarking::kNotEct;
|
||||||
Buffer& payload;
|
Buffer& payload;
|
||||||
};
|
};
|
||||||
virtual ~Socket() {}
|
virtual ~Socket() {}
|
||||||
|
@ -144,6 +146,8 @@ class RTC_EXPORT Socket {
|
||||||
OPT_RTP_SENDTIME_EXTN_ID, // This is a non-traditional socket option param.
|
OPT_RTP_SENDTIME_EXTN_ID, // This is a non-traditional socket option param.
|
||||||
// This is specific to libjingle and will be used
|
// This is specific to libjingle and will be used
|
||||||
// if SendTime option is needed at socket level.
|
// 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 GetOption(Option opt, int* value) = 0;
|
||||||
virtual int SetOption(Option opt, int value) = 0;
|
virtual int SetOption(Option opt, int value) = 0;
|
||||||
|
|
|
@ -234,6 +234,15 @@ void SocketTest::TestUdpSocketRecvTimestampUseRtcEpochIPv6() {
|
||||||
UdpSocketRecvTimestampUseRtcEpoch(kIPv6Loopback);
|
UdpSocketRecvTimestampUseRtcEpoch(kIPv6Loopback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SocketTest::TestSocketSendRecvWithEcnIPV4() {
|
||||||
|
SocketSendRecvWithEcn(kIPv4Loopback);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SocketTest::TestSocketSendRecvWithEcnIPV6() {
|
||||||
|
MAYBE_SKIP_IPV6;
|
||||||
|
SocketSendRecvWithEcn(kIPv6Loopback);
|
||||||
|
}
|
||||||
|
|
||||||
// For unbound sockets, GetLocalAddress / GetRemoteAddress return AF_UNSPEC
|
// For unbound sockets, GetLocalAddress / GetRemoteAddress return AF_UNSPEC
|
||||||
// values on Windows, but an empty address of the same family on Linux/MacOS X.
|
// values on Windows, but an empty address of the same family on Linux/MacOS X.
|
||||||
bool IsUnspecOrEmptyIP(const IPAddress& address) {
|
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->SetOption(Socket::OPT_DSCP, desired_dscp));
|
||||||
ASSERT_NE(-1, socket->GetOption(Socket::OPT_DSCP, ¤t_dscp));
|
ASSERT_NE(-1, socket->GetOption(Socket::OPT_DSCP, ¤t_dscp));
|
||||||
ASSERT_EQ(desired_dscp, current_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
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1143,4 +1164,41 @@ void SocketTest::UdpSocketRecvTimestampUseRtcEpoch(const IPAddress& loopback) {
|
||||||
EXPECT_GT(packet_2->packet_time->us(), packet_1->packet_time->us());
|
EXPECT_GT(packet_2->packet_time->us(), packet_1->packet_time->us());
|
||||||
EXPECT_NEAR(packet_2->packet_time->us(), rtc::TimeMicros(), 1000'000);
|
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
|
} // namespace rtc
|
||||||
|
|
|
@ -64,6 +64,8 @@ class SocketTest : public ::testing::Test {
|
||||||
void TestSocketRecvTimestampIPv6();
|
void TestSocketRecvTimestampIPv6();
|
||||||
void TestUdpSocketRecvTimestampUseRtcEpochIPv4();
|
void TestUdpSocketRecvTimestampUseRtcEpochIPv4();
|
||||||
void TestUdpSocketRecvTimestampUseRtcEpochIPv6();
|
void TestUdpSocketRecvTimestampUseRtcEpochIPv6();
|
||||||
|
void TestSocketSendRecvWithEcnIPV4();
|
||||||
|
void TestSocketSendRecvWithEcnIPV6();
|
||||||
|
|
||||||
static const int kTimeout = 5000; // ms
|
static const int kTimeout = 5000; // ms
|
||||||
const IPAddress kIPv4Loopback;
|
const IPAddress kIPv4Loopback;
|
||||||
|
@ -95,6 +97,7 @@ class SocketTest : public ::testing::Test {
|
||||||
void GetSetOptionsInternal(const IPAddress& loopback);
|
void GetSetOptionsInternal(const IPAddress& loopback);
|
||||||
void SocketRecvTimestamp(const IPAddress& loopback);
|
void SocketRecvTimestamp(const IPAddress& loopback);
|
||||||
void UdpSocketRecvTimestampUseRtcEpoch(const IPAddress& loopback);
|
void UdpSocketRecvTimestampUseRtcEpoch(const IPAddress& loopback);
|
||||||
|
void SocketSendRecvWithEcn(const IPAddress& loopback);
|
||||||
|
|
||||||
SocketFactory* socket_factory_;
|
SocketFactory* socket_factory_;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue