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 dhcpConf = std::make_unique<phosphor::network::dhcp::Configuration>( 158 bus, (this->objPath / "dhcp").str, *this); 159 } 160 161 void Manager::createInterface(const AllIntfInfo& info, bool enabled) 162 { 163 if (ignoredIntf.find(info.intf.idx) != ignoredIntf.end()) 164 { 165 return; 166 } 167 if (auto it = interfacesByIdx.find(info.intf.idx); 168 it != interfacesByIdx.end()) 169 { 170 if (info.intf.name && *info.intf.name != it->second->interfaceName()) 171 { 172 interfaces.erase(it->second->interfaceName()); 173 interfacesByIdx.erase(it); 174 } 175 else 176 { 177 it->second->updateInfo(info.intf); 178 return; 179 } 180 } 181 else if (info.intf.name) 182 { 183 auto it = interfaces.find(*info.intf.name); 184 if (it != interfaces.end()) 185 { 186 it->second->updateInfo(info.intf); 187 return; 188 } 189 } 190 if (!info.intf.name) 191 { 192 lg2::error("Can't create interface without name: {NET_IDX}", "NET_IDX", 193 info.intf.idx); 194 return; 195 } 196 config::Parser config(config::pathForIntfConf(confDir, *info.intf.name)); 197 auto intf = std::make_unique<EthernetInterface>( 198 bus, *this, info, objPath.str, config, enabled); 199 intf->loadNameServers(config); 200 intf->loadNTPServers(config); 201 auto ptr = intf.get(); 202 interfaces.insert_or_assign(*info.intf.name, std::move(intf)); 203 interfacesByIdx.insert_or_assign(info.intf.idx, ptr); 204 } 205 206 void Manager::addInterface(const InterfaceInfo& info) 207 { 208 if (info.type != ARPHRD_ETHER) 209 { 210 ignoredIntf.emplace(info.idx); 211 return; 212 } 213 if (info.name) 214 { 215 const auto& ignored = internal::getIgnoredInterfaces(); 216 if (ignored.find(*info.name) != ignored.end()) 217 { 218 static std::unordered_set<std::string> ignored; 219 if (!ignored.contains(*info.name)) 220 { 221 ignored.emplace(*info.name); 222 lg2::info("Ignoring interface {NET_INTF}", "NET_INTF", 223 *info.name); 224 } 225 ignoredIntf.emplace(info.idx); 226 return; 227 } 228 } 229 230 auto infoIt = intfInfo.find(info.idx); 231 if (infoIt != intfInfo.end()) 232 { 233 infoIt->second.intf = info; 234 } 235 else 236 { 237 infoIt = std::get<0>(intfInfo.emplace(info.idx, AllIntfInfo{info})); 238 } 239 240 if (auto it = systemdNetworkdEnabled.find(info.idx); 241 it != systemdNetworkdEnabled.end()) 242 { 243 createInterface(infoIt->second, it->second); 244 } 245 } 246 247 void Manager::removeInterface(const InterfaceInfo& info) 248 { 249 auto iit = interfacesByIdx.find(info.idx); 250 auto nit = interfaces.end(); 251 if (info.name) 252 { 253 nit = interfaces.find(*info.name); 254 if (nit != interfaces.end() && iit != interfacesByIdx.end() && 255 nit->second.get() != iit->second) 256 { 257 stdplus::print(stderr, "Removed interface desync detected\n"); 258 fflush(stderr); 259 std::abort(); 260 } 261 } 262 else if (iit != interfacesByIdx.end()) 263 { 264 for (nit = interfaces.begin(); nit != interfaces.end(); ++nit) 265 { 266 if (nit->second.get() == iit->second) 267 { 268 break; 269 } 270 } 271 } 272 273 if (iit != interfacesByIdx.end()) 274 { 275 interfacesByIdx.erase(iit); 276 } 277 else 278 { 279 ignoredIntf.erase(info.idx); 280 } 281 if (nit != interfaces.end()) 282 { 283 interfaces.erase(nit); 284 } 285 intfInfo.erase(info.idx); 286 } 287 288 void Manager::addAddress(const AddressInfo& info) 289 { 290 if (info.flags & IFA_F_DEPRECATED) 291 { 292 return; 293 } 294 if (auto it = intfInfo.find(info.ifidx); it != intfInfo.end()) 295 { 296 it->second.addrs.insert_or_assign(info.ifaddr, info); 297 if (auto it = interfacesByIdx.find(info.ifidx); 298 it != interfacesByIdx.end()) 299 { 300 it->second->addAddr(info); 301 } 302 } 303 else if (!ignoredIntf.contains(info.ifidx)) 304 { 305 throw std::runtime_error( 306 std::format("Interface `{}` not found for addr", info.ifidx)); 307 } 308 } 309 310 void Manager::removeAddress(const AddressInfo& info) 311 { 312 if (auto it = interfacesByIdx.find(info.ifidx); it != interfacesByIdx.end()) 313 { 314 it->second->addrs.erase(info.ifaddr); 315 if (auto it = intfInfo.find(info.ifidx); it != intfInfo.end()) 316 { 317 it->second.addrs.erase(info.ifaddr); 318 } 319 } 320 } 321 322 void Manager::addNeighbor(const NeighborInfo& info) 323 { 324 if (!(info.state & NUD_PERMANENT) || !info.addr) 325 { 326 return; 327 } 328 if (auto it = intfInfo.find(info.ifidx); it != intfInfo.end()) 329 { 330 it->second.staticNeighs.insert_or_assign(*info.addr, info); 331 if (auto it = interfacesByIdx.find(info.ifidx); 332 it != interfacesByIdx.end()) 333 { 334 it->second->addStaticNeigh(info); 335 } 336 } 337 else if (!ignoredIntf.contains(info.ifidx)) 338 { 339 throw std::runtime_error( 340 std::format("Interface `{}` not found for neigh", info.ifidx)); 341 } 342 } 343 344 void Manager::removeNeighbor(const NeighborInfo& info) 345 { 346 if (!info.addr) 347 { 348 return; 349 } 350 if (auto it = intfInfo.find(info.ifidx); it != intfInfo.end()) 351 { 352 it->second.staticNeighs.erase(*info.addr); 353 if (auto it = interfacesByIdx.find(info.ifidx); 354 it != interfacesByIdx.end()) 355 { 356 it->second->staticNeighbors.erase(*info.addr); 357 } 358 } 359 } 360 361 void Manager::addDefGw(unsigned ifidx, stdplus::InAnyAddr addr) 362 { 363 if (auto it = intfInfo.find(ifidx); it != intfInfo.end()) 364 { 365 std::visit( 366 [&](auto addr) { 367 if constexpr (std::is_same_v<stdplus::In4Addr, decltype(addr)>) 368 { 369 it->second.defgw4.emplace(addr); 370 } 371 else 372 { 373 static_assert(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, decltype(addr)>) 383 { 384 it->second->EthernetInterfaceIntf::defaultGateway( 385 stdplus::toStr(addr)); 386 } 387 else 388 { 389 static_assert( 390 std::is_same_v<stdplus::In6Addr, decltype(addr)>); 391 it->second->EthernetInterfaceIntf::defaultGateway6( 392 stdplus::toStr(addr)); 393 } 394 }, 395 addr); 396 } 397 } 398 else if (!ignoredIntf.contains(ifidx)) 399 { 400 lg2::error("Interface {NET_IDX} not found for gw", "NET_IDX", ifidx); 401 } 402 } 403 404 void Manager::removeDefGw(unsigned ifidx, stdplus::InAnyAddr addr) 405 { 406 if (auto it = intfInfo.find(ifidx); it != intfInfo.end()) 407 { 408 std::visit( 409 [&](auto addr) { 410 if constexpr (std::is_same_v<stdplus::In4Addr, decltype(addr)>) 411 { 412 if (it->second.defgw4 == addr) 413 { 414 it->second.defgw4.reset(); 415 } 416 } 417 else 418 { 419 static_assert(std::is_same_v<stdplus::In6Addr, decltype(addr)>); 420 if (it->second.defgw6 == addr) 421 { 422 it->second.defgw6.reset(); 423 } 424 } 425 }, 426 addr); 427 if (auto it = interfacesByIdx.find(ifidx); it != interfacesByIdx.end()) 428 { 429 std::visit( 430 [&](auto addr) { 431 if constexpr (std::is_same_v<stdplus::In4Addr, decltype(addr)>) 432 { 433 stdplus::ToStrHandle<stdplus::ToStr<stdplus::In4Addr>> tsh; 434 if (it->second->defaultGateway() == tsh(addr)) 435 { 436 it->second->EthernetInterfaceIntf::defaultGateway(""); 437 } 438 } 439 else 440 { 441 static_assert( 442 std::is_same_v<stdplus::In6Addr, decltype(addr)>); 443 stdplus::ToStrHandle<stdplus::ToStr<stdplus::In6Addr>> tsh; 444 if (it->second->defaultGateway6() == tsh(addr)) 445 { 446 it->second->EthernetInterfaceIntf::defaultGateway6(""); 447 } 448 } 449 }, 450 addr); 451 } 452 } 453 } 454 455 ObjectPath Manager::vlan(std::string interfaceName, uint32_t id) 456 { 457 if (id == 0 || id >= 4095) 458 { 459 lg2::error("VLAN ID {NET_VLAN} is not valid", "NET_VLAN", id); 460 elog<InvalidArgument>( 461 Argument::ARGUMENT_NAME("VLANId"), 462 Argument::ARGUMENT_VALUE(std::to_string(id).c_str())); 463 } 464 465 auto it = interfaces.find(interfaceName); 466 if (it == interfaces.end()) 467 { 468 using ResourceErr = 469 phosphor::logging::xyz::openbmc_project::Common::ResourceNotFound; 470 elog<ResourceNotFound>(ResourceErr::RESOURCE(interfaceName.c_str())); 471 } 472 return it->second->createVLAN(id); 473 } 474 475 void Manager::reset() 476 { 477 for (const auto& dirent : std::filesystem::directory_iterator(confDir)) 478 { 479 std::error_code ec; 480 std::filesystem::remove(dirent.path(), ec); 481 } 482 lg2::info("Network data purged."); 483 } 484 485 // Need to merge the below function with the code which writes the 486 // config file during factory reset. 487 // TODO openbmc/openbmc#1751 488 void Manager::writeToConfigurationFile() 489 { 490 // write all the static ip address in the systemd-network conf file 491 for (const auto& intf : interfaces) 492 { 493 intf.second->writeConfigurationFile(); 494 } 495 } 496 497 void Manager::handleAdminState(std::string_view state, unsigned ifidx) 498 { 499 if (state == "initialized" || state == "linger") 500 { 501 systemdNetworkdEnabled.erase(ifidx); 502 } 503 else 504 { 505 bool managed = state != "unmanaged"; 506 systemdNetworkdEnabled.insert_or_assign(ifidx, managed); 507 if (auto it = interfacesByIdx.find(ifidx); it != interfacesByIdx.end()) 508 { 509 it->second->EthernetInterfaceIntf::nicEnabled(managed); 510 } 511 else if (auto it = intfInfo.find(ifidx); it != intfInfo.end()) 512 { 513 createInterface(it->second, managed); 514 } 515 } 516 } 517 518 } // namespace network 519 } // namespace phosphor 520