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