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