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