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