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