blob: 43451bd5c846dc28cc7351a5e78c9debf469defc [file] [log] [blame] [edit]
// Copyright 2024 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "patchpanel/connmark_updater.h"
#include <string>
#include <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "patchpanel/mock_conntrack_monitor.h"
#include "patchpanel/mock_process_runner.h"
namespace patchpanel {
namespace {
using ::testing::_;
using ::testing::ElementsAreArray;
using ::testing::Mock;
using ::testing::Return;
constexpr char kIPAddress1[] = "8.8.8.8";
constexpr char kIPAddress2[] = "8.8.8.4";
constexpr uint16_t kPort1 = 10000;
constexpr uint16_t kPort2 = 20000;
constexpr ConntrackMonitor::EventType kConntrackEvents[] = {
ConntrackMonitor::EventType::kNew};
ConnmarkUpdater::Conntrack5Tuple CreateUDPConnection() {
const auto src_addr = net_base::IPAddress::CreateFromString(kIPAddress1);
const auto dst_addr = net_base::IPAddress::CreateFromString(kIPAddress2);
return ConnmarkUpdater::Conntrack5Tuple{
.src_addr = *src_addr,
.dst_addr = *dst_addr,
.sport = kPort1,
.dport = kPort2,
.proto = ConnmarkUpdater::IPProtocol::kUDP};
}
ConnmarkUpdater::Conntrack5Tuple CreateTCPConnection() {
const auto src_addr = net_base::IPAddress::CreateFromString(kIPAddress1);
const auto dst_addr = net_base::IPAddress::CreateFromString(kIPAddress2);
return ConnmarkUpdater::Conntrack5Tuple{
.src_addr = *src_addr,
.dst_addr = *dst_addr,
.sport = kPort1,
.dport = kPort2,
.proto = ConnmarkUpdater::IPProtocol::kTCP};
}
// Verifies that when creating connmark updater, a listener will be registered
// on ConntrackMonitor and initially the pending list is empty.
TEST(ConnmarkUpdaterTest, CreateConnmarkUpdater) {
MockConntrackMonitor conntrack_monitor;
MockProcessRunner runner;
EXPECT_CALL(conntrack_monitor,
AddListener(ElementsAreArray(kConntrackEvents), _));
ConnmarkUpdater updater_(&conntrack_monitor, &runner);
EXPECT_EQ(updater_.GetPendingListSizeForTesting(), 0);
}
// Verifies that whether initial try to update connmark for TCP connections
// succeeds or fails, the TCP connection will not be added to the pending list.
TEST(ConnmarkUpdaterTest, UpdateTCPConnectionConnmark) {
MockConntrackMonitor conntrack_monitor;
MockProcessRunner runner;
ConnmarkUpdater updater_(&conntrack_monitor, &runner);
std::vector<std::string> argv = {
"-p", "TCP",
"-s", kIPAddress1,
"-d", kIPAddress2,
"--sport", std::to_string(kPort1),
"--dport", std::to_string(kPort2),
"-m", QoSFwmarkWithMask(QoSCategory::kRealTimeInteractive)};
EXPECT_CALL(runner, conntrack("-U", ElementsAreArray(argv), _))
.WillOnce(Return(0));
updater_.UpdateConnmark(
CreateTCPConnection(),
Fwmark::FromQoSCategory(QoSCategory::kRealTimeInteractive),
kFwmarkQoSCategoryMask);
EXPECT_EQ(updater_.GetPendingListSizeForTesting(), 0);
argv = {"-p", "TCP",
"-s", kIPAddress1,
"-d", kIPAddress2,
"--sport", std::to_string(kPort1),
"--dport", std::to_string(kPort2),
"-m", QoSFwmarkWithMask(QoSCategory::kRealTimeInteractive)};
EXPECT_CALL(runner, conntrack("-U", ElementsAreArray(argv), _))
.WillOnce(Return(-1));
updater_.UpdateConnmark(
CreateTCPConnection(),
Fwmark::FromQoSCategory(QoSCategory::kRealTimeInteractive),
kFwmarkQoSCategoryMask);
EXPECT_EQ(updater_.GetPendingListSizeForTesting(), 0);
}
// Verifies that when initial try to update connmark succeeds, the UDP
// Connection will not be added to the pending list.
TEST(ConnmarkUpdaterTest, UpdateUDPConnectionConnmarkSucceed) {
MockConntrackMonitor conntrack_monitor;
MockProcessRunner runner;
ConnmarkUpdater updater_(&conntrack_monitor, &runner);
std::vector<std::string> argv = {
"-p", "UDP",
"-s", kIPAddress1,
"-d", kIPAddress2,
"--sport", std::to_string(kPort1),
"--dport", std::to_string(kPort2),
"-m", QoSFwmarkWithMask(QoSCategory::kRealTimeInteractive)};
EXPECT_CALL(runner, conntrack("-U", ElementsAreArray(argv), _))
.WillOnce(Return(0));
updater_.UpdateConnmark(
CreateUDPConnection(),
Fwmark::FromQoSCategory(QoSCategory::kRealTimeInteractive),
kFwmarkQoSCategoryMask);
EXPECT_EQ(updater_.GetPendingListSizeForTesting(), 0);
}
// Verifies that when initial try to update connmark fails, the UDP connection
// will be added to the pending list, and when trying to add the same UDP
// connection, it will only be added once.
TEST(ConnmarkUpdaterTest, UpdateUDPConnectionConnmarkFail) {
MockConntrackMonitor conntrack_monitor;
MockProcessRunner runner;
ConnmarkUpdater updater_(&conntrack_monitor, &runner);
std::vector<std::string> argv = {
"-p", "UDP",
"-s", kIPAddress1,
"-d", kIPAddress2,
"--sport", std::to_string(kPort1),
"--dport", std::to_string(kPort2),
"-m", QoSFwmarkWithMask(QoSCategory::kRealTimeInteractive)};
EXPECT_CALL(runner, conntrack("-U", ElementsAreArray(argv), _))
.WillOnce(Return(-1));
updater_.UpdateConnmark(
CreateUDPConnection(),
Fwmark::FromQoSCategory(QoSCategory::kRealTimeInteractive),
kFwmarkQoSCategoryMask);
EXPECT_EQ(updater_.GetPendingListSizeForTesting(), 1);
Mock::VerifyAndClearExpectations(&runner);
argv = {"-p", "UDP",
"-s", kIPAddress1,
"-d", kIPAddress2,
"--sport", std::to_string(kPort1),
"--dport", std::to_string(kPort2),
"-m", QoSFwmarkWithMask(QoSCategory::kRealTimeInteractive)};
EXPECT_CALL(runner, conntrack("-U", ElementsAreArray(argv), _))
.WillOnce(Return(-1));
updater_.UpdateConnmark(
CreateUDPConnection(),
Fwmark::FromQoSCategory(QoSCategory::kRealTimeInteractive),
kFwmarkQoSCategoryMask);
EXPECT_EQ(updater_.GetPendingListSizeForTesting(), 1);
Mock::VerifyAndClearExpectations(&runner);
}
// Verifies that connmark updater will retry updating connmark after receiving
// conntrack event that matches any entry in the pending list, and the pending
// UDP connection entry will be deleted from the pending list after retrying
// updating regardless of the result.
TEST(ConnmarkUpdaterTest, HandleConntrackMonitorEvent) {
MockConntrackMonitor conntrack_monitor;
MockProcessRunner runner;
ConnmarkUpdater updater_(&conntrack_monitor, &runner);
// Adds UDP connection to the pending list.
std::vector<std::string> argv = {
"-p", "UDP",
"-s", kIPAddress1,
"-d", kIPAddress2,
"--sport", std::to_string(kPort1),
"--dport", std::to_string(kPort2),
"-m", QoSFwmarkWithMask(QoSCategory::kRealTimeInteractive)};
EXPECT_CALL(runner, conntrack("-U", ElementsAreArray(argv), _))
.WillOnce(Return(-1));
updater_.UpdateConnmark(
CreateUDPConnection(),
Fwmark::FromQoSCategory(QoSCategory::kRealTimeInteractive),
kFwmarkQoSCategoryMask);
EXPECT_EQ(updater_.GetPendingListSizeForTesting(), 1);
Mock::VerifyAndClearExpectations(&runner);
// Verifies that connmark updater will not update connmark when protocol
// information does not match.
const ConntrackMonitor::Event kTCPEvent = ConntrackMonitor::Event{
.src = *net_base::IPAddress::CreateFromString(kIPAddress1),
.dst = *net_base::IPAddress::CreateFromString(kIPAddress2),
.sport = kPort1,
.dport = kPort2,
.proto = IPPROTO_TCP,
.type = ConntrackMonitor::EventType::kNew};
EXPECT_CALL(runner, conntrack("-U", _, _)).Times(0);
conntrack_monitor.DispatchEventForTesting(kTCPEvent);
EXPECT_EQ(updater_.GetPendingListSizeForTesting(), 1);
Mock::VerifyAndClearExpectations(&runner);
// Verifies that connmark updater will not update connmark when conntrack
// event type does not match.
const ConntrackMonitor::Event kUDPUpdateEvent = ConntrackMonitor::Event{
.src = *net_base::IPAddress::CreateFromString(kIPAddress1),
.dst = *net_base::IPAddress::CreateFromString(kIPAddress2),
.sport = kPort1,
.dport = kPort2,
.proto = IPPROTO_UDP,
.type = ConntrackMonitor::EventType::kUpdate};
EXPECT_CALL(runner, conntrack("-U", _, _)).Times(0);
conntrack_monitor.DispatchEventForTesting(kUDPUpdateEvent);
EXPECT_EQ(updater_.GetPendingListSizeForTesting(), 1);
Mock::VerifyAndClearExpectations(&runner);
// Verifies that UDP connection entry in the pending list will be deleted
// from the list regardless of the result of retrying.
const ConntrackMonitor::Event kUDPNewEvent = ConntrackMonitor::Event{
.src = *net_base::IPAddress::CreateFromString(kIPAddress1),
.dst = *net_base::IPAddress::CreateFromString(kIPAddress2),
.sport = kPort1,
.dport = kPort2,
.proto = IPPROTO_UDP,
.type = ConntrackMonitor::EventType::kNew};
EXPECT_CALL(runner, conntrack("-U", ElementsAreArray(argv), _))
.WillOnce(Return(-1));
conntrack_monitor.DispatchEventForTesting(kUDPNewEvent);
EXPECT_EQ(updater_.GetPendingListSizeForTesting(), 0);
Mock::VerifyAndClearExpectations(&runner);
}
} // namespace
} // namespace patchpanel