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