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 10 #include <filesystem> 11 #include <fstream> 12 #include <phosphor-logging/elog-errors.hpp> 13 #include <phosphor-logging/log.hpp> 14 #include <sdbusplus/message.hpp> 15 #include <xyz/openbmc_project/Common/error.hpp> 16 17 constexpr char SYSTEMD_BUSNAME[] = "org.freedesktop.systemd1"; 18 constexpr char SYSTEMD_PATH[] = "/org/freedesktop/systemd1"; 19 constexpr char SYSTEMD_INTERFACE[] = "org.freedesktop.systemd1.Manager"; 20 constexpr auto FirstBootFile = "/var/lib/network/firstBoot_"; 21 22 constexpr char NETWORKD_BUSNAME[] = "org.freedesktop.network1"; 23 constexpr char NETWORKD_PATH[] = "/org/freedesktop/network1"; 24 constexpr char NETWORKD_INTERFACE[] = "org.freedesktop.network1.Manager"; 25 26 namespace phosphor 27 { 28 namespace network 29 { 30 31 extern std::unique_ptr<Timer> refreshObjectTimer; 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 fs::path& confDir) : 45 details::VLANCreateIface(bus, objPath, 46 details::VLANCreateIface::action::defer_emit), 47 bus(bus), objectPath(objPath), 48 systemdNetworkdEnabledMatch( 49 bus, enabledMatch, [&](sdbusplus::message_t& m) { 50 std::string intf; 51 std::unordered_map<std::string, std::variant<std::string>> values; 52 try 53 { 54 m.read(intf, values); 55 auto it = values.find("AdministrativeState"); 56 if (it == values.end()) 57 { 58 return; 59 } 60 const std::string_view obj = m.get_path(); 61 auto sep = obj.rfind('/'); 62 if (sep == obj.npos || sep + 3 > obj.size()) 63 { 64 throw std::invalid_argument("Invalid obj path"); 65 } 66 auto ifidx = DecodeInt<unsigned, 10>{}(obj.substr(sep + 3)); 67 const auto& state = std::get<std::string>(it->second); 68 handleAdminState(state, ifidx); 69 } 70 catch (const std::exception& e) 71 { 72 log<level::ERR>( 73 fmt::format("AdministrativeState match parsing failed: {}", 74 e.what()) 75 .c_str(), 76 entry("ERROR=%s", e.what())); 77 } 78 }) 79 { 80 setConfDir(confDir); 81 std::vector< 82 std::tuple<int32_t, std::string, sdbusplus::message::object_path>> 83 links; 84 try 85 { 86 auto rsp = 87 bus.new_method_call("org.freedesktop.network1", 88 "/org/freedesktop/network1", 89 "org.freedesktop.network1.Manager", "ListLinks") 90 .call(); 91 rsp.read(links); 92 } 93 catch (const sdbusplus::exception::SdBusError& e) 94 { 95 // Any failures are systemd-network not being ready 96 } 97 for (const auto& link : links) 98 { 99 unsigned ifidx = std::get<0>(link); 100 auto obj = fmt::format("/org/freedesktop/network1/link/_3{}", ifidx); 101 auto req = 102 bus.new_method_call("org.freedesktop.network1", obj.c_str(), 103 "org.freedesktop.DBus.Properties", "Get"); 104 req.append("org.freedesktop.network1.Link", "AdministrativeState"); 105 auto rsp = req.call(); 106 std::variant<std::string> val; 107 rsp.read(val); 108 handleAdminState(std::get<std::string>(val), ifidx); 109 } 110 } 111 112 void Manager::setConfDir(const fs::path& dir) 113 { 114 confDir = dir; 115 116 if (!fs::exists(confDir)) 117 { 118 if (!fs::create_directories(confDir)) 119 { 120 log<level::ERR>("Unable to create the network conf dir", 121 entry("DIR=%s", confDir.c_str())); 122 elog<InternalFailure>(); 123 } 124 } 125 } 126 127 void Manager::addInterface(InterfaceInfo& info, bool enabled) 128 { 129 config::Parser config(config::pathForIntfConf(confDir, *info.name)); 130 auto intf = std::make_unique<EthernetInterface>( 131 bus, *this, info, objectPath, config, true, enabled); 132 intf->createIPAddressObjects(); 133 intf->createStaticNeighborObjects(); 134 intf->loadNameServers(config); 135 intf->loadNTPServers(config); 136 auto ptr = intf.get(); 137 interfaces.emplace(std::move(*info.name), std::move(intf)); 138 interfacesByIdx.emplace(info.idx, ptr); 139 } 140 141 inline void getIntfOrLog(const decltype(Manager::interfacesByIdx)& intfs, 142 unsigned idx, auto&& cb) 143 { 144 auto it = intfs.find(idx); 145 if (it == intfs.end()) 146 { 147 auto msg = fmt::format("Interface `{}` not found", idx); 148 log<level::ERR>(msg.c_str(), entry("IFIDX=%u", idx)); 149 return; 150 } 151 cb(*it->second); 152 } 153 154 void Manager::addAddress(const AddressInfo& info) 155 { 156 getIntfOrLog(interfacesByIdx, info.ifidx, 157 [&](auto& intf) { intf.addAddr(info); }); 158 } 159 160 void Manager::removeAddress(const AddressInfo& info) 161 { 162 getIntfOrLog(interfacesByIdx, info.ifidx, 163 [&](auto& intf) { intf.addrs.erase(info.ifaddr); }); 164 } 165 166 void Manager::addNeighbor(const NeighborInfo& info) 167 { 168 getIntfOrLog(interfacesByIdx, info.ifidx, 169 [&](auto& intf) { intf.addStaticNeigh(info); }); 170 } 171 172 void Manager::removeNeighbor(const NeighborInfo& info) 173 { 174 if (info.addr) 175 { 176 getIntfOrLog(interfacesByIdx, info.ifidx, [&](auto& intf) { 177 intf.staticNeighbors.erase(*info.addr); 178 }); 179 } 180 } 181 182 void Manager::addDefGw(unsigned ifidx, InAddrAny addr) 183 { 184 getIntfOrLog(interfacesByIdx, ifidx, [&](auto& intf) { 185 std::visit( 186 [&](auto addr) { 187 if constexpr (std::is_same_v<in_addr, decltype(addr)>) 188 { 189 intf.EthernetInterfaceIntf::defaultGateway( 190 std::to_string(addr)); 191 } 192 else if constexpr (std::is_same_v<in6_addr, decltype(addr)>) 193 { 194 intf.EthernetInterfaceIntf::defaultGateway6( 195 std::to_string(addr)); 196 } 197 else 198 { 199 static_assert(!std::is_same_v<void, decltype(addr)>); 200 } 201 }, 202 addr); 203 }); 204 } 205 206 void Manager::removeDefGw(unsigned ifidx, InAddrAny addr) 207 { 208 getIntfOrLog(interfacesByIdx, ifidx, [&](auto& intf) { 209 std::visit( 210 [&](auto addr) { 211 if constexpr (std::is_same_v<in_addr, decltype(addr)>) 212 { 213 if (intf.defaultGateway() == std::to_string(addr)) 214 { 215 intf.EthernetInterfaceIntf::defaultGateway(""); 216 } 217 } 218 else if constexpr (std::is_same_v<in6_addr, decltype(addr)>) 219 { 220 if (intf.defaultGateway6() == std::to_string(addr)) 221 { 222 intf.EthernetInterfaceIntf::defaultGateway6(""); 223 } 224 } 225 else 226 { 227 static_assert(!std::is_same_v<void, decltype(addr)>); 228 } 229 }, 230 addr); 231 }); 232 } 233 234 void Manager::createInterfaces() 235 { 236 // clear all the interfaces first 237 interfaces.clear(); 238 interfacesByIdx.clear(); 239 for (auto& info : system::getInterfaces()) 240 { 241 auto it = systemdNetworkdEnabled.find(info.idx); 242 if (it != systemdNetworkdEnabled.end()) 243 { 244 addInterface(info, it->second); 245 } 246 else 247 { 248 undiscoveredIntfInfo.insert_or_assign(info.idx, std::move(info)); 249 } 250 } 251 } 252 253 void Manager::createChildObjects() 254 { 255 routeTable.refresh(); 256 257 // creates the ethernet interface dbus object. 258 createInterfaces(); 259 260 systemConf.reset(nullptr); 261 dhcpConf.reset(nullptr); 262 263 fs::path objPath = objectPath; 264 objPath /= "config"; 265 266 // create the system conf object. 267 systemConf = std::make_unique<phosphor::network::SystemConfiguration>( 268 bus, objPath.string()); 269 // create the dhcp conf object. 270 objPath /= "dhcp"; 271 dhcpConf = std::make_unique<phosphor::network::dhcp::Configuration>( 272 bus, objPath.string(), *this); 273 } 274 275 ObjectPath Manager::vlan(std::string interfaceName, uint32_t id) 276 { 277 if (id == 0 || id >= 4095) 278 { 279 log<level::ERR>("VLAN ID is not valid", entry("VLANID=%u", id)); 280 elog<InvalidArgument>( 281 Argument::ARGUMENT_NAME("VLANId"), 282 Argument::ARGUMENT_VALUE(std::to_string(id).c_str())); 283 } 284 285 auto it = interfaces.find(interfaceName); 286 if (it == interfaces.end()) 287 { 288 using ResourceErr = 289 phosphor::logging::xyz::openbmc_project::Common::ResourceNotFound; 290 elog<ResourceNotFound>(ResourceErr::RESOURCE(interfaceName.c_str())); 291 } 292 return it->second->createVLAN(id); 293 } 294 295 void Manager::reset() 296 { 297 if (fs::is_directory(confDir)) 298 { 299 for (const auto& file : fs::directory_iterator(confDir)) 300 { 301 fs::remove(file.path()); 302 } 303 } 304 log<level::INFO>("Network Factory Reset queued."); 305 } 306 307 // Need to merge the below function with the code which writes the 308 // config file during factory reset. 309 // TODO openbmc/openbmc#1751 310 void Manager::writeToConfigurationFile() 311 { 312 // write all the static ip address in the systemd-network conf file 313 for (const auto& intf : interfaces) 314 { 315 intf.second->writeConfigurationFile(); 316 } 317 } 318 319 #ifdef SYNC_MAC_FROM_INVENTORY 320 void Manager::setFistBootMACOnInterface( 321 const std::pair<std::string, std::string>& inventoryEthPair) 322 { 323 for (const auto& interface : interfaces) 324 { 325 if (interface.first == inventoryEthPair.first) 326 { 327 auto returnMAC = 328 interface.second->macAddress(inventoryEthPair.second); 329 if (returnMAC == inventoryEthPair.second) 330 { 331 log<level::INFO>("Set the MAC on "), 332 entry("interface : ", interface.first.c_str()), 333 entry("MAC : ", inventoryEthPair.second.c_str()); 334 std::error_code ec; 335 if (std::filesystem::is_directory("/var/lib/network", ec)) 336 { 337 std::ofstream persistentFile(FirstBootFile + 338 interface.first); 339 } 340 break; 341 } 342 else 343 { 344 log<level::INFO>("MAC is Not Set on ethernet Interface"); 345 } 346 } 347 } 348 } 349 350 #endif 351 352 void Manager::reloadConfigsNoRefresh() 353 { 354 reloadTimer->restartOnce(reloadTimeout); 355 } 356 357 void Manager::reloadConfigs() 358 { 359 reloadConfigsNoRefresh(); 360 // Ensure that the next refresh happens after reconfiguration 361 refreshObjectTimer->setRemaining(reloadTimeout + refreshTimeout); 362 } 363 364 void Manager::doReloadConfigs() 365 { 366 for (auto& hook : reloadPreHooks) 367 { 368 try 369 { 370 hook(); 371 } 372 catch (const std::exception& ex) 373 { 374 log<level::ERR>("Failed executing reload hook, ignoring", 375 entry("ERR=%s", ex.what())); 376 } 377 } 378 reloadPreHooks.clear(); 379 try 380 { 381 auto method = bus.new_method_call(NETWORKD_BUSNAME, NETWORKD_PATH, 382 NETWORKD_INTERFACE, "Reload"); 383 bus.call_noreply(method); 384 } 385 catch (const sdbusplus::exception_t& ex) 386 { 387 log<level::ERR>("Failed to reload configuration", 388 entry("ERR=%s", ex.what())); 389 elog<InternalFailure>(); 390 } 391 // Ensure reconfiguration has enough time 392 if (refreshObjectTimer->isEnabled()) 393 { 394 refreshObjectTimer->setRemaining(refreshTimeout); 395 } 396 } 397 398 void Manager::handleAdminState(std::string_view state, unsigned ifidx) 399 { 400 if (state == "initialized" || state == "linger") 401 { 402 systemdNetworkdEnabled.erase(ifidx); 403 } 404 else 405 { 406 bool managed = state != "unmanaged"; 407 systemdNetworkdEnabled.insert_or_assign(ifidx, managed); 408 if (auto it = undiscoveredIntfInfo.find(ifidx); 409 it != undiscoveredIntfInfo.end()) 410 { 411 auto info = std::move(it->second); 412 undiscoveredIntfInfo.erase(it); 413 addInterface(info, managed); 414 } 415 else if (auto it = interfacesByIdx.find(ifidx); 416 it != interfacesByIdx.end()) 417 { 418 it->second->EthernetInterfaceIntf::nicEnabled(managed); 419 } 420 } 421 } 422 423 } // namespace network 424 } // namespace phosphor 425