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 std::vector<std::string> dnsUniqueValues; 468 for (auto& ip : value) 469 { 470 try 471 { 472 ip = stdplus::toStr(stdplus::fromStr<stdplus::InAnyAddr>(ip)); 473 } 474 catch (const std::exception& e) 475 { 476 lg2::error("Not a valid IP address {NET_IP}: {ERROR}", "NET_IP", ip, 477 "ERROR", e); 478 elog<InvalidArgument>(Argument::ARGUMENT_NAME("StaticNameserver"), 479 Argument::ARGUMENT_VALUE(ip.c_str())); 480 } 481 if (std::find(dnsUniqueValues.begin(), dnsUniqueValues.end(), ip) == 482 dnsUniqueValues.end()) 483 { 484 dnsUniqueValues.push_back(ip); 485 } 486 } 487 488 value = 489 EthernetInterfaceIntf::staticNameServers(std::move(dnsUniqueValues)); 490 491 writeConfigurationFile(); 492 manager.get().reloadConfigs(); 493 494 return value; 495 } 496 497 void EthernetInterface::loadNTPServers(const config::Parser& config) 498 { 499 EthernetInterfaceIntf::ntpServers(getNTPServerFromTimeSyncd()); 500 EthernetInterfaceIntf::staticNTPServers( 501 config.map.getValueStrings("Network", "NTP")); 502 } 503 504 void EthernetInterface::loadNameServers(const config::Parser& config) 505 { 506 EthernetInterfaceIntf::nameservers(getNameServerFromResolvd()); 507 EthernetInterfaceIntf::staticNameServers( 508 config.map.getValueStrings("Network", "DNS")); 509 } 510 511 ServerList EthernetInterface::getNTPServerFromTimeSyncd() 512 { 513 ServerList servers; // Variable to capture the NTP Server IPs 514 auto method = bus.get().new_method_call(TIMESYNCD_SERVICE, 515 TIMESYNCD_SERVICE_PATH, 516 PROPERTY_INTERFACE, METHOD_GET); 517 518 method.append(TIMESYNCD_INTERFACE, "LinkNTPServers"); 519 520 try 521 { 522 auto reply = bus.get().call(method); 523 std::variant<ServerList> response; 524 reply.read(response); 525 servers = std::get<ServerList>(response); 526 } 527 catch (const sdbusplus::exception::SdBusError& e) 528 { 529 lg2::error("Failed to get NTP server information from " 530 "systemd-timesyncd: {ERROR}", 531 "ERROR", e); 532 } 533 534 return servers; 535 } 536 537 ServerList EthernetInterface::nameservers() const 538 { 539 return getNameServerFromResolvd(); 540 } 541 542 ServerList EthernetInterface::getNameServerFromResolvd() const 543 { 544 ServerList servers; 545 auto OBJ_PATH = std::format("{}{}", RESOLVED_SERVICE_PATH, ifIdx); 546 547 /* 548 The DNS property under org.freedesktop.resolve1.Link interface contains 549 an array containing all DNS servers currently used by resolved. It 550 contains similar information as the DNS server data written to 551 /run/systemd/resolve/resolv.conf. 552 553 Each structure in the array consists of a numeric network interface index, 554 an address family, and a byte array containing the DNS server address 555 (either 4 bytes in length for IPv4 or 16 bytes in lengths for IPv6). 556 The array contains DNS servers configured system-wide, including those 557 possibly read from a foreign /etc/resolv.conf or the DNS= setting in 558 /etc/systemd/resolved.conf, as well as per-interface DNS server 559 information either retrieved from systemd-networkd or configured by 560 external software via SetLinkDNS(). 561 */ 562 563 using type = std::vector<std::tuple<int32_t, std::vector<uint8_t>>>; 564 std::variant<type> name; // Variable to capture the DNS property 565 auto method = bus.get().new_method_call(RESOLVED_SERVICE, OBJ_PATH.c_str(), 566 PROPERTY_INTERFACE, METHOD_GET); 567 568 method.append(RESOLVED_INTERFACE, "DNS"); 569 570 try 571 { 572 auto reply = bus.get().call(method); 573 reply.read(name); 574 } 575 catch (const sdbusplus::exception_t& e) 576 { 577 lg2::error( 578 "Failed to get DNS information from systemd-resolved: {ERROR}", 579 "ERROR", e); 580 } 581 auto tupleVector = std::get_if<type>(&name); 582 for (auto i = tupleVector->begin(); i != tupleVector->end(); ++i) 583 { 584 int addressFamily = std::get<0>(*i); 585 std::vector<uint8_t>& ipaddress = std::get<1>(*i); 586 servers.push_back(stdplus::toStr( 587 addrFromBuf(addressFamily, stdplus::raw::asView<char>(ipaddress)))); 588 } 589 return servers; 590 } 591 592 ObjectPath EthernetInterface::createVLAN(uint16_t id) 593 { 594 auto idStr = stdplus::toStr(id); 595 auto intfName = stdplus::strCat(interfaceName(), "."sv, idStr); 596 if (manager.get().interfaces.find(intfName) != 597 manager.get().interfaces.end()) 598 { 599 lg2::error("VLAN {NET_VLAN} already exists", "NET_VLAN", id); 600 elog<InvalidArgument>(Argument::ARGUMENT_NAME("VLANId"), 601 Argument::ARGUMENT_VALUE(idStr.c_str())); 602 } 603 604 auto objRoot = std::string_view(objPath).substr(0, objPath.rfind('/')); 605 auto macStr = MacAddressIntf::macAddress(); 606 std::optional<stdplus::EtherAddr> mac; 607 if (!macStr.empty()) 608 { 609 mac.emplace(stdplus::fromStr<stdplus::EtherAddr>(macStr)); 610 } 611 auto info = AllIntfInfo{InterfaceInfo{ 612 .type = ARPHRD_ETHER, 613 .idx = 0, // TODO: Query the correct value after creation 614 .flags = 0, 615 .name = intfName, 616 .mac = std::move(mac), 617 .mtu = mtu(), 618 .parent_idx = ifIdx, 619 .vlan_id = id, 620 }}; 621 622 // Pass the parents nicEnabled property, so that the child 623 // VLAN interface can inherit. 624 auto vlanIntf = std::make_unique<EthernetInterface>( 625 bus, manager, info, objRoot, config::Parser(), nicEnabled()); 626 ObjectPath ret = vlanIntf->objPath; 627 628 manager.get().interfaces.emplace(intfName, std::move(vlanIntf)); 629 630 // write the device file for the vlan interface. 631 config::Parser config; 632 auto& netdev = config.map["NetDev"].emplace_back(); 633 netdev["Name"].emplace_back(intfName); 634 netdev["Kind"].emplace_back("vlan"); 635 config.map["VLAN"].emplace_back()["Id"].emplace_back(std::move(idStr)); 636 config.writeFile( 637 config::pathForIntfDev(manager.get().getConfDir(), intfName)); 638 639 writeConfigurationFile(); 640 manager.get().reloadConfigs(); 641 642 return ret; 643 } 644 645 ServerList EthernetInterface::staticNTPServers(ServerList value) 646 { 647 value = EthernetInterfaceIntf::staticNTPServers(std::move(value)); 648 649 writeConfigurationFile(); 650 manager.get().reloadConfigs(); 651 652 return value; 653 } 654 655 ServerList EthernetInterface::ntpServers(ServerList /*servers*/) 656 { 657 elog<NotAllowed>(NotAllowedArgument::REASON("ReadOnly Property")); 658 } 659 660 void EthernetInterface::writeConfigurationFile() 661 { 662 config::Parser config; 663 config.map["Match"].emplace_back()["Name"].emplace_back(interfaceName()); 664 { 665 auto& link = config.map["Link"].emplace_back(); 666 #ifdef PERSIST_MAC 667 auto mac = MacAddressIntf::macAddress(); 668 if (!mac.empty()) 669 { 670 link["MACAddress"].emplace_back(mac); 671 } 672 #endif 673 if (!EthernetInterfaceIntf::nicEnabled()) 674 { 675 link["Unmanaged"].emplace_back("yes"); 676 } 677 } 678 { 679 auto& network = config.map["Network"].emplace_back(); 680 auto& lla = network["LinkLocalAddressing"]; 681 #ifdef LINK_LOCAL_AUTOCONFIGURATION 682 lla.emplace_back("yes"); 683 #else 684 lla.emplace_back("no"); 685 #endif 686 network["IPv6AcceptRA"].emplace_back(ipv6AcceptRA() ? "true" : "false"); 687 network["DHCP"].emplace_back(dhcp4() ? (dhcp6() ? "true" : "ipv4") 688 : (dhcp6() ? "ipv6" : "false")); 689 { 690 auto& vlans = network["VLAN"]; 691 for (const auto& [_, intf] : manager.get().interfaces) 692 { 693 if (intf->vlan && intf->vlan->parentIdx == ifIdx) 694 { 695 vlans.emplace_back(intf->interfaceName()); 696 } 697 } 698 } 699 { 700 auto& ntps = network["NTP"]; 701 for (const auto& ntp : EthernetInterfaceIntf::staticNTPServers()) 702 { 703 ntps.emplace_back(ntp); 704 } 705 } 706 { 707 auto& dnss = network["DNS"]; 708 for (const auto& dns : EthernetInterfaceIntf::staticNameServers()) 709 { 710 dnss.emplace_back(dns); 711 } 712 } 713 { 714 auto& address = network["Address"]; 715 for (const auto& addr : addrs) 716 { 717 if (originIsManuallyAssigned(addr.second->origin())) 718 { 719 address.emplace_back(stdplus::toStr(addr.first)); 720 } 721 } 722 } 723 { 724 auto& gateways = network["Gateway"]; 725 if (!dhcp4()) 726 { 727 auto gateway = EthernetInterfaceIntf::defaultGateway(); 728 if (!gateway.empty()) 729 { 730 gateways.emplace_back(gateway); 731 } 732 } 733 734 if (!ipv6AcceptRA()) 735 { 736 auto gateway6 = EthernetInterfaceIntf::defaultGateway6(); 737 if (!gateway6.empty()) 738 { 739 gateways.emplace_back(gateway6); 740 } 741 } 742 } 743 } 744 config.map["IPv6AcceptRA"].emplace_back()["DHCPv6Client"].emplace_back( 745 dhcp6() ? "true" : "false"); 746 { 747 auto& neighbors = config.map["Neighbor"]; 748 for (const auto& sneighbor : staticNeighbors) 749 { 750 auto& neighbor = neighbors.emplace_back(); 751 neighbor["Address"].emplace_back(sneighbor.second->ipAddress()); 752 neighbor["MACAddress"].emplace_back(sneighbor.second->macAddress()); 753 } 754 } 755 { 756 auto& dhcp = config.map["DHCP"].emplace_back(); 757 dhcp["ClientIdentifier"].emplace_back("mac"); 758 const auto& conf = manager.get().getDHCPConf(); 759 auto dns_enabled = conf.dnsEnabled() ? "true" : "false"; 760 dhcp["UseDNS"].emplace_back(dns_enabled); 761 dhcp["UseDomains"].emplace_back(dns_enabled); 762 dhcp["UseNTP"].emplace_back(conf.ntpEnabled() ? "true" : "false"); 763 dhcp["UseHostname"].emplace_back(conf.hostNameEnabled() ? "true" 764 : "false"); 765 dhcp["SendHostname"].emplace_back(conf.sendHostNameEnabled() ? "true" 766 : "false"); 767 } 768 auto path = config::pathForIntfConf(manager.get().getConfDir(), 769 interfaceName()); 770 config.writeFile(path); 771 lg2::info("Wrote networkd file: {CFG_FILE}", "CFG_FILE", path); 772 } 773 774 std::string EthernetInterface::macAddress([[maybe_unused]] std::string value) 775 { 776 if (vlan) 777 { 778 lg2::error("Tried to set MAC address on VLAN"); 779 elog<InternalFailure>(); 780 } 781 #ifdef PERSIST_MAC 782 stdplus::EtherAddr newMAC; 783 try 784 { 785 newMAC = stdplus::fromStr<stdplus::EtherAddr>(value); 786 } 787 catch (const std::invalid_argument&) 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 if (!newMAC.isUnicast()) 794 { 795 lg2::error("MAC Address {NET_MAC} is not valid", "NET_MAC", value); 796 elog<InvalidArgument>(Argument::ARGUMENT_NAME("MACAddress"), 797 Argument::ARGUMENT_VALUE(value.c_str())); 798 } 799 800 auto interface = interfaceName(); 801 auto validMAC = stdplus::toStr(newMAC); 802 803 // We don't need to update the system if the address is unchanged 804 auto oldMAC = 805 stdplus::fromStr<stdplus::EtherAddr>(MacAddressIntf::macAddress()); 806 if (newMAC != oldMAC) 807 { 808 auto path = config::pathForIntfConf(manager.get().getConfDir(), 809 interface); 810 // Update everything that depends on the MAC value 811 for (const auto& [_, intf] : manager.get().interfaces) 812 { 813 if (intf->vlan && intf->vlan->parentIdx == ifIdx) 814 { 815 intf->MacAddressIntf::macAddress(validMAC); 816 } 817 } 818 MacAddressIntf::macAddress(validMAC); 819 820 writeConfigurationFile(); 821 manager.get().addReloadPreHook([interface, path]() { 822 // The MAC and LLADDRs will only update if the NIC is already down 823 system::setNICUp(interface, false); 824 utimensat(AT_FDCWD, path.c_str(), NULL, 0); 825 }); 826 manager.get().reloadConfigs(); 827 } 828 829 #ifdef HAVE_UBOOT_ENV 830 // Ensure that the valid address is stored in the u-boot-env 831 auto envVar = interfaceToUbootEthAddr(interface); 832 if (envVar) 833 { 834 // Trimming MAC addresses that are out of range. eg: AA:FF:FF:FF:FF:100; 835 // and those having more than 6 bytes. eg: AA:AA:AA:AA:AA:AA:BB 836 execute("/sbin/fw_setenv", "fw_setenv", envVar->c_str(), 837 validMAC.c_str()); 838 } 839 #endif // HAVE_UBOOT_ENV 840 841 return value; 842 #else 843 elog<NotAllowed>( 844 NotAllowedArgument::REASON("Writing MAC address is not allowed")); 845 #endif // PERSIST_MAC 846 } 847 848 void EthernetInterface::deleteAll() 849 { 850 // clear all the ip on the interface 851 addrs.clear(); 852 853 writeConfigurationFile(); 854 manager.get().reloadConfigs(); 855 } 856 857 template <typename Addr> 858 static void normalizeGateway(std::string& gw) 859 { 860 if (gw.empty()) 861 { 862 return; 863 } 864 try 865 { 866 auto ip = stdplus::fromStr<Addr>(gw); 867 if (ip == Addr{}) 868 { 869 gw.clear(); 870 return; 871 } 872 if (!validIntfIP(ip)) 873 { 874 throw std::invalid_argument("Invalid unicast"); 875 } 876 gw = stdplus::toStr(ip); 877 } 878 catch (const std::exception& e) 879 { 880 lg2::error("Invalid GW `{NET_GW}`: {ERROR}", "NET_GW", gw, "ERROR", e); 881 elog<InvalidArgument>(Argument::ARGUMENT_NAME("GATEWAY"), 882 Argument::ARGUMENT_VALUE(gw.c_str())); 883 } 884 } 885 886 std::string EthernetInterface::defaultGateway(std::string gateway) 887 { 888 normalizeGateway<stdplus::In4Addr>(gateway); 889 if (gateway != defaultGateway()) 890 { 891 gateway = EthernetInterfaceIntf::defaultGateway(std::move(gateway)); 892 writeConfigurationFile(); 893 manager.get().reloadConfigs(); 894 } 895 return gateway; 896 } 897 898 std::string EthernetInterface::defaultGateway6(std::string gateway) 899 { 900 normalizeGateway<stdplus::In6Addr>(gateway); 901 if (gateway != defaultGateway6()) 902 { 903 gateway = EthernetInterfaceIntf::defaultGateway6(std::move(gateway)); 904 writeConfigurationFile(); 905 manager.get().reloadConfigs(); 906 } 907 return gateway; 908 } 909 910 EthernetInterface::VlanProperties::VlanProperties( 911 sdbusplus::bus_t& bus, stdplus::const_zstring objPath, 912 const InterfaceInfo& info, stdplus::PinnedRef<EthernetInterface> eth) : 913 VlanIfaces(bus, objPath.c_str(), VlanIfaces::action::defer_emit), 914 parentIdx(*info.parent_idx), eth(eth) 915 { 916 VlanIntf::id(*info.vlan_id, true); 917 emit_object_added(); 918 } 919 920 void EthernetInterface::VlanProperties::delete_() 921 { 922 auto intf = eth.get().interfaceName(); 923 924 // Remove all configs for the current interface 925 const auto& confDir = eth.get().manager.get().getConfDir(); 926 std::error_code ec; 927 std::filesystem::remove(config::pathForIntfConf(confDir, intf), ec); 928 std::filesystem::remove(config::pathForIntfDev(confDir, intf), ec); 929 930 if (eth.get().ifIdx > 0) 931 { 932 eth.get().manager.get().interfacesByIdx.erase(eth.get().ifIdx); 933 } 934 auto it = eth.get().manager.get().interfaces.find(intf); 935 auto obj = std::move(it->second); 936 eth.get().manager.get().interfaces.erase(it); 937 938 // Write an updated parent interface since it has a VLAN entry 939 for (const auto& [_, intf] : eth.get().manager.get().interfaces) 940 { 941 if (intf->ifIdx == parentIdx) 942 { 943 intf->writeConfigurationFile(); 944 } 945 } 946 947 if (eth.get().ifIdx > 0) 948 { 949 // We need to forcibly delete the interface as systemd does not 950 eth.get().manager.get().addReloadPostHook( 951 [idx = eth.get().ifIdx]() { system::deleteIntf(idx); }); 952 953 // Ignore the interface so the reload doesn't re-query it 954 eth.get().manager.get().ignoredIntf.emplace(eth.get().ifIdx); 955 } 956 957 eth.get().manager.get().reloadConfigs(); 958 } 959 960 } // namespace network 961 } // namespace phosphor 962