xref: /openbmc/phosphor-networkd/src/network_manager.cpp (revision 301e8ad6ed9ef19fb3cf2b0fe0047cca6bb7fd28)
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  #include "util.hpp"
10  
11  #include <linux/if_addr.h>
12  #include <linux/neighbour.h>
13  #include <net/if.h>
14  
15  #include <filesystem>
16  #include <fstream>
17  #include <phosphor-logging/elog-errors.hpp>
18  #include <phosphor-logging/log.hpp>
19  #include <sdbusplus/message.hpp>
20  #include <xyz/openbmc_project/Common/error.hpp>
21  
22  constexpr char SYSTEMD_BUSNAME[] = "org.freedesktop.systemd1";
23  constexpr char SYSTEMD_PATH[] = "/org/freedesktop/systemd1";
24  constexpr char SYSTEMD_INTERFACE[] = "org.freedesktop.systemd1.Manager";
25  constexpr auto FirstBootFile = "/var/lib/network/firstBoot_";
26  
27  constexpr char NETWORKD_BUSNAME[] = "org.freedesktop.network1";
28  constexpr char NETWORKD_PATH[] = "/org/freedesktop/network1";
29  constexpr char NETWORKD_INTERFACE[] = "org.freedesktop.network1.Manager";
30  
31  namespace phosphor
32  {
33  namespace network
34  {
35  
36  extern std::unique_ptr<Timer> refreshObjectTimer;
37  extern std::unique_ptr<Timer> reloadTimer;
38  using namespace phosphor::logging;
39  using namespace sdbusplus::xyz::openbmc_project::Common::Error;
40  using Argument = xyz::openbmc_project::Common::InvalidArgument;
41  
42  static constexpr const char enabledMatch[] =
43      "type='signal',sender='org.freedesktop.network1',path_namespace='/org/"
44      "freedesktop/network1/"
45      "link',interface='org.freedesktop.DBus.Properties',member='"
46      "PropertiesChanged',arg0='org.freedesktop.network1.Link',";
47  
48  Manager::Manager(sdbusplus::bus_t& bus, const char* objPath,
49                   const fs::path& confDir) :
50      details::VLANCreateIface(bus, objPath,
51                               details::VLANCreateIface::action::defer_emit),
52      bus(bus), objectPath(objPath),
53      systemdNetworkdEnabledMatch(
54          bus, enabledMatch, [&](sdbusplus::message_t& m) {
55              std::string intf;
56              std::unordered_map<std::string, std::variant<std::string>> values;
57              try
58              {
59                  m.read(intf, values);
60                  auto it = values.find("AdministrativeState");
61                  if (it == values.end())
62                  {
63                      return;
64                  }
65                  const std::string_view obj = m.get_path();
66                  auto sep = obj.rfind('/');
67                  if (sep == obj.npos || sep + 3 > obj.size())
68                  {
69                      throw std::invalid_argument("Invalid obj path");
70                  }
71                  auto ifidx = DecodeInt<unsigned, 10>{}(obj.substr(sep + 3));
72                  const auto& state = std::get<std::string>(it->second);
73                  handleAdminState(state, ifidx);
74              }
75              catch (const std::exception& e)
76              {
77                  log<level::ERR>(
78                      fmt::format("AdministrativeState match parsing failed: {}",
79                                  e.what())
80                          .c_str(),
81                      entry("ERROR=%s", e.what()));
82              }
83          })
84  {
85      setConfDir(confDir);
86      std::vector<
87          std::tuple<int32_t, std::string, sdbusplus::message::object_path>>
88          links;
89      try
90      {
91          auto rsp =
92              bus.new_method_call("org.freedesktop.network1",
93                                  "/org/freedesktop/network1",
94                                  "org.freedesktop.network1.Manager", "ListLinks")
95                  .call();
96          rsp.read(links);
97      }
98      catch (const sdbusplus::exception::SdBusError& e)
99      {
100          // Any failures are systemd-network not being ready
101      }
102      for (const auto& link : links)
103      {
104          unsigned ifidx = std::get<0>(link);
105          auto obj = fmt::format("/org/freedesktop/network1/link/_3{}", ifidx);
106          auto req =
107              bus.new_method_call("org.freedesktop.network1", obj.c_str(),
108                                  "org.freedesktop.DBus.Properties", "Get");
109          req.append("org.freedesktop.network1.Link", "AdministrativeState");
110          auto rsp = req.call();
111          std::variant<std::string> val;
112          rsp.read(val);
113          handleAdminState(std::get<std::string>(val), ifidx);
114      }
115  }
116  
117  void Manager::setConfDir(const fs::path& dir)
118  {
119      confDir = dir;
120  
121      if (!fs::exists(confDir))
122      {
123          if (!fs::create_directories(confDir))
124          {
125              log<level::ERR>("Unable to create the network conf dir",
126                              entry("DIR=%s", confDir.c_str()));
127              elog<InternalFailure>();
128          }
129      }
130  }
131  
132  void Manager::createInterface(const UndiscoveredInfo& info, bool enabled)
133  {
134      removeInterface(info.intf);
135      config::Parser config(config::pathForIntfConf(confDir, *info.intf.name));
136      auto intf = std::make_unique<EthernetInterface>(
137          bus, *this, info.intf, objectPath, config, true, enabled);
138      intf->createIPAddressObjects();
139      intf->createStaticNeighborObjects();
140      intf->loadNameServers(config);
141      intf->loadNTPServers(config);
142      auto ptr = intf.get();
143      interfaces.insert_or_assign(*info.intf.name, std::move(intf));
144      interfacesByIdx.insert_or_assign(info.intf.idx, ptr);
145  }
146  
147  void Manager::addInterface(const InterfaceInfo& info)
148  {
149      if (info.flags & IFF_LOOPBACK)
150      {
151          return;
152      }
153      if (!info.name)
154      {
155          throw std::invalid_argument("Interface missing name");
156      }
157      const auto& ignored = internal::getIgnoredInterfaces();
158      if (ignored.find(*info.name) != ignored.end())
159      {
160          auto msg = fmt::format("Ignoring interface {}\n", *info.name);
161          log<level::INFO>(msg.c_str());
162          return;
163      }
164  
165      auto it = systemdNetworkdEnabled.find(info.idx);
166      if (it != systemdNetworkdEnabled.end())
167      {
168          createInterface({info}, it->second);
169      }
170      else
171      {
172          undiscoveredIntfInfo.insert_or_assign(
173              info.idx, UndiscoveredInfo{std::move(info)});
174      }
175  }
176  
177  void Manager::removeInterface(const InterfaceInfo& info)
178  {
179      auto iit = interfacesByIdx.find(info.idx);
180      auto nit = interfaces.end();
181      if (info.name)
182      {
183          nit = interfaces.find(*info.name);
184          if (nit != interfaces.end() && iit != interfacesByIdx.end() &&
185              nit->second.get() != iit->second)
186          {
187              fmt::print(stderr, "Removed interface desync detected\n");
188              fflush(stderr);
189              std::abort();
190          }
191      }
192      else if (iit != interfacesByIdx.end())
193      {
194          for (nit = interfaces.begin(); nit != interfaces.end(); ++nit)
195          {
196              if (nit->second.get() == iit->second)
197              {
198                  break;
199              }
200          }
201      }
202  
203      if (iit != interfacesByIdx.end())
204      {
205          interfacesByIdx.erase(iit);
206      }
207      else
208      {
209          undiscoveredIntfInfo.erase(info.idx);
210      }
211      if (nit != interfaces.end())
212      {
213          interfaces.erase(nit);
214      }
215  }
216  
217  void Manager::addAddress(const AddressInfo& info)
218  {
219      if (info.flags & IFA_F_DEPRECATED)
220      {
221          return;
222      }
223      if (auto it = interfacesByIdx.find(info.ifidx); it != interfacesByIdx.end())
224      {
225          it->second->addAddr(info);
226      }
227      else if (auto it = undiscoveredIntfInfo.find(info.ifidx);
228               it != undiscoveredIntfInfo.end())
229      {
230          it->second.addrs.insert_or_assign(info.ifaddr, info);
231      }
232      else
233      {
234          throw std::runtime_error(
235              fmt::format("Interface `{}` not found for addr", info.ifidx));
236      }
237  }
238  
239  void Manager::removeAddress(const AddressInfo& info)
240  {
241      if (auto it = interfacesByIdx.find(info.ifidx); it != interfacesByIdx.end())
242      {
243          it->second->addrs.erase(info.ifaddr);
244      }
245      else if (auto it = undiscoveredIntfInfo.find(info.ifidx);
246               it != undiscoveredIntfInfo.end())
247      {
248          it->second.addrs.erase(info.ifaddr);
249      }
250  }
251  
252  void Manager::addNeighbor(const NeighborInfo& info)
253  {
254      if (!(info.state & NUD_PERMANENT) || !info.addr)
255      {
256          return;
257      }
258      if (auto it = interfacesByIdx.find(info.ifidx); it != interfacesByIdx.end())
259      {
260          it->second->addStaticNeigh(info);
261      }
262      else if (auto it = undiscoveredIntfInfo.find(info.ifidx);
263               it != undiscoveredIntfInfo.end())
264      {
265          it->second.staticNeighs.insert_or_assign(*info.addr, info);
266      }
267      else
268      {
269          throw std::runtime_error(
270              fmt::format("Interface `{}` not found for neigh", info.ifidx));
271      }
272  }
273  
274  void Manager::removeNeighbor(const NeighborInfo& info)
275  {
276      if (!info.addr)
277      {
278          return;
279      }
280      if (auto it = interfacesByIdx.find(info.ifidx); it != interfacesByIdx.end())
281      {
282          it->second->staticNeighbors.erase(*info.addr);
283      }
284      else if (auto it = undiscoveredIntfInfo.find(info.ifidx);
285               it != undiscoveredIntfInfo.end())
286      {
287          it->second.staticNeighs.erase(*info.addr);
288      }
289  }
290  
291  void Manager::addDefGw(unsigned ifidx, InAddrAny addr)
292  {
293      if (auto it = interfacesByIdx.find(ifidx); it != interfacesByIdx.end())
294      {
295          std::visit(
296              [&](auto addr) {
297                  if constexpr (std::is_same_v<in_addr, decltype(addr)>)
298                  {
299                      it->second->EthernetInterfaceIntf::defaultGateway(
300                          std::to_string(addr));
301                  }
302                  else if constexpr (std::is_same_v<in6_addr, decltype(addr)>)
303                  {
304                      it->second->EthernetInterfaceIntf::defaultGateway6(
305                          std::to_string(addr));
306                  }
307                  else
308                  {
309                      static_assert(!std::is_same_v<void, decltype(addr)>);
310                  }
311              },
312              addr);
313      }
314      else if (auto it = undiscoveredIntfInfo.find(ifidx);
315               it != undiscoveredIntfInfo.end())
316      {
317          std::visit(
318              [&](auto addr) {
319                  if constexpr (std::is_same_v<in_addr, decltype(addr)>)
320                  {
321                      it->second.defgw4.emplace(addr);
322                  }
323                  else if constexpr (std::is_same_v<in6_addr, decltype(addr)>)
324                  {
325                      it->second.defgw6.emplace(addr);
326                  }
327                  else
328                  {
329                      static_assert(!std::is_same_v<void, decltype(addr)>);
330                  }
331              },
332              addr);
333      }
334      else
335      {
336          auto msg = fmt::format("Interface `{}` not found for gw", ifidx);
337          log<level::ERR>(msg.c_str(), entry("IFIDX=%u", ifidx));
338      }
339  }
340  
341  void Manager::removeDefGw(unsigned ifidx, InAddrAny addr)
342  {
343      if (auto it = interfacesByIdx.find(ifidx); it != interfacesByIdx.end())
344      {
345          std::visit(
346              [&](auto addr) {
347                  if constexpr (std::is_same_v<in_addr, decltype(addr)>)
348                  {
349                      if (it->second->defaultGateway() == std::to_string(addr))
350                      {
351                          it->second->EthernetInterfaceIntf::defaultGateway("");
352                      }
353                  }
354                  else if constexpr (std::is_same_v<in6_addr, decltype(addr)>)
355                  {
356                      if (it->second->defaultGateway6() == std::to_string(addr))
357                      {
358                          it->second->EthernetInterfaceIntf::defaultGateway6("");
359                      }
360                  }
361                  else
362                  {
363                      static_assert(!std::is_same_v<void, decltype(addr)>);
364                  }
365              },
366              addr);
367      }
368      else if (auto it = undiscoveredIntfInfo.find(ifidx);
369               it != undiscoveredIntfInfo.end())
370      {
371          std::visit(
372              [&](auto addr) {
373                  if constexpr (std::is_same_v<in_addr, decltype(addr)>)
374                  {
375                      if (it->second.defgw4 == addr)
376                      {
377                          it->second.defgw4.reset();
378                      }
379                  }
380                  else if constexpr (std::is_same_v<in6_addr, decltype(addr)>)
381                  {
382                      if (it->second.defgw6 == addr)
383                      {
384                          it->second.defgw6.reset();
385                      }
386                  }
387                  else
388                  {
389                      static_assert(!std::is_same_v<void, decltype(addr)>);
390                  }
391              },
392              addr);
393      }
394  }
395  
396  void Manager::createInterfaces()
397  {
398      // clear all the interfaces first
399      interfaces.clear();
400      interfacesByIdx.clear();
401      for (auto& info : system::getInterfaces())
402      {
403          addInterface(info);
404      }
405  }
406  
407  void Manager::createChildObjects()
408  {
409      routeTable.refresh();
410  
411      // creates the ethernet interface dbus object.
412      createInterfaces();
413  
414      systemConf.reset(nullptr);
415      dhcpConf.reset(nullptr);
416  
417      fs::path objPath = objectPath;
418      objPath /= "config";
419  
420      // create the system conf object.
421      systemConf = std::make_unique<phosphor::network::SystemConfiguration>(
422          bus, objPath.string());
423      // create the dhcp conf object.
424      objPath /= "dhcp";
425      dhcpConf = std::make_unique<phosphor::network::dhcp::Configuration>(
426          bus, objPath.string(), *this);
427  }
428  
429  ObjectPath Manager::vlan(std::string interfaceName, uint32_t id)
430  {
431      if (id == 0 || id >= 4095)
432      {
433          log<level::ERR>("VLAN ID is not valid", entry("VLANID=%u", id));
434          elog<InvalidArgument>(
435              Argument::ARGUMENT_NAME("VLANId"),
436              Argument::ARGUMENT_VALUE(std::to_string(id).c_str()));
437      }
438  
439      auto it = interfaces.find(interfaceName);
440      if (it == interfaces.end())
441      {
442          using ResourceErr =
443              phosphor::logging::xyz::openbmc_project::Common::ResourceNotFound;
444          elog<ResourceNotFound>(ResourceErr::RESOURCE(interfaceName.c_str()));
445      }
446      return it->second->createVLAN(id);
447  }
448  
449  void Manager::reset()
450  {
451      if (fs::is_directory(confDir))
452      {
453          for (const auto& file : fs::directory_iterator(confDir))
454          {
455              fs::remove(file.path());
456          }
457      }
458      log<level::INFO>("Network Factory Reset queued.");
459  }
460  
461  // Need to merge the below function with the code which writes the
462  // config file during factory reset.
463  // TODO openbmc/openbmc#1751
464  void Manager::writeToConfigurationFile()
465  {
466      // write all the static ip address in the systemd-network conf file
467      for (const auto& intf : interfaces)
468      {
469          intf.second->writeConfigurationFile();
470      }
471  }
472  
473  #ifdef SYNC_MAC_FROM_INVENTORY
474  void Manager::setFistBootMACOnInterface(
475      const std::pair<std::string, std::string>& inventoryEthPair)
476  {
477      for (const auto& interface : interfaces)
478      {
479          if (interface.first == inventoryEthPair.first)
480          {
481              auto returnMAC =
482                  interface.second->macAddress(inventoryEthPair.second);
483              if (returnMAC == inventoryEthPair.second)
484              {
485                  log<level::INFO>("Set the MAC on "),
486                      entry("interface : ", interface.first.c_str()),
487                      entry("MAC : ", inventoryEthPair.second.c_str());
488                  std::error_code ec;
489                  if (std::filesystem::is_directory("/var/lib/network", ec))
490                  {
491                      std::ofstream persistentFile(FirstBootFile +
492                                                   interface.first);
493                  }
494                  break;
495              }
496              else
497              {
498                  log<level::INFO>("MAC is Not Set on ethernet Interface");
499              }
500          }
501      }
502  }
503  
504  #endif
505  
506  void Manager::reloadConfigsNoRefresh()
507  {
508      reloadTimer->restartOnce(reloadTimeout);
509  }
510  
511  void Manager::reloadConfigs()
512  {
513      reloadConfigsNoRefresh();
514      // Ensure that the next refresh happens after reconfiguration
515      refreshObjectTimer->setRemaining(reloadTimeout + refreshTimeout);
516  }
517  
518  void Manager::doReloadConfigs()
519  {
520      for (auto& hook : reloadPreHooks)
521      {
522          try
523          {
524              hook();
525          }
526          catch (const std::exception& ex)
527          {
528              log<level::ERR>("Failed executing reload hook, ignoring",
529                              entry("ERR=%s", ex.what()));
530          }
531      }
532      reloadPreHooks.clear();
533      try
534      {
535          auto method = bus.new_method_call(NETWORKD_BUSNAME, NETWORKD_PATH,
536                                            NETWORKD_INTERFACE, "Reload");
537          bus.call_noreply(method);
538      }
539      catch (const sdbusplus::exception_t& ex)
540      {
541          log<level::ERR>("Failed to reload configuration",
542                          entry("ERR=%s", ex.what()));
543          elog<InternalFailure>();
544      }
545      // Ensure reconfiguration has enough time
546      if (refreshObjectTimer->isEnabled())
547      {
548          refreshObjectTimer->setRemaining(refreshTimeout);
549      }
550  }
551  
552  void Manager::handleAdminState(std::string_view state, unsigned ifidx)
553  {
554      if (state == "initialized" || state == "linger")
555      {
556          systemdNetworkdEnabled.erase(ifidx);
557      }
558      else
559      {
560          bool managed = state != "unmanaged";
561          systemdNetworkdEnabled.insert_or_assign(ifidx, managed);
562          if (auto it = undiscoveredIntfInfo.find(ifidx);
563              it != undiscoveredIntfInfo.end())
564          {
565              auto info = std::move(it->second);
566              undiscoveredIntfInfo.erase(it);
567              createInterface(info, managed);
568          }
569          else if (auto it = interfacesByIdx.find(ifidx);
570                   it != interfacesByIdx.end())
571          {
572              it->second->EthernetInterfaceIntf::nicEnabled(managed);
573          }
574      }
575  }
576  
577  } // namespace network
578  } // namespace phosphor
579