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