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     manager.get().reloadConfigs();
452 
453     return value;
454 }
455 
456 ServerList EthernetInterface::staticNameServers(ServerList value)
457 {
458     std::vector<std::string> dnsUniqueValues;
459     for (auto& ip : value)
460     {
461         try
462         {
463             ip = stdplus::toStr(stdplus::fromStr<stdplus::InAnyAddr>(ip));
464         }
465         catch (const std::exception& e)
466         {
467             lg2::error("Not a valid IP address {NET_IP}: {ERROR}", "NET_IP", ip,
468                        "ERROR", e);
469             elog<InvalidArgument>(Argument::ARGUMENT_NAME("StaticNameserver"),
470                                   Argument::ARGUMENT_VALUE(ip.c_str()));
471         }
472         if (std::find(dnsUniqueValues.begin(), dnsUniqueValues.end(), ip) ==
473             dnsUniqueValues.end())
474         {
475             dnsUniqueValues.push_back(ip);
476         }
477     }
478 
479     value =
480         EthernetInterfaceIntf::staticNameServers(std::move(dnsUniqueValues));
481 
482     writeConfigurationFile();
483     manager.get().reloadConfigs();
484 
485     return value;
486 }
487 
488 void EthernetInterface::loadNTPServers(const config::Parser& config)
489 {
490     EthernetInterfaceIntf::ntpServers(getNTPServerFromTimeSyncd());
491     EthernetInterfaceIntf::staticNTPServers(
492         config.map.getValueStrings("Network", "NTP"));
493 }
494 
495 void EthernetInterface::loadNameServers(const config::Parser& config)
496 {
497     EthernetInterfaceIntf::nameservers(getNameServerFromResolvd());
498     EthernetInterfaceIntf::staticNameServers(
499         config.map.getValueStrings("Network", "DNS"));
500 }
501 
502 ServerList EthernetInterface::getNTPServerFromTimeSyncd()
503 {
504     ServerList servers; // Variable to capture the NTP Server IPs
505     auto method =
506         bus.get().new_method_call(TIMESYNCD_SERVICE, TIMESYNCD_SERVICE_PATH,
507                                   PROPERTY_INTERFACE, METHOD_GET);
508 
509     method.append(TIMESYNCD_INTERFACE, "LinkNTPServers");
510 
511     try
512     {
513         auto reply = bus.get().call(method);
514         std::variant<ServerList> response;
515         reply.read(response);
516         servers = std::get<ServerList>(response);
517     }
518     catch (const sdbusplus::exception::SdBusError& e)
519     {
520         lg2::error("Failed to get NTP server information from "
521                    "systemd-timesyncd: {ERROR}",
522                    "ERROR", e);
523     }
524 
525     return servers;
526 }
527 
528 ServerList EthernetInterface::nameservers() const
529 {
530     return getNameServerFromResolvd();
531 }
532 
533 ServerList EthernetInterface::getNameServerFromResolvd() const
534 {
535     ServerList servers;
536     auto OBJ_PATH = std::format("{}{}", RESOLVED_SERVICE_PATH, ifIdx);
537 
538     /*
539       The DNS property under org.freedesktop.resolve1.Link interface contains
540       an array containing all DNS servers currently used by resolved. It
541       contains similar information as the DNS server data written to
542       /run/systemd/resolve/resolv.conf.
543 
544       Each structure in the array consists of a numeric network interface index,
545       an address family, and a byte array containing the DNS server address
546       (either 4 bytes in length for IPv4 or 16 bytes in lengths for IPv6).
547       The array contains DNS servers configured system-wide, including those
548       possibly read from a foreign /etc/resolv.conf or the DNS= setting in
549       /etc/systemd/resolved.conf, as well as per-interface DNS server
550       information either retrieved from systemd-networkd or configured by
551       external software via SetLinkDNS().
552     */
553 
554     using type = std::vector<std::tuple<int32_t, std::vector<uint8_t>>>;
555     std::variant<type> name; // Variable to capture the DNS property
556     auto method = bus.get().new_method_call(RESOLVED_SERVICE, OBJ_PATH.c_str(),
557                                             PROPERTY_INTERFACE, METHOD_GET);
558 
559     method.append(RESOLVED_INTERFACE, "DNS");
560 
561     try
562     {
563         auto reply = bus.get().call(method);
564         reply.read(name);
565     }
566     catch (const sdbusplus::exception_t& e)
567     {
568         lg2::error(
569             "Failed to get DNS information from systemd-resolved: {ERROR}",
570             "ERROR", e);
571     }
572     auto tupleVector = std::get_if<type>(&name);
573     for (auto i = tupleVector->begin(); i != tupleVector->end(); ++i)
574     {
575         int addressFamily = std::get<0>(*i);
576         std::vector<uint8_t>& ipaddress = std::get<1>(*i);
577         servers.push_back(stdplus::toStr(
578             addrFromBuf(addressFamily, stdplus::raw::asView<char>(ipaddress))));
579     }
580     return servers;
581 }
582 
583 ObjectPath EthernetInterface::createVLAN(uint16_t id)
584 {
585     auto idStr = stdplus::toStr(id);
586     auto intfName = stdplus::strCat(interfaceName(), "."sv, idStr);
587     if (manager.get().interfaces.find(intfName) !=
588         manager.get().interfaces.end())
589     {
590         lg2::error("VLAN {NET_VLAN} already exists", "NET_VLAN", id);
591         elog<InvalidArgument>(Argument::ARGUMENT_NAME("VLANId"),
592                               Argument::ARGUMENT_VALUE(idStr.c_str()));
593     }
594 
595     auto objRoot = std::string_view(objPath).substr(0, objPath.rfind('/'));
596     auto macStr = MacAddressIntf::macAddress();
597     std::optional<stdplus::EtherAddr> mac;
598     if (!macStr.empty())
599     {
600         mac.emplace(stdplus::fromStr<stdplus::EtherAddr>(macStr));
601     }
602     auto info = AllIntfInfo{InterfaceInfo{
603         .type = ARPHRD_ETHER,
604         .idx = 0, // TODO: Query the correct value after creation
605         .flags = 0,
606         .name = intfName,
607         .mac = std::move(mac),
608         .mtu = mtu(),
609         .parent_idx = ifIdx,
610         .vlan_id = id,
611     }};
612 
613     // Pass the parents nicEnabled property, so that the child
614     // VLAN interface can inherit.
615     auto vlanIntf = std::make_unique<EthernetInterface>(
616         bus, manager, info, objRoot, config::Parser(), nicEnabled());
617     ObjectPath ret = vlanIntf->objPath;
618 
619     manager.get().interfaces.emplace(intfName, std::move(vlanIntf));
620 
621     // write the device file for the vlan interface.
622     config::Parser config;
623     auto& netdev = config.map["NetDev"].emplace_back();
624     netdev["Name"].emplace_back(intfName);
625     netdev["Kind"].emplace_back("vlan");
626     config.map["VLAN"].emplace_back()["Id"].emplace_back(std::move(idStr));
627     config.writeFile(
628         config::pathForIntfDev(manager.get().getConfDir(), intfName));
629 
630     writeConfigurationFile();
631     manager.get().reloadConfigs();
632 
633     return ret;
634 }
635 
636 ServerList EthernetInterface::staticNTPServers(ServerList value)
637 {
638     value = EthernetInterfaceIntf::staticNTPServers(std::move(value));
639 
640     writeConfigurationFile();
641     manager.get().reloadConfigs();
642 
643     return value;
644 }
645 
646 ServerList EthernetInterface::ntpServers(ServerList /*servers*/)
647 {
648     elog<NotAllowed>(NotAllowedArgument::REASON("ReadOnly Property"));
649 }
650 
651 static constexpr std::string_view tfStr(bool value)
652 {
653     return value ? "true"sv : "false"sv;
654 }
655 
656 static void writeUpdatedTime(const Manager& manager,
657                              const std::filesystem::path& netFile)
658 {
659     // JFFS2 doesn't have the time granularity to deal with sub-second
660     // updates. Since we can have multiple file updates within a second
661     // around a reload, we need a location which gives that precision for
662     // future networkd detected reloads. TMPFS gives us this property.
663     if (manager.getConfDir() == "/etc/systemd/network"sv)
664     {
665         auto dir = stdplus::strCat(netFile.native(), ".d");
666         dir.replace(1, 3, "run"); // Replace /etc with /run
667         auto file = dir + "/updated.conf";
668         try
669         {
670             std::filesystem::create_directories(dir);
671             using namespace stdplus::fd;
672             futimens(
673                 open(file,
674                      OpenFlags(OpenAccess::WriteOnly).set(OpenFlag::Create),
675                      0644)
676                     .get(),
677                 nullptr);
678         }
679         catch (const std::exception& e)
680         {
681             lg2::error("Failed to write time updated file {FILE}: {ERROR}",
682                        "FILE", file, "ERROR", e.what());
683         }
684     }
685 }
686 
687 void EthernetInterface::writeConfigurationFile()
688 {
689     config::Parser config;
690     config.map["Match"].emplace_back()["Name"].emplace_back(interfaceName());
691     {
692         auto& link = config.map["Link"].emplace_back();
693 #ifdef PERSIST_MAC
694         auto mac = MacAddressIntf::macAddress();
695         if (!mac.empty())
696         {
697             link["MACAddress"].emplace_back(mac);
698         }
699 #endif
700         if (!EthernetInterfaceIntf::nicEnabled())
701         {
702             link["ActivationPolicy"].emplace_back("down");
703         }
704     }
705     {
706         auto& network = config.map["Network"].emplace_back();
707         auto& lla = network["LinkLocalAddressing"];
708 #ifdef LINK_LOCAL_AUTOCONFIGURATION
709         lla.emplace_back("yes");
710 #else
711         lla.emplace_back("no");
712 #endif
713         network["IPv6AcceptRA"].emplace_back(ipv6AcceptRA() ? "true" : "false");
714         network["DHCP"].emplace_back(dhcp4() ? (dhcp6() ? "true" : "ipv4")
715                                              : (dhcp6() ? "ipv6" : "false"));
716         {
717             auto& vlans = network["VLAN"];
718             for (const auto& [_, intf] : manager.get().interfaces)
719             {
720                 if (intf->vlan && intf->vlan->parentIdx == ifIdx)
721                 {
722                     vlans.emplace_back(intf->interfaceName());
723                 }
724             }
725         }
726         {
727             auto& ntps = network["NTP"];
728             for (const auto& ntp : EthernetInterfaceIntf::staticNTPServers())
729             {
730                 ntps.emplace_back(ntp);
731             }
732         }
733         {
734             auto& dnss = network["DNS"];
735             for (const auto& dns : EthernetInterfaceIntf::staticNameServers())
736             {
737                 dnss.emplace_back(dns);
738             }
739         }
740         {
741             auto& address = network["Address"];
742             for (const auto& addr : addrs)
743             {
744                 if (originIsManuallyAssigned(addr.second->origin()))
745                 {
746                     address.emplace_back(stdplus::toStr(addr.first));
747                 }
748             }
749         }
750         {
751             if (!dhcp4())
752             {
753                 auto gateway4 = EthernetInterfaceIntf::defaultGateway();
754                 if (!gateway4.empty())
755                 {
756                     auto& gateway4route = config.map["Route"].emplace_back();
757                     gateway4route["Gateway"].emplace_back(gateway4);
758                     gateway4route["GatewayOnLink"].emplace_back("true");
759                 }
760             }
761 
762             if (!ipv6AcceptRA())
763             {
764                 auto gateway6 = EthernetInterfaceIntf::defaultGateway6();
765                 if (!gateway6.empty())
766                 {
767                     auto& gateway6route = config.map["Route"].emplace_back();
768                     gateway6route["Gateway"].emplace_back(gateway6);
769                     gateway6route["GatewayOnLink"].emplace_back("true");
770                 }
771             }
772         }
773     }
774     config.map["IPv6AcceptRA"].emplace_back()["DHCPv6Client"].emplace_back(
775         dhcp6() ? "true" : "false");
776     {
777         auto& neighbors = config.map["Neighbor"];
778         for (const auto& sneighbor : staticNeighbors)
779         {
780             auto& neighbor = neighbors.emplace_back();
781             neighbor["Address"].emplace_back(sneighbor.second->ipAddress());
782             neighbor["MACAddress"].emplace_back(sneighbor.second->macAddress());
783         }
784     }
785     {
786         auto& dhcp4 = config.map["DHCPv4"].emplace_back();
787         dhcp4["ClientIdentifier"].emplace_back("mac");
788         dhcp4["UseDNS"].emplace_back(tfStr(dhcp4Conf->dnsEnabled()));
789         dhcp4["UseDomains"].emplace_back(tfStr(dhcp4Conf->domainEnabled()));
790         dhcp4["UseNTP"].emplace_back(tfStr(dhcp4Conf->ntpEnabled()));
791         dhcp4["UseHostname"].emplace_back(tfStr(dhcp4Conf->hostNameEnabled()));
792         dhcp4["SendHostname"].emplace_back(
793             tfStr(dhcp4Conf->sendHostNameEnabled()));
794     }
795     {
796         auto& dhcp6 = config.map["DHCPv6"].emplace_back();
797         dhcp6["UseDNS"].emplace_back(tfStr(dhcp6Conf->dnsEnabled()));
798         dhcp6["UseDomains"].emplace_back(tfStr(dhcp6Conf->domainEnabled()));
799         dhcp6["UseNTP"].emplace_back(tfStr(dhcp6Conf->ntpEnabled()));
800         dhcp6["UseHostname"].emplace_back(tfStr(dhcp6Conf->hostNameEnabled()));
801         dhcp6["SendHostname"].emplace_back(
802             tfStr(dhcp6Conf->sendHostNameEnabled()));
803     }
804     auto path =
805         config::pathForIntfConf(manager.get().getConfDir(), interfaceName());
806     config.writeFile(path);
807     lg2::info("Wrote networkd file: {CFG_FILE}", "CFG_FILE", path);
808     writeUpdatedTime(manager, path);
809 }
810 
811 std::string EthernetInterface::macAddress([[maybe_unused]] std::string value)
812 {
813     if (vlan)
814     {
815         lg2::error("Tried to set MAC address on VLAN");
816         elog<InternalFailure>();
817     }
818 #ifdef PERSIST_MAC
819     stdplus::EtherAddr newMAC;
820     try
821     {
822         newMAC = stdplus::fromStr<stdplus::EtherAddr>(value);
823     }
824     catch (const std::invalid_argument&)
825     {
826         lg2::error("MAC Address {NET_MAC} is not valid", "NET_MAC", value);
827         elog<InvalidArgument>(Argument::ARGUMENT_NAME("MACAddress"),
828                               Argument::ARGUMENT_VALUE(value.c_str()));
829     }
830     if (!newMAC.isUnicast())
831     {
832         lg2::error("MAC Address {NET_MAC} is not valid", "NET_MAC", value);
833         elog<InvalidArgument>(Argument::ARGUMENT_NAME("MACAddress"),
834                               Argument::ARGUMENT_VALUE(value.c_str()));
835     }
836 
837     auto interface = interfaceName();
838     auto validMAC = stdplus::toStr(newMAC);
839 
840     // We don't need to update the system if the address is unchanged
841     auto oldMAC =
842         stdplus::fromStr<stdplus::EtherAddr>(MacAddressIntf::macAddress());
843     if (newMAC != oldMAC)
844     {
845         // Update everything that depends on the MAC value
846         for (const auto& [_, intf] : manager.get().interfaces)
847         {
848             if (intf->vlan && intf->vlan->parentIdx == ifIdx)
849             {
850                 intf->MacAddressIntf::macAddress(validMAC);
851             }
852         }
853         MacAddressIntf::macAddress(validMAC);
854 
855         writeConfigurationFile();
856         manager.get().addReloadPreHook([interface, manager = manager]() {
857             // The MAC and LLADDRs will only update if the NIC is already down
858             system::setNICUp(interface, false);
859             writeUpdatedTime(
860                 manager,
861                 config::pathForIntfConf(manager.get().getConfDir(), interface));
862         });
863         manager.get().reloadConfigs();
864     }
865 
866 #ifdef HAVE_UBOOT_ENV
867     // Ensure that the valid address is stored in the u-boot-env
868     auto envVar = interfaceToUbootEthAddr(interface);
869     if (envVar)
870     {
871         // Trimming MAC addresses that are out of range. eg: AA:FF:FF:FF:FF:100;
872         // and those having more than 6 bytes. eg: AA:AA:AA:AA:AA:AA:BB
873         execute("/sbin/fw_setenv", "fw_setenv", envVar->c_str(),
874                 validMAC.c_str());
875     }
876 #endif // HAVE_UBOOT_ENV
877 
878     return value;
879 #else
880     elog<NotAllowed>(
881         NotAllowedArgument::REASON("Writing MAC address is not allowed"));
882 #endif // PERSIST_MAC
883 }
884 
885 void EthernetInterface::deleteAll()
886 {
887     // clear all the ip on the interface
888     addrs.clear();
889 
890     writeConfigurationFile();
891     manager.get().reloadConfigs();
892 }
893 
894 template <typename Addr>
895 static void normalizeGateway(std::string& gw)
896 {
897     if (gw.empty())
898     {
899         return;
900     }
901     try
902     {
903         auto ip = stdplus::fromStr<Addr>(gw);
904         if (ip == Addr{})
905         {
906             gw.clear();
907             return;
908         }
909         if (!validIntfIP(ip))
910         {
911             throw std::invalid_argument("Invalid unicast");
912         }
913         gw = stdplus::toStr(ip);
914     }
915     catch (const std::exception& e)
916     {
917         lg2::error("Invalid GW `{NET_GW}`: {ERROR}", "NET_GW", gw, "ERROR", e);
918         elog<InvalidArgument>(Argument::ARGUMENT_NAME("GATEWAY"),
919                               Argument::ARGUMENT_VALUE(gw.c_str()));
920     }
921 }
922 
923 std::string EthernetInterface::defaultGateway(std::string gateway)
924 {
925     normalizeGateway<stdplus::In4Addr>(gateway);
926     if (gateway != defaultGateway())
927     {
928         gateway = EthernetInterfaceIntf::defaultGateway(std::move(gateway));
929         writeConfigurationFile();
930         manager.get().reloadConfigs();
931     }
932     return gateway;
933 }
934 
935 std::string EthernetInterface::defaultGateway6(std::string gateway)
936 {
937     normalizeGateway<stdplus::In6Addr>(gateway);
938     if (gateway != defaultGateway6())
939     {
940         gateway = EthernetInterfaceIntf::defaultGateway6(std::move(gateway));
941         writeConfigurationFile();
942         manager.get().reloadConfigs();
943     }
944     return gateway;
945 }
946 
947 EthernetInterface::VlanProperties::VlanProperties(
948     sdbusplus::bus_t& bus, stdplus::const_zstring objPath,
949     const InterfaceInfo& info, stdplus::PinnedRef<EthernetInterface> eth) :
950     VlanIfaces(bus, objPath.c_str(), VlanIfaces::action::defer_emit),
951     parentIdx(*info.parent_idx), eth(eth)
952 {
953     VlanIntf::id(*info.vlan_id, true);
954     emit_object_added();
955 }
956 
957 void EthernetInterface::VlanProperties::delete_()
958 {
959     auto intf = eth.get().interfaceName();
960 
961     // Remove all configs for the current interface
962     const auto& confDir = eth.get().manager.get().getConfDir();
963     std::error_code ec;
964     std::filesystem::remove(config::pathForIntfConf(confDir, intf), ec);
965     std::filesystem::remove(config::pathForIntfDev(confDir, intf), ec);
966 
967     if (eth.get().ifIdx > 0)
968     {
969         eth.get().manager.get().interfacesByIdx.erase(eth.get().ifIdx);
970     }
971     auto it = eth.get().manager.get().interfaces.find(intf);
972     auto obj = std::move(it->second);
973     eth.get().manager.get().interfaces.erase(it);
974 
975     // Write an updated parent interface since it has a VLAN entry
976     for (const auto& [_, intf] : eth.get().manager.get().interfaces)
977     {
978         if (intf->ifIdx == parentIdx)
979         {
980             intf->writeConfigurationFile();
981         }
982     }
983 
984     if (eth.get().ifIdx > 0)
985     {
986         // We need to forcibly delete the interface as systemd does not
987         eth.get().manager.get().addReloadPostHook([idx = eth.get().ifIdx]() {
988             system::deleteIntf(idx);
989         });
990 
991         // Ignore the interface so the reload doesn't re-query it
992         eth.get().manager.get().ignoredIntf.emplace(eth.get().ifIdx);
993     }
994 
995     eth.get().manager.get().reloadConfigs();
996 }
997 
998 void EthernetInterface::reloadConfigs()
999 {
1000     manager.get().reloadConfigs();
1001 }
1002 
1003 } // namespace network
1004 } // namespace phosphor
1005