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 <arpa/inet.h>
11 #include <fcntl.h>
12 #include <linux/rtnetlink.h>
13 #include <net/if.h>
14 #include <net/if_arp.h>
15 #include <sys/stat.h>
16 
17 #include <phosphor-logging/elog-errors.hpp>
18 #include <phosphor-logging/lg2.hpp>
19 #include <stdplus/fd/create.hpp>
20 #include <stdplus/raw.hpp>
21 #include <stdplus/str/cat.hpp>
22 #include <stdplus/zstring.hpp>
23 #include <xyz/openbmc_project/Common/error.hpp>
24 
25 #include <algorithm>
26 #include <filesystem>
27 #include <format>
28 #include <string>
29 #include <unordered_map>
30 #include <variant>
31 
32 namespace phosphor
33 {
34 namespace network
35 {
36 
37 using namespace phosphor::logging;
38 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
39 using NotAllowed = sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
40 using NotAllowedArgument = xyz::openbmc_project::Common::NotAllowed;
41 using Argument = xyz::openbmc_project::Common::InvalidArgument;
42 using std::literals::string_view_literals::operator""sv;
43 constexpr auto RESOLVED_SERVICE = "org.freedesktop.resolve1";
44 constexpr auto RESOLVED_INTERFACE = "org.freedesktop.resolve1.Link";
45 constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties";
46 constexpr auto RESOLVED_SERVICE_PATH = "/org/freedesktop/resolve1/link/";
47 
48 constexpr auto TIMESYNCD_SERVICE = "org.freedesktop.timesync1";
49 constexpr auto TIMESYNCD_INTERFACE = "org.freedesktop.timesync1.Manager";
50 constexpr auto TIMESYNCD_SERVICE_PATH = "/org/freedesktop/timesync1";
51 
52 constexpr auto METHOD_GET = "Get";
53 
54 template <typename Func>
55 inline decltype(std::declval<Func>()())
ignoreError(std::string_view msg,stdplus::zstring_view intf,decltype(std::declval<Func> ()())fallback,Func && func)56     ignoreError(std::string_view msg, stdplus::zstring_view intf,
57                 decltype(std::declval<Func>()()) fallback, Func&& func) noexcept
58 {
59     try
60     {
61         return func();
62     }
63     catch (const std::exception& e)
64     {
65         lg2::error("{MSG} failed on {NET_INTF}: {ERROR}", "MSG", msg,
66                    "NET_INTF", intf, "ERROR", e);
67     }
68     return fallback;
69 }
70 
makeObjPath(std::string_view root,std::string_view intf)71 static std::string makeObjPath(std::string_view root, std::string_view intf)
72 {
73     auto ret = stdplus::strCat(root, "/"sv, intf);
74     std::replace(ret.begin() + ret.size() - intf.size(), ret.end(), '.', '_');
75     return ret;
76 }
77 
78 template <typename Addr>
validIntfIP(Addr a)79 static bool validIntfIP(Addr a) noexcept
80 {
81     return a.isUnicast() && !a.isLoopback();
82 }
83 
EthernetInterface(stdplus::PinnedRef<sdbusplus::bus_t> bus,stdplus::PinnedRef<Manager> manager,const AllIntfInfo & info,std::string_view objRoot,const config::Parser & config,bool enabled)84 EthernetInterface::EthernetInterface(
85     stdplus::PinnedRef<sdbusplus::bus_t> bus,
86     stdplus::PinnedRef<Manager> manager, const AllIntfInfo& info,
87     std::string_view objRoot, const config::Parser& config, bool enabled) :
88     EthernetInterface(bus, manager, info, makeObjPath(objRoot, *info.intf.name),
89                       config, enabled)
90 {}
91 
EthernetInterface(stdplus::PinnedRef<sdbusplus::bus_t> bus,stdplus::PinnedRef<Manager> manager,const AllIntfInfo & info,std::string && objPath,const config::Parser & config,bool enabled)92 EthernetInterface::EthernetInterface(
93     stdplus::PinnedRef<sdbusplus::bus_t> bus,
94     stdplus::PinnedRef<Manager> manager, const AllIntfInfo& info,
95     std::string&& objPath, const config::Parser& config, bool enabled) :
96     Ifaces(bus, objPath.c_str(), Ifaces::action::defer_emit), manager(manager),
97     bus(bus), objPath(std::move(objPath))
98 {
99     interfaceName(*info.intf.name, true);
100     auto dhcpVal = getDHCPValue(config);
101     EthernetInterfaceIntf::dhcp4(dhcpVal.v4, true);
102     EthernetInterfaceIntf::dhcp6(dhcpVal.v6, true);
103     EthernetInterfaceIntf::ipv6AcceptRA(getIPv6AcceptRA(config), true);
104     EthernetInterfaceIntf::nicEnabled(enabled, true);
105 
106     EthernetInterfaceIntf::ntpServers(
107         config.map.getValueStrings("Network", "NTP"), true);
108 
109     updateInfo(info.intf, true);
110 
111     if (info.defgw4)
112     {
113         EthernetInterface::defaultGateway(stdplus::toStr(*info.defgw4), true);
114     }
115     if (info.defgw6)
116     {
117         EthernetInterface::defaultGateway6(stdplus::toStr(*info.defgw6), true);
118     }
119     emit_object_added();
120 
121     if (info.intf.vlan_id)
122     {
123         if (!info.intf.parent_idx)
124         {
125             std::runtime_error("Missing parent link");
126         }
127         vlan.emplace(bus, this->objPath.c_str(), info.intf, *this);
128     }
129     dhcp4Conf.emplace(bus, this->objPath + "/dhcp4", *this, DHCPType::v4);
130     dhcp6Conf.emplace(bus, this->objPath + "/dhcp6", *this, DHCPType::v6);
131     for (const auto& [_, addr] : info.addrs)
132     {
133         addAddr(addr);
134     }
135     for (const auto& [_, neigh] : info.staticNeighs)
136     {
137         addStaticNeigh(neigh);
138     }
139     for (const auto& [_, staticGateway] : info.staticGateways)
140     {
141         addStaticGateway(staticGateway);
142     }
143 }
144 
updateInfo(const InterfaceInfo & info,bool skipSignal)145 void EthernetInterface::updateInfo(const InterfaceInfo& info, bool skipSignal)
146 {
147     ifIdx = info.idx;
148     EthernetInterfaceIntf::linkUp(info.flags & IFF_RUNNING, skipSignal);
149     if (info.mac)
150     {
151         MacAddressIntf::macAddress(stdplus::toStr(*info.mac), skipSignal);
152     }
153     if (info.mtu)
154     {
155         EthernetInterfaceIntf::mtu(*info.mtu, skipSignal);
156     }
157     if (ifIdx > 0)
158     {
159         auto ethInfo = ignoreError("GetEthInfo", *info.name, {}, [&] {
160             return system::getEthInfo(*info.name);
161         });
162         EthernetInterfaceIntf::autoNeg(ethInfo.autoneg, skipSignal);
163         EthernetInterfaceIntf::speed(ethInfo.speed, skipSignal);
164     }
165 }
166 
addAddr(const AddressInfo & info)167 void EthernetInterface::addAddr(const AddressInfo& info)
168 {
169     IP::AddressOrigin origin = IP::AddressOrigin::Static;
170     if (dhcpIsEnabled(info.ifaddr.getAddr()))
171     {
172         origin = IP::AddressOrigin::DHCP;
173     }
174 
175 #ifdef LINK_LOCAL_AUTOCONFIGURATION
176     if (info.scope == RT_SCOPE_LINK)
177     {
178         origin = IP::AddressOrigin::LinkLocal;
179     }
180 #endif
181 
182     if ((info.scope == RT_SCOPE_UNIVERSE) && (info.flags & IFA_F_PERMANENT))
183     {
184         origin = IP::AddressOrigin::Static;
185     }
186     if ((info.scope == RT_SCOPE_UNIVERSE) &&
187         ((info.flags & IFA_F_NOPREFIXROUTE) &&
188          (info.flags & IFA_F_MANAGETEMPADDR)))
189     {
190         origin = IP::AddressOrigin::SLAAC;
191     }
192     else if ((info.scope == RT_SCOPE_UNIVERSE) &&
193              ((info.flags & IFA_F_NOPREFIXROUTE)))
194     {
195         origin = IP::AddressOrigin::DHCP;
196     }
197 
198     auto it = addrs.find(info.ifaddr);
199     if (it == addrs.end())
200     {
201         addrs.emplace(info.ifaddr, std::make_unique<IPAddress>(
202                                        bus, std::string_view(objPath), *this,
203                                        info.ifaddr, origin));
204     }
205     else
206     {
207         it->second->IPIfaces::origin(origin);
208     }
209 }
210 
addStaticNeigh(const NeighborInfo & info)211 void EthernetInterface::addStaticNeigh(const NeighborInfo& info)
212 {
213     if (!info.mac || !info.addr)
214     {
215         lg2::error("Missing neighbor mac on {NET_INTF}", "NET_INTF",
216                    interfaceName());
217         return;
218     }
219 
220     if (auto it = staticNeighbors.find(*info.addr); it != staticNeighbors.end())
221     {
222         it->second->NeighborObj::macAddress(stdplus::toStr(*info.mac));
223     }
224     else
225     {
226         staticNeighbors.emplace(
227             *info.addr, std::make_unique<Neighbor>(
228                             bus, std::string_view(objPath), *this, *info.addr,
229                             *info.mac, Neighbor::State::Permanent));
230     }
231 }
232 
addStaticGateway(const StaticGatewayInfo & info)233 void EthernetInterface::addStaticGateway(const StaticGatewayInfo& info)
234 {
235     if (!info.gateway)
236     {
237         lg2::error("Missing static gateway on {NET_INTF}", "NET_INTF",
238                    interfaceName());
239         return;
240     }
241 
242     IP::Protocol protocolType;
243     if (*info.protocol == "IPv4")
244     {
245         protocolType = IP::Protocol::IPv4;
246     }
247     else if (*info.protocol == "IPv6")
248     {
249         protocolType = IP::Protocol::IPv6;
250     }
251 
252     if (auto it = staticGateways.find(*info.gateway);
253         it != staticGateways.end())
254     {
255         it->second->StaticGatewayObj::gateway(*info.gateway);
256     }
257     else
258     {
259         staticGateways.emplace(*info.gateway,
260                                std::make_unique<StaticGateway>(
261                                    bus, std::string_view(objPath), *this,
262                                    *info.gateway, protocolType));
263     }
264 }
265 
ip(IP::Protocol protType,std::string ipaddress,uint8_t prefixLength,std::string)266 ObjectPath EthernetInterface::ip(IP::Protocol protType, std::string ipaddress,
267                                  uint8_t prefixLength, std::string)
268 {
269     std::optional<stdplus::InAnyAddr> addr;
270     try
271     {
272         switch (protType)
273         {
274             case IP::Protocol::IPv4:
275                 addr.emplace(stdplus::fromStr<stdplus::In4Addr>(ipaddress));
276                 break;
277             case IP::Protocol::IPv6:
278                 addr.emplace(stdplus::fromStr<stdplus::In6Addr>(ipaddress));
279                 break;
280             default:
281                 throw std::logic_error("Exhausted protocols");
282         }
283         if (!std::visit([](auto ip) { return validIntfIP(ip); }, *addr))
284         {
285             throw std::invalid_argument("not unicast");
286         }
287     }
288     catch (const std::exception& e)
289     {
290         lg2::error("Invalid IP {NET_IP}: {ERROR}", "NET_IP", ipaddress, "ERROR",
291                    e);
292         elog<InvalidArgument>(Argument::ARGUMENT_NAME("ipaddress"),
293                               Argument::ARGUMENT_VALUE(ipaddress.c_str()));
294     }
295     std::optional<stdplus::SubnetAny> ifaddr;
296     try
297     {
298         if (prefixLength == 0)
299         {
300             throw std::invalid_argument("default route");
301         }
302         ifaddr.emplace(*addr, prefixLength);
303     }
304     catch (const std::exception& e)
305     {
306         lg2::error("Invalid prefix length {NET_PFX}: {ERROR}", "NET_PFX",
307                    prefixLength, "ERROR", e);
308         elog<InvalidArgument>(
309             Argument::ARGUMENT_NAME("prefixLength"),
310             Argument::ARGUMENT_VALUE(stdplus::toStr(prefixLength).c_str()));
311     }
312 
313     auto it = addrs.find(*ifaddr);
314     if (it == addrs.end())
315     {
316         it = std::get<0>(addrs.emplace(
317             *ifaddr,
318             std::make_unique<IPAddress>(bus, std::string_view(objPath), *this,
319                                         *ifaddr, IP::AddressOrigin::Static)));
320     }
321     else
322     {
323         if (it->second->origin() == IP::AddressOrigin::Static)
324         {
325             return it->second->getObjPath();
326         }
327         it->second->IPIfaces::origin(IP::AddressOrigin::Static);
328     }
329 
330     writeConfigurationFile();
331     manager.get().reloadConfigs();
332 
333     return it->second->getObjPath();
334 }
335 
neighbor(std::string ipAddress,std::string macAddress)336 ObjectPath EthernetInterface::neighbor(std::string ipAddress,
337                                        std::string macAddress)
338 {
339     std::optional<stdplus::InAnyAddr> addr;
340     try
341     {
342         addr.emplace(stdplus::fromStr<stdplus::InAnyAddr>(ipAddress));
343     }
344     catch (const std::exception& e)
345     {
346         lg2::error("Not a valid IP address {NET_IP}: {ERROR}", "NET_IP",
347                    ipAddress, "ERROR", e);
348         elog<InvalidArgument>(Argument::ARGUMENT_NAME("ipAddress"),
349                               Argument::ARGUMENT_VALUE(ipAddress.c_str()));
350     }
351 
352     std::optional<stdplus::EtherAddr> lladdr;
353     try
354     {
355         lladdr.emplace(stdplus::fromStr<stdplus::EtherAddr>(macAddress));
356     }
357     catch (const std::exception& e)
358     {
359         lg2::error("Not a valid MAC address {NET_MAC}: {ERROR}", "NET_MAC",
360                    macAddress, "ERROR", e);
361         elog<InvalidArgument>(Argument::ARGUMENT_NAME("macAddress"),
362                               Argument::ARGUMENT_VALUE(macAddress.c_str()));
363     }
364 
365     auto it = staticNeighbors.find(*addr);
366     if (it == staticNeighbors.end())
367     {
368         it = std::get<0>(staticNeighbors.emplace(
369             *addr, std::make_unique<Neighbor>(bus, std::string_view(objPath),
370                                               *this, *addr, *lladdr,
371                                               Neighbor::State::Permanent)));
372     }
373     else
374     {
375         auto str = stdplus::toStr(*lladdr);
376         if (it->second->macAddress() == str)
377         {
378             return it->second->getObjPath();
379         }
380         it->second->NeighborObj::macAddress(str);
381     }
382 
383     writeConfigurationFile();
384     manager.get().reloadConfigs();
385 
386     return it->second->getObjPath();
387 }
388 
staticGateway(std::string gateway,IP::Protocol protocolType)389 ObjectPath EthernetInterface::staticGateway(std::string gateway,
390                                             IP::Protocol protocolType)
391 {
392     std::optional<stdplus::InAnyAddr> addr;
393     std::string route;
394     try
395     {
396         addr.emplace(stdplus::fromStr<stdplus::InAnyAddr>(gateway));
397         route = gateway;
398     }
399     catch (const std::exception& e)
400     {
401         lg2::error("Not a valid IP address {GATEWAY}: {ERROR}", "GATEWAY",
402                    gateway, "ERROR", e);
403         elog<InvalidArgument>(Argument::ARGUMENT_NAME("gateway"),
404                               Argument::ARGUMENT_VALUE(gateway.c_str()));
405     }
406 
407     auto it = staticGateways.find(route);
408     if (it == staticGateways.end())
409     {
410         it = std::get<0>(staticGateways.emplace(
411             route,
412             std::make_unique<StaticGateway>(bus, std::string_view(objPath),
413                                             *this, gateway, protocolType)));
414     }
415     else
416     {
417         it->second->StaticGatewayObj::gateway(gateway);
418     }
419 
420     writeConfigurationFile();
421     manager.get().reloadConfigs();
422 
423     return it->second->getObjPath();
424 }
425 
ipv6AcceptRA(bool value)426 bool EthernetInterface::ipv6AcceptRA(bool value)
427 {
428     if (ipv6AcceptRA() != EthernetInterfaceIntf::ipv6AcceptRA(value))
429     {
430         writeConfigurationFile();
431         manager.get().reloadConfigs();
432     }
433     return value;
434 }
435 
dhcp4(bool value)436 bool EthernetInterface::dhcp4(bool value)
437 {
438     if (dhcp4() != EthernetInterfaceIntf::dhcp4(value))
439     {
440         writeConfigurationFile();
441         manager.get().reloadConfigs();
442     }
443     return value;
444 }
445 
dhcp6(bool value)446 bool EthernetInterface::dhcp6(bool value)
447 {
448     if (dhcp6() != EthernetInterfaceIntf::dhcp6(value))
449     {
450         writeConfigurationFile();
451         manager.get().reloadConfigs();
452     }
453     return value;
454 }
455 
dhcpEnabled(DHCPConf value)456 EthernetInterface::DHCPConf EthernetInterface::dhcpEnabled(DHCPConf value)
457 {
458     auto old4 = EthernetInterfaceIntf::dhcp4();
459     auto new4 = EthernetInterfaceIntf::dhcp4(
460         value == DHCPConf::v4 || value == DHCPConf::v4v6stateless ||
461         value == DHCPConf::both);
462     auto old6 = EthernetInterfaceIntf::dhcp6();
463     auto new6 = EthernetInterfaceIntf::dhcp6(
464         value == DHCPConf::v6 || value == DHCPConf::both);
465     auto oldra = EthernetInterfaceIntf::ipv6AcceptRA();
466     auto newra = EthernetInterfaceIntf::ipv6AcceptRA(
467         value == DHCPConf::v6stateless || value == DHCPConf::v4v6stateless ||
468         value == DHCPConf::v6 || value == DHCPConf::both);
469 
470     if (old4 != new4 || old6 != new6 || oldra != newra)
471     {
472         writeConfigurationFile();
473         manager.get().reloadConfigs();
474     }
475     return value;
476 }
477 
dhcpEnabled() const478 EthernetInterface::DHCPConf EthernetInterface::dhcpEnabled() const
479 {
480     if (dhcp6())
481     {
482         return dhcp4() ? DHCPConf::both : DHCPConf::v6;
483     }
484     else if (dhcp4())
485     {
486         return ipv6AcceptRA() ? DHCPConf::v4v6stateless : DHCPConf::v4;
487     }
488     return ipv6AcceptRA() ? DHCPConf::v6stateless : DHCPConf::none;
489 }
490 
mtu(size_t value)491 size_t EthernetInterface::mtu(size_t value)
492 {
493     const size_t old = EthernetInterfaceIntf::mtu();
494     if (value == old)
495     {
496         return value;
497     }
498     const auto ifname = interfaceName();
499     return EthernetInterfaceIntf::mtu(ignoreError("SetMTU", ifname, old, [&] {
500         system::setMTU(ifname, value);
501         return value;
502     }));
503 }
504 
nicEnabled(bool value)505 bool EthernetInterface::nicEnabled(bool value)
506 {
507     if (value == EthernetInterfaceIntf::nicEnabled())
508     {
509         return value;
510     }
511 
512     EthernetInterfaceIntf::nicEnabled(value);
513     writeConfigurationFile();
514     manager.get().reloadConfigs();
515 
516     return value;
517 }
518 
staticNameServers(ServerList value)519 ServerList EthernetInterface::staticNameServers(ServerList value)
520 {
521     std::vector<std::string> dnsUniqueValues;
522     for (auto& ip : value)
523     {
524         try
525         {
526             ip = stdplus::toStr(stdplus::fromStr<stdplus::InAnyAddr>(ip));
527         }
528         catch (const std::exception& e)
529         {
530             lg2::error("Not a valid IP address {NET_IP}: {ERROR}", "NET_IP", ip,
531                        "ERROR", e);
532             elog<InvalidArgument>(Argument::ARGUMENT_NAME("StaticNameserver"),
533                                   Argument::ARGUMENT_VALUE(ip.c_str()));
534         }
535         if (std::find(dnsUniqueValues.begin(), dnsUniqueValues.end(), ip) ==
536             dnsUniqueValues.end())
537         {
538             dnsUniqueValues.push_back(ip);
539         }
540     }
541 
542     value =
543         EthernetInterfaceIntf::staticNameServers(std::move(dnsUniqueValues));
544 
545     writeConfigurationFile();
546     manager.get().reloadConfigs();
547 
548     return value;
549 }
550 
loadNTPServers(const config::Parser & config)551 void EthernetInterface::loadNTPServers(const config::Parser& config)
552 {
553     EthernetInterfaceIntf::ntpServers(getNTPServerFromTimeSyncd());
554     EthernetInterfaceIntf::staticNTPServers(
555         config.map.getValueStrings("Network", "NTP"));
556 }
557 
loadNameServers(const config::Parser & config)558 void EthernetInterface::loadNameServers(const config::Parser& config)
559 {
560     EthernetInterfaceIntf::nameservers(getNameServerFromResolvd());
561     EthernetInterfaceIntf::staticNameServers(
562         config.map.getValueStrings("Network", "DNS"));
563 }
564 
getNTPServerFromTimeSyncd()565 ServerList EthernetInterface::getNTPServerFromTimeSyncd()
566 {
567     ServerList servers; // Variable to capture the NTP Server IPs
568     auto method =
569         bus.get().new_method_call(TIMESYNCD_SERVICE, TIMESYNCD_SERVICE_PATH,
570                                   PROPERTY_INTERFACE, METHOD_GET);
571 
572     method.append(TIMESYNCD_INTERFACE, "LinkNTPServers");
573 
574     try
575     {
576         auto reply = bus.get().call(method);
577         std::variant<ServerList> response;
578         reply.read(response);
579         servers = std::get<ServerList>(response);
580     }
581     catch (const sdbusplus::exception::SdBusError& e)
582     {
583         lg2::error("Failed to get NTP server information from "
584                    "systemd-timesyncd: {ERROR}",
585                    "ERROR", e);
586     }
587 
588     return servers;
589 }
590 
nameservers() const591 ServerList EthernetInterface::nameservers() const
592 {
593     return getNameServerFromResolvd();
594 }
595 
getNameServerFromResolvd() const596 ServerList EthernetInterface::getNameServerFromResolvd() const
597 {
598     ServerList servers;
599     auto OBJ_PATH = std::format("{}{}", RESOLVED_SERVICE_PATH, ifIdx);
600 
601     /*
602       The DNS property under org.freedesktop.resolve1.Link interface contains
603       an array containing all DNS servers currently used by resolved. It
604       contains similar information as the DNS server data written to
605       /run/systemd/resolve/resolv.conf.
606 
607       Each structure in the array consists of a numeric network interface index,
608       an address family, and a byte array containing the DNS server address
609       (either 4 bytes in length for IPv4 or 16 bytes in lengths for IPv6).
610       The array contains DNS servers configured system-wide, including those
611       possibly read from a foreign /etc/resolv.conf or the DNS= setting in
612       /etc/systemd/resolved.conf, as well as per-interface DNS server
613       information either retrieved from systemd-networkd or configured by
614       external software via SetLinkDNS().
615     */
616 
617     using type = std::vector<std::tuple<int32_t, std::vector<uint8_t>>>;
618     std::variant<type> name; // Variable to capture the DNS property
619     auto method = bus.get().new_method_call(RESOLVED_SERVICE, OBJ_PATH.c_str(),
620                                             PROPERTY_INTERFACE, METHOD_GET);
621 
622     method.append(RESOLVED_INTERFACE, "DNS");
623 
624     try
625     {
626         auto reply = bus.get().call(method);
627         reply.read(name);
628     }
629     catch (const sdbusplus::exception_t& e)
630     {
631         lg2::error(
632             "Failed to get DNS information from systemd-resolved: {ERROR}",
633             "ERROR", e);
634     }
635     auto tupleVector = std::get_if<type>(&name);
636     for (auto i = tupleVector->begin(); i != tupleVector->end(); ++i)
637     {
638         int addressFamily = std::get<0>(*i);
639         std::vector<uint8_t>& ipaddress = std::get<1>(*i);
640         servers.push_back(stdplus::toStr(
641             addrFromBuf(addressFamily, stdplus::raw::asView<char>(ipaddress))));
642     }
643     return servers;
644 }
645 
createVLAN(uint16_t id)646 ObjectPath EthernetInterface::createVLAN(uint16_t id)
647 {
648     auto idStr = stdplus::toStr(id);
649     auto intfName = stdplus::strCat(interfaceName(), "."sv, idStr);
650     if (manager.get().interfaces.find(intfName) !=
651         manager.get().interfaces.end())
652     {
653         lg2::error("VLAN {NET_VLAN} already exists", "NET_VLAN", id);
654         elog<InvalidArgument>(Argument::ARGUMENT_NAME("VLANId"),
655                               Argument::ARGUMENT_VALUE(idStr.c_str()));
656     }
657 
658     auto objRoot = std::string_view(objPath).substr(0, objPath.rfind('/'));
659     auto macStr = MacAddressIntf::macAddress();
660     std::optional<stdplus::EtherAddr> mac;
661     if (!macStr.empty())
662     {
663         mac.emplace(stdplus::fromStr<stdplus::EtherAddr>(macStr));
664     }
665     auto info = AllIntfInfo{InterfaceInfo{
666         .type = ARPHRD_ETHER,
667         .idx = 0, // TODO: Query the correct value after creation
668         .flags = 0,
669         .name = intfName,
670         .mac = std::move(mac),
671         .mtu = mtu(),
672         .parent_idx = ifIdx,
673         .vlan_id = id,
674     }};
675 
676     // Pass the parents nicEnabled property, so that the child
677     // VLAN interface can inherit.
678     auto vlanIntf = std::make_unique<EthernetInterface>(
679         bus, manager, info, objRoot, config::Parser(), nicEnabled());
680     ObjectPath ret = vlanIntf->objPath;
681 
682     manager.get().interfaces.emplace(intfName, std::move(vlanIntf));
683 
684     // write the device file for the vlan interface.
685     config::Parser config;
686     auto& netdev = config.map["NetDev"].emplace_back();
687     netdev["Name"].emplace_back(intfName);
688     netdev["Kind"].emplace_back("vlan");
689     config.map["VLAN"].emplace_back()["Id"].emplace_back(std::move(idStr));
690     config.writeFile(
691         config::pathForIntfDev(manager.get().getConfDir(), intfName));
692 
693     writeConfigurationFile();
694     manager.get().reloadConfigs();
695 
696     return ret;
697 }
698 
staticNTPServers(ServerList value)699 ServerList EthernetInterface::staticNTPServers(ServerList value)
700 {
701     value = EthernetInterfaceIntf::staticNTPServers(std::move(value));
702 
703     writeConfigurationFile();
704     manager.get().reloadConfigs();
705 
706     return value;
707 }
708 
ntpServers(ServerList)709 ServerList EthernetInterface::ntpServers(ServerList /*servers*/)
710 {
711     elog<NotAllowed>(NotAllowedArgument::REASON("ReadOnly Property"));
712 }
713 
tfStr(bool value)714 static constexpr std::string_view tfStr(bool value)
715 {
716     return value ? "true"sv : "false"sv;
717 }
718 
writeUpdatedTime(const Manager & manager,const std::filesystem::path & netFile)719 static void writeUpdatedTime(const Manager& manager,
720                              const std::filesystem::path& netFile)
721 {
722     // JFFS2 doesn't have the time granularity to deal with sub-second
723     // updates. Since we can have multiple file updates within a second
724     // around a reload, we need a location which gives that precision for
725     // future networkd detected reloads. TMPFS gives us this property.
726     if (manager.getConfDir() == "/etc/systemd/network"sv)
727     {
728         auto dir = stdplus::strCat(netFile.native(), ".d");
729         dir.replace(1, 3, "run"); // Replace /etc with /run
730         auto file = dir + "/updated.conf";
731         try
732         {
733             std::filesystem::create_directories(dir);
734             using namespace stdplus::fd;
735             futimens(
736                 open(file,
737                      OpenFlags(OpenAccess::WriteOnly).set(OpenFlag::Create),
738                      0644)
739                     .get(),
740                 nullptr);
741         }
742         catch (const std::exception& e)
743         {
744             lg2::error("Failed to write time updated file {FILE}: {ERROR}",
745                        "FILE", file, "ERROR", e.what());
746         }
747     }
748 }
749 
writeConfigurationFile()750 void EthernetInterface::writeConfigurationFile()
751 {
752     config::Parser config;
753     config.map["Match"].emplace_back()["Name"].emplace_back(interfaceName());
754     {
755         auto& link = config.map["Link"].emplace_back();
756 #ifdef PERSIST_MAC
757         auto mac = MacAddressIntf::macAddress();
758         if (!mac.empty())
759         {
760             link["MACAddress"].emplace_back(mac);
761         }
762 #endif
763         if (!EthernetInterfaceIntf::nicEnabled())
764         {
765             link["ActivationPolicy"].emplace_back("down");
766         }
767     }
768     {
769         auto& network = config.map["Network"].emplace_back();
770         auto& lla = network["LinkLocalAddressing"];
771 #ifdef LINK_LOCAL_AUTOCONFIGURATION
772         lla.emplace_back("yes");
773 #else
774         lla.emplace_back("no");
775 #endif
776         network["IPv6AcceptRA"].emplace_back(ipv6AcceptRA() ? "true" : "false");
777         network["DHCP"].emplace_back(dhcp4() ? (dhcp6() ? "true" : "ipv4")
778                                              : (dhcp6() ? "ipv6" : "false"));
779         {
780             auto& vlans = network["VLAN"];
781             for (const auto& [_, intf] : manager.get().interfaces)
782             {
783                 if (intf->vlan && intf->vlan->parentIdx == ifIdx)
784                 {
785                     vlans.emplace_back(intf->interfaceName());
786                 }
787             }
788         }
789         {
790             auto& ntps = network["NTP"];
791             for (const auto& ntp : EthernetInterfaceIntf::staticNTPServers())
792             {
793                 ntps.emplace_back(ntp);
794             }
795         }
796         {
797             auto& dnss = network["DNS"];
798             for (const auto& dns : EthernetInterfaceIntf::staticNameServers())
799             {
800                 dnss.emplace_back(dns);
801             }
802         }
803         {
804             auto& address = network["Address"];
805             for (const auto& addr : addrs)
806             {
807                 if (addr.second->origin() == IP::AddressOrigin::Static)
808                 {
809                     address.emplace_back(stdplus::toStr(addr.first));
810                 }
811             }
812         }
813         {
814             if (!dhcp4())
815             {
816                 auto gateway4 = EthernetInterfaceIntf::defaultGateway();
817                 if (!gateway4.empty())
818                 {
819                     auto& gateway4route = config.map["Route"].emplace_back();
820                     gateway4route["Gateway"].emplace_back(gateway4);
821                     gateway4route["GatewayOnLink"].emplace_back("true");
822                 }
823             }
824 
825             if (!ipv6AcceptRA())
826             {
827                 auto gateway6 = EthernetInterfaceIntf::defaultGateway6();
828                 if (!gateway6.empty())
829                 {
830                     auto& gateway6route = config.map["Route"].emplace_back();
831                     gateway6route["Gateway"].emplace_back(gateway6);
832                     gateway6route["GatewayOnLink"].emplace_back("true");
833                 }
834             }
835         }
836     }
837     config.map["IPv6AcceptRA"].emplace_back()["DHCPv6Client"].emplace_back(
838         dhcp6() ? "true" : "false");
839     {
840         auto& neighbors = config.map["Neighbor"];
841         for (const auto& sneighbor : staticNeighbors)
842         {
843             auto& neighbor = neighbors.emplace_back();
844             neighbor["Address"].emplace_back(sneighbor.second->ipAddress());
845             neighbor["MACAddress"].emplace_back(sneighbor.second->macAddress());
846         }
847     }
848     {
849         auto& dhcp4 = config.map["DHCPv4"].emplace_back();
850         dhcp4["ClientIdentifier"].emplace_back("mac");
851         dhcp4["UseDNS"].emplace_back(tfStr(dhcp4Conf->dnsEnabled()));
852         dhcp4["UseDomains"].emplace_back(tfStr(dhcp4Conf->domainEnabled()));
853         dhcp4["UseNTP"].emplace_back(tfStr(dhcp4Conf->ntpEnabled()));
854         dhcp4["UseHostname"].emplace_back(tfStr(dhcp4Conf->hostNameEnabled()));
855         dhcp4["SendHostname"].emplace_back(
856             tfStr(dhcp4Conf->sendHostNameEnabled()));
857     }
858     {
859         auto& dhcp6 = config.map["DHCPv6"].emplace_back();
860         dhcp6["UseDNS"].emplace_back(tfStr(dhcp6Conf->dnsEnabled()));
861         dhcp6["UseDomains"].emplace_back(tfStr(dhcp6Conf->domainEnabled()));
862         dhcp6["UseNTP"].emplace_back(tfStr(dhcp6Conf->ntpEnabled()));
863         dhcp6["UseHostname"].emplace_back(tfStr(dhcp6Conf->hostNameEnabled()));
864         dhcp6["SendHostname"].emplace_back(
865             tfStr(dhcp6Conf->sendHostNameEnabled()));
866     }
867 
868     {
869         auto& sroutes = config.map["Route"];
870         for (const auto& temp : staticGateways)
871         {
872             auto& staticGateway = sroutes.emplace_back();
873             staticGateway["Gateway"].emplace_back(temp.second->gateway());
874             staticGateway["GatewayOnLink"].emplace_back("true");
875         }
876     }
877 
878     auto path =
879         config::pathForIntfConf(manager.get().getConfDir(), interfaceName());
880     config.writeFile(path);
881     lg2::info("Wrote networkd file: {CFG_FILE}", "CFG_FILE", path);
882     writeUpdatedTime(manager, path);
883 }
884 
macAddress(std::string value)885 std::string EthernetInterface::macAddress([[maybe_unused]] std::string value)
886 {
887     if (vlan)
888     {
889         lg2::error("Tried to set MAC address on VLAN");
890         elog<InternalFailure>();
891     }
892 #ifdef PERSIST_MAC
893     stdplus::EtherAddr newMAC;
894     try
895     {
896         newMAC = stdplus::fromStr<stdplus::EtherAddr>(value);
897     }
898     catch (const std::invalid_argument&)
899     {
900         lg2::error("MAC Address {NET_MAC} is not valid", "NET_MAC", value);
901         elog<InvalidArgument>(Argument::ARGUMENT_NAME("MACAddress"),
902                               Argument::ARGUMENT_VALUE(value.c_str()));
903     }
904     if (!newMAC.isUnicast())
905     {
906         lg2::error("MAC Address {NET_MAC} is not valid", "NET_MAC", value);
907         elog<InvalidArgument>(Argument::ARGUMENT_NAME("MACAddress"),
908                               Argument::ARGUMENT_VALUE(value.c_str()));
909     }
910 
911     auto interface = interfaceName();
912     auto validMAC = stdplus::toStr(newMAC);
913 
914     // We don't need to update the system if the address is unchanged
915     auto oldMAC =
916         stdplus::fromStr<stdplus::EtherAddr>(MacAddressIntf::macAddress());
917     if (newMAC != oldMAC)
918     {
919         // Update everything that depends on the MAC value
920         for (const auto& [_, intf] : manager.get().interfaces)
921         {
922             if (intf->vlan && intf->vlan->parentIdx == ifIdx)
923             {
924                 intf->MacAddressIntf::macAddress(validMAC);
925             }
926         }
927         MacAddressIntf::macAddress(validMAC);
928 
929         writeConfigurationFile();
930         manager.get().addReloadPreHook([interface, manager = manager]() {
931             // The MAC and LLADDRs will only update if the NIC is already down
932             system::setNICUp(interface, false);
933             writeUpdatedTime(
934                 manager,
935                 config::pathForIntfConf(manager.get().getConfDir(), interface));
936         });
937         manager.get().reloadConfigs();
938     }
939 
940 #ifdef HAVE_UBOOT_ENV
941     // Ensure that the valid address is stored in the u-boot-env
942     auto envVar = interfaceToUbootEthAddr(interface);
943     if (envVar)
944     {
945         // Trimming MAC addresses that are out of range. eg: AA:FF:FF:FF:FF:100;
946         // and those having more than 6 bytes. eg: AA:AA:AA:AA:AA:AA:BB
947         execute("/sbin/fw_setenv", "fw_setenv", envVar->c_str(),
948                 validMAC.c_str());
949     }
950 #endif // HAVE_UBOOT_ENV
951 
952     return value;
953 #else
954     elog<NotAllowed>(
955         NotAllowedArgument::REASON("Writing MAC address is not allowed"));
956 #endif // PERSIST_MAC
957 }
958 
deleteAll()959 void EthernetInterface::deleteAll()
960 {
961     // clear all the ip on the interface
962     addrs.clear();
963 
964     writeConfigurationFile();
965     manager.get().reloadConfigs();
966 }
967 
968 template <typename Addr>
normalizeGateway(std::string & gw)969 static void normalizeGateway(std::string& gw)
970 {
971     if (gw.empty())
972     {
973         return;
974     }
975     try
976     {
977         auto ip = stdplus::fromStr<Addr>(gw);
978         if (ip == Addr{})
979         {
980             gw.clear();
981             return;
982         }
983         if (!validIntfIP(ip))
984         {
985             throw std::invalid_argument("Invalid unicast");
986         }
987         gw = stdplus::toStr(ip);
988     }
989     catch (const std::exception& e)
990     {
991         lg2::error("Invalid GW `{NET_GW}`: {ERROR}", "NET_GW", gw, "ERROR", e);
992         elog<InvalidArgument>(Argument::ARGUMENT_NAME("GATEWAY"),
993                               Argument::ARGUMENT_VALUE(gw.c_str()));
994     }
995 }
996 
defaultGateway(std::string gateway)997 std::string EthernetInterface::defaultGateway(std::string gateway)
998 {
999     normalizeGateway<stdplus::In4Addr>(gateway);
1000     if (gateway != defaultGateway())
1001     {
1002         gateway = EthernetInterfaceIntf::defaultGateway(std::move(gateway));
1003         writeConfigurationFile();
1004         manager.get().reloadConfigs();
1005     }
1006     return gateway;
1007 }
1008 
defaultGateway6(std::string gateway)1009 std::string EthernetInterface::defaultGateway6(std::string gateway)
1010 {
1011     normalizeGateway<stdplus::In6Addr>(gateway);
1012     if (gateway != defaultGateway6())
1013     {
1014         gateway = EthernetInterfaceIntf::defaultGateway6(std::move(gateway));
1015         writeConfigurationFile();
1016         manager.get().reloadConfigs();
1017     }
1018     return gateway;
1019 }
1020 
VlanProperties(sdbusplus::bus_t & bus,stdplus::const_zstring objPath,const InterfaceInfo & info,stdplus::PinnedRef<EthernetInterface> eth)1021 EthernetInterface::VlanProperties::VlanProperties(
1022     sdbusplus::bus_t& bus, stdplus::const_zstring objPath,
1023     const InterfaceInfo& info, stdplus::PinnedRef<EthernetInterface> eth) :
1024     VlanIfaces(bus, objPath.c_str(), VlanIfaces::action::defer_emit),
1025     parentIdx(*info.parent_idx), eth(eth)
1026 {
1027     VlanIntf::id(*info.vlan_id, true);
1028     emit_object_added();
1029 }
1030 
delete_()1031 void EthernetInterface::VlanProperties::delete_()
1032 {
1033     auto intf = eth.get().interfaceName();
1034 
1035     // Remove all configs for the current interface
1036     const auto& confDir = eth.get().manager.get().getConfDir();
1037     std::error_code ec;
1038     std::filesystem::remove(config::pathForIntfConf(confDir, intf), ec);
1039     std::filesystem::remove(config::pathForIntfDev(confDir, intf), ec);
1040 
1041     if (eth.get().ifIdx > 0)
1042     {
1043         eth.get().manager.get().interfacesByIdx.erase(eth.get().ifIdx);
1044     }
1045     auto it = eth.get().manager.get().interfaces.find(intf);
1046     auto obj = std::move(it->second);
1047     eth.get().manager.get().interfaces.erase(it);
1048 
1049     // Write an updated parent interface since it has a VLAN entry
1050     for (const auto& [_, intf] : eth.get().manager.get().interfaces)
1051     {
1052         if (intf->ifIdx == parentIdx)
1053         {
1054             intf->writeConfigurationFile();
1055         }
1056     }
1057 
1058     if (eth.get().ifIdx > 0)
1059     {
1060         // We need to forcibly delete the interface as systemd does not
1061         eth.get().manager.get().addReloadPostHook([idx = eth.get().ifIdx]() {
1062             system::deleteIntf(idx);
1063         });
1064 
1065         // Ignore the interface so the reload doesn't re-query it
1066         eth.get().manager.get().ignoredIntf.emplace(eth.get().ifIdx);
1067     }
1068 
1069     eth.get().manager.get().reloadConfigs();
1070 }
1071 
reloadConfigs()1072 void EthernetInterface::reloadConfigs()
1073 {
1074     manager.get().reloadConfigs();
1075 }
1076 
1077 } // namespace network
1078 } // namespace phosphor
1079