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 removeInterface(info.intf); 135 config::Parser config(config::pathForIntfConf(confDir, *info.intf.name)); 136 auto intf = std::make_unique<EthernetInterface>( 137 bus, *this, info.intf, objectPath, config, true, enabled); 138 intf->createIPAddressObjects(); 139 intf->createStaticNeighborObjects(); 140 intf->loadNameServers(config); 141 intf->loadNTPServers(config); 142 auto ptr = intf.get(); 143 interfaces.insert_or_assign(*info.intf.name, std::move(intf)); 144 interfacesByIdx.insert_or_assign(info.intf.idx, ptr); 145 } 146 147 void Manager::addInterface(const InterfaceInfo& info) 148 { 149 if (info.flags & IFF_LOOPBACK) 150 { 151 return; 152 } 153 if (!info.name) 154 { 155 throw std::invalid_argument("Interface missing name"); 156 } 157 const auto& ignored = internal::getIgnoredInterfaces(); 158 if (ignored.find(*info.name) != ignored.end()) 159 { 160 auto msg = fmt::format("Ignoring interface {}\n", *info.name); 161 log<level::INFO>(msg.c_str()); 162 return; 163 } 164 165 auto it = systemdNetworkdEnabled.find(info.idx); 166 if (it != systemdNetworkdEnabled.end()) 167 { 168 createInterface({info}, it->second); 169 } 170 else 171 { 172 undiscoveredIntfInfo.insert_or_assign( 173 info.idx, UndiscoveredInfo{std::move(info)}); 174 } 175 } 176 177 void Manager::removeInterface(const InterfaceInfo& info) 178 { 179 auto iit = interfacesByIdx.find(info.idx); 180 auto nit = interfaces.end(); 181 if (info.name) 182 { 183 nit = interfaces.find(*info.name); 184 if (nit != interfaces.end() && iit != interfacesByIdx.end() && 185 nit->second.get() != iit->second) 186 { 187 fmt::print(stderr, "Removed interface desync detected\n"); 188 fflush(stderr); 189 std::abort(); 190 } 191 } 192 else if (iit != interfacesByIdx.end()) 193 { 194 for (nit = interfaces.begin(); nit != interfaces.end(); ++nit) 195 { 196 if (nit->second.get() == iit->second) 197 { 198 break; 199 } 200 } 201 } 202 203 if (iit != interfacesByIdx.end()) 204 { 205 interfacesByIdx.erase(iit); 206 } 207 else 208 { 209 undiscoveredIntfInfo.erase(info.idx); 210 } 211 if (nit != interfaces.end()) 212 { 213 interfaces.erase(nit); 214 } 215 } 216 217 void Manager::addAddress(const AddressInfo& info) 218 { 219 if (info.flags & IFA_F_DEPRECATED) 220 { 221 return; 222 } 223 if (auto it = interfacesByIdx.find(info.ifidx); it != interfacesByIdx.end()) 224 { 225 it->second->addAddr(info); 226 } 227 else if (auto it = undiscoveredIntfInfo.find(info.ifidx); 228 it != undiscoveredIntfInfo.end()) 229 { 230 it->second.addrs.insert_or_assign(info.ifaddr, info); 231 } 232 else 233 { 234 throw std::runtime_error( 235 fmt::format("Interface `{}` not found for addr", info.ifidx)); 236 } 237 } 238 239 void Manager::removeAddress(const AddressInfo& info) 240 { 241 if (auto it = interfacesByIdx.find(info.ifidx); it != interfacesByIdx.end()) 242 { 243 it->second->addrs.erase(info.ifaddr); 244 } 245 else if (auto it = undiscoveredIntfInfo.find(info.ifidx); 246 it != undiscoveredIntfInfo.end()) 247 { 248 it->second.addrs.erase(info.ifaddr); 249 } 250 } 251 252 void Manager::addNeighbor(const NeighborInfo& info) 253 { 254 if (!(info.state & NUD_PERMANENT) || !info.addr) 255 { 256 return; 257 } 258 if (auto it = interfacesByIdx.find(info.ifidx); it != interfacesByIdx.end()) 259 { 260 it->second->addStaticNeigh(info); 261 } 262 else if (auto it = undiscoveredIntfInfo.find(info.ifidx); 263 it != undiscoveredIntfInfo.end()) 264 { 265 it->second.staticNeighs.insert_or_assign(*info.addr, info); 266 } 267 else 268 { 269 throw std::runtime_error( 270 fmt::format("Interface `{}` not found for neigh", info.ifidx)); 271 } 272 } 273 274 void Manager::removeNeighbor(const NeighborInfo& info) 275 { 276 if (!info.addr) 277 { 278 return; 279 } 280 if (auto it = interfacesByIdx.find(info.ifidx); it != interfacesByIdx.end()) 281 { 282 it->second->staticNeighbors.erase(*info.addr); 283 } 284 else if (auto it = undiscoveredIntfInfo.find(info.ifidx); 285 it != undiscoveredIntfInfo.end()) 286 { 287 it->second.staticNeighs.erase(*info.addr); 288 } 289 } 290 291 void Manager::addDefGw(unsigned ifidx, InAddrAny addr) 292 { 293 if (auto it = interfacesByIdx.find(ifidx); it != interfacesByIdx.end()) 294 { 295 std::visit( 296 [&](auto addr) { 297 if constexpr (std::is_same_v<in_addr, decltype(addr)>) 298 { 299 it->second->EthernetInterfaceIntf::defaultGateway( 300 std::to_string(addr)); 301 } 302 else if constexpr (std::is_same_v<in6_addr, decltype(addr)>) 303 { 304 it->second->EthernetInterfaceIntf::defaultGateway6( 305 std::to_string(addr)); 306 } 307 else 308 { 309 static_assert(!std::is_same_v<void, decltype(addr)>); 310 } 311 }, 312 addr); 313 } 314 else if (auto it = undiscoveredIntfInfo.find(ifidx); 315 it != undiscoveredIntfInfo.end()) 316 { 317 std::visit( 318 [&](auto addr) { 319 if constexpr (std::is_same_v<in_addr, decltype(addr)>) 320 { 321 it->second.defgw4.emplace(addr); 322 } 323 else if constexpr (std::is_same_v<in6_addr, decltype(addr)>) 324 { 325 it->second.defgw6.emplace(addr); 326 } 327 else 328 { 329 static_assert(!std::is_same_v<void, decltype(addr)>); 330 } 331 }, 332 addr); 333 } 334 else 335 { 336 auto msg = fmt::format("Interface `{}` not found for gw", ifidx); 337 log<level::ERR>(msg.c_str(), entry("IFIDX=%u", ifidx)); 338 } 339 } 340 341 void Manager::removeDefGw(unsigned ifidx, InAddrAny addr) 342 { 343 if (auto it = interfacesByIdx.find(ifidx); it != interfacesByIdx.end()) 344 { 345 std::visit( 346 [&](auto addr) { 347 if constexpr (std::is_same_v<in_addr, decltype(addr)>) 348 { 349 if (it->second->defaultGateway() == std::to_string(addr)) 350 { 351 it->second->EthernetInterfaceIntf::defaultGateway(""); 352 } 353 } 354 else if constexpr (std::is_same_v<in6_addr, decltype(addr)>) 355 { 356 if (it->second->defaultGateway6() == std::to_string(addr)) 357 { 358 it->second->EthernetInterfaceIntf::defaultGateway6(""); 359 } 360 } 361 else 362 { 363 static_assert(!std::is_same_v<void, decltype(addr)>); 364 } 365 }, 366 addr); 367 } 368 else if (auto it = undiscoveredIntfInfo.find(ifidx); 369 it != undiscoveredIntfInfo.end()) 370 { 371 std::visit( 372 [&](auto addr) { 373 if constexpr (std::is_same_v<in_addr, decltype(addr)>) 374 { 375 if (it->second.defgw4 == addr) 376 { 377 it->second.defgw4.reset(); 378 } 379 } 380 else if constexpr (std::is_same_v<in6_addr, decltype(addr)>) 381 { 382 if (it->second.defgw6 == addr) 383 { 384 it->second.defgw6.reset(); 385 } 386 } 387 else 388 { 389 static_assert(!std::is_same_v<void, decltype(addr)>); 390 } 391 }, 392 addr); 393 } 394 } 395 396 void Manager::createInterfaces() 397 { 398 // clear all the interfaces first 399 interfaces.clear(); 400 interfacesByIdx.clear(); 401 for (auto& info : system::getInterfaces()) 402 { 403 addInterface(info); 404 } 405 } 406 407 void Manager::createChildObjects() 408 { 409 routeTable.refresh(); 410 411 // creates the ethernet interface dbus object. 412 createInterfaces(); 413 414 systemConf.reset(nullptr); 415 dhcpConf.reset(nullptr); 416 417 fs::path objPath = objectPath; 418 objPath /= "config"; 419 420 // create the system conf object. 421 systemConf = std::make_unique<phosphor::network::SystemConfiguration>( 422 bus, objPath.string()); 423 // create the dhcp conf object. 424 objPath /= "dhcp"; 425 dhcpConf = std::make_unique<phosphor::network::dhcp::Configuration>( 426 bus, objPath.string(), *this); 427 } 428 429 ObjectPath Manager::vlan(std::string interfaceName, uint32_t id) 430 { 431 if (id == 0 || id >= 4095) 432 { 433 log<level::ERR>("VLAN ID is not valid", entry("VLANID=%u", id)); 434 elog<InvalidArgument>( 435 Argument::ARGUMENT_NAME("VLANId"), 436 Argument::ARGUMENT_VALUE(std::to_string(id).c_str())); 437 } 438 439 auto it = interfaces.find(interfaceName); 440 if (it == interfaces.end()) 441 { 442 using ResourceErr = 443 phosphor::logging::xyz::openbmc_project::Common::ResourceNotFound; 444 elog<ResourceNotFound>(ResourceErr::RESOURCE(interfaceName.c_str())); 445 } 446 return it->second->createVLAN(id); 447 } 448 449 void Manager::reset() 450 { 451 if (fs::is_directory(confDir)) 452 { 453 for (const auto& file : fs::directory_iterator(confDir)) 454 { 455 fs::remove(file.path()); 456 } 457 } 458 log<level::INFO>("Network Factory Reset queued."); 459 } 460 461 // Need to merge the below function with the code which writes the 462 // config file during factory reset. 463 // TODO openbmc/openbmc#1751 464 void Manager::writeToConfigurationFile() 465 { 466 // write all the static ip address in the systemd-network conf file 467 for (const auto& intf : interfaces) 468 { 469 intf.second->writeConfigurationFile(); 470 } 471 } 472 473 #ifdef SYNC_MAC_FROM_INVENTORY 474 void Manager::setFistBootMACOnInterface( 475 const std::pair<std::string, std::string>& inventoryEthPair) 476 { 477 for (const auto& interface : interfaces) 478 { 479 if (interface.first == inventoryEthPair.first) 480 { 481 auto returnMAC = 482 interface.second->macAddress(inventoryEthPair.second); 483 if (returnMAC == inventoryEthPair.second) 484 { 485 log<level::INFO>("Set the MAC on "), 486 entry("interface : ", interface.first.c_str()), 487 entry("MAC : ", inventoryEthPair.second.c_str()); 488 std::error_code ec; 489 if (std::filesystem::is_directory("/var/lib/network", ec)) 490 { 491 std::ofstream persistentFile(FirstBootFile + 492 interface.first); 493 } 494 break; 495 } 496 else 497 { 498 log<level::INFO>("MAC is Not Set on ethernet Interface"); 499 } 500 } 501 } 502 } 503 504 #endif 505 506 void Manager::reloadConfigsNoRefresh() 507 { 508 reloadTimer->restartOnce(reloadTimeout); 509 } 510 511 void Manager::reloadConfigs() 512 { 513 reloadConfigsNoRefresh(); 514 // Ensure that the next refresh happens after reconfiguration 515 refreshObjectTimer->setRemaining(reloadTimeout + refreshTimeout); 516 } 517 518 void Manager::doReloadConfigs() 519 { 520 for (auto& hook : reloadPreHooks) 521 { 522 try 523 { 524 hook(); 525 } 526 catch (const std::exception& ex) 527 { 528 log<level::ERR>("Failed executing reload hook, ignoring", 529 entry("ERR=%s", ex.what())); 530 } 531 } 532 reloadPreHooks.clear(); 533 try 534 { 535 auto method = bus.new_method_call(NETWORKD_BUSNAME, NETWORKD_PATH, 536 NETWORKD_INTERFACE, "Reload"); 537 bus.call_noreply(method); 538 } 539 catch (const sdbusplus::exception_t& ex) 540 { 541 log<level::ERR>("Failed to reload configuration", 542 entry("ERR=%s", ex.what())); 543 elog<InternalFailure>(); 544 } 545 // Ensure reconfiguration has enough time 546 if (refreshObjectTimer->isEnabled()) 547 { 548 refreshObjectTimer->setRemaining(refreshTimeout); 549 } 550 } 551 552 void Manager::handleAdminState(std::string_view state, unsigned ifidx) 553 { 554 if (state == "initialized" || state == "linger") 555 { 556 systemdNetworkdEnabled.erase(ifidx); 557 } 558 else 559 { 560 bool managed = state != "unmanaged"; 561 systemdNetworkdEnabled.insert_or_assign(ifidx, managed); 562 if (auto it = undiscoveredIntfInfo.find(ifidx); 563 it != undiscoveredIntfInfo.end()) 564 { 565 auto info = std::move(it->second); 566 undiscoveredIntfInfo.erase(it); 567 createInterface(info, managed); 568 } 569 else if (auto it = interfacesByIdx.find(ifidx); 570 it != interfacesByIdx.end()) 571 { 572 it->second->EthernetInterfaceIntf::nicEnabled(managed); 573 } 574 } 575 } 576 577 } // namespace network 578 } // namespace phosphor 579