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