system: Implement ietf-ip neighbors
The YANG model ietf-ip [1] models mapping of IP addresses to link layer
addresses per interface.
We ask for the link layer neighbors via libnl.
[1] https://tools.ietf.org/html/rfc8344
Change-Id: I831bcdbb430c657ae9a08da9f0d5a999e302e3da
diff --git a/src/system/IETFInterfaces.cpp b/src/system/IETFInterfaces.cpp
index d82f5e5..75978d2 100644
--- a/src/system/IETFInterfaces.cpp
+++ b/src/system/IETFInterfaces.cpp
@@ -119,6 +119,38 @@
}
}
+/** @brief Returns YANG structure for ietf-ip:ipv(4|6)/neighbours. Set requestedAddrFamily to required ip version (AF_INET for ipv4 or AF_INET6 for ipv6). */
+std::map<std::string, std::string> collectNeighboursIP(std::shared_ptr<velia::system::Rtnetlink> rtnetlink, int requestedAddrFamily, velia::Log log)
+{
+ std::map<std::string, std::string> values;
+
+ for (const auto& [neigh, link] : rtnetlink->getNeighbours()) {
+ if (rtnl_neigh_get_state(neigh.get()) == NUD_NOARP) {
+ continue;
+ }
+
+ auto linkName = rtnl_link_get_name(link.get());
+
+ auto ipAddr = rtnl_neigh_get_dst(neigh.get());
+ auto ipAddrFamily = nl_addr_get_family(ipAddr);
+
+ if (ipAddrFamily != requestedAddrFamily) {
+ continue;
+ }
+
+ auto ipAddress = binaddrToString(nl_addr_get_binary_addr(ipAddr), ipAddrFamily);
+
+ auto llAddr = rtnl_neigh_get_lladdr(neigh.get());
+ std::array<char, PHYS_ADDR_BUF_SIZE> llAddrBuf {};
+ if (auto llAddress = nl_addr2str(llAddr, llAddrBuf.data(), llAddrBuf.size()); llAddress != "none"s) {
+ values[IETF_INTERFACES + "/interface[name='" + linkName + "']/ietf-ip:" + getIPVersion(ipAddrFamily) + "/neighbor[ip='" + ipAddress + "']/link-layer-address"] = llAddress;
+ } else {
+ log->warn("Neighbor '{}' on link '{}' returned link layer address 'none'", ipAddress, linkName);
+ }
+ }
+
+ return values;
+}
}
namespace velia::system {
@@ -157,6 +189,20 @@
return SR_ERR_OK;
},
(IETF_INTERFACES + "/interface/statistics").c_str());
+
+ m_srSubscribe->oper_get_items_subscribe(
+ IETF_INTERFACES_MODULE_NAME.c_str(), [this](auto session, auto, auto, auto, auto, auto& parent) {
+ utils::valuesToYang(collectNeighboursIP(m_rtnetlink, AF_INET, m_log), {}, session, parent);
+ return SR_ERR_OK;
+ },
+ (IETF_INTERFACES + "/interface/ietf-ip:ipv4/neighbor").c_str());
+
+ m_srSubscribe->oper_get_items_subscribe(
+ IETF_INTERFACES_MODULE_NAME.c_str(), [this](auto session, auto, auto, auto, auto, auto& parent) {
+ utils::valuesToYang(collectNeighboursIP(m_rtnetlink, AF_INET6, m_log), {}, session, parent);
+ return SR_ERR_OK;
+ },
+ (IETF_INTERFACES + "/interface/ietf-ip:ipv6/neighbor").c_str());
}
void IETFInterfaces::onLinkUpdate(rtnl_link* link, int action)
diff --git a/tests/sysrepo_system-ietfinterfaces.cpp b/tests/sysrepo_system-ietfinterfaces.cpp
index 766396f..a72bd62 100644
--- a/tests/sysrepo_system-ietfinterfaces.cpp
+++ b/tests/sysrepo_system-ietfinterfaces.cpp
@@ -48,4 +48,5 @@
{"/ietf-ip:ipv6/ietf-ipv6-unicast-routing:ipv6-router-advertisements", ""},
{"/ietf-ip:ipv6/ietf-ipv6-unicast-routing:ipv6-router-advertisements/prefix-list", ""},
});
+ // NOTE: There are no neighbours on loopback
}