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