mirror of
https://gitlab.com/prpl-foundation/prplmesh/prplMesh.git
synced 2025-12-20 01:21:22 +08:00
multi_vendor: airties: refactor Airties Ethernet Stats and Interface TLVs
- Except for radio temperature, Airties vendor-specific TLVs no longer access the data model directly. - Ethernet port metrics used in the Airties Ethernet Stats TLV are now retrieved from /proc/net/dev. - Parameters for the Airties Ethernet Interface TLV are now obtained via Linux system calls and ethtool. - Removed AgentDB dependency from both Ethernet Stats and Ethernet Interface TLVs, and reduced the AgentDB usage scope in several other places to avoid holding locks for too long. - Simplified the unique port-ID generation logic for Ethernet ports. If an interface does not already have an assigned ID, we generate a new one starting from 1. The first interface gets ID 1, and each new interface receives the next sequential ID (2, 3, 4, ...). If the interface is already present in the map, we simply return the previously assigned ID. - Consolidated the Airties Ethernet Stats TLV types into a single unified TLV. Previously there were two different TLV types (one for optional metrics). Now all metrics, optional or not, are included in a single TLV. PPM-3393 Signed-off-by: Durmus Koyuncu <durmus.koyuncu@airties.com>
This commit is contained in:
@@ -29,7 +29,9 @@ bool ieee802_3_link_metrics_collector::get_link_metrics(
|
||||
*/
|
||||
uint32_t phy_rate_mbps = UINT32_MAX;
|
||||
uint32_t max_speed_mbps = UINT32_MAX;
|
||||
net::network_utils::linux_iface_get_speed(local_interface_name, phy_rate_mbps, max_speed_mbps);
|
||||
bool is_full_duplex = false;
|
||||
net::network_utils::linux_iface_get_link_settings(local_interface_name, phy_rate_mbps,
|
||||
max_speed_mbps, is_full_duplex);
|
||||
|
||||
/**
|
||||
* Note: The MAC throughput capacity is a function of the physical data rate and
|
||||
|
||||
@@ -44,7 +44,9 @@ bool MediaType::get_media_type(const std::string &interface_name,
|
||||
if (ieee1905_1::eMediaTypeGroup::IEEE_802_3 == media_type_group) {
|
||||
uint32_t link_speed;
|
||||
uint32_t max_speed;
|
||||
if (net::network_utils::linux_iface_get_speed(interface_name, link_speed, max_speed)) {
|
||||
bool is_full_duplex;
|
||||
if (net::network_utils::linux_iface_get_link_settings(interface_name, link_speed, max_speed,
|
||||
is_full_duplex)) {
|
||||
if (SPEED_100 == max_speed) {
|
||||
media_type = ieee1905_1::eMediaType::IEEE_802_3U_FAST_ETHERNET;
|
||||
} else if (SPEED_1000 <= max_speed) {
|
||||
|
||||
@@ -86,6 +86,25 @@ public:
|
||||
struct in_addr nmask;
|
||||
} raw_iface_info;
|
||||
|
||||
typedef struct {
|
||||
uint64_t rx_bytes;
|
||||
uint64_t rx_packets;
|
||||
uint64_t rx_errs;
|
||||
uint64_t rx_drop;
|
||||
uint64_t rx_fifo;
|
||||
uint64_t rx_frame;
|
||||
uint64_t rx_compressed;
|
||||
uint64_t rx_multicast;
|
||||
uint64_t tx_bytes;
|
||||
uint64_t tx_packets;
|
||||
uint64_t tx_errs;
|
||||
uint64_t tx_drop;
|
||||
uint64_t tx_fifo;
|
||||
uint64_t tx_colls;
|
||||
uint64_t tx_carrier;
|
||||
uint64_t tx_compressed;
|
||||
} sNetDevStats;
|
||||
|
||||
static bool is_valid_mac(std::string mac);
|
||||
|
||||
static std::string ipv4_to_string(const net::sIpv4Addr &ip);
|
||||
@@ -183,11 +202,13 @@ public:
|
||||
* It equals SPEED_UNKNOWN if the interface's link is down.
|
||||
* @param[out] max_advertised_speed On success, maximum advertised speed in Mbps of the network interface
|
||||
* as defined in SPEED_* macros included in ethtool.h.
|
||||
* @param[out] is_full_duplex Set to true if the interface is in full-duplex mode,
|
||||
* false otherwise.
|
||||
*
|
||||
* @return True if speed could be successfully obtained and false otherwise.
|
||||
*/
|
||||
static bool linux_iface_get_speed(const std::string &iface, uint32_t &link_speed,
|
||||
uint32_t &max_advertised_speed);
|
||||
static bool linux_iface_get_link_settings(const std::string &iface, uint32_t &link_speed,
|
||||
uint32_t &max_advertised_speed, bool &is_full_duplex);
|
||||
|
||||
/**
|
||||
* @brief Gets interface statistics for the given network interface.
|
||||
@@ -359,6 +380,17 @@ public:
|
||||
* @return A vector of strings containing the names of the lan interfaces.
|
||||
*/
|
||||
static std::vector<std::string> linux_get_lan_interfaces();
|
||||
|
||||
/**
|
||||
* @brief Reads interface statistics from /proc/net/dev.
|
||||
*
|
||||
* Parses /proc/net/dev and fills the output struct for the given interface.
|
||||
*
|
||||
* @param[in] ifname Name of the interface to read statistics for.
|
||||
* @param[out] out Structure that will be filled with the interface metrics on success.
|
||||
* @return true on success, false if the interface is not found or cannot be read.
|
||||
*/
|
||||
static bool linux_get_net_dev_stats(const std::string &ifname, sNetDevStats &out);
|
||||
};
|
||||
} // namespace net
|
||||
} // namespace beerocks
|
||||
|
||||
@@ -920,183 +920,166 @@ bool network_utils::linux_iface_is_up_and_running(const std::string &iface)
|
||||
return (false);
|
||||
}
|
||||
|
||||
#define ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32 (SCHAR_MAX)
|
||||
#ifdef ETHTOOL_GLINKSETTINGS
|
||||
/*
|
||||
* Kernel exposes link mode bitmasks as arrays of u32.
|
||||
* 32 is the commonly used size for ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32.
|
||||
*/
|
||||
static const unsigned ETHTOOL_LINK_MODE_MASK_MAX_LEN = 32;
|
||||
|
||||
static bool linux_iface_get_max_speed_from_link_modes(const uint32_t *link_mode_flags,
|
||||
uint32_t link_mode_flags_nwords,
|
||||
uint32_t &max_speed)
|
||||
static bool ethtool_bit_set(const uint32_t *mask, unsigned int nr)
|
||||
{
|
||||
if (!link_mode_flags || link_mode_flags_nwords < 1) {
|
||||
unsigned int idx = nr / 32u;
|
||||
uint32_t bit = 1u << (nr % 32u);
|
||||
|
||||
return (idx < ETHTOOL_LINK_MODE_MASK_MAX_LEN) && (mask[idx] & bit);
|
||||
}
|
||||
|
||||
/*
|
||||
* Query link settings using the newer ETHTOOL_GLINKSETTINGS API.
|
||||
* This is the modern replacement for ETHTOOL_GSET on newer kernels.
|
||||
*/
|
||||
static bool ethtool_glinksettings(int sock, const std::string &ifname, uint32_t &link_speed,
|
||||
uint32_t &max_advertised_speed, bool &is_full_duplex)
|
||||
{
|
||||
struct ifreq ifr {
|
||||
};
|
||||
std::snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", ifname.c_str());
|
||||
|
||||
const int nwords = ETHTOOL_LINK_MODE_MASK_MAX_LEN;
|
||||
|
||||
// Space for supported, advertising, and link partner masks (3 x nwords)
|
||||
const size_t ecmd_size =
|
||||
sizeof(struct ethtool_link_settings) + 3u * static_cast<size_t>(nwords) * sizeof(uint32_t);
|
||||
|
||||
std::vector<uint8_t> storage(ecmd_size, 0);
|
||||
auto *req = reinterpret_cast<struct ethtool_link_settings *>(storage.data());
|
||||
|
||||
ifr.ifr_data = reinterpret_cast<caddr_t>(req);
|
||||
req->cmd = ETHTOOL_GLINKSETTINGS;
|
||||
req->link_mode_masks_nwords = nwords;
|
||||
|
||||
// Retry logic required because kernel may return negative nwords on first call.
|
||||
int retries = 2;
|
||||
while (retries-- > 0) {
|
||||
if (ioctl(sock, SIOCETHTOOL, &ifr) == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (req->link_mode_masks_nwords <= 0) {
|
||||
req->link_mode_masks_nwords = -req->link_mode_masks_nwords;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (req->link_mode_masks_nwords <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* map speed to common legacy ethtool link mode masks
|
||||
*/
|
||||
const std::map<uint32_t, std::vector<uint32_t>, std::greater<uint32_t>>
|
||||
legacy_link_modes_to_speed_mapping = {
|
||||
{SPEED_10,
|
||||
{
|
||||
ADVERTISED_10baseT_Half,
|
||||
ADVERTISED_10baseT_Full,
|
||||
}},
|
||||
{SPEED_100,
|
||||
{
|
||||
ADVERTISED_100baseT_Half,
|
||||
ADVERTISED_100baseT_Full,
|
||||
}},
|
||||
{SPEED_1000,
|
||||
{
|
||||
ADVERTISED_1000baseT_Half,
|
||||
ADVERTISED_1000baseT_Full,
|
||||
ADVERTISED_1000baseKX_Full,
|
||||
}},
|
||||
{SPEED_2500,
|
||||
{
|
||||
ADVERTISED_2500baseX_Full,
|
||||
}},
|
||||
{SPEED_10000,
|
||||
{
|
||||
ADVERTISED_10000baseT_Full,
|
||||
ADVERTISED_10000baseKX4_Full,
|
||||
ADVERTISED_10000baseKR_Full,
|
||||
ADVERTISED_10000baseR_FEC,
|
||||
}},
|
||||
{SPEED_20000,
|
||||
{
|
||||
ADVERTISED_20000baseMLD2_Full,
|
||||
ADVERTISED_20000baseKR2_Full,
|
||||
}},
|
||||
{SPEED_40000,
|
||||
{
|
||||
ADVERTISED_40000baseKR4_Full,
|
||||
ADVERTISED_40000baseCR4_Full,
|
||||
ADVERTISED_40000baseSR4_Full,
|
||||
ADVERTISED_40000baseLR4_Full,
|
||||
}},
|
||||
{SPEED_56000,
|
||||
{
|
||||
ADVERTISED_56000baseKR4_Full,
|
||||
ADVERTISED_56000baseCR4_Full,
|
||||
ADVERTISED_56000baseSR4_Full,
|
||||
ADVERTISED_56000baseLR4_Full,
|
||||
}},
|
||||
};
|
||||
for (const auto &link_mode_speed : legacy_link_modes_to_speed_mapping) {
|
||||
for (const auto &mask : link_mode_speed.second) {
|
||||
if (link_mode_flags[0] & mask) {
|
||||
max_speed = link_mode_speed.first;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
uint32_t *sbits = req->link_mode_masks;
|
||||
|
||||
// Determine maximum advertised capability based on link mode bits.
|
||||
if (ethtool_bit_set(sbits, ETHTOOL_LINK_MODE_10000baseT_Full_BIT)) {
|
||||
max_advertised_speed = SPEED_10000;
|
||||
} else if (ethtool_bit_set(sbits, ETHTOOL_LINK_MODE_5000baseT_Full_BIT)) {
|
||||
max_advertised_speed = SPEED_5000;
|
||||
} else if (ethtool_bit_set(sbits, ETHTOOL_LINK_MODE_2500baseT_Full_BIT)) {
|
||||
max_advertised_speed = SPEED_2500;
|
||||
} else if (ethtool_bit_set(sbits, ETHTOOL_LINK_MODE_1000baseT_Full_BIT) ||
|
||||
ethtool_bit_set(sbits, ETHTOOL_LINK_MODE_1000baseT_Half_BIT)) {
|
||||
max_advertised_speed = SPEED_1000;
|
||||
} else if (ethtool_bit_set(sbits, ETHTOOL_LINK_MODE_100baseT_Full_BIT) ||
|
||||
ethtool_bit_set(sbits, ETHTOOL_LINK_MODE_100baseT_Half_BIT)) {
|
||||
max_advertised_speed = SPEED_100;
|
||||
} else if (ethtool_bit_set(sbits, ETHTOOL_LINK_MODE_10baseT_Full_BIT) ||
|
||||
ethtool_bit_set(sbits, ETHTOOL_LINK_MODE_10baseT_Half_BIT)) {
|
||||
max_advertised_speed = SPEED_10;
|
||||
} else {
|
||||
max_advertised_speed = SPEED_UNKNOWN;
|
||||
}
|
||||
|
||||
return false;
|
||||
// Current link speed and duplex mode.
|
||||
link_speed = req->speed;
|
||||
is_full_duplex = (req->duplex == DUPLEX_FULL);
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif // ETHTOOL_GLINKSETTINGS
|
||||
|
||||
/*
|
||||
* Fallback using the legacy ETHTOOL_GSET API.
|
||||
* Older kernels use this. Modern kernels still support it but consider deprecated.
|
||||
*/
|
||||
static bool ethtool_gset(int sock, const std::string &ifname, uint32_t &link_speed,
|
||||
uint32_t &max_advertised_speed, bool &is_full_duplex)
|
||||
{
|
||||
struct ifreq ifr {
|
||||
};
|
||||
std::snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", ifname.c_str());
|
||||
|
||||
struct ethtool_cmd ecmd {
|
||||
};
|
||||
ifr.ifr_data = reinterpret_cast<caddr_t>(&ecmd);
|
||||
ecmd.cmd = ETHTOOL_GSET;
|
||||
|
||||
if (ioctl(sock, SIOCETHTOOL, &ifr) == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Detect maximum supported speed
|
||||
if (ecmd.supported & SUPPORTED_10000baseT_Full) {
|
||||
max_advertised_speed = SPEED_10000;
|
||||
} else if (ecmd.supported & SUPPORTED_2500baseX_Full) {
|
||||
max_advertised_speed = SPEED_2500;
|
||||
} else if (ecmd.supported & (SUPPORTED_1000baseT_Full | SUPPORTED_1000baseT_Half)) {
|
||||
max_advertised_speed = SPEED_1000;
|
||||
} else if (ecmd.supported & (SUPPORTED_100baseT_Full | SUPPORTED_100baseT_Half)) {
|
||||
max_advertised_speed = SPEED_100;
|
||||
} else if (ecmd.supported & (SUPPORTED_10baseT_Full | SUPPORTED_10baseT_Half)) {
|
||||
max_advertised_speed = SPEED_10;
|
||||
} else {
|
||||
max_advertised_speed = SPEED_UNKNOWN;
|
||||
}
|
||||
|
||||
// Current negotiated link speed
|
||||
link_speed = ethtool_cmd_speed(&ecmd);
|
||||
|
||||
// Duplex mode
|
||||
is_full_duplex = (ecmd.duplex == DUPLEX_FULL);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool network_utils::linux_iface_get_speed(const std::string &iface, uint32_t &link_speed,
|
||||
uint32_t &max_advertised_speed)
|
||||
bool network_utils::linux_iface_get_link_settings(const std::string &iface, uint32_t &link_speed,
|
||||
uint32_t &max_advertised_speed,
|
||||
bool &is_full_duplex)
|
||||
{
|
||||
int sock;
|
||||
bool result = false;
|
||||
|
||||
link_speed = SPEED_UNKNOWN;
|
||||
max_advertised_speed = link_speed;
|
||||
|
||||
sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
|
||||
int sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
|
||||
if (sock < 0) {
|
||||
LOG(ERROR) << "Can't create SOCK_DGRAM socket: " << strerror(errno);
|
||||
} else {
|
||||
struct ifreq ifr;
|
||||
int rc;
|
||||
|
||||
string_utils::copy_string(ifr.ifr_name, iface.c_str(), sizeof(ifr.ifr_name));
|
||||
#ifdef ETHTOOL_GLINKSETTINGS
|
||||
char ecmd[sizeof(struct ethtool_link_settings) +
|
||||
3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32 * sizeof(__u32)] = {};
|
||||
struct ethtool_link_settings *req = reinterpret_cast<struct ethtool_link_settings *>(ecmd);
|
||||
__u32 *link_mode_data = reinterpret_cast<__u32 *>(req + 1);
|
||||
ifr.ifr_data = ecmd;
|
||||
|
||||
/* Handshake with kernel to determine number of words for link
|
||||
* mode bitmaps. When requested number of bitmap words is not
|
||||
* the one expected by kernel, the latter returns the integer
|
||||
* opposite of what it is expecting. We request length 0 below
|
||||
* (aka. invalid bitmap length) to get this info.
|
||||
*/
|
||||
req->cmd = ETHTOOL_GLINKSETTINGS;
|
||||
rc = ioctl(sock, SIOCETHTOOL, &ifr);
|
||||
if (0 == rc) {
|
||||
/**
|
||||
* See above: we expect a strictly negative value from kernel.
|
||||
*/
|
||||
if ((req->link_mode_masks_nwords >= 0) || (ETHTOOL_GLINKSETTINGS != req->cmd)) {
|
||||
LOG(ERROR) << "ETHTOOL_GLINKSETTINGS handshake failed";
|
||||
} else {
|
||||
/**
|
||||
* Got the real req->link_mode_masks_nwords, now send the real request
|
||||
*/
|
||||
req->cmd = ETHTOOL_GLINKSETTINGS;
|
||||
req->link_mode_masks_nwords = -req->link_mode_masks_nwords;
|
||||
rc = ioctl(sock, SIOCETHTOOL, &ifr);
|
||||
if ((0 != rc) || (req->link_mode_masks_nwords <= 0) ||
|
||||
(req->cmd != ETHTOOL_GLINKSETTINGS)) {
|
||||
LOG(ERROR) << "ETHTOOL_GLINKSETTINGS request failed";
|
||||
} else {
|
||||
link_speed = req->speed;
|
||||
|
||||
/* layout of link_mode_data fields:
|
||||
* __u32 map_supported[link_mode_masks_nwords];
|
||||
* __u32 map_advertising[link_mode_masks_nwords];
|
||||
* __u32 map_lp_advertising[link_mode_masks_nwords];
|
||||
*/
|
||||
max_advertised_speed = link_speed;
|
||||
const __u32 *map_advertising = &link_mode_data[req->link_mode_masks_nwords];
|
||||
linux_iface_get_max_speed_from_link_modes(
|
||||
map_advertising, req->link_mode_masks_nwords, max_advertised_speed);
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* ETHTOOL_GSET is deprecated and must be used only if ETHTOOL_GLINKSETTINGS
|
||||
* didn't work
|
||||
* or when not supported
|
||||
*/
|
||||
#ifdef ETHTOOL_GLINKSETTINGS
|
||||
if (!result)
|
||||
#endif
|
||||
{
|
||||
struct ethtool_cmd ecmd_legacy;
|
||||
|
||||
ifr.ifr_data = reinterpret_cast<char *>(&ecmd_legacy);
|
||||
|
||||
memset(&ecmd_legacy, 0, sizeof(ecmd_legacy));
|
||||
ecmd_legacy.cmd = ETHTOOL_GSET;
|
||||
|
||||
rc = ioctl(sock, SIOCETHTOOL, &ifr);
|
||||
if (0 == rc) {
|
||||
link_speed = ethtool_cmd_speed(&ecmd_legacy);
|
||||
max_advertised_speed = link_speed;
|
||||
linux_iface_get_max_speed_from_link_modes(&ecmd_legacy.advertising, 1,
|
||||
max_advertised_speed);
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (rc < 0) {
|
||||
LOG(ERROR) << "ioctl failed: " << strerror(errno);
|
||||
}
|
||||
|
||||
close(sock);
|
||||
LOG(ERROR) << "Failed opening DGRAM socket: " << strerror(errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG(DEBUG) << "iface " << iface << " has speed current: " << link_speed
|
||||
<< " max: " << max_advertised_speed << " result: " << result;
|
||||
bool success = false;
|
||||
|
||||
return result;
|
||||
#ifdef ETHTOOL_GLINKSETTINGS
|
||||
// Try modern API first.
|
||||
success = ethtool_glinksettings(sock, iface, link_speed, max_advertised_speed, is_full_duplex);
|
||||
|
||||
// If unsupported, fallback to legacy API.
|
||||
if (!success)
|
||||
#endif
|
||||
{
|
||||
success = ethtool_gset(sock, iface, link_speed, max_advertised_speed, is_full_duplex);
|
||||
}
|
||||
|
||||
close(sock);
|
||||
|
||||
LOG(DEBUG) << "iface " << iface << " has speed current: " << link_speed
|
||||
<< " max: " << max_advertised_speed << " result: " << success;
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
std::vector<network_utils::ip_info> network_utils::get_ip_list()
|
||||
@@ -1819,3 +1802,43 @@ std::vector<std::string> network_utils::linux_get_lan_interfaces()
|
||||
|
||||
return lan_interfaces;
|
||||
}
|
||||
|
||||
bool network_utils::linux_get_net_dev_stats(const std::string &ifname, sNetDevStats &out)
|
||||
{
|
||||
const std::string path = "/proc/net/dev";
|
||||
std::ifstream file(path);
|
||||
if (!file) {
|
||||
LOG(ERROR) << "Failed to open " << path;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string line;
|
||||
|
||||
/* Skip the first two header lines ("Inter-| Receive | Transmit") */
|
||||
std::getline(file, line);
|
||||
std::getline(file, line);
|
||||
|
||||
while (std::getline(file, line)) {
|
||||
auto colon = line.find(':');
|
||||
if (colon == std::string::npos) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Trim leading spaces and extract the interface name */
|
||||
auto start = line.find_first_not_of(" \t");
|
||||
std::string name = line.substr(start, colon - start);
|
||||
if (name != ifname) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Extract the numeric fields after the colon into our struct */
|
||||
std::istringstream iss(line.substr(colon + 1));
|
||||
return static_cast<bool>(iss >> out.rx_bytes >> out.rx_packets >> out.rx_errs >>
|
||||
out.rx_drop >> out.rx_fifo >> out.rx_frame >> out.rx_compressed >>
|
||||
out.rx_multicast >> out.tx_bytes >> out.tx_packets >>
|
||||
out.tx_errs >> out.tx_drop >> out.tx_fifo >> out.tx_colls >>
|
||||
out.tx_carrier >> out.tx_compressed);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -45,31 +45,21 @@ public:
|
||||
|
||||
static bool add_airties_ethernet_interface_tlv(ieee1905_1::CmduMessageTx &cmdu_tx);
|
||||
|
||||
static uint64_t get_value_from_dm(std::string param, std::string cntr_path);
|
||||
|
||||
static bool add_airties_ethernet_stats_tlv(ieee1905_1::CmduMessageTx &cmdu_tx);
|
||||
|
||||
static bool add_radio_capability(ieee1905_1::CmduMessageTx &cmdu_tx);
|
||||
|
||||
static bool
|
||||
get_counters_info(std::shared_ptr<airties::tlvAirtiesEthernetStats> &tlvAirtiesEthStats);
|
||||
static bool get_all_counters_info(
|
||||
std::shared_ptr<airties::tlvAirtiesEthernetStatsallcntr> &tlvAirtiesEthStats);
|
||||
|
||||
/**
|
||||
* @brief Assigns a unique port ID for the given interface name.
|
||||
* @brief Assigns a sequential port ID for the given interface name.
|
||||
*
|
||||
* This function ensures that each interface name is assigned a unique port ID.
|
||||
* If the interface has already been assigned an ID, it returns the same ID.
|
||||
* Each interface name is mapped to a unique, persistent ID.
|
||||
* If the interface was already assigned an ID, the same ID is returned.
|
||||
* Otherwise, the next available ID is assigned, starting from 1.
|
||||
*
|
||||
* The function attempts to extract a numeric suffix from the interface name
|
||||
* (e.g., "eth3" → 3 or "eth0" → 0, "eth0.1" -> 1, eth3-> 2) and uses it as the port ID if it's not already used.
|
||||
* Otherwise, it assigns the next available ID starting from 1.
|
||||
*
|
||||
* @param interface_name The name of the interface (e.g., "eth1", "wan0").
|
||||
* @return A unique port ID associated with the given interface name.
|
||||
* @param interface_name The interface name used as the key (e.g., "eth0", "lan1", "wan").
|
||||
* @return The assigned port ID for this interface.
|
||||
*/
|
||||
static uint8_t assign_unique_port_id(const std::string &alias_raw);
|
||||
static uint8_t assign_unique_port_id(const std::string &interface_name);
|
||||
};
|
||||
} // namespace airties
|
||||
|
||||
|
||||
@@ -63,31 +63,30 @@ enum link_types_enum {
|
||||
TYPE_10GBPS
|
||||
};
|
||||
|
||||
//Macros for different Link Speed Types
|
||||
// Macros for different Link Speed Types
|
||||
#define BITRATE_10 10
|
||||
#define BITRATE_100 100
|
||||
#define BITRATE_1K 1000
|
||||
#define BITRATE_2K 2000
|
||||
#define BITRATE_2_5K 2500
|
||||
#define BITRATE_5K 5000
|
||||
#define BITRATE_10K 10000
|
||||
|
||||
/*
|
||||
* This enum contains all the optional counters.
|
||||
* Values are assigned based on whether the corresponding
|
||||
* counter support is present in Ethernet.Interface.Stats. data model
|
||||
* Optional Ethernet counters.
|
||||
* 1 = supported, 0 = not supported.
|
||||
*
|
||||
* If the DM has this counter, the value is assigned 1.
|
||||
* If the DM doesnt have this counter, the value is assigned 0.
|
||||
* Some counters cannot currently be retrieved from the platform (/sys, ethtool, etc.),
|
||||
* so they remain disabled. If we later find a valid source for any of them,
|
||||
* simply switch the corresponding value to 1.
|
||||
*/
|
||||
enum supported_stats_enum {
|
||||
BCAST_BYTES_SENT = 0,
|
||||
BCAST_BYTES_RECVD = 0,
|
||||
BCAST_PKTS_SENT = 1,
|
||||
BCAST_PKTS_RECVD = 1,
|
||||
BCAST_PKTS_SENT = 0,
|
||||
BCAST_PKTS_RECVD = 0,
|
||||
MCAST_BYTES_SENT = 0,
|
||||
MCAST_BYTES_RECVD = 0,
|
||||
MCAST_PKTS_SENT = 1,
|
||||
MCAST_PKTS_SENT = 0,
|
||||
MCAST_PKTS_RECVD = 1
|
||||
};
|
||||
|
||||
@@ -98,14 +97,14 @@ enum supported_stats_enum {
|
||||
#define MCAST_BYTES_SUPPORTED (MCAST_BYTES_SENT && MCAST_BYTES_RECVD)
|
||||
|
||||
/*
|
||||
* Following function returns the link speed.
|
||||
* Following function returns the link type.
|
||||
* This is based on the requirement,
|
||||
* Bit 7:4 Supported Link Type Link type (0=undefined, 1=10Mbps, 2=100Mbps,
|
||||
3=1Gbps, 4=2,5Gbps, 5=5Gpbs, 6=10Gbps)
|
||||
* Bit 3:0 Current Link Type Link type (0=undefined, 1=10Mbps, 2=100Mbps,
|
||||
3=1Gbps, 4=2,5Gbps, 5=5Gpbs, 6=10Gbps
|
||||
*/
|
||||
uint8_t get_bitvalue(uint32_t bit_rate)
|
||||
static uint8_t bitrate_to_link_type(uint32_t bit_rate)
|
||||
{
|
||||
uint8_t value;
|
||||
switch (bit_rate) {
|
||||
@@ -118,7 +117,6 @@ uint8_t get_bitvalue(uint32_t bit_rate)
|
||||
case BITRATE_1K:
|
||||
value = TYPE_1GBPS;
|
||||
break;
|
||||
case BITRATE_2K:
|
||||
case BITRATE_2_5K:
|
||||
value = TYPE_2_5GBPS;
|
||||
break;
|
||||
@@ -165,11 +163,6 @@ uint16_t set_supp_stats_val()
|
||||
return var;
|
||||
}
|
||||
|
||||
static AmbiorixVariantSmartPtr get_eth_interface_object(const std::string &path)
|
||||
{
|
||||
return (beerocks::bpl::m_ambiorix_cl.get_object(path));
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if its WAN or LAN interface.
|
||||
* If its WAN, then dont add it to the TLV.
|
||||
@@ -200,11 +193,6 @@ bool get_data_from_dm(AmbiorixVariantSmartPtr ð_interface, const std::string
|
||||
*/
|
||||
bool tlvf_airties_utils::add_airties_ethernet_interface_tlv(ieee1905_1::CmduMessageTx &m_cmdu_tx)
|
||||
{
|
||||
std::string dm_path = "Device.Ethernet.";
|
||||
std::string interface_path = "Interface.";
|
||||
std::string interface_dm_path;
|
||||
uint8_t num_ports = 0;
|
||||
|
||||
auto tlvAirtiesEthIntf = m_cmdu_tx.addClass<airties::tlvAirtiesEthernetInterface>();
|
||||
if (!tlvAirtiesEthIntf) {
|
||||
LOG(ERROR) << "addClass wfa_map::tlvDeviceEthernetInterface failed";
|
||||
@@ -216,115 +204,41 @@ bool tlvf_airties_utils::add_airties_ethernet_interface_tlv(ieee1905_1::CmduMess
|
||||
tlvAirtiesEthIntf->tlv_id() =
|
||||
static_cast<int>(airties::eAirtiesTlVId::AIRTIES_ETHERNET_INTERFACE);
|
||||
|
||||
//Get the Ambiorix object
|
||||
auto eth_interface = get_eth_interface_object(dm_path);
|
||||
if (!eth_interface) {
|
||||
LOG(ERROR) << "Failed to get the ambiorix object for path " << dm_path;
|
||||
return false;
|
||||
}
|
||||
auto lan_ifaces = beerocks::net::network_utils::linux_get_lan_interfaces();
|
||||
for (const auto &lan_iface : lan_ifaces) {
|
||||
std::string iface_mac;
|
||||
|
||||
//Number of Ethernet Interfaces present
|
||||
if (!get_data_from_dm(eth_interface, "LinkNumberOfEntries", num_ports) || (!num_ports)) {
|
||||
LOG(ERROR) << "Failed to populate Ethernet Interface TLV as "
|
||||
"LinkNumberOfEntries is not valid";
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint8_t i = 1; i <= num_ports; i++) {
|
||||
|
||||
interface_dm_path = dm_path + interface_path + std::to_string(i) + ".";
|
||||
|
||||
eth_interface = get_eth_interface_object(interface_dm_path);
|
||||
if (!eth_interface) {
|
||||
LOG(ERROR) << "Failed to get the ambiorix object for path " << interface_dm_path;
|
||||
if (!beerocks::net::network_utils::linux_iface_get_mac(lan_iface, iface_mac)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//Check if its a wan interface.
|
||||
if (check_wan_interface(eth_interface)) {
|
||||
uint32_t link_speed;
|
||||
uint32_t max_speed;
|
||||
bool is_full_duplex;
|
||||
|
||||
if (!net::network_utils::linux_iface_get_link_settings(lan_iface, link_speed, max_speed,
|
||||
is_full_duplex)) {
|
||||
LOG(WARNING) << "Failed to get link settings for " << lan_iface;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto interface_list = tlvAirtiesEthIntf->create_interface_list();
|
||||
|
||||
std::string interface_alias;
|
||||
if (!get_data_from_dm(eth_interface, "Alias", interface_alias) ||
|
||||
(interface_alias.empty())) {
|
||||
LOG(ERROR) << "Failed to read Alias value from " << interface_dm_path;
|
||||
continue;
|
||||
}
|
||||
interface_list->port_id() =
|
||||
airties::tlvf_airties_utils::assign_unique_port_id(interface_alias);
|
||||
interface_list->port_id() = airties::tlvf_airties_utils::assign_unique_port_id(lan_iface);
|
||||
interface_list->eth_mac() = tlvf::mac_from_string(iface_mac);
|
||||
interface_list->set_eth_intf_name(lan_iface);
|
||||
|
||||
std::string mac_addr;
|
||||
if (!get_data_from_dm(eth_interface, "MACAddress", mac_addr)) {
|
||||
LOG(ERROR) << "Failed to get the MAC address for port_id " << interface_list->port_id();
|
||||
continue;
|
||||
}
|
||||
interface_list->eth_mac() = tlvf::mac_from_string(mac_addr);
|
||||
|
||||
std::string eth_interface_name;
|
||||
if (!get_data_from_dm(eth_interface, "Name", eth_interface_name)) {
|
||||
LOG(ERROR) << "Failed to get the Interface Name for port_id "
|
||||
<< interface_list->port_id();
|
||||
continue;
|
||||
}
|
||||
interface_list->set_eth_intf_name(eth_interface_name);
|
||||
|
||||
/*
|
||||
* Port State Bitwise
|
||||
* Bit 7 - eth_port_admin_state - Device.Ethernet.Interface.1.Enable
|
||||
* Bit 6 - eth_port_link_state - Device.Ethernet.Interface.1.Status
|
||||
* Bit 5 - eth_port_duplex_mode - Device.Ethernet.Interface.1.DuplexMode
|
||||
* Bit 4 - 0 - Reserved
|
||||
*/
|
||||
bool port_admin_state = false;
|
||||
if (!get_data_from_dm(eth_interface, "Enable", port_admin_state)) {
|
||||
LOG(ERROR) << "Failed to get the admin state for the port_id "
|
||||
<< interface_list->port_id();
|
||||
continue;
|
||||
}
|
||||
interface_list->flags1().eth_port_admin_state = port_admin_state;
|
||||
|
||||
std::string port_link_state;
|
||||
if (!get_data_from_dm(eth_interface, "Status", port_link_state)) {
|
||||
LOG(ERROR) << "Failed to get the link state for the port_id "
|
||||
<< interface_list->port_id();
|
||||
continue;
|
||||
}
|
||||
interface_list->flags1().eth_port_link_state = (port_link_state == "Up" ? 1 : 0);
|
||||
|
||||
std::string port_dup_mode;
|
||||
if (!get_data_from_dm(eth_interface, "DuplexMode", port_dup_mode)) {
|
||||
LOG(ERROR) << "Failed to get the duplex mode for the port_id "
|
||||
<< interface_list->port_id();
|
||||
continue;
|
||||
}
|
||||
interface_list->flags1().eth_port_duplex_mode =
|
||||
(((port_dup_mode == "Auto") || (port_dup_mode == "Full")) ? 1 : 0);
|
||||
|
||||
/*
|
||||
* Next octet updation for Link Type
|
||||
* Bits 7 - 4 : Supported Link Type
|
||||
* Bits 3 - 0 : Current Link Type
|
||||
*/
|
||||
uint32_t supp_link_type = 0, cur_link_type = 0;
|
||||
if (!get_data_from_dm(eth_interface, "MaxBitRate", supp_link_type)) {
|
||||
LOG(ERROR) << "Failed to get the Maximum support bit rate for port_id "
|
||||
<< interface_list->port_id();
|
||||
continue;
|
||||
}
|
||||
interface_list->flags2().supported_link_type = get_bitvalue(supp_link_type);
|
||||
|
||||
if (!get_data_from_dm(eth_interface, "CurrentBitRate", cur_link_type)) {
|
||||
LOG(ERROR) << "Failed to get the current bit rate for port_id "
|
||||
<< interface_list->port_id();
|
||||
continue;
|
||||
}
|
||||
interface_list->flags2().current_link_type = get_bitvalue(cur_link_type);
|
||||
interface_list->flags1().eth_port_admin_state =
|
||||
beerocks::net::network_utils::linux_iface_is_up(lan_iface);
|
||||
interface_list->flags1().eth_port_link_state =
|
||||
beerocks::net::network_utils::linux_iface_is_up_and_running(lan_iface);
|
||||
interface_list->flags1().eth_port_duplex_mode = is_full_duplex ? 1 : 0;
|
||||
interface_list->flags2().supported_link_type = bitrate_to_link_type(max_speed);
|
||||
interface_list->flags2().current_link_type = bitrate_to_link_type(link_speed);
|
||||
|
||||
tlvAirtiesEthIntf->add_interface_list(interface_list);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -361,234 +275,20 @@ uint64_t swap_and_convert_counter(uint64_t val)
|
||||
*/
|
||||
uint64_t convertBytes_to_Kb(uint64_t bytes_val) { return (bytes_val / BYTES_IN_KB); }
|
||||
|
||||
uint64_t tlvf_airties_utils::get_value_from_dm(std::string param, std::string cntr_path)
|
||||
inline void insert_ethernet_stats_item(std::shared_ptr<airties::cPortList> &port_list,
|
||||
uint64_t counter)
|
||||
{
|
||||
uint64_t output = 0, value = 0;
|
||||
|
||||
auto eth_interface = get_eth_interface_object(cntr_path);
|
||||
if (!eth_interface) {
|
||||
LOG(ERROR) << "failed to get radio Stats object " << cntr_path;
|
||||
return output;
|
||||
}
|
||||
if (!eth_interface->read_child<>(value, param.c_str())) {
|
||||
LOG(INFO) << "Failed to read " << cntr_path << " " << param;
|
||||
return output;
|
||||
auto statsItem = port_list->create_statsItem();
|
||||
if (!statsItem) {
|
||||
LOG(ERROR) << "Failed to create stats item!";
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* As per the requirement, only bytes counter need
|
||||
* to be be converted to KiloByte. As of now, only
|
||||
* BytesSent and Received are supported in DM.
|
||||
*/
|
||||
if ((param == "BytesSent") || (param == "BytesReceived")) {
|
||||
if (value) {
|
||||
value = convertBytes_to_Kb(value);
|
||||
}
|
||||
}
|
||||
uint64_t swapped = swap_and_convert_counter(counter);
|
||||
|
||||
output = swap_and_convert_counter(value);
|
||||
return output;
|
||||
}
|
||||
statsItem->set_item(&swapped, COUNTERS_SIZE);
|
||||
|
||||
template <typename T> void populate_cntrs_info(std::shared_ptr<T> &port_list, std::string cntr_path)
|
||||
{
|
||||
uint64_t value = 0;
|
||||
value = tlvf_airties_utils::get_value_from_dm("BytesSent", cntr_path);
|
||||
port_list->set_bytes_sent(&value, COUNTERS_SIZE);
|
||||
|
||||
value = tlvf_airties_utils::get_value_from_dm("BytesReceived", cntr_path);
|
||||
port_list->set_bytes_recvd(&value, COUNTERS_SIZE);
|
||||
|
||||
value = tlvf_airties_utils::get_value_from_dm("PacketsSent", cntr_path);
|
||||
port_list->set_packets_sent(&value, COUNTERS_SIZE);
|
||||
|
||||
value = tlvf_airties_utils::get_value_from_dm("PacketsReceived", cntr_path);
|
||||
port_list->set_packets_recvd(&value, COUNTERS_SIZE);
|
||||
|
||||
value = tlvf_airties_utils::get_value_from_dm("ErrorsSent", cntr_path);
|
||||
port_list->set_tx_pkt_errors(&value, COUNTERS_SIZE);
|
||||
|
||||
value = tlvf_airties_utils::get_value_from_dm("ErrorsReceived", cntr_path);
|
||||
port_list->set_rx_pkt_errors(&value, COUNTERS_SIZE);
|
||||
|
||||
value = tlvf_airties_utils::get_value_from_dm("BroadcastPacketsSent", cntr_path);
|
||||
port_list->set_bcast_pkts_sent(&value, COUNTERS_SIZE);
|
||||
|
||||
value = tlvf_airties_utils::get_value_from_dm("BroadcastPacketsReceived", cntr_path);
|
||||
port_list->set_bcast_pkts_recvd(&value, COUNTERS_SIZE);
|
||||
|
||||
value = tlvf_airties_utils::get_value_from_dm("MulticastPacketsSent", cntr_path);
|
||||
port_list->set_mcast_pkts_sent(&value, COUNTERS_SIZE);
|
||||
|
||||
value = tlvf_airties_utils::get_value_from_dm("MulticastPacketsReceived", cntr_path);
|
||||
port_list->set_mcast_pkts_recvd(&value, COUNTERS_SIZE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Function to populate the Ethernet Stats TLV
|
||||
* for all the counters present.
|
||||
*/
|
||||
bool tlvf_airties_utils::get_all_counters_info(
|
||||
std::shared_ptr<airties::tlvAirtiesEthernetStatsallcntr> &tlvEthStats)
|
||||
{
|
||||
std::string dm_path = "Device.Ethernet.";
|
||||
std::string interface_path = "Interface.";
|
||||
std::string stats_path = "Stats.";
|
||||
std::string cntr_path, interface_dm_path;
|
||||
uint8_t num_ports = 0;
|
||||
|
||||
tlvEthStats->vendor_oui() =
|
||||
(sVendorOUI(airties::tlvAirtiesMsgType::airtiesVendorOUI::OUI_AIRTIES));
|
||||
tlvEthStats->tlv_id() = static_cast<int>(airties::eAirtiesTlVId::AIRTIES_ETHERNET_STATS);
|
||||
|
||||
//Start filling the fields
|
||||
|
||||
tlvEthStats->supported_extra_stats() = set_supp_stats_val();
|
||||
|
||||
auto eth_interf = get_eth_interface_object(dm_path);
|
||||
if (!eth_interf) {
|
||||
LOG(ERROR) << "Failed to get the ambiorix object for path " << dm_path;
|
||||
return false;
|
||||
}
|
||||
/*
|
||||
* Get the number of Ethernet ports
|
||||
* for the loop count
|
||||
*/
|
||||
if (!get_data_from_dm(eth_interf, "InterfaceNumberOfEntries", num_ports) || !num_ports) {
|
||||
LOG(ERROR) << "Failed to populate Ethernet Stats TLV as "
|
||||
"InterfaceNumberOfEntries is not valid";
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint8_t i = 1; i <= num_ports; i++) {
|
||||
|
||||
interface_dm_path = dm_path + interface_path + std::to_string(i) + ".";
|
||||
|
||||
auto eth_interface = beerocks::bpl::m_ambiorix_cl.get_object(interface_dm_path);
|
||||
if (!eth_interface) {
|
||||
LOG(ERROR) << "Failed to get the ambiorix object for path " << interface_dm_path;
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if its WAN or LAN interface.
|
||||
* If its WAN, then dont add it to the TLV.
|
||||
*/
|
||||
if (check_wan_interface(eth_interface)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto port_list = tlvEthStats->create_port_list();
|
||||
|
||||
cntr_path = dm_path + interface_path + std::to_string(i) + "." + stats_path;
|
||||
|
||||
std::string interface_alias;
|
||||
if (!get_data_from_dm(eth_interface, "Alias", interface_alias) ||
|
||||
(interface_alias.empty())) {
|
||||
LOG(ERROR) << "Failed to read Alias value from DM";
|
||||
return false;
|
||||
}
|
||||
port_list->port_id() = airties::tlvf_airties_utils::assign_unique_port_id(interface_alias);
|
||||
|
||||
//Populate the counters
|
||||
populate_cntrs_info(port_list, cntr_path);
|
||||
/*
|
||||
* TODO:
|
||||
* Broadcast/Multicast Byte counters are not supported in Data model
|
||||
* for which community bug has raised.
|
||||
* This is a placeholder for fetching those counters.
|
||||
* As of now, for all the ports, the hard coded values will be
|
||||
* populated in the TLV fields.
|
||||
* In future, if the solution to fetch these counters are
|
||||
* available, it can be implemented in separate function and called
|
||||
* here or implementented in populate_cntrs_info() itself.
|
||||
*/
|
||||
uint64_t c_bbytes_sent = swap_and_convert_counter(100);
|
||||
port_list->set_bcast_bytes_sent(&c_bbytes_sent, 6);
|
||||
|
||||
uint64_t c_bbytes_recvd = swap_and_convert_counter(200);
|
||||
port_list->set_bcast_pkts_recvd(&c_bbytes_recvd, 6);
|
||||
|
||||
uint64_t c_mbytes_sent = swap_and_convert_counter(300);
|
||||
port_list->set_mcast_bytes_sent(&c_mbytes_sent, 6);
|
||||
|
||||
uint64_t c_mbytes_recvd = swap_and_convert_counter(400);
|
||||
port_list->set_mcast_bytes_recvd(&c_mbytes_recvd, 6);
|
||||
|
||||
tlvEthStats->add_port_list(port_list);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tlvf_airties_utils::get_counters_info(
|
||||
std::shared_ptr<airties::tlvAirtiesEthernetStats> &tlvEthStats)
|
||||
{
|
||||
std::string dm_path = "Device.Ethernet.";
|
||||
std::string interface_path = "Interface.";
|
||||
std::string stats_path = "Stats.";
|
||||
std::string cntr_path, interface_dm_path;
|
||||
uint8_t num_ports = 0;
|
||||
|
||||
tlvEthStats->vendor_oui() =
|
||||
(sVendorOUI(airties::tlvAirtiesMsgType::airtiesVendorOUI::OUI_AIRTIES));
|
||||
tlvEthStats->tlv_id() = static_cast<int>(airties::eAirtiesTlVId::AIRTIES_ETHERNET_STATS);
|
||||
|
||||
//Start filling the fields
|
||||
|
||||
tlvEthStats->supported_extra_stats() = set_supp_stats_val();
|
||||
|
||||
auto eth_interf = beerocks::bpl::m_ambiorix_cl.get_object(dm_path);
|
||||
if (!eth_interf) {
|
||||
LOG(ERROR) << "Failed to get the ambiorix object for path " << dm_path;
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the number of Ethernet ports
|
||||
* for the loop count
|
||||
*/
|
||||
if (!get_data_from_dm(eth_interf, "InterfaceNumberOfEntries", num_ports) || (!num_ports)) {
|
||||
LOG(ERROR) << "Failed to populate Ethernet Stats TLV as "
|
||||
"InterfaceNumberOfEntries is not valid";
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint8_t i = 1; i <= num_ports; i++) {
|
||||
|
||||
interface_dm_path = dm_path + interface_path + std::to_string(i) + ".";
|
||||
|
||||
auto eth_interface = beerocks::bpl::m_ambiorix_cl.get_object(interface_dm_path);
|
||||
if (!eth_interface) {
|
||||
LOG(ERROR) << "Failed to get the ambiorix object for path " << interface_dm_path;
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if its WAN or LAN interface.
|
||||
* If its WAN, then dont add it to the TLV.
|
||||
*/
|
||||
if (check_wan_interface(eth_interface)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto port_list = tlvEthStats->create_port_list();
|
||||
|
||||
cntr_path = dm_path + interface_path + std::to_string(i) + "." + stats_path;
|
||||
|
||||
std::string interface_alias;
|
||||
if (!get_data_from_dm(eth_interface, "Alias", interface_alias) ||
|
||||
(interface_alias.empty())) {
|
||||
LOG(ERROR) << "Failed to read Alias value from DM";
|
||||
return false;
|
||||
}
|
||||
|
||||
port_list->port_id() = airties::tlvf_airties_utils::assign_unique_port_id(interface_alias);
|
||||
|
||||
populate_cntrs_info(port_list, cntr_path);
|
||||
|
||||
tlvEthStats->add_port_list(port_list);
|
||||
}
|
||||
return true;
|
||||
port_list->add_statsItem(statsItem);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -597,27 +297,58 @@ bool tlvf_airties_utils::get_counters_info(
|
||||
*/
|
||||
bool tlvf_airties_utils::add_airties_ethernet_stats_tlv(ieee1905_1::CmduMessageTx &m_cmdu_tx)
|
||||
{
|
||||
/*
|
||||
* If the optional counters support is present
|
||||
* in the DM, then populate TLV: tlvAirtiesEthernetStatsallcntr
|
||||
* else populate all the counters TLV:tlvAirtiesEthernetStats
|
||||
*/
|
||||
if (BCAST_BYTES_SUPPORTED) {
|
||||
auto tlvAirtiesEthStatsall = m_cmdu_tx.addClass<airties::tlvAirtiesEthernetStatsallcntr>();
|
||||
if (!tlvAirtiesEthStatsall) {
|
||||
LOG(ERROR) << "addClass wfa_map::tlvDeviceInfo failed";
|
||||
return false;
|
||||
}
|
||||
get_all_counters_info(tlvAirtiesEthStatsall);
|
||||
|
||||
} else {
|
||||
auto tlvAirtiesEthStats = m_cmdu_tx.addClass<airties::tlvAirtiesEthernetStats>();
|
||||
if (!tlvAirtiesEthStats) {
|
||||
LOG(ERROR) << "addClass wfa_map::tlvDeviceInfo failed";
|
||||
return false;
|
||||
}
|
||||
get_counters_info(tlvAirtiesEthStats);
|
||||
auto tlvAirtiesEthStats = m_cmdu_tx.addClass<airties::tlvAirtiesEthernetStats>();
|
||||
if (!tlvAirtiesEthStats) {
|
||||
LOG(ERROR) << "addClass wfa_map::tlvAirtiesEthStats failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
tlvAirtiesEthStats->vendor_oui() =
|
||||
(sVendorOUI(airties::tlvAirtiesMsgType::airtiesVendorOUI::OUI_AIRTIES));
|
||||
tlvAirtiesEthStats->tlv_id() = static_cast<int>(airties::eAirtiesTlVId::AIRTIES_ETHERNET_STATS);
|
||||
tlvAirtiesEthStats->supported_extra_stats() = set_supp_stats_val();
|
||||
|
||||
auto lan_ifaces = beerocks::net::network_utils::linux_get_lan_interfaces();
|
||||
for (auto &lan_iface : lan_ifaces) {
|
||||
auto port_list = tlvAirtiesEthStats->create_port_list();
|
||||
|
||||
port_list->port_id() = airties::tlvf_airties_utils::assign_unique_port_id(lan_iface);
|
||||
|
||||
beerocks::net::network_utils::sNetDevStats netDevStats = {0};
|
||||
if (!beerocks::net::network_utils::linux_get_net_dev_stats(lan_iface, netDevStats)) {
|
||||
LOG(WARNING) << "Failed to get net dev stats for " << lan_iface;
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* As per the requirement, only byte counters need
|
||||
* to be be converted to KiloBytes.
|
||||
*
|
||||
* The commented out items below correspond to counters that we currently
|
||||
* cannot obtain from the platform (e.g. multicast/broadcast bytes).
|
||||
* These fields are optional, and we keep them commented out for now.
|
||||
* If we later find a reliable source for these counters under /sys or
|
||||
* elsewhere, they can simply be enabled. The insertion order must remain
|
||||
* unchanged.
|
||||
*/
|
||||
insert_ethernet_stats_item(port_list, convertBytes_to_Kb(netDevStats.tx_bytes));
|
||||
insert_ethernet_stats_item(port_list, convertBytes_to_Kb(netDevStats.rx_bytes));
|
||||
insert_ethernet_stats_item(port_list, netDevStats.tx_packets);
|
||||
insert_ethernet_stats_item(port_list, netDevStats.rx_packets);
|
||||
insert_ethernet_stats_item(port_list, netDevStats.tx_errs);
|
||||
insert_ethernet_stats_item(port_list, netDevStats.rx_errs);
|
||||
//insert_ethernet_stats_item(port_list, multicast_bytes_sent);
|
||||
//insert_ethernet_stats_item(port_list, multicast_bytes_received);
|
||||
//insert_ethernet_stats_item(port_list, multicast_packets_sent);
|
||||
insert_ethernet_stats_item(port_list, netDevStats.rx_multicast);
|
||||
//insert_ethernet_stats_item(port_list, broadcast_bytes_sent);
|
||||
//insert_ethernet_stats_item(port_list, broadcast_bytes_received);
|
||||
//insert_ethernet_stats_item(port_list, broadcast_packets_sent);
|
||||
//insert_ethernet_stats_item(port_list, broadcast_packets_received);
|
||||
|
||||
tlvAirtiesEthStats->add_port_list(port_list);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -813,7 +544,6 @@ void update_client_details(std::shared_ptr<airties::tlvAirtiesDeviceInfo> &tlvDe
|
||||
bool tlvf_airties_utils::add_airties_deviceinfo_tlv(ieee1905_1::CmduMessageTx &m_cmdu_tx)
|
||||
{
|
||||
uint32_t randomBootid;
|
||||
auto db = beerocks::AgentDB::get();
|
||||
|
||||
srand((unsigned)time(NULL));
|
||||
randomBootid = rand();
|
||||
@@ -830,7 +560,12 @@ bool tlvf_airties_utils::add_airties_deviceinfo_tlv(ieee1905_1::CmduMessageTx &m
|
||||
|
||||
update_client_details(tlvAirtiesDeviceInfo);
|
||||
|
||||
if (db->device_conf.local_gw) { //it's a controller
|
||||
bool local_gw = false;
|
||||
{
|
||||
local_gw = beerocks::AgentDB::get()->device_conf.local_gw;
|
||||
}
|
||||
|
||||
if (local_gw) { //it's a controller
|
||||
tlvAirtiesDeviceInfo->flags1().gateway_product_class = TLV_BIT_ENABLE;
|
||||
tlvAirtiesDeviceInfo->flags2().device_role_indication = TLV_BIT_ENABLE;
|
||||
} else {
|
||||
@@ -983,10 +718,11 @@ bool devicemetrics_get_radio_info(std::shared_ptr<airties::tlvAirtiesDeviceMetri
|
||||
std::string stats_string = "Stats.";
|
||||
std::string rad_details_path;
|
||||
|
||||
auto db = AgentDB::get();
|
||||
int num_of_radios = 0;
|
||||
{
|
||||
num_of_radios = AgentDB::get()->get_radios_list().size();
|
||||
}
|
||||
|
||||
num_of_radios = db->get_radios_list().size();
|
||||
LOG(INFO) << "Device Metrics TLV: Number of radios " << num_of_radios;
|
||||
|
||||
for (int radio_index = 1; radio_index <= num_of_radios; radio_index++) {
|
||||
@@ -1190,44 +926,20 @@ bool tlvf_airties_utils::add_radio_capability(ieee1905_1::CmduMessageTx &cmdu_tx
|
||||
|
||||
uint8_t tlvf_airties_utils::assign_unique_port_id(const std::string &interface_name)
|
||||
{
|
||||
static std::mutex port_id_mutex;
|
||||
std::lock_guard<std::mutex> lock(port_id_mutex);
|
||||
static std::mutex mtx;
|
||||
std::lock_guard<std::mutex> lock(mtx);
|
||||
|
||||
static std::map<std::string, uint8_t> assigned_ids;
|
||||
static std::set<uint8_t> used_ids;
|
||||
static std::map<std::string, uint8_t> iface_to_id;
|
||||
|
||||
// Return immediately if already assigned
|
||||
auto it = assigned_ids.find(interface_name);
|
||||
if (it != assigned_ids.end()) {
|
||||
// If already assigned, return existing ID
|
||||
auto it = iface_to_id.find(interface_name);
|
||||
if (it != iface_to_id.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
// Extract trailing digit if present
|
||||
int i = interface_name.size() - 1;
|
||||
while (i >= 0 && isdigit(interface_name[i])) {
|
||||
--i;
|
||||
}
|
||||
// Assign next sequential ID starting from 1
|
||||
uint8_t new_id = static_cast<uint8_t>(iface_to_id.size() + 1);
|
||||
iface_to_id[interface_name] = new_id;
|
||||
|
||||
uint8_t suffix = 0;
|
||||
if (i != (int)interface_name.size() - 1) {
|
||||
std::string number_part = interface_name.substr(i + 1);
|
||||
suffix = static_cast<uint8_t>(std::stoi(number_part));
|
||||
}
|
||||
|
||||
// Assign suffix if valid and unused
|
||||
if (used_ids.find(suffix) == used_ids.end()) {
|
||||
assigned_ids[interface_name] = suffix;
|
||||
used_ids.insert(suffix);
|
||||
return suffix;
|
||||
}
|
||||
|
||||
// Assign next available number
|
||||
uint8_t next_id = 1;
|
||||
while (used_ids.find(next_id) != used_ids.end()) {
|
||||
++next_id;
|
||||
}
|
||||
|
||||
assigned_ids[interface_name] = next_id;
|
||||
used_ids.insert(next_id);
|
||||
return next_id;
|
||||
return new_id;
|
||||
}
|
||||
|
||||
@@ -28,8 +28,7 @@
|
||||
namespace airties {
|
||||
|
||||
class cPortList;
|
||||
class tlvAirtiesEthernetStatsallcntr;
|
||||
class cPortList_ext;
|
||||
class cStatsItem;
|
||||
|
||||
class tlvAirtiesEthernetStats : public BaseClass
|
||||
{
|
||||
@@ -74,26 +73,10 @@ class cPortList : public BaseClass
|
||||
~cPortList();
|
||||
|
||||
uint8_t& port_id();
|
||||
uint8_t* bytes_sent(size_t idx = 0);
|
||||
bool set_bytes_sent(const void* buffer, size_t size);
|
||||
uint8_t* bytes_recvd(size_t idx = 0);
|
||||
bool set_bytes_recvd(const void* buffer, size_t size);
|
||||
uint8_t* packets_sent(size_t idx = 0);
|
||||
bool set_packets_sent(const void* buffer, size_t size);
|
||||
uint8_t* packets_recvd(size_t idx = 0);
|
||||
bool set_packets_recvd(const void* buffer, size_t size);
|
||||
uint8_t* tx_pkt_errors(size_t idx = 0);
|
||||
bool set_tx_pkt_errors(const void* buffer, size_t size);
|
||||
uint8_t* rx_pkt_errors(size_t idx = 0);
|
||||
bool set_rx_pkt_errors(const void* buffer, size_t size);
|
||||
uint8_t* bcast_pkts_sent(size_t idx = 0);
|
||||
bool set_bcast_pkts_sent(const void* buffer, size_t size);
|
||||
uint8_t* bcast_pkts_recvd(size_t idx = 0);
|
||||
bool set_bcast_pkts_recvd(const void* buffer, size_t size);
|
||||
uint8_t* mcast_pkts_sent(size_t idx = 0);
|
||||
bool set_mcast_pkts_sent(const void* buffer, size_t size);
|
||||
uint8_t* mcast_pkts_recvd(size_t idx = 0);
|
||||
bool set_mcast_pkts_recvd(const void* buffer, size_t size);
|
||||
size_t statsItem_length();
|
||||
std::tuple<bool, cStatsItem&> statsItem(size_t idx);
|
||||
std::shared_ptr<cStatsItem> create_statsItem();
|
||||
bool add_statsItem(std::shared_ptr<cStatsItem> ptr);
|
||||
void class_swap() override;
|
||||
bool finalize() override;
|
||||
static size_t get_initial_size();
|
||||
@@ -101,136 +84,31 @@ class cPortList : public BaseClass
|
||||
private:
|
||||
bool init();
|
||||
uint8_t* m_port_id = nullptr;
|
||||
uint8_t* m_bytes_sent = nullptr;
|
||||
size_t m_bytes_sent_idx__ = 0;
|
||||
int m_lock_order_counter__ = 0;
|
||||
uint8_t* m_bytes_recvd = nullptr;
|
||||
size_t m_bytes_recvd_idx__ = 0;
|
||||
uint8_t* m_packets_sent = nullptr;
|
||||
size_t m_packets_sent_idx__ = 0;
|
||||
uint8_t* m_packets_recvd = nullptr;
|
||||
size_t m_packets_recvd_idx__ = 0;
|
||||
uint8_t* m_tx_pkt_errors = nullptr;
|
||||
size_t m_tx_pkt_errors_idx__ = 0;
|
||||
uint8_t* m_rx_pkt_errors = nullptr;
|
||||
size_t m_rx_pkt_errors_idx__ = 0;
|
||||
uint8_t* m_bcast_pkts_sent = nullptr;
|
||||
size_t m_bcast_pkts_sent_idx__ = 0;
|
||||
uint8_t* m_bcast_pkts_recvd = nullptr;
|
||||
size_t m_bcast_pkts_recvd_idx__ = 0;
|
||||
uint8_t* m_mcast_pkts_sent = nullptr;
|
||||
size_t m_mcast_pkts_sent_idx__ = 0;
|
||||
uint8_t* m_mcast_pkts_recvd = nullptr;
|
||||
size_t m_mcast_pkts_recvd_idx__ = 0;
|
||||
};
|
||||
|
||||
class tlvAirtiesEthernetStatsallcntr : public BaseClass
|
||||
{
|
||||
public:
|
||||
tlvAirtiesEthernetStatsallcntr(uint8_t* buff, size_t buff_len, bool parse = false);
|
||||
explicit tlvAirtiesEthernetStatsallcntr(std::shared_ptr<BaseClass> base, bool parse = false);
|
||||
~tlvAirtiesEthernetStatsallcntr();
|
||||
|
||||
const eAirtiesTlvTypeMap& type();
|
||||
const uint16_t& length();
|
||||
sVendorOUI& vendor_oui();
|
||||
uint16_t& tlv_id();
|
||||
uint16_t& supported_extra_stats();
|
||||
uint8_t& num_of_ports();
|
||||
std::tuple<bool, cPortList_ext&> port_list(size_t idx);
|
||||
std::shared_ptr<cPortList_ext> create_port_list();
|
||||
bool add_port_list(std::shared_ptr<cPortList_ext> ptr);
|
||||
void class_swap() override;
|
||||
bool finalize() override;
|
||||
static size_t get_initial_size();
|
||||
|
||||
private:
|
||||
bool init();
|
||||
eAirtiesTlvTypeMap* m_type = nullptr;
|
||||
uint16_t* m_length = nullptr;
|
||||
sVendorOUI* m_vendor_oui = nullptr;
|
||||
uint16_t* m_tlv_id = nullptr;
|
||||
uint16_t* m_supported_extra_stats = nullptr;
|
||||
uint8_t* m_num_of_ports = nullptr;
|
||||
cPortList_ext* m_port_list = nullptr;
|
||||
size_t m_port_list_idx__ = 0;
|
||||
std::vector<std::shared_ptr<cPortList_ext>> m_port_list_vector;
|
||||
cStatsItem* m_statsItem = nullptr;
|
||||
size_t m_statsItem_idx__ = 0;
|
||||
std::vector<std::shared_ptr<cStatsItem>> m_statsItem_vector;
|
||||
bool m_lock_allocation__ = false;
|
||||
int m_lock_order_counter__ = 0;
|
||||
};
|
||||
|
||||
class cPortList_ext : public BaseClass
|
||||
class cStatsItem : public BaseClass
|
||||
{
|
||||
public:
|
||||
cPortList_ext(uint8_t* buff, size_t buff_len, bool parse = false);
|
||||
explicit cPortList_ext(std::shared_ptr<BaseClass> base, bool parse = false);
|
||||
~cPortList_ext();
|
||||
cStatsItem(uint8_t* buff, size_t buff_len, bool parse = false);
|
||||
explicit cStatsItem(std::shared_ptr<BaseClass> base, bool parse = false);
|
||||
~cStatsItem();
|
||||
|
||||
uint8_t& port_id();
|
||||
uint8_t* bytes_sent(size_t idx = 0);
|
||||
bool set_bytes_sent(const void* buffer, size_t size);
|
||||
uint8_t* bytes_recvd(size_t idx = 0);
|
||||
bool set_bytes_recvd(const void* buffer, size_t size);
|
||||
uint8_t* packets_sent(size_t idx = 0);
|
||||
bool set_packets_sent(const void* buffer, size_t size);
|
||||
uint8_t* packets_recvd(size_t idx = 0);
|
||||
bool set_packets_recvd(const void* buffer, size_t size);
|
||||
uint8_t* tx_pkt_errors(size_t idx = 0);
|
||||
bool set_tx_pkt_errors(const void* buffer, size_t size);
|
||||
uint8_t* rx_pkt_errors(size_t idx = 0);
|
||||
bool set_rx_pkt_errors(const void* buffer, size_t size);
|
||||
uint8_t* bcast_bytes_sent(size_t idx = 0);
|
||||
bool set_bcast_bytes_sent(const void* buffer, size_t size);
|
||||
uint8_t* bcast_bytes_recvd(size_t idx = 0);
|
||||
bool set_bcast_bytes_recvd(const void* buffer, size_t size);
|
||||
uint8_t* bcast_pkts_sent(size_t idx = 0);
|
||||
bool set_bcast_pkts_sent(const void* buffer, size_t size);
|
||||
uint8_t* bcast_pkts_recvd(size_t idx = 0);
|
||||
bool set_bcast_pkts_recvd(const void* buffer, size_t size);
|
||||
uint8_t* mcast_bytes_sent(size_t idx = 0);
|
||||
bool set_mcast_bytes_sent(const void* buffer, size_t size);
|
||||
uint8_t* mcast_bytes_recvd(size_t idx = 0);
|
||||
bool set_mcast_bytes_recvd(const void* buffer, size_t size);
|
||||
uint8_t* mcast_pkts_sent(size_t idx = 0);
|
||||
bool set_mcast_pkts_sent(const void* buffer, size_t size);
|
||||
uint8_t* mcast_pkts_recvd(size_t idx = 0);
|
||||
bool set_mcast_pkts_recvd(const void* buffer, size_t size);
|
||||
uint8_t* item(size_t idx = 0);
|
||||
bool set_item(const void* buffer, size_t size);
|
||||
void class_swap() override;
|
||||
bool finalize() override;
|
||||
static size_t get_initial_size();
|
||||
|
||||
private:
|
||||
bool init();
|
||||
uint8_t* m_port_id = nullptr;
|
||||
uint8_t* m_bytes_sent = nullptr;
|
||||
size_t m_bytes_sent_idx__ = 0;
|
||||
uint8_t* m_item = nullptr;
|
||||
size_t m_item_idx__ = 0;
|
||||
int m_lock_order_counter__ = 0;
|
||||
uint8_t* m_bytes_recvd = nullptr;
|
||||
size_t m_bytes_recvd_idx__ = 0;
|
||||
uint8_t* m_packets_sent = nullptr;
|
||||
size_t m_packets_sent_idx__ = 0;
|
||||
uint8_t* m_packets_recvd = nullptr;
|
||||
size_t m_packets_recvd_idx__ = 0;
|
||||
uint8_t* m_tx_pkt_errors = nullptr;
|
||||
size_t m_tx_pkt_errors_idx__ = 0;
|
||||
uint8_t* m_rx_pkt_errors = nullptr;
|
||||
size_t m_rx_pkt_errors_idx__ = 0;
|
||||
uint8_t* m_bcast_bytes_sent = nullptr;
|
||||
size_t m_bcast_bytes_sent_idx__ = 0;
|
||||
uint8_t* m_bcast_bytes_recvd = nullptr;
|
||||
size_t m_bcast_bytes_recvd_idx__ = 0;
|
||||
uint8_t* m_bcast_pkts_sent = nullptr;
|
||||
size_t m_bcast_pkts_sent_idx__ = 0;
|
||||
uint8_t* m_bcast_pkts_recvd = nullptr;
|
||||
size_t m_bcast_pkts_recvd_idx__ = 0;
|
||||
uint8_t* m_mcast_bytes_sent = nullptr;
|
||||
size_t m_mcast_bytes_sent_idx__ = 0;
|
||||
uint8_t* m_mcast_bytes_recvd = nullptr;
|
||||
size_t m_mcast_bytes_recvd_idx__ = 0;
|
||||
uint8_t* m_mcast_pkts_sent = nullptr;
|
||||
size_t m_mcast_pkts_sent_idx__ = 0;
|
||||
uint8_t* m_mcast_pkts_recvd = nullptr;
|
||||
size_t m_mcast_pkts_recvd_idx__ = 0;
|
||||
};
|
||||
|
||||
}; // close namespace: airties
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -23,110 +23,21 @@ tlvAirtiesEthernetStats:
|
||||
_type: uint16_t
|
||||
num_of_ports:
|
||||
_type: uint8_t
|
||||
_length_var: True
|
||||
port_list:
|
||||
_length_var: True
|
||||
port_list:
|
||||
_type: cPortList
|
||||
_length: [ num_of_ports ]
|
||||
_length: [ num_of_ports ]
|
||||
|
||||
cPortList:
|
||||
cPortList:
|
||||
_type: class
|
||||
port_id:
|
||||
_type: uint8_t
|
||||
bytes_sent:
|
||||
_type: uint8_t
|
||||
_length: [6]
|
||||
bytes_recvd:
|
||||
_type: uint8_t
|
||||
_length: [6]
|
||||
packets_sent:
|
||||
_type: uint8_t
|
||||
_length: [6]
|
||||
packets_recvd:
|
||||
_type: uint8_t
|
||||
_length: [6]
|
||||
tx_pkt_errors:
|
||||
_type: uint8_t
|
||||
_length: [6]
|
||||
rx_pkt_errors:
|
||||
_type: uint8_t
|
||||
_length: [6]
|
||||
bcast_pkts_sent:
|
||||
_type: uint8_t
|
||||
_length: [6]
|
||||
bcast_pkts_recvd:
|
||||
_type: uint8_t
|
||||
_length: [6]
|
||||
mcast_pkts_sent:
|
||||
_type: uint8_t
|
||||
_length: [6]
|
||||
mcast_pkts_recvd:
|
||||
_type: uint8_t
|
||||
_length: [6]
|
||||
statsItem:
|
||||
_type: cStatsItem
|
||||
_length: []
|
||||
|
||||
|
||||
tlvAirtiesEthernetStatsallcntr:
|
||||
cStatsItem:
|
||||
_type: class
|
||||
_is_tlv_class: True
|
||||
type:
|
||||
_type: eAirtiesTlvTypeMap
|
||||
_value_const: TLV_VENDOR_SPECIFIC
|
||||
length: uint16_t
|
||||
vendor_oui:
|
||||
_type: sVendorOUI
|
||||
tlv_id:
|
||||
_type: uint16_t
|
||||
supported_extra_stats:
|
||||
_type: uint16_t
|
||||
num_of_ports:
|
||||
_type: uint8_t
|
||||
_length_var: True
|
||||
port_list:
|
||||
_type: cPortList_ext
|
||||
_length: [ num_of_ports ]
|
||||
|
||||
cPortList_ext:
|
||||
_type: class
|
||||
port_id:
|
||||
_type: uint8_t
|
||||
bytes_sent:
|
||||
_type: uint8_t
|
||||
_length: [6]
|
||||
bytes_recvd:
|
||||
_type: uint8_t
|
||||
_length: [6]
|
||||
packets_sent:
|
||||
_type: uint8_t
|
||||
_length: [6]
|
||||
packets_recvd:
|
||||
_type: uint8_t
|
||||
_length: [6]
|
||||
tx_pkt_errors:
|
||||
_type: uint8_t
|
||||
_length: [6]
|
||||
rx_pkt_errors:
|
||||
_type: uint8_t
|
||||
_length: [6]
|
||||
bcast_bytes_sent:
|
||||
_type: uint8_t
|
||||
_length: [6]
|
||||
bcast_bytes_recvd:
|
||||
_type: uint8_t
|
||||
_length: [6]
|
||||
bcast_pkts_sent:
|
||||
_type: uint8_t
|
||||
_length: [6]
|
||||
bcast_pkts_recvd:
|
||||
_type: uint8_t
|
||||
_length: [6]
|
||||
mcast_bytes_sent:
|
||||
_type: uint8_t
|
||||
_length: [6]
|
||||
mcast_bytes_recvd:
|
||||
_type: uint8_t
|
||||
_length: [6]
|
||||
mcast_pkts_sent:
|
||||
_type: uint8_t
|
||||
_length: [6]
|
||||
mcast_pkts_recvd:
|
||||
item:
|
||||
_type: uint8_t
|
||||
_length: [6]
|
||||
|
||||
Reference in New Issue
Block a user