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 extern std::unique_ptr<Timer> reloadTimer; 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(sdbusplus::bus_t& bus, const char* objPath, 44 const std::filesystem::path& confDir) : 45 ManagerIface(bus, objPath, ManagerIface::action::defer_emit), 46 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 std::vector< 80 std::tuple<int32_t, std::string, sdbusplus::message::object_path>> 81 links; 82 try 83 { 84 auto rsp = 85 bus.new_method_call("org.freedesktop.network1", 86 "/org/freedesktop/network1", 87 "org.freedesktop.network1.Manager", "ListLinks") 88 .call(); 89 rsp.read(links); 90 } 91 catch (const sdbusplus::exception::SdBusError& e) 92 { 93 // Any failures are systemd-network not being ready 94 } 95 for (const auto& link : links) 96 { 97 unsigned ifidx = std::get<0>(link); 98 auto obj = fmt::format("/org/freedesktop/network1/link/_3{}", ifidx); 99 auto req = 100 bus.new_method_call("org.freedesktop.network1", obj.c_str(), 101 "org.freedesktop.DBus.Properties", "Get"); 102 req.append("org.freedesktop.network1.Link", "AdministrativeState"); 103 auto rsp = req.call(); 104 std::variant<std::string> val; 105 rsp.read(val); 106 handleAdminState(std::get<std::string>(val), ifidx); 107 } 108 109 std::filesystem::create_directories(confDir); 110 systemConf = std::make_unique<phosphor::network::SystemConfiguration>( 111 bus, (this->objPath / "config").str); 112 dhcpConf = std::make_unique<phosphor::network::dhcp::Configuration>( 113 bus, (this->objPath / "dhcp").str, *this); 114 } 115 116 void Manager::createInterface(const AllIntfInfo& info, bool enabled) 117 { 118 if (ignoredIntf.find(info.intf.idx) != ignoredIntf.end()) 119 { 120 return; 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 void Manager::reloadConfigs() 467 { 468 reloadTimer->restartOnce(reloadTimeout); 469 } 470 471 void Manager::doReloadConfigs() 472 { 473 for (auto& hook : reloadPreHooks) 474 { 475 try 476 { 477 hook(); 478 } 479 catch (const std::exception& ex) 480 { 481 log<level::ERR>("Failed executing reload hook, ignoring", 482 entry("ERR=%s", ex.what())); 483 } 484 } 485 reloadPreHooks.clear(); 486 try 487 { 488 auto method = bus.new_method_call(NETWORKD_BUSNAME, NETWORKD_PATH, 489 NETWORKD_INTERFACE, "Reload"); 490 bus.call_noreply(method); 491 } 492 catch (const sdbusplus::exception_t& ex) 493 { 494 log<level::ERR>("Failed to reload configuration", 495 entry("ERR=%s", ex.what())); 496 reloadPostHooks.clear(); 497 } 498 for (auto& hook : reloadPostHooks) 499 { 500 try 501 { 502 hook(); 503 } 504 catch (const std::exception& ex) 505 { 506 log<level::ERR>("Failed executing reload hook, ignoring", 507 entry("ERR=%s", ex.what())); 508 } 509 } 510 reloadPostHooks.clear(); 511 } 512 513 void Manager::handleAdminState(std::string_view state, unsigned ifidx) 514 { 515 if (state == "initialized" || state == "linger") 516 { 517 systemdNetworkdEnabled.erase(ifidx); 518 } 519 else 520 { 521 bool managed = state != "unmanaged"; 522 systemdNetworkdEnabled.insert_or_assign(ifidx, managed); 523 if (auto it = interfacesByIdx.find(ifidx); it != interfacesByIdx.end()) 524 { 525 it->second->EthernetInterfaceIntf::nicEnabled(managed); 526 } 527 else if (auto it = intfInfo.find(ifidx); it != intfInfo.end()) 528 { 529 createInterface(it->second, managed); 530 } 531 } 532 } 533 534 } // namespace network 535 } // namespace phosphor 536