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