xref: /openbmc/phosphor-networkd/src/ethernet_interface.cpp (revision 59e5b91d9784274d1d99b4c10e939c38606efacc)
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 (const auto& nameserverip : value)
567     {
568         if (!isValidIP(nameserverip))
569         {
570             log<level::ERR>("Not a valid IP address"),
571                 entry("ADDRESS=%s", nameserverip.c_str());
572             elog<InvalidArgument>(
573                 Argument::ARGUMENT_NAME("StaticNameserver"),
574                 Argument::ARGUMENT_VALUE(nameserverip.c_str()));
575         }
576     }
577     try
578     {
579         EthernetInterfaceIntf::staticNameServers(value);
580 
581         writeConfigurationFile();
582         manager.reloadConfigs();
583     }
584     catch (const InternalFailure& e)
585     {
586         log<level::ERR>("Exception processing DNS entries");
587     }
588     return EthernetInterfaceIntf::staticNameServers();
589 }
590 
591 void EthernetInterface::loadNTPServers(const config::Parser& config)
592 {
593     EthernetInterfaceIntf::ntpServers(getNTPServerFromTimeSyncd());
594     EthernetInterfaceIntf::staticNTPServers(
595         config.map.getValueStrings("Network", "NTP"));
596 }
597 
598 void EthernetInterface::loadNameServers(const config::Parser& config)
599 {
600     EthernetInterfaceIntf::nameservers(getNameServerFromResolvd());
601     EthernetInterfaceIntf::staticNameServers(
602         config.map.getValueStrings("Network", "DNS"));
603 }
604 
605 ServerList EthernetInterface::getNTPServerFromTimeSyncd()
606 {
607     ServerList servers; // Variable to capture the NTP Server IPs
608     auto method = bus.new_method_call(TIMESYNCD_SERVICE, TIMESYNCD_SERVICE_PATH,
609                                       PROPERTY_INTERFACE, METHOD_GET);
610 
611     method.append(TIMESYNCD_INTERFACE, "LinkNTPServers");
612 
613     try
614     {
615         auto reply = bus.call(method);
616         std::variant<ServerList> response;
617         reply.read(response);
618         servers = std::get<ServerList>(response);
619     }
620     catch (const sdbusplus::exception::SdBusError& e)
621     {
622         log<level::ERR>(
623             "Failed to get NTP server information from Systemd-Timesyncd");
624     }
625 
626     return servers;
627 }
628 
629 ServerList EthernetInterface::getNameServerFromResolvd()
630 {
631     ServerList servers;
632     auto OBJ_PATH = fmt::format("{}{}", RESOLVED_SERVICE_PATH, ifIdx);
633 
634     /*
635       The DNS property under org.freedesktop.resolve1.Link interface contains
636       an array containing all DNS servers currently used by resolved. It
637       contains similar information as the DNS server data written to
638       /run/systemd/resolve/resolv.conf.
639 
640       Each structure in the array consists of a numeric network interface index,
641       an address family, and a byte array containing the DNS server address
642       (either 4 bytes in length for IPv4 or 16 bytes in lengths for IPv6).
643       The array contains DNS servers configured system-wide, including those
644       possibly read from a foreign /etc/resolv.conf or the DNS= setting in
645       /etc/systemd/resolved.conf, as well as per-interface DNS server
646       information either retrieved from systemd-networkd or configured by
647       external software via SetLinkDNS().
648     */
649 
650     using type = std::vector<std::tuple<int32_t, std::vector<uint8_t>>>;
651     std::variant<type> name; // Variable to capture the DNS property
652     auto method = bus.new_method_call(RESOLVED_SERVICE, OBJ_PATH.c_str(),
653                                       PROPERTY_INTERFACE, METHOD_GET);
654 
655     method.append(RESOLVED_INTERFACE, "DNS");
656 
657     try
658     {
659         auto reply = bus.call(method);
660         reply.read(name);
661     }
662     catch (const sdbusplus::exception_t& e)
663     {
664         log<level::ERR>("Failed to get DNS information from Systemd-Resolved");
665     }
666     auto tupleVector = std::get_if<type>(&name);
667     for (auto i = tupleVector->begin(); i != tupleVector->end(); ++i)
668     {
669         int addressFamily = std::get<0>(*i);
670         std::vector<uint8_t>& ipaddress = std::get<1>(*i);
671         servers.push_back(std::to_string(
672             addrFromBuf(addressFamily, stdplus::raw::asView<char>(ipaddress))));
673     }
674     return servers;
675 }
676 
677 ObjectPath EthernetInterface::createVLAN(uint16_t id)
678 {
679     auto intfName = fmt::format(FMT_COMPILE("{}.{}"), interfaceName(), id);
680     auto idStr = std::to_string(id);
681     if (manager.interfaces.find(intfName) != manager.interfaces.end())
682     {
683         log<level::ERR>("VLAN already exists", entry("VLANID=%u", id));
684         elog<InvalidArgument>(Argument::ARGUMENT_NAME("VLANId"),
685                               Argument::ARGUMENT_VALUE(idStr.c_str()));
686     }
687 
688     auto objRoot = std::string_view(objPath).substr(0, objPath.rfind('/'));
689     auto macStr = MacAddressIntf::macAddress();
690     std::optional<ether_addr> mac;
691     if (!macStr.empty())
692     {
693         mac.emplace(ToAddr<ether_addr>{}(macStr));
694     }
695     auto info = system::InterfaceInfo{
696         .idx = 0, // TODO: Query the correct value after creation
697         .flags = 0,
698         .name = intfName,
699         .mac = std::move(mac),
700         .mtu = mtu(),
701         .parent_idx = ifIdx,
702         .vlan_id = id,
703     };
704 
705     // Pass the parents nicEnabled property, so that the child
706     // VLAN interface can inherit.
707     auto vlanIntf = std::make_unique<EthernetInterface>(
708         bus, manager, info, objRoot, config::Parser(), /*emit=*/true,
709         nicEnabled());
710     ObjectPath ret = vlanIntf->objPath;
711 
712     manager.interfaces.emplace(intfName, std::move(vlanIntf));
713 
714     // write the device file for the vlan interface.
715     config::Parser config;
716     auto& netdev = config.map["NetDev"].emplace_back();
717     netdev["Name"].emplace_back(intfName);
718     netdev["Kind"].emplace_back("vlan");
719     config.map["VLAN"].emplace_back()["Id"].emplace_back(std::move(idStr));
720     config.writeFile(config::pathForIntfDev(manager.getConfDir(), intfName));
721 
722     writeConfigurationFile();
723     manager.reloadConfigs();
724 
725     return objPath;
726 }
727 
728 ServerList EthernetInterface::staticNTPServers(ServerList value)
729 {
730     try
731     {
732         EthernetInterfaceIntf::staticNTPServers(value);
733 
734         writeConfigurationFile();
735         manager.reloadConfigs();
736     }
737     catch (InternalFailure& e)
738     {
739         log<level::ERR>("Exception processing NTP entries");
740     }
741     return EthernetInterfaceIntf::staticNTPServers();
742 }
743 
744 ServerList EthernetInterface::ntpServers(ServerList /*servers*/)
745 {
746     elog<NotAllowed>(NotAllowedArgument::REASON("ReadOnly Property"));
747 }
748 // Need to merge the below function with the code which writes the
749 // config file during factory reset.
750 // TODO openbmc/openbmc#1751
751 
752 void EthernetInterface::writeConfigurationFile()
753 {
754     config::Parser config;
755     config.map["Match"].emplace_back()["Name"].emplace_back(interfaceName());
756     {
757         auto& link = config.map["Link"].emplace_back();
758 #ifdef PERSIST_MAC
759         auto mac = MacAddressIntf::macAddress();
760         if (!mac.empty())
761         {
762             link["MACAddress"].emplace_back(mac);
763         }
764 #endif
765         if (!EthernetInterfaceIntf::nicEnabled())
766         {
767             link["Unmanaged"].emplace_back("yes");
768         }
769     }
770     {
771         auto& network = config.map["Network"].emplace_back();
772         auto& lla = network["LinkLocalAddressing"];
773 #ifdef LINK_LOCAL_AUTOCONFIGURATION
774         lla.emplace_back("yes");
775 #else
776         lla.emplace_back("no");
777 #endif
778         network["IPv6AcceptRA"].emplace_back(ipv6AcceptRA() ? "true" : "false");
779         network["DHCP"].emplace_back(dhcp4() ? (dhcp6() ? "true" : "ipv4")
780                                              : (dhcp6() ? "ipv6" : "false"));
781         {
782             auto& vlans = network["VLAN"];
783             for (const auto& [_, intf] : manager.interfaces)
784             {
785                 if (intf->vlan && intf->vlan->parentIdx == ifIdx)
786                 {
787                     vlans.emplace_back(intf->interfaceName());
788                 }
789             }
790         }
791         {
792             auto& ntps = network["NTP"];
793             for (const auto& ntp : EthernetInterfaceIntf::staticNTPServers())
794             {
795                 ntps.emplace_back(ntp);
796             }
797         }
798         {
799             auto& dnss = network["DNS"];
800             for (const auto& dns : EthernetInterfaceIntf::staticNameServers())
801             {
802                 dnss.emplace_back(dns);
803             }
804         }
805         {
806             auto& address = network["Address"];
807             for (const auto& addr : addrs)
808             {
809                 if (originIsManuallyAssigned(addr.second->origin()))
810                 {
811                     address.emplace_back(
812                         fmt::format("{}/{}", addr.second->address(),
813                                     addr.second->prefixLength()));
814                 }
815             }
816         }
817         {
818             auto& gateways = network["Gateway"];
819             if (!dhcp4())
820             {
821                 auto gateway = EthernetInterfaceIntf::defaultGateway();
822                 if (!gateway.empty())
823                 {
824                     gateways.emplace_back(gateway);
825                 }
826             }
827 
828             if (!dhcp6())
829             {
830                 auto gateway6 = EthernetInterfaceIntf::defaultGateway6();
831                 if (!gateway6.empty())
832                 {
833                     gateways.emplace_back(gateway6);
834                 }
835             }
836         }
837     }
838     config.map["IPv6AcceptRA"].emplace_back()["DHCPv6Client"].emplace_back(
839         dhcp6() ? "true" : "false");
840     {
841         auto& neighbors = config.map["Neighbor"];
842         for (const auto& sneighbor : staticNeighbors)
843         {
844             auto& neighbor = neighbors.emplace_back();
845             neighbor["Address"].emplace_back(sneighbor.second->ipAddress());
846             neighbor["MACAddress"].emplace_back(sneighbor.second->macAddress());
847         }
848     }
849     {
850         auto& dhcp = config.map["DHCP"].emplace_back();
851         dhcp["ClientIdentifier"].emplace_back("mac");
852         if (manager.getDHCPConf())
853         {
854             const auto& conf = *manager.getDHCPConf();
855             auto dns_enabled = conf.dnsEnabled() ? "true" : "false";
856             dhcp["UseDNS"].emplace_back(dns_enabled);
857             dhcp["UseDomains"].emplace_back(dns_enabled);
858             dhcp["UseNTP"].emplace_back(conf.ntpEnabled() ? "true" : "false");
859             dhcp["UseHostname"].emplace_back(conf.hostNameEnabled() ? "true"
860                                                                     : "false");
861             dhcp["SendHostname"].emplace_back(
862                 conf.sendHostNameEnabled() ? "true" : "false");
863         }
864     }
865     auto path = config::pathForIntfConf(manager.getConfDir(), interfaceName());
866     config.writeFile(path);
867     auto msg = fmt::format("Wrote networkd file: {}", path.native());
868     log<level::INFO>(msg.c_str(), entry("FILE=%s", path.c_str()));
869 }
870 
871 std::string EthernetInterface::macAddress([[maybe_unused]] std::string value)
872 {
873     if (vlan)
874     {
875         log<level::ERR>("Tried to set MAC address on VLAN");
876         elog<InternalFailure>();
877     }
878 #ifdef PERSIST_MAC
879     ether_addr newMAC;
880     try
881     {
882         newMAC = ToAddr<ether_addr>{}(value);
883     }
884     catch (const std::invalid_argument&)
885     {
886         log<level::ERR>("MACAddress is not valid.",
887                         entry("MAC=%s", value.c_str()));
888         elog<InvalidArgument>(Argument::ARGUMENT_NAME("MACAddress"),
889                               Argument::ARGUMENT_VALUE(value.c_str()));
890     }
891     if (!mac_address::isUnicast(newMAC))
892     {
893         log<level::ERR>("MACAddress is not valid.",
894                         entry("MAC=%s", value.c_str()));
895         elog<InvalidArgument>(Argument::ARGUMENT_NAME("MACAddress"),
896                               Argument::ARGUMENT_VALUE(value.c_str()));
897     }
898 
899     auto interface = interfaceName();
900     auto validMAC = std::to_string(newMAC);
901 
902     // We don't need to update the system if the address is unchanged
903     ether_addr oldMAC = ToAddr<ether_addr>{}(MacAddressIntf::macAddress());
904     if (newMAC != oldMAC)
905     {
906         // Update everything that depends on the MAC value
907         for (const auto& [_, intf] : manager.interfaces)
908         {
909             if (intf->vlan && intf->vlan->parentIdx == ifIdx)
910             {
911                 intf->MacAddressIntf::macAddress(validMAC);
912             }
913         }
914         MacAddressIntf::macAddress(validMAC);
915 
916         writeConfigurationFile();
917         manager.addReloadPreHook([interface]() {
918             // The MAC and LLADDRs will only update if the NIC is already down
919             system::setNICUp(interface, false);
920         });
921         manager.reloadConfigs();
922     }
923 
924 #ifdef HAVE_UBOOT_ENV
925     // Ensure that the valid address is stored in the u-boot-env
926     auto envVar = interfaceToUbootEthAddr(interface);
927     if (envVar)
928     {
929         // Trimming MAC addresses that are out of range. eg: AA:FF:FF:FF:FF:100;
930         // and those having more than 6 bytes. eg: AA:AA:AA:AA:AA:AA:BB
931         execute("/sbin/fw_setenv", "fw_setenv", envVar->c_str(),
932                 validMAC.c_str());
933     }
934 #endif // HAVE_UBOOT_ENV
935 
936     return value;
937 #else
938     elog<NotAllowed>(
939         NotAllowedArgument::REASON("Writing MAC address is not allowed"));
940 #endif // PERSIST_MAC
941 }
942 
943 void EthernetInterface::deleteAll()
944 {
945     // clear all the ip on the interface
946     addrs.clear();
947 
948     writeConfigurationFile();
949     manager.reloadConfigs();
950 }
951 
952 std::string EthernetInterface::defaultGateway(std::string gateway)
953 {
954     auto gw = EthernetInterfaceIntf::defaultGateway();
955     if (gw == gateway)
956     {
957         return gw;
958     }
959 
960     if (!isValidIP(AF_INET, gateway) && !gateway.empty())
961     {
962         log<level::ERR>("Not a valid v4 Gateway",
963                         entry("GATEWAY=%s", gateway.c_str()));
964         elog<InvalidArgument>(Argument::ARGUMENT_NAME("GATEWAY"),
965                               Argument::ARGUMENT_VALUE(gateway.c_str()));
966     }
967     gw = EthernetInterfaceIntf::defaultGateway(gateway);
968 
969     writeConfigurationFile();
970     manager.reloadConfigs();
971 
972     return gw;
973 }
974 
975 std::string EthernetInterface::defaultGateway6(std::string gateway)
976 {
977     auto gw = EthernetInterfaceIntf::defaultGateway6();
978     if (gw == gateway)
979     {
980         return gw;
981     }
982 
983     if (!isValidIP(AF_INET6, gateway) && !gateway.empty())
984     {
985         log<level::ERR>("Not a valid v6 Gateway",
986                         entry("GATEWAY=%s", gateway.c_str()));
987         elog<InvalidArgument>(Argument::ARGUMENT_NAME("GATEWAY"),
988                               Argument::ARGUMENT_VALUE(gateway.c_str()));
989     }
990     gw = EthernetInterfaceIntf::defaultGateway6(gateway);
991 
992     writeConfigurationFile();
993     manager.reloadConfigs();
994 
995     return gw;
996 }
997 
998 EthernetInterface::VlanProperties::VlanProperties(
999     sdbusplus::bus_t& bus, stdplus::const_zstring objPath,
1000     const system::InterfaceInfo& info, EthernetInterface& eth,
1001     bool emitSignal) :
1002     VlanIfaces(bus, objPath.c_str(),
1003                emitSignal ? VlanIfaces::action::defer_emit
1004                           : VlanIfaces::action::emit_no_signals),
1005     parentIdx(*info.parent_idx), eth(eth)
1006 {
1007     VlanIntf::id(*info.vlan_id);
1008     if (emitSignal)
1009     {
1010         this->emit_object_added();
1011     }
1012 }
1013 
1014 void EthernetInterface::VlanProperties::delete_()
1015 {
1016     auto intf = eth.interfaceName();
1017 
1018     // Remove all configs for the current interface
1019     const auto& confDir = eth.manager.getConfDir();
1020     std::error_code ec;
1021     std::filesystem::remove(config::pathForIntfConf(confDir, intf), ec);
1022     std::filesystem::remove(config::pathForIntfDev(confDir, intf), ec);
1023 
1024     // Write an updated parent interface since it has a VLAN entry
1025     for (const auto& [_, intf] : eth.manager.interfaces)
1026     {
1027         if (intf->ifIdx == parentIdx)
1028         {
1029             intf->writeConfigurationFile();
1030         }
1031     }
1032 
1033     // We need to forcibly delete the interface as systemd does not
1034     deleteInterface(intf);
1035 
1036     if (eth.ifIdx > 0)
1037     {
1038         eth.manager.interfacesByIdx.erase(eth.ifIdx);
1039     }
1040     eth.manager.interfaces.erase(intf);
1041 }
1042 
1043 } // namespace network
1044 } // namespace phosphor
1045