Skip to content
39 changes: 38 additions & 1 deletion dhcp4relay/src/dhcp4relay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ const struct sock_fprog ether_relay_fprog = {
/* interface to vlan mapping */
std::unordered_map<std::string, std::string> vlan_map;

/* VXLAN tunnel names cached from CONFIG_DB VXLAN_TUNNEL table */
std::unordered_set<std::string> vxlan_tunnel_set;

/* VRF sock map is created to avoid multiple sockets for same VRF
We can expect multiple servers on same VRF, we no need to open VRF sockets
for each VRF instead we can make use of existing VRF socket opened.
Expand Down Expand Up @@ -99,6 +102,28 @@ std::shared_ptr<swss::DBConnector> config_db = std::make_shared<swss::DBConnecto

std::shared_ptr<swss::DBConnector> state_db = std::make_shared<swss::DBConnector>("STATE_DB", 0);

/**
* @code is_vxlan_interface(const std::string &interface_name);
*
* @brief Check if an interface is a VxLAN tunnel by querying CONFIG_DB
* Handles interface names with VLAN suffix (e.g., "VXLAN-101" -> "VXLAN")
*
* @param interface_name Name of the interface to check (may include VLAN suffix like "VXLAN-101")
*
* @return true if interface is a VxLAN tunnel, false otherwise
*/
bool is_vxlan_interface(const std::string &interface_name) {
// Extract base interface name (before "-" if present)
// e.g., "VXLAN-101" -> "VXLAN", "Vtep-200" -> "Vtep", "vtep1" -> "vtep1"
std::string base_intf_name = interface_name;
auto dash_pos = interface_name.find('-');
if (dash_pos != std::string::npos) {
base_intf_name = interface_name.substr(0, dash_pos);
}

return vxlan_tunnel_set.count(base_intf_name) > 0;
}

/**
* @code sock_open(const struct sock_fprog *fprog);
*
Expand Down Expand Up @@ -987,7 +1012,7 @@ void pkt_in_callback(evutil_socket_t fd, short event, void *arg) {
auto itr = std::find(interface_list.begin(), interface_list.end(), intf);
/* To avoid duplicate packets, we are only processing packets from
interface in PORT_TABLE and packets from VXLAN interface and docker0 interfaces */
if ((itr == interface_list.end()) && (intf.rfind("VXLAN", 0) != 0) && (intf.rfind("docker0", 0) != 0)) {
if ((itr == interface_list.end()) && !is_vxlan_interface(intf) && (intf.rfind("docker0", 0) != 0)) {
continue;
}

Expand Down Expand Up @@ -1473,6 +1498,18 @@ static void apply_config_event(const event_config &received_event,
}
delete port_msg;
}
} else if (received_event.type == DHCPv4_RELAY_VXLAN_TUNNEL_UPDATE) {
vxlan_tunnel_config *msg = static_cast<vxlan_tunnel_config *>(received_event.msg);
if (msg) {
if (msg->is_add) {
vxlan_tunnel_set.insert(msg->tunnel_name);
syslog(LOG_INFO, "[DHCPV4_RELAY] Added VXLAN tunnel %s to cache\n", msg->tunnel_name.c_str());
} else {
vxlan_tunnel_set.erase(msg->tunnel_name);
syslog(LOG_INFO, "[DHCPV4_RELAY] Removed VXLAN tunnel %s from cache\n", msg->tunnel_name.c_str());
}
delete msg;
}
}
}

Expand Down
8 changes: 7 additions & 1 deletion dhcp4relay/src/dhcp4relay.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

#include <map>
#include <string>
#include <unordered_set>
#include <vector>

#include "dbconnector.h"
Expand Down Expand Up @@ -144,7 +145,8 @@ typedef enum {
* initial DHCPV4_RELAY snapshot. Reusable for Redis-reconnect
* resync, atomic config replace, etc.
*/
DHCPv4_RELAY_SYNC_BARRIER
DHCPv4_RELAY_SYNC_BARRIER,
DHCPv4_RELAY_VXLAN_TUNNEL_UPDATE
} event_type;

struct event_config {
Expand All @@ -169,6 +171,10 @@ struct port_config {
bool is_add;
};

struct vxlan_tunnel_config {
std::string tunnel_name;
bool is_add;
};
struct metadata_config {
std::string host_mac_addr;
std::string hostname = "sonic";
Expand Down
65 changes: 65 additions & 0 deletions dhcp4relay/src/dhcp4relay_mgr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ void DHCPMgr::handle_swss_notification() {
swss::SubscriberStateTable config_db_port_table(config_db_ptr.get(), "PORT");
swss::SubscriberStateTable config_db_dpu_table(config_db_ptr.get(), "DPUS");
swss::SubscriberStateTable state_db_interface_table(state_db_ptr.get(), "INTERFACE_TABLE");
swss::SubscriberStateTable config_db_vxlan_tunnel_table(config_db_ptr.get(), "VXLAN_TUNNEL");

std::deque<swss::KeyOpFieldsValuesTuple> entries;
swss::Select swss_select;
Expand All @@ -83,6 +84,40 @@ void DHCPMgr::handle_swss_notification() {
swss_select.addSelectable(&config_db_port_table);
swss_select.addSelectable(&config_db_dpu_table);
swss_select.addSelectable(&state_db_interface_table);
swss_select.addSelectable(&config_db_vxlan_tunnel_table);

/* Prime VXLAN tunnel cache from existing CONFIG_DB entries so that
tunnels configured before dhcp4relay starts are recognized
immediately — SubscriberStateTable delivers existing keys on its
first pops(), but packets may arrive before that event is processed. */
{
swss::Table vxlan_tunnel_table(config_db_ptr.get(), "VXLAN_TUNNEL");
std::vector<std::string> keys;
vxlan_tunnel_table.getKeys(keys);
for (const auto &key : keys) {
vxlan_tunnel_config *msg = nullptr;
try {
msg = new vxlan_tunnel_config();
} catch (const std::bad_alloc &e) {
syslog(LOG_ERR, "[DHCPV4_RELAY] Memory allocation failed: %s\n", e.what());
continue;
}
msg->tunnel_name = key;
msg->is_add = true;

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

if (write(config_pipe[1], &event, sizeof(event)) == -1) {
syslog(LOG_ERR, "[DHCPV4_RELAY] Failed to send initial VXLAN tunnel for %s\n", key.c_str());
delete msg;
}
}
if (!keys.empty()) {
syslog(LOG_NOTICE, "[DHCPV4_RELAY] Primed VXLAN tunnel cache with %zu entries\n", keys.size());
}
}

/*
* Push the initial DHCPV4_RELAY snapshot down config_pipe, then
Expand Down Expand Up @@ -177,6 +212,9 @@ void DHCPMgr::handle_swss_notification() {
} else if (selectable == static_cast<swss::Selectable *>(&config_db_dpu_table)) {
config_db_dpu_table.pops(entries);
process_port_notification(entries);
} else if (selectable == static_cast<swss::Selectable *>(&config_db_vxlan_tunnel_table)) {
config_db_vxlan_tunnel_table.pops(entries);
process_vxlan_tunnel_notification(entries);
}
}
}
Expand Down Expand Up @@ -901,6 +939,33 @@ void DHCPMgr::process_port_notification(std::deque<swss::KeyOpFieldsValuesTuple>
}
}

void DHCPMgr::process_vxlan_tunnel_notification(std::deque<swss::KeyOpFieldsValuesTuple> &entries) {
for (auto &entry : entries) {
std::string tunnel_name = kfvKey(entry);
std::string operation = kfvOp(entry);

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

msg->tunnel_name = tunnel_name;
msg->is_add = (operation == "SET");

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

if (write(config_pipe[1], &event, sizeof(event)) == -1) {
syslog(LOG_ERR, "[DHCPV4_RELAY] Failed to send VXLAN tunnel update for %s\n", tunnel_name.c_str());
delete msg;
}
}
}

/**
* @code void DHCPMgr::stop_db_updates();
*
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 @@ -38,4 +38,5 @@ class DHCPMgr {
void process_dhcp_server_ipv4_notification(std::deque<swss::KeyOpFieldsValuesTuple> &entries);
void process_vlan_notification(std::deque<swss::KeyOpFieldsValuesTuple> &entries);
void process_port_notification(std::deque<swss::KeyOpFieldsValuesTuple> &entries);
void process_vxlan_tunnel_notification(std::deque<swss::KeyOpFieldsValuesTuple> &entries);
};
Loading