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 = stdplus::StrToInt<10, uint16_t>{}(obj.substr(sep + 3)); 68 const auto& state = std::get<std::string>(it->second); 69 man.get().handleAdminState(state, ifidx); 70 } 71 catch (const std::exception& e) 72 { 73 lg2::error("AdministrativeState match parsing failed: {ERROR}", "ERROR", 74 e); 75 } 76 }) 77 { 78 reload.get().setCallback([self = stdplus::PinnedRef(*this)]() { 79 for (auto& hook : self.get().reloadPreHooks) 80 { 81 try 82 { 83 hook(); 84 } 85 catch (const std::exception& ex) 86 { 87 lg2::error("Failed executing reload hook, ignoring: {ERROR}", 88 "ERROR", ex); 89 } 90 } 91 self.get().reloadPreHooks.clear(); 92 try 93 { 94 self.get() 95 .bus.get() 96 .new_method_call("org.freedesktop.network1", 97 "/org/freedesktop/network1", 98 "org.freedesktop.network1.Manager", "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 self.get().reloadPostHooks.clear(); 106 } 107 for (auto& hook : self.get().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 self.get().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 stdplus::ToStrHandle<stdplus::IntToStr<10, unsigned>> tsh; 142 auto obj = stdplus::strCat("/org/freedesktop/network1/link/_3"sv, 143 tsh(ifidx)); 144 auto req = 145 bus.get().new_method_call("org.freedesktop.network1", obj.c_str(), 146 "org.freedesktop.DBus.Properties", "Get"); 147 req.append("org.freedesktop.network1.Link", "AdministrativeState"); 148 auto rsp = req.call(); 149 std::variant<std::string> val; 150 rsp.read(val); 151 handleAdminState(std::get<std::string>(val), ifidx); 152 } 153 154 std::filesystem::create_directories(confDir); 155 systemConf = std::make_unique<phosphor::network::SystemConfiguration>( 156 bus, (this->objPath / "config").str); 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 stdplus::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 std::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 std::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, stdplus::InAnyAddr 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<stdplus::In4Addr, decltype(addr)>) 366 { 367 it->second.defgw4.emplace(addr); 368 } 369 else 370 { 371 static_assert(std::is_same_v<stdplus::In6Addr, decltype(addr)>); 372 it->second.defgw6.emplace(addr); 373 } 374 }, 375 addr); 376 if (auto it = interfacesByIdx.find(ifidx); it != interfacesByIdx.end()) 377 { 378 std::visit( 379 [&](auto addr) { 380 if constexpr (std::is_same_v<stdplus::In4Addr, decltype(addr)>) 381 { 382 it->second->EthernetInterfaceIntf::defaultGateway( 383 stdplus::toStr(addr)); 384 } 385 else 386 { 387 static_assert( 388 std::is_same_v<stdplus::In6Addr, decltype(addr)>); 389 it->second->EthernetInterfaceIntf::defaultGateway6( 390 stdplus::toStr(addr)); 391 } 392 }, 393 addr); 394 } 395 } 396 else if (!ignoredIntf.contains(ifidx)) 397 { 398 lg2::error("Interface {NET_IDX} not found for gw", "NET_IDX", ifidx); 399 } 400 } 401 402 void Manager::removeDefGw(unsigned ifidx, stdplus::InAnyAddr addr) 403 { 404 if (auto it = intfInfo.find(ifidx); it != intfInfo.end()) 405 { 406 std::visit( 407 [&](auto addr) { 408 if constexpr (std::is_same_v<stdplus::In4Addr, decltype(addr)>) 409 { 410 if (it->second.defgw4 == addr) 411 { 412 it->second.defgw4.reset(); 413 } 414 } 415 else 416 { 417 static_assert(std::is_same_v<stdplus::In6Addr, decltype(addr)>); 418 if (it->second.defgw6 == addr) 419 { 420 it->second.defgw6.reset(); 421 } 422 } 423 }, 424 addr); 425 if (auto it = interfacesByIdx.find(ifidx); it != interfacesByIdx.end()) 426 { 427 std::visit( 428 [&](auto addr) { 429 if constexpr (std::is_same_v<stdplus::In4Addr, decltype(addr)>) 430 { 431 stdplus::ToStrHandle<stdplus::ToStr<stdplus::In4Addr>> tsh; 432 if (it->second->defaultGateway() == tsh(addr)) 433 { 434 it->second->EthernetInterfaceIntf::defaultGateway(""); 435 } 436 } 437 else 438 { 439 static_assert( 440 std::is_same_v<stdplus::In6Addr, decltype(addr)>); 441 stdplus::ToStrHandle<stdplus::ToStr<stdplus::In6Addr>> tsh; 442 if (it->second->defaultGateway6() == tsh(addr)) 443 { 444 it->second->EthernetInterfaceIntf::defaultGateway6(""); 445 } 446 } 447 }, 448 addr); 449 } 450 } 451 } 452 453 ObjectPath Manager::vlan(std::string interfaceName, uint32_t id) 454 { 455 if (id == 0 || id >= 4095) 456 { 457 lg2::error("VLAN ID {NET_VLAN} is not valid", "NET_VLAN", id); 458 elog<InvalidArgument>( 459 Argument::ARGUMENT_NAME("VLANId"), 460 Argument::ARGUMENT_VALUE(std::to_string(id).c_str())); 461 } 462 463 auto it = interfaces.find(interfaceName); 464 if (it == interfaces.end()) 465 { 466 using ResourceErr = 467 phosphor::logging::xyz::openbmc_project::Common::ResourceNotFound; 468 elog<ResourceNotFound>(ResourceErr::RESOURCE(interfaceName.c_str())); 469 } 470 return it->second->createVLAN(id); 471 } 472 473 void Manager::reset() 474 { 475 for (const auto& dirent : std::filesystem::directory_iterator(confDir)) 476 { 477 std::error_code ec; 478 std::filesystem::remove(dirent.path(), ec); 479 } 480 lg2::info("Network data purged."); 481 } 482 483 void Manager::writeToConfigurationFile() 484 { 485 // write all the static ip address in the systemd-network conf file 486 for (const auto& intf : interfaces) 487 { 488 intf.second->writeConfigurationFile(); 489 } 490 } 491 492 void Manager::handleAdminState(std::string_view state, unsigned ifidx) 493 { 494 if (state == "initialized" || state == "linger") 495 { 496 systemdNetworkdEnabled.erase(ifidx); 497 } 498 else 499 { 500 bool managed = state != "unmanaged"; 501 systemdNetworkdEnabled.insert_or_assign(ifidx, managed); 502 if (auto it = interfacesByIdx.find(ifidx); it != interfacesByIdx.end()) 503 { 504 it->second->EthernetInterfaceIntf::nicEnabled(managed); 505 } 506 else if (auto it = intfInfo.find(ifidx); it != intfInfo.end()) 507 { 508 createInterface(it->second, managed); 509 } 510 } 511 } 512 513 } // namespace network 514 } // namespace phosphor 515