xref: /openbmc/phosphor-networkd/src/network_manager.cpp (revision aac4b22fc42e3037832f2df0b25e393e134ea707)
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