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