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