Skip to content
Draft
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
222 changes: 116 additions & 106 deletions src/dhcp_device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,7 @@

extern bool debug_on;

extern std::string agg_dev_all;
extern std::string agg_dev_prefix;

extern std::unordered_map<std::string, std::unordered_set<std::string>> rev_vlan_map;
extern std::unordered_map<std::string, std::unordered_set<std::string>> rev_portchan_map;

const std::string db_counter_name[DHCP_MESSAGE_TYPE_COUNT] = {
"Unknown", "Discover", "Offer", "Request", "Decline", "Ack", "Nak", "Release", "Inform", "Bootp", "Malformed", "Ignored"
Expand Down Expand Up @@ -55,6 +51,18 @@ const dhcpv6_message_type_t monitored_v6_msgs[] = {

uint8_t monitored_v6_msg_sz = sizeof(monitored_v6_msgs) / sizeof(*monitored_v6_msgs);

static const dhcpv6_message_type_t monitored_v6_forward_rx_msgs[] = {
DHCPV6_MESSAGE_TYPE_SOLICIT,
DHCPV6_MESSAGE_TYPE_REQUEST,
DHCPV6_MESSAGE_TYPE_RELAY_FORW
};

static const dhcpv6_message_type_t monitored_v6_reply_tx_msgs[] = {
DHCPV6_MESSAGE_TYPE_ADVERTISE,
DHCPV6_MESSAGE_TYPE_REPLY,
DHCPV6_MESSAGE_TYPE_RELAY_REPL
};

const char *intf_type_name[DHCP_DEVICE_INTF_TYPE_COUNT] = {
[DHCP_DEVICE_INTF_TYPE_UPLINK] = "uplink (north)",
[DHCP_DEVICE_INTF_TYPE_DOWNLINK] = "downlink (south)",
Expand Down Expand Up @@ -103,31 +111,18 @@ static bool check_counter_not_transmitted(const std::string &ifname, int rx_sock
}

/**
* @code dhcp_device_check_positive_health(ifname);
* @brief Check that DHCP relayed messages are being transmitted out of this interface/dev
* using its counters. The interface is positively healthy if there are DHCP message
* travelling through it.
* @param ifname interface name
* @return DHCP_MON_STATUS_HEALTHY, DHCP_MON_STATUS_UNHEALTHY, or DHCP_MON_STATUS_INDETERMINATE
*/
static dhcp_mon_status_t dhcp_device_check_positive_health(const std::string &ifname)
{
return check_counter_not_transmitted(ifname, rx_sock, tx_sock, (const int *)monitored_msgs, monitored_msg_sz) ?
DHCP_MON_STATUS_UNHEALTHY : DHCP_MON_STATUS_HEALTHY;
}

/**
* @code dhcp_device_check_positive_health_v6(ifname);
* @brief Check that DHCPv6 relayed messages are being transmitted out of this interface/dev
* using its counters. The interface is positively healthy if there are DHCPv6 message
* travelling through it.
* @param ifname interface name
* @return DHCP_MON_STATUS_HEALTHY, DHCP_MON_STATUS_UNHEALTHY, or DHCP_MON_STATUS_INDETERMINATE
* @code get_counter_delta(ifname, sock, msg_type);
* @brief Get the increase in one message-type counter since the last snapshot.
* @param ifname interface name
* @param sock socket containing the counter
* @param msg_type message type
* @return counter increase since the last snapshot
*/
static dhcp_mon_status_t dhcp_device_check_positive_health_v6(const std::string &ifname)
static uint64_t get_counter_delta(const std::string &ifname, int sock, int msg_type)
{
return check_counter_not_transmitted(ifname, rx_sock_v6, tx_sock_v6, (const int *)monitored_v6_msgs, monitored_v6_msg_sz) ?
DHCP_MON_STATUS_UNHEALTHY : DHCP_MON_STATUS_HEALTHY;
const sock_info_t &sock_info = sock_mgr_get_sock_info(sock);
return sock_info.all_counters.at(ifname).at(msg_type) -
sock_info.all_counters_snapshot.at(ifname).at(msg_type);
}

/**
Expand All @@ -144,19 +139,51 @@ static dhcp_mon_status_t dhcp_device_check_positive_health_v6(const std::string
*/
static bool check_counter_increased(const std::string &ifname, int sock, const int *monitored_msgs, size_t monitored_msg_cnt)
{
const sock_info_t &sock_info = sock_mgr_get_sock_info(sock);
const counter_t &counters = sock_info.all_counters.at(ifname);
const counter_t &counters_snapshot = sock_info.all_counters_snapshot.at(ifname);

// true if any counter has increased
for (size_t i = 0; i < monitored_msg_cnt; i++) {
if (counters.at(monitored_msgs[i]) > counters_snapshot.at(monitored_msgs[i])) {
if (get_counter_delta(ifname, sock, monitored_msgs[i]) > 0) {
return true;
}
}
return false;
}

/**
* @code dhcp_device_check_positive_health(ifname);
* @brief Check that RX Discover, Offer, Request, and Ack activity has matching same-type TX activity.
* @param ifname interface name
* @return DHCP_MON_STATUS_HEALTHY, DHCP_MON_STATUS_UNHEALTHY, or DHCP_MON_STATUS_INDETERMINATE
*/
static dhcp_mon_status_t dhcp_device_check_positive_health(const std::string &ifname)
{
return check_counter_not_transmitted(ifname, rx_sock, tx_sock, (const int *)monitored_msgs, monitored_msg_sz) ?
DHCP_MON_STATUS_UNHEALTHY : DHCP_MON_STATUS_HEALTHY;
}

/**
* @code dhcp_device_check_positive_health_v6(ifname);
* @brief Check that RX Solicit, Request, or Relay-Forward activity has TX Relay-Forward activity,
* and RX Relay-Reply activity has TX Advertise, Reply, or Relay-Reply activity.
* @param ifname interface name
* @return DHCP_MON_STATUS_HEALTHY, DHCP_MON_STATUS_UNHEALTHY, or DHCP_MON_STATUS_INDETERMINATE
*/
static dhcp_mon_status_t dhcp_device_check_positive_health_v6(const std::string &ifname)
{
const bool forward_rx = check_counter_increased(
ifname, rx_sock_v6, (const int *)monitored_v6_forward_rx_msgs,
sizeof(monitored_v6_forward_rx_msgs) / sizeof(*monitored_v6_forward_rx_msgs));
const bool forward_tx = get_counter_delta(ifname, tx_sock_v6, DHCPV6_MESSAGE_TYPE_RELAY_FORW) > 0;
const bool reply_rx = get_counter_delta(ifname, rx_sock_v6, DHCPV6_MESSAGE_TYPE_RELAY_REPL) > 0;
const bool reply_tx = check_counter_increased(
ifname, tx_sock_v6, (const int *)monitored_v6_reply_tx_msgs,
sizeof(monitored_v6_reply_tx_msgs) / sizeof(*monitored_v6_reply_tx_msgs));

if ((forward_rx && !forward_tx) || (reply_rx && !reply_tx)) {
return DHCP_MON_STATUS_UNHEALTHY;
}
return forward_rx || reply_rx ? DHCP_MON_STATUS_HEALTHY : DHCP_MON_STATUS_INDETERMINATE;
}

/**
* @code dhcp_device_check_negative_health(ifname);
*
Expand Down Expand Up @@ -204,7 +231,7 @@ static dhcp_mon_status_t dhcp_device_check_negative_health_v6(const std::string
* @return true if deltas are equal with given ratio, false otherwise
*/
static bool check_counters_delta_expected(const std::string &ifname, const std::string &other_ifname, int sock,
uint8_t ratio, const int *monitored_msgs, size_t monitored_msg_cnt)
size_t ratio, const int *monitored_msgs, size_t monitored_msg_cnt)
{
const sock_info_t &sock_info = sock_mgr_get_sock_info(sock);
const counter_t &counters = sock_info.all_counters.at(ifname);
Expand All @@ -223,63 +250,49 @@ static bool check_counters_delta_expected(const std::string &ifname, const std::
return true;
}

static dhcp_mon_status_t dhcp_device_check_agg_equal_rx(const std::string &ifname)
{
std::string agg_ifname = agg_dev_prefix + ifname;
return check_counters_delta_expected(ifname, agg_ifname, rx_sock, 1, (const int *)monitored_msgs, monitored_msg_sz) ?
DHCP_MON_STATUS_HEALTHY : DHCP_MON_STATUS_UNHEALTHY;
}

static dhcp_mon_status_t dhcp_device_check_agg_equal_tx(const std::string &ifname)
{
std::string agg_ifname = agg_dev_prefix + ifname;
return check_counters_delta_expected(ifname, agg_ifname, tx_sock, 1, (const int *)monitored_msgs, monitored_msg_sz) ?
DHCP_MON_STATUS_HEALTHY : DHCP_MON_STATUS_UNHEALTHY;
}

static dhcp_mon_status_t dhcp_device_check_agg_equal_rx_v6(const std::string &ifname)
{
std::string agg_ifname = agg_dev_prefix + ifname;
return check_counters_delta_expected(ifname, agg_ifname, rx_sock_v6, 1, (const int *)monitored_v6_msgs, monitored_v6_msg_sz) ?
DHCP_MON_STATUS_HEALTHY : DHCP_MON_STATUS_UNHEALTHY;
}

static dhcp_mon_status_t dhcp_device_check_agg_equal_tx_v6(const std::string &ifname)
{
std::string agg_ifname = agg_dev_prefix + ifname;
return check_counters_delta_expected(ifname, agg_ifname, tx_sock_v6, 1, (const int *)monitored_v6_msgs, monitored_v6_msg_sz) ?
DHCP_MON_STATUS_HEALTHY : DHCP_MON_STATUS_UNHEALTHY;
}

static dhcp_mon_status_t dhcp_device_check_agg_multiple_rx(const std::string &ifname)
{
std::string agg_ifname = agg_dev_prefix + ifname;
return check_counters_delta_expected(ifname, agg_ifname, rx_sock, readonly_access(rev_vlan_map, ifname).size() + readonly_access(rev_portchan_map, ifname).size(),
(const int *)monitored_msgs, monitored_msg_sz) ?
DHCP_MON_STATUS_HEALTHY : DHCP_MON_STATUS_UNHEALTHY;
}

static dhcp_mon_status_t dhcp_device_check_agg_multiple_tx(const std::string &ifname)
{
std::string agg_ifname = agg_dev_prefix + ifname;
return check_counters_delta_expected(ifname, agg_ifname, tx_sock, readonly_access(rev_vlan_map, ifname).size() + readonly_access(rev_portchan_map, ifname).size(),
(const int *)monitored_msgs, monitored_msg_sz) ?
DHCP_MON_STATUS_HEALTHY : DHCP_MON_STATUS_UNHEALTHY;
}

static dhcp_mon_status_t dhcp_device_check_agg_multiple_rx_v6(const std::string &ifname)
/**
* @code get_aggregate_ratio(ifname, sock);
*
* @brief Get the expected aggregate ratio for an interface and packet direction
*
* @param ifname Parent interface name
* @param sock Socket identifying the protocol family and direction
*
* @return One for RX and PortChannel TX; direct VLAN member count for VLAN TX
*/
static size_t get_aggregate_ratio(const std::string &ifname, int sock)
{
std::string agg_ifname = agg_dev_prefix + ifname;
return check_counters_delta_expected(ifname, agg_ifname, rx_sock_v6, readonly_access(rev_vlan_map, ifname).size() + readonly_access(rev_portchan_map, ifname).size(),
(const int *)monitored_v6_msgs, monitored_v6_msg_sz) ?
DHCP_MON_STATUS_HEALTHY : DHCP_MON_STATUS_UNHEALTHY;
// RX crosses one edge. VLAN TX is observed on every direct member; PortChannel TX is observed on one member.
if (sock_mgr_get_sock_info(sock).is_rx) {
return 1;
}
const auto vlan = rev_vlan_map.find(ifname);
return vlan == rev_vlan_map.end() ? 1 : vlan->second.size();
}

static dhcp_mon_status_t dhcp_device_check_agg_multiple_tx_v6(const std::string &ifname)
/**
* @code check_aggregate_health(ifname, sock, monitored_msgs, monitored_msg_cnt);
*
* @brief Compare a parent interface counter with the aggregate of its direct member-interface counters
*
* @param ifname Parent interface name
* @param sock Socket identifying the protocol family and direction
* @param monitored_msgs Message types to compare
* @param monitored_msg_cnt Number of message types to compare
*
* @return HEALTHY when idle or matching, UNHEALTHY when the expected ratio does not match
*/
static dhcp_mon_status_t check_aggregate_health(const std::string &ifname, int sock, const int *monitored_msgs,
size_t monitored_msg_cnt)
{
std::string agg_ifname = agg_dev_prefix + ifname;
return check_counters_delta_expected(ifname, agg_ifname, tx_sock_v6, readonly_access(rev_vlan_map, ifname).size() + readonly_access(rev_portchan_map, ifname).size(),
(const int *)monitored_v6_msgs, monitored_v6_msg_sz) ?
const std::string agg_ifname = get_agg_counter_ifname(ifname);
if (!check_counter_increased(ifname, sock, monitored_msgs, monitored_msg_cnt) &&
!check_counter_increased(agg_ifname, sock, monitored_msgs, monitored_msg_cnt)) {
return DHCP_MON_STATUS_HEALTHY;
}
return check_counters_delta_expected(
ifname, agg_ifname, sock, get_aggregate_ratio(ifname, sock),
monitored_msgs, monitored_msg_cnt) ?
DHCP_MON_STATUS_HEALTHY : DHCP_MON_STATUS_UNHEALTHY;
}

Expand Down Expand Up @@ -382,7 +395,14 @@ void dhcp_device_print_status_debug(const std::string &ifname, dhcp_counters_typ

dhcp_mon_status_t dhcp_device_get_status(const std::string &ifname, dhcp_device_check_t check_type)
{
if (sock_mgr_counters_unchanged(ifname, (const int *)monitored_msgs, monitored_msg_sz, (const int *)monitored_v6_msgs, monitored_v6_msg_sz)) {
bool aggregate_check =
check_type == DHCP_DEVICE_CHECK_AGG_RX ||
check_type == DHCP_DEVICE_CHECK_AGG_TX ||
check_type == DHCP_DEVICE_CHECK_AGG_RX_V6 ||
check_type == DHCP_DEVICE_CHECK_AGG_TX_V6;
if (!aggregate_check &&
sock_mgr_counters_unchanged(ifname, (const int *)monitored_msgs, monitored_msg_sz,
(const int *)monitored_v6_msgs, monitored_v6_msg_sz)) {
return DHCP_MON_STATUS_INDETERMINATE;
}

Expand All @@ -395,27 +415,17 @@ dhcp_mon_status_t dhcp_device_get_status(const std::string &ifname, dhcp_device_
return dhcp_device_check_positive_health_v6(ifname);
case DHCP_DEVICE_CHECK_NEGATIVE_V6:
return dhcp_device_check_negative_health_v6(ifname);
case DHCP_DEVICE_CHECK_AGG_EQUAL_RX:
return dhcp_device_check_agg_equal_rx(ifname);
case DHCP_DEVICE_CHECK_AGG_EQUAL_TX:
return dhcp_device_check_agg_equal_tx(ifname);
case DHCP_DEVICE_CHECK_AGG_EQUAL_RX_V6:
return dhcp_device_check_agg_equal_rx_v6(ifname);
case DHCP_DEVICE_CHECK_AGG_EQUAL_TX_V6:
return dhcp_device_check_agg_equal_tx_v6(ifname);
case DHCP_DEVICE_CHECK_AGG_MULTIPLE_RX:
return dhcp_device_check_agg_multiple_rx(ifname);
case DHCP_DEVICE_CHECK_AGG_MULTIPLE_TX:
return dhcp_device_check_agg_multiple_tx(ifname);
case DHCP_DEVICE_CHECK_AGG_MULTIPLE_RX_V6:
return dhcp_device_check_agg_multiple_rx_v6(ifname);
case DHCP_DEVICE_CHECK_AGG_MULTIPLE_TX_V6:
return dhcp_device_check_agg_multiple_tx_v6(ifname);
case DHCP_DEVICE_CHECK_AGG_RX:
return check_aggregate_health(ifname, rx_sock, (const int *)monitored_msgs, monitored_msg_sz);
case DHCP_DEVICE_CHECK_AGG_TX:
return check_aggregate_health(ifname, tx_sock, (const int *)monitored_msgs, monitored_msg_sz);
case DHCP_DEVICE_CHECK_AGG_RX_V6:
return check_aggregate_health(ifname, rx_sock_v6, (const int *)monitored_v6_msgs, monitored_v6_msg_sz);
case DHCP_DEVICE_CHECK_AGG_TX_V6:
return check_aggregate_health(ifname, tx_sock_v6, (const int *)monitored_v6_msgs, monitored_v6_msg_sz);
default:
break;
return DHCP_MON_STATUS_UNHEALTHY;
}

return DHCP_MON_STATUS_UNHEALTHY;
}

int initialize_intf_mac_and_ip_addr(dhcp_device_context_t *context)
Expand Down
14 changes: 5 additions & 9 deletions src/dhcp_device.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,15 +135,11 @@ typedef enum
DHCP_DEVICE_CHECK_NEGATIVE, /** Presence of relayed DHCP packets activity is flagged as unhealthy state */
DHCP_DEVICE_CHECK_POSITIVE, /** Validate that received DORA packets are relayed */
DHCP_DEVICE_CHECK_NEGATIVE_V6, /** Presence of relayed DHCPv6 packets activity is flagged as unhealthy state */
DHCP_DEVICE_CHECK_POSITIVE_V6, /** Validate that received SARR packets are relayed */
DHCP_DEVICE_CHECK_AGG_EQUAL_RX, /** Validate that aggregate device rx counters equal sum of member interfaces rx counters */
DHCP_DEVICE_CHECK_AGG_EQUAL_TX, /** Validate that aggregate device tx counters equal sum of member interfaces tx counters */
DHCP_DEVICE_CHECK_AGG_EQUAL_RX_V6, /** Validate that aggregate device rx counters equal sum of member interfaces rx counters for IPv6 */
DHCP_DEVICE_CHECK_AGG_EQUAL_TX_V6, /** Validate that aggregate device tx counters equal sum of member interfaces tx counters for IPv6 */
DHCP_DEVICE_CHECK_AGG_MULTIPLE_RX, /** Validate that aggregate device rx counters are multiple of member interfaces rx counters */
DHCP_DEVICE_CHECK_AGG_MULTIPLE_TX, /** Validate that aggregate device tx counters are multiple of member interfaces tx counters */
DHCP_DEVICE_CHECK_AGG_MULTIPLE_RX_V6, /** Validate that aggregate device rx counters are multiple of member interfaces rx counters for IPv6 */
DHCP_DEVICE_CHECK_AGG_MULTIPLE_TX_V6 /** Validate that aggregate device tx counters are multiple of member interfaces tx counters for IPv6 */
DHCP_DEVICE_CHECK_POSITIVE_V6, /** Validate SARR and DHCPv6 relay-wrapper transformations */
DHCP_DEVICE_CHECK_AGG_RX, /** Compare IPv4 RX on a parent interface with its member aggregate */
DHCP_DEVICE_CHECK_AGG_TX, /** Compare IPv4 TX on a parent interface with its member aggregate */
DHCP_DEVICE_CHECK_AGG_RX_V6, /** Compare IPv6 RX on a parent interface with its member aggregate */
DHCP_DEVICE_CHECK_AGG_TX_V6 /** Compare IPv6 TX on a parent interface with its member aggregate */
} dhcp_device_check_t;

/** Monitored DHCP message type */
Expand Down
Loading