Skip to content
26 changes: 25 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 kernel netdev names mirrored from CONFIG_DB VXLAN_TUNNEL_MAP. */
std::unordered_set<std::string> vxlan_netdev_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,15 @@ 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);

/**
* @brief Return true iff @p interface_name is a known VxLAN tunnel kernel netdev.
*
* vxlan_netdev_set is populated by DHCPMgr from CONFIG_DB VXLAN_TUNNEL_MAP.
*/
bool is_vxlan_interface(const std::string &interface_name) {
return vxlan_netdev_set.count(interface_name) > 0;
}

/**
* @code sock_open(const struct sock_fprog *fprog);
*
Expand Down Expand Up @@ -982,7 +994,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 @@ -1466,6 +1478,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_netdev_set.insert(msg->netdev_name);
SWSS_LOG_INFO("[DHCPV4_RELAY] Added VXLAN netdev %s to cache", msg->netdev_name.c_str());
} else {
vxlan_netdev_set.erase(msg->netdev_name);
SWSS_LOG_INFO("[DHCPV4_RELAY] Removed VXLAN netdev %s from cache", msg->netdev_name.c_str());
}
delete msg;
}
}
}

Expand Down
9 changes: 8 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,11 @@ struct port_config {
bool is_add;
};

/* Add/remove a VxLAN kernel netdev in vxlan_netdev_set. */
struct vxlan_tunnel_config {
std::string netdev_name;
bool is_add;
};
struct metadata_config {
std::string host_mac_addr;
std::string hostname = "sonic";
Expand Down
95 changes: 95 additions & 0 deletions dhcp4relay/src/dhcp4relay_mgr.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "dhcp4relay_mgr.h"

#include <algorithm>
#include <cctype>
#include <cstdlib>
#include <sstream>
constexpr auto DEFAULT_TIMEOUT_MSEC = 1000;
Expand Down Expand Up @@ -67,6 +68,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_map_table(config_db_ptr.get(), "VXLAN_TUNNEL_MAP");

std::deque<swss::KeyOpFieldsValuesTuple> entries;
swss::Select swss_select;
Expand All @@ -83,6 +85,15 @@ 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_map_table);

{
std::deque<swss::KeyOpFieldsValuesTuple> initial_entries;
config_db_vxlan_tunnel_map_table.pops(initial_entries);
if (!initial_entries.empty()) {
process_vxlan_tunnel_map_notification(initial_entries);
}
}

/*
* Push the initial DHCPV4_RELAY snapshot down config_pipe, then
Expand Down Expand Up @@ -174,6 +185,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_map_table)) {
config_db_vxlan_tunnel_map_table.pops(entries);
process_vxlan_tunnel_map_notification(entries);
}
}
}
Expand Down Expand Up @@ -898,6 +912,87 @@ void DHCPMgr::process_port_notification(std::deque<swss::KeyOpFieldsValuesTuple>
}
}

/**
* @brief Translate VXLAN_TUNNEL_MAP CONFIG_DB events into VXLAN_NETDEV_UPDATE
* events on config_pipe.
*
* The kernel netdev name is "<tunnel_name>-<vlan_numeric_id>", derived from
* the row key ("<tunnel>|<mapname>") and the row's "vlan" field ("Vlan<id>").
*
* On DEL, SubscriberStateTable delivers no field values (the row is already
* gone from CONFIG_DB), so vxlan_tunnel_map_netdev_cache_ holds the netdev
* name from the prior SET to allow eviction.
*/
void DHCPMgr::process_vxlan_tunnel_map_notification(std::deque<swss::KeyOpFieldsValuesTuple> &entries) {
static const std::string kVlanPrefix = "Vlan";

for (auto &entry : entries) {
const std::string &key = kfvKey(entry);
const std::string &operation = kfvOp(entry);
bool is_add = (operation == "SET");

std::string netdev_name;
if (is_add) {
auto bar = key.find('|');
if (bar == std::string::npos || bar == 0 || bar == key.size() - 1) {
SWSS_LOG_INFO("[DHCPV4_RELAY] Skipping malformed VXLAN_TUNNEL_MAP key '%s'",
key.c_str());
continue;
}
std::string tunnel_name = key.substr(0, bar);

std::string vlan_field;
for (const auto &fv : kfvFieldsValues(entry)) {
if (fvField(fv) == "vlan") {
vlan_field = fvValue(fv);
break;
}
}
if (vlan_field.size() <= kVlanPrefix.size() ||
vlan_field.compare(0, kVlanPrefix.size(), kVlanPrefix) != 0) {
SWSS_LOG_INFO("[DHCPV4_RELAY] VXLAN_TUNNEL_MAP '%s' has unexpected vlan '%s'",
key.c_str(), vlan_field.c_str());
continue;
}
std::string vlan_id = vlan_field.substr(kVlanPrefix.size());
if (!std::all_of(vlan_id.begin(), vlan_id.end(), ::isdigit)) {
SWSS_LOG_INFO("[DHCPV4_RELAY] VXLAN_TUNNEL_MAP '%s' non-numeric vlan id '%s'",
key.c_str(), vlan_id.c_str());
continue;
}
netdev_name = tunnel_name + "-" + vlan_id;
vxlan_tunnel_map_netdev_cache_[key] = netdev_name;
} else {
auto it = vxlan_tunnel_map_netdev_cache_.find(key);
if (it == vxlan_tunnel_map_netdev_cache_.end()) {
continue;
}
netdev_name = it->second;
vxlan_tunnel_map_netdev_cache_.erase(it);
}

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

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) {
SWSS_LOG_ERROR("[DHCPV4_RELAY] Failed to send VXLAN netdev update for %s",
netdev_name.c_str());
delete msg;
}
}
}

/**
* @code void DHCPMgr::stop_db_updates();
*
Expand Down
5 changes: 5 additions & 0 deletions dhcp4relay/src/dhcp4relay_mgr.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ class DHCPMgr {
private:
std::atomic<bool> stop_thread;

/* CONFIG_DB key -> kernel netdev name. Populated on SET, consulted on
* DEL because SubscriberStateTable hands DEL events with no fields. */
std::unordered_map<std::string, std::string> vxlan_tunnel_map_netdev_cache_;

public:
DHCPMgr() : stop_thread(false) {}
~DHCPMgr();
Expand All @@ -38,4 +42,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_map_notification(std::deque<swss::KeyOpFieldsValuesTuple> &entries);
};
Loading
Loading