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