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