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 if (!value) 452 { 453 // We only need to bring down the interface, networkd will always bring 454 // up managed interfaces 455 manager.get().addReloadPreHook([ifname = interfaceName()]() { 456 system::setNICUp(ifname, false); 457 }); 458 } 459 manager.get().reloadConfigs(); 460 461 return value; 462 } 463 464 ServerList EthernetInterface::staticNameServers(ServerList value) 465 { 466 std::vector<std::string> dnsUniqueValues; 467 for (auto& ip : value) 468 { 469 try 470 { 471 ip = stdplus::toStr(stdplus::fromStr<stdplus::InAnyAddr>(ip)); 472 } 473 catch (const std::exception& e) 474 { 475 lg2::error("Not a valid IP address {NET_IP}: {ERROR}", "NET_IP", ip, 476 "ERROR", e); 477 elog<InvalidArgument>(Argument::ARGUMENT_NAME("StaticNameserver"), 478 Argument::ARGUMENT_VALUE(ip.c_str())); 479 } 480 if (std::find(dnsUniqueValues.begin(), dnsUniqueValues.end(), ip) == 481 dnsUniqueValues.end()) 482 { 483 dnsUniqueValues.push_back(ip); 484 } 485 } 486 487 value = 488 EthernetInterfaceIntf::staticNameServers(std::move(dnsUniqueValues)); 489 490 writeConfigurationFile(); 491 manager.get().reloadConfigs(); 492 493 return value; 494 } 495 496 void EthernetInterface::loadNTPServers(const config::Parser& config) 497 { 498 EthernetInterfaceIntf::ntpServers(getNTPServerFromTimeSyncd()); 499 EthernetInterfaceIntf::staticNTPServers( 500 config.map.getValueStrings("Network", "NTP")); 501 } 502 503 void EthernetInterface::loadNameServers(const config::Parser& config) 504 { 505 EthernetInterfaceIntf::nameservers(getNameServerFromResolvd()); 506 EthernetInterfaceIntf::staticNameServers( 507 config.map.getValueStrings("Network", "DNS")); 508 } 509 510 ServerList EthernetInterface::getNTPServerFromTimeSyncd() 511 { 512 ServerList servers; // Variable to capture the NTP Server IPs 513 auto method = 514 bus.get().new_method_call(TIMESYNCD_SERVICE, TIMESYNCD_SERVICE_PATH, 515 PROPERTY_INTERFACE, METHOD_GET); 516 517 method.append(TIMESYNCD_INTERFACE, "LinkNTPServers"); 518 519 try 520 { 521 auto reply = bus.get().call(method); 522 std::variant<ServerList> response; 523 reply.read(response); 524 servers = std::get<ServerList>(response); 525 } 526 catch (const sdbusplus::exception::SdBusError& e) 527 { 528 lg2::error("Failed to get NTP server information from " 529 "systemd-timesyncd: {ERROR}", 530 "ERROR", e); 531 } 532 533 return servers; 534 } 535 536 ServerList EthernetInterface::nameservers() const 537 { 538 return getNameServerFromResolvd(); 539 } 540 541 ServerList EthernetInterface::getNameServerFromResolvd() const 542 { 543 ServerList servers; 544 auto OBJ_PATH = std::format("{}{}", RESOLVED_SERVICE_PATH, ifIdx); 545 546 /* 547 The DNS property under org.freedesktop.resolve1.Link interface contains 548 an array containing all DNS servers currently used by resolved. It 549 contains similar information as the DNS server data written to 550 /run/systemd/resolve/resolv.conf. 551 552 Each structure in the array consists of a numeric network interface index, 553 an address family, and a byte array containing the DNS server address 554 (either 4 bytes in length for IPv4 or 16 bytes in lengths for IPv6). 555 The array contains DNS servers configured system-wide, including those 556 possibly read from a foreign /etc/resolv.conf or the DNS= setting in 557 /etc/systemd/resolved.conf, as well as per-interface DNS server 558 information either retrieved from systemd-networkd or configured by 559 external software via SetLinkDNS(). 560 */ 561 562 using type = std::vector<std::tuple<int32_t, std::vector<uint8_t>>>; 563 std::variant<type> name; // Variable to capture the DNS property 564 auto method = bus.get().new_method_call(RESOLVED_SERVICE, OBJ_PATH.c_str(), 565 PROPERTY_INTERFACE, METHOD_GET); 566 567 method.append(RESOLVED_INTERFACE, "DNS"); 568 569 try 570 { 571 auto reply = bus.get().call(method); 572 reply.read(name); 573 } 574 catch (const sdbusplus::exception_t& e) 575 { 576 lg2::error( 577 "Failed to get DNS information from systemd-resolved: {ERROR}", 578 "ERROR", e); 579 } 580 auto tupleVector = std::get_if<type>(&name); 581 for (auto i = tupleVector->begin(); i != tupleVector->end(); ++i) 582 { 583 int addressFamily = std::get<0>(*i); 584 std::vector<uint8_t>& ipaddress = std::get<1>(*i); 585 servers.push_back(stdplus::toStr( 586 addrFromBuf(addressFamily, stdplus::raw::asView<char>(ipaddress)))); 587 } 588 return servers; 589 } 590 591 ObjectPath EthernetInterface::createVLAN(uint16_t id) 592 { 593 auto idStr = stdplus::toStr(id); 594 auto intfName = stdplus::strCat(interfaceName(), "."sv, idStr); 595 if (manager.get().interfaces.find(intfName) != 596 manager.get().interfaces.end()) 597 { 598 lg2::error("VLAN {NET_VLAN} already exists", "NET_VLAN", id); 599 elog<InvalidArgument>(Argument::ARGUMENT_NAME("VLANId"), 600 Argument::ARGUMENT_VALUE(idStr.c_str())); 601 } 602 603 auto objRoot = std::string_view(objPath).substr(0, objPath.rfind('/')); 604 auto macStr = MacAddressIntf::macAddress(); 605 std::optional<stdplus::EtherAddr> mac; 606 if (!macStr.empty()) 607 { 608 mac.emplace(stdplus::fromStr<stdplus::EtherAddr>(macStr)); 609 } 610 auto info = AllIntfInfo{InterfaceInfo{ 611 .type = ARPHRD_ETHER, 612 .idx = 0, // TODO: Query the correct value after creation 613 .flags = 0, 614 .name = intfName, 615 .mac = std::move(mac), 616 .mtu = mtu(), 617 .parent_idx = ifIdx, 618 .vlan_id = id, 619 }}; 620 621 // Pass the parents nicEnabled property, so that the child 622 // VLAN interface can inherit. 623 auto vlanIntf = std::make_unique<EthernetInterface>( 624 bus, manager, info, objRoot, config::Parser(), nicEnabled()); 625 ObjectPath ret = vlanIntf->objPath; 626 627 manager.get().interfaces.emplace(intfName, std::move(vlanIntf)); 628 629 // write the device file for the vlan interface. 630 config::Parser config; 631 auto& netdev = config.map["NetDev"].emplace_back(); 632 netdev["Name"].emplace_back(intfName); 633 netdev["Kind"].emplace_back("vlan"); 634 config.map["VLAN"].emplace_back()["Id"].emplace_back(std::move(idStr)); 635 config.writeFile( 636 config::pathForIntfDev(manager.get().getConfDir(), intfName)); 637 638 writeConfigurationFile(); 639 manager.get().reloadConfigs(); 640 641 return ret; 642 } 643 644 ServerList EthernetInterface::staticNTPServers(ServerList value) 645 { 646 value = EthernetInterfaceIntf::staticNTPServers(std::move(value)); 647 648 writeConfigurationFile(); 649 manager.get().reloadConfigs(); 650 651 return value; 652 } 653 654 ServerList EthernetInterface::ntpServers(ServerList /*servers*/) 655 { 656 elog<NotAllowed>(NotAllowedArgument::REASON("ReadOnly Property")); 657 } 658 659 static constexpr std::string_view tfStr(bool value) 660 { 661 return value ? "true"sv : "false"sv; 662 } 663 664 static void writeUpdatedTime(const Manager& manager, 665 const std::filesystem::path& netFile) 666 { 667 // JFFS2 doesn't have the time granularity to deal with sub-second 668 // updates. Since we can have multiple file updates within a second 669 // around a reload, we need a location which gives that precision for 670 // future networkd detected reloads. TMPFS gives us this property. 671 if (manager.getConfDir() == "/etc/systemd/network"sv) 672 { 673 auto dir = stdplus::strCat(netFile.native(), ".d"); 674 dir.replace(1, 3, "run"); // Replace /etc with /run 675 auto file = dir + "/updated.conf"; 676 try 677 { 678 std::filesystem::create_directories(dir); 679 using namespace stdplus::fd; 680 futimens( 681 open(file, 682 OpenFlags(OpenAccess::WriteOnly).set(OpenFlag::Create), 683 0644) 684 .get(), 685 nullptr); 686 } 687 catch (const std::exception& e) 688 { 689 lg2::error("Failed to write time updated file {FILE}: {ERROR}", 690 "FILE", file, "ERROR", e.what()); 691 } 692 } 693 } 694 695 void EthernetInterface::writeConfigurationFile() 696 { 697 config::Parser config; 698 config.map["Match"].emplace_back()["Name"].emplace_back(interfaceName()); 699 { 700 auto& link = config.map["Link"].emplace_back(); 701 #ifdef PERSIST_MAC 702 auto mac = MacAddressIntf::macAddress(); 703 if (!mac.empty()) 704 { 705 link["MACAddress"].emplace_back(mac); 706 } 707 #endif 708 if (!EthernetInterfaceIntf::nicEnabled()) 709 { 710 link["Unmanaged"].emplace_back("yes"); 711 } 712 } 713 { 714 auto& network = config.map["Network"].emplace_back(); 715 auto& lla = network["LinkLocalAddressing"]; 716 #ifdef LINK_LOCAL_AUTOCONFIGURATION 717 lla.emplace_back("yes"); 718 #else 719 lla.emplace_back("no"); 720 #endif 721 network["IPv6AcceptRA"].emplace_back(ipv6AcceptRA() ? "true" : "false"); 722 network["DHCP"].emplace_back(dhcp4() ? (dhcp6() ? "true" : "ipv4") 723 : (dhcp6() ? "ipv6" : "false")); 724 { 725 auto& vlans = network["VLAN"]; 726 for (const auto& [_, intf] : manager.get().interfaces) 727 { 728 if (intf->vlan && intf->vlan->parentIdx == ifIdx) 729 { 730 vlans.emplace_back(intf->interfaceName()); 731 } 732 } 733 } 734 { 735 auto& ntps = network["NTP"]; 736 for (const auto& ntp : EthernetInterfaceIntf::staticNTPServers()) 737 { 738 ntps.emplace_back(ntp); 739 } 740 } 741 { 742 auto& dnss = network["DNS"]; 743 for (const auto& dns : EthernetInterfaceIntf::staticNameServers()) 744 { 745 dnss.emplace_back(dns); 746 } 747 } 748 { 749 auto& address = network["Address"]; 750 for (const auto& addr : addrs) 751 { 752 if (originIsManuallyAssigned(addr.second->origin())) 753 { 754 address.emplace_back(stdplus::toStr(addr.first)); 755 } 756 } 757 } 758 { 759 if (!dhcp4()) 760 { 761 auto gateway4 = EthernetInterfaceIntf::defaultGateway(); 762 if (!gateway4.empty()) 763 { 764 auto& gateway4route = config.map["Route"].emplace_back(); 765 gateway4route["Gateway"].emplace_back(gateway4); 766 gateway4route["GatewayOnLink"].emplace_back("true"); 767 } 768 } 769 770 if (!ipv6AcceptRA()) 771 { 772 auto gateway6 = EthernetInterfaceIntf::defaultGateway6(); 773 if (!gateway6.empty()) 774 { 775 auto& gateway6route = config.map["Route"].emplace_back(); 776 gateway6route["Gateway"].emplace_back(gateway6); 777 gateway6route["GatewayOnLink"].emplace_back("true"); 778 } 779 } 780 } 781 } 782 config.map["IPv6AcceptRA"].emplace_back()["DHCPv6Client"].emplace_back( 783 dhcp6() ? "true" : "false"); 784 { 785 auto& neighbors = config.map["Neighbor"]; 786 for (const auto& sneighbor : staticNeighbors) 787 { 788 auto& neighbor = neighbors.emplace_back(); 789 neighbor["Address"].emplace_back(sneighbor.second->ipAddress()); 790 neighbor["MACAddress"].emplace_back(sneighbor.second->macAddress()); 791 } 792 } 793 { 794 auto& dhcp4 = config.map["DHCPv4"].emplace_back(); 795 dhcp4["ClientIdentifier"].emplace_back("mac"); 796 dhcp4["UseDNS"].emplace_back(tfStr(dhcp4Conf->dnsEnabled())); 797 dhcp4["UseDomains"].emplace_back(tfStr(dhcp4Conf->domainEnabled())); 798 dhcp4["UseNTP"].emplace_back(tfStr(dhcp4Conf->ntpEnabled())); 799 dhcp4["UseHostname"].emplace_back(tfStr(dhcp4Conf->hostNameEnabled())); 800 dhcp4["SendHostname"].emplace_back( 801 tfStr(dhcp4Conf->sendHostNameEnabled())); 802 } 803 { 804 auto& dhcp6 = config.map["DHCPv6"].emplace_back(); 805 dhcp6["UseDNS"].emplace_back(tfStr(dhcp6Conf->dnsEnabled())); 806 dhcp6["UseDomains"].emplace_back(tfStr(dhcp6Conf->domainEnabled())); 807 dhcp6["UseNTP"].emplace_back(tfStr(dhcp6Conf->ntpEnabled())); 808 dhcp6["UseHostname"].emplace_back(tfStr(dhcp6Conf->hostNameEnabled())); 809 dhcp6["SendHostname"].emplace_back( 810 tfStr(dhcp6Conf->sendHostNameEnabled())); 811 } 812 auto path = 813 config::pathForIntfConf(manager.get().getConfDir(), interfaceName()); 814 config.writeFile(path); 815 lg2::info("Wrote networkd file: {CFG_FILE}", "CFG_FILE", path); 816 writeUpdatedTime(manager, path); 817 } 818 819 std::string EthernetInterface::macAddress([[maybe_unused]] std::string value) 820 { 821 if (vlan) 822 { 823 lg2::error("Tried to set MAC address on VLAN"); 824 elog<InternalFailure>(); 825 } 826 #ifdef PERSIST_MAC 827 stdplus::EtherAddr newMAC; 828 try 829 { 830 newMAC = stdplus::fromStr<stdplus::EtherAddr>(value); 831 } 832 catch (const std::invalid_argument&) 833 { 834 lg2::error("MAC Address {NET_MAC} is not valid", "NET_MAC", value); 835 elog<InvalidArgument>(Argument::ARGUMENT_NAME("MACAddress"), 836 Argument::ARGUMENT_VALUE(value.c_str())); 837 } 838 if (!newMAC.isUnicast()) 839 { 840 lg2::error("MAC Address {NET_MAC} is not valid", "NET_MAC", value); 841 elog<InvalidArgument>(Argument::ARGUMENT_NAME("MACAddress"), 842 Argument::ARGUMENT_VALUE(value.c_str())); 843 } 844 845 auto interface = interfaceName(); 846 auto validMAC = stdplus::toStr(newMAC); 847 848 // We don't need to update the system if the address is unchanged 849 auto oldMAC = 850 stdplus::fromStr<stdplus::EtherAddr>(MacAddressIntf::macAddress()); 851 if (newMAC != oldMAC) 852 { 853 // Update everything that depends on the MAC value 854 for (const auto& [_, intf] : manager.get().interfaces) 855 { 856 if (intf->vlan && intf->vlan->parentIdx == ifIdx) 857 { 858 intf->MacAddressIntf::macAddress(validMAC); 859 } 860 } 861 MacAddressIntf::macAddress(validMAC); 862 863 writeConfigurationFile(); 864 manager.get().addReloadPreHook([interface, manager = manager]() { 865 // The MAC and LLADDRs will only update if the NIC is already down 866 system::setNICUp(interface, false); 867 writeUpdatedTime( 868 manager, 869 config::pathForIntfConf(manager.get().getConfDir(), interface)); 870 }); 871 manager.get().reloadConfigs(); 872 } 873 874 #ifdef HAVE_UBOOT_ENV 875 // Ensure that the valid address is stored in the u-boot-env 876 auto envVar = interfaceToUbootEthAddr(interface); 877 if (envVar) 878 { 879 // Trimming MAC addresses that are out of range. eg: AA:FF:FF:FF:FF:100; 880 // and those having more than 6 bytes. eg: AA:AA:AA:AA:AA:AA:BB 881 execute("/sbin/fw_setenv", "fw_setenv", envVar->c_str(), 882 validMAC.c_str()); 883 } 884 #endif // HAVE_UBOOT_ENV 885 886 return value; 887 #else 888 elog<NotAllowed>( 889 NotAllowedArgument::REASON("Writing MAC address is not allowed")); 890 #endif // PERSIST_MAC 891 } 892 893 void EthernetInterface::deleteAll() 894 { 895 // clear all the ip on the interface 896 addrs.clear(); 897 898 writeConfigurationFile(); 899 manager.get().reloadConfigs(); 900 } 901 902 template <typename Addr> 903 static void normalizeGateway(std::string& gw) 904 { 905 if (gw.empty()) 906 { 907 return; 908 } 909 try 910 { 911 auto ip = stdplus::fromStr<Addr>(gw); 912 if (ip == Addr{}) 913 { 914 gw.clear(); 915 return; 916 } 917 if (!validIntfIP(ip)) 918 { 919 throw std::invalid_argument("Invalid unicast"); 920 } 921 gw = stdplus::toStr(ip); 922 } 923 catch (const std::exception& e) 924 { 925 lg2::error("Invalid GW `{NET_GW}`: {ERROR}", "NET_GW", gw, "ERROR", e); 926 elog<InvalidArgument>(Argument::ARGUMENT_NAME("GATEWAY"), 927 Argument::ARGUMENT_VALUE(gw.c_str())); 928 } 929 } 930 931 std::string EthernetInterface::defaultGateway(std::string gateway) 932 { 933 normalizeGateway<stdplus::In4Addr>(gateway); 934 if (gateway != defaultGateway()) 935 { 936 gateway = EthernetInterfaceIntf::defaultGateway(std::move(gateway)); 937 writeConfigurationFile(); 938 manager.get().reloadConfigs(); 939 } 940 return gateway; 941 } 942 943 std::string EthernetInterface::defaultGateway6(std::string gateway) 944 { 945 normalizeGateway<stdplus::In6Addr>(gateway); 946 if (gateway != defaultGateway6()) 947 { 948 gateway = EthernetInterfaceIntf::defaultGateway6(std::move(gateway)); 949 writeConfigurationFile(); 950 manager.get().reloadConfigs(); 951 } 952 return gateway; 953 } 954 955 EthernetInterface::VlanProperties::VlanProperties( 956 sdbusplus::bus_t& bus, stdplus::const_zstring objPath, 957 const InterfaceInfo& info, stdplus::PinnedRef<EthernetInterface> eth) : 958 VlanIfaces(bus, objPath.c_str(), VlanIfaces::action::defer_emit), 959 parentIdx(*info.parent_idx), eth(eth) 960 { 961 VlanIntf::id(*info.vlan_id, true); 962 emit_object_added(); 963 } 964 965 void EthernetInterface::VlanProperties::delete_() 966 { 967 auto intf = eth.get().interfaceName(); 968 969 // Remove all configs for the current interface 970 const auto& confDir = eth.get().manager.get().getConfDir(); 971 std::error_code ec; 972 std::filesystem::remove(config::pathForIntfConf(confDir, intf), ec); 973 std::filesystem::remove(config::pathForIntfDev(confDir, intf), ec); 974 975 if (eth.get().ifIdx > 0) 976 { 977 eth.get().manager.get().interfacesByIdx.erase(eth.get().ifIdx); 978 } 979 auto it = eth.get().manager.get().interfaces.find(intf); 980 auto obj = std::move(it->second); 981 eth.get().manager.get().interfaces.erase(it); 982 983 // Write an updated parent interface since it has a VLAN entry 984 for (const auto& [_, intf] : eth.get().manager.get().interfaces) 985 { 986 if (intf->ifIdx == parentIdx) 987 { 988 intf->writeConfigurationFile(); 989 } 990 } 991 992 if (eth.get().ifIdx > 0) 993 { 994 // We need to forcibly delete the interface as systemd does not 995 eth.get().manager.get().addReloadPostHook([idx = eth.get().ifIdx]() { 996 system::deleteIntf(idx); 997 }); 998 999 // Ignore the interface so the reload doesn't re-query it 1000 eth.get().manager.get().ignoredIntf.emplace(eth.get().ifIdx); 1001 } 1002 1003 eth.get().manager.get().reloadConfigs(); 1004 } 1005 1006 void EthernetInterface::reloadConfigs() 1007 { 1008 manager.get().reloadConfigs(); 1009 } 1010 1011 } // namespace network 1012 } // namespace phosphor 1013