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