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