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