1 #include "util.hpp" 2 3 #include "config_parser.hpp" 4 #include "types.hpp" 5 6 #include <arpa/inet.h> 7 #include <dirent.h> 8 #include <fmt/compile.h> 9 #include <fmt/format.h> 10 #include <net/if.h> 11 #include <sys/wait.h> 12 13 #include <algorithm> 14 #include <cctype> 15 #include <cstdlib> 16 #include <cstring> 17 #include <filesystem> 18 #include <fstream> 19 #include <list> 20 #ifdef SYNC_MAC_FROM_INVENTORY 21 #include <nlohmann/json.hpp> 22 #endif 23 #include <phosphor-logging/elog-errors.hpp> 24 #include <phosphor-logging/log.hpp> 25 #include <stdexcept> 26 #include <stdplus/raw.hpp> 27 #include <string> 28 #include <variant> 29 #include <xyz/openbmc_project/Common/error.hpp> 30 31 namespace phosphor 32 { 33 namespace network 34 { 35 36 using namespace phosphor::logging; 37 using namespace sdbusplus::xyz::openbmc_project::Common::Error; 38 namespace fs = std::filesystem; 39 40 namespace internal 41 { 42 43 void executeCommandinChildProcess(const char* path, char** args) 44 { 45 using namespace std::string_literals; 46 pid_t pid = fork(); 47 int status{}; 48 49 if (pid == 0) 50 { 51 execv(path, args); 52 auto error = errno; 53 // create the command from var args. 54 std::string command = path + " "s; 55 56 for (int i = 0; args[i]; i++) 57 { 58 command += args[i] + " "s; 59 } 60 61 log<level::ERR>("Couldn't exceute the command", 62 entry("ERRNO=%d", error), 63 entry("CMD=%s", command.c_str())); 64 elog<InternalFailure>(); 65 } 66 else if (pid < 0) 67 { 68 auto error = errno; 69 log<level::ERR>("Error occurred during fork", entry("ERRNO=%d", error)); 70 elog<InternalFailure>(); 71 } 72 else if (pid > 0) 73 { 74 while (waitpid(pid, &status, 0) == -1) 75 { 76 if (errno != EINTR) 77 { // Error other than EINTR 78 status = -1; 79 break; 80 } 81 } 82 83 if (status < 0) 84 { 85 std::string command = path + " "s; 86 for (int i = 0; args[i]; i++) 87 { 88 command += args[i] + " "s; 89 } 90 91 log<level::ERR>("Unable to execute the command", 92 entry("CMD=%s", command.c_str()), 93 entry("STATUS=%d", status)); 94 elog<InternalFailure>(); 95 } 96 } 97 } 98 99 /** @brief Get ignored interfaces from environment */ 100 std::string_view getIgnoredInterfacesEnv() 101 { 102 auto r = std::getenv("IGNORED_INTERFACES"); 103 if (r == nullptr) 104 { 105 return ""; 106 } 107 return r; 108 } 109 110 /** @brief Parse the comma separated interface names */ 111 std::set<std::string_view> parseInterfaces(std::string_view interfaces) 112 { 113 std::set<std::string_view> result; 114 while (true) 115 { 116 auto sep = interfaces.find(','); 117 auto interface = interfaces.substr(0, sep); 118 while (!interface.empty() && std::isspace(interface.front())) 119 { 120 interface.remove_prefix(1); 121 } 122 while (!interface.empty() && std::isspace(interface.back())) 123 { 124 interface.remove_suffix(1); 125 } 126 if (!interface.empty()) 127 { 128 result.insert(interface); 129 } 130 if (sep == interfaces.npos) 131 { 132 break; 133 } 134 interfaces = interfaces.substr(sep + 1); 135 } 136 return result; 137 } 138 139 /** @brief Get the ignored interfaces */ 140 const std::set<std::string_view>& getIgnoredInterfaces() 141 { 142 static auto ignoredInterfaces = parseInterfaces(getIgnoredInterfacesEnv()); 143 return ignoredInterfaces; 144 } 145 146 } // namespace internal 147 148 uint8_t toCidr(int addressFamily, const std::string& subnetMask) 149 { 150 uint32_t subnet[sizeof(in6_addr) / sizeof(uint32_t)]; 151 if (inet_pton(addressFamily, subnetMask.c_str(), &subnet) != 1) 152 { 153 log<level::ERR>("inet_pton failed:", 154 entry("SUBNETMASK=%s", subnetMask.c_str())); 155 return 0; 156 } 157 158 static_assert(sizeof(in6_addr) % sizeof(uint32_t) == 0); 159 static_assert(sizeof(in_addr) % sizeof(uint32_t) == 0); 160 auto i = (addressFamily == AF_INET ? sizeof(in_addr) : sizeof(in6_addr)) / 161 sizeof(uint32_t); 162 while (i > 0) 163 { 164 if (subnet[--i] != 0) 165 { 166 auto v = be32toh(subnet[i]); 167 static_assert(sizeof(unsigned) == sizeof(uint32_t)); 168 auto trailing = __builtin_ctz(v); 169 auto ret = (i + 1) * 32 - trailing; 170 bool valid = ~v == 0 || 32 == trailing + __builtin_clz(~v); 171 while (i > 0 && (valid = (~subnet[--i] == 0) && valid)) 172 ; 173 if (!valid) 174 { 175 log<level::ERR>("Invalid netmask", 176 entry("SUBNETMASK=%s", subnetMask.c_str())); 177 return 0; 178 } 179 return ret; 180 } 181 } 182 return 0; 183 } 184 185 std::string toMask(int addressFamily, uint8_t prefix) 186 { 187 if (addressFamily == AF_INET6) 188 { 189 // TODO:- conversion for v6 190 return ""; 191 } 192 193 if (prefix < 1 || prefix > 30) 194 { 195 log<level::ERR>("Invalid Prefix", entry("PREFIX=%d", prefix)); 196 return ""; 197 } 198 /* Create the netmask from the number of bits */ 199 unsigned long mask = 0; 200 for (auto i = 0; i < prefix; i++) 201 { 202 mask |= 1 << (31 - i); 203 } 204 struct in_addr netmask; 205 netmask.s_addr = htonl(mask); 206 return inet_ntoa(netmask); 207 } 208 209 InAddrAny addrFromBuf(int addressFamily, std::string_view buf) 210 { 211 if (addressFamily == AF_INET) 212 { 213 struct in_addr ret; 214 if (buf.size() != sizeof(ret)) 215 { 216 throw std::runtime_error("Buf not in_addr sized"); 217 } 218 memcpy(&ret, buf.data(), sizeof(ret)); 219 return ret; 220 } 221 else if (addressFamily == AF_INET6) 222 { 223 struct in6_addr ret; 224 if (buf.size() != sizeof(ret)) 225 { 226 throw std::runtime_error("Buf not in6_addr sized"); 227 } 228 memcpy(&ret, buf.data(), sizeof(ret)); 229 return ret; 230 } 231 232 throw std::runtime_error("Unsupported address family"); 233 } 234 235 std::string toString(const struct in_addr& addr) 236 { 237 std::string ip(INET_ADDRSTRLEN, '\0'); 238 if (inet_ntop(AF_INET, &addr, ip.data(), ip.size()) == nullptr) 239 { 240 throw std::runtime_error("Failed to convert IP4 to string"); 241 } 242 243 ip.resize(strlen(ip.c_str())); 244 return ip; 245 } 246 247 std::string toString(const struct in6_addr& addr) 248 { 249 std::string ip(INET6_ADDRSTRLEN, '\0'); 250 if (inet_ntop(AF_INET6, &addr, ip.data(), ip.size()) == nullptr) 251 { 252 throw std::runtime_error("Failed to convert IP6 to string"); 253 } 254 255 ip.resize(strlen(ip.c_str())); 256 return ip; 257 } 258 259 std::string toString(const InAddrAny& addr) 260 { 261 if (std::holds_alternative<struct in_addr>(addr)) 262 { 263 const auto& v = std::get<struct in_addr>(addr); 264 return toString(v); 265 } 266 else if (std::holds_alternative<struct in6_addr>(addr)) 267 { 268 const auto& v = std::get<struct in6_addr>(addr); 269 return toString(v); 270 } 271 272 throw std::runtime_error("Invalid addr type"); 273 } 274 275 bool isLinkLocalIP(const std::string& address) 276 { 277 return address.find(IPV4_PREFIX) == 0 || address.find(IPV6_PREFIX) == 0; 278 } 279 280 bool isValidIP(int addressFamily, const std::string& address) 281 { 282 unsigned char buf[sizeof(struct in6_addr)]; 283 284 return inet_pton(addressFamily, address.c_str(), buf) > 0; 285 } 286 287 bool isValidPrefix(int addressFamily, uint8_t prefixLength) 288 { 289 if (addressFamily == AF_INET) 290 { 291 if (prefixLength < IPV4_MIN_PREFIX_LENGTH || 292 prefixLength > IPV4_MAX_PREFIX_LENGTH) 293 { 294 return false; 295 } 296 } 297 298 if (addressFamily == AF_INET6) 299 { 300 if (prefixLength < IPV4_MIN_PREFIX_LENGTH || 301 prefixLength > IPV6_MAX_PREFIX_LENGTH) 302 { 303 return false; 304 } 305 } 306 307 return true; 308 } 309 310 IntfAddrMap getInterfaceAddrs() 311 { 312 IntfAddrMap intfMap{}; 313 struct ifaddrs* ifaddr = nullptr; 314 315 // attempt to fill struct with ifaddrs 316 if (getifaddrs(&ifaddr) == -1) 317 { 318 auto error = errno; 319 log<level::ERR>("Error occurred during the getifaddrs call", 320 entry("ERRNO=%s", strerror(error))); 321 elog<InternalFailure>(); 322 } 323 324 AddrPtr ifaddrPtr(ifaddr); 325 ifaddr = nullptr; 326 327 std::string intfName{}; 328 329 for (ifaddrs* ifa = ifaddrPtr.get(); ifa != nullptr; ifa = ifa->ifa_next) 330 { 331 // walk interfaces 332 if (ifa->ifa_addr == nullptr) 333 { 334 continue; 335 } 336 337 // get only INET interfaces not ipv6 338 if (ifa->ifa_addr->sa_family == AF_INET || 339 ifa->ifa_addr->sa_family == AF_INET6) 340 { 341 // if loopback, or not running ignore 342 if ((ifa->ifa_flags & IFF_LOOPBACK) || 343 !(ifa->ifa_flags & IFF_RUNNING)) 344 { 345 continue; 346 } 347 intfName = ifa->ifa_name; 348 AddrInfo info{}; 349 char ip[INET6_ADDRSTRLEN] = {0}; 350 char subnetMask[INET6_ADDRSTRLEN] = {0}; 351 352 if (ifa->ifa_addr->sa_family == AF_INET) 353 { 354 355 inet_ntop(ifa->ifa_addr->sa_family, 356 &(((struct sockaddr_in*)(ifa->ifa_addr))->sin_addr), 357 ip, sizeof(ip)); 358 359 inet_ntop( 360 ifa->ifa_addr->sa_family, 361 &(((struct sockaddr_in*)(ifa->ifa_netmask))->sin_addr), 362 subnetMask, sizeof(subnetMask)); 363 } 364 else 365 { 366 inet_ntop(ifa->ifa_addr->sa_family, 367 &(((struct sockaddr_in6*)(ifa->ifa_addr))->sin6_addr), 368 ip, sizeof(ip)); 369 370 inet_ntop( 371 ifa->ifa_addr->sa_family, 372 &(((struct sockaddr_in6*)(ifa->ifa_netmask))->sin6_addr), 373 subnetMask, sizeof(subnetMask)); 374 } 375 376 info.addrType = ifa->ifa_addr->sa_family; 377 info.ipaddress = ip; 378 info.prefix = toCidr(info.addrType, std::string(subnetMask)); 379 intfMap[intfName].push_back(info); 380 } 381 } 382 return intfMap; 383 } 384 385 InterfaceList getInterfaces() 386 { 387 InterfaceList interfaces{}; 388 struct ifaddrs* ifaddr = nullptr; 389 390 // attempt to fill struct with ifaddrs 391 if (getifaddrs(&ifaddr) == -1) 392 { 393 auto error = errno; 394 log<level::ERR>("Error occurred during the getifaddrs call", 395 entry("ERRNO=%d", error)); 396 elog<InternalFailure>(); 397 } 398 399 AddrPtr ifaddrPtr(ifaddr); 400 ifaddr = nullptr; 401 const auto& ignoredInterfaces = internal::getIgnoredInterfaces(); 402 403 for (ifaddrs* ifa = ifaddrPtr.get(); ifa != nullptr; ifa = ifa->ifa_next) 404 { 405 // walk interfaces 406 // if loopback ignore 407 if (ifa->ifa_flags & IFF_LOOPBACK || 408 ignoredInterfaces.find(ifa->ifa_name) != ignoredInterfaces.end()) 409 { 410 continue; 411 } 412 interfaces.emplace(ifa->ifa_name); 413 } 414 return interfaces; 415 } 416 417 void deleteInterface(const std::string& intf) 418 { 419 pid_t pid = fork(); 420 int status{}; 421 422 if (pid == 0) 423 { 424 425 execl("/sbin/ip", "ip", "link", "delete", "dev", intf.c_str(), nullptr); 426 auto error = errno; 427 log<level::ERR>("Couldn't delete the device", entry("ERRNO=%d", error), 428 entry("INTF=%s", intf.c_str())); 429 elog<InternalFailure>(); 430 } 431 else if (pid < 0) 432 { 433 auto error = errno; 434 log<level::ERR>("Error occurred during fork", entry("ERRNO=%d", error)); 435 elog<InternalFailure>(); 436 } 437 else if (pid > 0) 438 { 439 while (waitpid(pid, &status, 0) == -1) 440 { 441 if (errno != EINTR) 442 { /* Error other than EINTR */ 443 status = -1; 444 break; 445 } 446 } 447 448 if (status < 0) 449 { 450 log<level::ERR>("Unable to delete the interface", 451 entry("INTF=%s", intf.c_str()), 452 entry("STATUS=%d", status)); 453 elog<InternalFailure>(); 454 } 455 } 456 } 457 458 std::optional<std::string> interfaceToUbootEthAddr(const char* intf) 459 { 460 constexpr char ethPrefix[] = "eth"; 461 constexpr size_t ethPrefixLen = sizeof(ethPrefix) - 1; 462 if (strncmp(ethPrefix, intf, ethPrefixLen) != 0) 463 { 464 return std::nullopt; 465 } 466 const auto intfSuffix = intf + ethPrefixLen; 467 if (intfSuffix[0] == '\0') 468 { 469 return std::nullopt; 470 } 471 char* end; 472 unsigned long idx = strtoul(intfSuffix, &end, 10); 473 if (end[0] != '\0') 474 { 475 return std::nullopt; 476 } 477 if (idx == 0) 478 { 479 return "ethaddr"; 480 } 481 return "eth" + std::to_string(idx) + "addr"; 482 } 483 484 EthernetInterfaceIntf::DHCPConf getDHCPValue(const std::string& confDir, 485 const std::string& intf) 486 { 487 EthernetInterfaceIntf::DHCPConf dhcp = 488 EthernetInterfaceIntf::DHCPConf::none; 489 // Get the interface mode value from systemd conf 490 // using namespace std::string_literals; 491 fs::path confPath = confDir; 492 std::string fileName = systemd::config::networkFilePrefix + intf + 493 systemd::config::networkFileSuffix; 494 confPath /= fileName; 495 496 auto rc = config::ReturnCode::SUCCESS; 497 config::ValueList values; 498 config::Parser parser(confPath.string()); 499 500 std::tie(rc, values) = parser.getValues("Network", "DHCP"); 501 if (rc != config::ReturnCode::SUCCESS) 502 { 503 log<level::DEBUG>("Unable to get the value for Network[DHCP]", 504 entry("RC=%d", rc)); 505 return dhcp; 506 } 507 // There will be only single value for DHCP key. 508 if (values[0] == "true") 509 { 510 dhcp = EthernetInterfaceIntf::DHCPConf::both; 511 } 512 else if (values[0] == "ipv4") 513 { 514 dhcp = EthernetInterfaceIntf::DHCPConf::v4; 515 } 516 else if (values[0] == "ipv6") 517 { 518 dhcp = EthernetInterfaceIntf::DHCPConf::v6; 519 } 520 return dhcp; 521 } 522 523 namespace mac_address 524 { 525 526 constexpr auto mapperBus = "xyz.openbmc_project.ObjectMapper"; 527 constexpr auto mapperObj = "/xyz/openbmc_project/object_mapper"; 528 constexpr auto mapperIntf = "xyz.openbmc_project.ObjectMapper"; 529 constexpr auto propIntf = "org.freedesktop.DBus.Properties"; 530 constexpr auto methodGet = "Get"; 531 constexpr auto configFile = "/usr/share/network/config.json"; 532 533 using DbusObjectPath = std::string; 534 using DbusService = std::string; 535 using DbusInterface = std::string; 536 using ObjectTree = 537 std::map<DbusObjectPath, std::map<DbusService, std::vector<DbusInterface>>>; 538 539 constexpr auto invBus = "xyz.openbmc_project.Inventory.Manager"; 540 constexpr auto invNetworkIntf = 541 "xyz.openbmc_project.Inventory.Item.NetworkInterface"; 542 constexpr auto invRoot = "/xyz/openbmc_project/inventory"; 543 544 ether_addr getfromInventory(sdbusplus::bus::bus& bus, 545 const std::string& intfName) 546 { 547 548 std::string interfaceName = intfName; 549 550 #ifdef SYNC_MAC_FROM_INVENTORY 551 // load the config JSON from the Read Only Path 552 std::ifstream in(configFile); 553 nlohmann::json configJson; 554 in >> configJson; 555 interfaceName = configJson[intfName]; 556 #endif 557 558 std::vector<DbusInterface> interfaces; 559 interfaces.emplace_back(invNetworkIntf); 560 561 auto depth = 0; 562 563 auto mapperCall = 564 bus.new_method_call(mapperBus, mapperObj, mapperIntf, "GetSubTree"); 565 566 mapperCall.append(invRoot, depth, interfaces); 567 568 auto mapperReply = bus.call(mapperCall); 569 if (mapperReply.is_method_error()) 570 { 571 log<level::ERR>("Error in mapper call"); 572 elog<InternalFailure>(); 573 } 574 575 ObjectTree objectTree; 576 mapperReply.read(objectTree); 577 578 if (objectTree.empty()) 579 { 580 log<level::ERR>("No Object has implemented the interface", 581 entry("INTERFACE=%s", invNetworkIntf)); 582 elog<InternalFailure>(); 583 } 584 585 DbusObjectPath objPath; 586 DbusService service; 587 588 if (1 == objectTree.size()) 589 { 590 objPath = objectTree.begin()->first; 591 service = objectTree.begin()->second.begin()->first; 592 } 593 else 594 { 595 // If there are more than 2 objects, object path must contain the 596 // interface name 597 for (auto const& object : objectTree) 598 { 599 log<level::INFO>("interface", 600 entry("INT=%s", interfaceName.c_str())); 601 log<level::INFO>("object", entry("OBJ=%s", object.first.c_str())); 602 603 if (std::string::npos != object.first.find(interfaceName.c_str())) 604 { 605 objPath = object.first; 606 service = object.second.begin()->first; 607 break; 608 } 609 } 610 611 if (objPath.empty()) 612 { 613 log<level::ERR>("Can't find the object for the interface", 614 entry("intfName=%s", interfaceName.c_str())); 615 elog<InternalFailure>(); 616 } 617 } 618 619 auto method = bus.new_method_call(service.c_str(), objPath.c_str(), 620 propIntf, methodGet); 621 622 method.append(invNetworkIntf, "MACAddress"); 623 624 auto reply = bus.call(method); 625 if (reply.is_method_error()) 626 { 627 log<level::ERR>("Failed to get MACAddress", 628 entry("PATH=%s", objPath.c_str()), 629 entry("INTERFACE=%s", invNetworkIntf)); 630 elog<InternalFailure>(); 631 } 632 633 std::variant<std::string> value; 634 reply.read(value); 635 return fromString(std::get<std::string>(value)); 636 } 637 638 ether_addr fromString(const char* str) 639 { 640 std::string genstr; 641 642 // MAC address without colons 643 std::string_view strv = str; 644 if (strv.size() == 12 && strv.find(":") == strv.npos) 645 { 646 genstr = 647 fmt::format(FMT_COMPILE("{}:{}:{}:{}:{}:{}"), strv.substr(0, 2), 648 strv.substr(2, 2), strv.substr(4, 2), strv.substr(6, 2), 649 strv.substr(8, 2), strv.substr(10, 2)); 650 str = genstr.c_str(); 651 } 652 653 ether_addr addr; 654 if (ether_aton_r(str, &addr) == nullptr) 655 { 656 throw std::invalid_argument("Invalid MAC Address"); 657 } 658 return addr; 659 } 660 661 std::string toString(const ether_addr& mac) 662 { 663 char buf[18] = {0}; 664 snprintf(buf, 18, "%02x:%02x:%02x:%02x:%02x:%02x", mac.ether_addr_octet[0], 665 mac.ether_addr_octet[1], mac.ether_addr_octet[2], 666 mac.ether_addr_octet[3], mac.ether_addr_octet[4], 667 mac.ether_addr_octet[5]); 668 return buf; 669 } 670 671 bool isEmpty(const ether_addr& mac) 672 { 673 return stdplus::raw::equal(mac, ether_addr{}); 674 } 675 676 bool isMulticast(const ether_addr& mac) 677 { 678 return mac.ether_addr_octet[0] & 0b1; 679 } 680 681 bool isUnicast(const ether_addr& mac) 682 { 683 return !isEmpty(mac) && !isMulticast(mac); 684 } 685 686 } // namespace mac_address 687 } // namespace network 688 } // namespace phosphor 689