Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions dhcp4relay/src/dhcp4relay_mgr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ constexpr auto DEFAULT_TIMEOUT_MSEC = 1000;

std::unordered_map<std::string, relay_config> vlans_copy;

// Source-interface IP events can arrive before the matching relay config.
static std::unordered_map<std::string, sockaddr_in> intf_to_addr_cache;

#ifdef UNIT_TEST
using namespace swss;
#endif
Expand Down Expand Up @@ -104,6 +107,23 @@ void DHCPMgr::handle_swss_notification() {
SWSS_LOG_INFO("[DHCPV4_RELAY] No DHCPV4_RELAY entries present at startup");
}

// Drain interface snapshots before barrier to apply cached source_interface IPs.
std::deque<swss::KeyOpFieldsValuesTuple> intf_entries;
config_db_loopback_table.pops(intf_entries);
if (!intf_entries.empty()) {
process_interface_notification(intf_entries);
}
intf_entries.clear();
config_db_interface_table.pops(intf_entries);
if (!intf_entries.empty()) {
process_interface_notification(intf_entries);
}
intf_entries.clear();
config_db_portchannel_table.pops(intf_entries);
if (!intf_entries.empty()) {
process_interface_notification(intf_entries);
}

event_config barrier_event{};
barrier_event.type = DHCPv4_RELAY_SYNC_BARRIER;
barrier_event.msg = nullptr;
Expand All @@ -129,10 +149,15 @@ void DHCPMgr::handle_swss_notification() {
continue;
}

entries.clear();
if (!feature_dhcp_server_enabled) {
if (config_db_relaymgr_table_ptr && selectable == config_db_relaymgr_table_ptr.get()) {
config_db_relaymgr_table_ptr->pops(entries);
process_relay_notification(entries);
if (!entries.empty()) {
// Keep replay scoped to this DHCPV4_RELAY batch.
dispatch_source_intf_from_cache(entries);
}
Comment thread
cshivashgit marked this conversation as resolved.
} else if (selectable == static_cast<swss::Selectable *>(&config_db_interface_table)) {
config_db_interface_table.pops(entries);
process_interface_notification(entries);
Expand Down Expand Up @@ -312,6 +337,18 @@ void DHCPMgr::process_interface_notification(std::deque<swss::KeyOpFieldsValuesT
continue;
}

if (ip.find(':') == std::string::npos) {
if (operation == "SET") {
sockaddr_in tmp{};
if (inet_pton(AF_INET, ip.c_str(), &tmp.sin_addr) == 1) {
tmp.sin_family = AF_INET;
intf_to_addr_cache[intf_name] = tmp;
}
} else if (operation == "DEL") {
intf_to_addr_cache.erase(intf_name);
}
}

// Check the source interface is configured in dhcp relay config.
for (auto &vlan : vlans_copy) {
if (vlan.second.source_interface == intf_name) {
Expand Down Expand Up @@ -456,6 +493,55 @@ void DHCPMgr::process_relay_notification(std::deque<swss::KeyOpFieldsValuesTuple
}
}

void DHCPMgr::dispatch_source_intf_from_cache(const std::deque<swss::KeyOpFieldsValuesTuple> &entries) {
if (entries.empty() || intf_to_addr_cache.empty()) {
return;
}

for (const auto &entry : entries) {
if (kfvOp(entry) != "SET") {
continue;
}

auto vlan_entry = vlans_copy.find(kfvKey(entry));
if (vlan_entry == vlans_copy.end()) {
continue;
}

const std::string &intf = vlan_entry->second.source_interface;
if (intf.empty()) {
continue;
}

auto it = intf_to_addr_cache.find(intf);
if (it == intf_to_addr_cache.end()) {
continue;
}

relay_config *relay_msg = nullptr;
try {
relay_msg = new relay_config();
} catch (const std::bad_alloc &e) {
SWSS_LOG_ERROR("[DHCPV4_RELAY] Memory allocation failed: %s", e.what());
return;
}

relay_msg->vlan = vlan_entry->second.vlan;
relay_msg->is_add = true;
relay_msg->src_intf_sel_addr = it->second;

event_config event;
event.type = DHCPv4_RELAY_INTERFACE_UPDATE;
event.msg = static_cast<void *>(relay_msg);

if (write(config_pipe[1], &event, sizeof(event)) == -1) {
SWSS_LOG_ERROR("[DHCPV4_RELAY] Failed to write to config update pipe: %s",
strerror(errno));
delete relay_msg;
}
}
}

/**
* @brief Processes the feature table updates to configure the dhcp_server enabled/disabled.
*
Expand Down
1 change: 1 addition & 0 deletions dhcp4relay/src/dhcp4relay_mgr.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class DHCPMgr {
void stop_db_updates();
void process_relay_notification(std::deque<swss::KeyOpFieldsValuesTuple> &entries);
void process_interface_notification(std::deque<swss::KeyOpFieldsValuesTuple> &entries);
void dispatch_source_intf_from_cache(const std::deque<swss::KeyOpFieldsValuesTuple> &entries);
void process_device_metadata_notification(std::deque<swss::KeyOpFieldsValuesTuple> &entries);
void process_vlan_member_notification(std::deque<swss::KeyOpFieldsValuesTuple> &entries);
void process_vlan_interface_notification(std::deque<swss::KeyOpFieldsValuesTuple> &entries);
Expand Down
60 changes: 60 additions & 0 deletions dhcp4relay/test/mock_relay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -650,6 +650,66 @@ TEST(DHCPMgrTest, process_vlan_events) {
dhcpMgr.process_vlan_notification(entries);
}

TEST(DHCPMgrTest, replay_cached_source_interface_for_updated_relay_entries) {
DHCPMgr dhcpMgr;
vlans_copy.clear();
testing_db::reset();

std::deque<swss::KeyOpFieldsValuesTuple> intf_entries;
intf_entries.emplace_back("Loopback4096|10.10.10.1/32", "DEL", std::vector<swss::FieldValueTuple>{});
dhcpMgr.process_interface_notification(intf_entries);

intf_entries.clear();
intf_entries.emplace_back("Loopback4096|10.10.10.1/32", "SET", std::vector<swss::FieldValueTuple>{
{"NULL", "NULL"}
});
dhcpMgr.process_interface_notification(intf_entries);

relay_config existing_config{};
existing_config.vlan = "Vlan200";
existing_config.source_interface = "Loopback4096";
existing_config.servers = {"192.0.2.1"};
vlans_copy[existing_config.vlan] = existing_config;

std::vector<event_type> written_types;
std::vector<std::string> replayed_vlans;
EXPECT_GLOBAL_CALL(write, write(_, _, _))
.Times(2)
.WillRepeatedly(Invoke([&](int, const void *buf, size_t count) -> ssize_t {
EXPECT_EQ(count, sizeof(event_config));

const auto *event = static_cast<const event_config *>(buf);
written_types.push_back(event->type);

auto *relay_msg = static_cast<relay_config *>(event->msg);
if (event->type == DHCPv4_RELAY_INTERFACE_UPDATE) {
replayed_vlans.push_back(relay_msg->vlan);
EXPECT_EQ(relay_msg->src_intf_sel_addr.sin_addr.s_addr, inet_addr("10.10.10.1"));
}

delete relay_msg;
return static_cast<ssize_t>(count);
}));

std::deque<swss::KeyOpFieldsValuesTuple> relay_entries;
relay_entries.emplace_back("Vlan100", "SET", std::vector<swss::FieldValueTuple>{
{"dhcpv4_servers", "192.0.2.10"},
{"source_interface", "Loopback4096"}
});

dhcpMgr.process_relay_notification(relay_entries);
dhcpMgr.dispatch_source_intf_from_cache(relay_entries);

EXPECT_THAT(written_types, ElementsAre(DHCPv4_RELAY_CONFIG_UPDATE, DHCPv4_RELAY_INTERFACE_UPDATE));
EXPECT_THAT(replayed_vlans, ElementsAre("Vlan100"));

vlans_copy.clear();
intf_entries.clear();
intf_entries.emplace_back("Loopback4096|10.10.10.1/32", "DEL", std::vector<swss::FieldValueTuple>{});
dhcpMgr.process_interface_notification(intf_entries);
testing_db::reset();
}

TEST(DHCPMgrTest, dhcp_server_feature_enable) {
DHCPMgr dhcpMgr;
EXPECT_GLOBAL_CALL(write, write(_, _, _))
Expand Down
Loading