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