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