1 #include "config.h"
2 
3 #include "ethernet_interface.hpp"
4 
5 #include "config_parser.hpp"
6 #include "network_manager.hpp"
7 #include "system_queries.hpp"
8 #include "util.hpp"
9 
10 #include <fmt/compile.h>
11 #include <fmt/format.h>
12 #include <linux/rtnetlink.h>
13 #include <net/if.h>
14 
15 #include <algorithm>
16 #include <filesystem>
17 #include <phosphor-logging/elog-errors.hpp>
18 #include <phosphor-logging/log.hpp>
19 #include <stdplus/raw.hpp>
20 #include <stdplus/zstring.hpp>
21 #include <string>
22 #include <unordered_map>
23 #include <variant>
24 #include <xyz/openbmc_project/Common/error.hpp>
25 
26 namespace phosphor
27 {
28 namespace network
29 {
30 
31 using namespace phosphor::logging;
32 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
33 using NotAllowed = sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
34 using NotAllowedArgument = xyz::openbmc_project::Common::NotAllowed;
35 using Argument = xyz::openbmc_project::Common::InvalidArgument;
36 using std::literals::string_view_literals::operator""sv;
37 constexpr auto RESOLVED_SERVICE = "org.freedesktop.resolve1";
38 constexpr auto RESOLVED_INTERFACE = "org.freedesktop.resolve1.Link";
39 constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties";
40 constexpr auto RESOLVED_SERVICE_PATH = "/org/freedesktop/resolve1/link/";
41 
42 constexpr auto TIMESYNCD_SERVICE = "org.freedesktop.timesync1";
43 constexpr auto TIMESYNCD_INTERFACE = "org.freedesktop.timesync1.Manager";
44 constexpr auto TIMESYNCD_SERVICE_PATH = "/org/freedesktop/timesync1";
45 
46 constexpr auto METHOD_GET = "Get";
47 
48 template <typename Func>
49 inline decltype(std::declval<Func>()())
50     ignoreError(std::string_view msg, stdplus::zstring_view intf,
51                 decltype(std::declval<Func>()()) fallback, Func&& func) noexcept
52 {
53     try
54     {
55         return func();
56     }
57     catch (const std::exception& e)
58     {
59         auto err = fmt::format("{} failed on {}: {}", msg, intf, e.what());
60         log<level::ERR>(err.c_str(), entry("INTERFACE=%s", intf.c_str()));
61     }
62     return fallback;
63 }
64 
65 static std::string makeObjPath(std::string_view root, std::string_view intf)
66 {
67     auto ret = fmt::format(FMT_COMPILE("{}/{}"), root, intf);
68     std::replace(ret.begin() + ret.size() - intf.size(), ret.end(), '.', '_');
69     return ret;
70 }
71 
72 EthernetInterface::EthernetInterface(sdbusplus::bus_t& bus, Manager& manager,
73                                      const AllIntfInfo& info,
74                                      std::string_view objRoot,
75                                      const config::Parser& config,
76                                      bool enabled) :
77     EthernetInterface(bus, manager, info, makeObjPath(objRoot, *info.intf.name),
78                       config, enabled)
79 {
80 }
81 
82 EthernetInterface::EthernetInterface(sdbusplus::bus_t& bus, Manager& manager,
83                                      const AllIntfInfo& info,
84                                      std::string&& objPath,
85                                      const config::Parser& config,
86                                      bool enabled) :
87     Ifaces(bus, objPath.c_str(), Ifaces::action::defer_emit),
88     manager(manager), bus(bus), objPath(std::move(objPath))
89 {
90     interfaceName(*info.intf.name, true);
91     auto dhcpVal = getDHCPValue(config);
92     EthernetInterfaceIntf::dhcp4(dhcpVal.v4, true);
93     EthernetInterfaceIntf::dhcp6(dhcpVal.v6, true);
94     EthernetInterfaceIntf::ipv6AcceptRA(getIPv6AcceptRA(config), true);
95     EthernetInterfaceIntf::nicEnabled(enabled, true);
96 
97     EthernetInterfaceIntf::ntpServers(
98         config.map.getValueStrings("Network", "NTP"), true);
99 
100     updateInfo(info.intf, true);
101 
102     if (info.defgw4)
103     {
104         EthernetInterface::defaultGateway(std::to_string(*info.defgw4), true);
105     }
106     if (info.defgw6)
107     {
108         EthernetInterface::defaultGateway6(std::to_string(*info.defgw6), true);
109     }
110     emit_object_added();
111 
112     if (info.intf.vlan_id)
113     {
114         if (!info.intf.parent_idx)
115         {
116             std::runtime_error("Missing parent link");
117         }
118         vlan.emplace(bus, this->objPath.c_str(), info.intf, *this);
119     }
120     for (const auto& [_, addr] : info.addrs)
121     {
122         addAddr(addr);
123     }
124     for (const auto& [_, neigh] : info.staticNeighs)
125     {
126         addStaticNeigh(neigh);
127     }
128 }
129 
130 void EthernetInterface::updateInfo(const InterfaceInfo& info, bool skipSignal)
131 {
132     ifIdx = info.idx;
133     EthernetInterfaceIntf::linkUp(info.flags & IFF_RUNNING, skipSignal);
134     if (info.mac)
135     {
136         MacAddressIntf::macAddress(std::to_string(*info.mac), skipSignal);
137     }
138     if (info.mtu)
139     {
140         EthernetInterfaceIntf::mtu(*info.mtu, skipSignal);
141     }
142     if (ifIdx > 0)
143     {
144         auto ethInfo = ignoreError("GetEthInfo", *info.name, {}, [&] {
145             return system::getEthInfo(*info.name);
146         });
147         EthernetInterfaceIntf::autoNeg(ethInfo.autoneg, skipSignal);
148         EthernetInterfaceIntf::speed(ethInfo.speed, skipSignal);
149     }
150 }
151 
152 bool EthernetInterface::originIsManuallyAssigned(IP::AddressOrigin origin)
153 {
154     return (
155 #ifdef LINK_LOCAL_AUTOCONFIGURATION
156         (origin == IP::AddressOrigin::Static)
157 #else
158         (origin == IP::AddressOrigin::Static ||
159          origin == IP::AddressOrigin::LinkLocal)
160 #endif
161 
162     );
163 }
164 
165 void EthernetInterface::addAddr(const AddressInfo& info)
166 {
167     IP::AddressOrigin origin = IP::AddressOrigin::Static;
168     if (dhcpIsEnabled(info.ifaddr.getAddr()))
169     {
170         origin = IP::AddressOrigin::DHCP;
171     }
172 #ifdef LINK_LOCAL_AUTOCONFIGURATION
173     if (info.scope == RT_SCOPE_LINK)
174     {
175         origin = IP::AddressOrigin::LinkLocal;
176     }
177 #endif
178 
179     auto it = addrs.find(info.ifaddr);
180     if (it == addrs.end())
181     {
182         addrs.emplace(info.ifaddr, std::make_unique<IPAddress>(
183                                        bus, std::string_view(objPath), *this,
184                                        info.ifaddr, origin));
185     }
186     else
187     {
188         it->second->IPIfaces::origin(origin);
189     }
190 }
191 
192 void EthernetInterface::addStaticNeigh(const NeighborInfo& info)
193 {
194     if (!info.mac || !info.addr)
195     {
196         auto msg = fmt::format("Missing neighbor mac on {}\n", interfaceName());
197         log<level::ERR>(msg.c_str());
198         return;
199     }
200 
201     if (auto it = staticNeighbors.find(*info.addr); it != staticNeighbors.end())
202     {
203         it->second->NeighborObj::macAddress(std::to_string(*info.mac));
204     }
205     else
206     {
207         staticNeighbors.emplace(*info.addr, std::make_unique<Neighbor>(
208                                                 bus, std::string_view(objPath),
209                                                 *this, *info.addr, *info.mac,
210                                                 Neighbor::State::Permanent));
211     }
212 }
213 
214 ObjectPath EthernetInterface::ip(IP::Protocol protType, std::string ipaddress,
215                                  uint8_t prefixLength, std::string)
216 {
217     InAddrAny addr;
218     try
219     {
220         switch (protType)
221         {
222             case IP::Protocol::IPv4:
223                 addr = ToAddr<in_addr>{}(ipaddress);
224                 break;
225             case IP::Protocol::IPv6:
226                 addr = ToAddr<in6_addr>{}(ipaddress);
227                 break;
228             default:
229                 throw std::logic_error("Exhausted protocols");
230         }
231     }
232     catch (const std::exception& e)
233     {
234         auto msg = fmt::format("Invalid IP `{}`: {}\n", ipaddress, e.what());
235         log<level::ERR>(msg.c_str(), entry("ADDRESS=%s", ipaddress.c_str()));
236         elog<InvalidArgument>(Argument::ARGUMENT_NAME("ipaddress"),
237                               Argument::ARGUMENT_VALUE(ipaddress.c_str()));
238     }
239     IfAddr ifaddr;
240     try
241     {
242         ifaddr = {addr, prefixLength};
243     }
244     catch (const std::exception& e)
245     {
246         auto msg = fmt::format("Invalid prefix length `{}`: {}\n", prefixLength,
247                                e.what());
248         log<level::ERR>(msg.c_str(),
249                         entry("PREFIXLENGTH=%" PRIu8, prefixLength));
250         elog<InvalidArgument>(
251             Argument::ARGUMENT_NAME("prefixLength"),
252             Argument::ARGUMENT_VALUE(std::to_string(prefixLength).c_str()));
253     }
254 
255     auto it = addrs.find(ifaddr);
256     if (it == addrs.end())
257     {
258         it = std::get<0>(addrs.emplace(
259             ifaddr,
260             std::make_unique<IPAddress>(bus, std::string_view(objPath), *this,
261                                         ifaddr, IP::AddressOrigin::Static)));
262     }
263     else
264     {
265         it->second->IPIfaces::origin(IP::AddressOrigin::Static);
266     }
267 
268     writeConfigurationFile();
269     manager.reloadConfigs();
270 
271     return it->second->getObjPath();
272 }
273 
274 ObjectPath EthernetInterface::neighbor(std::string ipAddress,
275                                        std::string macAddress)
276 {
277     InAddrAny addr;
278     try
279     {
280         addr = ToAddr<InAddrAny>{}(ipAddress);
281     }
282     catch (const std::exception& e)
283     {
284         auto msg =
285             fmt::format("Not a valid IP address `{}`: {}", ipAddress, e.what());
286         log<level::ERR>(msg.c_str(), entry("ADDRESS=%s", ipAddress.c_str()));
287         elog<InvalidArgument>(Argument::ARGUMENT_NAME("ipAddress"),
288                               Argument::ARGUMENT_VALUE(ipAddress.c_str()));
289     }
290 
291     ether_addr lladdr;
292     try
293     {
294         lladdr = ToAddr<ether_addr>{}(macAddress);
295     }
296     catch (const std::exception& e)
297     {
298         auto msg = fmt::format("Not a valid MAC address `{}`: {}", macAddress,
299                                e.what());
300         log<level::ERR>(msg.c_str(),
301                         entry("MACADDRESS=%s", macAddress.c_str()));
302         elog<InvalidArgument>(Argument::ARGUMENT_NAME("macAddress"),
303                               Argument::ARGUMENT_VALUE(macAddress.c_str()));
304     }
305 
306     auto it = staticNeighbors.find(addr);
307     if (it == staticNeighbors.end())
308     {
309         it = std::get<0>(staticNeighbors.emplace(
310             addr, std::make_unique<Neighbor>(bus, std::string_view(objPath),
311                                              *this, addr, lladdr,
312                                              Neighbor::State::Permanent)));
313     }
314     else
315     {
316         it->second->NeighborObj::macAddress(std::to_string(lladdr));
317     }
318 
319     writeConfigurationFile();
320     manager.reloadConfigs();
321 
322     return it->second->getObjPath();
323 }
324 
325 bool EthernetInterface::ipv6AcceptRA(bool value)
326 {
327     if (ipv6AcceptRA() != EthernetInterfaceIntf::ipv6AcceptRA(value))
328     {
329         writeConfigurationFile();
330         manager.reloadConfigs();
331     }
332     return value;
333 }
334 
335 bool EthernetInterface::dhcp4(bool value)
336 {
337     if (dhcp4() != EthernetInterfaceIntf::dhcp4(value))
338     {
339         writeConfigurationFile();
340         manager.reloadConfigs();
341     }
342     return value;
343 }
344 
345 bool EthernetInterface::dhcp6(bool value)
346 {
347     if (dhcp6() != EthernetInterfaceIntf::dhcp6(value))
348     {
349         writeConfigurationFile();
350         manager.reloadConfigs();
351     }
352     return value;
353 }
354 
355 EthernetInterface::DHCPConf EthernetInterface::dhcpEnabled(DHCPConf value)
356 {
357     auto old4 = EthernetInterfaceIntf::dhcp4();
358     auto new4 = EthernetInterfaceIntf::dhcp4(value == DHCPConf::v4 ||
359                                              value == DHCPConf::v4v6stateless ||
360                                              value == DHCPConf::both);
361     auto old6 = EthernetInterfaceIntf::dhcp6();
362     auto new6 = EthernetInterfaceIntf::dhcp6(value == DHCPConf::v6 ||
363                                              value == DHCPConf::both);
364     auto oldra = EthernetInterfaceIntf::ipv6AcceptRA();
365     auto newra = EthernetInterfaceIntf::ipv6AcceptRA(
366         value == DHCPConf::v6stateless || value == DHCPConf::v4v6stateless ||
367         value == DHCPConf::v6 || value == DHCPConf::both);
368 
369     if (old4 != new4 || old6 != new6 || oldra != newra)
370     {
371         writeConfigurationFile();
372         manager.reloadConfigs();
373     }
374     return value;
375 }
376 
377 EthernetInterface::DHCPConf EthernetInterface::dhcpEnabled() const
378 {
379     if (dhcp6())
380     {
381         return dhcp4() ? DHCPConf::both : DHCPConf::v6;
382     }
383     else if (dhcp4())
384     {
385         return ipv6AcceptRA() ? DHCPConf::v4v6stateless : DHCPConf::v4;
386     }
387     return ipv6AcceptRA() ? DHCPConf::v6stateless : DHCPConf::none;
388 }
389 
390 size_t EthernetInterface::mtu(size_t value)
391 {
392     const size_t old = EthernetInterfaceIntf::mtu();
393     if (value == old)
394     {
395         return value;
396     }
397     const auto ifname = interfaceName();
398     return EthernetInterfaceIntf::mtu(ignoreError("SetMTU", ifname, old, [&] {
399         system::setMTU(ifname, value);
400         return value;
401     }));
402 }
403 
404 bool EthernetInterface::nicEnabled(bool value)
405 {
406     if (value == EthernetInterfaceIntf::nicEnabled())
407     {
408         return value;
409     }
410 
411     EthernetInterfaceIntf::nicEnabled(value);
412     writeConfigurationFile();
413     if (!value)
414     {
415         // We only need to bring down the interface, networkd will always bring
416         // up managed interfaces
417         manager.addReloadPreHook(
418             [ifname = interfaceName()]() { system::setNICUp(ifname, false); });
419     }
420     manager.reloadConfigs();
421 
422     return value;
423 }
424 
425 ServerList EthernetInterface::staticNameServers(ServerList value)
426 {
427     for (auto& ip : value)
428     {
429         try
430         {
431             ip = std::to_string(ToAddr<InAddrAny>{}(ip));
432         }
433         catch (const std::exception& e)
434         {
435             auto msg =
436                 fmt::format("Not a valid IP address `{}`: {}", ip, e.what());
437             log<level::ERR>(msg.c_str()), entry("ADDRESS=%s", ip.c_str());
438             elog<InvalidArgument>(Argument::ARGUMENT_NAME("StaticNameserver"),
439                                   Argument::ARGUMENT_VALUE(ip.c_str()));
440         }
441     }
442     try
443     {
444         EthernetInterfaceIntf::staticNameServers(value);
445 
446         writeConfigurationFile();
447         manager.reloadConfigs();
448     }
449     catch (const InternalFailure& e)
450     {
451         log<level::ERR>("Exception processing DNS entries");
452     }
453     return EthernetInterfaceIntf::staticNameServers();
454 }
455 
456 void EthernetInterface::loadNTPServers(const config::Parser& config)
457 {
458     EthernetInterfaceIntf::ntpServers(getNTPServerFromTimeSyncd());
459     EthernetInterfaceIntf::staticNTPServers(
460         config.map.getValueStrings("Network", "NTP"));
461 }
462 
463 void EthernetInterface::loadNameServers(const config::Parser& config)
464 {
465     EthernetInterfaceIntf::nameservers(getNameServerFromResolvd());
466     EthernetInterfaceIntf::staticNameServers(
467         config.map.getValueStrings("Network", "DNS"));
468 }
469 
470 ServerList EthernetInterface::getNTPServerFromTimeSyncd()
471 {
472     ServerList servers; // Variable to capture the NTP Server IPs
473     auto method = bus.new_method_call(TIMESYNCD_SERVICE, TIMESYNCD_SERVICE_PATH,
474                                       PROPERTY_INTERFACE, METHOD_GET);
475 
476     method.append(TIMESYNCD_INTERFACE, "LinkNTPServers");
477 
478     try
479     {
480         auto reply = bus.call(method);
481         std::variant<ServerList> response;
482         reply.read(response);
483         servers = std::get<ServerList>(response);
484     }
485     catch (const sdbusplus::exception::SdBusError& e)
486     {
487         log<level::ERR>(
488             "Failed to get NTP server information from Systemd-Timesyncd");
489     }
490 
491     return servers;
492 }
493 
494 ServerList EthernetInterface::getNameServerFromResolvd()
495 {
496     ServerList servers;
497     auto OBJ_PATH = fmt::format("{}{}", RESOLVED_SERVICE_PATH, ifIdx);
498 
499     /*
500       The DNS property under org.freedesktop.resolve1.Link interface contains
501       an array containing all DNS servers currently used by resolved. It
502       contains similar information as the DNS server data written to
503       /run/systemd/resolve/resolv.conf.
504 
505       Each structure in the array consists of a numeric network interface index,
506       an address family, and a byte array containing the DNS server address
507       (either 4 bytes in length for IPv4 or 16 bytes in lengths for IPv6).
508       The array contains DNS servers configured system-wide, including those
509       possibly read from a foreign /etc/resolv.conf or the DNS= setting in
510       /etc/systemd/resolved.conf, as well as per-interface DNS server
511       information either retrieved from systemd-networkd or configured by
512       external software via SetLinkDNS().
513     */
514 
515     using type = std::vector<std::tuple<int32_t, std::vector<uint8_t>>>;
516     std::variant<type> name; // Variable to capture the DNS property
517     auto method = bus.new_method_call(RESOLVED_SERVICE, OBJ_PATH.c_str(),
518                                       PROPERTY_INTERFACE, METHOD_GET);
519 
520     method.append(RESOLVED_INTERFACE, "DNS");
521 
522     try
523     {
524         auto reply = bus.call(method);
525         reply.read(name);
526     }
527     catch (const sdbusplus::exception_t& e)
528     {
529         log<level::ERR>("Failed to get DNS information from Systemd-Resolved");
530     }
531     auto tupleVector = std::get_if<type>(&name);
532     for (auto i = tupleVector->begin(); i != tupleVector->end(); ++i)
533     {
534         int addressFamily = std::get<0>(*i);
535         std::vector<uint8_t>& ipaddress = std::get<1>(*i);
536         servers.push_back(std::to_string(
537             addrFromBuf(addressFamily, stdplus::raw::asView<char>(ipaddress))));
538     }
539     return servers;
540 }
541 
542 ObjectPath EthernetInterface::createVLAN(uint16_t id)
543 {
544     auto intfName = fmt::format(FMT_COMPILE("{}.{}"), interfaceName(), id);
545     auto idStr = std::to_string(id);
546     if (manager.interfaces.find(intfName) != manager.interfaces.end())
547     {
548         log<level::ERR>("VLAN already exists", entry("VLANID=%u", id));
549         elog<InvalidArgument>(Argument::ARGUMENT_NAME("VLANId"),
550                               Argument::ARGUMENT_VALUE(idStr.c_str()));
551     }
552 
553     auto objRoot = std::string_view(objPath).substr(0, objPath.rfind('/'));
554     auto macStr = MacAddressIntf::macAddress();
555     std::optional<ether_addr> mac;
556     if (!macStr.empty())
557     {
558         mac.emplace(ToAddr<ether_addr>{}(macStr));
559     }
560     auto info = AllIntfInfo{InterfaceInfo{
561         .idx = 0, // TODO: Query the correct value after creation
562         .flags = 0,
563         .name = intfName,
564         .mac = std::move(mac),
565         .mtu = mtu(),
566         .parent_idx = ifIdx,
567         .vlan_id = id,
568     }};
569 
570     // Pass the parents nicEnabled property, so that the child
571     // VLAN interface can inherit.
572     auto vlanIntf = std::make_unique<EthernetInterface>(
573         bus, manager, info, objRoot, config::Parser(), nicEnabled());
574     ObjectPath ret = vlanIntf->objPath;
575 
576     manager.interfaces.emplace(intfName, std::move(vlanIntf));
577 
578     // write the device file for the vlan interface.
579     config::Parser config;
580     auto& netdev = config.map["NetDev"].emplace_back();
581     netdev["Name"].emplace_back(intfName);
582     netdev["Kind"].emplace_back("vlan");
583     config.map["VLAN"].emplace_back()["Id"].emplace_back(std::move(idStr));
584     config.writeFile(config::pathForIntfDev(manager.getConfDir(), intfName));
585 
586     writeConfigurationFile();
587     manager.reloadConfigs();
588 
589     return ret;
590 }
591 
592 ServerList EthernetInterface::staticNTPServers(ServerList value)
593 {
594     try
595     {
596         EthernetInterfaceIntf::staticNTPServers(value);
597 
598         writeConfigurationFile();
599         manager.reloadConfigs();
600     }
601     catch (InternalFailure& e)
602     {
603         log<level::ERR>("Exception processing NTP entries");
604     }
605     return EthernetInterfaceIntf::staticNTPServers();
606 }
607 
608 ServerList EthernetInterface::ntpServers(ServerList /*servers*/)
609 {
610     elog<NotAllowed>(NotAllowedArgument::REASON("ReadOnly Property"));
611 }
612 // Need to merge the below function with the code which writes the
613 // config file during factory reset.
614 // TODO openbmc/openbmc#1751
615 
616 void EthernetInterface::writeConfigurationFile()
617 {
618     config::Parser config;
619     config.map["Match"].emplace_back()["Name"].emplace_back(interfaceName());
620     {
621         auto& link = config.map["Link"].emplace_back();
622 #ifdef PERSIST_MAC
623         auto mac = MacAddressIntf::macAddress();
624         if (!mac.empty())
625         {
626             link["MACAddress"].emplace_back(mac);
627         }
628 #endif
629         if (!EthernetInterfaceIntf::nicEnabled())
630         {
631             link["Unmanaged"].emplace_back("yes");
632         }
633     }
634     {
635         auto& network = config.map["Network"].emplace_back();
636         auto& lla = network["LinkLocalAddressing"];
637 #ifdef LINK_LOCAL_AUTOCONFIGURATION
638         lla.emplace_back("yes");
639 #else
640         lla.emplace_back("no");
641 #endif
642         network["IPv6AcceptRA"].emplace_back(ipv6AcceptRA() ? "true" : "false");
643         network["DHCP"].emplace_back(dhcp4() ? (dhcp6() ? "true" : "ipv4")
644                                              : (dhcp6() ? "ipv6" : "false"));
645         {
646             auto& vlans = network["VLAN"];
647             for (const auto& [_, intf] : manager.interfaces)
648             {
649                 if (intf->vlan && intf->vlan->parentIdx == ifIdx)
650                 {
651                     vlans.emplace_back(intf->interfaceName());
652                 }
653             }
654         }
655         {
656             auto& ntps = network["NTP"];
657             for (const auto& ntp : EthernetInterfaceIntf::staticNTPServers())
658             {
659                 ntps.emplace_back(ntp);
660             }
661         }
662         {
663             auto& dnss = network["DNS"];
664             for (const auto& dns : EthernetInterfaceIntf::staticNameServers())
665             {
666                 dnss.emplace_back(dns);
667             }
668         }
669         {
670             auto& address = network["Address"];
671             for (const auto& addr : addrs)
672             {
673                 if (originIsManuallyAssigned(addr.second->origin()))
674                 {
675                     address.emplace_back(
676                         fmt::format("{}/{}", addr.second->address(),
677                                     addr.second->prefixLength()));
678                 }
679             }
680         }
681         {
682             auto& gateways = network["Gateway"];
683             if (!dhcp4())
684             {
685                 auto gateway = EthernetInterfaceIntf::defaultGateway();
686                 if (!gateway.empty())
687                 {
688                     gateways.emplace_back(gateway);
689                 }
690             }
691 
692             if (!dhcp6())
693             {
694                 auto gateway6 = EthernetInterfaceIntf::defaultGateway6();
695                 if (!gateway6.empty())
696                 {
697                     gateways.emplace_back(gateway6);
698                 }
699             }
700         }
701     }
702     config.map["IPv6AcceptRA"].emplace_back()["DHCPv6Client"].emplace_back(
703         dhcp6() ? "true" : "false");
704     {
705         auto& neighbors = config.map["Neighbor"];
706         for (const auto& sneighbor : staticNeighbors)
707         {
708             auto& neighbor = neighbors.emplace_back();
709             neighbor["Address"].emplace_back(sneighbor.second->ipAddress());
710             neighbor["MACAddress"].emplace_back(sneighbor.second->macAddress());
711         }
712     }
713     {
714         auto& dhcp = config.map["DHCP"].emplace_back();
715         dhcp["ClientIdentifier"].emplace_back("mac");
716         const auto& conf = manager.getDHCPConf();
717         auto dns_enabled = conf.dnsEnabled() ? "true" : "false";
718         dhcp["UseDNS"].emplace_back(dns_enabled);
719         dhcp["UseDomains"].emplace_back(dns_enabled);
720         dhcp["UseNTP"].emplace_back(conf.ntpEnabled() ? "true" : "false");
721         dhcp["UseHostname"].emplace_back(conf.hostNameEnabled() ? "true"
722                                                                 : "false");
723         dhcp["SendHostname"].emplace_back(conf.sendHostNameEnabled() ? "true"
724                                                                      : "false");
725     }
726     auto path = config::pathForIntfConf(manager.getConfDir(), interfaceName());
727     config.writeFile(path);
728     auto msg = fmt::format("Wrote networkd file: {}", path.native());
729     log<level::INFO>(msg.c_str(), entry("FILE=%s", path.c_str()));
730 }
731 
732 std::string EthernetInterface::macAddress([[maybe_unused]] std::string value)
733 {
734     if (vlan)
735     {
736         log<level::ERR>("Tried to set MAC address on VLAN");
737         elog<InternalFailure>();
738     }
739 #ifdef PERSIST_MAC
740     ether_addr newMAC;
741     try
742     {
743         newMAC = ToAddr<ether_addr>{}(value);
744     }
745     catch (const std::invalid_argument&)
746     {
747         log<level::ERR>("MACAddress is not valid.",
748                         entry("MAC=%s", value.c_str()));
749         elog<InvalidArgument>(Argument::ARGUMENT_NAME("MACAddress"),
750                               Argument::ARGUMENT_VALUE(value.c_str()));
751     }
752     if (!mac_address::isUnicast(newMAC))
753     {
754         log<level::ERR>("MACAddress is not valid.",
755                         entry("MAC=%s", value.c_str()));
756         elog<InvalidArgument>(Argument::ARGUMENT_NAME("MACAddress"),
757                               Argument::ARGUMENT_VALUE(value.c_str()));
758     }
759 
760     auto interface = interfaceName();
761     auto validMAC = std::to_string(newMAC);
762 
763     // We don't need to update the system if the address is unchanged
764     ether_addr oldMAC = ToAddr<ether_addr>{}(MacAddressIntf::macAddress());
765     if (newMAC != oldMAC)
766     {
767         // Update everything that depends on the MAC value
768         for (const auto& [_, intf] : manager.interfaces)
769         {
770             if (intf->vlan && intf->vlan->parentIdx == ifIdx)
771             {
772                 intf->MacAddressIntf::macAddress(validMAC);
773             }
774         }
775         MacAddressIntf::macAddress(validMAC);
776 
777         writeConfigurationFile();
778         manager.addReloadPreHook([interface]() {
779             // The MAC and LLADDRs will only update if the NIC is already down
780             system::setNICUp(interface, false);
781         });
782         manager.reloadConfigs();
783     }
784 
785 #ifdef HAVE_UBOOT_ENV
786     // Ensure that the valid address is stored in the u-boot-env
787     auto envVar = interfaceToUbootEthAddr(interface);
788     if (envVar)
789     {
790         // Trimming MAC addresses that are out of range. eg: AA:FF:FF:FF:FF:100;
791         // and those having more than 6 bytes. eg: AA:AA:AA:AA:AA:AA:BB
792         execute("/sbin/fw_setenv", "fw_setenv", envVar->c_str(),
793                 validMAC.c_str());
794     }
795 #endif // HAVE_UBOOT_ENV
796 
797     return value;
798 #else
799     elog<NotAllowed>(
800         NotAllowedArgument::REASON("Writing MAC address is not allowed"));
801 #endif // PERSIST_MAC
802 }
803 
804 void EthernetInterface::deleteAll()
805 {
806     // clear all the ip on the interface
807     addrs.clear();
808 
809     writeConfigurationFile();
810     manager.reloadConfigs();
811 }
812 
813 std::string EthernetInterface::defaultGateway(std::string gateway)
814 {
815     try
816     {
817         if (!gateway.empty())
818         {
819             gateway = std::to_string(ToAddr<in_addr>{}(gateway));
820         }
821     }
822     catch (const std::exception& e)
823     {
824         auto msg = fmt::format("Invalid v4 GW `{}`: {}", gateway, e.what());
825         log<level::ERR>(msg.c_str(), entry("GATEWAY=%s", gateway.c_str()));
826         elog<InvalidArgument>(Argument::ARGUMENT_NAME("GATEWAY"),
827                               Argument::ARGUMENT_VALUE(gateway.c_str()));
828     }
829 
830     if (EthernetInterfaceIntf::defaultGateway() == gateway)
831     {
832         return gateway;
833     }
834     EthernetInterfaceIntf::defaultGateway(gateway);
835 
836     writeConfigurationFile();
837     manager.reloadConfigs();
838 
839     return gateway;
840 }
841 
842 std::string EthernetInterface::defaultGateway6(std::string gateway)
843 {
844     try
845     {
846         if (!gateway.empty())
847         {
848             gateway = std::to_string(ToAddr<in6_addr>{}(gateway));
849         }
850     }
851     catch (const std::exception& e)
852     {
853         auto msg = fmt::format("Invalid v6 GW `{}`: {}", gateway, e.what());
854         log<level::ERR>(msg.c_str(), entry("GATEWAY=%s", gateway.c_str()));
855         elog<InvalidArgument>(Argument::ARGUMENT_NAME("GATEWAY"),
856                               Argument::ARGUMENT_VALUE(gateway.c_str()));
857     }
858 
859     if (EthernetInterfaceIntf::defaultGateway6() == gateway)
860     {
861         return gateway;
862     }
863     EthernetInterfaceIntf::defaultGateway6(gateway);
864 
865     writeConfigurationFile();
866     manager.reloadConfigs();
867 
868     return gateway;
869 }
870 
871 EthernetInterface::VlanProperties::VlanProperties(
872     sdbusplus::bus_t& bus, stdplus::const_zstring objPath,
873     const InterfaceInfo& info, EthernetInterface& eth) :
874     VlanIfaces(bus, objPath.c_str(), VlanIfaces::action::defer_emit),
875     parentIdx(*info.parent_idx), eth(eth)
876 {
877     VlanIntf::id(*info.vlan_id, true);
878     emit_object_added();
879 }
880 
881 void EthernetInterface::VlanProperties::delete_()
882 {
883     auto intf = eth.interfaceName();
884 
885     // Remove all configs for the current interface
886     const auto& confDir = eth.manager.getConfDir();
887     std::error_code ec;
888     std::filesystem::remove(config::pathForIntfConf(confDir, intf), ec);
889     std::filesystem::remove(config::pathForIntfDev(confDir, intf), ec);
890 
891     if (eth.ifIdx > 0)
892     {
893         eth.manager.interfacesByIdx.erase(eth.ifIdx);
894     }
895     auto it = eth.manager.interfaces.find(intf);
896     auto obj = std::move(it->second);
897     eth.manager.interfaces.erase(it);
898 
899     // Write an updated parent interface since it has a VLAN entry
900     for (const auto& [_, intf] : eth.manager.interfaces)
901     {
902         if (intf->ifIdx == parentIdx)
903         {
904             intf->writeConfigurationFile();
905         }
906     }
907 
908     if (eth.ifIdx > 0)
909     {
910         // We need to forcibly delete the interface as systemd does not
911         eth.manager.addReloadPostHook(
912             [idx = eth.ifIdx]() { system::deleteIntf(idx); });
913 
914         // Ignore the interface so the reload doesn't re-query it
915         eth.manager.ignoredIntf.emplace(eth.ifIdx);
916     }
917 
918     eth.manager.reloadConfigs();
919 }
920 
921 } // namespace network
922 } // namespace phosphor
923