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