xref: /openbmc/phosphor-networkd/src/ethernet_interface.cpp (revision 970c7b60eaa16f081477965833d60401f0702d46)
1 #include "config.h"
2 
3 #include "ethernet_interface.hpp"
4 
5 #include "config_parser.hpp"
6 #include "network_manager.hpp"
7 #include "system_queries.hpp"
8 #include "util.hpp"
9 
10 #include <arpa/inet.h>
11 #include <fcntl.h>
12 #include <linux/rtnetlink.h>
13 #include <net/if.h>
14 #include <net/if_arp.h>
15 #include <sys/stat.h>
16 
17 #include <phosphor-logging/elog-errors.hpp>
18 #include <phosphor-logging/lg2.hpp>
19 #include <stdplus/fd/create.hpp>
20 #include <stdplus/raw.hpp>
21 #include <stdplus/str/cat.hpp>
22 #include <stdplus/zstring.hpp>
23 #include <xyz/openbmc_project/Common/error.hpp>
24 
25 #include <algorithm>
26 #include <filesystem>
27 #include <format>
28 #include <string>
29 #include <unordered_map>
30 #include <variant>
31 
32 namespace phosphor
33 {
34 namespace network
35 {
36 
37 using namespace phosphor::logging;
38 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
39 using NotAllowed = sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
40 using NotAllowedArgument = xyz::openbmc_project::Common::NotAllowed;
41 using Argument = xyz::openbmc_project::Common::InvalidArgument;
42 using std::literals::string_view_literals::operator""sv;
43 constexpr auto RESOLVED_SERVICE = "org.freedesktop.resolve1";
44 constexpr auto RESOLVED_INTERFACE = "org.freedesktop.resolve1.Link";
45 constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties";
46 constexpr auto RESOLVED_SERVICE_PATH = "/org/freedesktop/resolve1/link/";
47 
48 constexpr auto TIMESYNCD_SERVICE = "org.freedesktop.timesync1";
49 constexpr auto TIMESYNCD_INTERFACE = "org.freedesktop.timesync1.Manager";
50 constexpr auto TIMESYNCD_SERVICE_PATH = "/org/freedesktop/timesync1";
51 
52 constexpr auto METHOD_GET = "Get";
53 
54 template <typename Func>
ignoreError(std::string_view msg,stdplus::zstring_view intf,decltype(std::declval<Func> ()())fallback,Func && func)55 inline decltype(std::declval<Func>()()) ignoreError(
56     std::string_view msg, stdplus::zstring_view intf,
57     decltype(std::declval<Func>()()) fallback, Func&& func) noexcept
58 {
59     try
60     {
61         return func();
62     }
63     catch (const std::exception& e)
64     {
65         lg2::error("{MSG} failed on {NET_INTF}: {ERROR}", "MSG", msg,
66                    "NET_INTF", intf, "ERROR", e);
67     }
68     return fallback;
69 }
70 
makeObjPath(std::string_view root,std::string_view intf)71 static std::string makeObjPath(std::string_view root, std::string_view intf)
72 {
73     auto ret = stdplus::strCat(root, "/"sv, intf);
74     std::replace(ret.begin() + ret.size() - intf.size(), ret.end(), '.', '_');
75     return ret;
76 }
77 
78 template <typename Addr>
validIntfIP(Addr a)79 static bool validIntfIP(Addr a) noexcept
80 {
81     return a.isUnicast() && !a.isLoopback();
82 }
83 
EthernetInterface(stdplus::PinnedRef<sdbusplus::bus_t> bus,stdplus::PinnedRef<Manager> manager,const AllIntfInfo & info,std::string_view objRoot,const config::Parser & config,bool enabled)84 EthernetInterface::EthernetInterface(
85     stdplus::PinnedRef<sdbusplus::bus_t> bus,
86     stdplus::PinnedRef<Manager> manager, const AllIntfInfo& info,
87     std::string_view objRoot, const config::Parser& config, bool enabled) :
88     EthernetInterface(bus, manager, info, makeObjPath(objRoot, *info.intf.name),
89                       config, enabled)
90 {}
91 
EthernetInterface(stdplus::PinnedRef<sdbusplus::bus_t> bus,stdplus::PinnedRef<Manager> manager,const AllIntfInfo & info,std::string && objPath,const config::Parser & config,bool enabled)92 EthernetInterface::EthernetInterface(
93     stdplus::PinnedRef<sdbusplus::bus_t> bus,
94     stdplus::PinnedRef<Manager> manager, const AllIntfInfo& info,
95     std::string&& objPath, const config::Parser& config, bool enabled) :
96     Ifaces(bus, objPath.c_str(), Ifaces::action::defer_emit), manager(manager),
97     bus(bus), objPath(std::move(objPath))
98 {
99     interfaceName(*info.intf.name, true);
100     auto dhcpVal = getDHCPValue(config);
101     EthernetInterfaceIntf::dhcp4(dhcpVal.v4, true);
102     EthernetInterfaceIntf::dhcp6(dhcpVal.v6, true);
103     EthernetInterfaceIntf::ipv6AcceptRA(getIPv6AcceptRA(config), true);
104     EthernetInterfaceIntf::nicEnabled(enabled, true);
105     auto lldpVal = parseLLDPConf();
106     if (!lldpVal.empty())
107     {
108         EthernetInterfaceIntf::emitLLDP(lldpVal[interfaceName()], true);
109     }
110     EthernetInterfaceIntf::ntpServers(
111         config.map.getValueStrings("Network", "NTP"), true);
112 
113     updateInfo(info.intf, true);
114 
115     if (info.defgw4)
116     {
117         EthernetInterface::defaultGateway(stdplus::toStr(*info.defgw4), true);
118     }
119     if (info.defgw6)
120     {
121         EthernetInterface::defaultGateway6(stdplus::toStr(*info.defgw6), true);
122     }
123     emit_object_added();
124 
125     if (info.intf.vlan_id)
126     {
127         if (!info.intf.parent_idx)
128         {
129             std::runtime_error("Missing parent link");
130         }
131         vlan.emplace(bus, this->objPath.c_str(), info.intf, *this);
132     }
133     dhcp4Conf.emplace(bus, this->objPath + "/dhcp4", *this, DHCPType::v4);
134     dhcp6Conf.emplace(bus, this->objPath + "/dhcp6", *this, DHCPType::v6);
135     for (const auto& [_, addr] : info.addrs)
136     {
137         addAddr(addr);
138     }
139     for (const auto& [_, neigh] : info.staticNeighs)
140     {
141         addStaticNeigh(neigh);
142     }
143     for (const auto& [_, staticGateway] : info.staticGateways)
144     {
145         addStaticGateway(staticGateway);
146     }
147 }
148 
updateInfo(const InterfaceInfo & info,bool skipSignal)149 void EthernetInterface::updateInfo(const InterfaceInfo& info, bool skipSignal)
150 {
151     ifIdx = info.idx;
152     EthernetInterfaceIntf::linkUp(info.flags & IFF_RUNNING, skipSignal);
153     if (info.mac)
154     {
155         MacAddressIntf::macAddress(stdplus::toStr(*info.mac), skipSignal);
156     }
157     if (info.mtu)
158     {
159         EthernetInterfaceIntf::mtu(*info.mtu, skipSignal);
160     }
161     if (ifIdx > 0)
162     {
163         auto ethInfo = ignoreError("GetEthInfo", *info.name, {}, [&] {
164             return system::getEthInfo(*info.name);
165         });
166         EthernetInterfaceIntf::autoNeg(ethInfo.autoneg, skipSignal);
167         EthernetInterfaceIntf::speed(ethInfo.speed, skipSignal);
168         EthernetInterfaceIntf::fullDuplex(ethInfo.fullDuplex, skipSignal);
169     }
170 }
171 
addAddr(const AddressInfo & info)172 void EthernetInterface::addAddr(const AddressInfo& info)
173 {
174     IP::AddressOrigin origin = IP::AddressOrigin::Static;
175     if (dhcpIsEnabled(info.ifaddr.getAddr()))
176     {
177         origin = IP::AddressOrigin::DHCP;
178     }
179 
180 #ifdef LINK_LOCAL_AUTOCONFIGURATION
181     if (info.scope == RT_SCOPE_LINK)
182     {
183         origin = IP::AddressOrigin::LinkLocal;
184     }
185 #endif
186 
187     if ((info.scope == RT_SCOPE_UNIVERSE) && (info.flags & IFA_F_PERMANENT))
188     {
189         origin = IP::AddressOrigin::Static;
190     }
191     if ((info.scope == RT_SCOPE_UNIVERSE) &&
192         ((info.flags & IFA_F_NOPREFIXROUTE) &&
193          (info.flags & IFA_F_MANAGETEMPADDR)))
194     {
195         origin = IP::AddressOrigin::SLAAC;
196     }
197     else if ((info.scope == RT_SCOPE_UNIVERSE) &&
198              ((info.flags & IFA_F_NOPREFIXROUTE)))
199     {
200         origin = IP::AddressOrigin::DHCP;
201     }
202 
203     auto it = addrs.find(info.ifaddr);
204     if (it == addrs.end())
205     {
206         addrs.emplace(info.ifaddr, std::make_unique<IPAddress>(
207                                        bus, std::string_view(objPath), *this,
208                                        info.ifaddr, origin));
209     }
210     else
211     {
212         it->second->IPIfaces::origin(origin);
213     }
214 }
215 
addStaticNeigh(const NeighborInfo & info)216 void EthernetInterface::addStaticNeigh(const NeighborInfo& info)
217 {
218     if (!info.mac || !info.addr)
219     {
220         lg2::error("Missing neighbor mac on {NET_INTF}", "NET_INTF",
221                    interfaceName());
222         return;
223     }
224 
225     if (auto it = staticNeighbors.find(*info.addr); it != staticNeighbors.end())
226     {
227         it->second->NeighborObj::macAddress(stdplus::toStr(*info.mac));
228     }
229     else
230     {
231         staticNeighbors.emplace(
232             *info.addr, std::make_unique<Neighbor>(
233                             bus, std::string_view(objPath), *this, *info.addr,
234                             *info.mac, Neighbor::State::Permanent));
235     }
236 }
237 
addStaticGateway(const StaticGatewayInfo & info)238 void EthernetInterface::addStaticGateway(const StaticGatewayInfo& info)
239 {
240     if (!info.gateway)
241     {
242         lg2::error("Missing static gateway on {NET_INTF}", "NET_INTF",
243                    interfaceName());
244         return;
245     }
246 
247     IP::Protocol protocolType;
248     if (*info.protocol == "IPv4")
249     {
250         protocolType = IP::Protocol::IPv4;
251     }
252     else if (*info.protocol == "IPv6")
253     {
254         protocolType = IP::Protocol::IPv6;
255     }
256 
257     if (auto it = staticGateways.find(*info.gateway);
258         it != staticGateways.end())
259     {
260         it->second->StaticGatewayObj::gateway(*info.gateway);
261     }
262     else
263     {
264         staticGateways.emplace(*info.gateway,
265                                std::make_unique<StaticGateway>(
266                                    bus, std::string_view(objPath), *this,
267                                    *info.gateway, protocolType));
268     }
269 }
270 
ip(IP::Protocol protType,std::string ipaddress,uint8_t prefixLength,std::string)271 ObjectPath EthernetInterface::ip(IP::Protocol protType, std::string ipaddress,
272                                  uint8_t prefixLength, std::string)
273 {
274     std::optional<stdplus::InAnyAddr> addr;
275     try
276     {
277         switch (protType)
278         {
279             case IP::Protocol::IPv4:
280                 addr.emplace(stdplus::fromStr<stdplus::In4Addr>(ipaddress));
281                 break;
282             case IP::Protocol::IPv6:
283                 addr.emplace(stdplus::fromStr<stdplus::In6Addr>(ipaddress));
284                 break;
285             default:
286                 throw std::logic_error("Exhausted protocols");
287         }
288         if (!std::visit([](auto ip) { return validIntfIP(ip); }, *addr))
289         {
290             throw std::invalid_argument("not unicast");
291         }
292     }
293     catch (const std::exception& e)
294     {
295         lg2::error("Invalid IP {NET_IP}: {ERROR}", "NET_IP", ipaddress, "ERROR",
296                    e);
297         elog<InvalidArgument>(Argument::ARGUMENT_NAME("ipaddress"),
298                               Argument::ARGUMENT_VALUE(ipaddress.c_str()));
299     }
300     std::optional<stdplus::SubnetAny> ifaddr;
301     try
302     {
303         if (prefixLength == 0)
304         {
305             throw std::invalid_argument("default route");
306         }
307         ifaddr.emplace(*addr, prefixLength);
308     }
309     catch (const std::exception& e)
310     {
311         lg2::error("Invalid prefix length {NET_PFX}: {ERROR}", "NET_PFX",
312                    prefixLength, "ERROR", e);
313         elog<InvalidArgument>(
314             Argument::ARGUMENT_NAME("prefixLength"),
315             Argument::ARGUMENT_VALUE(stdplus::toStr(prefixLength).c_str()));
316     }
317 
318     auto it = addrs.find(*ifaddr);
319     if (it == addrs.end())
320     {
321         it = std::get<0>(addrs.emplace(
322             *ifaddr,
323             std::make_unique<IPAddress>(bus, std::string_view(objPath), *this,
324                                         *ifaddr, IP::AddressOrigin::Static)));
325     }
326     else
327     {
328         if (it->second->origin() == IP::AddressOrigin::Static)
329         {
330             return it->second->getObjPath();
331         }
332         it->second->IPIfaces::origin(IP::AddressOrigin::Static);
333     }
334 
335     writeConfigurationFile();
336     manager.get().reloadConfigs();
337 
338     return it->second->getObjPath();
339 }
340 
neighbor(std::string ipAddress,std::string macAddress)341 ObjectPath EthernetInterface::neighbor(std::string ipAddress,
342                                        std::string macAddress)
343 {
344     std::optional<stdplus::InAnyAddr> addr;
345     try
346     {
347         addr.emplace(stdplus::fromStr<stdplus::InAnyAddr>(ipAddress));
348     }
349     catch (const std::exception& e)
350     {
351         lg2::error("Not a valid IP address {NET_IP}: {ERROR}", "NET_IP",
352                    ipAddress, "ERROR", e);
353         elog<InvalidArgument>(Argument::ARGUMENT_NAME("ipAddress"),
354                               Argument::ARGUMENT_VALUE(ipAddress.c_str()));
355     }
356 
357     std::optional<stdplus::EtherAddr> lladdr;
358     try
359     {
360         lladdr.emplace(stdplus::fromStr<stdplus::EtherAddr>(macAddress));
361     }
362     catch (const std::exception& e)
363     {
364         lg2::error("Not a valid MAC address {NET_MAC}: {ERROR}", "NET_MAC",
365                    macAddress, "ERROR", e);
366         elog<InvalidArgument>(Argument::ARGUMENT_NAME("macAddress"),
367                               Argument::ARGUMENT_VALUE(macAddress.c_str()));
368     }
369 
370     auto it = staticNeighbors.find(*addr);
371     if (it == staticNeighbors.end())
372     {
373         it = std::get<0>(staticNeighbors.emplace(
374             *addr, std::make_unique<Neighbor>(bus, std::string_view(objPath),
375                                               *this, *addr, *lladdr,
376                                               Neighbor::State::Permanent)));
377     }
378     else
379     {
380         auto str = stdplus::toStr(*lladdr);
381         if (it->second->macAddress() == str)
382         {
383             return it->second->getObjPath();
384         }
385         it->second->NeighborObj::macAddress(str);
386     }
387 
388     writeConfigurationFile();
389     manager.get().reloadConfigs();
390 
391     return it->second->getObjPath();
392 }
393 
staticGateway(std::string gateway,IP::Protocol protocolType)394 ObjectPath EthernetInterface::staticGateway(std::string gateway,
395                                             IP::Protocol protocolType)
396 {
397     std::optional<stdplus::InAnyAddr> addr;
398     std::string route;
399     try
400     {
401         switch (protocolType)
402         {
403             case IP::Protocol::IPv4:
404                 addr.emplace(stdplus::fromStr<stdplus::In4Addr>(gateway));
405                 break;
406             case IP::Protocol::IPv6:
407                 addr.emplace(stdplus::fromStr<stdplus::In6Addr>(gateway));
408                 break;
409             default:
410                 throw std::logic_error("Exhausted protocols");
411         }
412         route = gateway;
413     }
414     catch (const std::exception& e)
415     {
416         lg2::error("Not a valid IP address {GATEWAY}: {ERROR}", "GATEWAY",
417                    gateway, "ERROR", e);
418         elog<InvalidArgument>(Argument::ARGUMENT_NAME("gateway"),
419                               Argument::ARGUMENT_VALUE(gateway.c_str()));
420     }
421 
422     auto it = staticGateways.find(route);
423     if (it == staticGateways.end())
424     {
425         it = std::get<0>(staticGateways.emplace(
426             route,
427             std::make_unique<StaticGateway>(bus, std::string_view(objPath),
428                                             *this, gateway, protocolType)));
429     }
430     else
431     {
432         it->second->StaticGatewayObj::gateway(gateway);
433     }
434 
435     writeConfigurationFile();
436     manager.get().reloadConfigs();
437 
438     return it->second->getObjPath();
439 }
440 
ipv6AcceptRA(bool value)441 bool EthernetInterface::ipv6AcceptRA(bool value)
442 {
443     if (ipv6AcceptRA() != EthernetInterfaceIntf::ipv6AcceptRA(value))
444     {
445         writeConfigurationFile();
446         manager.get().reloadConfigs();
447     }
448     return value;
449 }
450 
dhcp4(bool value)451 bool EthernetInterface::dhcp4(bool value)
452 {
453     if (dhcp4() != EthernetInterfaceIntf::dhcp4(value))
454     {
455         writeConfigurationFile();
456         manager.get().reloadConfigs();
457     }
458     return value;
459 }
460 
dhcp6(bool value)461 bool EthernetInterface::dhcp6(bool value)
462 {
463     if (dhcp6() != EthernetInterfaceIntf::dhcp6(value))
464     {
465         writeConfigurationFile();
466         manager.get().reloadConfigs();
467     }
468     return value;
469 }
470 
dhcpEnabled(DHCPConf value)471 EthernetInterface::DHCPConf EthernetInterface::dhcpEnabled(DHCPConf value)
472 {
473     auto old4 = EthernetInterfaceIntf::dhcp4();
474     auto new4 = EthernetInterfaceIntf::dhcp4(
475         value == DHCPConf::v4 || value == DHCPConf::v4v6stateless ||
476         value == DHCPConf::both);
477     auto old6 = EthernetInterfaceIntf::dhcp6();
478     auto new6 = EthernetInterfaceIntf::dhcp6(
479         value == DHCPConf::v6 || value == DHCPConf::both);
480     auto oldra = EthernetInterfaceIntf::ipv6AcceptRA();
481     auto newra = EthernetInterfaceIntf::ipv6AcceptRA(
482         value == DHCPConf::v6stateless || value == DHCPConf::v4v6stateless ||
483         value == DHCPConf::v6 || value == DHCPConf::both);
484 
485     if (old4 != new4 || old6 != new6 || oldra != newra)
486     {
487         writeConfigurationFile();
488         manager.get().reloadConfigs();
489     }
490     return value;
491 }
492 
dhcpEnabled() const493 EthernetInterface::DHCPConf EthernetInterface::dhcpEnabled() const
494 {
495     if (dhcp6())
496     {
497         return dhcp4() ? DHCPConf::both : DHCPConf::v6;
498     }
499     else if (dhcp4())
500     {
501         return ipv6AcceptRA() ? DHCPConf::v4v6stateless : DHCPConf::v4;
502     }
503     return ipv6AcceptRA() ? DHCPConf::v6stateless : DHCPConf::none;
504 }
505 
mtu(size_t value)506 size_t EthernetInterface::mtu(size_t value)
507 {
508     const size_t old = EthernetInterfaceIntf::mtu();
509     if (value == old)
510     {
511         return value;
512     }
513     const auto ifname = interfaceName();
514     return EthernetInterfaceIntf::mtu(ignoreError("SetMTU", ifname, old, [&] {
515         system::setMTU(ifname, value);
516         return value;
517     }));
518 }
519 
nicEnabled(bool value)520 bool EthernetInterface::nicEnabled(bool value)
521 {
522     if (value == EthernetInterfaceIntf::nicEnabled())
523     {
524         return value;
525     }
526 
527     EthernetInterfaceIntf::nicEnabled(value);
528     writeConfigurationFile();
529     manager.get().reloadConfigs();
530 
531     return value;
532 }
533 
staticNameServers(ServerList value)534 ServerList EthernetInterface::staticNameServers(ServerList value)
535 {
536     std::vector<std::string> dnsUniqueValues;
537     for (auto& ip : value)
538     {
539         try
540         {
541             ip = stdplus::toStr(stdplus::fromStr<stdplus::InAnyAddr>(ip));
542         }
543         catch (const std::exception& e)
544         {
545             lg2::error("Not a valid IP address {NET_IP}: {ERROR}", "NET_IP", ip,
546                        "ERROR", e);
547             elog<InvalidArgument>(Argument::ARGUMENT_NAME("StaticNameserver"),
548                                   Argument::ARGUMENT_VALUE(ip.c_str()));
549         }
550         if (std::find(dnsUniqueValues.begin(), dnsUniqueValues.end(), ip) ==
551             dnsUniqueValues.end())
552         {
553             dnsUniqueValues.push_back(ip);
554         }
555     }
556 
557     value =
558         EthernetInterfaceIntf::staticNameServers(std::move(dnsUniqueValues));
559 
560     writeConfigurationFile();
561     manager.get().reloadConfigs();
562 
563     return value;
564 }
565 
loadNTPServers(const config::Parser & config)566 void EthernetInterface::loadNTPServers(const config::Parser& config)
567 {
568     ServerList ntpServerList = getNTPServerFromTimeSyncd();
569     ServerList staticNTPServers = config.map.getValueStrings("Network", "NTP");
570 
571     std::unordered_set<std::string> staticNTPServersSet(
572         staticNTPServers.begin(), staticNTPServers.end());
573     ServerList networkSuppliedServers;
574 
575     std::copy_if(ntpServerList.begin(), ntpServerList.end(),
576                  std::back_inserter(networkSuppliedServers),
577                  [&staticNTPServersSet](const std::string& server) {
578                      return staticNTPServersSet.find(server) ==
579                             staticNTPServersSet.end();
580                  });
581 
582     EthernetInterfaceIntf::ntpServers(networkSuppliedServers);
583     EthernetInterfaceIntf::staticNTPServers(staticNTPServers);
584 }
585 
loadNameServers(const config::Parser & config)586 void EthernetInterface::loadNameServers(const config::Parser& config)
587 {
588     EthernetInterfaceIntf::nameservers(getNameServerFromResolvd());
589     EthernetInterfaceIntf::staticNameServers(
590         config.map.getValueStrings("Network", "DNS"));
591 }
592 
getNTPServerFromTimeSyncd()593 ServerList EthernetInterface::getNTPServerFromTimeSyncd()
594 {
595     ServerList servers; // Variable to capture the NTP Server IPs
596     auto method =
597         bus.get().new_method_call(TIMESYNCD_SERVICE, TIMESYNCD_SERVICE_PATH,
598                                   PROPERTY_INTERFACE, METHOD_GET);
599 
600     method.append(TIMESYNCD_INTERFACE, "LinkNTPServers");
601 
602     try
603     {
604         auto reply = bus.get().call(method);
605         auto response = reply.unpack<std::variant<ServerList>>();
606 
607         servers = std::get<ServerList>(response);
608     }
609     catch (const sdbusplus::exception::SdBusError& e)
610     {
611         lg2::error("Failed to get NTP server information from "
612                    "systemd-timesyncd: {ERROR}",
613                    "ERROR", e);
614     }
615 
616     return servers;
617 }
618 
nameservers() const619 ServerList EthernetInterface::nameservers() const
620 {
621     return getNameServerFromResolvd();
622 }
623 
getNameServerFromResolvd() const624 ServerList EthernetInterface::getNameServerFromResolvd() const
625 {
626     ServerList servers;
627     auto OBJ_PATH = std::format("{}{}", RESOLVED_SERVICE_PATH, ifIdx);
628 
629     /*
630       The DNS property under org.freedesktop.resolve1.Link interface contains
631       an array containing all DNS servers currently used by resolved. It
632       contains similar information as the DNS server data written to
633       /run/systemd/resolve/resolv.conf.
634 
635       Each structure in the array consists of a numeric network interface index,
636       an address family, and a byte array containing the DNS server address
637       (either 4 bytes in length for IPv4 or 16 bytes in lengths for IPv6).
638       The array contains DNS servers configured system-wide, including those
639       possibly read from a foreign /etc/resolv.conf or the DNS= setting in
640       /etc/systemd/resolved.conf, as well as per-interface DNS server
641       information either retrieved from systemd-networkd or configured by
642       external software via SetLinkDNS().
643     */
644 
645     using type = std::vector<std::tuple<int32_t, std::vector<uint8_t>>>;
646     std::variant<type> name; // Variable to capture the DNS property
647     auto method = bus.get().new_method_call(RESOLVED_SERVICE, OBJ_PATH.c_str(),
648                                             PROPERTY_INTERFACE, METHOD_GET);
649 
650     method.append(RESOLVED_INTERFACE, "DNS");
651 
652     try
653     {
654         auto reply = bus.get().call(method);
655         reply.read(name);
656     }
657     catch (const sdbusplus::exception_t& e)
658     {
659         lg2::error(
660             "Failed to get DNS information from systemd-resolved: {ERROR}",
661             "ERROR", e);
662     }
663     auto tupleVector = std::get_if<type>(&name);
664     for (auto i = tupleVector->begin(); i != tupleVector->end(); ++i)
665     {
666         int addressFamily = std::get<0>(*i);
667         std::vector<uint8_t>& ipaddress = std::get<1>(*i);
668         servers.push_back(stdplus::toStr(
669             addrFromBuf(addressFamily, stdplus::raw::asView<char>(ipaddress))));
670     }
671     return servers;
672 }
673 
createVLAN(uint16_t id)674 ObjectPath EthernetInterface::createVLAN(uint16_t id)
675 {
676     auto idStr = stdplus::toStr(id);
677     auto intfName = stdplus::strCat(interfaceName(), "."sv, idStr);
678     if (manager.get().interfaces.find(intfName) !=
679         manager.get().interfaces.end())
680     {
681         lg2::error("VLAN {NET_VLAN} already exists", "NET_VLAN", id);
682         elog<InvalidArgument>(Argument::ARGUMENT_NAME("VLANId"),
683                               Argument::ARGUMENT_VALUE(idStr.c_str()));
684     }
685 
686     auto objRoot = std::string_view(objPath).substr(0, objPath.rfind('/'));
687     auto macStr = MacAddressIntf::macAddress();
688     std::optional<stdplus::EtherAddr> mac;
689     if (!macStr.empty())
690     {
691         mac.emplace(stdplus::fromStr<stdplus::EtherAddr>(macStr));
692     }
693     auto info = AllIntfInfo{InterfaceInfo{
694         .type = ARPHRD_ETHER,
695         .idx = 0, // TODO: Query the correct value after creation
696         .flags = 0,
697         .name = intfName,
698         .mac = std::move(mac),
699         .mtu = mtu(),
700         .parent_idx = ifIdx,
701         .vlan_id = id,
702     }};
703 
704     // Pass the parents nicEnabled property, so that the child
705     // VLAN interface can inherit.
706     auto vlanIntf = std::make_unique<EthernetInterface>(
707         bus, manager, info, objRoot, config::Parser(), nicEnabled());
708     ObjectPath ret = vlanIntf->objPath;
709 
710     manager.get().interfaces.emplace(intfName, std::move(vlanIntf));
711 
712     // write the device file for the vlan interface.
713     config::Parser config;
714     auto& netdev = config.map["NetDev"].emplace_back();
715     netdev["Name"].emplace_back(intfName);
716     netdev["Kind"].emplace_back("vlan");
717     config.map["VLAN"].emplace_back()["Id"].emplace_back(std::move(idStr));
718     config.writeFile(
719         config::pathForIntfDev(manager.get().getConfDir(), intfName));
720 
721     writeConfigurationFile();
722     manager.get().reloadConfigs();
723 
724     return ret;
725 }
726 
staticNTPServers(ServerList value)727 ServerList EthernetInterface::staticNTPServers(ServerList value)
728 {
729     value = EthernetInterfaceIntf::staticNTPServers(std::move(value));
730 
731     writeConfigurationFile();
732     manager.get().reloadConfigs();
733 
734     return value;
735 }
736 
ntpServers(ServerList)737 ServerList EthernetInterface::ntpServers(ServerList /*servers*/)
738 {
739     elog<NotAllowed>(NotAllowedArgument::REASON("ReadOnly Property"));
740 }
741 
tfStr(bool value)742 static constexpr std::string_view tfStr(bool value)
743 {
744     return value ? "true"sv : "false"sv;
745 }
746 
writeUpdatedTime(const Manager & manager,const std::filesystem::path & netFile)747 static void writeUpdatedTime(const Manager& manager,
748                              const std::filesystem::path& netFile)
749 {
750     // JFFS2 doesn't have the time granularity to deal with sub-second
751     // updates. Since we can have multiple file updates within a second
752     // around a reload, we need a location which gives that precision for
753     // future networkd detected reloads. TMPFS gives us this property.
754     if (manager.getConfDir() == "/etc/systemd/network"sv)
755     {
756         auto dir = stdplus::strCat(netFile.native(), ".d");
757         dir.replace(1, 3, "run"); // Replace /etc with /run
758         auto file = dir + "/updated.conf";
759         try
760         {
761             std::filesystem::create_directories(dir);
762             using namespace stdplus::fd;
763             futimens(
764                 open(file,
765                      OpenFlags(OpenAccess::WriteOnly).set(OpenFlag::Create),
766                      0644)
767                     .get(),
768                 nullptr);
769         }
770         catch (const std::exception& e)
771         {
772             lg2::error("Failed to write time updated file {FILE}: {ERROR}",
773                        "FILE", file, "ERROR", e.what());
774         }
775     }
776 }
777 
writeConfigurationFile()778 void EthernetInterface::writeConfigurationFile()
779 {
780     config::Parser config;
781     config.map["Match"].emplace_back()["Name"].emplace_back(interfaceName());
782     {
783         auto& link = config.map["Link"].emplace_back();
784 #ifdef PERSIST_MAC
785         auto mac = MacAddressIntf::macAddress();
786         if (!mac.empty())
787         {
788             link["MACAddress"].emplace_back(mac);
789         }
790 #endif
791         if (!EthernetInterfaceIntf::nicEnabled())
792         {
793             link["ActivationPolicy"].emplace_back("down");
794         }
795     }
796     {
797         auto& network = config.map["Network"].emplace_back();
798         auto& lla = network["LinkLocalAddressing"];
799 #ifdef LINK_LOCAL_AUTOCONFIGURATION
800         lla.emplace_back("yes");
801 #else
802         lla.emplace_back("no");
803 #endif
804         network["IPv6AcceptRA"].emplace_back(ipv6AcceptRA() ? "true" : "false");
805         network["DHCP"].emplace_back(dhcp4() ? (dhcp6() ? "true" : "ipv4")
806                                              : (dhcp6() ? "ipv6" : "false"));
807         {
808             auto& vlans = network["VLAN"];
809             for (const auto& [_, intf] : manager.get().interfaces)
810             {
811                 if (intf->vlan && intf->vlan->parentIdx == ifIdx)
812                 {
813                     vlans.emplace_back(intf->interfaceName());
814                 }
815             }
816         }
817         {
818             auto& ntps = network["NTP"];
819             for (const auto& ntp : EthernetInterfaceIntf::staticNTPServers())
820             {
821                 ntps.emplace_back(ntp);
822             }
823         }
824         {
825             auto& dnss = network["DNS"];
826             for (const auto& dns : EthernetInterfaceIntf::staticNameServers())
827             {
828                 dnss.emplace_back(dns);
829             }
830         }
831         {
832             auto& address = network["Address"];
833             for (const auto& addr : addrs)
834             {
835                 if (addr.second->origin() == IP::AddressOrigin::Static)
836                 {
837                     address.emplace_back(stdplus::toStr(addr.first));
838                 }
839             }
840         }
841         {
842             if (!dhcp4())
843             {
844                 auto gateway4 = EthernetInterfaceIntf::defaultGateway();
845                 if (!gateway4.empty())
846                 {
847                     auto& gateway4route = config.map["Route"].emplace_back();
848                     gateway4route["Gateway"].emplace_back(gateway4);
849                     gateway4route["GatewayOnLink"].emplace_back("true");
850                 }
851             }
852 
853             if (!ipv6AcceptRA())
854             {
855                 auto gateway6 = EthernetInterfaceIntf::defaultGateway6();
856                 if (!gateway6.empty())
857                 {
858                     auto& gateway6route = config.map["Route"].emplace_back();
859                     gateway6route["Gateway"].emplace_back(gateway6);
860                     gateway6route["GatewayOnLink"].emplace_back("true");
861                 }
862             }
863         }
864     }
865     config.map["IPv6AcceptRA"].emplace_back()["DHCPv6Client"].emplace_back(
866         dhcp6() ? "true" : "false");
867     {
868         auto& neighbors = config.map["Neighbor"];
869         for (const auto& sneighbor : staticNeighbors)
870         {
871             auto& neighbor = neighbors.emplace_back();
872             neighbor["Address"].emplace_back(sneighbor.second->ipAddress());
873             neighbor["MACAddress"].emplace_back(sneighbor.second->macAddress());
874         }
875     }
876     {
877         auto& dhcp4 = config.map["DHCPv4"].emplace_back();
878         dhcp4["ClientIdentifier"].emplace_back("mac");
879         dhcp4["UseDNS"].emplace_back(tfStr(dhcp4Conf->dnsEnabled()));
880         dhcp4["UseDomains"].emplace_back(tfStr(dhcp4Conf->domainEnabled()));
881         dhcp4["UseNTP"].emplace_back(tfStr(dhcp4Conf->ntpEnabled()));
882         dhcp4["UseHostname"].emplace_back(tfStr(dhcp4Conf->hostNameEnabled()));
883         dhcp4["SendHostname"].emplace_back(
884             tfStr(dhcp4Conf->sendHostNameEnabled()));
885     }
886     {
887         auto& dhcp6 = config.map["DHCPv6"].emplace_back();
888         dhcp6["UseDNS"].emplace_back(tfStr(dhcp6Conf->dnsEnabled()));
889         dhcp6["UseDomains"].emplace_back(tfStr(dhcp6Conf->domainEnabled()));
890         dhcp6["UseNTP"].emplace_back(tfStr(dhcp6Conf->ntpEnabled()));
891         dhcp6["UseHostname"].emplace_back(tfStr(dhcp6Conf->hostNameEnabled()));
892         dhcp6["SendHostname"].emplace_back(
893             tfStr(dhcp6Conf->sendHostNameEnabled()));
894     }
895 
896     {
897         auto& sroutes = config.map["Route"];
898         for (const auto& temp : staticGateways)
899         {
900             auto& staticGateway = sroutes.emplace_back();
901             staticGateway["Gateway"].emplace_back(temp.second->gateway());
902             staticGateway["GatewayOnLink"].emplace_back("true");
903         }
904     }
905 
906     auto path =
907         config::pathForIntfConf(manager.get().getConfDir(), interfaceName());
908     config.writeFile(path);
909     lg2::info("Wrote networkd file: {CFG_FILE}", "CFG_FILE", path);
910     writeUpdatedTime(manager, path);
911 }
912 
macAddress(std::string value)913 std::string EthernetInterface::macAddress([[maybe_unused]] std::string value)
914 {
915     if (vlan)
916     {
917         lg2::error("Tried to set MAC address on VLAN");
918         elog<InternalFailure>();
919     }
920 #ifdef PERSIST_MAC
921     stdplus::EtherAddr newMAC;
922     try
923     {
924         newMAC = stdplus::fromStr<stdplus::EtherAddr>(value);
925     }
926     catch (const std::exception& e)
927     {
928         lg2::error("Invalid MAC address {NET_MAC}: {ERROR}", "NET_MAC", value,
929                    "ERROR", e);
930         elog<InvalidArgument>(Argument::ARGUMENT_NAME("netmac"),
931                               Argument::ARGUMENT_VALUE(value.c_str()));
932     }
933 
934     if (!newMAC.isUnicast())
935     {
936         lg2::error("MAC Address {NET_MAC} is not valid", "NET_MAC", value);
937         elog<InvalidArgument>(Argument::ARGUMENT_NAME("MACAddress"),
938                               Argument::ARGUMENT_VALUE(value.c_str()));
939     }
940 
941     auto interface = interfaceName();
942     auto validMAC = stdplus::toStr(newMAC);
943 
944     // We don't need to update the system if the address is unchanged
945     auto oldMAC =
946         stdplus::fromStr<stdplus::EtherAddr>(MacAddressIntf::macAddress());
947     if (newMAC != oldMAC)
948     {
949         // Update everything that depends on the MAC value
950         for (const auto& [_, intf] : manager.get().interfaces)
951         {
952             if (intf->vlan && intf->vlan->parentIdx == ifIdx)
953             {
954                 intf->MacAddressIntf::macAddress(validMAC);
955             }
956         }
957         MacAddressIntf::macAddress(validMAC);
958 
959         writeConfigurationFile();
960         manager.get().addReloadPreHook([interface, manager = manager]() {
961             // The MAC and LLADDRs will only update if the NIC is already down
962             system::setNICUp(interface, false);
963             writeUpdatedTime(
964                 manager,
965                 config::pathForIntfConf(manager.get().getConfDir(), interface));
966         });
967         manager.get().reloadConfigs();
968     }
969 
970     std::error_code ec;
971     const auto fw_setenv = std::filesystem::path("/sbin/fw_setenv");
972     if (std::filesystem::exists(fw_setenv, ec))
973     {
974         // Ensure that the valid address is stored in the u-boot-env
975         auto envVar = interfaceToUbootEthAddr(interface);
976         if (envVar)
977         {
978             execute(fw_setenv.native(), "fw_setenv", envVar->c_str(),
979                     validMAC.c_str());
980         }
981     }
982 
983     return value;
984 #else
985     elog<NotAllowed>(
986         NotAllowedArgument::REASON("Writing MAC address is not allowed"));
987 #endif // PERSIST_MAC
988 }
989 
deleteAll()990 void EthernetInterface::deleteAll()
991 {
992     // clear all the ip on the interface
993     addrs.clear();
994 
995     writeConfigurationFile();
996     manager.get().reloadConfigs();
997 }
998 
999 template <typename Addr>
normalizeGateway(std::string & gw)1000 static void normalizeGateway(std::string& gw)
1001 {
1002     if (gw.empty())
1003     {
1004         return;
1005     }
1006     try
1007     {
1008         auto ip = stdplus::fromStr<Addr>(gw);
1009         if (ip == Addr{})
1010         {
1011             gw.clear();
1012             return;
1013         }
1014         if (!validIntfIP(ip))
1015         {
1016             throw std::invalid_argument("Invalid unicast");
1017         }
1018         gw = stdplus::toStr(ip);
1019     }
1020     catch (const std::exception& e)
1021     {
1022         lg2::error("Invalid GW `{NET_GW}`: {ERROR}", "NET_GW", gw, "ERROR", e);
1023         elog<InvalidArgument>(Argument::ARGUMENT_NAME("GATEWAY"),
1024                               Argument::ARGUMENT_VALUE(gw.c_str()));
1025     }
1026 }
1027 
defaultGateway(std::string gateway)1028 std::string EthernetInterface::defaultGateway(std::string gateway)
1029 {
1030     normalizeGateway<stdplus::In4Addr>(gateway);
1031     if (gateway != defaultGateway())
1032     {
1033         gateway = EthernetInterfaceIntf::defaultGateway(std::move(gateway));
1034         writeConfigurationFile();
1035         manager.get().reloadConfigs();
1036     }
1037     return gateway;
1038 }
1039 
defaultGateway6(std::string gateway)1040 std::string EthernetInterface::defaultGateway6(std::string gateway)
1041 {
1042     normalizeGateway<stdplus::In6Addr>(gateway);
1043     if (gateway != defaultGateway6())
1044     {
1045         gateway = EthernetInterfaceIntf::defaultGateway6(std::move(gateway));
1046         writeConfigurationFile();
1047         manager.get().reloadConfigs();
1048     }
1049     return gateway;
1050 }
1051 
VlanProperties(sdbusplus::bus_t & bus,stdplus::const_zstring objPath,const InterfaceInfo & info,stdplus::PinnedRef<EthernetInterface> eth)1052 EthernetInterface::VlanProperties::VlanProperties(
1053     sdbusplus::bus_t& bus, stdplus::const_zstring objPath,
1054     const InterfaceInfo& info, stdplus::PinnedRef<EthernetInterface> eth) :
1055     VlanIfaces(bus, objPath.c_str(), VlanIfaces::action::defer_emit),
1056     parentIdx(*info.parent_idx), eth(eth)
1057 {
1058     VlanIntf::id(*info.vlan_id, true);
1059     emit_object_added();
1060 }
1061 
delete_()1062 void EthernetInterface::VlanProperties::delete_()
1063 {
1064     auto intf = eth.get().interfaceName();
1065 
1066     // Remove all configs for the current interface
1067     const auto& confDir = eth.get().manager.get().getConfDir();
1068     std::error_code ec;
1069     std::filesystem::remove(config::pathForIntfConf(confDir, intf), ec);
1070     std::filesystem::remove(config::pathForIntfDev(confDir, intf), ec);
1071 
1072     if (eth.get().ifIdx > 0)
1073     {
1074         eth.get().manager.get().interfacesByIdx.erase(eth.get().ifIdx);
1075     }
1076     auto it = eth.get().manager.get().interfaces.find(intf);
1077     auto obj = std::move(it->second);
1078     eth.get().manager.get().interfaces.erase(it);
1079 
1080     // Write an updated parent interface since it has a VLAN entry
1081     for (const auto& [_, intf] : eth.get().manager.get().interfaces)
1082     {
1083         if (intf->ifIdx == parentIdx)
1084         {
1085             intf->writeConfigurationFile();
1086         }
1087     }
1088 
1089     if (eth.get().ifIdx > 0)
1090     {
1091         // We need to forcibly delete the interface as systemd does not
1092         eth.get().manager.get().addReloadPostHook([idx = eth.get().ifIdx]() {
1093             system::deleteIntf(idx);
1094         });
1095 
1096         // Ignore the interface so the reload doesn't re-query it
1097         eth.get().manager.get().ignoredIntf.emplace(eth.get().ifIdx);
1098     }
1099 
1100     eth.get().manager.get().reloadConfigs();
1101 }
1102 
emitLLDP(bool value)1103 bool EthernetInterface::emitLLDP(bool value)
1104 {
1105     if (emitLLDP() != EthernetInterfaceIntf::emitLLDP(value))
1106     {
1107         manager.get().writeLLDPDConfigurationFile();
1108         manager.get().reloadLLDPService();
1109     }
1110     return value;
1111 }
1112 
reloadConfigs()1113 void EthernetInterface::reloadConfigs()
1114 {
1115     manager.get().reloadConfigs();
1116 }
1117 
1118 } // namespace network
1119 } // namespace phosphor
1120