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