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