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 
Manager(stdplus::PinnedRef<sdbusplus::bus_t> bus,stdplus::PinnedRef<DelayedExecutor> reload,stdplus::zstring_view objPath,const std::filesystem::path & confDir)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 = stdplus::StrToInt<10, uint16_t>{}(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         lg2::error("AdministrativeState match parsing failed: {ERROR}", "ERROR",
74                    e);
75     }
76 })
77 {
__anond1a9d47b0202() 78     reload.get().setCallback([self = stdplus::PinnedRef(*this)]() {
79         for (auto& hook : self.get().reloadPreHooks)
80         {
81             try
82             {
83                 hook();
84             }
85             catch (const std::exception& ex)
86             {
87                 lg2::error("Failed executing reload hook, ignoring: {ERROR}",
88                            "ERROR", ex);
89             }
90         }
91         self.get().reloadPreHooks.clear();
92         try
93         {
94             self.get()
95                 .bus.get()
96                 .new_method_call("org.freedesktop.network1",
97                                  "/org/freedesktop/network1",
98                                  "org.freedesktop.network1.Manager", "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             self.get().reloadPostHooks.clear();
106         }
107         for (auto& hook : self.get().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         self.get().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         stdplus::ToStrHandle<stdplus::IntToStr<10, unsigned>> tsh;
142         auto obj = stdplus::strCat("/org/freedesktop/network1/link/_3"sv,
143                                    tsh(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 }
158 
createInterface(const AllIntfInfo & info,bool enabled)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 
addInterface(const InterfaceInfo & info)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 
removeInterface(const InterfaceInfo & info)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             stdplus::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 
addAddress(const AddressInfo & info)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             std::format("Interface `{}` not found for addr", info.ifidx));
305     }
306 }
307 
removeAddress(const AddressInfo & info)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 
addNeighbor(const NeighborInfo & info)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             std::format("Interface `{}` not found for neigh", info.ifidx));
339     }
340 }
341 
removeNeighbor(const NeighborInfo & info)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 
addDefGw(unsigned ifidx,stdplus::InAnyAddr addr)359 void Manager::addDefGw(unsigned ifidx, stdplus::InAnyAddr 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<stdplus::In4Addr, decltype(addr)>)
366             {
367                 it->second.defgw4.emplace(addr);
368             }
369             else
370             {
371                 static_assert(std::is_same_v<stdplus::In6Addr, decltype(addr)>);
372                 it->second.defgw6.emplace(addr);
373             }
374         },
375             addr);
376         if (auto it = interfacesByIdx.find(ifidx); it != interfacesByIdx.end())
377         {
378             std::visit(
379                 [&](auto addr) {
380                 if constexpr (std::is_same_v<stdplus::In4Addr, decltype(addr)>)
381                 {
382                     it->second->EthernetInterfaceIntf::defaultGateway(
383                         stdplus::toStr(addr));
384                 }
385                 else
386                 {
387                     static_assert(
388                         std::is_same_v<stdplus::In6Addr, decltype(addr)>);
389                     it->second->EthernetInterfaceIntf::defaultGateway6(
390                         stdplus::toStr(addr));
391                 }
392             },
393                 addr);
394         }
395     }
396     else if (!ignoredIntf.contains(ifidx))
397     {
398         lg2::error("Interface {NET_IDX} not found for gw", "NET_IDX", ifidx);
399     }
400 }
401 
removeDefGw(unsigned ifidx,stdplus::InAnyAddr addr)402 void Manager::removeDefGw(unsigned ifidx, stdplus::InAnyAddr addr)
403 {
404     if (auto it = intfInfo.find(ifidx); it != intfInfo.end())
405     {
406         std::visit(
407             [&](auto addr) {
408             if constexpr (std::is_same_v<stdplus::In4Addr, decltype(addr)>)
409             {
410                 if (it->second.defgw4 == addr)
411                 {
412                     it->second.defgw4.reset();
413                 }
414             }
415             else
416             {
417                 static_assert(std::is_same_v<stdplus::In6Addr, decltype(addr)>);
418                 if (it->second.defgw6 == addr)
419                 {
420                     it->second.defgw6.reset();
421                 }
422             }
423         },
424             addr);
425         if (auto it = interfacesByIdx.find(ifidx); it != interfacesByIdx.end())
426         {
427             std::visit(
428                 [&](auto addr) {
429                 if constexpr (std::is_same_v<stdplus::In4Addr, decltype(addr)>)
430                 {
431                     stdplus::ToStrHandle<stdplus::ToStr<stdplus::In4Addr>> tsh;
432                     if (it->second->defaultGateway() == tsh(addr))
433                     {
434                         it->second->EthernetInterfaceIntf::defaultGateway("");
435                     }
436                 }
437                 else
438                 {
439                     static_assert(
440                         std::is_same_v<stdplus::In6Addr, decltype(addr)>);
441                     stdplus::ToStrHandle<stdplus::ToStr<stdplus::In6Addr>> tsh;
442                     if (it->second->defaultGateway6() == tsh(addr))
443                     {
444                         it->second->EthernetInterfaceIntf::defaultGateway6("");
445                     }
446                 }
447             },
448                 addr);
449         }
450     }
451 }
452 
vlan(std::string interfaceName,uint32_t id)453 ObjectPath Manager::vlan(std::string interfaceName, uint32_t id)
454 {
455     if (id == 0 || id >= 4095)
456     {
457         lg2::error("VLAN ID {NET_VLAN} is not valid", "NET_VLAN", id);
458         elog<InvalidArgument>(
459             Argument::ARGUMENT_NAME("VLANId"),
460             Argument::ARGUMENT_VALUE(std::to_string(id).c_str()));
461     }
462 
463     auto it = interfaces.find(interfaceName);
464     if (it == interfaces.end())
465     {
466         using ResourceErr =
467             phosphor::logging::xyz::openbmc_project::Common::ResourceNotFound;
468         elog<ResourceNotFound>(ResourceErr::RESOURCE(interfaceName.c_str()));
469     }
470     return it->second->createVLAN(id);
471 }
472 
reset()473 void Manager::reset()
474 {
475     for (const auto& dirent : std::filesystem::directory_iterator(confDir))
476     {
477         std::error_code ec;
478         std::filesystem::remove(dirent.path(), ec);
479     }
480     lg2::info("Network data purged.");
481 }
482 
writeToConfigurationFile()483 void Manager::writeToConfigurationFile()
484 {
485     // write all the static ip address in the systemd-network conf file
486     for (const auto& intf : interfaces)
487     {
488         intf.second->writeConfigurationFile();
489     }
490 }
491 
handleAdminState(std::string_view state,unsigned ifidx)492 void Manager::handleAdminState(std::string_view state, unsigned ifidx)
493 {
494     if (state == "initialized" || state == "linger")
495     {
496         systemdNetworkdEnabled.erase(ifidx);
497     }
498     else
499     {
500         bool managed = state != "unmanaged";
501         systemdNetworkdEnabled.insert_or_assign(ifidx, managed);
502         if (auto it = interfacesByIdx.find(ifidx); it != interfacesByIdx.end())
503         {
504             it->second->EthernetInterfaceIntf::nicEnabled(managed);
505         }
506         else if (auto it = intfInfo.find(ifidx); it != intfInfo.end())
507         {
508             createInterface(it->second, managed);
509         }
510     }
511 }
512 
513 } // namespace network
514 } // namespace phosphor
515