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