1 #include "config.h"
2 
3 #include "ethernet_interface.hpp"
4 
5 #include "config_parser.hpp"
6 #include "network_manager.hpp"
7 #include "util.hpp"
8 
9 #include <arpa/inet.h>
10 #include <fmt/compile.h>
11 #include <fmt/format.h>
12 #include <linux/ethtool.h>
13 #include <linux/rtnetlink.h>
14 #include <linux/sockios.h>
15 #include <net/if.h>
16 
17 #include <algorithm>
18 #include <filesystem>
19 #include <phosphor-logging/elog-errors.hpp>
20 #include <phosphor-logging/log.hpp>
21 #include <sdbusplus/bus/match.hpp>
22 #include <stdplus/fd/create.hpp>
23 #include <stdplus/raw.hpp>
24 #include <stdplus/zstring.hpp>
25 #include <string>
26 #include <unordered_map>
27 #include <variant>
28 #include <xyz/openbmc_project/Common/error.hpp>
29 
30 namespace phosphor
31 {
32 namespace network
33 {
34 
35 using namespace phosphor::logging;
36 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
37 using NotAllowed = sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
38 using NotAllowedArgument = xyz::openbmc_project::Common::NotAllowed;
39 using Argument = xyz::openbmc_project::Common::InvalidArgument;
40 using std::literals::string_view_literals::operator""sv;
41 constexpr auto RESOLVED_SERVICE = "org.freedesktop.resolve1";
42 constexpr auto RESOLVED_INTERFACE = "org.freedesktop.resolve1.Link";
43 constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties";
44 constexpr auto RESOLVED_SERVICE_PATH = "/org/freedesktop/resolve1/link/";
45 
46 constexpr auto TIMESYNCD_SERVICE = "org.freedesktop.timesync1";
47 constexpr auto TIMESYNCD_INTERFACE = "org.freedesktop.timesync1.Manager";
48 constexpr auto TIMESYNCD_SERVICE_PATH = "/org/freedesktop/timesync1";
49 
50 constexpr auto METHOD_GET = "Get";
51 
52 static stdplus::Fd& getIFSock()
53 {
54     using namespace stdplus::fd;
55     static auto fd =
56         socket(SocketDomain::INet, SocketType::Datagram, SocketProto::IP);
57     return fd;
58 }
59 
60 struct LinkInfo
61 {
62     bool autoneg;
63     uint16_t speed;
64 };
65 
66 static LinkInfo getLinkInfo(stdplus::zstring_view ifname)
67 {
68     LinkInfo ret;
69     try
70     {
71         ethtool_cmd edata = {};
72         edata.cmd = ETHTOOL_GSET;
73 
74         ifreq ifr = {};
75         const auto copied = std::min<std::size_t>(ifname.size(), IFNAMSIZ - 1);
76         std::copy_n(ifname.begin(), copied, ifr.ifr_name);
77         ifr.ifr_data = reinterpret_cast<char*>(&edata);
78 
79         getIFSock().ioctl(SIOCETHTOOL, &ifr);
80 
81         ret.speed = edata.speed;
82         ret.autoneg = edata.autoneg;
83     }
84     catch (const std::system_error& e)
85     {
86         if (e.code() == std::errc::operation_not_supported)
87         {
88             auto msg = fmt::format("ETHTOOL not supported on {}", ifname);
89             log<level::NOTICE>(msg.c_str(),
90                                entry("INTERFACE=%s", ifname.c_str()));
91         }
92         else
93         {
94             auto msg =
95                 fmt::format("ETHTOOL failed on {}: {}", ifname, e.what());
96             log<level::ERR>(msg.c_str(), entry("INTERFACE=%s", ifname.c_str()));
97         }
98     }
99     return ret;
100 }
101 
102 EthernetInterface::EthernetInterface(sdbusplus::bus_t& bus,
103                                      stdplus::zstring_view objPath,
104                                      const config::Parser& config,
105                                      Manager& parent, bool emitSignal,
106                                      std::optional<bool> enabled) :
107     Ifaces(bus, objPath.c_str(),
108            emitSignal ? Ifaces::action::defer_emit
109                       : Ifaces::action::emit_no_signals),
110     bus(bus), manager(parent), objPath(objPath.c_str())
111 {
112     auto intfName = std::string(objPath.substr(objPath.rfind('/') + 1));
113     std::replace(intfName.begin(), intfName.end(), '_', '.');
114     interfaceName(intfName);
115     auto dhcpVal = getDHCPValue(config);
116     EthernetInterfaceIntf::dhcp4(dhcpVal.v4);
117     EthernetInterfaceIntf::dhcp6(dhcpVal.v6);
118     EthernetInterfaceIntf::ipv6AcceptRA(getIPv6AcceptRA(config));
119     EthernetInterfaceIntf::nicEnabled(enabled ? *enabled : queryNicEnabled());
120     const auto& gatewayList = manager.getRouteTable().getDefaultGateway();
121     const auto& gateway6List = manager.getRouteTable().getDefaultGateway6();
122     std::string defaultGateway;
123     std::string defaultGateway6;
124 
125     for (const auto& gateway : gatewayList)
126     {
127         if (gateway.first == intfName)
128         {
129             defaultGateway = gateway.second;
130             break;
131         }
132     }
133 
134     for (const auto& gateway6 : gateway6List)
135     {
136         if (gateway6.first == intfName)
137         {
138             defaultGateway6 = gateway6.second;
139             break;
140         }
141     }
142 
143     EthernetInterfaceIntf::defaultGateway(defaultGateway);
144     EthernetInterfaceIntf::defaultGateway6(defaultGateway6);
145     // Don't get the mac address from the system as the mac address
146     // would be same as parent interface.
147     if (intfName.find(".") == std::string::npos)
148     {
149         try
150         {
151             MacAddressIntf::macAddress(getMACAddress(intfName));
152         }
153         catch (const std::exception& e)
154         {
155             auto msg =
156                 fmt::format("Failed to get MAC for {}: {}", intfName, e.what());
157             log<level::ERR>(msg.c_str(),
158                             entry("INTERFACE=%s", intfName.c_str()));
159         }
160     }
161     EthernetInterfaceIntf::ntpServers(
162         config.map.getValueStrings("Network", "NTP"));
163 
164     EthernetInterfaceIntf::linkUp(linkUp());
165     EthernetInterfaceIntf::mtu(mtu());
166 
167     auto info = getLinkInfo(intfName);
168     EthernetInterfaceIntf::autoNeg(info.autoneg);
169     EthernetInterfaceIntf::speed(info.speed);
170 
171     // Emit deferred signal.
172     if (emitSignal)
173     {
174         this->emit_object_added();
175     }
176 }
177 
178 static IP::Protocol getProtocol(const InAddrAny& addr)
179 {
180     if (std::holds_alternative<in_addr>(addr))
181     {
182         return IP::Protocol::IPv4;
183     }
184     else if (std::holds_alternative<in6_addr>(addr))
185     {
186         return IP::Protocol::IPv6;
187     }
188 
189     throw std::runtime_error("Invalid addr type");
190 }
191 
192 bool EthernetInterface::dhcpIsEnabled(IP::Protocol family)
193 {
194     switch (family)
195     {
196         case IP::Protocol::IPv6:
197             return dhcp6();
198         case IP::Protocol::IPv4:
199             return dhcp4();
200     }
201     throw std::logic_error("Unreachable");
202 }
203 
204 bool EthernetInterface::originIsManuallyAssigned(IP::AddressOrigin origin)
205 {
206     return (
207 #ifdef LINK_LOCAL_AUTOCONFIGURATION
208         (origin == IP::AddressOrigin::Static)
209 #else
210         (origin == IP::AddressOrigin::Static ||
211          origin == IP::AddressOrigin::LinkLocal)
212 #endif
213 
214     );
215 }
216 
217 void EthernetInterface::createIPAddressObjects()
218 {
219     addrs.clear();
220 
221     AddressFilter filter;
222     filter.interface = ifIndex();
223     auto currentAddrs = getCurrentAddresses(filter);
224     for (const auto& addr : currentAddrs)
225     {
226         if (addr.flags & IFA_F_DEPRECATED)
227         {
228             continue;
229         }
230         auto address = toString(addr.address);
231         IP::Protocol addressType = getProtocol(addr.address);
232         IP::AddressOrigin origin = IP::AddressOrigin::Static;
233         if (dhcpIsEnabled(addressType))
234         {
235             origin = IP::AddressOrigin::DHCP;
236         }
237 #ifdef LINK_LOCAL_AUTOCONFIGURATION
238         if (addr.scope == RT_SCOPE_LINK)
239         {
240             origin = IP::AddressOrigin::LinkLocal;
241         }
242 #endif
243 
244         auto ipAddressObjectPath =
245             generateObjectPath(addressType, address, addr.prefix, origin);
246 
247         this->addrs.insert_or_assign(
248             address, std::make_unique<IPAddress>(bus, ipAddressObjectPath,
249                                                  *this, addressType, address,
250                                                  origin, addr.prefix));
251     }
252 }
253 
254 void EthernetInterface::createStaticNeighborObjects()
255 {
256     staticNeighbors.clear();
257 
258     NeighborFilter filter;
259     filter.interface = ifIndex();
260     filter.state = NUD_PERMANENT;
261     auto neighbors = getCurrentNeighbors(filter);
262     for (const auto& neighbor : neighbors)
263     {
264         if (!neighbor.mac)
265         {
266             continue;
267         }
268         auto ip = toString(neighbor.address);
269         auto mac = mac_address::toString(*neighbor.mac);
270         auto objectPath = generateStaticNeighborObjectPath(ip, mac);
271         staticNeighbors.emplace(
272             ip, std::make_unique<Neighbor>(bus, objectPath, *this, ip, mac,
273                                            Neighbor::State::Permanent));
274     }
275 }
276 
277 unsigned EthernetInterface::ifIndex() const
278 {
279     unsigned idx = if_nametoindex(interfaceName().c_str());
280     if (idx == 0)
281     {
282         throw std::system_error(errno, std::generic_category(),
283                                 "if_nametoindex");
284     }
285     return idx;
286 }
287 
288 ObjectPath EthernetInterface::ip(IP::Protocol protType, std::string ipaddress,
289                                  uint8_t prefixLength, std::string)
290 {
291     if (dhcpIsEnabled(protType))
292     {
293         log<level::INFO>("DHCP enabled on the interface, disabling"),
294             entry("INTERFACE=%s", interfaceName().c_str());
295         switch (protType)
296         {
297             case IP::Protocol::IPv4:
298                 dhcp4(false);
299                 break;
300             case IP::Protocol::IPv6:
301                 dhcp6(false);
302                 break;
303         }
304         // Delete the IP address object and that reloads the networkd
305         // to allow the same IP address to be set as Static IP
306         deleteObject(ipaddress);
307     }
308 
309     IP::AddressOrigin origin = IP::AddressOrigin::Static;
310 
311     int addressFamily = (protType == IP::Protocol::IPv4) ? AF_INET : AF_INET6;
312 
313     if (!isValidIP(addressFamily, ipaddress))
314     {
315         log<level::ERR>("Not a valid IP address"),
316             entry("ADDRESS=%s", ipaddress.c_str());
317         elog<InvalidArgument>(Argument::ARGUMENT_NAME("ipaddress"),
318                               Argument::ARGUMENT_VALUE(ipaddress.c_str()));
319     }
320 
321     if (!isValidPrefix(addressFamily, prefixLength))
322     {
323         log<level::ERR>("PrefixLength is not correct "),
324             entry("PREFIXLENGTH=%" PRIu8, prefixLength);
325         elog<InvalidArgument>(
326             Argument::ARGUMENT_NAME("prefixLength"),
327             Argument::ARGUMENT_VALUE(std::to_string(prefixLength).c_str()));
328     }
329 
330     auto objectPath =
331         generateObjectPath(protType, ipaddress, prefixLength, origin);
332     this->addrs.insert_or_assign(
333         ipaddress,
334         std::make_unique<IPAddress>(bus, objectPath, *this, protType, ipaddress,
335                                     origin, prefixLength));
336 
337     writeConfigurationFile();
338     manager.reloadConfigs();
339 
340     return objectPath;
341 }
342 
343 ObjectPath EthernetInterface::neighbor(std::string ipAddress,
344                                        std::string macAddress)
345 {
346     if (!isValidIP(ipAddress))
347     {
348         log<level::ERR>("Not a valid IP address",
349                         entry("ADDRESS=%s", ipAddress.c_str()));
350         elog<InvalidArgument>(Argument::ARGUMENT_NAME("ipAddress"),
351                               Argument::ARGUMENT_VALUE(ipAddress.c_str()));
352     }
353     if (!mac_address::isUnicast(mac_address::fromString(macAddress)))
354     {
355         log<level::ERR>("Not a valid MAC address",
356                         entry("MACADDRESS=%s", ipAddress.c_str()));
357         elog<InvalidArgument>(Argument::ARGUMENT_NAME("macAddress"),
358                               Argument::ARGUMENT_VALUE(macAddress.c_str()));
359     }
360 
361     auto objectPath = generateStaticNeighborObjectPath(ipAddress, macAddress);
362     staticNeighbors.emplace(
363         ipAddress,
364         std::make_unique<Neighbor>(bus, objectPath, *this, ipAddress,
365                                    macAddress, Neighbor::State::Permanent));
366 
367     writeConfigurationFile();
368     manager.reloadConfigs();
369 
370     return objectPath;
371 }
372 
373 /** @brief get the mac address of the interface.
374  *  @return macaddress on success
375  */
376 
377 std::string
378     EthernetInterface::getMACAddress(stdplus::const_zstring interfaceName) const
379 {
380     std::string activeMACAddr = MacAddressIntf::macAddress();
381 
382     ifreq ifr = {};
383     std::strncpy(ifr.ifr_name, interfaceName.c_str(), IFNAMSIZ - 1);
384     try
385     {
386         getIFSock().ioctl(SIOCGIFHWADDR, &ifr);
387     }
388     catch (const std::exception& e)
389     {
390         log<level::ERR>("ioctl failed for SIOCGIFHWADDR:",
391                         entry("ERROR=%s", e.what()));
392         elog<InternalFailure>();
393     }
394     return mac_address::toString(
395         stdplus::raw::refFrom<ether_addr>(ifr.ifr_hwaddr.sa_data));
396 }
397 
398 void EthernetInterface::deleteObject(std::string_view ipaddress)
399 {
400     auto it = addrs.find(ipaddress);
401     if (it == addrs.end())
402     {
403         log<level::ERR>("DeleteObject:Unable to find the object.");
404         return;
405     }
406     this->addrs.erase(it);
407 
408     writeConfigurationFile();
409     manager.reloadConfigs();
410 }
411 
412 void EthernetInterface::deleteStaticNeighborObject(std::string_view ipAddress)
413 {
414     auto it = staticNeighbors.find(ipAddress);
415     if (it == staticNeighbors.end())
416     {
417         log<level::ERR>(
418             "DeleteStaticNeighborObject:Unable to find the object.");
419         return;
420     }
421     staticNeighbors.erase(it);
422 
423     writeConfigurationFile();
424     manager.reloadConfigs();
425 }
426 
427 void EthernetInterface::deleteVLANFromSystem(stdplus::zstring_view interface)
428 {
429     const auto& confDir = manager.getConfDir();
430     auto networkFile = config::pathForIntfConf(confDir, interface);
431     auto deviceFile = config::pathForIntfDev(confDir, interface);
432 
433     // delete the vlan network file
434     std::error_code ec;
435     std::filesystem::remove(networkFile, ec);
436     std::filesystem::remove(deviceFile, ec);
437 
438     // TODO  systemd doesn't delete the virtual network interface
439     // even after deleting all the related configuartion.
440     // https://github.com/systemd/systemd/issues/6600
441     try
442     {
443         deleteInterface(interface);
444     }
445     catch (const InternalFailure& e)
446     {
447         commit<InternalFailure>();
448     }
449 }
450 
451 void EthernetInterface::deleteVLANObject(stdplus::zstring_view interface)
452 {
453     auto it = vlanInterfaces.find(interface);
454     if (it == vlanInterfaces.end())
455     {
456         log<level::ERR>("DeleteVLANObject:Unable to find the object",
457                         entry("INTERFACE=%s", interface.c_str()));
458         return;
459     }
460 
461     deleteVLANFromSystem(interface);
462     // delete the interface
463     vlanInterfaces.erase(it);
464 
465     writeConfigurationFile();
466     manager.reloadConfigs();
467 }
468 
469 std::string EthernetInterface::generateObjectPath(
470     IP::Protocol addressType, std::string_view ipAddress, uint8_t prefixLength,
471     IP::AddressOrigin origin) const
472 {
473     std::string_view type;
474     switch (addressType)
475     {
476         case IP::Protocol::IPv4:
477             type = "ipv4"sv;
478             break;
479         case IP::Protocol::IPv6:
480             type = "ipv6"sv;
481             break;
482     }
483     return fmt::format(
484         FMT_COMPILE("{}/{}/{:08x}"), objPath, type,
485         static_cast<uint32_t>(hash_multi(
486             ipAddress, prefixLength,
487             static_cast<std::underlying_type_t<IP::AddressOrigin>>(origin))));
488 }
489 
490 std::string EthernetInterface::generateStaticNeighborObjectPath(
491     std::string_view ipAddress, std::string_view macAddress) const
492 {
493     return fmt::format(
494         FMT_COMPILE("{}/static_neighbor/{:08x}"), objPath,
495         static_cast<uint32_t>(hash_multi(ipAddress, macAddress)));
496 }
497 
498 bool EthernetInterface::ipv6AcceptRA(bool value)
499 {
500     if (ipv6AcceptRA() != EthernetInterfaceIntf::ipv6AcceptRA(value))
501     {
502         writeConfigurationFile();
503         manager.reloadConfigs();
504     }
505     return value;
506 }
507 
508 bool EthernetInterface::dhcp4(bool value)
509 {
510     if (dhcp4() != EthernetInterfaceIntf::dhcp4(value))
511     {
512         writeConfigurationFile();
513         manager.reloadConfigs();
514     }
515     return value;
516 }
517 
518 bool EthernetInterface::dhcp6(bool value)
519 {
520     if (dhcp6() != EthernetInterfaceIntf::dhcp6(value))
521     {
522         writeConfigurationFile();
523         manager.reloadConfigs();
524     }
525     return value;
526 }
527 
528 EthernetInterface::DHCPConf EthernetInterface::dhcpEnabled(DHCPConf value)
529 {
530     auto old4 = EthernetInterfaceIntf::dhcp4();
531     auto new4 = EthernetInterfaceIntf::dhcp4(value == DHCPConf::v4 ||
532                                              value == DHCPConf::v4v6stateless ||
533                                              value == DHCPConf::both);
534     auto old6 = EthernetInterfaceIntf::dhcp6();
535     auto new6 = EthernetInterfaceIntf::dhcp6(value == DHCPConf::v6 ||
536                                              value == DHCPConf::both);
537     auto oldra = EthernetInterfaceIntf::ipv6AcceptRA();
538     auto newra = EthernetInterfaceIntf::ipv6AcceptRA(
539         value == DHCPConf::v6stateless || value == DHCPConf::v4v6stateless ||
540         value == DHCPConf::v6 || value == DHCPConf::both);
541 
542     if (old4 != new4 || old6 != new6 || oldra != newra)
543     {
544         writeConfigurationFile();
545         manager.reloadConfigs();
546     }
547     return value;
548 }
549 
550 EthernetInterface::DHCPConf EthernetInterface::dhcpEnabled() const
551 {
552     if (dhcp6())
553     {
554         return dhcp4() ? DHCPConf::both : DHCPConf::v6;
555     }
556     else if (dhcp4())
557     {
558         return ipv6AcceptRA() ? DHCPConf::v4v6stateless : DHCPConf::v4;
559     }
560     return ipv6AcceptRA() ? DHCPConf::v6stateless : DHCPConf::none;
561 }
562 
563 bool EthernetInterface::linkUp() const
564 {
565     bool value = EthernetInterfaceIntf::linkUp();
566 
567     ifreq ifr = {};
568     std::strncpy(ifr.ifr_name, interfaceName().c_str(), IF_NAMESIZE - 1);
569     try
570     {
571         getIFSock().ioctl(SIOCGIFFLAGS, &ifr);
572         value = static_cast<bool>(ifr.ifr_flags & IFF_RUNNING);
573     }
574     catch (const std::exception& e)
575     {
576         log<level::ERR>("ioctl failed for SIOCGIFFLAGS:",
577                         entry("ERROR=%s", e.what()));
578     }
579     return value;
580 }
581 
582 size_t EthernetInterface::mtu() const
583 {
584     size_t value = EthernetInterfaceIntf::mtu();
585 
586     ifreq ifr = {};
587     std::strncpy(ifr.ifr_name, interfaceName().c_str(), IF_NAMESIZE - 1);
588     try
589     {
590         getIFSock().ioctl(SIOCGIFMTU, &ifr);
591         value = ifr.ifr_mtu;
592     }
593     catch (const std::exception& e)
594     {
595         log<level::ERR>("ioctl failed for SIOCGIFMTU:",
596                         entry("ERROR=%s", e.what()));
597     }
598     return value;
599 }
600 
601 size_t EthernetInterface::mtu(size_t value)
602 {
603     if (value == EthernetInterfaceIntf::mtu())
604     {
605         return value;
606     }
607     else if (value == 0)
608     {
609         return EthernetInterfaceIntf::mtu();
610     }
611 
612     ifreq ifr = {};
613     std::strncpy(ifr.ifr_name, interfaceName().c_str(), IF_NAMESIZE - 1);
614     ifr.ifr_mtu = value;
615 
616     try
617     {
618         getIFSock().ioctl(SIOCSIFMTU, &ifr);
619     }
620     catch (const std::exception& e)
621     {
622         log<level::ERR>("ioctl failed for SIOCSIFMTU:",
623                         entry("ERROR=%s", strerror(errno)));
624         return EthernetInterfaceIntf::mtu();
625     }
626 
627     EthernetInterfaceIntf::mtu(value);
628     return value;
629 }
630 
631 bool EthernetInterface::queryNicEnabled() const
632 {
633     constexpr auto svc = "org.freedesktop.network1";
634     constexpr auto intf = "org.freedesktop.network1.Link";
635     constexpr auto prop = "AdministrativeState";
636     char* rpath;
637     sd_bus_path_encode("/org/freedesktop/network1/link",
638                        std::to_string(ifIndex()).c_str(), &rpath);
639     std::string path(rpath);
640     free(rpath);
641 
642     // Store / Parser for the AdministrativeState return value
643     std::optional<bool> ret;
644     auto cb = [&](std::string_view state) {
645         if (state != "initialized")
646         {
647             ret = state != "unmanaged";
648         }
649     };
650 
651     // Build a matcher before making the property call to ensure we
652     // can eventually get the value.
653     sdbusplus::bus::match_t match(
654         bus,
655         fmt::format("type='signal',sender='{}',path='{}',interface='{}',member="
656                     "'PropertiesChanged',arg0='{}',",
657                     svc, path, PROPERTY_INTERFACE, intf)
658             .c_str(),
659         [&](sdbusplus::message_t& m) {
660             std::string intf;
661             std::unordered_map<std::string, std::variant<std::string>> values;
662             try
663             {
664                 m.read(intf, values);
665                 auto it = values.find(prop);
666                 // Ignore properties that aren't AdministrativeState
667                 if (it != values.end())
668                 {
669                     cb(std::get<std::string>(it->second));
670                 }
671             }
672             catch (const std::exception& e)
673             {
674                 log<level::ERR>(
675                     fmt::format(
676                         "AdministrativeState match parsing failed on {}: {}",
677                         interfaceName(), e.what())
678                         .c_str(),
679                     entry("INTERFACE=%s", interfaceName().c_str()),
680                     entry("ERROR=%s", e.what()));
681             }
682         });
683 
684     // Actively call for the value in case the interface is already configured
685     auto method =
686         bus.new_method_call(svc, path.c_str(), PROPERTY_INTERFACE, METHOD_GET);
687     method.append(intf, prop);
688     try
689     {
690         auto reply = bus.call(method);
691         std::variant<std::string> state;
692         reply.read(state);
693         cb(std::get<std::string>(state));
694     }
695     catch (const std::exception& e)
696     {
697         log<level::ERR>(
698             fmt::format("Failed to get AdministrativeState on {}: {}",
699                         interfaceName(), e.what())
700                 .c_str(),
701             entry("INTERFACE=%s", interfaceName().c_str()),
702             entry("ERROR=%s", e.what()));
703     }
704 
705     // The interface is not yet configured by systemd-networkd, wait until it
706     // signals us a valid state.
707     while (!ret)
708     {
709         bus.wait();
710         bus.process_discard();
711     }
712 
713     return *ret;
714 }
715 
716 static void setNICAdminState(stdplus::const_zstring intf, bool up)
717 {
718     ifreq ifr = {};
719     std::strncpy(ifr.ifr_name, intf.data(), IF_NAMESIZE - 1);
720     getIFSock().ioctl(SIOCGIFFLAGS, &ifr);
721 
722     ifr.ifr_flags &= ~IFF_UP;
723     ifr.ifr_flags |= up ? IFF_UP : 0;
724     getIFSock().ioctl(SIOCSIFFLAGS, &ifr);
725 }
726 
727 bool EthernetInterface::nicEnabled(bool value)
728 {
729     if (value == EthernetInterfaceIntf::nicEnabled())
730     {
731         return value;
732     }
733 
734     EthernetInterfaceIntf::nicEnabled(value);
735     writeConfigurationFile();
736     if (!value)
737     {
738         // We only need to bring down the interface, networkd will always bring
739         // up managed interfaces
740         manager.addReloadPreHook(
741             [ifname = interfaceName()]() { setNICAdminState(ifname, false); });
742     }
743     manager.reloadConfigs();
744 
745     return value;
746 }
747 
748 ServerList EthernetInterface::staticNameServers(ServerList value)
749 {
750     for (const auto& nameserverip : value)
751     {
752         if (!isValidIP(nameserverip))
753         {
754             log<level::ERR>("Not a valid IP address"),
755                 entry("ADDRESS=%s", nameserverip.c_str());
756             elog<InvalidArgument>(
757                 Argument::ARGUMENT_NAME("StaticNameserver"),
758                 Argument::ARGUMENT_VALUE(nameserverip.c_str()));
759         }
760     }
761     try
762     {
763         EthernetInterfaceIntf::staticNameServers(value);
764 
765         writeConfigurationFile();
766         manager.reloadConfigs();
767     }
768     catch (const InternalFailure& e)
769     {
770         log<level::ERR>("Exception processing DNS entries");
771     }
772     return EthernetInterfaceIntf::staticNameServers();
773 }
774 
775 void EthernetInterface::loadNTPServers(const config::Parser& config)
776 {
777     EthernetInterfaceIntf::ntpServers(getNTPServerFromTimeSyncd());
778     EthernetInterfaceIntf::staticNTPServers(
779         config.map.getValueStrings("Network", "NTP"));
780 }
781 
782 void EthernetInterface::loadNameServers(const config::Parser& config)
783 {
784     EthernetInterfaceIntf::nameservers(getNameServerFromResolvd());
785     EthernetInterfaceIntf::staticNameServers(
786         config.map.getValueStrings("Network", "DNS"));
787 }
788 
789 ServerList EthernetInterface::getNTPServerFromTimeSyncd()
790 {
791     ServerList servers; // Variable to capture the NTP Server IPs
792     auto method = bus.new_method_call(TIMESYNCD_SERVICE, TIMESYNCD_SERVICE_PATH,
793                                       PROPERTY_INTERFACE, METHOD_GET);
794 
795     method.append(TIMESYNCD_INTERFACE, "LinkNTPServers");
796 
797     try
798     {
799         auto reply = bus.call(method);
800         std::variant<ServerList> response;
801         reply.read(response);
802         servers = std::get<ServerList>(response);
803     }
804     catch (const sdbusplus::exception::SdBusError& e)
805     {
806         log<level::ERR>(
807             "Failed to get NTP server information from Systemd-Timesyncd");
808     }
809 
810     return servers;
811 }
812 
813 ServerList EthernetInterface::getNameServerFromResolvd()
814 {
815     ServerList servers;
816     std::string OBJ_PATH = RESOLVED_SERVICE_PATH + std::to_string(ifIndex());
817 
818     /*
819       The DNS property under org.freedesktop.resolve1.Link interface contains
820       an array containing all DNS servers currently used by resolved. It
821       contains similar information as the DNS server data written to
822       /run/systemd/resolve/resolv.conf.
823 
824       Each structure in the array consists of a numeric network interface index,
825       an address family, and a byte array containing the DNS server address
826       (either 4 bytes in length for IPv4 or 16 bytes in lengths for IPv6).
827       The array contains DNS servers configured system-wide, including those
828       possibly read from a foreign /etc/resolv.conf or the DNS= setting in
829       /etc/systemd/resolved.conf, as well as per-interface DNS server
830       information either retrieved from systemd-networkd or configured by
831       external software via SetLinkDNS().
832     */
833 
834     using type = std::vector<std::tuple<int32_t, std::vector<uint8_t>>>;
835     std::variant<type> name; // Variable to capture the DNS property
836     auto method = bus.new_method_call(RESOLVED_SERVICE, OBJ_PATH.c_str(),
837                                       PROPERTY_INTERFACE, METHOD_GET);
838 
839     method.append(RESOLVED_INTERFACE, "DNS");
840 
841     try
842     {
843         auto reply = bus.call(method);
844         reply.read(name);
845     }
846     catch (const sdbusplus::exception_t& e)
847     {
848         log<level::ERR>("Failed to get DNS information from Systemd-Resolved");
849     }
850     auto tupleVector = std::get_if<type>(&name);
851     for (auto i = tupleVector->begin(); i != tupleVector->end(); ++i)
852     {
853         int addressFamily = std::get<0>(*i);
854         std::vector<uint8_t>& ipaddress = std::get<1>(*i);
855 
856         switch (addressFamily)
857         {
858             case AF_INET:
859                 if (ipaddress.size() == sizeof(struct in_addr))
860                 {
861                     servers.push_back(toString(
862                         *reinterpret_cast<struct in_addr*>(ipaddress.data())));
863                 }
864                 else
865                 {
866                     log<level::ERR>(
867                         "Invalid data recived from Systemd-Resolved");
868                 }
869                 break;
870 
871             case AF_INET6:
872                 if (ipaddress.size() == sizeof(struct in6_addr))
873                 {
874                     servers.push_back(toString(
875                         *reinterpret_cast<struct in6_addr*>(ipaddress.data())));
876                 }
877                 else
878                 {
879                     log<level::ERR>(
880                         "Invalid data recived from Systemd-Resolved");
881                 }
882                 break;
883 
884             default:
885                 log<level::ERR>(
886                     "Unsupported address family in DNS from Systemd-Resolved");
887                 break;
888         }
889     }
890     return servers;
891 }
892 
893 std::string EthernetInterface::vlanIntfName(VlanId id) const
894 {
895     return fmt::format(FMT_COMPILE("{}.{}"), interfaceName(), id);
896 }
897 
898 std::string EthernetInterface::vlanObjPath(VlanId id) const
899 {
900     return fmt::format(FMT_COMPILE("{}_{}"), objPath, id);
901 }
902 
903 void EthernetInterface::loadVLAN(VlanId id)
904 {
905     auto vlanInterfaceName = vlanIntfName(id);
906     auto path = vlanObjPath(id);
907 
908     config::Parser config(
909         config::pathForIntfConf(manager.getConfDir(), vlanInterfaceName));
910 
911     auto vlanIntf = std::make_unique<phosphor::network::VlanInterface>(
912         bus, path.c_str(), config, EthernetInterfaceIntf::nicEnabled(), id,
913         *this, manager);
914 
915     // Fetch the ip address from the system
916     // and create the dbus object.
917     vlanIntf->createIPAddressObjects();
918     vlanIntf->createStaticNeighborObjects();
919     vlanIntf->loadNameServers(config);
920     vlanIntf->loadNTPServers(config);
921 
922     this->vlanInterfaces.emplace(std::move(vlanInterfaceName),
923                                  std::move(vlanIntf));
924 }
925 
926 ObjectPath EthernetInterface::createVLAN(VlanId id)
927 {
928     auto vlanInterfaceName = vlanIntfName(id);
929     if (this->vlanInterfaces.find(vlanInterfaceName) !=
930         this->vlanInterfaces.end())
931     {
932         log<level::ERR>("VLAN already exists", entry("VLANID=%u", id));
933         elog<InvalidArgument>(
934             Argument::ARGUMENT_NAME("VLANId"),
935             Argument::ARGUMENT_VALUE(std::to_string(id).c_str()));
936     }
937 
938     auto path = vlanObjPath(id);
939 
940     // Pass the parents nicEnabled property, so that the child
941     // VLAN interface can inherit.
942     auto vlanIntf = std::make_unique<phosphor::network::VlanInterface>(
943         bus, path.c_str(), config::Parser(),
944         EthernetInterfaceIntf::nicEnabled(), id, *this, manager);
945 
946     // write the device file for the vlan interface.
947     vlanIntf->writeDeviceFile();
948 
949     this->vlanInterfaces.emplace(vlanInterfaceName, std::move(vlanIntf));
950 
951     writeConfigurationFile();
952     manager.reloadConfigs();
953 
954     return path;
955 }
956 
957 ServerList EthernetInterface::staticNTPServers(ServerList value)
958 {
959     try
960     {
961         EthernetInterfaceIntf::staticNTPServers(value);
962 
963         writeConfigurationFile();
964         manager.reloadConfigs();
965     }
966     catch (InternalFailure& e)
967     {
968         log<level::ERR>("Exception processing NTP entries");
969     }
970     return EthernetInterfaceIntf::staticNTPServers();
971 }
972 
973 ServerList EthernetInterface::ntpServers(ServerList /*servers*/)
974 {
975     elog<NotAllowed>(NotAllowedArgument::REASON("ReadOnly Property"));
976 }
977 // Need to merge the below function with the code which writes the
978 // config file during factory reset.
979 // TODO openbmc/openbmc#1751
980 
981 void EthernetInterface::writeConfigurationFile()
982 {
983     for (const auto& intf : vlanInterfaces)
984     {
985         intf.second->writeConfigurationFile();
986     }
987 
988     config::Parser config;
989     config.map["Match"].emplace_back()["Name"].emplace_back(interfaceName());
990     {
991         auto& link = config.map["Link"].emplace_back();
992 #ifdef PERSIST_MAC
993         auto mac = MacAddressIntf::macAddress();
994         if (!mac.empty())
995         {
996             link["MACAddress"].emplace_back(mac);
997         }
998 #endif
999         if (!EthernetInterfaceIntf::nicEnabled())
1000         {
1001             link["Unmanaged"].emplace_back("yes");
1002         }
1003     }
1004     {
1005         auto& network = config.map["Network"].emplace_back();
1006         auto& lla = network["LinkLocalAddressing"];
1007 #ifdef LINK_LOCAL_AUTOCONFIGURATION
1008         lla.emplace_back("yes");
1009 #else
1010         lla.emplace_back("no");
1011 #endif
1012         network["IPv6AcceptRA"].emplace_back(ipv6AcceptRA() ? "true" : "false");
1013         network["DHCP"].emplace_back(dhcp4() ? (dhcp6() ? "true" : "ipv4")
1014                                              : (dhcp6() ? "ipv6" : "false"));
1015         {
1016             auto& vlans = network["VLAN"];
1017             for (const auto& intf : vlanInterfaces)
1018             {
1019                 vlans.emplace_back(
1020                     intf.second->EthernetInterface::interfaceName());
1021             }
1022         }
1023         {
1024             auto& ntps = network["NTP"];
1025             for (const auto& ntp : EthernetInterfaceIntf::staticNTPServers())
1026             {
1027                 ntps.emplace_back(ntp);
1028             }
1029         }
1030         {
1031             auto& dnss = network["DNS"];
1032             for (const auto& dns : EthernetInterfaceIntf::staticNameServers())
1033             {
1034                 dnss.emplace_back(dns);
1035             }
1036         }
1037         {
1038             auto& address = network["Address"];
1039             for (const auto& addr : getAddresses())
1040             {
1041                 if (originIsManuallyAssigned(addr.second->origin()) &&
1042                     !dhcpIsEnabled(addr.second->type()))
1043                 {
1044                     address.emplace_back(
1045                         fmt::format("{}/{}", addr.second->address(),
1046                                     addr.second->prefixLength()));
1047                 }
1048             }
1049         }
1050         {
1051             auto& gateways = network["Gateway"];
1052             if (!dhcp4())
1053             {
1054                 auto gateway = EthernetInterfaceIntf::defaultGateway();
1055                 if (!gateway.empty())
1056                 {
1057                     gateways.emplace_back(gateway);
1058                 }
1059             }
1060 
1061             if (!dhcp6())
1062             {
1063                 auto gateway6 = EthernetInterfaceIntf::defaultGateway6();
1064                 if (!gateway6.empty())
1065                 {
1066                     gateways.emplace_back(gateway6);
1067                 }
1068             }
1069         }
1070     }
1071     config.map["IPv6AcceptRA"].emplace_back()["DHCPv6Client"].emplace_back(
1072         dhcp6() ? "true" : "false");
1073     {
1074         auto& neighbors = config.map["Neighbor"];
1075         for (const auto& sneighbor : staticNeighbors)
1076         {
1077             auto& neighbor = neighbors.emplace_back();
1078             neighbor["Address"].emplace_back(sneighbor.second->ipAddress());
1079             neighbor["MACAddress"].emplace_back(sneighbor.second->macAddress());
1080         }
1081     }
1082     {
1083         auto& dhcp = config.map["DHCP"].emplace_back();
1084         dhcp["ClientIdentifier"].emplace_back("mac");
1085         if (manager.getDHCPConf())
1086         {
1087             const auto& conf = *manager.getDHCPConf();
1088             auto dns_enabled = conf.dnsEnabled() ? "true" : "false";
1089             dhcp["UseDNS"].emplace_back(dns_enabled);
1090             dhcp["UseDomains"].emplace_back(dns_enabled);
1091             dhcp["UseNTP"].emplace_back(conf.ntpEnabled() ? "true" : "false");
1092             dhcp["UseHostname"].emplace_back(conf.hostNameEnabled() ? "true"
1093                                                                     : "false");
1094             dhcp["SendHostname"].emplace_back(
1095                 conf.sendHostNameEnabled() ? "true" : "false");
1096         }
1097     }
1098     auto path = config::pathForIntfConf(manager.getConfDir(), interfaceName());
1099     config.writeFile(path);
1100     auto msg = fmt::format("Wrote networkd file: {}", path.native());
1101     log<level::INFO>(msg.c_str(), entry("FILE=%s", path.c_str()));
1102 }
1103 
1104 std::string EthernetInterface::macAddress([[maybe_unused]] std::string value)
1105 {
1106 #ifdef PERSIST_MAC
1107     ether_addr newMAC;
1108     try
1109     {
1110         newMAC = mac_address::fromString(value);
1111     }
1112     catch (const std::invalid_argument&)
1113     {
1114         log<level::ERR>("MACAddress is not valid.",
1115                         entry("MAC=%s", value.c_str()));
1116         elog<InvalidArgument>(Argument::ARGUMENT_NAME("MACAddress"),
1117                               Argument::ARGUMENT_VALUE(value.c_str()));
1118     }
1119     if (!mac_address::isUnicast(newMAC))
1120     {
1121         log<level::ERR>("MACAddress is not valid.",
1122                         entry("MAC=%s", value.c_str()));
1123         elog<InvalidArgument>(Argument::ARGUMENT_NAME("MACAddress"),
1124                               Argument::ARGUMENT_VALUE(value.c_str()));
1125     }
1126 
1127     auto interface = interfaceName();
1128     std::string validMAC = mac_address::toString(newMAC);
1129 
1130     // We don't need to update the system if the address is unchanged
1131     ether_addr oldMAC = mac_address::fromString(MacAddressIntf::macAddress());
1132     if (!stdplus::raw::equal(newMAC, oldMAC))
1133     {
1134         // Update everything that depends on the MAC value
1135         for (const auto& [name, intf] : vlanInterfaces)
1136         {
1137             intf->MacAddressIntf::macAddress(validMAC);
1138         }
1139         MacAddressIntf::macAddress(validMAC);
1140 
1141         writeConfigurationFile();
1142         manager.addReloadPreHook([interface]() {
1143             // The MAC and LLADDRs will only update if the NIC is already down
1144             setNICAdminState(interface, false);
1145         });
1146         manager.reloadConfigs();
1147     }
1148 
1149 #ifdef HAVE_UBOOT_ENV
1150     // Ensure that the valid address is stored in the u-boot-env
1151     auto envVar = interfaceToUbootEthAddr(interface);
1152     if (envVar)
1153     {
1154         // Trimming MAC addresses that are out of range. eg: AA:FF:FF:FF:FF:100;
1155         // and those having more than 6 bytes. eg: AA:AA:AA:AA:AA:AA:BB
1156         execute("/sbin/fw_setenv", "fw_setenv", envVar->c_str(),
1157                 validMAC.c_str());
1158     }
1159 #endif // HAVE_UBOOT_ENV
1160 
1161     return value;
1162 #else
1163     elog<NotAllowed>(
1164         NotAllowedArgument::REASON("Writing MAC address is not allowed"));
1165 #endif // PERSIST_MAC
1166 }
1167 
1168 void EthernetInterface::deleteAll()
1169 {
1170     // clear all the ip on the interface
1171     addrs.clear();
1172 
1173     writeConfigurationFile();
1174     manager.reloadConfigs();
1175 }
1176 
1177 std::string EthernetInterface::defaultGateway(std::string gateway)
1178 {
1179     auto gw = EthernetInterfaceIntf::defaultGateway();
1180     if (gw == gateway)
1181     {
1182         return gw;
1183     }
1184 
1185     if (!isValidIP(AF_INET, gateway) && !gateway.empty())
1186     {
1187         log<level::ERR>("Not a valid v4 Gateway",
1188                         entry("GATEWAY=%s", gateway.c_str()));
1189         elog<InvalidArgument>(Argument::ARGUMENT_NAME("GATEWAY"),
1190                               Argument::ARGUMENT_VALUE(gateway.c_str()));
1191     }
1192     gw = EthernetInterfaceIntf::defaultGateway(gateway);
1193 
1194     writeConfigurationFile();
1195     manager.reloadConfigs();
1196 
1197     return gw;
1198 }
1199 
1200 std::string EthernetInterface::defaultGateway6(std::string gateway)
1201 {
1202     auto gw = EthernetInterfaceIntf::defaultGateway6();
1203     if (gw == gateway)
1204     {
1205         return gw;
1206     }
1207 
1208     if (!isValidIP(AF_INET6, gateway) && !gateway.empty())
1209     {
1210         log<level::ERR>("Not a valid v6 Gateway",
1211                         entry("GATEWAY=%s", gateway.c_str()));
1212         elog<InvalidArgument>(Argument::ARGUMENT_NAME("GATEWAY"),
1213                               Argument::ARGUMENT_VALUE(gateway.c_str()));
1214     }
1215     gw = EthernetInterfaceIntf::defaultGateway6(gateway);
1216 
1217     writeConfigurationFile();
1218     manager.reloadConfigs();
1219 
1220     return gw;
1221 }
1222 } // namespace network
1223 } // namespace phosphor
1224