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