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