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 std::string toMask(int addressFamily, uint8_t prefix) 149 { 150 if (addressFamily == AF_INET6) 151 { 152 // TODO:- conversion for v6 153 return ""; 154 } 155 156 if (prefix < 1 || prefix > 30) 157 { 158 log<level::ERR>("Invalid Prefix", entry("PREFIX=%d", prefix)); 159 return ""; 160 } 161 /* Create the netmask from the number of bits */ 162 unsigned long mask = 0; 163 for (auto i = 0; i < prefix; i++) 164 { 165 mask |= 1 << (31 - i); 166 } 167 struct in_addr netmask; 168 netmask.s_addr = htonl(mask); 169 return inet_ntoa(netmask); 170 } 171 172 InAddrAny addrFromBuf(int addressFamily, std::string_view buf) 173 { 174 if (addressFamily == AF_INET) 175 { 176 struct in_addr ret; 177 if (buf.size() != sizeof(ret)) 178 { 179 throw std::runtime_error("Buf not in_addr sized"); 180 } 181 memcpy(&ret, buf.data(), sizeof(ret)); 182 return ret; 183 } 184 else if (addressFamily == AF_INET6) 185 { 186 struct in6_addr ret; 187 if (buf.size() != sizeof(ret)) 188 { 189 throw std::runtime_error("Buf not in6_addr sized"); 190 } 191 memcpy(&ret, buf.data(), sizeof(ret)); 192 return ret; 193 } 194 195 throw std::runtime_error("Unsupported address family"); 196 } 197 198 std::string toString(const struct in_addr& addr) 199 { 200 std::string ip(INET_ADDRSTRLEN, '\0'); 201 if (inet_ntop(AF_INET, &addr, ip.data(), ip.size()) == nullptr) 202 { 203 throw std::runtime_error("Failed to convert IP4 to string"); 204 } 205 206 ip.resize(strlen(ip.c_str())); 207 return ip; 208 } 209 210 std::string toString(const struct in6_addr& addr) 211 { 212 std::string ip(INET6_ADDRSTRLEN, '\0'); 213 if (inet_ntop(AF_INET6, &addr, ip.data(), ip.size()) == nullptr) 214 { 215 throw std::runtime_error("Failed to convert IP6 to string"); 216 } 217 218 ip.resize(strlen(ip.c_str())); 219 return ip; 220 } 221 222 std::string toString(const InAddrAny& addr) 223 { 224 if (std::holds_alternative<struct in_addr>(addr)) 225 { 226 const auto& v = std::get<struct in_addr>(addr); 227 return toString(v); 228 } 229 else if (std::holds_alternative<struct in6_addr>(addr)) 230 { 231 const auto& v = std::get<struct in6_addr>(addr); 232 return toString(v); 233 } 234 235 throw std::runtime_error("Invalid addr type"); 236 } 237 238 bool isValidIP(int addressFamily, const std::string& address) 239 { 240 unsigned char buf[sizeof(struct in6_addr)]; 241 242 return inet_pton(addressFamily, address.c_str(), buf) > 0; 243 } 244 245 bool isValidPrefix(int addressFamily, uint8_t prefixLength) 246 { 247 if (addressFamily == AF_INET) 248 { 249 if (prefixLength < IPV4_MIN_PREFIX_LENGTH || 250 prefixLength > IPV4_MAX_PREFIX_LENGTH) 251 { 252 return false; 253 } 254 } 255 256 if (addressFamily == AF_INET6) 257 { 258 if (prefixLength < IPV4_MIN_PREFIX_LENGTH || 259 prefixLength > IPV6_MAX_PREFIX_LENGTH) 260 { 261 return false; 262 } 263 } 264 265 return true; 266 } 267 268 InterfaceList getInterfaces() 269 { 270 InterfaceList interfaces{}; 271 struct ifaddrs* ifaddr = nullptr; 272 273 // attempt to fill struct with ifaddrs 274 if (getifaddrs(&ifaddr) == -1) 275 { 276 auto error = errno; 277 log<level::ERR>("Error occurred during the getifaddrs call", 278 entry("ERRNO=%d", error)); 279 elog<InternalFailure>(); 280 } 281 282 AddrPtr ifaddrPtr(ifaddr); 283 ifaddr = nullptr; 284 const auto& ignoredInterfaces = internal::getIgnoredInterfaces(); 285 286 for (ifaddrs* ifa = ifaddrPtr.get(); ifa != nullptr; ifa = ifa->ifa_next) 287 { 288 // walk interfaces 289 // if loopback ignore 290 if (ifa->ifa_flags & IFF_LOOPBACK || 291 ignoredInterfaces.find(ifa->ifa_name) != ignoredInterfaces.end()) 292 { 293 continue; 294 } 295 interfaces.emplace(ifa->ifa_name); 296 } 297 return interfaces; 298 } 299 300 void deleteInterface(const std::string& intf) 301 { 302 pid_t pid = fork(); 303 int status{}; 304 305 if (pid == 0) 306 { 307 308 execl("/sbin/ip", "ip", "link", "delete", "dev", intf.c_str(), nullptr); 309 auto error = errno; 310 log<level::ERR>("Couldn't delete the device", entry("ERRNO=%d", error), 311 entry("INTF=%s", intf.c_str())); 312 elog<InternalFailure>(); 313 } 314 else if (pid < 0) 315 { 316 auto error = errno; 317 log<level::ERR>("Error occurred during fork", entry("ERRNO=%d", error)); 318 elog<InternalFailure>(); 319 } 320 else if (pid > 0) 321 { 322 while (waitpid(pid, &status, 0) == -1) 323 { 324 if (errno != EINTR) 325 { /* Error other than EINTR */ 326 status = -1; 327 break; 328 } 329 } 330 331 if (status < 0) 332 { 333 log<level::ERR>("Unable to delete the interface", 334 entry("INTF=%s", intf.c_str()), 335 entry("STATUS=%d", status)); 336 elog<InternalFailure>(); 337 } 338 } 339 } 340 341 std::optional<std::string> interfaceToUbootEthAddr(const char* intf) 342 { 343 constexpr char ethPrefix[] = "eth"; 344 constexpr size_t ethPrefixLen = sizeof(ethPrefix) - 1; 345 if (strncmp(ethPrefix, intf, ethPrefixLen) != 0) 346 { 347 return std::nullopt; 348 } 349 const auto intfSuffix = intf + ethPrefixLen; 350 if (intfSuffix[0] == '\0') 351 { 352 return std::nullopt; 353 } 354 char* end; 355 unsigned long idx = strtoul(intfSuffix, &end, 10); 356 if (end[0] != '\0') 357 { 358 return std::nullopt; 359 } 360 if (idx == 0) 361 { 362 return "ethaddr"; 363 } 364 return "eth" + std::to_string(idx) + "addr"; 365 } 366 367 EthernetInterfaceIntf::DHCPConf getDHCPValue(const std::string& confDir, 368 const std::string& intf) 369 { 370 EthernetInterfaceIntf::DHCPConf dhcp = 371 EthernetInterfaceIntf::DHCPConf::none; 372 // Get the interface mode value from systemd conf 373 // using namespace std::string_literals; 374 fs::path confPath = confDir; 375 std::string fileName = systemd::config::networkFilePrefix + intf + 376 systemd::config::networkFileSuffix; 377 confPath /= fileName; 378 379 config::Parser parser(confPath.string()); 380 const auto& values = parser.getValues("Network", "DHCP"); 381 if (values.empty()) 382 { 383 log<level::NOTICE>("Unable to get the value for Network[DHCP]"); 384 return dhcp; 385 } 386 if (values.back() == "true") 387 { 388 dhcp = EthernetInterfaceIntf::DHCPConf::both; 389 } 390 else if (values.back() == "ipv4") 391 { 392 dhcp = EthernetInterfaceIntf::DHCPConf::v4; 393 } 394 else if (values.back() == "ipv6") 395 { 396 dhcp = EthernetInterfaceIntf::DHCPConf::v6; 397 } 398 return dhcp; 399 } 400 401 namespace mac_address 402 { 403 404 constexpr auto mapperBus = "xyz.openbmc_project.ObjectMapper"; 405 constexpr auto mapperObj = "/xyz/openbmc_project/object_mapper"; 406 constexpr auto mapperIntf = "xyz.openbmc_project.ObjectMapper"; 407 constexpr auto propIntf = "org.freedesktop.DBus.Properties"; 408 constexpr auto methodGet = "Get"; 409 constexpr auto configFile = "/usr/share/network/config.json"; 410 411 using DbusObjectPath = std::string; 412 using DbusService = std::string; 413 using DbusInterface = std::string; 414 using ObjectTree = 415 std::map<DbusObjectPath, std::map<DbusService, std::vector<DbusInterface>>>; 416 417 constexpr auto invBus = "xyz.openbmc_project.Inventory.Manager"; 418 constexpr auto invNetworkIntf = 419 "xyz.openbmc_project.Inventory.Item.NetworkInterface"; 420 constexpr auto invRoot = "/xyz/openbmc_project/inventory"; 421 422 ether_addr getfromInventory(sdbusplus::bus_t& bus, const std::string& intfName) 423 { 424 425 std::string interfaceName = intfName; 426 427 #ifdef SYNC_MAC_FROM_INVENTORY 428 // load the config JSON from the Read Only Path 429 std::ifstream in(configFile); 430 nlohmann::json configJson; 431 in >> configJson; 432 interfaceName = configJson[intfName]; 433 #endif 434 435 std::vector<DbusInterface> interfaces; 436 interfaces.emplace_back(invNetworkIntf); 437 438 auto depth = 0; 439 440 auto mapperCall = 441 bus.new_method_call(mapperBus, mapperObj, mapperIntf, "GetSubTree"); 442 443 mapperCall.append(invRoot, depth, interfaces); 444 445 auto mapperReply = bus.call(mapperCall); 446 if (mapperReply.is_method_error()) 447 { 448 log<level::ERR>("Error in mapper call"); 449 elog<InternalFailure>(); 450 } 451 452 ObjectTree objectTree; 453 mapperReply.read(objectTree); 454 455 if (objectTree.empty()) 456 { 457 log<level::ERR>("No Object has implemented the interface", 458 entry("INTERFACE=%s", invNetworkIntf)); 459 elog<InternalFailure>(); 460 } 461 462 DbusObjectPath objPath; 463 DbusService service; 464 465 if (1 == objectTree.size()) 466 { 467 objPath = objectTree.begin()->first; 468 service = objectTree.begin()->second.begin()->first; 469 } 470 else 471 { 472 // If there are more than 2 objects, object path must contain the 473 // interface name 474 for (auto const& object : objectTree) 475 { 476 log<level::INFO>("interface", 477 entry("INT=%s", interfaceName.c_str())); 478 log<level::INFO>("object", entry("OBJ=%s", object.first.c_str())); 479 480 if (std::string::npos != object.first.find(interfaceName.c_str())) 481 { 482 objPath = object.first; 483 service = object.second.begin()->first; 484 break; 485 } 486 } 487 488 if (objPath.empty()) 489 { 490 log<level::ERR>("Can't find the object for the interface", 491 entry("intfName=%s", interfaceName.c_str())); 492 elog<InternalFailure>(); 493 } 494 } 495 496 auto method = bus.new_method_call(service.c_str(), objPath.c_str(), 497 propIntf, methodGet); 498 499 method.append(invNetworkIntf, "MACAddress"); 500 501 auto reply = bus.call(method); 502 if (reply.is_method_error()) 503 { 504 log<level::ERR>("Failed to get MACAddress", 505 entry("PATH=%s", objPath.c_str()), 506 entry("INTERFACE=%s", invNetworkIntf)); 507 elog<InternalFailure>(); 508 } 509 510 std::variant<std::string> value; 511 reply.read(value); 512 return fromString(std::get<std::string>(value)); 513 } 514 515 ether_addr fromString(const char* str) 516 { 517 std::string genstr; 518 519 // MAC address without colons 520 std::string_view strv = str; 521 if (strv.size() == 12 && strv.find(":") == strv.npos) 522 { 523 genstr = 524 fmt::format(FMT_COMPILE("{}:{}:{}:{}:{}:{}"), strv.substr(0, 2), 525 strv.substr(2, 2), strv.substr(4, 2), strv.substr(6, 2), 526 strv.substr(8, 2), strv.substr(10, 2)); 527 str = genstr.c_str(); 528 } 529 530 ether_addr addr; 531 if (ether_aton_r(str, &addr) == nullptr) 532 { 533 throw std::invalid_argument("Invalid MAC Address"); 534 } 535 return addr; 536 } 537 538 std::string toString(const ether_addr& mac) 539 { 540 char buf[18] = {0}; 541 snprintf(buf, 18, "%02x:%02x:%02x:%02x:%02x:%02x", mac.ether_addr_octet[0], 542 mac.ether_addr_octet[1], mac.ether_addr_octet[2], 543 mac.ether_addr_octet[3], mac.ether_addr_octet[4], 544 mac.ether_addr_octet[5]); 545 return buf; 546 } 547 548 bool isEmpty(const ether_addr& mac) 549 { 550 return stdplus::raw::equal(mac, ether_addr{}); 551 } 552 553 bool isMulticast(const ether_addr& mac) 554 { 555 return mac.ether_addr_octet[0] & 0b1; 556 } 557 558 bool isUnicast(const ether_addr& mac) 559 { 560 return !isEmpty(mac) && !isMulticast(mac); 561 } 562 563 } // namespace mac_address 564 } // namespace network 565 } // namespace phosphor 566