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