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