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 inline void getIntfOrLog(const decltype(Manager::interfacesByIdx)& intfs, 218 unsigned idx, auto&& cb) 219 { 220 auto it = intfs.find(idx); 221 if (it == intfs.end()) 222 { 223 auto msg = fmt::format("Interface `{}` not found", idx); 224 log<level::ERR>(msg.c_str(), entry("IFIDX=%u", idx)); 225 return; 226 } 227 cb(*it->second); 228 } 229 230 void Manager::addAddress(const AddressInfo& info) 231 { 232 if (info.flags & IFA_F_DEPRECATED) 233 { 234 return; 235 } 236 if (auto it = interfacesByIdx.find(info.ifidx); it != interfacesByIdx.end()) 237 { 238 it->second->addAddr(info); 239 } 240 else if (auto it = undiscoveredIntfInfo.find(info.ifidx); 241 it != undiscoveredIntfInfo.end()) 242 { 243 it->second.addrs.insert_or_assign(info.ifaddr, info); 244 } 245 else 246 { 247 throw std::runtime_error( 248 fmt::format("Interface `{}` not found for addr", info.ifidx)); 249 } 250 } 251 252 void Manager::removeAddress(const AddressInfo& info) 253 { 254 if (auto it = interfacesByIdx.find(info.ifidx); it != interfacesByIdx.end()) 255 { 256 it->second->addrs.erase(info.ifaddr); 257 } 258 else if (auto it = undiscoveredIntfInfo.find(info.ifidx); 259 it != undiscoveredIntfInfo.end()) 260 { 261 it->second.addrs.erase(info.ifaddr); 262 } 263 } 264 265 void Manager::addNeighbor(const NeighborInfo& info) 266 { 267 if (!(info.state & NUD_PERMANENT) || !info.addr) 268 { 269 return; 270 } 271 if (auto it = interfacesByIdx.find(info.ifidx); it != interfacesByIdx.end()) 272 { 273 it->second->addStaticNeigh(info); 274 } 275 else if (auto it = undiscoveredIntfInfo.find(info.ifidx); 276 it != undiscoveredIntfInfo.end()) 277 { 278 it->second.staticNeighs.insert_or_assign(*info.addr, info); 279 } 280 else 281 { 282 throw std::runtime_error( 283 fmt::format("Interface `{}` not found for neigh", info.ifidx)); 284 } 285 } 286 287 void Manager::removeNeighbor(const NeighborInfo& info) 288 { 289 if (!info.addr) 290 { 291 return; 292 } 293 if (auto it = interfacesByIdx.find(info.ifidx); it != interfacesByIdx.end()) 294 { 295 it->second->staticNeighbors.erase(*info.addr); 296 } 297 else if (auto it = undiscoveredIntfInfo.find(info.ifidx); 298 it != undiscoveredIntfInfo.end()) 299 { 300 it->second.staticNeighs.erase(*info.addr); 301 } 302 } 303 304 void Manager::addDefGw(unsigned ifidx, InAddrAny addr) 305 { 306 getIntfOrLog(interfacesByIdx, ifidx, [&](auto& intf) { 307 std::visit( 308 [&](auto addr) { 309 if constexpr (std::is_same_v<in_addr, decltype(addr)>) 310 { 311 intf.EthernetInterfaceIntf::defaultGateway( 312 std::to_string(addr)); 313 } 314 else if constexpr (std::is_same_v<in6_addr, decltype(addr)>) 315 { 316 intf.EthernetInterfaceIntf::defaultGateway6( 317 std::to_string(addr)); 318 } 319 else 320 { 321 static_assert(!std::is_same_v<void, decltype(addr)>); 322 } 323 }, 324 addr); 325 }); 326 } 327 328 void Manager::removeDefGw(unsigned ifidx, InAddrAny addr) 329 { 330 getIntfOrLog(interfacesByIdx, ifidx, [&](auto& intf) { 331 std::visit( 332 [&](auto addr) { 333 if constexpr (std::is_same_v<in_addr, decltype(addr)>) 334 { 335 if (intf.defaultGateway() == std::to_string(addr)) 336 { 337 intf.EthernetInterfaceIntf::defaultGateway(""); 338 } 339 } 340 else if constexpr (std::is_same_v<in6_addr, decltype(addr)>) 341 { 342 if (intf.defaultGateway6() == std::to_string(addr)) 343 { 344 intf.EthernetInterfaceIntf::defaultGateway6(""); 345 } 346 } 347 else 348 { 349 static_assert(!std::is_same_v<void, decltype(addr)>); 350 } 351 }, 352 addr); 353 }); 354 } 355 356 void Manager::createInterfaces() 357 { 358 // clear all the interfaces first 359 interfaces.clear(); 360 interfacesByIdx.clear(); 361 for (auto& info : system::getInterfaces()) 362 { 363 addInterface(info); 364 } 365 } 366 367 void Manager::createChildObjects() 368 { 369 routeTable.refresh(); 370 371 // creates the ethernet interface dbus object. 372 createInterfaces(); 373 374 systemConf.reset(nullptr); 375 dhcpConf.reset(nullptr); 376 377 fs::path objPath = objectPath; 378 objPath /= "config"; 379 380 // create the system conf object. 381 systemConf = std::make_unique<phosphor::network::SystemConfiguration>( 382 bus, objPath.string()); 383 // create the dhcp conf object. 384 objPath /= "dhcp"; 385 dhcpConf = std::make_unique<phosphor::network::dhcp::Configuration>( 386 bus, objPath.string(), *this); 387 } 388 389 ObjectPath Manager::vlan(std::string interfaceName, uint32_t id) 390 { 391 if (id == 0 || id >= 4095) 392 { 393 log<level::ERR>("VLAN ID is not valid", entry("VLANID=%u", id)); 394 elog<InvalidArgument>( 395 Argument::ARGUMENT_NAME("VLANId"), 396 Argument::ARGUMENT_VALUE(std::to_string(id).c_str())); 397 } 398 399 auto it = interfaces.find(interfaceName); 400 if (it == interfaces.end()) 401 { 402 using ResourceErr = 403 phosphor::logging::xyz::openbmc_project::Common::ResourceNotFound; 404 elog<ResourceNotFound>(ResourceErr::RESOURCE(interfaceName.c_str())); 405 } 406 return it->second->createVLAN(id); 407 } 408 409 void Manager::reset() 410 { 411 if (fs::is_directory(confDir)) 412 { 413 for (const auto& file : fs::directory_iterator(confDir)) 414 { 415 fs::remove(file.path()); 416 } 417 } 418 log<level::INFO>("Network Factory Reset queued."); 419 } 420 421 // Need to merge the below function with the code which writes the 422 // config file during factory reset. 423 // TODO openbmc/openbmc#1751 424 void Manager::writeToConfigurationFile() 425 { 426 // write all the static ip address in the systemd-network conf file 427 for (const auto& intf : interfaces) 428 { 429 intf.second->writeConfigurationFile(); 430 } 431 } 432 433 #ifdef SYNC_MAC_FROM_INVENTORY 434 void Manager::setFistBootMACOnInterface( 435 const std::pair<std::string, std::string>& inventoryEthPair) 436 { 437 for (const auto& interface : interfaces) 438 { 439 if (interface.first == inventoryEthPair.first) 440 { 441 auto returnMAC = 442 interface.second->macAddress(inventoryEthPair.second); 443 if (returnMAC == inventoryEthPair.second) 444 { 445 log<level::INFO>("Set the MAC on "), 446 entry("interface : ", interface.first.c_str()), 447 entry("MAC : ", inventoryEthPair.second.c_str()); 448 std::error_code ec; 449 if (std::filesystem::is_directory("/var/lib/network", ec)) 450 { 451 std::ofstream persistentFile(FirstBootFile + 452 interface.first); 453 } 454 break; 455 } 456 else 457 { 458 log<level::INFO>("MAC is Not Set on ethernet Interface"); 459 } 460 } 461 } 462 } 463 464 #endif 465 466 void Manager::reloadConfigsNoRefresh() 467 { 468 reloadTimer->restartOnce(reloadTimeout); 469 } 470 471 void Manager::reloadConfigs() 472 { 473 reloadConfigsNoRefresh(); 474 // Ensure that the next refresh happens after reconfiguration 475 refreshObjectTimer->setRemaining(reloadTimeout + refreshTimeout); 476 } 477 478 void Manager::doReloadConfigs() 479 { 480 for (auto& hook : reloadPreHooks) 481 { 482 try 483 { 484 hook(); 485 } 486 catch (const std::exception& ex) 487 { 488 log<level::ERR>("Failed executing reload hook, ignoring", 489 entry("ERR=%s", ex.what())); 490 } 491 } 492 reloadPreHooks.clear(); 493 try 494 { 495 auto method = bus.new_method_call(NETWORKD_BUSNAME, NETWORKD_PATH, 496 NETWORKD_INTERFACE, "Reload"); 497 bus.call_noreply(method); 498 } 499 catch (const sdbusplus::exception_t& ex) 500 { 501 log<level::ERR>("Failed to reload configuration", 502 entry("ERR=%s", ex.what())); 503 elog<InternalFailure>(); 504 } 505 // Ensure reconfiguration has enough time 506 if (refreshObjectTimer->isEnabled()) 507 { 508 refreshObjectTimer->setRemaining(refreshTimeout); 509 } 510 } 511 512 void Manager::handleAdminState(std::string_view state, unsigned ifidx) 513 { 514 if (state == "initialized" || state == "linger") 515 { 516 systemdNetworkdEnabled.erase(ifidx); 517 } 518 else 519 { 520 bool managed = state != "unmanaged"; 521 systemdNetworkdEnabled.insert_or_assign(ifidx, managed); 522 if (auto it = undiscoveredIntfInfo.find(ifidx); 523 it != undiscoveredIntfInfo.end()) 524 { 525 auto info = std::move(it->second); 526 undiscoveredIntfInfo.erase(it); 527 createInterface(info, managed); 528 } 529 else if (auto it = interfacesByIdx.find(ifidx); 530 it != interfacesByIdx.end()) 531 { 532 it->second->EthernetInterfaceIntf::nicEnabled(managed); 533 } 534 } 535 } 536 537 } // namespace network 538 } // namespace phosphor 539