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