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