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> reloadTimer; 37 using namespace phosphor::logging; 38 using namespace sdbusplus::xyz::openbmc_project::Common::Error; 39 using Argument = xyz::openbmc_project::Common::InvalidArgument; 40 41 static constexpr const char enabledMatch[] = 42 "type='signal',sender='org.freedesktop.network1',path_namespace='/org/" 43 "freedesktop/network1/" 44 "link',interface='org.freedesktop.DBus.Properties',member='" 45 "PropertiesChanged',arg0='org.freedesktop.network1.Link',"; 46 47 Manager::Manager(sdbusplus::bus_t& bus, const char* objPath, 48 const std::filesystem::path& confDir) : 49 ManagerIface(bus, objPath, ManagerIface::action::defer_emit), 50 bus(bus), objPath(std::string(objPath)), confDir(confDir), 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 std::vector< 84 std::tuple<int32_t, std::string, sdbusplus::message::object_path>> 85 links; 86 try 87 { 88 auto rsp = 89 bus.new_method_call("org.freedesktop.network1", 90 "/org/freedesktop/network1", 91 "org.freedesktop.network1.Manager", "ListLinks") 92 .call(); 93 rsp.read(links); 94 } 95 catch (const sdbusplus::exception::SdBusError& e) 96 { 97 // Any failures are systemd-network not being ready 98 } 99 for (const auto& link : links) 100 { 101 unsigned ifidx = std::get<0>(link); 102 auto obj = fmt::format("/org/freedesktop/network1/link/_3{}", ifidx); 103 auto req = 104 bus.new_method_call("org.freedesktop.network1", obj.c_str(), 105 "org.freedesktop.DBus.Properties", "Get"); 106 req.append("org.freedesktop.network1.Link", "AdministrativeState"); 107 auto rsp = req.call(); 108 std::variant<std::string> val; 109 rsp.read(val); 110 handleAdminState(std::get<std::string>(val), ifidx); 111 } 112 113 std::filesystem::create_directories(confDir); 114 systemConf = std::make_unique<phosphor::network::SystemConfiguration>( 115 bus, (this->objPath / "config").str); 116 dhcpConf = std::make_unique<phosphor::network::dhcp::Configuration>( 117 bus, (this->objPath / "dhcp").str, *this); 118 } 119 120 void Manager::createInterface(const AllIntfInfo& info, bool enabled) 121 { 122 if (auto it = interfacesByIdx.find(info.intf.idx); 123 it != interfacesByIdx.end()) 124 { 125 if (info.intf.name && *info.intf.name != it->second->interfaceName()) 126 { 127 interfaces.erase(it->second->interfaceName()); 128 interfacesByIdx.erase(it); 129 } 130 else 131 { 132 it->second->updateInfo(info.intf); 133 return; 134 } 135 } 136 if (!info.intf.name) 137 { 138 auto msg = fmt::format("Can't create interface without name: {}", 139 info.intf.idx); 140 log<level::ERR>(msg.c_str(), entry("IFIDX=%u", info.intf.idx)); 141 return; 142 } 143 config::Parser config(config::pathForIntfConf(confDir, *info.intf.name)); 144 auto intf = std::make_unique<EthernetInterface>( 145 bus, *this, info.intf, objPath.str, config, enabled); 146 if (info.defgw4) 147 { 148 intf->EthernetInterface::defaultGateway(std::to_string(*info.defgw4)); 149 } 150 if (info.defgw6) 151 { 152 intf->EthernetInterface::defaultGateway6(std::to_string(*info.defgw6)); 153 } 154 for (const auto& [_, addr] : info.addrs) 155 { 156 intf->addAddr(addr); 157 } 158 for (const auto& [_, neigh] : info.staticNeighs) 159 { 160 intf->addStaticNeigh(neigh); 161 } 162 intf->loadNameServers(config); 163 intf->loadNTPServers(config); 164 auto ptr = intf.get(); 165 interfaces.insert_or_assign(*info.intf.name, std::move(intf)); 166 interfacesByIdx.insert_or_assign(info.intf.idx, ptr); 167 } 168 169 void Manager::addInterface(const InterfaceInfo& info) 170 { 171 if (info.flags & IFF_LOOPBACK) 172 { 173 ignoredIntf.emplace(info.idx); 174 return; 175 } 176 if (info.name) 177 { 178 const auto& ignored = internal::getIgnoredInterfaces(); 179 if (ignored.find(*info.name) != ignored.end()) 180 { 181 static std::unordered_set<std::string> ignored; 182 if (!ignored.contains(*info.name)) 183 { 184 ignored.emplace(*info.name); 185 auto msg = fmt::format("Ignoring interface {}\n", *info.name); 186 log<level::INFO>(msg.c_str()); 187 } 188 ignoredIntf.emplace(info.idx); 189 return; 190 } 191 } 192 193 auto infoIt = intfInfo.find(info.idx); 194 if (infoIt != intfInfo.end()) 195 { 196 infoIt->second.intf = info; 197 } 198 else 199 { 200 infoIt = std::get<0>(intfInfo.emplace(info.idx, AllIntfInfo{info})); 201 } 202 203 if (auto it = systemdNetworkdEnabled.find(info.idx); 204 it != systemdNetworkdEnabled.end()) 205 { 206 createInterface(infoIt->second, it->second); 207 } 208 } 209 210 void Manager::removeInterface(const InterfaceInfo& info) 211 { 212 auto iit = interfacesByIdx.find(info.idx); 213 auto nit = interfaces.end(); 214 if (info.name) 215 { 216 nit = interfaces.find(*info.name); 217 if (nit != interfaces.end() && iit != interfacesByIdx.end() && 218 nit->second.get() != iit->second) 219 { 220 fmt::print(stderr, "Removed interface desync detected\n"); 221 fflush(stderr); 222 std::abort(); 223 } 224 } 225 else if (iit != interfacesByIdx.end()) 226 { 227 for (nit = interfaces.begin(); nit != interfaces.end(); ++nit) 228 { 229 if (nit->second.get() == iit->second) 230 { 231 break; 232 } 233 } 234 } 235 236 if (iit != interfacesByIdx.end()) 237 { 238 interfacesByIdx.erase(iit); 239 } 240 else 241 { 242 ignoredIntf.erase(info.idx); 243 } 244 if (nit != interfaces.end()) 245 { 246 interfaces.erase(nit); 247 } 248 intfInfo.erase(info.idx); 249 } 250 251 void Manager::addAddress(const AddressInfo& info) 252 { 253 if (info.flags & IFA_F_DEPRECATED) 254 { 255 return; 256 } 257 if (auto it = intfInfo.find(info.ifidx); it != intfInfo.end()) 258 { 259 it->second.addrs.insert_or_assign(info.ifaddr, info); 260 if (auto it = interfacesByIdx.find(info.ifidx); 261 it != interfacesByIdx.end()) 262 { 263 it->second->addAddr(info); 264 } 265 } 266 else if (!ignoredIntf.contains(info.ifidx)) 267 { 268 throw std::runtime_error( 269 fmt::format("Interface `{}` not found for addr", info.ifidx)); 270 } 271 } 272 273 void Manager::removeAddress(const AddressInfo& info) 274 { 275 if (auto it = interfacesByIdx.find(info.ifidx); it != interfacesByIdx.end()) 276 { 277 it->second->addrs.erase(info.ifaddr); 278 if (auto it = intfInfo.find(info.ifidx); it != intfInfo.end()) 279 { 280 it->second.addrs.erase(info.ifaddr); 281 } 282 } 283 } 284 285 void Manager::addNeighbor(const NeighborInfo& info) 286 { 287 if (!(info.state & NUD_PERMANENT) || !info.addr) 288 { 289 return; 290 } 291 if (auto it = intfInfo.find(info.ifidx); it != intfInfo.end()) 292 { 293 it->second.staticNeighs.insert_or_assign(*info.addr, info); 294 if (auto it = interfacesByIdx.find(info.ifidx); 295 it != interfacesByIdx.end()) 296 { 297 it->second->addStaticNeigh(info); 298 } 299 } 300 else if (!ignoredIntf.contains(info.ifidx)) 301 { 302 throw std::runtime_error( 303 fmt::format("Interface `{}` not found for neigh", info.ifidx)); 304 } 305 } 306 307 void Manager::removeNeighbor(const NeighborInfo& info) 308 { 309 if (!info.addr) 310 { 311 return; 312 } 313 if (auto it = intfInfo.find(info.ifidx); it != intfInfo.end()) 314 { 315 it->second.staticNeighs.erase(*info.addr); 316 if (auto it = interfacesByIdx.find(info.ifidx); 317 it != interfacesByIdx.end()) 318 { 319 it->second->staticNeighbors.erase(*info.addr); 320 } 321 } 322 } 323 324 void Manager::addDefGw(unsigned ifidx, InAddrAny addr) 325 { 326 if (auto it = intfInfo.find(ifidx); it != intfInfo.end()) 327 { 328 std::visit( 329 [&](auto addr) { 330 if constexpr (std::is_same_v<in_addr, decltype(addr)>) 331 { 332 it->second.defgw4.emplace(addr); 333 } 334 else if constexpr (std::is_same_v<in6_addr, decltype(addr)>) 335 { 336 it->second.defgw6.emplace(addr); 337 } 338 else 339 { 340 static_assert(!std::is_same_v<void, decltype(addr)>); 341 } 342 }, 343 addr); 344 if (auto it = interfacesByIdx.find(ifidx); it != interfacesByIdx.end()) 345 { 346 std::visit( 347 [&](auto addr) { 348 if constexpr (std::is_same_v<in_addr, decltype(addr)>) 349 { 350 it->second->EthernetInterfaceIntf::defaultGateway( 351 std::to_string(addr)); 352 } 353 else if constexpr (std::is_same_v<in6_addr, decltype(addr)>) 354 { 355 it->second->EthernetInterfaceIntf::defaultGateway6( 356 std::to_string(addr)); 357 } 358 else 359 { 360 static_assert(!std::is_same_v<void, decltype(addr)>); 361 } 362 }, 363 addr); 364 } 365 } 366 else if (!ignoredIntf.contains(ifidx)) 367 { 368 auto msg = fmt::format("Interface `{}` not found for gw", ifidx); 369 log<level::ERR>(msg.c_str(), entry("IFIDX=%u", ifidx)); 370 } 371 } 372 373 void Manager::removeDefGw(unsigned ifidx, InAddrAny addr) 374 { 375 if (auto it = intfInfo.find(ifidx); it != intfInfo.end()) 376 { 377 std::visit( 378 [&](auto addr) { 379 if constexpr (std::is_same_v<in_addr, decltype(addr)>) 380 { 381 if (it->second.defgw4 == addr) 382 { 383 it->second.defgw4.reset(); 384 } 385 } 386 else if constexpr (std::is_same_v<in6_addr, decltype(addr)>) 387 { 388 if (it->second.defgw6 == addr) 389 { 390 it->second.defgw6.reset(); 391 } 392 } 393 else 394 { 395 static_assert(!std::is_same_v<void, decltype(addr)>); 396 } 397 }, 398 addr); 399 if (auto it = interfacesByIdx.find(ifidx); it != interfacesByIdx.end()) 400 { 401 std::visit( 402 [&](auto addr) { 403 if constexpr (std::is_same_v<in_addr, decltype(addr)>) 404 { 405 if (it->second->defaultGateway() == 406 std::to_string(addr)) 407 { 408 it->second->EthernetInterfaceIntf::defaultGateway( 409 ""); 410 } 411 } 412 else if constexpr (std::is_same_v<in6_addr, decltype(addr)>) 413 { 414 if (it->second->defaultGateway6() == 415 std::to_string(addr)) 416 { 417 it->second->EthernetInterfaceIntf::defaultGateway6( 418 ""); 419 } 420 } 421 else 422 { 423 static_assert(!std::is_same_v<void, decltype(addr)>); 424 } 425 }, 426 addr); 427 } 428 } 429 } 430 431 ObjectPath Manager::vlan(std::string interfaceName, uint32_t id) 432 { 433 if (id == 0 || id >= 4095) 434 { 435 log<level::ERR>("VLAN ID is not valid", entry("VLANID=%u", id)); 436 elog<InvalidArgument>( 437 Argument::ARGUMENT_NAME("VLANId"), 438 Argument::ARGUMENT_VALUE(std::to_string(id).c_str())); 439 } 440 441 auto it = interfaces.find(interfaceName); 442 if (it == interfaces.end()) 443 { 444 using ResourceErr = 445 phosphor::logging::xyz::openbmc_project::Common::ResourceNotFound; 446 elog<ResourceNotFound>(ResourceErr::RESOURCE(interfaceName.c_str())); 447 } 448 return it->second->createVLAN(id); 449 } 450 451 void Manager::reset() 452 { 453 for (const auto& dirent : std::filesystem::directory_iterator(confDir)) 454 { 455 std::error_code ec; 456 std::filesystem::remove(dirent.path(), ec); 457 } 458 log<level::INFO>("Network data purged."); 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::reloadConfigs() 507 { 508 reloadTimer->restartOnce(reloadTimeout); 509 } 510 511 void Manager::doReloadConfigs() 512 { 513 for (auto& hook : reloadPreHooks) 514 { 515 try 516 { 517 hook(); 518 } 519 catch (const std::exception& ex) 520 { 521 log<level::ERR>("Failed executing reload hook, ignoring", 522 entry("ERR=%s", ex.what())); 523 } 524 } 525 reloadPreHooks.clear(); 526 try 527 { 528 auto method = bus.new_method_call(NETWORKD_BUSNAME, NETWORKD_PATH, 529 NETWORKD_INTERFACE, "Reload"); 530 bus.call_noreply(method); 531 } 532 catch (const sdbusplus::exception_t& ex) 533 { 534 log<level::ERR>("Failed to reload configuration", 535 entry("ERR=%s", ex.what())); 536 elog<InternalFailure>(); 537 } 538 } 539 540 void Manager::handleAdminState(std::string_view state, unsigned ifidx) 541 { 542 if (state == "initialized" || state == "linger") 543 { 544 systemdNetworkdEnabled.erase(ifidx); 545 } 546 else 547 { 548 bool managed = state != "unmanaged"; 549 systemdNetworkdEnabled.insert_or_assign(ifidx, managed); 550 if (auto it = interfacesByIdx.find(ifidx); it != interfacesByIdx.end()) 551 { 552 it->second->EthernetInterfaceIntf::nicEnabled(managed); 553 } 554 else if (auto it = intfInfo.find(ifidx); it != intfInfo.end()) 555 { 556 createInterface(it->second, managed); 557 } 558 } 559 } 560 561 } // namespace network 562 } // namespace phosphor 563