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