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