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