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