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