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