Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
185 changes: 174 additions & 11 deletions dhcp6relay/src/config_interface.cpp
Original file line number Diff line number Diff line change
@@ -1,13 +1,27 @@
#include <sstream>
#include <syslog.h>
#include <algorithm>
#include <atomic>
#include <deque>
#include <mutex>
#include <thread>
#include <unistd.h>
#include "config_interface.h"

constexpr auto DEFAULT_TIMEOUT_MSEC = 1000;

bool pollSwssNotifcation = true;
swss::Select swssSelect;

// Runtime config monitor state: the monitor thread publishes the desired
// per-vlan config under g_cfg_mutex and wakes the main loop via g_notify_fd;
// config_change_callback reconciles it with the live vlans map.
static std::mutex g_cfg_mutex;
static std::unordered_map<std::string, relay_config> g_desired_cfg;
static std::thread g_monitor_thread;
static std::atomic<bool> g_stop_monitor{false};
static int g_notify_fd = -1;

/**
* @code void initialize_swss()
*
Expand All @@ -21,7 +35,7 @@ void initialize_swss(std::unordered_map<std::string, relay_config> &vlans)
std::shared_ptr<swss::DBConnector> configDbPtr = std::make_shared<swss::DBConnector> ("CONFIG_DB", 0);
swss::SubscriberStateTable ipHelpersTable(configDbPtr.get(), "DHCP_RELAY");
swssSelect.addSelectable(&ipHelpersTable);
get_dhcp(vlans, &ipHelpersTable, false, configDbPtr);
get_dhcp(vlans, &ipHelpersTable, configDbPtr);
}
catch (const std::bad_alloc &e) {
syslog(LOG_ERR, "Failed allocate memory. Exception details: %s", e.what());
Expand Down Expand Up @@ -53,14 +67,14 @@ void deinitialize_swss()

/**

* @code void get_dhcp(std::unordered_map<std::string, relay_config> &vlans, swss::SubscriberStateTable *ipHelpersTable, bool dynamic,
* @code void get_dhcp(std::unordered_map<std::string, relay_config> &vlans, swss::SubscriberStateTable *ipHelpersTable,
std::shared_ptr<swss::DBConnector> config_db)
*
* @brief initialize and get vlan table information from DHCP_RELAY
*
* @return none
*/
void get_dhcp(std::unordered_map<std::string, relay_config> &vlans, swss::SubscriberStateTable *ipHelpersTable, bool dynamic,
void get_dhcp(std::unordered_map<std::string, relay_config> &vlans, swss::SubscriberStateTable *ipHelpersTable,
std::shared_ptr<swss::DBConnector> config_db) {
swss::Selectable *selectable;
int ret = swssSelect.select(&selectable, DEFAULT_TIMEOUT_MSEC);
Expand All @@ -70,12 +84,7 @@ void get_dhcp(std::unordered_map<std::string, relay_config> &vlans, swss::Subscr
} else if (ret == swss::Select::TIMEOUT) {
}
if (selectable == static_cast<swss::Selectable *> (ipHelpersTable)) {
if (!dynamic) {
handleRelayNotification(*ipHelpersTable, vlans, config_db);
} else {
syslog(LOG_WARNING, "relay config changed, "
"need restart container to take effect");
}
handleRelayNotification(*ipHelpersTable, vlans, config_db);
}
}

Expand Down Expand Up @@ -154,6 +163,7 @@ void processRelayNotification(std::deque<swss::KeyOpFieldsValuesTuple> &entries,
intf.mux_key = "";
intf.state_db = nullptr;
intf.is_lla_ready = false;
std::string server_vrf;
for (auto &fieldValue: fieldValues) {
std::string f = fvField(fieldValue);
std::string v = fvValue(fieldValue);
Expand All @@ -172,13 +182,32 @@ void processRelayNotification(std::deque<swss::KeyOpFieldsValuesTuple> &entries,
if(f == "dhcpv6_option|interface_id" && v == "true") { // interface-id is off by default on non-Dual-ToR, unless specified in config db
intf.is_interface_id = true;
}
if(f == SERVER_VRF_FIELD) {
server_vrf = v;
}
}
// The upstream (gua) socket binds to the VLAN's own VRF (vrf_name), so a
// VLAN placed in a non-default VRF reaches servers reachable in that VRF.
intf.vrf = DEFAULT_VRF;
{
std::string vlan_vrf;
swss::Table vlan_intf_table(config_db.get(), "VLAN_INTERFACE");
vlan_intf_table.hget(vlan, VRF_NAME_FIELD, vlan_vrf);
if (!vlan_vrf.empty()) {
intf.vrf = vlan_vrf;
}
}
// An explicit server_vrf only matters when the servers live in a VRF
// different from the VLAN's own; otherwise the gua socket already reaches
// them. Empty server_vrf selects the same-VRF (gua socket) path.
intf.server_vrf = (!server_vrf.empty() && server_vrf != intf.vrf) ? server_vrf : "";
if (intf.servers.empty()) {
syslog(LOG_WARNING, "No servers found for VLAN %s, skipping configuration.", vlan.c_str());
continue;
}
syslog(LOG_INFO, "add %s relay config, option79 %s interface-id %s\n", vlan.c_str(),
intf.is_option_79 ? "enable" : "disable", intf.is_interface_id ? "enable" : "disable");
syslog(LOG_INFO, "add %s relay config, option79 %s interface-id %s vrf %s server_vrf %s\n", vlan.c_str(),
intf.is_option_79 ? "enable" : "disable", intf.is_interface_id ? "enable" : "disable",
intf.vrf.c_str(), intf.server_vrf.empty() ? "-" : intf.server_vrf.c_str());
vlans[vlan] = intf;
}
}
Expand Down Expand Up @@ -207,3 +236,137 @@ bool check_is_lla_ready(std::string vlan) {
}
return false;
}

/**
* @code std::unordered_map<std::string, relay_config> build_desired_config(std::shared_ptr<swss::DBConnector> config_db)
*
* @brief read the full DHCP_RELAY table and build the desired per-vlan relay config
*
* @param config_db CONFIG_DB connector used to read DHCP_RELAY and VLAN_INTERFACE
*
* @return desired map of vlan name to relay_config (config fields only)
*/
std::unordered_map<std::string, relay_config> build_desired_config(std::shared_ptr<swss::DBConnector> config_db)
{
std::unordered_map<std::string, relay_config> desired;
swss::Table dhcp_relay_table(config_db.get(), "DHCP_RELAY");
std::vector<std::string> keys;
dhcp_relay_table.getKeys(keys);

std::deque<swss::KeyOpFieldsValuesTuple> entries;
for (const auto &key : keys) {
std::vector<swss::FieldValueTuple> field_values;
dhcp_relay_table.get(key, field_values);
entries.emplace_back(key, "SET", field_values);
}
// Reuse the notification parser so the server list, options and the
// VLAN_INTERFACE IPv6-presence check stay in one code path.
processRelayNotification(entries, desired, config_db);
return desired;
}

/**
* @code static void publish_desired_config(std::shared_ptr<swss::DBConnector> config_db)
*
* @brief snapshot the desired config, store it under lock and wake the main loop
*
* @param config_db CONFIG_DB connector used to read the desired config
*
* @return none
*/
static void publish_desired_config(std::shared_ptr<swss::DBConnector> config_db)
{
auto desired = build_desired_config(config_db);
{
std::lock_guard<std::mutex> lock(g_cfg_mutex);
g_desired_cfg = std::move(desired);
}
if (g_notify_fd >= 0) {
char notify_byte = 1;
ssize_t written = write(g_notify_fd, &notify_byte, sizeof(notify_byte));
if (written < 0) {
syslog(LOG_WARNING, "Failed to notify main loop of config change: %s", strerror(errno));
}
}
}

/**
* @code static void config_monitor_loop()
*
* @brief detached thread: watch CONFIG_DB tables and publish desired config on change
*
* @return none
*/
static void config_monitor_loop()
{
auto config_db = std::make_shared<swss::DBConnector>("CONFIG_DB", 0);
auto state_db = std::make_shared<swss::DBConnector>("STATE_DB", 0);
swss::SubscriberStateTable dhcpRelaySub(config_db.get(), "DHCP_RELAY");
swss::SubscriberStateTable vlanIntfSub(config_db.get(), "VLAN_INTERFACE");
swss::SubscriberStateTable vlanSub(config_db.get(), "VLAN");
// STATE_DB INTERFACE_TABLE signals interface readiness (link-local address
// present). Watching it reconciles a vlan as soon as its interface comes up
// instead of waiting for the periodic 60s link-local check.
swss::SubscriberStateTable intfStateSub(state_db.get(), "INTERFACE_TABLE");

swss::Select select;
select.addSelectable(&dhcpRelaySub);
select.addSelectable(&vlanIntfSub);
select.addSelectable(&vlanSub);
select.addSelectable(&intfStateSub);

// Drain the initial snapshot the subscriber tables cache at construction so
// the first real notification is not preceded by a redundant reprocessing.
std::deque<swss::KeyOpFieldsValuesTuple> drain;
dhcpRelaySub.pops(drain);
vlanIntfSub.pops(drain);
vlanSub.pops(drain);
intfStateSub.pops(drain);

// Publish the current configuration once at startup.
publish_desired_config(config_db);

while (!g_stop_monitor.load()) {
swss::Selectable *selectable = nullptr;
int ret = select.select(&selectable, DEFAULT_TIMEOUT_MSEC);
if (ret == swss::Select::TIMEOUT) {
continue;
}
if (ret == swss::Select::ERROR) {
syslog(LOG_WARNING, "Select: returned ERROR in config monitor");
continue;
}

std::deque<swss::KeyOpFieldsValuesTuple> entries;
dhcpRelaySub.pops(entries);
entries.clear();
vlanIntfSub.pops(entries);
entries.clear();
vlanSub.pops(entries);
entries.clear();
intfStateSub.pops(entries);
entries.clear();

publish_desired_config(config_db);
}
}

void start_dhcp_config_monitor(int notify_fd)
{
g_notify_fd = notify_fd;
g_stop_monitor.store(false);
g_monitor_thread = std::thread(config_monitor_loop);
g_monitor_thread.detach();
Comment thread
AnantKishorSharma marked this conversation as resolved.
Outdated
}

void stop_dhcp_config_monitor()
{
g_stop_monitor.store(true);
}

bool fetch_desired_config(std::unordered_map<std::string, relay_config> &out)
{
std::lock_guard<std::mutex> lock(g_cfg_mutex);
out = g_desired_cfg;
return true;
}
46 changes: 44 additions & 2 deletions dhcp6relay/src/config_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,14 @@ void initialize_swss(std::unordered_map<std::string, relay_config> &vlans);
void deinitialize_swss();

/**
* @code void get_dhcp(std::unordered_map<std::string, relay_config> &vlans, swss::SubscriberStateTable *ipHelpersTable, bool dynamic,
* @code void get_dhcp(std::unordered_map<std::string, relay_config> &vlans, swss::SubscriberStateTable *ipHelpersTable,
* std::shared_ptr<swss::DBConnector> config_db)
*
* @brief initialize and get vlan information from DHCP_RELAY
*
* @return none
*/
void get_dhcp(std::unordered_map<std::string, relay_config> &vlans, swss::SubscriberStateTable *ipHelpersTable, bool dynamic,
void get_dhcp(std::unordered_map<std::string, relay_config> &vlans, swss::SubscriberStateTable *ipHelpersTable,
std::shared_ptr<swss::DBConnector> config_db);

/**
Expand Down Expand Up @@ -81,3 +81,45 @@ void processRelayNotification(std::deque<swss::KeyOpFieldsValuesTuple> &entries,
* @return bool value indicates whether lla ready
*/
bool check_is_lla_ready(std::string vlan);

/**
* @code build_desired_config(std::shared_ptr<swss::DBConnector> config_db);
*
* @brief read the full DHCP_RELAY table and build the desired per-vlan relay config
*
* @param config_db CONFIG_DB connector used to read DHCP_RELAY and VLAN_INTERFACE
*
* @return desired map of vlan name to relay_config (config fields only)
*/
std::unordered_map<std::string, relay_config> build_desired_config(std::shared_ptr<swss::DBConnector> config_db);

/**
* @code start_dhcp_config_monitor(int notify_fd);
*
* @brief start the detached thread that watches CONFIG_DB and publishes desired config
*
* @param notify_fd write end of the pipe used to wake the libevent main loop
*
* @return none
*/
void start_dhcp_config_monitor(int notify_fd);

/**
* @code stop_dhcp_config_monitor();
*
* @brief signal the config monitor thread to stop
*
* @return none
*/
void stop_dhcp_config_monitor();

/**
* @code fetch_desired_config(std::unordered_map<std::string, relay_config> &out);
*
* @brief copy the latest desired config published by the monitor thread
*
* @param out map populated with the latest desired per-vlan relay config
*
* @return true on success
*/
bool fetch_desired_config(std::unordered_map<std::string, relay_config> &out);
Loading