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