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