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