From 3d37e06fed5762e342707ea541ab07282cb23f04 Mon Sep 17 00:00:00 2001 From: Artem Titov Date: Fri, 19 Feb 2021 20:26:32 +0100 Subject: [PATCH] Introduce default routes for network emulation Change-Id: If9bc941d54844e0f22147fb13e148ced1bc49c71 Bug: b/180750880 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/208227 Reviewed-by: Andrey Logvin Commit-Queue: Artem Titov Cr-Commit-Position: refs/heads/master@{#33310} --- .../network_emulation_interfaces.h | 9 ++ api/test/network_emulation_manager.h | 37 +++++ test/network/BUILD.gn | 1 + test/network/cross_traffic_unittest.cc | 15 +- test/network/network_emulation.cc | 140 +++++++++++++----- test/network/network_emulation.h | 67 ++++++--- test/network/network_emulation_manager.cc | 44 +++++- test/network/network_emulation_manager.h | 5 + test/network/network_emulation_unittest.cc | 45 ++++++ 9 files changed, 291 insertions(+), 72 deletions(-) diff --git a/api/test/network_emulation/network_emulation_interfaces.h b/api/test/network_emulation/network_emulation_interfaces.h index 36fb996549..152b69baf7 100644 --- a/api/test/network_emulation/network_emulation_interfaces.h +++ b/api/test/network_emulation/network_emulation_interfaces.h @@ -225,7 +225,16 @@ class EmulatedEndpoint : public EmulatedNetworkReceiverInterface { virtual absl::optional BindReceiver( uint16_t desired_port, EmulatedNetworkReceiverInterface* receiver) = 0; + // Unbinds receiver from the specified port. Do nothing if no receiver was + // binded before. virtual void UnbindReceiver(uint16_t port) = 0; + // Binds receiver that will accept all packets which arrived on any port + // for which there are no binded receiver. + virtual void BindDefaultReceiver( + EmulatedNetworkReceiverInterface* receiver) = 0; + // Unbinds default receiver. Do nothing if no default receiver was binded + // before. + virtual void UnbindDefaultReceiver() = 0; virtual rtc::IPAddress GetPeerLocalAddress() const = 0; private: diff --git a/api/test/network_emulation_manager.h b/api/test/network_emulation_manager.h index a08dc5aab6..ec51b290e0 100644 --- a/api/test/network_emulation_manager.h +++ b/api/test/network_emulation_manager.h @@ -68,6 +68,14 @@ struct EmulatedEndpointConfig { // Network type which will be used to represent endpoint to WebRTC. rtc::AdapterType type = rtc::AdapterType::ADAPTER_TYPE_UNKNOWN; StatsGatheringMode stats_gathering_mode = StatsGatheringMode::kDefault; + // Allow endpoint to send packets specifying source IP address different to + // the current endpoint IP address. If false endpoint will crash if attempt + // to send such packet will be done. + bool allow_send_packet_with_different_source_ip = false; + // Allow endpoint to receive packet with destination IP address different to + // the current endpoint IP address. If false endpoint will crash if such + // packet will arrive. + bool allow_receive_packets_with_different_dest_ip = false; }; struct EmulatedTURNServerConfig { @@ -226,6 +234,35 @@ class NetworkEmulationManager { virtual EmulatedRoute* CreateRoute( const std::vector& via_nodes) = 0; + // Creates a default route between endpoints going through specified network + // nodes. Default route is used for packet when there is no known route for + // packet's destination IP. + // + // This route is single direction only and describe how traffic that was + // sent by network interface |from| have to be delivered in case if routing + // was unspecified. Return object can be used to remove created route. The + // route must contains at least one network node inside it. + // + // Assume that E{0-9} are endpoints and N{0-9} are network nodes, then + // creation of the route have to follow these rules: + // 1. A route consists of a source endpoint, an ordered list of one or + // more network nodes, and a destination endpoint. + // 2. If (E1, ..., E2) is a route, then E1 != E2. + // In other words, the source and the destination may not be the same. + // 3. Given two simultaneously existing routes (E1, ..., E2) and + // (E3, ..., E4), either E1 != E3 or E2 != E4. + // In other words, there may be at most one route from any given source + // endpoint to any given destination endpoint. + // 4. Given two simultaneously existing routes (E1, ..., N1, ..., E2) + // and (E3, ..., N2, ..., E4), either N1 != N2 or E2 != E4. + // In other words, a network node may not belong to two routes that lead + // to the same destination endpoint. + // 5. Any node N can belong to only one default route. + virtual EmulatedRoute* CreateDefaultRoute( + EmulatedEndpoint* from, + const std::vector& via_nodes, + EmulatedEndpoint* to) = 0; + // Removes route previously created by CreateRoute(...). // Caller mustn't call this function with route, that have been already // removed earlier. Removing a route that is currently in use will lead to diff --git a/test/network/BUILD.gn b/test/network/BUILD.gn index bcd25c060f..a4fa900ab1 100644 --- a/test/network/BUILD.gn +++ b/test/network/BUILD.gn @@ -46,6 +46,7 @@ rtc_library("emulated_network") { "../../api:simulated_network_api", "../../api:time_controller", "../../api/numerics", + "../../api/test/network_emulation", "../../api/transport:stun_types", "../../api/units:data_rate", "../../api/units:data_size", diff --git a/test/network/cross_traffic_unittest.cc b/test/network/cross_traffic_unittest.cc index ac45e11d5b..2744a90ce3 100644 --- a/test/network/cross_traffic_unittest.cc +++ b/test/network/cross_traffic_unittest.cc @@ -49,15 +49,12 @@ struct TrafficCounterFixture { SimulatedClock clock{0}; CountingReceiver counter; TaskQueueForTest task_queue_; - EmulatedEndpointImpl endpoint{ - /*id=*/1, - absl::nullopt, - rtc::IPAddress(kTestIpAddress), - EmulatedEndpointConfig::StatsGatheringMode::kDefault, - /*is_enabled=*/true, - /*type=*/rtc::AdapterType::ADAPTER_TYPE_UNKNOWN, - &task_queue_, - &clock}; + EmulatedEndpointImpl endpoint{EmulatedEndpointImpl::Options{ + /*id=*/1, + rtc::IPAddress(kTestIpAddress), + EmulatedEndpointConfig(), + }, + /*is_enabled=*/true, &task_queue_, &clock}; }; } // namespace diff --git a/test/network/network_emulation.cc b/test/network/network_emulation.cc index cbea37a937..9251cfe2b4 100644 --- a/test/network/network_emulation.cc +++ b/test/network/network_emulation.cc @@ -13,9 +13,11 @@ #include #include #include +#include #include "absl/types/optional.h" #include "api/numerics/samples_stats_counter.h" +#include "api/test/network_emulation/network_emulation_interfaces.h" #include "api/units/data_size.h" #include "rtc_base/logging.h" @@ -346,6 +348,9 @@ void NetworkRouterNode::OnPacketReceived(EmulatedIpPacket packet) { } auto receiver_it = routing_.find(packet.to.ipaddr()); if (receiver_it == routing_.end()) { + if (default_receiver_.has_value()) { + (*default_receiver_)->OnPacketReceived(std::move(packet)); + } return; } RTC_CHECK(receiver_it != routing_.end()); @@ -370,6 +375,23 @@ void NetworkRouterNode::RemoveReceiver(const rtc::IPAddress& dest_ip) { routing_.erase(dest_ip); } +void NetworkRouterNode::SetDefaultReceiver( + EmulatedNetworkReceiverInterface* receiver) { + task_queue_->PostTask([=] { + RTC_DCHECK_RUN_ON(task_queue_); + if (default_receiver_.has_value()) { + RTC_CHECK_EQ(*default_receiver_, receiver) + << "Router already default receiver"; + } + default_receiver_ = receiver; + }); +} + +void NetworkRouterNode::RemoveDefaultReceiver() { + RTC_DCHECK_RUN_ON(task_queue_); + default_receiver_ = absl::nullopt; +} + void NetworkRouterNode::SetWatcher( std::function watcher) { task_queue_->PostTask([=] { @@ -415,64 +437,72 @@ void EmulatedNetworkNode::ClearRoute(const rtc::IPAddress& receiver_ip, EmulatedNetworkNode::~EmulatedNetworkNode() = default; -EmulatedEndpointImpl::EmulatedEndpointImpl( - uint64_t id, - absl::optional name, - const rtc::IPAddress& ip, - EmulatedEndpointConfig::StatsGatheringMode stats_gathering_mode, - bool is_enabled, - rtc::AdapterType type, - rtc::TaskQueue* task_queue, - Clock* clock) - : id_(id), - log_name_(ip.ToString() + " (" + name.value_or("") + ")"), - peer_local_addr_(ip), - stats_gathering_mode_(stats_gathering_mode), +EmulatedEndpointImpl::Options::Options(uint64_t id, + const rtc::IPAddress& ip, + const EmulatedEndpointConfig& config) + : id(id), + ip(ip), + stats_gathering_mode(config.stats_gathering_mode), + type(config.type), + allow_send_packet_with_different_source_ip( + config.allow_send_packet_with_different_source_ip), + allow_receive_packets_with_different_dest_ip( + config.allow_send_packet_with_different_source_ip), + log_name(ip.ToString() + " (" + config.name.value_or("") + ")") {} + +EmulatedEndpointImpl::EmulatedEndpointImpl(const Options& options, + bool is_enabled, + rtc::TaskQueue* task_queue, + Clock* clock) + : options_(options), is_enabled_(is_enabled), - type_(type), clock_(clock), task_queue_(task_queue), router_(task_queue_), next_port_(kFirstEphemeralPort), - stats_builder_(peer_local_addr_) { + stats_builder_(options_.ip) { constexpr int kIPv4NetworkPrefixLength = 24; constexpr int kIPv6NetworkPrefixLength = 64; int prefix_length = 0; - if (ip.family() == AF_INET) { + if (options_.ip.family() == AF_INET) { prefix_length = kIPv4NetworkPrefixLength; - } else if (ip.family() == AF_INET6) { + } else if (options_.ip.family() == AF_INET6) { prefix_length = kIPv6NetworkPrefixLength; } - rtc::IPAddress prefix = TruncateIP(ip, prefix_length); + rtc::IPAddress prefix = TruncateIP(options_.ip, prefix_length); network_ = std::make_unique( - ip.ToString(), "Endpoint id=" + std::to_string(id_), prefix, - prefix_length, type_); - network_->AddIP(ip); + options_.ip.ToString(), "Endpoint id=" + std::to_string(options_.id), + prefix, prefix_length, options_.type); + network_->AddIP(options_.ip); enabled_state_checker_.Detach(); - RTC_LOG(INFO) << "Created emulated endpoint " << log_name_ << "; id=" << id_; + RTC_LOG(INFO) << "Created emulated endpoint " << options_.log_name + << "; id=" << options_.id; } EmulatedEndpointImpl::~EmulatedEndpointImpl() = default; uint64_t EmulatedEndpointImpl::GetId() const { - return id_; + return options_.id; } void EmulatedEndpointImpl::SendPacket(const rtc::SocketAddress& from, const rtc::SocketAddress& to, rtc::CopyOnWriteBuffer packet_data, uint16_t application_overhead) { - RTC_CHECK(from.ipaddr() == peer_local_addr_); + if (!options_.allow_send_packet_with_different_source_ip) { + RTC_CHECK(from.ipaddr() == options_.ip); + } EmulatedIpPacket packet(from, to, std::move(packet_data), clock_->CurrentTime(), application_overhead); task_queue_->PostTask([this, packet = std::move(packet)]() mutable { RTC_DCHECK_RUN_ON(task_queue_); - stats_builder_.OnPacketSent( - packet.arrival_time, clock_->CurrentTime(), packet.to.ipaddr(), - DataSize::Bytes(packet.ip_packet_size()), stats_gathering_mode_); + stats_builder_.OnPacketSent(packet.arrival_time, clock_->CurrentTime(), + packet.to.ipaddr(), + DataSize::Bytes(packet.ip_packet_size()), + options_.stats_gathering_mode); - if (packet.to.ipaddr() == peer_local_addr_) { + if (packet.to.ipaddr() == options_.ip) { OnPacketReceived(std::move(packet)); } else { router_.OnPacketReceived(std::move(packet)); @@ -499,15 +529,16 @@ absl::optional EmulatedEndpointImpl::BindReceiver( } } RTC_CHECK(port != 0) << "Can't find free port for receiver in endpoint " - << log_name_ << "; id=" << id_; + << options_.log_name << "; id=" << options_.id; bool result = port_to_receiver_.insert({port, receiver}).second; if (!result) { RTC_LOG(INFO) << "Can't bind receiver to used port " << desired_port - << " in endpoint " << log_name_ << "; id=" << id_; + << " in endpoint " << options_.log_name + << "; id=" << options_.id; return absl::nullopt; } - RTC_LOG(INFO) << "New receiver is binded to endpoint " << log_name_ - << "; id=" << id_ << " on port " << port; + RTC_LOG(INFO) << "New receiver is binded to endpoint " << options_.log_name + << "; id=" << options_.id << " on port " << port; return port; } @@ -523,33 +554,60 @@ uint16_t EmulatedEndpointImpl::NextPort() { void EmulatedEndpointImpl::UnbindReceiver(uint16_t port) { rtc::CritScope crit(&receiver_lock_); + RTC_LOG(INFO) << "Receiver is removed on port " << port << " from endpoint " + << options_.log_name << "; id=" << options_.id; port_to_receiver_.erase(port); } +void EmulatedEndpointImpl::BindDefaultReceiver( + EmulatedNetworkReceiverInterface* receiver) { + rtc::CritScope crit(&receiver_lock_); + RTC_CHECK(!default_receiver_.has_value()) + << "Endpoint " << options_.log_name << "; id=" << options_.id + << " already has default receiver"; + RTC_LOG(INFO) << "Default receiver is binded to endpoint " + << options_.log_name << "; id=" << options_.id; + default_receiver_ = receiver; +} + +void EmulatedEndpointImpl::UnbindDefaultReceiver() { + rtc::CritScope crit(&receiver_lock_); + RTC_LOG(INFO) << "Default receiver is removed from endpoint " + << options_.log_name << "; id=" << options_.id; + default_receiver_ = absl::nullopt; +} + rtc::IPAddress EmulatedEndpointImpl::GetPeerLocalAddress() const { - return peer_local_addr_; + return options_.ip; } void EmulatedEndpointImpl::OnPacketReceived(EmulatedIpPacket packet) { RTC_DCHECK_RUN_ON(task_queue_); - RTC_CHECK(packet.to.ipaddr() == peer_local_addr_) - << "Routing error: wrong destination endpoint. Packet.to.ipaddr()=: " - << packet.to.ipaddr().ToString() - << "; Receiver peer_local_addr_=" << peer_local_addr_.ToString(); + if (options_.allow_receive_packets_with_different_dest_ip) { + RTC_CHECK(packet.to.ipaddr() == options_.ip) + << "Routing error: wrong destination endpoint. Packet.to.ipaddr()=: " + << packet.to.ipaddr().ToString() + << "; Receiver options_.ip=" << options_.ip.ToString(); + } rtc::CritScope crit(&receiver_lock_); stats_builder_.OnPacketReceived(clock_->CurrentTime(), packet.from.ipaddr(), DataSize::Bytes(packet.ip_packet_size()), - stats_gathering_mode_); + options_.stats_gathering_mode); auto it = port_to_receiver_.find(packet.to.port()); if (it == port_to_receiver_.end()) { + if (default_receiver_.has_value()) { + (*default_receiver_)->OnPacketReceived(std::move(packet)); + return; + } // It can happen, that remote peer closed connection, but there still some // packets, that are going to it. It can happen during peer connection close // process: one peer closed connection, second still sending data. - RTC_LOG(INFO) << "Drop packet: no receiver registered in " << log_name_ - << "; id=" << id_ << " on port " << packet.to.port(); + RTC_LOG(INFO) << "Drop packet: no receiver registered in " + << options_.log_name << "; id=" << options_.id << " on port " + << packet.to.port(); stats_builder_.OnPacketDropped(packet.from.ipaddr(), DataSize::Bytes(packet.ip_packet_size()), - stats_gathering_mode_); + options_.stats_gathering_mode); return; } // Endpoint assumes frequent calls to bind and unbind methods, so it holds diff --git a/test/network/network_emulation.h b/test/network/network_emulation.h index 6d56d50ad5..64720fe833 100644 --- a/test/network/network_emulation.h +++ b/test/network/network_emulation.h @@ -418,6 +418,10 @@ class LinkEmulation : public EmulatedNetworkReceiverInterface { uint64_t next_packet_id_ RTC_GUARDED_BY(task_queue_) = 1; }; +// Represents a component responsible for routing packets based on their IP +// address. All possible routes have to be set explicitly before packet for +// desired destination will be seen for the first time. If route is unknown +// the packet will be silently dropped. class NetworkRouterNode : public EmulatedNetworkReceiverInterface { public: explicit NetworkRouterNode(rtc::TaskQueue* task_queue); @@ -426,11 +430,17 @@ class NetworkRouterNode : public EmulatedNetworkReceiverInterface { void SetReceiver(const rtc::IPAddress& dest_ip, EmulatedNetworkReceiverInterface* receiver); void RemoveReceiver(const rtc::IPAddress& dest_ip); + // Sets a default receive that will be used for all incoming packets for which + // there is no specific receiver binded to their destination port. + void SetDefaultReceiver(EmulatedNetworkReceiverInterface* receiver); + void RemoveDefaultReceiver(); void SetWatcher(std::function watcher); void SetFilter(std::function filter); private: rtc::TaskQueue* const task_queue_; + absl::optional default_receiver_ + RTC_GUARDED_BY(task_queue_); std::map routing_ RTC_GUARDED_BY(task_queue_); std::function watcher_ @@ -481,15 +491,33 @@ class EmulatedNetworkNode : public EmulatedNetworkReceiverInterface { // from other EmulatedNetworkNodes. class EmulatedEndpointImpl : public EmulatedEndpoint { public: - EmulatedEndpointImpl( - uint64_t id, - absl::optional name, - const rtc::IPAddress& ip, - EmulatedEndpointConfig::StatsGatheringMode stats_gathering_mode, - bool is_enabled, - rtc::AdapterType type, - rtc::TaskQueue* task_queue, - Clock* clock); + struct Options { + Options(uint64_t id, + const rtc::IPAddress& ip, + const EmulatedEndpointConfig& config); + + // TODO(titovartem) check if we can remove id. + uint64_t id; + // Endpoint local IP address. + rtc::IPAddress ip; + EmulatedEndpointConfig::StatsGatheringMode stats_gathering_mode; + rtc::AdapterType type; + // Allow endpoint to send packets specifying source IP address different to + // the current endpoint IP address. If false endpoint will crash if attempt + // to send such packet will be done. + bool allow_send_packet_with_different_source_ip; + // Allow endpoint to receive packet with destination IP address different to + // the current endpoint IP address. If false endpoint will crash if such + // packet will arrive. + bool allow_receive_packets_with_different_dest_ip; + // Name of the endpoint used for logging purposes. + std::string log_name; + }; + + EmulatedEndpointImpl(const Options& options, + bool is_enabled, + rtc::TaskQueue* task_queue, + Clock* clock); ~EmulatedEndpointImpl() override; uint64_t GetId() const; @@ -505,6 +533,8 @@ class EmulatedEndpointImpl : public EmulatedEndpoint { uint16_t desired_port, EmulatedNetworkReceiverInterface* receiver) override; void UnbindReceiver(uint16_t port) override; + void BindDefaultReceiver(EmulatedNetworkReceiverInterface* receiver) override; + void UnbindDefaultReceiver() override; rtc::IPAddress GetPeerLocalAddress() const override; @@ -526,19 +556,16 @@ class EmulatedEndpointImpl : public EmulatedEndpoint { rtc::RecursiveCriticalSection receiver_lock_; SequenceChecker enabled_state_checker_; - const uint64_t id_; - const std::string log_name_; - // Peer's local IP address for this endpoint network interface. - const rtc::IPAddress peer_local_addr_; - const EmulatedEndpointConfig::StatsGatheringMode stats_gathering_mode_; + const Options options_; bool is_enabled_ RTC_GUARDED_BY(enabled_state_checker_); - const rtc::AdapterType type_; Clock* const clock_; rtc::TaskQueue* const task_queue_; std::unique_ptr network_; NetworkRouterNode router_; uint16_t next_port_ RTC_GUARDED_BY(receiver_lock_); + absl::optional default_receiver_ + RTC_GUARDED_BY(receiver_lock_); std::map port_to_receiver_ RTC_GUARDED_BY(receiver_lock_); @@ -549,13 +576,19 @@ class EmulatedRoute { public: EmulatedRoute(EmulatedEndpointImpl* from, std::vector via_nodes, - EmulatedEndpointImpl* to) - : from(from), via_nodes(std::move(via_nodes)), to(to), active(true) {} + EmulatedEndpointImpl* to, + bool is_default) + : from(from), + via_nodes(std::move(via_nodes)), + to(to), + active(true), + is_default(is_default) {} EmulatedEndpointImpl* from; std::vector via_nodes; EmulatedEndpointImpl* to; bool active; + bool is_default; }; // This object is immutable and so thread safe. diff --git a/test/network/network_emulation_manager.cc b/test/network/network_emulation_manager.cc index 70e9c67491..d88ffb72cb 100644 --- a/test/network/network_emulation_manager.cc +++ b/test/network/network_emulation_manager.cc @@ -107,8 +107,8 @@ EmulatedEndpoint* NetworkEmulationManagerImpl::CreateEndpoint( bool res = used_ip_addresses_.insert(*ip).second; RTC_CHECK(res) << "IP=" << ip->ToString() << " already in use"; auto node = std::make_unique( - next_node_id_++, config.name, *ip, config.stats_gathering_mode, - config.start_as_enabled, config.type, &task_queue_, clock_); + EmulatedEndpointImpl::Options(next_node_id_++, *ip, config), + config.start_as_enabled, &task_queue_, clock_); EmulatedEndpoint* out = node.get(); endpoints_.push_back(std::move(node)); return out; @@ -148,7 +148,7 @@ EmulatedRoute* NetworkEmulationManagerImpl::CreateRoute( std::unique_ptr route = std::make_unique( static_cast(from), std::move(via_nodes), - static_cast(to)); + static_cast(to), /*is_default=*/false); EmulatedRoute* out = route.get(); routes_.push_back(std::move(route)); return out; @@ -161,16 +161,50 @@ EmulatedRoute* NetworkEmulationManagerImpl::CreateRoute( return CreateRoute(from, via_nodes, to); } +EmulatedRoute* NetworkEmulationManagerImpl::CreateDefaultRoute( + EmulatedEndpoint* from, + const std::vector& via_nodes, + EmulatedEndpoint* to) { + // Because endpoint has no send node by default at least one should be + // provided here. + RTC_CHECK(!via_nodes.empty()); + + static_cast(from)->router()->SetDefaultReceiver( + via_nodes[0]); + EmulatedNetworkNode* cur_node = via_nodes[0]; + for (size_t i = 1; i < via_nodes.size(); ++i) { + cur_node->router()->SetDefaultReceiver(via_nodes[i]); + cur_node = via_nodes[i]; + } + cur_node->router()->SetDefaultReceiver(to); + + std::unique_ptr route = std::make_unique( + static_cast(from), std::move(via_nodes), + static_cast(to), /*is_default=*/true); + EmulatedRoute* out = route.get(); + routes_.push_back(std::move(route)); + return out; +} + void NetworkEmulationManagerImpl::ClearRoute(EmulatedRoute* route) { RTC_CHECK(route->active) << "Route already cleared"; task_queue_.SendTask( [route]() { // Remove receiver from intermediate nodes. for (auto* node : route->via_nodes) { - node->router()->RemoveReceiver(route->to->GetPeerLocalAddress()); + if (route->is_default) { + node->router()->RemoveDefaultReceiver(); + } else { + node->router()->RemoveReceiver(route->to->GetPeerLocalAddress()); + } } // Remove destination endpoint from source endpoint's router. - route->from->router()->RemoveReceiver(route->to->GetPeerLocalAddress()); + if (route->is_default) { + route->from->router()->RemoveDefaultReceiver(); + } else { + route->from->router()->RemoveReceiver( + route->to->GetPeerLocalAddress()); + } route->active = false; }, diff --git a/test/network/network_emulation_manager.h b/test/network/network_emulation_manager.h index 1eb1b2bc35..f716f963d0 100644 --- a/test/network/network_emulation_manager.h +++ b/test/network/network_emulation_manager.h @@ -61,6 +61,11 @@ class NetworkEmulationManagerImpl : public NetworkEmulationManager { EmulatedRoute* CreateRoute( const std::vector& via_nodes) override; + EmulatedRoute* CreateDefaultRoute( + EmulatedEndpoint* from, + const std::vector& via_nodes, + EmulatedEndpoint* to) override; + void ClearRoute(EmulatedRoute* route) override; TcpMessageRoute* CreateTcpRoute(EmulatedRoute* send_route, diff --git a/test/network/network_emulation_unittest.cc b/test/network/network_emulation_unittest.cc index 7bebc66680..fca10c40b7 100644 --- a/test/network/network_emulation_unittest.cc +++ b/test/network/network_emulation_unittest.cc @@ -587,6 +587,51 @@ TEST(NetworkEmulationManagerTest, EndpointLoopback) { network_manager.time_controller()->AdvanceTime(TimeDelta::Seconds(1)); } +TEST(NetworkEmulationManagerTest, EndpointCanSendWithDifferentSourceIp) { + constexpr uint32_t kEndpointIp = 0xC0A80011; // 192.168.0.17 + constexpr uint32_t kSourceIp = 0xC0A80012; // 192.168.0.18 + NetworkEmulationManagerImpl network_manager(TimeMode::kSimulated); + EmulatedEndpointConfig endpoint_config; + endpoint_config.ip = rtc::IPAddress(kEndpointIp); + endpoint_config.allow_send_packet_with_different_source_ip = true; + auto endpoint = network_manager.CreateEndpoint(endpoint_config); + + MockReceiver receiver; + EXPECT_CALL(receiver, OnPacketReceived(::testing::_)).Times(1); + ASSERT_EQ(endpoint->BindReceiver(80, &receiver), 80); + + endpoint->SendPacket(rtc::SocketAddress(kSourceIp, 80), + rtc::SocketAddress(endpoint->GetPeerLocalAddress(), 80), + "Hello"); + network_manager.time_controller()->AdvanceTime(TimeDelta::Seconds(1)); +} + +TEST(NetworkEmulationManagerTest, + EndpointCanReceiveWithDifferentDestIpThroughDefaultRoute) { + constexpr uint32_t kDestEndpointIp = 0xC0A80011; // 192.168.0.17 + constexpr uint32_t kDestIp = 0xC0A80012; // 192.168.0.18 + NetworkEmulationManagerImpl network_manager(TimeMode::kSimulated); + auto sender_endpoint = + network_manager.CreateEndpoint(EmulatedEndpointConfig()); + EmulatedEndpointConfig endpoint_config; + endpoint_config.ip = rtc::IPAddress(kDestEndpointIp); + endpoint_config.allow_receive_packets_with_different_dest_ip = true; + auto receiver_endpoint = network_manager.CreateEndpoint(endpoint_config); + + MockReceiver receiver; + EXPECT_CALL(receiver, OnPacketReceived(::testing::_)).Times(1); + ASSERT_EQ(receiver_endpoint->BindReceiver(80, &receiver), 80); + + network_manager.CreateDefaultRoute( + sender_endpoint, {network_manager.NodeBuilder().Build().node}, + receiver_endpoint); + + sender_endpoint->SendPacket( + rtc::SocketAddress(sender_endpoint->GetPeerLocalAddress(), 80), + rtc::SocketAddress(kDestIp, 80), "Hello"); + network_manager.time_controller()->AdvanceTime(TimeDelta::Seconds(1)); +} + TEST(NetworkEmulationManagerTURNTest, GetIceServerConfig) { NetworkEmulationManagerImpl network_manager(TimeMode::kRealTime); auto turn = network_manager.CreateTURNServer(EmulatedTURNServerConfig());