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