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 14 #include <phosphor-logging/elog-errors.hpp> 15 #include <phosphor-logging/lg2.hpp> 16 #include <stdplus/raw.hpp> 17 #include <stdplus/str/cat.hpp> 18 #include <stdplus/zstring.hpp> 19 #include <xyz/openbmc_project/Common/error.hpp> 20 21 #include <algorithm> 22 #include <filesystem> 23 #include <format> 24 #include <string> 25 #include <unordered_map> 26 #include <variant> 27 28 namespace phosphor 29 { 30 namespace network 31 { 32 33 using namespace phosphor::logging; 34 using namespace sdbusplus::xyz::openbmc_project::Common::Error; 35 using NotAllowed = sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed; 36 using NotAllowedArgument = xyz::openbmc_project::Common::NotAllowed; 37 using Argument = xyz::openbmc_project::Common::InvalidArgument; 38 using std::literals::string_view_literals::operator""sv; 39 constexpr auto RESOLVED_SERVICE = "org.freedesktop.resolve1"; 40 constexpr auto RESOLVED_INTERFACE = "org.freedesktop.resolve1.Link"; 41 constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties"; 42 constexpr auto RESOLVED_SERVICE_PATH = "/org/freedesktop/resolve1/link/"; 43 44 constexpr auto TIMESYNCD_SERVICE = "org.freedesktop.timesync1"; 45 constexpr auto TIMESYNCD_INTERFACE = "org.freedesktop.timesync1.Manager"; 46 constexpr auto TIMESYNCD_SERVICE_PATH = "/org/freedesktop/timesync1"; 47 48 constexpr auto METHOD_GET = "Get"; 49 50 template <typename Func> 51 inline decltype(std::declval<Func>()()) 52 ignoreError(std::string_view msg, stdplus::zstring_view intf, 53 decltype(std::declval<Func>()()) fallback, Func&& func) noexcept 54 { 55 try 56 { 57 return func(); 58 } 59 catch (const std::exception& e) 60 { 61 lg2::error("{MSG} failed on {NET_INTF}: {ERROR}", "MSG", msg, 62 "NET_INTF", intf, "ERROR", e); 63 } 64 return fallback; 65 } 66 67 static std::string makeObjPath(std::string_view root, std::string_view intf) 68 { 69 auto ret = stdplus::strCat(root, "/"sv, intf); 70 std::replace(ret.begin() + ret.size() - intf.size(), ret.end(), '.', '_'); 71 return ret; 72 } 73 74 template <typename Addr> 75 static bool validIntfIP(Addr a) noexcept 76 { 77 return a.isUnicast() && !a.isLoopback(); 78 } 79 80 EthernetInterface::EthernetInterface(stdplus::PinnedRef<sdbusplus::bus_t> bus, 81 stdplus::PinnedRef<Manager> manager, 82 const AllIntfInfo& info, 83 std::string_view objRoot, 84 const config::Parser& config, 85 bool enabled) : 86 EthernetInterface(bus, manager, info, makeObjPath(objRoot, *info.intf.name), 87 config, enabled) 88 {} 89 90 EthernetInterface::EthernetInterface(stdplus::PinnedRef<sdbusplus::bus_t> bus, 91 stdplus::PinnedRef<Manager> manager, 92 const AllIntfInfo& info, 93 std::string&& objPath, 94 const config::Parser& config, 95 bool enabled) : 96 Ifaces(bus, objPath.c_str(), Ifaces::action::defer_emit), 97 manager(manager), bus(bus), objPath(std::move(objPath)) 98 { 99 interfaceName(*info.intf.name, true); 100 auto dhcpVal = getDHCPValue(config); 101 EthernetInterfaceIntf::dhcp4(dhcpVal.v4, true); 102 EthernetInterfaceIntf::dhcp6(dhcpVal.v6, true); 103 EthernetInterfaceIntf::ipv6AcceptRA(getIPv6AcceptRA(config), true); 104 EthernetInterfaceIntf::nicEnabled(enabled, true); 105 106 EthernetInterfaceIntf::ntpServers( 107 config.map.getValueStrings("Network", "NTP"), true); 108 109 updateInfo(info.intf, true); 110 111 if (info.defgw4) 112 { 113 EthernetInterface::defaultGateway(stdplus::toStr(*info.defgw4), true); 114 } 115 if (info.defgw6) 116 { 117 EthernetInterface::defaultGateway6(stdplus::toStr(*info.defgw6), true); 118 } 119 emit_object_added(); 120 121 if (info.intf.vlan_id) 122 { 123 if (!info.intf.parent_idx) 124 { 125 std::runtime_error("Missing parent link"); 126 } 127 vlan.emplace(bus, this->objPath.c_str(), info.intf, *this); 128 } 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 origin == IP::AddressOrigin::LinkLocal) 167 #else 168 (origin == IP::AddressOrigin::Static) 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(*info.addr, std::make_unique<Neighbor>( 234 bus, std::string_view(objPath), 235 *this, *info.addr, *info.mac, 236 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(value == DHCPConf::v4 || 397 value == DHCPConf::v4v6stateless || 398 value == DHCPConf::both); 399 auto old6 = EthernetInterfaceIntf::dhcp6(); 400 auto new6 = EthernetInterfaceIntf::dhcp6(value == DHCPConf::v6 || 401 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( 456 [ifname = interfaceName()]() { system::setNICUp(ifname, false); }); 457 } 458 manager.get().reloadConfigs(); 459 460 return value; 461 } 462 463 ServerList EthernetInterface::staticNameServers(ServerList value) 464 { 465 for (auto& ip : value) 466 { 467 try 468 { 469 ip = stdplus::toStr(stdplus::fromStr<stdplus::InAnyAddr>(ip)); 470 } 471 catch (const std::exception& e) 472 { 473 lg2::error("Not a valid IP address {NET_IP}: {ERROR}", "NET_IP", ip, 474 "ERROR", e); 475 elog<InvalidArgument>(Argument::ARGUMENT_NAME("StaticNameserver"), 476 Argument::ARGUMENT_VALUE(ip.c_str())); 477 } 478 } 479 480 value = EthernetInterfaceIntf::staticNameServers(std::move(value)); 481 482 writeConfigurationFile(); 483 manager.get().reloadConfigs(); 484 485 return value; 486 } 487 488 void EthernetInterface::loadNTPServers(const config::Parser& config) 489 { 490 EthernetInterfaceIntf::ntpServers(getNTPServerFromTimeSyncd()); 491 EthernetInterfaceIntf::staticNTPServers( 492 config.map.getValueStrings("Network", "NTP")); 493 } 494 495 void EthernetInterface::loadNameServers(const config::Parser& config) 496 { 497 EthernetInterfaceIntf::nameservers(getNameServerFromResolvd()); 498 EthernetInterfaceIntf::staticNameServers( 499 config.map.getValueStrings("Network", "DNS")); 500 } 501 502 ServerList EthernetInterface::getNTPServerFromTimeSyncd() 503 { 504 ServerList servers; // Variable to capture the NTP Server IPs 505 auto method = bus.get().new_method_call(TIMESYNCD_SERVICE, 506 TIMESYNCD_SERVICE_PATH, 507 PROPERTY_INTERFACE, METHOD_GET); 508 509 method.append(TIMESYNCD_INTERFACE, "LinkNTPServers"); 510 511 try 512 { 513 auto reply = bus.get().call(method); 514 std::variant<ServerList> response; 515 reply.read(response); 516 servers = std::get<ServerList>(response); 517 } 518 catch (const sdbusplus::exception::SdBusError& e) 519 { 520 lg2::error("Failed to get NTP server information from " 521 "systemd-timesyncd: {ERROR}", 522 "ERROR", e); 523 } 524 525 return servers; 526 } 527 528 ServerList EthernetInterface::nameservers() const 529 { 530 return getNameServerFromResolvd(); 531 } 532 533 ServerList EthernetInterface::getNameServerFromResolvd() const 534 { 535 ServerList servers; 536 auto OBJ_PATH = std::format("{}{}", RESOLVED_SERVICE_PATH, ifIdx); 537 538 /* 539 The DNS property under org.freedesktop.resolve1.Link interface contains 540 an array containing all DNS servers currently used by resolved. It 541 contains similar information as the DNS server data written to 542 /run/systemd/resolve/resolv.conf. 543 544 Each structure in the array consists of a numeric network interface index, 545 an address family, and a byte array containing the DNS server address 546 (either 4 bytes in length for IPv4 or 16 bytes in lengths for IPv6). 547 The array contains DNS servers configured system-wide, including those 548 possibly read from a foreign /etc/resolv.conf or the DNS= setting in 549 /etc/systemd/resolved.conf, as well as per-interface DNS server 550 information either retrieved from systemd-networkd or configured by 551 external software via SetLinkDNS(). 552 */ 553 554 using type = std::vector<std::tuple<int32_t, std::vector<uint8_t>>>; 555 std::variant<type> name; // Variable to capture the DNS property 556 auto method = bus.get().new_method_call(RESOLVED_SERVICE, OBJ_PATH.c_str(), 557 PROPERTY_INTERFACE, METHOD_GET); 558 559 method.append(RESOLVED_INTERFACE, "DNS"); 560 561 try 562 { 563 auto reply = bus.get().call(method); 564 reply.read(name); 565 } 566 catch (const sdbusplus::exception_t& e) 567 { 568 lg2::error( 569 "Failed to get DNS information from systemd-resolved: {ERROR}", 570 "ERROR", e); 571 } 572 auto tupleVector = std::get_if<type>(&name); 573 for (auto i = tupleVector->begin(); i != tupleVector->end(); ++i) 574 { 575 int addressFamily = std::get<0>(*i); 576 std::vector<uint8_t>& ipaddress = std::get<1>(*i); 577 servers.push_back(stdplus::toStr( 578 addrFromBuf(addressFamily, stdplus::raw::asView<char>(ipaddress)))); 579 } 580 return servers; 581 } 582 583 ObjectPath EthernetInterface::createVLAN(uint16_t id) 584 { 585 auto idStr = stdplus::toStr(id); 586 auto intfName = stdplus::strCat(interfaceName(), "."sv, idStr); 587 if (manager.get().interfaces.find(intfName) != 588 manager.get().interfaces.end()) 589 { 590 lg2::error("VLAN {NET_VLAN} already exists", "NET_VLAN", id); 591 elog<InvalidArgument>(Argument::ARGUMENT_NAME("VLANId"), 592 Argument::ARGUMENT_VALUE(idStr.c_str())); 593 } 594 595 auto objRoot = std::string_view(objPath).substr(0, objPath.rfind('/')); 596 auto macStr = MacAddressIntf::macAddress(); 597 std::optional<stdplus::EtherAddr> mac; 598 if (!macStr.empty()) 599 { 600 mac.emplace(stdplus::fromStr<stdplus::EtherAddr>(macStr)); 601 } 602 auto info = AllIntfInfo{InterfaceInfo{ 603 .type = ARPHRD_ETHER, 604 .idx = 0, // TODO: Query the correct value after creation 605 .flags = 0, 606 .name = intfName, 607 .mac = std::move(mac), 608 .mtu = mtu(), 609 .parent_idx = ifIdx, 610 .vlan_id = id, 611 }}; 612 613 // Pass the parents nicEnabled property, so that the child 614 // VLAN interface can inherit. 615 auto vlanIntf = std::make_unique<EthernetInterface>( 616 bus, manager, info, objRoot, config::Parser(), nicEnabled()); 617 ObjectPath ret = vlanIntf->objPath; 618 619 manager.get().interfaces.emplace(intfName, std::move(vlanIntf)); 620 621 // write the device file for the vlan interface. 622 config::Parser config; 623 auto& netdev = config.map["NetDev"].emplace_back(); 624 netdev["Name"].emplace_back(intfName); 625 netdev["Kind"].emplace_back("vlan"); 626 config.map["VLAN"].emplace_back()["Id"].emplace_back(std::move(idStr)); 627 config.writeFile( 628 config::pathForIntfDev(manager.get().getConfDir(), intfName)); 629 630 writeConfigurationFile(); 631 manager.get().reloadConfigs(); 632 633 return ret; 634 } 635 636 ServerList EthernetInterface::staticNTPServers(ServerList value) 637 { 638 value = EthernetInterfaceIntf::staticNTPServers(std::move(value)); 639 640 writeConfigurationFile(); 641 manager.get().reloadConfigs(); 642 643 return value; 644 } 645 646 ServerList EthernetInterface::ntpServers(ServerList /*servers*/) 647 { 648 elog<NotAllowed>(NotAllowedArgument::REASON("ReadOnly Property")); 649 } 650 // Need to merge the below function with the code which writes the 651 // config file during factory reset. 652 // TODO openbmc/openbmc#1751 653 654 void EthernetInterface::writeConfigurationFile() 655 { 656 config::Parser config; 657 config.map["Match"].emplace_back()["Name"].emplace_back(interfaceName()); 658 { 659 auto& link = config.map["Link"].emplace_back(); 660 #ifdef PERSIST_MAC 661 auto mac = MacAddressIntf::macAddress(); 662 if (!mac.empty()) 663 { 664 link["MACAddress"].emplace_back(mac); 665 } 666 #endif 667 if (!EthernetInterfaceIntf::nicEnabled()) 668 { 669 link["Unmanaged"].emplace_back("yes"); 670 } 671 } 672 { 673 auto& network = config.map["Network"].emplace_back(); 674 auto& lla = network["LinkLocalAddressing"]; 675 #ifdef LINK_LOCAL_AUTOCONFIGURATION 676 lla.emplace_back("yes"); 677 #else 678 lla.emplace_back("no"); 679 #endif 680 network["IPv6AcceptRA"].emplace_back(ipv6AcceptRA() ? "true" : "false"); 681 network["DHCP"].emplace_back(dhcp4() ? (dhcp6() ? "true" : "ipv4") 682 : (dhcp6() ? "ipv6" : "false")); 683 { 684 auto& vlans = network["VLAN"]; 685 for (const auto& [_, intf] : manager.get().interfaces) 686 { 687 if (intf->vlan && intf->vlan->parentIdx == ifIdx) 688 { 689 vlans.emplace_back(intf->interfaceName()); 690 } 691 } 692 } 693 { 694 auto& ntps = network["NTP"]; 695 for (const auto& ntp : EthernetInterfaceIntf::staticNTPServers()) 696 { 697 ntps.emplace_back(ntp); 698 } 699 } 700 { 701 auto& dnss = network["DNS"]; 702 for (const auto& dns : EthernetInterfaceIntf::staticNameServers()) 703 { 704 dnss.emplace_back(dns); 705 } 706 } 707 { 708 auto& address = network["Address"]; 709 for (const auto& addr : addrs) 710 { 711 if (originIsManuallyAssigned(addr.second->origin())) 712 { 713 address.emplace_back(stdplus::toStr(addr.first)); 714 } 715 } 716 } 717 { 718 auto& gateways = network["Gateway"]; 719 if (!dhcp4()) 720 { 721 auto gateway = EthernetInterfaceIntf::defaultGateway(); 722 if (!gateway.empty()) 723 { 724 gateways.emplace_back(gateway); 725 } 726 } 727 728 if (!ipv6AcceptRA()) 729 { 730 auto gateway6 = EthernetInterfaceIntf::defaultGateway6(); 731 if (!gateway6.empty()) 732 { 733 gateways.emplace_back(gateway6); 734 } 735 } 736 } 737 } 738 config.map["IPv6AcceptRA"].emplace_back()["DHCPv6Client"].emplace_back( 739 dhcp6() ? "true" : "false"); 740 { 741 auto& neighbors = config.map["Neighbor"]; 742 for (const auto& sneighbor : staticNeighbors) 743 { 744 auto& neighbor = neighbors.emplace_back(); 745 neighbor["Address"].emplace_back(sneighbor.second->ipAddress()); 746 neighbor["MACAddress"].emplace_back(sneighbor.second->macAddress()); 747 } 748 } 749 { 750 auto& dhcp = config.map["DHCP"].emplace_back(); 751 dhcp["ClientIdentifier"].emplace_back("mac"); 752 const auto& conf = manager.get().getDHCPConf(); 753 auto dns_enabled = conf.dnsEnabled() ? "true" : "false"; 754 dhcp["UseDNS"].emplace_back(dns_enabled); 755 dhcp["UseDomains"].emplace_back(dns_enabled); 756 dhcp["UseNTP"].emplace_back(conf.ntpEnabled() ? "true" : "false"); 757 dhcp["UseHostname"].emplace_back(conf.hostNameEnabled() ? "true" 758 : "false"); 759 dhcp["SendHostname"].emplace_back(conf.sendHostNameEnabled() ? "true" 760 : "false"); 761 } 762 auto path = config::pathForIntfConf(manager.get().getConfDir(), 763 interfaceName()); 764 config.writeFile(path); 765 lg2::info("Wrote networkd file: {CFG_FILE}", "CFG_FILE", path); 766 } 767 768 std::string EthernetInterface::macAddress([[maybe_unused]] std::string value) 769 { 770 if (vlan) 771 { 772 lg2::error("Tried to set MAC address on VLAN"); 773 elog<InternalFailure>(); 774 } 775 #ifdef PERSIST_MAC 776 stdplus::EtherAddr newMAC; 777 try 778 { 779 newMAC = stdplus::fromStr<stdplus::EtherAddr>(value); 780 } 781 catch (const std::invalid_argument&) 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 if (!newMAC.isUnicast()) 788 { 789 lg2::error("MAC Address {NET_MAC} is not valid", "NET_MAC", value); 790 elog<InvalidArgument>(Argument::ARGUMENT_NAME("MACAddress"), 791 Argument::ARGUMENT_VALUE(value.c_str())); 792 } 793 794 auto interface = interfaceName(); 795 auto validMAC = stdplus::toStr(newMAC); 796 797 // We don't need to update the system if the address is unchanged 798 auto oldMAC = 799 stdplus::fromStr<stdplus::EtherAddr>(MacAddressIntf::macAddress()); 800 if (newMAC != oldMAC) 801 { 802 // Update everything that depends on the MAC value 803 for (const auto& [_, intf] : manager.get().interfaces) 804 { 805 if (intf->vlan && intf->vlan->parentIdx == ifIdx) 806 { 807 intf->MacAddressIntf::macAddress(validMAC); 808 } 809 } 810 MacAddressIntf::macAddress(validMAC); 811 812 writeConfigurationFile(); 813 manager.get().addReloadPreHook([interface]() { 814 // The MAC and LLADDRs will only update if the NIC is already down 815 system::setNICUp(interface, false); 816 }); 817 manager.get().reloadConfigs(); 818 } 819 820 #ifdef HAVE_UBOOT_ENV 821 // Ensure that the valid address is stored in the u-boot-env 822 auto envVar = interfaceToUbootEthAddr(interface); 823 if (envVar) 824 { 825 // Trimming MAC addresses that are out of range. eg: AA:FF:FF:FF:FF:100; 826 // and those having more than 6 bytes. eg: AA:AA:AA:AA:AA:AA:BB 827 execute("/sbin/fw_setenv", "fw_setenv", envVar->c_str(), 828 validMAC.c_str()); 829 } 830 #endif // HAVE_UBOOT_ENV 831 832 return value; 833 #else 834 elog<NotAllowed>( 835 NotAllowedArgument::REASON("Writing MAC address is not allowed")); 836 #endif // PERSIST_MAC 837 } 838 839 void EthernetInterface::deleteAll() 840 { 841 // clear all the ip on the interface 842 addrs.clear(); 843 844 writeConfigurationFile(); 845 manager.get().reloadConfigs(); 846 } 847 848 template <typename Addr> 849 static void normalizeGateway(std::string& gw) 850 { 851 if (gw.empty()) 852 { 853 return; 854 } 855 try 856 { 857 auto ip = stdplus::fromStr<Addr>(gw); 858 if (ip == Addr{}) 859 { 860 gw.clear(); 861 return; 862 } 863 if (!validIntfIP(ip)) 864 { 865 throw std::invalid_argument("Invalid unicast"); 866 } 867 gw = stdplus::toStr(ip); 868 } 869 catch (const std::exception& e) 870 { 871 lg2::error("Invalid GW `{NET_GW}`: {ERROR}", "NET_GW", gw, "ERROR", e); 872 elog<InvalidArgument>(Argument::ARGUMENT_NAME("GATEWAY"), 873 Argument::ARGUMENT_VALUE(gw.c_str())); 874 } 875 } 876 877 std::string EthernetInterface::defaultGateway(std::string gateway) 878 { 879 normalizeGateway<stdplus::In4Addr>(gateway); 880 if (gateway != defaultGateway()) 881 { 882 gateway = EthernetInterfaceIntf::defaultGateway(std::move(gateway)); 883 manager.get().reloadConfigs(); 884 } 885 return gateway; 886 } 887 888 std::string EthernetInterface::defaultGateway6(std::string gateway) 889 { 890 normalizeGateway<stdplus::In6Addr>(gateway); 891 if (gateway != defaultGateway6()) 892 { 893 gateway = EthernetInterfaceIntf::defaultGateway6(std::move(gateway)); 894 manager.get().reloadConfigs(); 895 } 896 return gateway; 897 } 898 899 EthernetInterface::VlanProperties::VlanProperties( 900 sdbusplus::bus_t& bus, stdplus::const_zstring objPath, 901 const InterfaceInfo& info, stdplus::PinnedRef<EthernetInterface> eth) : 902 VlanIfaces(bus, objPath.c_str(), VlanIfaces::action::defer_emit), 903 parentIdx(*info.parent_idx), eth(eth) 904 { 905 VlanIntf::id(*info.vlan_id, true); 906 emit_object_added(); 907 } 908 909 void EthernetInterface::VlanProperties::delete_() 910 { 911 auto intf = eth.get().interfaceName(); 912 913 // Remove all configs for the current interface 914 const auto& confDir = eth.get().manager.get().getConfDir(); 915 std::error_code ec; 916 std::filesystem::remove(config::pathForIntfConf(confDir, intf), ec); 917 std::filesystem::remove(config::pathForIntfDev(confDir, intf), ec); 918 919 if (eth.get().ifIdx > 0) 920 { 921 eth.get().manager.get().interfacesByIdx.erase(eth.get().ifIdx); 922 } 923 auto it = eth.get().manager.get().interfaces.find(intf); 924 auto obj = std::move(it->second); 925 eth.get().manager.get().interfaces.erase(it); 926 927 // Write an updated parent interface since it has a VLAN entry 928 for (const auto& [_, intf] : eth.get().manager.get().interfaces) 929 { 930 if (intf->ifIdx == parentIdx) 931 { 932 intf->writeConfigurationFile(); 933 } 934 } 935 936 if (eth.get().ifIdx > 0) 937 { 938 // We need to forcibly delete the interface as systemd does not 939 eth.get().manager.get().addReloadPostHook( 940 [idx = eth.get().ifIdx]() { system::deleteIntf(idx); }); 941 942 // Ignore the interface so the reload doesn't re-query it 943 eth.get().manager.get().ignoredIntf.emplace(eth.get().ifIdx); 944 } 945 946 eth.get().manager.get().reloadConfigs(); 947 } 948 949 } // namespace network 950 } // namespace phosphor 951