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