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 
14 #include <phosphor-logging/elog-errors.hpp>
15 #include <phosphor-logging/lg2.hpp>
16 #include <stdplus/raw.hpp>
17 #include <stdplus/str/cat.hpp>
18 #include <stdplus/zstring.hpp>
19 #include <xyz/openbmc_project/Common/error.hpp>
20 
21 #include <algorithm>
22 #include <filesystem>
23 #include <format>
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 = stdplus::strCat(root, "/"sv, intf);
70     std::replace(ret.begin() + ret.size() - intf.size(), ret.end(), '.', '_');
71     return ret;
72 }
73 
74 template <typename Addr>
75 static bool validIntfIP(Addr a) noexcept
76 {
77     return a.isUnicast() && !a.isLoopback();
78 }
79 
80 EthernetInterface::EthernetInterface(stdplus::PinnedRef<sdbusplus::bus_t> bus,
81                                      stdplus::PinnedRef<Manager> manager,
82                                      const AllIntfInfo& info,
83                                      std::string_view objRoot,
84                                      const config::Parser& config,
85                                      bool enabled) :
86     EthernetInterface(bus, manager, info, makeObjPath(objRoot, *info.intf.name),
87                       config, enabled)
88 {}
89 
90 EthernetInterface::EthernetInterface(stdplus::PinnedRef<sdbusplus::bus_t> bus,
91                                      stdplus::PinnedRef<Manager> manager,
92                                      const AllIntfInfo& info,
93                                      std::string&& objPath,
94                                      const config::Parser& config,
95                                      bool enabled) :
96     Ifaces(bus, objPath.c_str(), Ifaces::action::defer_emit),
97     manager(manager), bus(bus), objPath(std::move(objPath))
98 {
99     interfaceName(*info.intf.name, true);
100     auto dhcpVal = getDHCPValue(config);
101     EthernetInterfaceIntf::dhcp4(dhcpVal.v4, true);
102     EthernetInterfaceIntf::dhcp6(dhcpVal.v6, true);
103     EthernetInterfaceIntf::ipv6AcceptRA(getIPv6AcceptRA(config), true);
104     EthernetInterfaceIntf::nicEnabled(enabled, true);
105 
106     EthernetInterfaceIntf::ntpServers(
107         config.map.getValueStrings("Network", "NTP"), true);
108 
109     updateInfo(info.intf, true);
110 
111     if (info.defgw4)
112     {
113         EthernetInterface::defaultGateway(stdplus::toStr(*info.defgw4), true);
114     }
115     if (info.defgw6)
116     {
117         EthernetInterface::defaultGateway6(stdplus::toStr(*info.defgw6), true);
118     }
119     emit_object_added();
120 
121     if (info.intf.vlan_id)
122     {
123         if (!info.intf.parent_idx)
124         {
125             std::runtime_error("Missing parent link");
126         }
127         vlan.emplace(bus, this->objPath.c_str(), info.intf, *this);
128     }
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          origin == IP::AddressOrigin::LinkLocal)
167 #else
168         (origin == IP::AddressOrigin::Static)
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(*info.addr, std::make_unique<Neighbor>(
234                                                 bus, std::string_view(objPath),
235                                                 *this, *info.addr, *info.mac,
236                                                 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(value == DHCPConf::v4 ||
397                                              value == DHCPConf::v4v6stateless ||
398                                              value == DHCPConf::both);
399     auto old6 = EthernetInterfaceIntf::dhcp6();
400     auto new6 = EthernetInterfaceIntf::dhcp6(value == DHCPConf::v6 ||
401                                              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(
456             [ifname = interfaceName()]() { system::setNICUp(ifname, false); });
457     }
458     manager.get().reloadConfigs();
459 
460     return value;
461 }
462 
463 ServerList EthernetInterface::staticNameServers(ServerList value)
464 {
465     for (auto& ip : value)
466     {
467         try
468         {
469             ip = stdplus::toStr(stdplus::fromStr<stdplus::InAnyAddr>(ip));
470         }
471         catch (const std::exception& e)
472         {
473             lg2::error("Not a valid IP address {NET_IP}: {ERROR}", "NET_IP", ip,
474                        "ERROR", e);
475             elog<InvalidArgument>(Argument::ARGUMENT_NAME("StaticNameserver"),
476                                   Argument::ARGUMENT_VALUE(ip.c_str()));
477         }
478     }
479 
480     value = EthernetInterfaceIntf::staticNameServers(std::move(value));
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 = bus.get().new_method_call(TIMESYNCD_SERVICE,
506                                             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 // Need to merge the below function with the code which writes the
651 // config file during factory reset.
652 // TODO openbmc/openbmc#1751
653 
654 void EthernetInterface::writeConfigurationFile()
655 {
656     config::Parser config;
657     config.map["Match"].emplace_back()["Name"].emplace_back(interfaceName());
658     {
659         auto& link = config.map["Link"].emplace_back();
660 #ifdef PERSIST_MAC
661         auto mac = MacAddressIntf::macAddress();
662         if (!mac.empty())
663         {
664             link["MACAddress"].emplace_back(mac);
665         }
666 #endif
667         if (!EthernetInterfaceIntf::nicEnabled())
668         {
669             link["Unmanaged"].emplace_back("yes");
670         }
671     }
672     {
673         auto& network = config.map["Network"].emplace_back();
674         auto& lla = network["LinkLocalAddressing"];
675 #ifdef LINK_LOCAL_AUTOCONFIGURATION
676         lla.emplace_back("yes");
677 #else
678         lla.emplace_back("no");
679 #endif
680         network["IPv6AcceptRA"].emplace_back(ipv6AcceptRA() ? "true" : "false");
681         network["DHCP"].emplace_back(dhcp4() ? (dhcp6() ? "true" : "ipv4")
682                                              : (dhcp6() ? "ipv6" : "false"));
683         {
684             auto& vlans = network["VLAN"];
685             for (const auto& [_, intf] : manager.get().interfaces)
686             {
687                 if (intf->vlan && intf->vlan->parentIdx == ifIdx)
688                 {
689                     vlans.emplace_back(intf->interfaceName());
690                 }
691             }
692         }
693         {
694             auto& ntps = network["NTP"];
695             for (const auto& ntp : EthernetInterfaceIntf::staticNTPServers())
696             {
697                 ntps.emplace_back(ntp);
698             }
699         }
700         {
701             auto& dnss = network["DNS"];
702             for (const auto& dns : EthernetInterfaceIntf::staticNameServers())
703             {
704                 dnss.emplace_back(dns);
705             }
706         }
707         {
708             auto& address = network["Address"];
709             for (const auto& addr : addrs)
710             {
711                 if (originIsManuallyAssigned(addr.second->origin()))
712                 {
713                     address.emplace_back(stdplus::toStr(addr.first));
714                 }
715             }
716         }
717         {
718             auto& gateways = network["Gateway"];
719             if (!dhcp4())
720             {
721                 auto gateway = EthernetInterfaceIntf::defaultGateway();
722                 if (!gateway.empty())
723                 {
724                     gateways.emplace_back(gateway);
725                 }
726             }
727 
728             if (!ipv6AcceptRA())
729             {
730                 auto gateway6 = EthernetInterfaceIntf::defaultGateway6();
731                 if (!gateway6.empty())
732                 {
733                     gateways.emplace_back(gateway6);
734                 }
735             }
736         }
737     }
738     config.map["IPv6AcceptRA"].emplace_back()["DHCPv6Client"].emplace_back(
739         dhcp6() ? "true" : "false");
740     {
741         auto& neighbors = config.map["Neighbor"];
742         for (const auto& sneighbor : staticNeighbors)
743         {
744             auto& neighbor = neighbors.emplace_back();
745             neighbor["Address"].emplace_back(sneighbor.second->ipAddress());
746             neighbor["MACAddress"].emplace_back(sneighbor.second->macAddress());
747         }
748     }
749     {
750         auto& dhcp = config.map["DHCP"].emplace_back();
751         dhcp["ClientIdentifier"].emplace_back("mac");
752         const auto& conf = manager.get().getDHCPConf();
753         auto dns_enabled = conf.dnsEnabled() ? "true" : "false";
754         dhcp["UseDNS"].emplace_back(dns_enabled);
755         dhcp["UseDomains"].emplace_back(dns_enabled);
756         dhcp["UseNTP"].emplace_back(conf.ntpEnabled() ? "true" : "false");
757         dhcp["UseHostname"].emplace_back(conf.hostNameEnabled() ? "true"
758                                                                 : "false");
759         dhcp["SendHostname"].emplace_back(conf.sendHostNameEnabled() ? "true"
760                                                                      : "false");
761     }
762     auto path = config::pathForIntfConf(manager.get().getConfDir(),
763                                         interfaceName());
764     config.writeFile(path);
765     lg2::info("Wrote networkd file: {CFG_FILE}", "CFG_FILE", path);
766 }
767 
768 std::string EthernetInterface::macAddress([[maybe_unused]] std::string value)
769 {
770     if (vlan)
771     {
772         lg2::error("Tried to set MAC address on VLAN");
773         elog<InternalFailure>();
774     }
775 #ifdef PERSIST_MAC
776     stdplus::EtherAddr newMAC;
777     try
778     {
779         newMAC = stdplus::fromStr<stdplus::EtherAddr>(value);
780     }
781     catch (const std::invalid_argument&)
782     {
783         lg2::error("MAC Address {NET_MAC} is not valid", "NET_MAC", value);
784         elog<InvalidArgument>(Argument::ARGUMENT_NAME("MACAddress"),
785                               Argument::ARGUMENT_VALUE(value.c_str()));
786     }
787     if (!newMAC.isUnicast())
788     {
789         lg2::error("MAC Address {NET_MAC} is not valid", "NET_MAC", value);
790         elog<InvalidArgument>(Argument::ARGUMENT_NAME("MACAddress"),
791                               Argument::ARGUMENT_VALUE(value.c_str()));
792     }
793 
794     auto interface = interfaceName();
795     auto validMAC = stdplus::toStr(newMAC);
796 
797     // We don't need to update the system if the address is unchanged
798     auto oldMAC =
799         stdplus::fromStr<stdplus::EtherAddr>(MacAddressIntf::macAddress());
800     if (newMAC != oldMAC)
801     {
802         // Update everything that depends on the MAC value
803         for (const auto& [_, intf] : manager.get().interfaces)
804         {
805             if (intf->vlan && intf->vlan->parentIdx == ifIdx)
806             {
807                 intf->MacAddressIntf::macAddress(validMAC);
808             }
809         }
810         MacAddressIntf::macAddress(validMAC);
811 
812         writeConfigurationFile();
813         manager.get().addReloadPreHook([interface]() {
814             // The MAC and LLADDRs will only update if the NIC is already down
815             system::setNICUp(interface, false);
816         });
817         manager.get().reloadConfigs();
818     }
819 
820 #ifdef HAVE_UBOOT_ENV
821     // Ensure that the valid address is stored in the u-boot-env
822     auto envVar = interfaceToUbootEthAddr(interface);
823     if (envVar)
824     {
825         // Trimming MAC addresses that are out of range. eg: AA:FF:FF:FF:FF:100;
826         // and those having more than 6 bytes. eg: AA:AA:AA:AA:AA:AA:BB
827         execute("/sbin/fw_setenv", "fw_setenv", envVar->c_str(),
828                 validMAC.c_str());
829     }
830 #endif // HAVE_UBOOT_ENV
831 
832     return value;
833 #else
834     elog<NotAllowed>(
835         NotAllowedArgument::REASON("Writing MAC address is not allowed"));
836 #endif // PERSIST_MAC
837 }
838 
839 void EthernetInterface::deleteAll()
840 {
841     // clear all the ip on the interface
842     addrs.clear();
843 
844     writeConfigurationFile();
845     manager.get().reloadConfigs();
846 }
847 
848 template <typename Addr>
849 static void normalizeGateway(std::string& gw)
850 {
851     if (gw.empty())
852     {
853         return;
854     }
855     try
856     {
857         auto ip = stdplus::fromStr<Addr>(gw);
858         if (ip == Addr{})
859         {
860             gw.clear();
861             return;
862         }
863         if (!validIntfIP(ip))
864         {
865             throw std::invalid_argument("Invalid unicast");
866         }
867         gw = stdplus::toStr(ip);
868     }
869     catch (const std::exception& e)
870     {
871         lg2::error("Invalid GW `{NET_GW}`: {ERROR}", "NET_GW", gw, "ERROR", e);
872         elog<InvalidArgument>(Argument::ARGUMENT_NAME("GATEWAY"),
873                               Argument::ARGUMENT_VALUE(gw.c_str()));
874     }
875 }
876 
877 std::string EthernetInterface::defaultGateway(std::string gateway)
878 {
879     normalizeGateway<stdplus::In4Addr>(gateway);
880     if (gateway != defaultGateway())
881     {
882         gateway = EthernetInterfaceIntf::defaultGateway(std::move(gateway));
883         manager.get().reloadConfigs();
884     }
885     return gateway;
886 }
887 
888 std::string EthernetInterface::defaultGateway6(std::string gateway)
889 {
890     normalizeGateway<stdplus::In6Addr>(gateway);
891     if (gateway != defaultGateway6())
892     {
893         gateway = EthernetInterfaceIntf::defaultGateway6(std::move(gateway));
894         manager.get().reloadConfigs();
895     }
896     return gateway;
897 }
898 
899 EthernetInterface::VlanProperties::VlanProperties(
900     sdbusplus::bus_t& bus, stdplus::const_zstring objPath,
901     const InterfaceInfo& info, stdplus::PinnedRef<EthernetInterface> eth) :
902     VlanIfaces(bus, objPath.c_str(), VlanIfaces::action::defer_emit),
903     parentIdx(*info.parent_idx), eth(eth)
904 {
905     VlanIntf::id(*info.vlan_id, true);
906     emit_object_added();
907 }
908 
909 void EthernetInterface::VlanProperties::delete_()
910 {
911     auto intf = eth.get().interfaceName();
912 
913     // Remove all configs for the current interface
914     const auto& confDir = eth.get().manager.get().getConfDir();
915     std::error_code ec;
916     std::filesystem::remove(config::pathForIntfConf(confDir, intf), ec);
917     std::filesystem::remove(config::pathForIntfDev(confDir, intf), ec);
918 
919     if (eth.get().ifIdx > 0)
920     {
921         eth.get().manager.get().interfacesByIdx.erase(eth.get().ifIdx);
922     }
923     auto it = eth.get().manager.get().interfaces.find(intf);
924     auto obj = std::move(it->second);
925     eth.get().manager.get().interfaces.erase(it);
926 
927     // Write an updated parent interface since it has a VLAN entry
928     for (const auto& [_, intf] : eth.get().manager.get().interfaces)
929     {
930         if (intf->ifIdx == parentIdx)
931         {
932             intf->writeConfigurationFile();
933         }
934     }
935 
936     if (eth.get().ifIdx > 0)
937     {
938         // We need to forcibly delete the interface as systemd does not
939         eth.get().manager.get().addReloadPostHook(
940             [idx = eth.get().ifIdx]() { system::deleteIntf(idx); });
941 
942         // Ignore the interface so the reload doesn't re-query it
943         eth.get().manager.get().ignoredIntf.emplace(eth.get().ifIdx);
944     }
945 
946     eth.get().manager.get().reloadConfigs();
947 }
948 
949 } // namespace network
950 } // namespace phosphor
951