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 <filesystem>
14 #include <phosphor-logging/elog-errors.hpp>
15 #include <phosphor-logging/log.hpp>
16 #include <sdbusplus/message.hpp>
17 #include <xyz/openbmc_project/Common/error.hpp>
18 
19 constexpr char SYSTEMD_BUSNAME[] = "org.freedesktop.systemd1";
20 constexpr char SYSTEMD_PATH[] = "/org/freedesktop/systemd1";
21 constexpr char SYSTEMD_INTERFACE[] = "org.freedesktop.systemd1.Manager";
22 
23 constexpr char NETWORKD_BUSNAME[] = "org.freedesktop.network1";
24 constexpr char NETWORKD_PATH[] = "/org/freedesktop/network1";
25 constexpr char NETWORKD_INTERFACE[] = "org.freedesktop.network1.Manager";
26 
27 namespace phosphor
28 {
29 namespace network
30 {
31 
32 using namespace phosphor::logging;
33 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
34 using Argument = xyz::openbmc_project::Common::InvalidArgument;
35 
36 static constexpr const char enabledMatch[] =
37     "type='signal',sender='org.freedesktop.network1',path_namespace='/org/"
38     "freedesktop/network1/"
39     "link',interface='org.freedesktop.DBus.Properties',member='"
40     "PropertiesChanged',arg0='org.freedesktop.network1.Link',";
41 
42 Manager::Manager(sdbusplus::bus_t& bus, DelayedExecutor& reload,
43                  stdplus::zstring_view objPath,
44                  const std::filesystem::path& confDir) :
45     ManagerIface(bus, objPath.c_str(), ManagerIface::action::defer_emit),
46     reload(reload), bus(bus), objPath(std::string(objPath)), confDir(confDir),
47     systemdNetworkdEnabledMatch(
48         bus, enabledMatch, [&](sdbusplus::message_t& m) {
49             std::string intf;
50             std::unordered_map<std::string, std::variant<std::string>> values;
51             try
52             {
53                 m.read(intf, values);
54                 auto it = values.find("AdministrativeState");
55                 if (it == values.end())
56                 {
57                     return;
58                 }
59                 const std::string_view obj = m.get_path();
60                 auto sep = obj.rfind('/');
61                 if (sep == obj.npos || sep + 3 > obj.size())
62                 {
63                     throw std::invalid_argument("Invalid obj path");
64                 }
65                 auto ifidx = DecodeInt<unsigned, 10>{}(obj.substr(sep + 3));
66                 const auto& state = std::get<std::string>(it->second);
67                 handleAdminState(state, ifidx);
68             }
69             catch (const std::exception& e)
70             {
71                 log<level::ERR>(
72                     fmt::format("AdministrativeState match parsing failed: {}",
73                                 e.what())
74                         .c_str(),
75                     entry("ERROR=%s", e.what()));
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                 log<level::ERR>("Failed executing reload hook, ignoring",
89                                 entry("ERR=%s", ex.what()));
90             }
91         }
92         reloadPreHooks.clear();
93         try
94         {
95             auto method = bus.new_method_call(NETWORKD_BUSNAME, NETWORKD_PATH,
96                                               NETWORKD_INTERFACE, "Reload");
97             bus.call_noreply(method);
98             log<level::INFO>("Reloaded systemd-networkd");
99         }
100         catch (const sdbusplus::exception_t& ex)
101         {
102             log<level::ERR>("Failed to reload configuration",
103                             entry("ERR=%s", ex.what()));
104             reloadPostHooks.clear();
105         }
106         for (auto& hook : reloadPostHooks)
107         {
108             try
109             {
110                 hook();
111             }
112             catch (const std::exception& ex)
113             {
114                 log<level::ERR>("Failed executing reload hook, ignoring",
115                                 entry("ERR=%s", ex.what()));
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 =
126             bus.new_method_call("org.freedesktop.network1",
127                                 "/org/freedesktop/network1",
128                                 "org.freedesktop.network1.Manager", "ListLinks")
129                 .call();
130         rsp.read(links);
131     }
132     catch (const sdbusplus::exception::SdBusError& e)
133     {
134         // Any failures are systemd-network not being ready
135     }
136     for (const auto& link : links)
137     {
138         unsigned ifidx = std::get<0>(link);
139         auto obj = fmt::format("/org/freedesktop/network1/link/_3{}", ifidx);
140         auto req =
141             bus.new_method_call("org.freedesktop.network1", obj.c_str(),
142                                 "org.freedesktop.DBus.Properties", "Get");
143         req.append("org.freedesktop.network1.Link", "AdministrativeState");
144         auto rsp = req.call();
145         std::variant<std::string> val;
146         rsp.read(val);
147         handleAdminState(std::get<std::string>(val), ifidx);
148     }
149 
150     std::filesystem::create_directories(confDir);
151     systemConf = std::make_unique<phosphor::network::SystemConfiguration>(
152         bus, (this->objPath / "config").str);
153     dhcpConf = std::make_unique<phosphor::network::dhcp::Configuration>(
154         bus, (this->objPath / "dhcp").str, *this);
155 }
156 
157 void Manager::createInterface(const AllIntfInfo& info, bool enabled)
158 {
159     if (ignoredIntf.find(info.intf.idx) != ignoredIntf.end())
160     {
161         return;
162     }
163     if (auto it = interfacesByIdx.find(info.intf.idx);
164         it != interfacesByIdx.end())
165     {
166         if (info.intf.name && *info.intf.name != it->second->interfaceName())
167         {
168             interfaces.erase(it->second->interfaceName());
169             interfacesByIdx.erase(it);
170         }
171         else
172         {
173             it->second->updateInfo(info.intf);
174             return;
175         }
176     }
177     else if (info.intf.name)
178     {
179         auto it = interfaces.find(*info.intf.name);
180         if (it != interfaces.end())
181         {
182             it->second->updateInfo(info.intf);
183             return;
184         }
185     }
186     if (!info.intf.name)
187     {
188         auto msg = fmt::format("Can't create interface without name: {}",
189                                info.intf.idx);
190         log<level::ERR>(msg.c_str(), entry("IFIDX=%u", 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                 auto msg = fmt::format("Ignoring interface {}\n", *info.name);
220                 log<level::INFO>(msg.c_str());
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         auto msg = fmt::format("Interface `{}` not found for gw", ifidx);
403         log<level::ERR>(msg.c_str(), entry("IFIDX=%u", 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() ==
440                             std::to_string(addr))
441                         {
442                             it->second->EthernetInterfaceIntf::defaultGateway(
443                                 "");
444                         }
445                     }
446                     else if constexpr (std::is_same_v<in6_addr, decltype(addr)>)
447                     {
448                         if (it->second->defaultGateway6() ==
449                             std::to_string(addr))
450                         {
451                             it->second->EthernetInterfaceIntf::defaultGateway6(
452                                 "");
453                         }
454                     }
455                     else
456                     {
457                         static_assert(!std::is_same_v<void, decltype(addr)>);
458                     }
459                 },
460                 addr);
461         }
462     }
463 }
464 
465 ObjectPath Manager::vlan(std::string interfaceName, uint32_t id)
466 {
467     if (id == 0 || id >= 4095)
468     {
469         log<level::ERR>("VLAN ID is not valid", entry("VLANID=%u", id));
470         elog<InvalidArgument>(
471             Argument::ARGUMENT_NAME("VLANId"),
472             Argument::ARGUMENT_VALUE(std::to_string(id).c_str()));
473     }
474 
475     auto it = interfaces.find(interfaceName);
476     if (it == interfaces.end())
477     {
478         using ResourceErr =
479             phosphor::logging::xyz::openbmc_project::Common::ResourceNotFound;
480         elog<ResourceNotFound>(ResourceErr::RESOURCE(interfaceName.c_str()));
481     }
482     return it->second->createVLAN(id);
483 }
484 
485 void Manager::reset()
486 {
487     for (const auto& dirent : std::filesystem::directory_iterator(confDir))
488     {
489         std::error_code ec;
490         std::filesystem::remove(dirent.path(), ec);
491     }
492     log<level::INFO>("Network data purged.");
493 }
494 
495 // Need to merge the below function with the code which writes the
496 // config file during factory reset.
497 // TODO openbmc/openbmc#1751
498 void Manager::writeToConfigurationFile()
499 {
500     // write all the static ip address in the systemd-network conf file
501     for (const auto& intf : interfaces)
502     {
503         intf.second->writeConfigurationFile();
504     }
505 }
506 
507 void Manager::handleAdminState(std::string_view state, unsigned ifidx)
508 {
509     if (state == "initialized" || state == "linger")
510     {
511         systemdNetworkdEnabled.erase(ifidx);
512     }
513     else
514     {
515         bool managed = state != "unmanaged";
516         systemdNetworkdEnabled.insert_or_assign(ifidx, managed);
517         if (auto it = interfacesByIdx.find(ifidx); it != interfacesByIdx.end())
518         {
519             it->second->EthernetInterfaceIntf::nicEnabled(managed);
520         }
521         else if (auto it = intfInfo.find(ifidx); it != intfInfo.end())
522         {
523             createInterface(it->second, managed);
524         }
525     }
526 }
527 
528 } // namespace network
529 } // namespace phosphor
530