xref: /openbmc/phosphor-networkd/src/ethernet_interface.cpp (revision c922d5e8f5baed49b605b378b45a226c0d657286)
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 
788     EthernetInterfaceIntf::nicEnabled(value);
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 = interfaceName(), eifSocket = std::move(eifSocket)]() {
796                 setNICAdminState(eifSocket.sock, ifname.c_str(), false);
797             });
798     }
799     manager.reloadConfigs();
800 
801     return value;
802 }
803 
804 ServerList EthernetInterface::nameservers(ServerList /*value*/)
805 {
806     elog<NotAllowed>(NotAllowedArgument::REASON("ReadOnly Property"));
807     return EthernetInterfaceIntf::nameservers();
808 }
809 
810 ServerList EthernetInterface::staticNameServers(ServerList value)
811 {
812     for (const auto& nameserverip : value)
813     {
814         if (!isValidIP(AF_INET, nameserverip) &&
815             !isValidIP(AF_INET6, nameserverip))
816         {
817             log<level::ERR>("Not a valid IP address"),
818                 entry("ADDRESS=%s", nameserverip.c_str());
819             elog<InvalidArgument>(
820                 Argument::ARGUMENT_NAME("StaticNameserver"),
821                 Argument::ARGUMENT_VALUE(nameserverip.c_str()));
822         }
823     }
824     try
825     {
826         EthernetInterfaceIntf::staticNameServers(value);
827 
828         writeConfigurationFile();
829         manager.reloadConfigs();
830     }
831     catch (const InternalFailure& e)
832     {
833         log<level::ERR>("Exception processing DNS entries");
834     }
835     return EthernetInterfaceIntf::staticNameServers();
836 }
837 
838 void EthernetInterface::loadNameServers()
839 {
840     EthernetInterfaceIntf::nameservers(getNameServerFromResolvd());
841     EthernetInterfaceIntf::staticNameServers(getstaticNameServerFromConf());
842 }
843 
844 ServerList EthernetInterface::getstaticNameServerFromConf()
845 {
846     fs::path confPath = manager.getConfDir();
847 
848     std::string fileName = systemd::config::networkFilePrefix +
849                            interfaceName() + systemd::config::networkFileSuffix;
850     confPath /= fileName;
851     ServerList servers;
852     config::Parser parser(confPath.string());
853     auto rc = config::ReturnCode::SUCCESS;
854 
855     std::tie(rc, servers) = parser.getValues("Network", "DNS");
856     if (rc != config::ReturnCode::SUCCESS)
857     {
858         log<level::DEBUG>("Unable to get the value for network[DNS]",
859                           entry("RC=%d", rc));
860     }
861     return servers;
862 }
863 
864 ServerList EthernetInterface::getNameServerFromResolvd()
865 {
866     ServerList servers;
867     std::string OBJ_PATH = RESOLVED_SERVICE_PATH + std::to_string(ifIndex());
868 
869     /*
870       The DNS property under org.freedesktop.resolve1.Link interface contains
871       an array containing all DNS servers currently used by resolved. It
872       contains similar information as the DNS server data written to
873       /run/systemd/resolve/resolv.conf.
874 
875       Each structure in the array consists of a numeric network interface index,
876       an address family, and a byte array containing the DNS server address
877       (either 4 bytes in length for IPv4 or 16 bytes in lengths for IPv6).
878       The array contains DNS servers configured system-wide, including those
879       possibly read from a foreign /etc/resolv.conf or the DNS= setting in
880       /etc/systemd/resolved.conf, as well as per-interface DNS server
881       information either retrieved from systemd-networkd or configured by
882       external software via SetLinkDNS().
883     */
884 
885     using type = std::vector<std::tuple<int32_t, std::vector<uint8_t>>>;
886     std::variant<type> name; // Variable to capture the DNS property
887     auto method = bus.new_method_call(RESOLVED_SERVICE, OBJ_PATH.c_str(),
888                                       PROPERTY_INTERFACE, METHOD_GET);
889 
890     method.append(RESOLVED_INTERFACE, "DNS");
891     auto reply = bus.call(method);
892 
893     try
894     {
895         reply.read(name);
896     }
897     catch (const sdbusplus::exception::exception& e)
898     {
899         log<level::ERR>("Failed to get DNS information from Systemd-Resolved");
900     }
901     auto tupleVector = std::get_if<type>(&name);
902     for (auto i = tupleVector->begin(); i != tupleVector->end(); ++i)
903     {
904         int addressFamily = std::get<0>(*i);
905         std::vector<uint8_t>& ipaddress = std::get<1>(*i);
906 
907         switch (addressFamily)
908         {
909             case AF_INET:
910                 if (ipaddress.size() == sizeof(struct in_addr))
911                 {
912                     servers.push_back(toString(
913                         *reinterpret_cast<struct in_addr*>(ipaddress.data())));
914                 }
915                 else
916                 {
917                     log<level::ERR>(
918                         "Invalid data recived from Systemd-Resolved");
919                 }
920                 break;
921 
922             case AF_INET6:
923                 if (ipaddress.size() == sizeof(struct in6_addr))
924                 {
925                     servers.push_back(toString(
926                         *reinterpret_cast<struct in6_addr*>(ipaddress.data())));
927                 }
928                 else
929                 {
930                     log<level::ERR>(
931                         "Invalid data recived from Systemd-Resolved");
932                 }
933                 break;
934 
935             default:
936                 log<level::ERR>(
937                     "Unsupported address family in DNS from Systemd-Resolved");
938                 break;
939         }
940     }
941     return servers;
942 }
943 
944 void EthernetInterface::loadVLAN(VlanId id)
945 {
946     std::string vlanInterfaceName = interfaceName() + "." + std::to_string(id);
947     std::string path = objPath;
948     path += "_" + std::to_string(id);
949 
950     DHCPConf dhcpEnabled =
951         getDHCPValue(manager.getConfDir().string(), vlanInterfaceName);
952     auto vlanIntf = std::make_unique<phosphor::network::VlanInterface>(
953         bus, path.c_str(), dhcpEnabled, EthernetInterfaceIntf::nicEnabled(), id,
954         *this, manager);
955 
956     // Fetch the ip address from the system
957     // and create the dbus object.
958     vlanIntf->createIPAddressObjects();
959     vlanIntf->createStaticNeighborObjects();
960     vlanIntf->loadNameServers();
961 
962     this->vlanInterfaces.emplace(std::move(vlanInterfaceName),
963                                  std::move(vlanIntf));
964 }
965 
966 ObjectPath EthernetInterface::createVLAN(VlanId id)
967 {
968     std::string vlanInterfaceName = interfaceName() + "." + std::to_string(id);
969     std::string path = objPath;
970     path += "_" + std::to_string(id);
971 
972     // Pass the parents nicEnabled property, so that the child
973     // VLAN interface can inherit.
974 
975     auto vlanIntf = std::make_unique<phosphor::network::VlanInterface>(
976         bus, path.c_str(), EthernetInterface::DHCPConf::none,
977         EthernetInterfaceIntf::nicEnabled(), id, *this, manager);
978 
979     // write the device file for the vlan interface.
980     vlanIntf->writeDeviceFile();
981 
982     this->vlanInterfaces.emplace(vlanInterfaceName, std::move(vlanIntf));
983 
984     writeConfigurationFile();
985     manager.reloadConfigs();
986 
987     return path;
988 }
989 
990 bool EthernetInterface::getIPv6AcceptRAFromConf()
991 {
992     fs::path confPath = manager.getConfDir();
993 
994     std::string fileName = systemd::config::networkFilePrefix +
995                            interfaceName() + systemd::config::networkFileSuffix;
996     confPath /= fileName;
997     config::ValueList values;
998     config::Parser parser(confPath.string());
999     auto rc = config::ReturnCode::SUCCESS;
1000     std::tie(rc, values) = parser.getValues("Network", "IPv6AcceptRA");
1001     if (rc != config::ReturnCode::SUCCESS)
1002     {
1003         log<level::DEBUG>("Unable to get the value for Network[IPv6AcceptRA]",
1004                           entry("rc=%d", rc));
1005         return false;
1006     }
1007     return (values[0] == "true");
1008 }
1009 
1010 ServerList EthernetInterface::getNTPServersFromConf()
1011 {
1012     fs::path confPath = manager.getConfDir();
1013 
1014     std::string fileName = systemd::config::networkFilePrefix +
1015                            interfaceName() + systemd::config::networkFileSuffix;
1016     confPath /= fileName;
1017 
1018     ServerList servers;
1019     config::Parser parser(confPath.string());
1020     auto rc = config::ReturnCode::SUCCESS;
1021 
1022     std::tie(rc, servers) = parser.getValues("Network", "NTP");
1023     if (rc != config::ReturnCode::SUCCESS)
1024     {
1025         log<level::DEBUG>("Unable to get the value for Network[NTP]",
1026                           entry("rc=%d", rc));
1027     }
1028 
1029     return servers;
1030 }
1031 
1032 ServerList EthernetInterface::ntpServers(ServerList servers)
1033 {
1034     auto ntpServers = EthernetInterfaceIntf::ntpServers(servers);
1035 
1036     writeConfigurationFile();
1037     manager.reloadConfigs();
1038 
1039     return ntpServers;
1040 }
1041 // Need to merge the below function with the code which writes the
1042 // config file during factory reset.
1043 // TODO openbmc/openbmc#1751
1044 
1045 void EthernetInterface::writeConfigurationFile()
1046 {
1047     // write all the static ip address in the systemd-network conf file
1048 
1049     using namespace std::string_literals;
1050     namespace fs = std::filesystem;
1051 
1052     // if there is vlan interafce then write the configuration file
1053     // for vlan also.
1054 
1055     for (const auto& intf : vlanInterfaces)
1056     {
1057         intf.second->writeConfigurationFile();
1058     }
1059 
1060     fs::path confPath = manager.getConfDir();
1061 
1062     std::string fileName = systemd::config::networkFilePrefix +
1063                            interfaceName() + systemd::config::networkFileSuffix;
1064     confPath /= fileName;
1065     std::fstream stream;
1066 
1067     stream.open(confPath.c_str(), std::fstream::out);
1068     if (!stream.is_open())
1069     {
1070         log<level::ERR>("Unable to open the file",
1071                         entry("FILE=%s", confPath.c_str()));
1072         elog<InternalFailure>();
1073     }
1074 
1075     // Write the device
1076     stream << "[Match]\n";
1077     stream << "Name=" << interfaceName() << "\n";
1078 
1079     auto addrs = getAddresses();
1080 
1081     // Write the link section
1082     stream << "[Link]\n";
1083     auto mac = MacAddressIntf::macAddress();
1084     if (!mac.empty())
1085     {
1086         stream << "MACAddress=" << mac << "\n";
1087     }
1088 
1089     if (!EthernetInterfaceIntf::nicEnabled())
1090     {
1091         stream << "Unmanaged=yes\n";
1092     }
1093 
1094     // write the network section
1095     stream << "[Network]\n";
1096 #ifdef LINK_LOCAL_AUTOCONFIGURATION
1097     stream << "LinkLocalAddressing=yes\n";
1098 #else
1099     stream << "LinkLocalAddressing=no\n";
1100 #endif
1101     stream << std::boolalpha
1102            << "IPv6AcceptRA=" << EthernetInterfaceIntf::ipv6AcceptRA() << "\n";
1103 
1104     // Add the VLAN entry
1105     for (const auto& intf : vlanInterfaces)
1106     {
1107         stream << "VLAN=" << intf.second->EthernetInterface::interfaceName()
1108                << "\n";
1109     }
1110     // Add the NTP server
1111     for (const auto& ntp : EthernetInterfaceIntf::ntpServers())
1112     {
1113         stream << "NTP=" << ntp << "\n";
1114     }
1115 
1116     // Add the DNS entry
1117     for (const auto& dns : EthernetInterfaceIntf::staticNameServers())
1118     {
1119         stream << "DNS=" << dns << "\n";
1120     }
1121 
1122     // Add the DHCP entry
1123     stream << "DHCP="s +
1124                   mapDHCPToSystemd[EthernetInterfaceIntf::dhcpEnabled()] + "\n";
1125 
1126     stream << "[IPv6AcceptRA]\n";
1127     stream << "DHCPv6Client=";
1128     stream << (dhcpIsEnabled(IP::Protocol::IPv6) ? "true" : "false");
1129     stream << "\n";
1130 
1131     // Static IP addresses
1132     for (const auto& addr : addrs)
1133     {
1134         if (originIsManuallyAssigned(addr.second->origin()) &&
1135             !dhcpIsEnabled(addr.second->type()))
1136         {
1137             // Process all static addresses
1138             std::string address = addr.second->address() + "/" +
1139                                   std::to_string(addr.second->prefixLength());
1140 
1141             // build the address entries. Do not use [Network] shortcuts to
1142             // insert address entries.
1143             stream << "[Address]\n";
1144             stream << "Address=" << address << "\n";
1145         }
1146     }
1147 
1148     if (!dhcpIsEnabled(IP::Protocol::IPv4))
1149     {
1150         auto gateway = EthernetInterfaceIntf::defaultGateway();
1151         if (!gateway.empty())
1152         {
1153             stream << "[Route]\n";
1154             stream << "Gateway=" << gateway << "\n";
1155         }
1156     }
1157 
1158     if (!dhcpIsEnabled(IP::Protocol::IPv6))
1159     {
1160         auto gateway6 = EthernetInterfaceIntf::defaultGateway6();
1161         if (!gateway6.empty())
1162         {
1163             stream << "[Route]\n";
1164             stream << "Gateway=" << gateway6 << "\n";
1165         }
1166     }
1167 
1168     // Write the neighbor sections
1169     for (const auto& neighbor : staticNeighbors)
1170     {
1171         stream << "[Neighbor]"
1172                << "\n";
1173         stream << "Address=" << neighbor.second->ipAddress() << "\n";
1174         stream << "MACAddress=" << neighbor.second->macAddress() << "\n";
1175     }
1176 
1177     // Write the dhcp section irrespective of whether DHCP is enabled or not
1178     writeDHCPSection(stream);
1179 
1180     stream.close();
1181 }
1182 
1183 void EthernetInterface::writeDHCPSection(std::fstream& stream)
1184 {
1185     using namespace std::string_literals;
1186     // write the dhcp section
1187     stream << "[DHCP]\n";
1188 
1189     // Hardcoding the client identifier to mac, to address below issue
1190     // https://github.com/openbmc/openbmc/issues/1280
1191     stream << "ClientIdentifier=mac\n";
1192     if (manager.getDHCPConf())
1193     {
1194         auto value = manager.getDHCPConf()->dnsEnabled() ? "true"s : "false"s;
1195         stream << "UseDNS="s + value + "\n";
1196 
1197         value = manager.getDHCPConf()->ntpEnabled() ? "true"s : "false"s;
1198         stream << "UseNTP="s + value + "\n";
1199 
1200         value = manager.getDHCPConf()->hostNameEnabled() ? "true"s : "false"s;
1201         stream << "UseHostname="s + value + "\n";
1202 
1203         value =
1204             manager.getDHCPConf()->sendHostNameEnabled() ? "true"s : "false"s;
1205         stream << "SendHostname="s + value + "\n";
1206     }
1207 }
1208 
1209 std::string EthernetInterface::macAddress(std::string value)
1210 {
1211     ether_addr newMAC;
1212     try
1213     {
1214         newMAC = mac_address::fromString(value);
1215     }
1216     catch (const std::invalid_argument&)
1217     {
1218         log<level::ERR>("MACAddress is not valid.",
1219                         entry("MAC=%s", value.c_str()));
1220         elog<InvalidArgument>(Argument::ARGUMENT_NAME("MACAddress"),
1221                               Argument::ARGUMENT_VALUE(value.c_str()));
1222     }
1223     if (!mac_address::isUnicast(newMAC))
1224     {
1225         log<level::ERR>("MACAddress is not valid.",
1226                         entry("MAC=%s", value.c_str()));
1227         elog<InvalidArgument>(Argument::ARGUMENT_NAME("MACAddress"),
1228                               Argument::ARGUMENT_VALUE(value.c_str()));
1229     }
1230 
1231     auto interface = interfaceName();
1232     std::string validMAC = mac_address::toString(newMAC);
1233 
1234     // We don't need to update the system if the address is unchanged
1235     ether_addr oldMAC = mac_address::fromString(MacAddressIntf::macAddress());
1236     if (!stdplus::raw::equal(newMAC, oldMAC))
1237     {
1238         // Update everything that depends on the MAC value
1239         for (const auto& [name, intf] : vlanInterfaces)
1240         {
1241             intf->MacAddressIntf::macAddress(validMAC);
1242         }
1243         MacAddressIntf::macAddress(validMAC);
1244 
1245         writeConfigurationFile();
1246         manager.addReloadPreHook([interface]() {
1247             // The MAC and LLADDRs will only update if the NIC is already down
1248             EthernetIntfSocket eifSocket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
1249             setNICAdminState(eifSocket.sock, interface.c_str(), false);
1250         });
1251         manager.reloadConfigs();
1252     }
1253 
1254 #ifdef HAVE_UBOOT_ENV
1255     // Ensure that the valid address is stored in the u-boot-env
1256     auto envVar = interfaceToUbootEthAddr(interface.c_str());
1257     if (envVar)
1258     {
1259         // Trimming MAC addresses that are out of range. eg: AA:FF:FF:FF:FF:100;
1260         // and those having more than 6 bytes. eg: AA:AA:AA:AA:AA:AA:BB
1261         execute("/sbin/fw_setenv", "fw_setenv", envVar->c_str(),
1262                 validMAC.c_str());
1263     }
1264 #endif // HAVE_UBOOT_ENV
1265 
1266     return value;
1267 }
1268 
1269 void EthernetInterface::deleteAll()
1270 {
1271     // clear all the ip on the interface
1272     addrs.clear();
1273 
1274     writeConfigurationFile();
1275     manager.reloadConfigs();
1276 }
1277 
1278 std::string EthernetInterface::defaultGateway(std::string gateway)
1279 {
1280     auto gw = EthernetInterfaceIntf::defaultGateway();
1281     if (gw == gateway)
1282     {
1283         return gw;
1284     }
1285 
1286     if (!isValidIP(AF_INET, gateway))
1287     {
1288         log<level::ERR>("Not a valid v4 Gateway",
1289                         entry("GATEWAY=%s", gateway.c_str()));
1290         elog<InvalidArgument>(Argument::ARGUMENT_NAME("GATEWAY"),
1291                               Argument::ARGUMENT_VALUE(gateway.c_str()));
1292     }
1293     gw = EthernetInterfaceIntf::defaultGateway(gateway);
1294 
1295     writeConfigurationFile();
1296     manager.reloadConfigs();
1297 
1298     return gw;
1299 }
1300 
1301 std::string EthernetInterface::defaultGateway6(std::string gateway)
1302 {
1303     auto gw = EthernetInterfaceIntf::defaultGateway6();
1304     if (gw == gateway)
1305     {
1306         return gw;
1307     }
1308 
1309     if (!isValidIP(AF_INET6, gateway))
1310     {
1311         log<level::ERR>("Not a valid v6 Gateway",
1312                         entry("GATEWAY=%s", gateway.c_str()));
1313         elog<InvalidArgument>(Argument::ARGUMENT_NAME("GATEWAY"),
1314                               Argument::ARGUMENT_VALUE(gateway.c_str()));
1315     }
1316     gw = EthernetInterfaceIntf::defaultGateway6(gateway);
1317 
1318     writeConfigurationFile();
1319     manager.reloadConfigs();
1320 
1321     return gw;
1322 }
1323 } // namespace network
1324 } // namespace phosphor
1325