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