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