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