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