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