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