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