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 // Need to merge the below function with the code which writes the 653 // config file during factory reset. 654 // TODO openbmc/openbmc#1751 655 656 void EthernetInterface::writeConfigurationFile() 657 { 658 config::Parser config; 659 config.map["Match"].emplace_back()["Name"].emplace_back(interfaceName()); 660 { 661 auto& link = config.map["Link"].emplace_back(); 662 #ifdef PERSIST_MAC 663 auto mac = MacAddressIntf::macAddress(); 664 if (!mac.empty()) 665 { 666 link["MACAddress"].emplace_back(mac); 667 } 668 #endif 669 if (!EthernetInterfaceIntf::nicEnabled()) 670 { 671 link["Unmanaged"].emplace_back("yes"); 672 } 673 } 674 { 675 auto& network = config.map["Network"].emplace_back(); 676 auto& lla = network["LinkLocalAddressing"]; 677 #ifdef LINK_LOCAL_AUTOCONFIGURATION 678 lla.emplace_back("yes"); 679 #else 680 lla.emplace_back("no"); 681 #endif 682 network["IPv6AcceptRA"].emplace_back(ipv6AcceptRA() ? "true" : "false"); 683 network["DHCP"].emplace_back(dhcp4() ? (dhcp6() ? "true" : "ipv4") 684 : (dhcp6() ? "ipv6" : "false")); 685 { 686 auto& vlans = network["VLAN"]; 687 for (const auto& [_, intf] : manager.get().interfaces) 688 { 689 if (intf->vlan && intf->vlan->parentIdx == ifIdx) 690 { 691 vlans.emplace_back(intf->interfaceName()); 692 } 693 } 694 } 695 { 696 auto& ntps = network["NTP"]; 697 for (const auto& ntp : EthernetInterfaceIntf::staticNTPServers()) 698 { 699 ntps.emplace_back(ntp); 700 } 701 } 702 { 703 auto& dnss = network["DNS"]; 704 for (const auto& dns : EthernetInterfaceIntf::staticNameServers()) 705 { 706 dnss.emplace_back(dns); 707 } 708 } 709 { 710 auto& address = network["Address"]; 711 for (const auto& addr : addrs) 712 { 713 if (originIsManuallyAssigned(addr.second->origin())) 714 { 715 address.emplace_back(stdplus::toStr(addr.first)); 716 } 717 } 718 } 719 { 720 auto& gateways = network["Gateway"]; 721 if (!dhcp4()) 722 { 723 auto gateway = EthernetInterfaceIntf::defaultGateway(); 724 if (!gateway.empty()) 725 { 726 gateways.emplace_back(gateway); 727 } 728 } 729 730 if (!ipv6AcceptRA()) 731 { 732 auto gateway6 = EthernetInterfaceIntf::defaultGateway6(); 733 if (!gateway6.empty()) 734 { 735 gateways.emplace_back(gateway6); 736 } 737 } 738 } 739 } 740 config.map["IPv6AcceptRA"].emplace_back()["DHCPv6Client"].emplace_back( 741 dhcp6() ? "true" : "false"); 742 { 743 auto& neighbors = config.map["Neighbor"]; 744 for (const auto& sneighbor : staticNeighbors) 745 { 746 auto& neighbor = neighbors.emplace_back(); 747 neighbor["Address"].emplace_back(sneighbor.second->ipAddress()); 748 neighbor["MACAddress"].emplace_back(sneighbor.second->macAddress()); 749 } 750 } 751 { 752 auto& dhcp = config.map["DHCP"].emplace_back(); 753 dhcp["ClientIdentifier"].emplace_back("mac"); 754 const auto& conf = manager.get().getDHCPConf(); 755 auto dns_enabled = conf.dnsEnabled() ? "true" : "false"; 756 dhcp["UseDNS"].emplace_back(dns_enabled); 757 dhcp["UseDomains"].emplace_back(dns_enabled); 758 dhcp["UseNTP"].emplace_back(conf.ntpEnabled() ? "true" : "false"); 759 dhcp["UseHostname"].emplace_back(conf.hostNameEnabled() ? "true" 760 : "false"); 761 dhcp["SendHostname"].emplace_back(conf.sendHostNameEnabled() ? "true" 762 : "false"); 763 } 764 auto path = config::pathForIntfConf(manager.get().getConfDir(), 765 interfaceName()); 766 config.writeFile(path); 767 lg2::info("Wrote networkd file: {CFG_FILE}", "CFG_FILE", path); 768 } 769 770 std::string EthernetInterface::macAddress([[maybe_unused]] std::string value) 771 { 772 if (vlan) 773 { 774 lg2::error("Tried to set MAC address on VLAN"); 775 elog<InternalFailure>(); 776 } 777 #ifdef PERSIST_MAC 778 stdplus::EtherAddr newMAC; 779 try 780 { 781 newMAC = stdplus::fromStr<stdplus::EtherAddr>(value); 782 } 783 catch (const std::invalid_argument&) 784 { 785 lg2::error("MAC Address {NET_MAC} is not valid", "NET_MAC", value); 786 elog<InvalidArgument>(Argument::ARGUMENT_NAME("MACAddress"), 787 Argument::ARGUMENT_VALUE(value.c_str())); 788 } 789 if (!newMAC.isUnicast()) 790 { 791 lg2::error("MAC Address {NET_MAC} is not valid", "NET_MAC", value); 792 elog<InvalidArgument>(Argument::ARGUMENT_NAME("MACAddress"), 793 Argument::ARGUMENT_VALUE(value.c_str())); 794 } 795 796 auto interface = interfaceName(); 797 auto validMAC = stdplus::toStr(newMAC); 798 799 // We don't need to update the system if the address is unchanged 800 auto oldMAC = 801 stdplus::fromStr<stdplus::EtherAddr>(MacAddressIntf::macAddress()); 802 if (newMAC != oldMAC) 803 { 804 auto path = config::pathForIntfConf(manager.get().getConfDir(), 805 interface); 806 // Update everything that depends on the MAC value 807 for (const auto& [_, intf] : manager.get().interfaces) 808 { 809 if (intf->vlan && intf->vlan->parentIdx == ifIdx) 810 { 811 intf->MacAddressIntf::macAddress(validMAC); 812 } 813 } 814 MacAddressIntf::macAddress(validMAC); 815 816 writeConfigurationFile(); 817 manager.get().addReloadPreHook([interface, path]() { 818 // The MAC and LLADDRs will only update if the NIC is already down 819 system::setNICUp(interface, false); 820 utimensat(AT_FDCWD, path.c_str(), NULL, 0); 821 }); 822 manager.get().reloadConfigs(); 823 } 824 825 #ifdef HAVE_UBOOT_ENV 826 // Ensure that the valid address is stored in the u-boot-env 827 auto envVar = interfaceToUbootEthAddr(interface); 828 if (envVar) 829 { 830 // Trimming MAC addresses that are out of range. eg: AA:FF:FF:FF:FF:100; 831 // and those having more than 6 bytes. eg: AA:AA:AA:AA:AA:AA:BB 832 execute("/sbin/fw_setenv", "fw_setenv", envVar->c_str(), 833 validMAC.c_str()); 834 } 835 #endif // HAVE_UBOOT_ENV 836 837 return value; 838 #else 839 elog<NotAllowed>( 840 NotAllowedArgument::REASON("Writing MAC address is not allowed")); 841 #endif // PERSIST_MAC 842 } 843 844 void EthernetInterface::deleteAll() 845 { 846 // clear all the ip on the interface 847 addrs.clear(); 848 849 writeConfigurationFile(); 850 manager.get().reloadConfigs(); 851 } 852 853 template <typename Addr> 854 static void normalizeGateway(std::string& gw) 855 { 856 if (gw.empty()) 857 { 858 return; 859 } 860 try 861 { 862 auto ip = stdplus::fromStr<Addr>(gw); 863 if (ip == Addr{}) 864 { 865 gw.clear(); 866 return; 867 } 868 if (!validIntfIP(ip)) 869 { 870 throw std::invalid_argument("Invalid unicast"); 871 } 872 gw = stdplus::toStr(ip); 873 } 874 catch (const std::exception& e) 875 { 876 lg2::error("Invalid GW `{NET_GW}`: {ERROR}", "NET_GW", gw, "ERROR", e); 877 elog<InvalidArgument>(Argument::ARGUMENT_NAME("GATEWAY"), 878 Argument::ARGUMENT_VALUE(gw.c_str())); 879 } 880 } 881 882 std::string EthernetInterface::defaultGateway(std::string gateway) 883 { 884 normalizeGateway<stdplus::In4Addr>(gateway); 885 if (gateway != defaultGateway()) 886 { 887 gateway = EthernetInterfaceIntf::defaultGateway(std::move(gateway)); 888 manager.get().reloadConfigs(); 889 } 890 return gateway; 891 } 892 893 std::string EthernetInterface::defaultGateway6(std::string gateway) 894 { 895 normalizeGateway<stdplus::In6Addr>(gateway); 896 if (gateway != defaultGateway6()) 897 { 898 gateway = EthernetInterfaceIntf::defaultGateway6(std::move(gateway)); 899 manager.get().reloadConfigs(); 900 } 901 return gateway; 902 } 903 904 EthernetInterface::VlanProperties::VlanProperties( 905 sdbusplus::bus_t& bus, stdplus::const_zstring objPath, 906 const InterfaceInfo& info, stdplus::PinnedRef<EthernetInterface> eth) : 907 VlanIfaces(bus, objPath.c_str(), VlanIfaces::action::defer_emit), 908 parentIdx(*info.parent_idx), eth(eth) 909 { 910 VlanIntf::id(*info.vlan_id, true); 911 emit_object_added(); 912 } 913 914 void EthernetInterface::VlanProperties::delete_() 915 { 916 auto intf = eth.get().interfaceName(); 917 918 // Remove all configs for the current interface 919 const auto& confDir = eth.get().manager.get().getConfDir(); 920 std::error_code ec; 921 std::filesystem::remove(config::pathForIntfConf(confDir, intf), ec); 922 std::filesystem::remove(config::pathForIntfDev(confDir, intf), ec); 923 924 if (eth.get().ifIdx > 0) 925 { 926 eth.get().manager.get().interfacesByIdx.erase(eth.get().ifIdx); 927 } 928 auto it = eth.get().manager.get().interfaces.find(intf); 929 auto obj = std::move(it->second); 930 eth.get().manager.get().interfaces.erase(it); 931 932 // Write an updated parent interface since it has a VLAN entry 933 for (const auto& [_, intf] : eth.get().manager.get().interfaces) 934 { 935 if (intf->ifIdx == parentIdx) 936 { 937 intf->writeConfigurationFile(); 938 } 939 } 940 941 if (eth.get().ifIdx > 0) 942 { 943 // We need to forcibly delete the interface as systemd does not 944 eth.get().manager.get().addReloadPostHook( 945 [idx = eth.get().ifIdx]() { system::deleteIntf(idx); }); 946 947 // Ignore the interface so the reload doesn't re-query it 948 eth.get().manager.get().ignoredIntf.emplace(eth.get().ifIdx); 949 } 950 951 eth.get().manager.get().reloadConfigs(); 952 } 953 954 } // namespace network 955 } // namespace phosphor 956