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