1 #include "config.h" 2 3 #include "network_manager.hpp" 4 5 #include "config_parser.hpp" 6 #include "ipaddress.hpp" 7 #include "system_queries.hpp" 8 #include "types.hpp" 9 #include "util.hpp" 10 11 #include <linux/if_addr.h> 12 #include <linux/neighbour.h> 13 #include <net/if.h> 14 15 #include <filesystem> 16 #include <fstream> 17 #include <phosphor-logging/elog-errors.hpp> 18 #include <phosphor-logging/log.hpp> 19 #include <sdbusplus/message.hpp> 20 #include <xyz/openbmc_project/Common/error.hpp> 21 22 constexpr char SYSTEMD_BUSNAME[] = "org.freedesktop.systemd1"; 23 constexpr char SYSTEMD_PATH[] = "/org/freedesktop/systemd1"; 24 constexpr char SYSTEMD_INTERFACE[] = "org.freedesktop.systemd1.Manager"; 25 constexpr auto FirstBootFile = "/var/lib/network/firstBoot_"; 26 27 constexpr char NETWORKD_BUSNAME[] = "org.freedesktop.network1"; 28 constexpr char NETWORKD_PATH[] = "/org/freedesktop/network1"; 29 constexpr char NETWORKD_INTERFACE[] = "org.freedesktop.network1.Manager"; 30 31 namespace phosphor 32 { 33 namespace network 34 { 35 36 extern std::unique_ptr<Timer> refreshObjectTimer; 37 extern std::unique_ptr<Timer> reloadTimer; 38 using namespace phosphor::logging; 39 using namespace sdbusplus::xyz::openbmc_project::Common::Error; 40 using Argument = xyz::openbmc_project::Common::InvalidArgument; 41 42 static constexpr const char enabledMatch[] = 43 "type='signal',sender='org.freedesktop.network1',path_namespace='/org/" 44 "freedesktop/network1/" 45 "link',interface='org.freedesktop.DBus.Properties',member='" 46 "PropertiesChanged',arg0='org.freedesktop.network1.Link',"; 47 48 Manager::Manager(sdbusplus::bus_t& bus, const char* objPath, 49 const fs::path& confDir) : 50 details::VLANCreateIface(bus, objPath, 51 details::VLANCreateIface::action::defer_emit), 52 bus(bus), objectPath(objPath), 53 systemdNetworkdEnabledMatch( 54 bus, enabledMatch, [&](sdbusplus::message_t& m) { 55 std::string intf; 56 std::unordered_map<std::string, std::variant<std::string>> values; 57 try 58 { 59 m.read(intf, values); 60 auto it = values.find("AdministrativeState"); 61 if (it == values.end()) 62 { 63 return; 64 } 65 const std::string_view obj = m.get_path(); 66 auto sep = obj.rfind('/'); 67 if (sep == obj.npos || sep + 3 > obj.size()) 68 { 69 throw std::invalid_argument("Invalid obj path"); 70 } 71 auto ifidx = DecodeInt<unsigned, 10>{}(obj.substr(sep + 3)); 72 const auto& state = std::get<std::string>(it->second); 73 handleAdminState(state, ifidx); 74 } 75 catch (const std::exception& e) 76 { 77 log<level::ERR>( 78 fmt::format("AdministrativeState match parsing failed: {}", 79 e.what()) 80 .c_str(), 81 entry("ERROR=%s", e.what())); 82 } 83 }) 84 { 85 setConfDir(confDir); 86 std::vector< 87 std::tuple<int32_t, std::string, sdbusplus::message::object_path>> 88 links; 89 try 90 { 91 auto rsp = 92 bus.new_method_call("org.freedesktop.network1", 93 "/org/freedesktop/network1", 94 "org.freedesktop.network1.Manager", "ListLinks") 95 .call(); 96 rsp.read(links); 97 } 98 catch (const sdbusplus::exception::SdBusError& e) 99 { 100 // Any failures are systemd-network not being ready 101 } 102 for (const auto& link : links) 103 { 104 unsigned ifidx = std::get<0>(link); 105 auto obj = fmt::format("/org/freedesktop/network1/link/_3{}", ifidx); 106 auto req = 107 bus.new_method_call("org.freedesktop.network1", obj.c_str(), 108 "org.freedesktop.DBus.Properties", "Get"); 109 req.append("org.freedesktop.network1.Link", "AdministrativeState"); 110 auto rsp = req.call(); 111 std::variant<std::string> val; 112 rsp.read(val); 113 handleAdminState(std::get<std::string>(val), ifidx); 114 } 115 } 116 117 void Manager::setConfDir(const fs::path& dir) 118 { 119 confDir = dir; 120 121 if (!fs::exists(confDir)) 122 { 123 if (!fs::create_directories(confDir)) 124 { 125 log<level::ERR>("Unable to create the network conf dir", 126 entry("DIR=%s", confDir.c_str())); 127 elog<InternalFailure>(); 128 } 129 } 130 } 131 132 void Manager::createInterface(const UndiscoveredInfo& info, bool enabled) 133 { 134 if (!info.intf.name) 135 { 136 auto msg = fmt::format("Can't create interface without name: {}", 137 info.intf.idx); 138 log<level::ERR>(msg.c_str(), entry("IFIDX=%u", info.intf.idx)); 139 return; 140 } 141 removeInterface(info.intf); 142 config::Parser config(config::pathForIntfConf(confDir, *info.intf.name)); 143 auto intf = std::make_unique<EthernetInterface>( 144 bus, *this, info.intf, objectPath, config, true, enabled); 145 intf->createIPAddressObjects(); 146 intf->createStaticNeighborObjects(); 147 intf->loadNameServers(config); 148 intf->loadNTPServers(config); 149 auto ptr = intf.get(); 150 interfaces.insert_or_assign(*info.intf.name, std::move(intf)); 151 interfacesByIdx.insert_or_assign(info.intf.idx, ptr); 152 } 153 154 void Manager::addInterface(const InterfaceInfo& info) 155 { 156 if (info.flags & IFF_LOOPBACK) 157 { 158 ignoredIntf.emplace(info.idx); 159 return; 160 } 161 if (info.name) 162 { 163 const auto& ignored = internal::getIgnoredInterfaces(); 164 if (ignored.find(*info.name) != ignored.end()) 165 { 166 static std::unordered_set<std::string> ignored; 167 if (!ignored.contains(*info.name)) 168 { 169 ignored.emplace(*info.name); 170 auto msg = fmt::format("Ignoring interface {}\n", *info.name); 171 log<level::INFO>(msg.c_str()); 172 } 173 ignoredIntf.emplace(info.idx); 174 return; 175 } 176 } 177 178 auto it = systemdNetworkdEnabled.find(info.idx); 179 if (it != systemdNetworkdEnabled.end()) 180 { 181 createInterface({info}, it->second); 182 } 183 else 184 { 185 undiscoveredIntfInfo.insert_or_assign( 186 info.idx, UndiscoveredInfo{std::move(info)}); 187 } 188 } 189 190 void Manager::removeInterface(const InterfaceInfo& info) 191 { 192 auto iit = interfacesByIdx.find(info.idx); 193 auto nit = interfaces.end(); 194 if (info.name) 195 { 196 nit = interfaces.find(*info.name); 197 if (nit != interfaces.end() && iit != interfacesByIdx.end() && 198 nit->second.get() != iit->second) 199 { 200 fmt::print(stderr, "Removed interface desync detected\n"); 201 fflush(stderr); 202 std::abort(); 203 } 204 } 205 else if (iit != interfacesByIdx.end()) 206 { 207 for (nit = interfaces.begin(); nit != interfaces.end(); ++nit) 208 { 209 if (nit->second.get() == iit->second) 210 { 211 break; 212 } 213 } 214 } 215 216 if (iit != interfacesByIdx.end()) 217 { 218 interfacesByIdx.erase(iit); 219 } 220 else 221 { 222 undiscoveredIntfInfo.erase(info.idx); 223 ignoredIntf.erase(info.idx); 224 } 225 if (nit != interfaces.end()) 226 { 227 interfaces.erase(nit); 228 } 229 } 230 231 void Manager::addAddress(const AddressInfo& info) 232 { 233 if (info.flags & IFA_F_DEPRECATED) 234 { 235 return; 236 } 237 if (auto it = interfacesByIdx.find(info.ifidx); it != interfacesByIdx.end()) 238 { 239 it->second->addAddr(info); 240 } 241 else if (auto it = undiscoveredIntfInfo.find(info.ifidx); 242 it != undiscoveredIntfInfo.end()) 243 { 244 it->second.addrs.insert_or_assign(info.ifaddr, info); 245 } 246 else if (!ignoredIntf.contains(info.ifidx)) 247 { 248 throw std::runtime_error( 249 fmt::format("Interface `{}` not found for addr", info.ifidx)); 250 } 251 } 252 253 void Manager::removeAddress(const AddressInfo& info) 254 { 255 if (auto it = interfacesByIdx.find(info.ifidx); it != interfacesByIdx.end()) 256 { 257 it->second->addrs.erase(info.ifaddr); 258 } 259 else if (auto it = undiscoveredIntfInfo.find(info.ifidx); 260 it != undiscoveredIntfInfo.end()) 261 { 262 it->second.addrs.erase(info.ifaddr); 263 } 264 } 265 266 void Manager::addNeighbor(const NeighborInfo& info) 267 { 268 if (!(info.state & NUD_PERMANENT) || !info.addr) 269 { 270 return; 271 } 272 if (auto it = interfacesByIdx.find(info.ifidx); it != interfacesByIdx.end()) 273 { 274 it->second->addStaticNeigh(info); 275 } 276 else if (auto it = undiscoveredIntfInfo.find(info.ifidx); 277 it != undiscoveredIntfInfo.end()) 278 { 279 it->second.staticNeighs.insert_or_assign(*info.addr, info); 280 } 281 else if (!ignoredIntf.contains(info.ifidx)) 282 { 283 throw std::runtime_error( 284 fmt::format("Interface `{}` not found for neigh", info.ifidx)); 285 } 286 } 287 288 void Manager::removeNeighbor(const NeighborInfo& info) 289 { 290 if (!info.addr) 291 { 292 return; 293 } 294 if (auto it = interfacesByIdx.find(info.ifidx); it != interfacesByIdx.end()) 295 { 296 it->second->staticNeighbors.erase(*info.addr); 297 } 298 else if (auto it = undiscoveredIntfInfo.find(info.ifidx); 299 it != undiscoveredIntfInfo.end()) 300 { 301 it->second.staticNeighs.erase(*info.addr); 302 } 303 } 304 305 void Manager::addDefGw(unsigned ifidx, InAddrAny addr) 306 { 307 if (auto it = interfacesByIdx.find(ifidx); it != interfacesByIdx.end()) 308 { 309 std::visit( 310 [&](auto addr) { 311 if constexpr (std::is_same_v<in_addr, decltype(addr)>) 312 { 313 it->second->EthernetInterfaceIntf::defaultGateway( 314 std::to_string(addr)); 315 } 316 else if constexpr (std::is_same_v<in6_addr, decltype(addr)>) 317 { 318 it->second->EthernetInterfaceIntf::defaultGateway6( 319 std::to_string(addr)); 320 } 321 else 322 { 323 static_assert(!std::is_same_v<void, decltype(addr)>); 324 } 325 }, 326 addr); 327 } 328 else if (auto it = undiscoveredIntfInfo.find(ifidx); 329 it != undiscoveredIntfInfo.end()) 330 { 331 std::visit( 332 [&](auto addr) { 333 if constexpr (std::is_same_v<in_addr, decltype(addr)>) 334 { 335 it->second.defgw4.emplace(addr); 336 } 337 else if constexpr (std::is_same_v<in6_addr, decltype(addr)>) 338 { 339 it->second.defgw6.emplace(addr); 340 } 341 else 342 { 343 static_assert(!std::is_same_v<void, decltype(addr)>); 344 } 345 }, 346 addr); 347 } 348 else if (!ignoredIntf.contains(ifidx)) 349 { 350 auto msg = fmt::format("Interface `{}` not found for gw", ifidx); 351 log<level::ERR>(msg.c_str(), entry("IFIDX=%u", ifidx)); 352 } 353 } 354 355 void Manager::removeDefGw(unsigned ifidx, InAddrAny addr) 356 { 357 if (auto it = interfacesByIdx.find(ifidx); it != interfacesByIdx.end()) 358 { 359 std::visit( 360 [&](auto addr) { 361 if constexpr (std::is_same_v<in_addr, decltype(addr)>) 362 { 363 if (it->second->defaultGateway() == std::to_string(addr)) 364 { 365 it->second->EthernetInterfaceIntf::defaultGateway(""); 366 } 367 } 368 else if constexpr (std::is_same_v<in6_addr, decltype(addr)>) 369 { 370 if (it->second->defaultGateway6() == std::to_string(addr)) 371 { 372 it->second->EthernetInterfaceIntf::defaultGateway6(""); 373 } 374 } 375 else 376 { 377 static_assert(!std::is_same_v<void, decltype(addr)>); 378 } 379 }, 380 addr); 381 } 382 else if (auto it = undiscoveredIntfInfo.find(ifidx); 383 it != undiscoveredIntfInfo.end()) 384 { 385 std::visit( 386 [&](auto addr) { 387 if constexpr (std::is_same_v<in_addr, decltype(addr)>) 388 { 389 if (it->second.defgw4 == addr) 390 { 391 it->second.defgw4.reset(); 392 } 393 } 394 else if constexpr (std::is_same_v<in6_addr, decltype(addr)>) 395 { 396 if (it->second.defgw6 == addr) 397 { 398 it->second.defgw6.reset(); 399 } 400 } 401 else 402 { 403 static_assert(!std::is_same_v<void, decltype(addr)>); 404 } 405 }, 406 addr); 407 } 408 } 409 410 void Manager::createInterfaces() 411 { 412 // clear all the interfaces first 413 interfaces.clear(); 414 interfacesByIdx.clear(); 415 for (auto& info : system::getInterfaces()) 416 { 417 addInterface(info); 418 } 419 } 420 421 void Manager::createChildObjects() 422 { 423 routeTable.refresh(); 424 425 // creates the ethernet interface dbus object. 426 createInterfaces(); 427 428 systemConf.reset(nullptr); 429 dhcpConf.reset(nullptr); 430 431 fs::path objPath = objectPath; 432 objPath /= "config"; 433 434 // create the system conf object. 435 systemConf = std::make_unique<phosphor::network::SystemConfiguration>( 436 bus, objPath.string()); 437 // create the dhcp conf object. 438 objPath /= "dhcp"; 439 dhcpConf = std::make_unique<phosphor::network::dhcp::Configuration>( 440 bus, objPath.string(), *this); 441 } 442 443 ObjectPath Manager::vlan(std::string interfaceName, uint32_t id) 444 { 445 if (id == 0 || id >= 4095) 446 { 447 log<level::ERR>("VLAN ID is not valid", entry("VLANID=%u", id)); 448 elog<InvalidArgument>( 449 Argument::ARGUMENT_NAME("VLANId"), 450 Argument::ARGUMENT_VALUE(std::to_string(id).c_str())); 451 } 452 453 auto it = interfaces.find(interfaceName); 454 if (it == interfaces.end()) 455 { 456 using ResourceErr = 457 phosphor::logging::xyz::openbmc_project::Common::ResourceNotFound; 458 elog<ResourceNotFound>(ResourceErr::RESOURCE(interfaceName.c_str())); 459 } 460 return it->second->createVLAN(id); 461 } 462 463 void Manager::reset() 464 { 465 if (fs::is_directory(confDir)) 466 { 467 for (const auto& file : fs::directory_iterator(confDir)) 468 { 469 fs::remove(file.path()); 470 } 471 } 472 log<level::INFO>("Network Factory Reset queued."); 473 } 474 475 // Need to merge the below function with the code which writes the 476 // config file during factory reset. 477 // TODO openbmc/openbmc#1751 478 void Manager::writeToConfigurationFile() 479 { 480 // write all the static ip address in the systemd-network conf file 481 for (const auto& intf : interfaces) 482 { 483 intf.second->writeConfigurationFile(); 484 } 485 } 486 487 #ifdef SYNC_MAC_FROM_INVENTORY 488 void Manager::setFistBootMACOnInterface( 489 const std::pair<std::string, std::string>& inventoryEthPair) 490 { 491 for (const auto& interface : interfaces) 492 { 493 if (interface.first == inventoryEthPair.first) 494 { 495 auto returnMAC = 496 interface.second->macAddress(inventoryEthPair.second); 497 if (returnMAC == inventoryEthPair.second) 498 { 499 log<level::INFO>("Set the MAC on "), 500 entry("interface : ", interface.first.c_str()), 501 entry("MAC : ", inventoryEthPair.second.c_str()); 502 std::error_code ec; 503 if (std::filesystem::is_directory("/var/lib/network", ec)) 504 { 505 std::ofstream persistentFile(FirstBootFile + 506 interface.first); 507 } 508 break; 509 } 510 else 511 { 512 log<level::INFO>("MAC is Not Set on ethernet Interface"); 513 } 514 } 515 } 516 } 517 518 #endif 519 520 void Manager::reloadConfigsNoRefresh() 521 { 522 reloadTimer->restartOnce(reloadTimeout); 523 } 524 525 void Manager::reloadConfigs() 526 { 527 reloadConfigsNoRefresh(); 528 // Ensure that the next refresh happens after reconfiguration 529 refreshObjectTimer->setRemaining(reloadTimeout + refreshTimeout); 530 } 531 532 void Manager::doReloadConfigs() 533 { 534 for (auto& hook : reloadPreHooks) 535 { 536 try 537 { 538 hook(); 539 } 540 catch (const std::exception& ex) 541 { 542 log<level::ERR>("Failed executing reload hook, ignoring", 543 entry("ERR=%s", ex.what())); 544 } 545 } 546 reloadPreHooks.clear(); 547 try 548 { 549 auto method = bus.new_method_call(NETWORKD_BUSNAME, NETWORKD_PATH, 550 NETWORKD_INTERFACE, "Reload"); 551 bus.call_noreply(method); 552 } 553 catch (const sdbusplus::exception_t& ex) 554 { 555 log<level::ERR>("Failed to reload configuration", 556 entry("ERR=%s", ex.what())); 557 elog<InternalFailure>(); 558 } 559 // Ensure reconfiguration has enough time 560 if (refreshObjectTimer->isEnabled()) 561 { 562 refreshObjectTimer->setRemaining(refreshTimeout); 563 } 564 } 565 566 void Manager::handleAdminState(std::string_view state, unsigned ifidx) 567 { 568 if (state == "initialized" || state == "linger") 569 { 570 systemdNetworkdEnabled.erase(ifidx); 571 } 572 else 573 { 574 bool managed = state != "unmanaged"; 575 systemdNetworkdEnabled.insert_or_assign(ifidx, managed); 576 if (auto it = undiscoveredIntfInfo.find(ifidx); 577 it != undiscoveredIntfInfo.end()) 578 { 579 auto info = std::move(it->second); 580 undiscoveredIntfInfo.erase(it); 581 createInterface(info, managed); 582 } 583 else if (auto it = interfacesByIdx.find(ifidx); 584 it != interfacesByIdx.end()) 585 { 586 it->second->EthernetInterfaceIntf::nicEnabled(managed); 587 } 588 } 589 } 590 591 } // namespace network 592 } // namespace phosphor 593