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