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