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 26 namespace phosphor 27 { 28 namespace network 29 { 30 31 using namespace phosphor::logging; 32 using namespace sdbusplus::xyz::openbmc_project::Common::Error; 33 using Argument = xyz::openbmc_project::Common::InvalidArgument; 34 using std::literals::string_view_literals::operator""sv; 35 36 static constexpr const char enabledMatch[] = 37 "type='signal',sender='org.freedesktop.network1',path_namespace='/org/" 38 "freedesktop/network1/" 39 "link',interface='org.freedesktop.DBus.Properties',member='" 40 "PropertiesChanged',arg0='org.freedesktop.network1.Link',"; 41 42 Manager::Manager(stdplus::PinnedRef<sdbusplus::bus_t> bus, 43 stdplus::PinnedRef<DelayedExecutor> reload, 44 stdplus::zstring_view objPath, 45 const std::filesystem::path& confDir) : 46 ManagerIface(bus, objPath.c_str(), ManagerIface::action::defer_emit), 47 reload(reload), bus(bus), objPath(std::string(objPath)), confDir(confDir), 48 systemdNetworkdEnabledMatch( 49 bus, enabledMatch, 50 [man = stdplus::PinnedRef(*this)](sdbusplus::message_t& m) { 51 std::string intf; 52 std::unordered_map<std::string, std::variant<std::string>> values; 53 try 54 { 55 m.read(intf, values); 56 auto it = values.find("AdministrativeState"); 57 if (it == values.end()) 58 { 59 return; 60 } 61 const std::string_view obj = m.get_path(); 62 auto sep = obj.rfind('/'); 63 if (sep == obj.npos || sep + 3 > obj.size()) 64 { 65 throw std::invalid_argument("Invalid obj path"); 66 } 67 auto ifidx = 68 stdplus::StrToInt<10, uint16_t>{}(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}", 75 "ERROR", e); 76 } 77 }) 78 { 79 reload.get().setCallback([self = stdplus::PinnedRef(*this)]() { 80 for (auto& hook : self.get().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 self.get().reloadPreHooks.clear(); 93 try 94 { 95 self.get() 96 .bus.get() 97 .new_method_call("org.freedesktop.network1", 98 "/org/freedesktop/network1", 99 "org.freedesktop.network1.Manager", "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 self.get().reloadPostHooks.clear(); 107 } 108 for (auto& hook : self.get().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 self.get().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 stdplus::ToStrHandle<stdplus::IntToStr<10, unsigned>> tsh; 143 auto obj = 144 stdplus::strCat("/org/freedesktop/network1/link/_3"sv, tsh(ifidx)); 145 auto req = 146 bus.get().new_method_call("org.freedesktop.network1", obj.c_str(), 147 "org.freedesktop.DBus.Properties", "Get"); 148 req.append("org.freedesktop.network1.Link", "AdministrativeState"); 149 auto rsp = req.call(); 150 std::variant<std::string> val; 151 rsp.read(val); 152 handleAdminState(std::get<std::string>(val), ifidx); 153 } 154 155 std::filesystem::create_directories(confDir); 156 systemConf = std::make_unique<phosphor::network::SystemConfiguration>( 157 bus, (this->objPath / "config").str); 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 stdplus::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 std::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 std::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( 373 std::is_same_v<stdplus::In6Addr, decltype(addr)>); 374 it->second.defgw6.emplace(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<stdplus::In4Addr, 383 decltype(addr)>) 384 { 385 it->second->EthernetInterfaceIntf::defaultGateway( 386 stdplus::toStr(addr)); 387 } 388 else 389 { 390 static_assert( 391 std::is_same_v<stdplus::In6Addr, decltype(addr)>); 392 it->second->EthernetInterfaceIntf::defaultGateway6( 393 stdplus::toStr(addr)); 394 } 395 }, 396 addr); 397 } 398 } 399 else if (!ignoredIntf.contains(ifidx)) 400 { 401 lg2::error("Interface {NET_IDX} not found for gw", "NET_IDX", ifidx); 402 } 403 } 404 405 void Manager::removeDefGw(unsigned ifidx, stdplus::InAnyAddr addr) 406 { 407 if (auto it = intfInfo.find(ifidx); it != intfInfo.end()) 408 { 409 std::visit( 410 [&](auto addr) { 411 if constexpr (std::is_same_v<stdplus::In4Addr, decltype(addr)>) 412 { 413 if (it->second.defgw4 == addr) 414 { 415 it->second.defgw4.reset(); 416 } 417 } 418 else 419 { 420 static_assert( 421 std::is_same_v<stdplus::In6Addr, decltype(addr)>); 422 if (it->second.defgw6 == addr) 423 { 424 it->second.defgw6.reset(); 425 } 426 } 427 }, 428 addr); 429 if (auto it = interfacesByIdx.find(ifidx); it != interfacesByIdx.end()) 430 { 431 std::visit( 432 [&](auto addr) { 433 if constexpr (std::is_same_v<stdplus::In4Addr, 434 decltype(addr)>) 435 { 436 stdplus::ToStrHandle<stdplus::ToStr<stdplus::In4Addr>> 437 tsh; 438 if (it->second->defaultGateway() == tsh(addr)) 439 { 440 it->second->EthernetInterfaceIntf::defaultGateway( 441 ""); 442 } 443 } 444 else 445 { 446 static_assert( 447 std::is_same_v<stdplus::In6Addr, decltype(addr)>); 448 stdplus::ToStrHandle<stdplus::ToStr<stdplus::In6Addr>> 449 tsh; 450 if (it->second->defaultGateway6() == tsh(addr)) 451 { 452 it->second->EthernetInterfaceIntf::defaultGateway6( 453 ""); 454 } 455 } 456 }, 457 addr); 458 } 459 } 460 } 461 462 ObjectPath Manager::vlan(std::string interfaceName, uint32_t id) 463 { 464 if (id == 0 || id >= 4095) 465 { 466 lg2::error("VLAN ID {NET_VLAN} is not valid", "NET_VLAN", id); 467 elog<InvalidArgument>( 468 Argument::ARGUMENT_NAME("VLANId"), 469 Argument::ARGUMENT_VALUE(std::to_string(id).c_str())); 470 } 471 472 auto it = interfaces.find(interfaceName); 473 if (it == interfaces.end()) 474 { 475 using ResourceErr = 476 phosphor::logging::xyz::openbmc_project::Common::ResourceNotFound; 477 elog<ResourceNotFound>(ResourceErr::RESOURCE(interfaceName.c_str())); 478 } 479 return it->second->createVLAN(id); 480 } 481 482 void Manager::reset() 483 { 484 for (const auto& dirent : std::filesystem::directory_iterator(confDir)) 485 { 486 std::error_code ec; 487 std::filesystem::remove(dirent.path(), ec); 488 } 489 lg2::info("Network data purged."); 490 } 491 492 void Manager::writeToConfigurationFile() 493 { 494 // write all the static ip address in the systemd-network conf file 495 for (const auto& intf : interfaces) 496 { 497 intf.second->writeConfigurationFile(); 498 } 499 } 500 501 void Manager::handleAdminState(std::string_view state, unsigned ifidx) 502 { 503 if (state == "initialized" || state == "linger") 504 { 505 systemdNetworkdEnabled.erase(ifidx); 506 } 507 else 508 { 509 bool managed = state != "unmanaged"; 510 systemdNetworkdEnabled.insert_or_assign(ifidx, managed); 511 if (auto it = interfacesByIdx.find(ifidx); it != interfacesByIdx.end()) 512 { 513 it->second->EthernetInterfaceIntf::nicEnabled(managed); 514 } 515 else if (auto it = intfInfo.find(ifidx); it != intfInfo.end()) 516 { 517 createInterface(it->second, managed); 518 } 519 } 520 } 521 522 } // namespace network 523 } // namespace phosphor 524