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