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