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