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