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