Skip to content

Commit

Permalink
Merge branch 'master' into master_fail_build
Browse files Browse the repository at this point in the history
  • Loading branch information
yaqiangz authored Dec 23, 2024
2 parents af53b5b + 011066a commit 6694ea5
Show file tree
Hide file tree
Showing 5 changed files with 220 additions and 55 deletions.
71 changes: 62 additions & 9 deletions src/config_interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,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);
get_dhcp(vlans, &ipHelpersTable, false, configDbPtr);
}
catch (const std::bad_alloc &e) {
syslog(LOG_ERR, "Failed allocate memory. Exception details: %s", e.what());
Expand Down Expand Up @@ -53,13 +53,15 @@ 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, bool dynamic,
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, bool dynamic,
std::shared_ptr<swss::DBConnector> config_db) {
swss::Selectable *selectable;
int ret = swssSelect.select(&selectable, DEFAULT_TIMEOUT_MSEC);
if (ret == swss::Select::ERROR) {
Expand All @@ -69,7 +71,7 @@ void get_dhcp(std::unordered_map<std::string, relay_config> &vlans, swss::Subscr
}
if (selectable == static_cast<swss::Selectable *> (ipHelpersTable)) {
if (!dynamic) {
handleRelayNotification(*ipHelpersTable, vlans);
handleRelayNotification(*ipHelpersTable, vlans, config_db);
} else {
syslog(LOG_WARNING, "relay config changed, "
"need restart container to take effect");
Expand All @@ -78,7 +80,8 @@ void get_dhcp(std::unordered_map<std::string, relay_config> &vlans, swss::Subscr
}

/**
* @code void handleRelayNotification(swss::SubscriberStateTable &ipHelpersTable, std::unordered_map<std::string, relay_config> &vlans)
* @code void handleRelayNotification(swss::SubscriberStateTable &ipHelpersTable, std::unordered_map<std::string, relay_config> &vlans,
* std::shared_ptr<swss::DBConnector> config_db)
*
* @brief handles DHCPv6 relay configuration change notification
*
Expand All @@ -87,16 +90,18 @@ void get_dhcp(std::unordered_map<std::string, relay_config> &vlans, swss::Subscr
*
* @return none
*/
void handleRelayNotification(swss::SubscriberStateTable &ipHelpersTable, std::unordered_map<std::string, relay_config> &vlans)
void handleRelayNotification(swss::SubscriberStateTable &ipHelpersTable, std::unordered_map<std::string, relay_config> &vlans,
std::shared_ptr<swss::DBConnector> config_db)
{
std::deque<swss::KeyOpFieldsValuesTuple> entries;

ipHelpersTable.pops(entries);
processRelayNotification(entries, vlans);
processRelayNotification(entries, vlans, config_db);
}

/**
* @code void processRelayNotification(std::deque<swss::KeyOpFieldsValuesTuple> &entries, std::unordered_map<std::string, relay_config> vlans)
* @code void processRelayNotification(std::deque<swss::KeyOpFieldsValuesTuple> &entries, std::unordered_map<std::string, relay_config> vlans,
std::shared_ptr<swss::DBConnector> config_db)
*
* @brief process DHCPv6 relay servers and options configuration change notification
*
Expand All @@ -105,7 +110,8 @@ void handleRelayNotification(swss::SubscriberStateTable &ipHelpersTable, std::un
*
* @return none
*/
void processRelayNotification(std::deque<swss::KeyOpFieldsValuesTuple> &entries, std::unordered_map<std::string, relay_config> &vlans)
void processRelayNotification(std::deque<swss::KeyOpFieldsValuesTuple> &entries, std::unordered_map<std::string, relay_config> &vlans,
std::shared_ptr<swss::DBConnector> config_db)
{
std::vector<std::string> servers;
bool option_79_default = true;
Expand All @@ -119,13 +125,35 @@ void processRelayNotification(std::deque<swss::KeyOpFieldsValuesTuple> &entries,
std::string vlan = kfvKey(entry);
std::string operation = kfvOp(entry);
std::vector<swss::FieldValueTuple> fieldValues = kfvFieldsValues(entry);
bool has_ipv6_address = false;

const std::string match_pattern = "VLAN_INTERFACE|" + vlan + "|*";
auto keys = config_db->keys(match_pattern);
for (const auto &itr : keys) {
auto found = itr.find_last_of('|');
if (found == std::string::npos) {
syslog(LOG_WARNING, "%s doesn't exist in VLAN_INTERFACE table, skip it", vlan.c_str());
continue;
}
std::string ip_address = itr.substr(found + 1);
if (ip_address.find(":") != std::string::npos) {
has_ipv6_address = true;
break;
}
}

if (!has_ipv6_address) {
syslog(LOG_WARNING, "%s doesn't have IPv6 address configured, skip it", vlan.c_str());
continue;
}

relay_config intf;
intf.is_option_79 = option_79_default;
intf.is_interface_id = interface_id_default;
intf.interface = vlan;
intf.mux_key = "";
intf.state_db = nullptr;
intf.is_lla_ready = false;
for (auto &fieldValue: fieldValues) {
std::string f = fvField(fieldValue);
std::string v = fvValue(fieldValue);
Expand Down Expand Up @@ -154,3 +182,28 @@ void processRelayNotification(std::deque<swss::KeyOpFieldsValuesTuple> &entries,
vlans[vlan] = intf;
}
}

/**
* @code bool check_is_lla_ready(std::string vlan)
*
* @brief Check whether link local address appear in vlan interface
*
* @param vlan string of vlan name
*
* @return bool value indicates whether lla ready
*/
bool check_is_lla_ready(std::string vlan) {
const std::string cmd = "ip -6 addr show " + vlan + " scope link 2> /dev/null";
std::array<char, 256> buffer;
std::string result;
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd.c_str(), "r"), pclose);
if (pipe) {
while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
result += buffer.data();
}
if (!result.empty()) {
return true;
}
}
return false;
}
29 changes: 23 additions & 6 deletions src/config_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,19 @@ 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, bool dynamic,
* 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, bool dynamic,
std::shared_ptr<swss::DBConnector> config_db);

/**
* @code void handleRelayNotification(swss::SubscriberStateTable &ipHelpersTable, std::unordered_map<std::string, relay_config> &vlans)
* @code void handleRelayNotification(swss::SubscriberStateTable &ipHelpersTable, std::unordered_map<std::string, relay_config> &vlans,
* std::shared_ptr<swss::DBConnector> config_db)
*
* @brief handles DHCPv6 relay configuration change notification
*
Expand All @@ -51,10 +54,12 @@ void get_dhcp(std::unordered_map<std::string, relay_config> &vlans, swss::Subscr
*
* @return none
*/
void handleRelayNotification(swss::SubscriberStateTable &ipHelpersTable, std::unordered_map<std::string, relay_config> &vlans);
void handleRelayNotification(swss::SubscriberStateTable &ipHelpersTable, std::unordered_map<std::string, relay_config> &vlans,
std::shared_ptr<swss::DBConnector> config_db);

/**
* @code void processRelayNotification(std::deque<swss::KeyOpFieldsValuesTuple> &entries, std::unordered_map<std::string, relay_config> &vlans)
* @code void processRelayNotification(std::deque<swss::KeyOpFieldsValuesTuple> &entries, std::unordered_map<std::string, relay_config> &vlans,
* std::shared_ptr<swss::DBConnector> config_db)
*
* @brief process DHCPv6 relay servers and options configuration change notification
*
Expand All @@ -63,4 +68,16 @@ void handleRelayNotification(swss::SubscriberStateTable &ipHelpersTable, std::un
*
* @return none
*/
void processRelayNotification(std::deque<swss::KeyOpFieldsValuesTuple> &entries, std::unordered_map<std::string, relay_config> &vlans);
void processRelayNotification(std::deque<swss::KeyOpFieldsValuesTuple> &entries, std::unordered_map<std::string, relay_config> &vlans,
std::shared_ptr<swss::DBConnector> config_db);

/**
* @code bool check_is_lla_ready(std::string vlan)
*
* @brief Check whether link local address appear in vlan interface
*
* @param vlan string of vlan name
*
* @return bool value indicates whether lla ready
*/
bool check_is_lla_ready(std::string vlan);
144 changes: 110 additions & 34 deletions src/relay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -895,6 +895,8 @@ void client_callback(evutil_socket_t fd, short event, void *arg) {
}

std::string intf(interfaceName);
// For Vlans that lla is not ready, they wouldn't be added into vlan_map, hence it would be blocked here, no need to
// add is_lla_ready flag check in this callback func
auto vlan = vlan_map.find(intf);
if (vlan == vlan_map.end()) {
if (intf.find(CLIENT_IF_PREFIX) != std::string::npos) {
Expand Down Expand Up @@ -1088,6 +1090,10 @@ void server_callback_dualtor(evutil_socket_t fd, short event, void *arg) {
syslog(LOG_WARNING, "Invalid DHCPv6 header content on loopback socket, packet will be dropped\n");
continue;
}
if (!config->is_lla_ready) {
syslog(LOG_WARNING, "Link local address for %s is not ready, packet will be dropped\n", config->interface.c_str());
continue;
}
auto loopback_str = std::string(loopback);
increase_counter(config->state_db, loopback_str, msg_type);
relay_relay_reply(server_recv_buffer, buffer_sz, config);
Expand Down Expand Up @@ -1279,40 +1285,29 @@ void loop_relay(std::unordered_map<std::string, relay_config> &vlans) {
}
}

for(auto &vlan : vlans) {
int gua_sock = 0;
int lla_sock = 0;
vlan.second.config_db = config_db;
vlan.second.mux_table = mStateDbMuxTablePtr;
vlan.second.state_db = state_db;
vlan.second.mux_key = vlan_member + vlan.second.interface + "|";

update_vlan_mapping(vlan.first, config_db);

initialize_counter(vlan.second.state_db, vlan.second.interface);

if (prepare_vlan_sockets(gua_sock, lla_sock, vlan.second) != -1) {
vlan.second.gua_sock = gua_sock;
vlan.second.lla_sock = lla_sock;
vlan.second.lo_sock = lo_sock;

sockets.push_back(gua_sock);
sockets.push_back(lla_sock);
prepare_relay_config(vlan.second, gua_sock, filter);
if (!dual_tor_sock) {
auto event = event_new(base, gua_sock, EV_READ|EV_PERSIST,
server_callback, &(vlan.second));
if (event == NULL) {
syslog(LOG_ERR, "libevent: Failed to create server listen libevent\n");
}
event_add(event, NULL);
syslog(LOG_INFO, "libevent: add server listen socket for %s\n", vlan.first.c_str());
}
} else {
syslog(LOG_ERR, "Failed to create dualtor loopback listen socket");
exit(EXIT_FAILURE);
}
}
// Add timer to periodly check lla un-ready vlan
struct event *timer_event;
struct timeval tv;
auto timer_args = new std::tuple<
std::unordered_map<std::string, struct relay_config> &,
std::shared_ptr<swss::DBConnector>,
std::shared_ptr<swss::DBConnector>,
std::shared_ptr<swss::Table>,
std::vector<int>,
int,
int,
struct event *
>(vlans, config_db, state_db, mStateDbMuxTablePtr, sockets, lo_sock, lo_sock, nullptr);
timer_event = event_new(base, -1, EV_PERSIST, lla_check_callback, timer_args);
std::get<7>(*timer_args) = timer_event;
evutil_timerclear(&tv);
// Check timer is set to 60s
tv.tv_sec = 60;
event_add(timer_event, &tv);

// We set check timer to be executed every 60s, it would case that its first excution be delayed 60s,
// hence manually invoke it here to immediate execute it
lla_check_callback(-1, 0, timer_args);

if(signal_init() == 0 && signal_start() == 0) {
shutdown_relay();
Expand Down Expand Up @@ -1351,3 +1346,84 @@ void clear_counter(std::shared_ptr<swss::DBConnector> state_db) {
state_db->del(itr);
}
}

/**
* @code void lla_check_callback(evutil_socket_t fd, short event, void *arg);
*
* @brief callback for libevent timer to check whether lla is ready for vlan
*
* @param fd libevent socket
* @param event libevent triggered event
* @param arg callback argument provided by user
*
* @return none
*/
void lla_check_callback(evutil_socket_t fd, short event, void *arg) {
auto args = reinterpret_cast<std::tuple<
std::unordered_map<std::string, struct relay_config> *,
std::shared_ptr<swss::DBConnector>,
std::shared_ptr<swss::DBConnector>,
std::shared_ptr<swss::Table>,
std::vector<int>,
int,
int,
struct event *
> *>(arg);
auto vlans = std::get<0>(*args);
auto config_db = std::get<1>(*args);
auto state_db = std::get<2>(*args);
auto mStateDbMuxTablePtr = std::get<3>(*args);
auto sockets = std::get<4>(*args);
auto lo_sock = std::get<5>(*args);
auto filter = std::get<6>(*args);
auto timer_event = std::get<7>(*args);

bool all_llas_are_ready = true;
for(auto &vlan : *vlans) {
if (vlan.second.is_lla_ready) {
continue;
}
if (!check_is_lla_ready(vlan.first)) {
syslog(LOG_WARNING, "Link local address for %s is not ready\n", vlan.first.c_str());
all_llas_are_ready = false;
continue;
}
vlan.second.is_lla_ready = true;
int gua_sock = 0;
int lla_sock = 0;
vlan.second.config_db = config_db;
vlan.second.mux_table = mStateDbMuxTablePtr;
vlan.second.state_db = state_db;
vlan.second.mux_key = vlan_member + vlan.second.interface + "|";

update_vlan_mapping(vlan.first, config_db);

initialize_counter(vlan.second.state_db, vlan.second.interface);

if (prepare_vlan_sockets(gua_sock, lla_sock, vlan.second) != -1) {
vlan.second.gua_sock = gua_sock;
vlan.second.lla_sock = lla_sock;
vlan.second.lo_sock = lo_sock;

sockets.push_back(gua_sock);
sockets.push_back(lla_sock);
prepare_relay_config(vlan.second, gua_sock, filter);
if (!dual_tor_sock) {
auto server_callback_event = event_new(base, gua_sock, EV_READ|EV_PERSIST,
server_callback, &(vlan.second));
if (server_callback_event == NULL) {
syslog(LOG_ERR, "libevent: Failed to create server listen libevent\n");
}
event_add(server_callback_event, NULL);
syslog(LOG_INFO, "libevent: add server listen socket for %s\n", vlan.first.c_str());
}
} else {
syslog(LOG_ERR, "Failed to create dualtor loopback listen socket");
exit(EXIT_FAILURE);
}
}
if (all_llas_are_ready) {
syslog(LOG_INFO, "All Vlans' lla are ready, terminate check timer");
event_del(timer_event);
}
}
14 changes: 14 additions & 0 deletions src/relay.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ struct relay_config {
bool is_interface_id;
std::shared_ptr<swss::Table> mux_table;
std::shared_ptr<swss::DBConnector> config_db;
bool is_lla_ready;
};

/* DHCPv6 messages and options */
Expand Down Expand Up @@ -483,3 +484,16 @@ void server_callback(evutil_socket_t fd, short event, void *arg);
*
*/
void clear_counter(std::shared_ptr<swss::DBConnector> state_db);

/**
* @code void lla_check_callback(evutil_socket_t fd, short event, void *arg);
*
* @brief callback for libevent timer to check whether lla is ready for vlan
*
* @param fd libevent socket
* @param event libevent triggered event
* @param arg callback argument provided by user
*
* @return none
*/
void lla_check_callback(evutil_socket_t fd, short event, void *arg);
Loading

0 comments on commit 6694ea5

Please sign in to comment.