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