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
171 changes: 162 additions & 9 deletions dhcp6relay/src/config_interface.cpp
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
#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;

// Config-monitor thread state: publishes desired config under g_cfg_mutex, wakes the main loop via g_notify_fd.
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 +33,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 +65,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 +82,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 @@ -207,3 +214,149 @@ 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 to keep parsing 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 monitor 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");
// Watch STATE_DB INTERFACE_TABLE to reconcile a vlan as soon as its interface is up.
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 cached snapshot to avoid a redundant first reprocess.
std::deque<swss::KeyOpFieldsValuesTuple> drain;
dhcpRelaySub.pops(drain);
vlanIntfSub.pops(drain);
vlanSub.pops(drain);
intfStateSub.pops(drain);

try {
publish_desired_config(config_db);
} catch (const std::exception &e) {
syslog(LOG_WARNING, "config monitor: initial publish failed, will retry on next change: %s", e.what());
}

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;
}

try {
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);
} catch (const std::exception &e) {
// Don't let a transient CONFIG_DB failure terminate the monitor thread.
syslog(LOG_WARNING, "config monitor: reconcile failed, will retry: %s", e.what());
}
}
}

void start_dhcp_config_monitor(int notify_fd)
{
// Stop any previous monitor before starting a new one.
if (g_monitor_thread.joinable()) {
stop_dhcp_config_monitor();
}
g_notify_fd = notify_fd;
g_stop_monitor.store(false);
g_monitor_thread = std::thread(config_monitor_loop);
}

void stop_dhcp_config_monitor()
{
g_stop_monitor.store(true);
// Join before shutdown_relay tears down the state the thread uses.
if (g_monitor_thread.joinable()) {
g_monitor_thread.join();
}
g_notify_fd = -1;
}

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