Tomáš Pecka | f10b930 | 2021-02-23 19:02:02 +0100 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2021 CESNET, https://photonics.cesnet.cz/ |
| 3 | * |
| 4 | * Written by Tomáš Pecka <tomas.pecka@cesnet.cz> |
| 5 | * |
| 6 | */ |
| 7 | |
Tomáš Pecka | 0972938 | 2021-03-08 19:36:50 +0100 | [diff] [blame] | 8 | #include <arpa/inet.h> |
Tomáš Pecka | f10b930 | 2021-02-23 19:02:02 +0100 | [diff] [blame] | 9 | #include <linux/if_arp.h> |
| 10 | #include <linux/netdevice.h> |
| 11 | #include "IETFInterfaces.h" |
| 12 | #include "Rtnetlink.h" |
| 13 | #include "utils/log.h" |
| 14 | #include "utils/sysrepo.h" |
| 15 | |
| 16 | using namespace std::string_literals; |
| 17 | |
| 18 | namespace { |
| 19 | |
Tomáš Pecka | b29f36b | 2021-03-31 20:02:06 +0200 | [diff] [blame] | 20 | /** @brief Computes the length of the const C-string (array of const char) *including* the terminating zero |
| 21 | * |
| 22 | * Credits: https://dbj.org/cpp-zero-time-strlen-and-strnlen/ |
| 23 | */ |
| 24 | template <size_t N> |
| 25 | inline constexpr size_t arrlen(const char (&)[N]) noexcept |
| 26 | { |
| 27 | return N; |
| 28 | } |
| 29 | |
Tomáš Pecka | f10b930 | 2021-02-23 19:02:02 +0100 | [diff] [blame] | 30 | const auto CZECHLIGHT_NETWORK_MODULE_NAME = "czechlight-network"s; |
| 31 | const auto IETF_IP_MODULE_NAME = "ietf-ip"s; |
| 32 | const auto IETF_INTERFACES_MODULE_NAME = "ietf-interfaces"s; |
Tomáš Pecka | 3611c0e | 2021-04-14 09:02:14 +0200 | [diff] [blame] | 33 | const auto IETF_ROUTING_MODULE_NAME = "ietf-routing"s; |
| 34 | const auto IETF_IPV4_UNICAST_ROUTING_MODULE_NAME = "ietf-ipv4-unicast-routing"; |
| 35 | const auto IETF_IPV6_UNICAST_ROUTING_MODULE_NAME = "ietf-ipv6-unicast-routing"; |
Tomáš Pecka | f10b930 | 2021-02-23 19:02:02 +0100 | [diff] [blame] | 36 | const auto IETF_INTERFACES = "/"s + IETF_INTERFACES_MODULE_NAME + ":interfaces"s; |
| 37 | |
| 38 | const auto PHYS_ADDR_BUF_SIZE = 6 * 2 /* 2 chars per 6 bytes in the address */ + 5 /* delimiters (':') between bytes */ + 1 /* \0 */; |
Tomáš Pecka | b29f36b | 2021-03-31 20:02:06 +0200 | [diff] [blame] | 39 | const auto IPV6ADDRSTRLEN_WITH_PREFIX = INET6_ADDRSTRLEN + 1 + 3 /* plus slash and max three-digits prefix */; |
Tomáš Pecka | f10b930 | 2021-02-23 19:02:02 +0100 | [diff] [blame] | 40 | |
| 41 | std::string operStatusToString(uint8_t operStatus, velia::Log log) |
| 42 | { |
| 43 | // unfortunately we can't use libnl's rtnl_link_operstate2str, because it creates different strings than the YANG model expects |
| 44 | switch (operStatus) { |
| 45 | case IF_OPER_UP: |
| 46 | return "up"; |
| 47 | case IF_OPER_DOWN: |
| 48 | return "down"; |
| 49 | case IF_OPER_TESTING: |
| 50 | return "testing"; |
| 51 | case IF_OPER_DORMANT: |
| 52 | return "dormant"; |
| 53 | case IF_OPER_NOTPRESENT: |
| 54 | return "not-present"; |
| 55 | case IF_OPER_LOWERLAYERDOWN: |
| 56 | return "lower-layer-down"; |
| 57 | case IF_OPER_UNKNOWN: |
| 58 | return "unknown"; |
| 59 | default: |
| 60 | log->warn("Encountered unknown operational status {}, using 'unknown'", operStatus); |
| 61 | return "unknown"; |
| 62 | } |
| 63 | } |
| 64 | |
| 65 | std::string arpTypeToString(unsigned int arptype, velia::Log log) |
| 66 | { |
| 67 | switch (arptype) { |
| 68 | case ARPHRD_ETHER: |
| 69 | return "iana-if-type:ethernetCsmacd"; |
| 70 | case ARPHRD_LOOPBACK: |
| 71 | return "iana-if-type:softwareLoopback"; |
| 72 | case ARPHRD_SIT: |
| 73 | return "iana-if-type:sixToFour"; |
| 74 | default: |
| 75 | log->warn("Encountered unknown interface type {}, using 'iana-if-type:other'", arptype); |
| 76 | return "iana-if-type:other"; |
| 77 | } |
| 78 | } |
| 79 | |
| 80 | std::string nlActionToString(int action) |
| 81 | { |
| 82 | switch (action) { |
| 83 | case NL_ACT_NEW: |
| 84 | return "NEW"; |
| 85 | case NL_ACT_DEL: |
| 86 | return "DEL"; |
| 87 | case NL_ACT_CHANGE: |
| 88 | return "CHANGE"; |
| 89 | case NL_ACT_UNSPEC: |
| 90 | return "UNSPEC"; |
| 91 | case NL_ACT_GET: |
| 92 | return "GET"; |
| 93 | case NL_ACT_SET: |
| 94 | return "SET"; |
| 95 | default: |
| 96 | return "<unknown action>"; |
| 97 | } |
| 98 | } |
| 99 | |
Tomáš Pecka | 0972938 | 2021-03-08 19:36:50 +0100 | [diff] [blame] | 100 | std::string binaddrToString(void* binaddr, int addrFamily) |
| 101 | { |
| 102 | // any IPv4 address fits into a buffer allocated for an IPv6 address |
| 103 | static_assert(INET6_ADDRSTRLEN >= INET_ADDRSTRLEN); |
| 104 | std::array<char, INET6_ADDRSTRLEN> buf; |
| 105 | |
| 106 | if (const char* res = inet_ntop(addrFamily, binaddr, buf.data(), buf.size()); res != nullptr) { |
| 107 | return res; |
| 108 | } else { |
| 109 | throw std::system_error {errno, std::generic_category(), "inet_ntop"}; |
| 110 | } |
| 111 | } |
| 112 | |
| 113 | std::string getIPVersion(int addrFamily) |
| 114 | { |
| 115 | switch (addrFamily) { |
| 116 | case AF_INET: |
| 117 | return "ipv4"; |
| 118 | case AF_INET6: |
| 119 | return "ipv6"; |
| 120 | default: |
| 121 | throw std::runtime_error("Unexpected address family " + std::to_string(addrFamily)); |
| 122 | } |
| 123 | } |
| 124 | |
Tomáš Pecka | 3663aae | 2021-03-10 13:46:31 +0100 | [diff] [blame] | 125 | /** @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). */ |
| 126 | std::map<std::string, std::string> collectNeighboursIP(std::shared_ptr<velia::system::Rtnetlink> rtnetlink, int requestedAddrFamily, velia::Log log) |
| 127 | { |
| 128 | std::map<std::string, std::string> values; |
| 129 | |
| 130 | for (const auto& [neigh, link] : rtnetlink->getNeighbours()) { |
| 131 | if (rtnl_neigh_get_state(neigh.get()) == NUD_NOARP) { |
| 132 | continue; |
| 133 | } |
| 134 | |
| 135 | auto linkName = rtnl_link_get_name(link.get()); |
| 136 | |
| 137 | auto ipAddr = rtnl_neigh_get_dst(neigh.get()); |
| 138 | auto ipAddrFamily = nl_addr_get_family(ipAddr); |
| 139 | |
| 140 | if (ipAddrFamily != requestedAddrFamily) { |
| 141 | continue; |
| 142 | } |
| 143 | |
| 144 | auto ipAddress = binaddrToString(nl_addr_get_binary_addr(ipAddr), ipAddrFamily); |
| 145 | |
| 146 | auto llAddr = rtnl_neigh_get_lladdr(neigh.get()); |
| 147 | std::array<char, PHYS_ADDR_BUF_SIZE> llAddrBuf {}; |
| 148 | if (auto llAddress = nl_addr2str(llAddr, llAddrBuf.data(), llAddrBuf.size()); llAddress != "none"s) { |
| 149 | values[IETF_INTERFACES + "/interface[name='" + linkName + "']/ietf-ip:" + getIPVersion(ipAddrFamily) + "/neighbor[ip='" + ipAddress + "']/link-layer-address"] = llAddress; |
| 150 | } else { |
| 151 | log->warn("Neighbor '{}' on link '{}' returned link layer address 'none'", ipAddress, linkName); |
| 152 | } |
| 153 | } |
| 154 | |
| 155 | return values; |
| 156 | } |
Tomáš Pecka | f10b930 | 2021-02-23 19:02:02 +0100 | [diff] [blame] | 157 | } |
| 158 | |
| 159 | namespace velia::system { |
| 160 | |
| 161 | IETFInterfaces::IETFInterfaces(std::shared_ptr<::sysrepo::Session> srSess) |
| 162 | : m_srSession(std::move(srSess)) |
Tomáš Pecka | 70e5456 | 2021-03-10 12:39:03 +0100 | [diff] [blame] | 163 | , m_srSubscribe(std::make_shared<::sysrepo::Subscribe>(m_srSession)) |
Tomáš Pecka | f10b930 | 2021-02-23 19:02:02 +0100 | [diff] [blame] | 164 | , m_log(spdlog::get("system")) |
Tomáš Pecka | 0972938 | 2021-03-08 19:36:50 +0100 | [diff] [blame] | 165 | , m_rtnetlink(std::make_shared<Rtnetlink>( |
| 166 | [this](rtnl_link* link, int action) { onLinkUpdate(link, action); }, |
Tomáš Pecka | b29f36b | 2021-03-31 20:02:06 +0200 | [diff] [blame] | 167 | [this](rtnl_addr* addr, int action) { onAddrUpdate(addr, action); }, |
| 168 | [this](rtnl_route* addr, int action) { onRouteUpdate(addr, action); })) |
Tomáš Pecka | f10b930 | 2021-02-23 19:02:02 +0100 | [diff] [blame] | 169 | { |
| 170 | utils::ensureModuleImplemented(m_srSession, IETF_INTERFACES_MODULE_NAME, "2018-02-20"); |
| 171 | utils::ensureModuleImplemented(m_srSession, IETF_IP_MODULE_NAME, "2018-02-22"); |
Tomáš Pecka | 3611c0e | 2021-04-14 09:02:14 +0200 | [diff] [blame] | 172 | utils::ensureModuleImplemented(m_srSession, IETF_ROUTING_MODULE_NAME, "2018-03-13"); |
| 173 | utils::ensureModuleImplemented(m_srSession, IETF_IPV4_UNICAST_ROUTING_MODULE_NAME, "2018-03-13"); |
| 174 | utils::ensureModuleImplemented(m_srSession, IETF_IPV6_UNICAST_ROUTING_MODULE_NAME, "2018-03-13"); |
Tomáš Pecka | f10b930 | 2021-02-23 19:02:02 +0100 | [diff] [blame] | 175 | utils::ensureModuleImplemented(m_srSession, CZECHLIGHT_NETWORK_MODULE_NAME, "2021-02-22"); |
Tomáš Pecka | 3d3cf61 | 2021-03-31 19:51:31 +0200 | [diff] [blame] | 176 | |
| 177 | m_rtnetlink->invokeInitialCallbacks(); |
Tomáš Pecka | b29f36b | 2021-03-31 20:02:06 +0200 | [diff] [blame] | 178 | // TODO: Implement /ietf-routing:routing/interfaces and /ietf-routing:routing/router-id |
Tomáš Pecka | 70e5456 | 2021-03-10 12:39:03 +0100 | [diff] [blame] | 179 | |
| 180 | m_srSubscribe->oper_get_items_subscribe( |
| 181 | IETF_INTERFACES_MODULE_NAME.c_str(), [this](auto session, auto, auto, auto, auto, auto& parent) { |
| 182 | std::map<std::string, std::string> values; |
| 183 | for (const auto& link : m_rtnetlink->getLinks()) { |
| 184 | const auto yangPrefix = IETF_INTERFACES + "/interface[name='" + rtnl_link_get_name(link.get()) + "']/statistics"; |
| 185 | |
| 186 | values[yangPrefix + "/in-octets"] = std::to_string(rtnl_link_get_stat(link.get(), RTNL_LINK_RX_BYTES)); |
| 187 | values[yangPrefix + "/out-octets"] = std::to_string(rtnl_link_get_stat(link.get(), RTNL_LINK_TX_BYTES)); |
| 188 | values[yangPrefix + "/in-discards"] = std::to_string(rtnl_link_get_stat(link.get(), RTNL_LINK_RX_DROPPED)); |
| 189 | values[yangPrefix + "/out-discards"] = std::to_string(rtnl_link_get_stat(link.get(), RTNL_LINK_TX_DROPPED)); |
| 190 | values[yangPrefix + "/in-errors"] = std::to_string(rtnl_link_get_stat(link.get(), RTNL_LINK_RX_ERRORS)); |
| 191 | values[yangPrefix + "/out-errors"] = std::to_string(rtnl_link_get_stat(link.get(), RTNL_LINK_TX_ERRORS)); |
| 192 | } |
| 193 | |
| 194 | utils::valuesToYang(values, {}, session, parent); |
| 195 | return SR_ERR_OK; |
| 196 | }, |
| 197 | (IETF_INTERFACES + "/interface/statistics").c_str()); |
Tomáš Pecka | 3663aae | 2021-03-10 13:46:31 +0100 | [diff] [blame] | 198 | |
| 199 | m_srSubscribe->oper_get_items_subscribe( |
| 200 | IETF_INTERFACES_MODULE_NAME.c_str(), [this](auto session, auto, auto, auto, auto, auto& parent) { |
| 201 | utils::valuesToYang(collectNeighboursIP(m_rtnetlink, AF_INET, m_log), {}, session, parent); |
| 202 | return SR_ERR_OK; |
| 203 | }, |
| 204 | (IETF_INTERFACES + "/interface/ietf-ip:ipv4/neighbor").c_str()); |
| 205 | |
| 206 | m_srSubscribe->oper_get_items_subscribe( |
| 207 | IETF_INTERFACES_MODULE_NAME.c_str(), [this](auto session, auto, auto, auto, auto, auto& parent) { |
| 208 | utils::valuesToYang(collectNeighboursIP(m_rtnetlink, AF_INET6, m_log), {}, session, parent); |
| 209 | return SR_ERR_OK; |
| 210 | }, |
| 211 | (IETF_INTERFACES + "/interface/ietf-ip:ipv6/neighbor").c_str()); |
Tomáš Pecka | f10b930 | 2021-02-23 19:02:02 +0100 | [diff] [blame] | 212 | } |
| 213 | |
| 214 | void IETFInterfaces::onLinkUpdate(rtnl_link* link, int action) |
| 215 | { |
| 216 | char* name = rtnl_link_get_name(link); |
| 217 | m_log->trace("Netlink update on link '{}', action {}", name, nlActionToString(action)); |
| 218 | |
| 219 | if (action == NL_ACT_DEL) { |
Tomáš Pecka | 53f08ee | 2021-04-28 12:38:11 +0200 | [diff] [blame^] | 220 | utils::valuesPush(std::vector<utils::YANGPair>{}, {IETF_INTERFACES + "/interface[name='" + name + "']"}, m_srSession, SR_DS_OPERATIONAL); |
Tomáš Pecka | f10b930 | 2021-02-23 19:02:02 +0100 | [diff] [blame] | 221 | } else if (action == NL_ACT_CHANGE || action == NL_ACT_NEW) { |
| 222 | std::map<std::string, std::string> values; |
| 223 | std::vector<std::string> deletePaths; |
| 224 | |
Tomáš Pecka | db08413 | 2021-03-10 08:37:10 +0100 | [diff] [blame] | 225 | auto linkAddr = rtnl_link_get_addr(link); |
Tomáš Pecka | f10b930 | 2021-02-23 19:02:02 +0100 | [diff] [blame] | 226 | std::array<char, PHYS_ADDR_BUF_SIZE> buf; |
Tomáš Pecka | db08413 | 2021-03-10 08:37:10 +0100 | [diff] [blame] | 227 | if (auto physAddr = nl_addr2str(linkAddr, buf.data(), buf.size()); physAddr != "none"s && nl_addr_get_family(linkAddr) == AF_LLC) { // set physical address if the link has one |
Tomáš Pecka | f10b930 | 2021-02-23 19:02:02 +0100 | [diff] [blame] | 228 | values[IETF_INTERFACES + "/interface[name='" + name + "']/phys-address"] = physAddr; |
| 229 | } else { |
| 230 | // delete physical address from sysrepo if not provided by rtnetlink |
| 231 | // Note: During testing I have noticed that my wireless interface loses a physical address. There were several change callbacks invoked |
| 232 | // when simply bringing the interface down and up. In some of those, nl_addr2str returned "none". |
| 233 | deletePaths.push_back({IETF_INTERFACES + "/interface[name='" + name + "']/phys-address"}); |
| 234 | } |
| 235 | |
Tomáš Pecka | 019748f | 2021-04-13 21:34:49 +0200 | [diff] [blame] | 236 | values[IETF_INTERFACES + "/interface[name='" + name + "']/type"] = rtnl_link_get_family(link) == AF_BRIDGE ? "iana-if-type:bridge" : arpTypeToString(rtnl_link_get_arptype(link), m_log); |
Tomáš Pecka | f10b930 | 2021-02-23 19:02:02 +0100 | [diff] [blame] | 237 | values[IETF_INTERFACES + "/interface[name='" + name + "']/oper-status"] = operStatusToString(rtnl_link_get_operstate(link), m_log); |
| 238 | |
| 239 | utils::valuesPush(values, deletePaths, m_srSession, SR_DS_OPERATIONAL); |
| 240 | } else { |
| 241 | m_log->warn("Unhandled cache update action {} ({})", action, nlActionToString(action)); |
| 242 | } |
| 243 | } |
| 244 | |
Tomáš Pecka | 0972938 | 2021-03-08 19:36:50 +0100 | [diff] [blame] | 245 | void IETFInterfaces::onAddrUpdate(rtnl_addr* addr, int action) |
| 246 | { |
| 247 | std::unique_ptr<rtnl_link, std::function<void(rtnl_link*)>> link(rtnl_addr_get_link(addr), [](rtnl_link* obj) { nl_object_put(OBJ_CAST(obj)); }); |
| 248 | |
| 249 | auto linkName = rtnl_link_get_name(link.get()); |
| 250 | auto addrFamily = rtnl_addr_get_family(addr); |
| 251 | if (addrFamily != AF_INET && addrFamily != AF_INET6) { |
| 252 | return; |
| 253 | } |
| 254 | |
| 255 | m_log->trace("Netlink update on address of link '{}', action {}", linkName, nlActionToString(action)); |
| 256 | |
| 257 | auto nlAddr = rtnl_addr_get_local(addr); |
| 258 | std::string ipAddress = binaddrToString(nl_addr_get_binary_addr(nlAddr), addrFamily); // We don't use libnl's nl_addr2str because it appends a prefix length to the string (e.g. 192.168.0.1/24) |
| 259 | std::string ipVersion = getIPVersion(addrFamily); |
| 260 | |
| 261 | std::map<std::string, std::string> values; |
| 262 | std::vector<std::string> deletePaths; |
| 263 | const auto yangPrefix = IETF_INTERFACES + "/interface[name='" + linkName + "']/ietf-ip:" + ipVersion + "/address[ip='" + ipAddress + "']"; |
| 264 | |
| 265 | if (action == NL_ACT_DEL) { |
| 266 | deletePaths.push_back({yangPrefix}); |
| 267 | } else if (action == NL_ACT_CHANGE || action == NL_ACT_NEW) { |
| 268 | values[yangPrefix + "/prefix-length"] = std::to_string(rtnl_addr_get_prefixlen(addr)); |
| 269 | } else { |
| 270 | m_log->warn("Unhandled cache update action {} ({})", action, nlActionToString(action)); |
| 271 | } |
| 272 | |
| 273 | utils::valuesPush(values, deletePaths, m_srSession, SR_DS_OPERATIONAL); |
| 274 | } |
Tomáš Pecka | b29f36b | 2021-03-31 20:02:06 +0200 | [diff] [blame] | 275 | |
| 276 | void IETFInterfaces::onRouteUpdate(rtnl_route*, int) |
| 277 | { |
| 278 | /* NOTE: |
| 279 | * We don't know the position of the changed route in the list of routes |
| 280 | * Replace the whole subtree (and therefore fetch all routes to publish fresh data) |
| 281 | * Unfortunately, this function may be called several times during the "reconstruction" of the routing table. |
| 282 | */ |
| 283 | |
| 284 | std::map<std::string, std::string> values; |
| 285 | std::vector<std::string> deletePaths; |
| 286 | |
| 287 | auto routes = m_rtnetlink->getRoutes(); |
| 288 | auto links = m_rtnetlink->getLinks(); |
| 289 | |
| 290 | // ipv4 and ipv6 routes are in separate lists; keep a track of current index to the list so we correctly append the route to the end of the list |
| 291 | std::map<decltype(AF_INET), unsigned> routeIdx {{AF_INET, 1}, {AF_INET6, 1}}; |
| 292 | |
| 293 | for (const auto& route : routes) { |
| 294 | if (rtnl_route_get_table(route.get()) != RT_TABLE_MAIN) { |
| 295 | continue; |
| 296 | } |
| 297 | |
| 298 | if (rtnl_route_get_type(route.get()) != RTN_UNICAST) { |
| 299 | continue; |
| 300 | } |
| 301 | |
| 302 | auto family = rtnl_route_get_family(route.get()); |
| 303 | if (family != AF_INET && family != AF_INET6) { |
| 304 | continue; |
| 305 | } |
| 306 | |
| 307 | auto proto = rtnl_route_get_protocol(route.get()); |
| 308 | if (proto != RTPROT_KERNEL && proto != RTPROT_RA && proto != RTPROT_DHCP && proto != RTPROT_STATIC && proto != RTPROT_BOOT) { |
| 309 | std::array<char, arrlen("redirect")> buf; /* "redirect" is the longest value (libnl/lib/route/route_utils.c, init_proto_names) */ |
| 310 | m_log->warn("Unimplemented routing protocol {} '{}'", proto, rtnl_route_proto2str(proto, buf.data(), buf.size())); |
| 311 | continue; |
| 312 | } |
| 313 | |
| 314 | const auto ribName = family == AF_INET ? "ipv4-master"s : "ipv6-master"s; |
| 315 | const auto yangPrefix = "/ietf-routing:routing/ribs/rib[name='" + ribName + "']/routes/route["s + std::to_string(routeIdx[family]++) + "]/"; |
| 316 | const auto familyYangPrefix = family == AF_INET ? "ietf-ipv4-unicast-routing"s : "ietf-ipv6-unicast-routing"s; |
| 317 | |
| 318 | std::string destPrefix; |
| 319 | if (auto* addr = rtnl_route_get_dst(route.get()); addr != nullptr) { |
| 320 | if (nl_addr_iszero(addr)) { |
| 321 | destPrefix = family == AF_INET ? "0.0.0.0/0" : "::/0"; |
| 322 | } else { |
| 323 | std::array<char, IPV6ADDRSTRLEN_WITH_PREFIX> data; |
| 324 | destPrefix = nl_addr2str(addr, data.data(), data.size()); |
| 325 | |
| 326 | // append prefix len if nl_addr2str fails to do that (when prefix length is 32 in ipv4 or 128 in ipv6) |
| 327 | if (destPrefix.find_first_of('/') == std::string::npos) { |
| 328 | destPrefix += "/" + std::to_string(nl_addr_get_prefixlen(addr)); |
| 329 | } |
| 330 | } |
| 331 | } |
| 332 | |
| 333 | values[yangPrefix + familyYangPrefix + ":destination-prefix"] = destPrefix; |
| 334 | |
| 335 | auto scope = rtnl_route_get_scope(route.get()); |
| 336 | std::string protoStr; |
| 337 | switch (proto) { |
| 338 | case RTPROT_KERNEL: |
| 339 | protoStr = scope == RT_SCOPE_LINK ? "direct" : "static"; |
| 340 | break; |
| 341 | case RTPROT_STATIC: |
| 342 | case RTPROT_BOOT: |
| 343 | protoStr = "static"; |
| 344 | break; |
| 345 | case RTPROT_DHCP: |
| 346 | protoStr = "czechlight-network:dhcp"; |
| 347 | break; |
| 348 | case RTPROT_RA: |
| 349 | protoStr = "czechlight-network:ra"; |
| 350 | break; |
| 351 | default: |
| 352 | throw std::invalid_argument("Unexpected route protocol ("s + std::to_string(proto) + ")"); |
| 353 | } |
| 354 | |
| 355 | values[yangPrefix + "source-protocol"] = protoStr; |
| 356 | |
| 357 | const auto hops = rtnl_route_get_nnexthops(route.get()); |
| 358 | const bool multihop = hops > 1; |
| 359 | for (auto i = 0; i < hops; i++) { |
| 360 | rtnl_nexthop* nh = rtnl_route_nexthop_n(route.get(), i); |
| 361 | |
| 362 | if (nl_addr* addr = rtnl_route_nh_get_gateway(nh); addr) { |
| 363 | std::string yangKey; |
| 364 | if (!multihop) { |
| 365 | yangKey = yangPrefix + "next-hop/" + familyYangPrefix + ":next-hop-address"; |
| 366 | } else { |
| 367 | yangKey = yangPrefix + "next-hop/next-hop-list/next-hop[" + std::to_string(i + 1) + "]/" + familyYangPrefix + ":address"; |
| 368 | } |
| 369 | |
| 370 | std::array<char, IPV6ADDRSTRLEN_WITH_PREFIX> buf; |
| 371 | values[yangKey] = nl_addr2str(addr, buf.data(), buf.size()); |
| 372 | } |
| 373 | |
| 374 | auto if_index = rtnl_route_nh_get_ifindex(nh); |
| 375 | if (auto linkIt = std::find_if(links.begin(), links.end(), [if_index](const Rtnetlink::nlLink& link) { return rtnl_link_get_ifindex(link.get()) == if_index; }); linkIt != links.end()) { |
| 376 | if (char* ifname = rtnl_link_get_name(linkIt->get()); ifname) { |
| 377 | std::string yangKey; |
| 378 | if (!multihop) { |
| 379 | yangKey = yangPrefix + "next-hop/outgoing-interface"; |
| 380 | } else { |
| 381 | yangKey = yangPrefix + "next-hop/next-hop-list/next-hop[" + std::to_string(i + 1) + "]/outgoing-interface"; |
| 382 | } |
| 383 | |
| 384 | values[yangKey] = rtnl_link_get_name(linkIt->get()); |
| 385 | } |
| 386 | } |
| 387 | } |
| 388 | } |
| 389 | |
| 390 | utils::valuesPush(values, deletePaths, m_srSession, SR_DS_OPERATIONAL); |
| 391 | } |
Tomáš Pecka | f10b930 | 2021-02-23 19:02:02 +0100 | [diff] [blame] | 392 | } |