xref: /openbmc/phosphor-networkd/src/ethernet_interface.cpp (revision f02fc4a5adaf8c9d1b8e45980b50c12174d23bb4)
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     manager.get().interfaces.emplace(intfName, std::move(vlanIntf));
763 
764     // write the device file for the vlan interface.
765     config::Parser config;
766     auto& netdev = config.map["NetDev"].emplace_back();
767     netdev["Name"].emplace_back(intfName);
768     netdev["Kind"].emplace_back("vlan");
769     config.map["VLAN"].emplace_back()["Id"].emplace_back(std::move(idStr));
770     config.writeFile(
771         config::pathForIntfDev(manager.get().getConfDir(), intfName));
772 
773     writeConfigurationFile();
774     manager.get().reloadConfigs();
775 
776     return ret;
777 }
778 
staticNTPServers(ServerList value)779 ServerList EthernetInterface::staticNTPServers(ServerList value)
780 {
781     value = EthernetInterfaceIntf::staticNTPServers(std::move(value));
782 
783     writeConfigurationFile();
784     manager.get().reloadConfigs();
785 
786     return value;
787 }
788 
ntpServers(ServerList)789 ServerList EthernetInterface::ntpServers(ServerList /*servers*/)
790 {
791     elog<NotAllowed>(NotAllowedArgument::REASON("ReadOnly Property"));
792 }
793 
tfStr(bool value)794 static constexpr std::string_view tfStr(bool value)
795 {
796     return value ? "true"sv : "false"sv;
797 }
798 
writeUpdatedTime(const Manager & manager,const std::filesystem::path & netFile)799 static void writeUpdatedTime(const Manager& manager,
800                              const std::filesystem::path& netFile)
801 {
802     // JFFS2 doesn't have the time granularity to deal with sub-second
803     // updates. Since we can have multiple file updates within a second
804     // around a reload, we need a location which gives that precision for
805     // future networkd detected reloads. TMPFS gives us this property.
806     if (manager.getConfDir() == "/etc/systemd/network"sv)
807     {
808         auto dir = stdplus::strCat(netFile.native(), ".d");
809         dir.replace(1, 3, "run"); // Replace /etc with /run
810         auto file = dir + "/updated.conf";
811         try
812         {
813             std::filesystem::create_directories(dir);
814             using namespace stdplus::fd;
815             futimens(
816                 open(file,
817                      OpenFlags(OpenAccess::WriteOnly).set(OpenFlag::Create),
818                      0644)
819                     .get(),
820                 nullptr);
821         }
822         catch (const std::exception& e)
823         {
824             lg2::error("Failed to write time updated file {FILE}: {ERROR}",
825                        "FILE", file, "ERROR", e.what());
826         }
827     }
828 }
829 
writeConfigurationFile()830 void EthernetInterface::writeConfigurationFile()
831 {
832     config::Parser config;
833     config.map["Match"].emplace_back()["Name"].emplace_back(interfaceName());
834     {
835         auto& link = config.map["Link"].emplace_back();
836 #ifdef PERSIST_MAC
837         auto mac = MacAddressIntf::macAddress();
838         if (!mac.empty())
839         {
840             link["MACAddress"].emplace_back(mac);
841         }
842 #endif
843         if (!EthernetInterfaceIntf::nicEnabled())
844         {
845             link["ActivationPolicy"].emplace_back("down");
846         }
847     }
848     {
849         auto& network = config.map["Network"].emplace_back();
850         auto& lla = network["LinkLocalAddressing"];
851 #ifdef LINK_LOCAL_AUTOCONFIGURATION
852         lla.emplace_back("yes");
853 #else
854         lla.emplace_back("no");
855 #endif
856         network["IPv6AcceptRA"].emplace_back(ipv6AcceptRA() ? "true" : "false");
857         network["DHCP"].emplace_back(dhcp4() ? (dhcp6() ? "true" : "ipv4")
858                                              : (dhcp6() ? "ipv6" : "false"));
859         {
860             auto& vlans = network["VLAN"];
861             for (const auto& [_, intf] : manager.get().interfaces)
862             {
863                 if (intf->vlan && intf->vlan->parentIdx == ifIdx)
864                 {
865                     vlans.emplace_back(intf->interfaceName());
866                 }
867             }
868         }
869         {
870             auto& ntps = network["NTP"];
871             for (const auto& ntp : EthernetInterfaceIntf::staticNTPServers())
872             {
873                 ntps.emplace_back(ntp);
874             }
875         }
876         {
877             auto& dnss = network["DNS"];
878             for (const auto& dns : EthernetInterfaceIntf::staticNameServers())
879             {
880                 dnss.emplace_back(dns);
881             }
882         }
883         {
884             auto& address = network["Address"];
885             for (const auto& addr : addrs)
886             {
887                 if (addr.second->origin() == IP::AddressOrigin::Static)
888                 {
889                     address.emplace_back(stdplus::toStr(addr.first));
890                 }
891             }
892         }
893         {
894             if (!dhcp4())
895             {
896                 auto gateway4 = EthernetInterfaceIntf::defaultGateway();
897                 if (!gateway4.empty())
898                 {
899                     auto& gateway4route = config.map["Route"].emplace_back();
900                     gateway4route["Gateway"].emplace_back(gateway4);
901                     gateway4route["GatewayOnLink"].emplace_back("true");
902                 }
903             }
904 
905             if (!ipv6AcceptRA())
906             {
907                 auto gateway6 = EthernetInterfaceIntf::defaultGateway6();
908                 if (!gateway6.empty())
909                 {
910                     auto& gateway6route = config.map["Route"].emplace_back();
911                     gateway6route["Gateway"].emplace_back(gateway6);
912                     gateway6route["GatewayOnLink"].emplace_back("true");
913                 }
914             }
915         }
916     }
917     config.map["IPv6AcceptRA"].emplace_back()["DHCPv6Client"].emplace_back(
918         dhcp6() ? "true" : "false");
919     {
920         auto& neighbors = config.map["Neighbor"];
921         for (const auto& sneighbor : staticNeighbors)
922         {
923             auto& neighbor = neighbors.emplace_back();
924             neighbor["Address"].emplace_back(sneighbor.second->ipAddress());
925             neighbor["MACAddress"].emplace_back(sneighbor.second->macAddress());
926         }
927     }
928     {
929         auto& dhcp4 = config.map["DHCPv4"].emplace_back();
930         dhcp4["ClientIdentifier"].emplace_back("mac");
931         dhcp4["UseDNS"].emplace_back(tfStr(dhcp4Conf->dnsEnabled()));
932         dhcp4["UseDomains"].emplace_back(tfStr(dhcp4Conf->domainEnabled()));
933         dhcp4["UseNTP"].emplace_back(tfStr(dhcp4Conf->ntpEnabled()));
934         dhcp4["UseHostname"].emplace_back(tfStr(dhcp4Conf->hostNameEnabled()));
935         dhcp4["SendHostname"].emplace_back(
936             tfStr(dhcp4Conf->sendHostNameEnabled()));
937     }
938     {
939         auto& dhcp6 = config.map["DHCPv6"].emplace_back();
940         dhcp6["UseDNS"].emplace_back(tfStr(dhcp6Conf->dnsEnabled()));
941         dhcp6["UseDomains"].emplace_back(tfStr(dhcp6Conf->domainEnabled()));
942         dhcp6["UseNTP"].emplace_back(tfStr(dhcp6Conf->ntpEnabled()));
943         dhcp6["UseHostname"].emplace_back(tfStr(dhcp6Conf->hostNameEnabled()));
944         dhcp6["SendHostname"].emplace_back(
945             tfStr(dhcp6Conf->sendHostNameEnabled()));
946     }
947 
948     {
949         auto& sroutes = config.map["Route"];
950         for (const auto& temp : staticGateways)
951         {
952             auto& staticGateway = sroutes.emplace_back();
953             staticGateway["Gateway"].emplace_back(temp.second->gateway());
954             staticGateway["GatewayOnLink"].emplace_back("true");
955         }
956     }
957 
958     auto path =
959         config::pathForIntfConf(manager.get().getConfDir(), interfaceName());
960     config.writeFile(path);
961     lg2::info("Wrote networkd file: {CFG_FILE}", "CFG_FILE", path);
962     writeUpdatedTime(manager, path);
963 }
964 
macAddress(std::string value)965 std::string EthernetInterface::macAddress([[maybe_unused]] std::string value)
966 {
967     if (vlan)
968     {
969         lg2::error("Tried to set MAC address on VLAN");
970         elog<InternalFailure>();
971     }
972 #ifdef PERSIST_MAC
973     stdplus::EtherAddr newMAC;
974     try
975     {
976         newMAC = stdplus::fromStr<stdplus::EtherAddr>(value);
977     }
978     catch (const std::exception& e)
979     {
980         lg2::error("Invalid MAC address {NET_MAC}: {ERROR}", "NET_MAC", value,
981                    "ERROR", e);
982         elog<InvalidArgument>(Argument::ARGUMENT_NAME("netmac"),
983                               Argument::ARGUMENT_VALUE(value.c_str()));
984     }
985 
986     if (!newMAC.isUnicast())
987     {
988         lg2::error("MAC Address {NET_MAC} is not valid", "NET_MAC", value);
989         elog<InvalidArgument>(Argument::ARGUMENT_NAME("MACAddress"),
990                               Argument::ARGUMENT_VALUE(value.c_str()));
991     }
992 
993     auto interface = interfaceName();
994     auto validMAC = stdplus::toStr(newMAC);
995 
996     // We don't need to update the system if the address is unchanged
997     auto oldMAC =
998         stdplus::fromStr<stdplus::EtherAddr>(MacAddressIntf::macAddress());
999     if (newMAC != oldMAC)
1000     {
1001         // Update everything that depends on the MAC value
1002         for (const auto& [_, intf] : manager.get().interfaces)
1003         {
1004             if (intf->vlan && intf->vlan->parentIdx == ifIdx)
1005             {
1006                 intf->MacAddressIntf::macAddress(validMAC);
1007             }
1008         }
1009         MacAddressIntf::macAddress(validMAC);
1010 
1011         writeConfigurationFile();
1012         manager.get().addReloadPreHook([interface, manager = manager]() {
1013             // The MAC and LLADDRs will only update if the NIC is already down
1014             system::setNICUp(interface, false);
1015             writeUpdatedTime(
1016                 manager,
1017                 config::pathForIntfConf(manager.get().getConfDir(), interface));
1018         });
1019         manager.get().reloadConfigs();
1020     }
1021 
1022     std::error_code ec;
1023     const auto fw_setenv = std::filesystem::path("/sbin/fw_setenv");
1024     if (std::filesystem::exists(fw_setenv, ec))
1025     {
1026         // Ensure that the valid address is stored in the u-boot-env
1027         auto envVar = interfaceToUbootEthAddr(interface);
1028         if (envVar)
1029         {
1030             execute(fw_setenv.native(), "fw_setenv", envVar->c_str(),
1031                     validMAC.c_str());
1032         }
1033     }
1034 
1035     return value;
1036 #else
1037     elog<NotAllowed>(
1038         NotAllowedArgument::REASON("Writing MAC address is not allowed"));
1039 #endif // PERSIST_MAC
1040 }
1041 
deleteAll()1042 void EthernetInterface::deleteAll()
1043 {
1044     // clear all the ip on the interface
1045     addrs.clear();
1046 
1047     writeConfigurationFile();
1048     manager.get().reloadConfigs();
1049 }
1050 
1051 template <typename Addr>
normalizeGateway(std::string & gw)1052 static void normalizeGateway(std::string& gw)
1053 {
1054     if (gw.empty())
1055     {
1056         return;
1057     }
1058     try
1059     {
1060         auto ip = stdplus::fromStr<Addr>(gw);
1061         if (ip == Addr{})
1062         {
1063             gw.clear();
1064             return;
1065         }
1066         if (!validIntfIP(ip))
1067         {
1068             throw std::invalid_argument("Invalid unicast");
1069         }
1070         gw = stdplus::toStr(ip);
1071     }
1072     catch (const std::exception& e)
1073     {
1074         lg2::error("Invalid GW `{NET_GW}`: {ERROR}", "NET_GW", gw, "ERROR", e);
1075         elog<InvalidArgument>(Argument::ARGUMENT_NAME("GATEWAY"),
1076                               Argument::ARGUMENT_VALUE(gw.c_str()));
1077     }
1078 }
1079 
defaultGateway(std::string gateway)1080 std::string EthernetInterface::defaultGateway(std::string gateway)
1081 {
1082     normalizeGateway<stdplus::In4Addr>(gateway);
1083     if (gateway != defaultGateway())
1084     {
1085         gateway = EthernetInterfaceIntf::defaultGateway(std::move(gateway));
1086         writeConfigurationFile();
1087         manager.get().reloadConfigs();
1088     }
1089     return gateway;
1090 }
1091 
defaultGateway6(std::string gateway)1092 std::string EthernetInterface::defaultGateway6(std::string gateway)
1093 {
1094     normalizeGateway<stdplus::In6Addr>(gateway);
1095     if (gateway != defaultGateway6())
1096     {
1097         gateway = EthernetInterfaceIntf::defaultGateway6(std::move(gateway));
1098         writeConfigurationFile();
1099         manager.get().reloadConfigs();
1100     }
1101     return gateway;
1102 }
1103 
VlanProperties(sdbusplus::bus_t & bus,stdplus::const_zstring objPath,const InterfaceInfo & info,stdplus::PinnedRef<EthernetInterface> eth)1104 EthernetInterface::VlanProperties::VlanProperties(
1105     sdbusplus::bus_t& bus, stdplus::const_zstring objPath,
1106     const InterfaceInfo& info, stdplus::PinnedRef<EthernetInterface> eth) :
1107     VlanIfaces(bus, objPath.c_str(), VlanIfaces::action::defer_emit),
1108     parentIdx(*info.parent_idx), eth(eth)
1109 {
1110     VlanIntf::id(*info.vlan_id, true);
1111     emit_object_added();
1112 }
1113 
delete_()1114 void EthernetInterface::VlanProperties::delete_()
1115 {
1116     auto intf = eth.get().interfaceName();
1117 
1118     // Remove all configs for the current interface
1119     const auto& confDir = eth.get().manager.get().getConfDir();
1120     std::error_code ec;
1121     std::filesystem::remove(config::pathForIntfConf(confDir, intf), ec);
1122     std::filesystem::remove(config::pathForIntfDev(confDir, intf), ec);
1123 
1124     if (eth.get().ifIdx > 0)
1125     {
1126         eth.get().manager.get().interfacesByIdx.erase(eth.get().ifIdx);
1127     }
1128     auto it = eth.get().manager.get().interfaces.find(intf);
1129     auto obj = std::move(it->second);
1130     eth.get().manager.get().interfaces.erase(it);
1131 
1132     // Write an updated parent interface since it has a VLAN entry
1133     for (const auto& [_, intf] : eth.get().manager.get().interfaces)
1134     {
1135         if (intf->ifIdx == parentIdx)
1136         {
1137             intf->writeConfigurationFile();
1138         }
1139     }
1140 
1141     if (eth.get().ifIdx > 0)
1142     {
1143         // We need to forcibly delete the interface as systemd does not
1144         eth.get().manager.get().addReloadPostHook([idx = eth.get().ifIdx]() {
1145             system::deleteIntf(idx);
1146         });
1147 
1148         // Ignore the interface so the reload doesn't re-query it
1149         eth.get().manager.get().ignoredIntf.emplace(eth.get().ifIdx);
1150     }
1151 
1152     eth.get().manager.get().reloadConfigs();
1153 }
1154 
emitLLDP(bool value)1155 bool EthernetInterface::emitLLDP(bool value)
1156 {
1157     if (emitLLDP() != EthernetInterfaceIntf::emitLLDP(value))
1158     {
1159         manager.get().writeLLDPDConfigurationFile();
1160         manager.get().reloadLLDPService();
1161     }
1162     return value;
1163 }
1164 
reloadConfigs()1165 void EthernetInterface::reloadConfigs()
1166 {
1167     manager.get().reloadConfigs();
1168 }
1169 
1170 } // namespace network
1171 } // namespace phosphor
1172