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