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