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