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/if_addr.h> 13 #include <linux/neighbour.h> 14 #include <linux/rtnetlink.h> 15 #include <net/if.h> 16 17 #include <algorithm> 18 #include <filesystem> 19 #include <phosphor-logging/elog-errors.hpp> 20 #include <phosphor-logging/log.hpp> 21 #include <sdbusplus/bus/match.hpp> 22 #include <stdplus/raw.hpp> 23 #include <stdplus/zstring.hpp> 24 #include <string> 25 #include <unordered_map> 26 #include <variant> 27 #include <xyz/openbmc_project/Common/error.hpp> 28 29 namespace phosphor 30 { 31 namespace network 32 { 33 34 using namespace phosphor::logging; 35 using namespace sdbusplus::xyz::openbmc_project::Common::Error; 36 using NotAllowed = sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed; 37 using NotAllowedArgument = xyz::openbmc_project::Common::NotAllowed; 38 using Argument = xyz::openbmc_project::Common::InvalidArgument; 39 using std::literals::string_view_literals::operator""sv; 40 constexpr auto RESOLVED_SERVICE = "org.freedesktop.resolve1"; 41 constexpr auto RESOLVED_INTERFACE = "org.freedesktop.resolve1.Link"; 42 constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties"; 43 constexpr auto RESOLVED_SERVICE_PATH = "/org/freedesktop/resolve1/link/"; 44 45 constexpr auto TIMESYNCD_SERVICE = "org.freedesktop.timesync1"; 46 constexpr auto TIMESYNCD_INTERFACE = "org.freedesktop.timesync1.Manager"; 47 constexpr auto TIMESYNCD_SERVICE_PATH = "/org/freedesktop/timesync1"; 48 49 constexpr auto METHOD_GET = "Get"; 50 51 template <typename Func> 52 inline decltype(std::declval<Func>()()) 53 ignoreError(std::string_view msg, stdplus::zstring_view intf, 54 decltype(std::declval<Func>()()) fallback, Func&& func) noexcept 55 { 56 try 57 { 58 return func(); 59 } 60 catch (const std::exception& e) 61 { 62 auto err = fmt::format("{} failed on {}: {}", msg, intf, e.what()); 63 log<level::ERR>(err.c_str(), entry("INTERFACE=%s", intf.c_str())); 64 } 65 return fallback; 66 } 67 68 static std::string makeObjPath(std::string_view root, std::string_view intf) 69 { 70 auto ret = fmt::format(FMT_COMPILE("{}/{}"), root, intf); 71 std::replace(ret.begin() + ret.size() - intf.size(), ret.end(), '.', '_'); 72 return ret; 73 } 74 75 EthernetInterface::EthernetInterface(sdbusplus::bus_t& bus, Manager& manager, 76 const system::InterfaceInfo& info, 77 std::string_view objRoot, 78 const config::Parser& config, 79 bool emitSignal, 80 std::optional<bool> enabled) : 81 EthernetInterface(bus, manager, info, makeObjPath(objRoot, *info.name), 82 config, emitSignal, enabled) 83 { 84 } 85 86 EthernetInterface::EthernetInterface(sdbusplus::bus_t& bus, Manager& manager, 87 const system::InterfaceInfo& info, 88 std::string&& objPath, 89 const config::Parser& config, 90 bool emitSignal, 91 std::optional<bool> enabled) : 92 Ifaces(bus, objPath.c_str(), 93 emitSignal ? Ifaces::action::defer_emit 94 : Ifaces::action::emit_no_signals), 95 manager(manager), bus(bus), objPath(std::move(objPath)), ifIdx(info.idx) 96 { 97 interfaceName(*info.name); 98 auto dhcpVal = getDHCPValue(config); 99 EthernetInterfaceIntf::dhcp4(dhcpVal.v4); 100 EthernetInterfaceIntf::dhcp6(dhcpVal.v6); 101 EthernetInterfaceIntf::ipv6AcceptRA(getIPv6AcceptRA(config)); 102 EthernetInterfaceIntf::nicEnabled(enabled ? *enabled : queryNicEnabled()); 103 { 104 const auto& gws = manager.getRouteTable().getDefaultGateway(); 105 auto it = gws.find(ifIdx); 106 if (it != gws.end()) 107 { 108 EthernetInterfaceIntf::defaultGateway(std::to_string(it->second)); 109 } 110 } 111 { 112 const auto& gws = manager.getRouteTable().getDefaultGateway6(); 113 auto it = gws.find(ifIdx); 114 if (it != gws.end()) 115 { 116 EthernetInterfaceIntf::defaultGateway6(std::to_string(it->second)); 117 } 118 } 119 120 EthernetInterfaceIntf::ntpServers( 121 config.map.getValueStrings("Network", "NTP")); 122 123 if (ifIdx > 0) 124 { 125 auto ethInfo = ignoreError("GetEthInfo", *info.name, {}, [&] { 126 return system::getEthInfo(*info.name); 127 }); 128 EthernetInterfaceIntf::autoNeg(ethInfo.autoneg); 129 EthernetInterfaceIntf::speed(ethInfo.speed); 130 } 131 132 updateInfo(info); 133 134 if (info.vlan_id) 135 { 136 if (!info.parent_idx) 137 { 138 std::runtime_error("Missing parent link"); 139 } 140 vlan.emplace(bus, this->objPath.c_str(), info, *this, emitSignal); 141 } 142 143 // Emit deferred signal. 144 if (emitSignal) 145 { 146 this->emit_object_added(); 147 } 148 } 149 150 void EthernetInterface::updateInfo(const system::InterfaceInfo& info) 151 { 152 EthernetInterfaceIntf::linkUp(info.flags & IFF_RUNNING); 153 if (info.mac) 154 { 155 MacAddressIntf::macAddress(std::to_string(*info.mac)); 156 } 157 if (info.mtu) 158 { 159 EthernetInterfaceIntf::mtu(*info.mtu); 160 } 161 } 162 163 bool EthernetInterface::originIsManuallyAssigned(IP::AddressOrigin origin) 164 { 165 return ( 166 #ifdef LINK_LOCAL_AUTOCONFIGURATION 167 (origin == IP::AddressOrigin::Static) 168 #else 169 (origin == IP::AddressOrigin::Static || 170 origin == IP::AddressOrigin::LinkLocal) 171 #endif 172 173 ); 174 } 175 176 void EthernetInterface::createIPAddressObjects() 177 { 178 addrs.clear(); 179 180 AddressFilter filter; 181 filter.interface = ifIdx; 182 auto currentAddrs = getCurrentAddresses(filter); 183 for (const auto& addr : currentAddrs) 184 { 185 if (addr.flags & IFA_F_DEPRECATED) 186 { 187 continue; 188 } 189 auto ifaddr = IfAddr(addr.address, addr.prefix); 190 IP::AddressOrigin origin = IP::AddressOrigin::Static; 191 if (dhcpIsEnabled(addr.address)) 192 { 193 origin = IP::AddressOrigin::DHCP; 194 } 195 #ifdef LINK_LOCAL_AUTOCONFIGURATION 196 if (addr.scope == RT_SCOPE_LINK) 197 { 198 origin = IP::AddressOrigin::LinkLocal; 199 } 200 #endif 201 202 this->addrs.insert_or_assign( 203 ifaddr, std::make_unique<IPAddress>(bus, std::string_view(objPath), 204 *this, ifaddr, origin)); 205 } 206 } 207 208 void EthernetInterface::createStaticNeighborObjects() 209 { 210 staticNeighbors.clear(); 211 212 NeighborFilter filter; 213 filter.interface = ifIdx; 214 filter.state = NUD_PERMANENT; 215 auto neighbors = getCurrentNeighbors(filter); 216 for (const auto& neighbor : neighbors) 217 { 218 if (!neighbor.mac) 219 { 220 continue; 221 } 222 staticNeighbors.emplace( 223 neighbor.address, 224 std::make_unique<Neighbor>(bus, std::string_view(objPath), *this, 225 neighbor.address, *neighbor.mac, 226 Neighbor::State::Permanent)); 227 } 228 } 229 230 ObjectPath EthernetInterface::ip(IP::Protocol protType, std::string ipaddress, 231 uint8_t prefixLength, std::string) 232 { 233 InAddrAny addr; 234 try 235 { 236 switch (protType) 237 { 238 case IP::Protocol::IPv4: 239 addr = ToAddr<in_addr>{}(ipaddress); 240 break; 241 case IP::Protocol::IPv6: 242 addr = ToAddr<in6_addr>{}(ipaddress); 243 break; 244 default: 245 throw std::logic_error("Exhausted protocols"); 246 } 247 } 248 catch (const std::exception& e) 249 { 250 auto msg = fmt::format("Invalid IP `{}`: {}\n", ipaddress, e.what()); 251 log<level::ERR>(msg.c_str(), entry("ADDRESS=%s", ipaddress.c_str())); 252 elog<InvalidArgument>(Argument::ARGUMENT_NAME("ipaddress"), 253 Argument::ARGUMENT_VALUE(ipaddress.c_str())); 254 } 255 IfAddr ifaddr; 256 try 257 { 258 ifaddr = {addr, prefixLength}; 259 } 260 catch (const std::exception& e) 261 { 262 auto msg = fmt::format("Invalid prefix length `{}`: {}\n", prefixLength, 263 e.what()); 264 log<level::ERR>(msg.c_str(), 265 entry("PREFIXLENGTH=%" PRIu8, prefixLength)); 266 elog<InvalidArgument>( 267 Argument::ARGUMENT_NAME("prefixLength"), 268 Argument::ARGUMENT_VALUE(std::to_string(prefixLength).c_str())); 269 } 270 271 auto [it, _] = this->addrs.insert_or_assign( 272 ifaddr, 273 std::make_unique<IPAddress>(bus, std::string_view(objPath), *this, 274 ifaddr, IP::AddressOrigin::Static)); 275 276 writeConfigurationFile(); 277 manager.reloadConfigs(); 278 279 return it->second->getObjPath(); 280 } 281 282 ObjectPath EthernetInterface::neighbor(std::string ipAddress, 283 std::string macAddress) 284 { 285 InAddrAny addr; 286 try 287 { 288 addr = ToAddr<InAddrAny>{}(ipAddress); 289 } 290 catch (const std::exception& e) 291 { 292 auto msg = 293 fmt::format("Not a valid IP address `{}`: {}", ipAddress, e.what()); 294 log<level::ERR>(msg.c_str(), entry("ADDRESS=%s", ipAddress.c_str())); 295 elog<InvalidArgument>(Argument::ARGUMENT_NAME("ipAddress"), 296 Argument::ARGUMENT_VALUE(ipAddress.c_str())); 297 } 298 299 ether_addr lladdr; 300 try 301 { 302 lladdr = ToAddr<ether_addr>{}(macAddress); 303 } 304 catch (const std::exception& e) 305 { 306 auto msg = fmt::format("Not a valid MAC address `{}`: {}", macAddress, 307 e.what()); 308 log<level::ERR>(msg.c_str(), 309 entry("MACADDRESS=%s", macAddress.c_str())); 310 elog<InvalidArgument>(Argument::ARGUMENT_NAME("macAddress"), 311 Argument::ARGUMENT_VALUE(macAddress.c_str())); 312 } 313 314 auto [it, _] = staticNeighbors.emplace( 315 addr, 316 std::make_unique<Neighbor>(bus, std::string_view(objPath), *this, addr, 317 lladdr, Neighbor::State::Permanent)); 318 319 writeConfigurationFile(); 320 manager.reloadConfigs(); 321 322 return it->second->getObjPath(); 323 } 324 325 bool EthernetInterface::ipv6AcceptRA(bool value) 326 { 327 if (ipv6AcceptRA() != EthernetInterfaceIntf::ipv6AcceptRA(value)) 328 { 329 writeConfigurationFile(); 330 manager.reloadConfigs(); 331 } 332 return value; 333 } 334 335 bool EthernetInterface::dhcp4(bool value) 336 { 337 if (dhcp4() != EthernetInterfaceIntf::dhcp4(value)) 338 { 339 writeConfigurationFile(); 340 manager.reloadConfigs(); 341 } 342 return value; 343 } 344 345 bool EthernetInterface::dhcp6(bool value) 346 { 347 if (dhcp6() != EthernetInterfaceIntf::dhcp6(value)) 348 { 349 writeConfigurationFile(); 350 manager.reloadConfigs(); 351 } 352 return value; 353 } 354 355 EthernetInterface::DHCPConf EthernetInterface::dhcpEnabled(DHCPConf value) 356 { 357 auto old4 = EthernetInterfaceIntf::dhcp4(); 358 auto new4 = EthernetInterfaceIntf::dhcp4(value == DHCPConf::v4 || 359 value == DHCPConf::v4v6stateless || 360 value == DHCPConf::both); 361 auto old6 = EthernetInterfaceIntf::dhcp6(); 362 auto new6 = EthernetInterfaceIntf::dhcp6(value == DHCPConf::v6 || 363 value == DHCPConf::both); 364 auto oldra = EthernetInterfaceIntf::ipv6AcceptRA(); 365 auto newra = EthernetInterfaceIntf::ipv6AcceptRA( 366 value == DHCPConf::v6stateless || value == DHCPConf::v4v6stateless || 367 value == DHCPConf::v6 || value == DHCPConf::both); 368 369 if (old4 != new4 || old6 != new6 || oldra != newra) 370 { 371 writeConfigurationFile(); 372 manager.reloadConfigs(); 373 } 374 return value; 375 } 376 377 EthernetInterface::DHCPConf EthernetInterface::dhcpEnabled() const 378 { 379 if (dhcp6()) 380 { 381 return dhcp4() ? DHCPConf::both : DHCPConf::v6; 382 } 383 else if (dhcp4()) 384 { 385 return ipv6AcceptRA() ? DHCPConf::v4v6stateless : DHCPConf::v4; 386 } 387 return ipv6AcceptRA() ? DHCPConf::v6stateless : DHCPConf::none; 388 } 389 390 bool EthernetInterface::linkUp() const 391 { 392 if (ifIdx == 0) 393 { 394 return EthernetInterfaceIntf::linkUp(); 395 } 396 return system::intfIsRunning(interfaceName()); 397 } 398 399 size_t EthernetInterface::mtu() const 400 { 401 if (ifIdx == 0) 402 { 403 return EthernetInterfaceIntf::mtu(); 404 } 405 const auto ifname = interfaceName(); 406 return ignoreError("GetMTU", ifname, std::nullopt, 407 [&] { return system::getMTU(ifname); }) 408 .value_or(EthernetInterfaceIntf::mtu()); 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::queryNicEnabled() const 426 { 427 constexpr auto svc = "org.freedesktop.network1"; 428 constexpr auto intf = "org.freedesktop.network1.Link"; 429 constexpr auto prop = "AdministrativeState"; 430 char* rpath; 431 sd_bus_path_encode("/org/freedesktop/network1/link", 432 std::to_string(ifIdx).c_str(), &rpath); 433 std::string path(rpath); 434 free(rpath); 435 436 // Store / Parser for the AdministrativeState return value 437 std::optional<bool> ret; 438 auto cb = [&](std::string_view state) { 439 if (state != "initialized") 440 { 441 ret = state != "unmanaged"; 442 } 443 }; 444 445 // Build a matcher before making the property call to ensure we 446 // can eventually get the value. 447 sdbusplus::bus::match_t match( 448 bus, 449 fmt::format("type='signal',sender='{}',path='{}',interface='{}',member=" 450 "'PropertiesChanged',arg0='{}',", 451 svc, path, PROPERTY_INTERFACE, intf) 452 .c_str(), 453 [&](sdbusplus::message_t& m) { 454 std::string intf; 455 std::unordered_map<std::string, std::variant<std::string>> values; 456 try 457 { 458 m.read(intf, values); 459 auto it = values.find(prop); 460 // Ignore properties that aren't AdministrativeState 461 if (it != values.end()) 462 { 463 cb(std::get<std::string>(it->second)); 464 } 465 } 466 catch (const std::exception& e) 467 { 468 log<level::ERR>( 469 fmt::format( 470 "AdministrativeState match parsing failed on {}: {}", 471 interfaceName(), e.what()) 472 .c_str(), 473 entry("INTERFACE=%s", interfaceName().c_str()), 474 entry("ERROR=%s", e.what())); 475 } 476 }); 477 478 // Actively call for the value in case the interface is already configured 479 auto method = 480 bus.new_method_call(svc, path.c_str(), PROPERTY_INTERFACE, METHOD_GET); 481 method.append(intf, prop); 482 try 483 { 484 auto reply = bus.call(method); 485 std::variant<std::string> state; 486 reply.read(state); 487 cb(std::get<std::string>(state)); 488 } 489 catch (const std::exception& e) 490 { 491 log<level::ERR>( 492 fmt::format("Failed to get AdministrativeState on {}: {}", 493 interfaceName(), e.what()) 494 .c_str(), 495 entry("INTERFACE=%s", interfaceName().c_str()), 496 entry("ERROR=%s", e.what())); 497 } 498 499 // The interface is not yet configured by systemd-networkd, wait until it 500 // signals us a valid state. 501 while (!ret) 502 { 503 bus.wait(); 504 bus.process_discard(); 505 } 506 507 return *ret; 508 } 509 510 bool EthernetInterface::nicEnabled(bool value) 511 { 512 if (value == EthernetInterfaceIntf::nicEnabled()) 513 { 514 return value; 515 } 516 517 EthernetInterfaceIntf::nicEnabled(value); 518 writeConfigurationFile(); 519 if (!value) 520 { 521 // We only need to bring down the interface, networkd will always bring 522 // up managed interfaces 523 manager.addReloadPreHook( 524 [ifname = interfaceName()]() { system::setNICUp(ifname, false); }); 525 } 526 manager.reloadConfigs(); 527 528 return value; 529 } 530 531 ServerList EthernetInterface::staticNameServers(ServerList value) 532 { 533 for (auto& ip : value) 534 { 535 try 536 { 537 ip = std::to_string(ToAddr<InAddrAny>{}(ip)); 538 } 539 catch (const std::exception& e) 540 { 541 auto msg = 542 fmt::format("Not a valid IP address `{}`: {}", ip, e.what()); 543 log<level::ERR>(msg.c_str()), entry("ADDRESS=%s", ip.c_str()); 544 elog<InvalidArgument>(Argument::ARGUMENT_NAME("StaticNameserver"), 545 Argument::ARGUMENT_VALUE(ip.c_str())); 546 } 547 } 548 try 549 { 550 EthernetInterfaceIntf::staticNameServers(value); 551 552 writeConfigurationFile(); 553 manager.reloadConfigs(); 554 } 555 catch (const InternalFailure& e) 556 { 557 log<level::ERR>("Exception processing DNS entries"); 558 } 559 return EthernetInterfaceIntf::staticNameServers(); 560 } 561 562 void EthernetInterface::loadNTPServers(const config::Parser& config) 563 { 564 EthernetInterfaceIntf::ntpServers(getNTPServerFromTimeSyncd()); 565 EthernetInterfaceIntf::staticNTPServers( 566 config.map.getValueStrings("Network", "NTP")); 567 } 568 569 void EthernetInterface::loadNameServers(const config::Parser& config) 570 { 571 EthernetInterfaceIntf::nameservers(getNameServerFromResolvd()); 572 EthernetInterfaceIntf::staticNameServers( 573 config.map.getValueStrings("Network", "DNS")); 574 } 575 576 ServerList EthernetInterface::getNTPServerFromTimeSyncd() 577 { 578 ServerList servers; // Variable to capture the NTP Server IPs 579 auto method = bus.new_method_call(TIMESYNCD_SERVICE, TIMESYNCD_SERVICE_PATH, 580 PROPERTY_INTERFACE, METHOD_GET); 581 582 method.append(TIMESYNCD_INTERFACE, "LinkNTPServers"); 583 584 try 585 { 586 auto reply = bus.call(method); 587 std::variant<ServerList> response; 588 reply.read(response); 589 servers = std::get<ServerList>(response); 590 } 591 catch (const sdbusplus::exception::SdBusError& e) 592 { 593 log<level::ERR>( 594 "Failed to get NTP server information from Systemd-Timesyncd"); 595 } 596 597 return servers; 598 } 599 600 ServerList EthernetInterface::getNameServerFromResolvd() 601 { 602 ServerList servers; 603 auto OBJ_PATH = fmt::format("{}{}", RESOLVED_SERVICE_PATH, ifIdx); 604 605 /* 606 The DNS property under org.freedesktop.resolve1.Link interface contains 607 an array containing all DNS servers currently used by resolved. It 608 contains similar information as the DNS server data written to 609 /run/systemd/resolve/resolv.conf. 610 611 Each structure in the array consists of a numeric network interface index, 612 an address family, and a byte array containing the DNS server address 613 (either 4 bytes in length for IPv4 or 16 bytes in lengths for IPv6). 614 The array contains DNS servers configured system-wide, including those 615 possibly read from a foreign /etc/resolv.conf or the DNS= setting in 616 /etc/systemd/resolved.conf, as well as per-interface DNS server 617 information either retrieved from systemd-networkd or configured by 618 external software via SetLinkDNS(). 619 */ 620 621 using type = std::vector<std::tuple<int32_t, std::vector<uint8_t>>>; 622 std::variant<type> name; // Variable to capture the DNS property 623 auto method = bus.new_method_call(RESOLVED_SERVICE, OBJ_PATH.c_str(), 624 PROPERTY_INTERFACE, METHOD_GET); 625 626 method.append(RESOLVED_INTERFACE, "DNS"); 627 628 try 629 { 630 auto reply = bus.call(method); 631 reply.read(name); 632 } 633 catch (const sdbusplus::exception_t& e) 634 { 635 log<level::ERR>("Failed to get DNS information from Systemd-Resolved"); 636 } 637 auto tupleVector = std::get_if<type>(&name); 638 for (auto i = tupleVector->begin(); i != tupleVector->end(); ++i) 639 { 640 int addressFamily = std::get<0>(*i); 641 std::vector<uint8_t>& ipaddress = std::get<1>(*i); 642 servers.push_back(std::to_string( 643 addrFromBuf(addressFamily, stdplus::raw::asView<char>(ipaddress)))); 644 } 645 return servers; 646 } 647 648 ObjectPath EthernetInterface::createVLAN(uint16_t id) 649 { 650 auto intfName = fmt::format(FMT_COMPILE("{}.{}"), interfaceName(), id); 651 auto idStr = std::to_string(id); 652 if (manager.interfaces.find(intfName) != manager.interfaces.end()) 653 { 654 log<level::ERR>("VLAN already exists", entry("VLANID=%u", id)); 655 elog<InvalidArgument>(Argument::ARGUMENT_NAME("VLANId"), 656 Argument::ARGUMENT_VALUE(idStr.c_str())); 657 } 658 659 auto objRoot = std::string_view(objPath).substr(0, objPath.rfind('/')); 660 auto macStr = MacAddressIntf::macAddress(); 661 std::optional<ether_addr> mac; 662 if (!macStr.empty()) 663 { 664 mac.emplace(ToAddr<ether_addr>{}(macStr)); 665 } 666 auto info = system::InterfaceInfo{ 667 .idx = 0, // TODO: Query the correct value after creation 668 .flags = 0, 669 .name = intfName, 670 .mac = std::move(mac), 671 .mtu = mtu(), 672 .parent_idx = ifIdx, 673 .vlan_id = id, 674 }; 675 676 // Pass the parents nicEnabled property, so that the child 677 // VLAN interface can inherit. 678 auto vlanIntf = std::make_unique<EthernetInterface>( 679 bus, manager, info, objRoot, config::Parser(), /*emit=*/true, 680 nicEnabled()); 681 ObjectPath ret = vlanIntf->objPath; 682 683 manager.interfaces.emplace(intfName, std::move(vlanIntf)); 684 685 // write the device file for the vlan interface. 686 config::Parser config; 687 auto& netdev = config.map["NetDev"].emplace_back(); 688 netdev["Name"].emplace_back(intfName); 689 netdev["Kind"].emplace_back("vlan"); 690 config.map["VLAN"].emplace_back()["Id"].emplace_back(std::move(idStr)); 691 config.writeFile(config::pathForIntfDev(manager.getConfDir(), intfName)); 692 693 writeConfigurationFile(); 694 manager.reloadConfigs(); 695 696 return objPath; 697 } 698 699 ServerList EthernetInterface::staticNTPServers(ServerList value) 700 { 701 try 702 { 703 EthernetInterfaceIntf::staticNTPServers(value); 704 705 writeConfigurationFile(); 706 manager.reloadConfigs(); 707 } 708 catch (InternalFailure& e) 709 { 710 log<level::ERR>("Exception processing NTP entries"); 711 } 712 return EthernetInterfaceIntf::staticNTPServers(); 713 } 714 715 ServerList EthernetInterface::ntpServers(ServerList /*servers*/) 716 { 717 elog<NotAllowed>(NotAllowedArgument::REASON("ReadOnly Property")); 718 } 719 // Need to merge the below function with the code which writes the 720 // config file during factory reset. 721 // TODO openbmc/openbmc#1751 722 723 void EthernetInterface::writeConfigurationFile() 724 { 725 config::Parser config; 726 config.map["Match"].emplace_back()["Name"].emplace_back(interfaceName()); 727 { 728 auto& link = config.map["Link"].emplace_back(); 729 #ifdef PERSIST_MAC 730 auto mac = MacAddressIntf::macAddress(); 731 if (!mac.empty()) 732 { 733 link["MACAddress"].emplace_back(mac); 734 } 735 #endif 736 if (!EthernetInterfaceIntf::nicEnabled()) 737 { 738 link["Unmanaged"].emplace_back("yes"); 739 } 740 } 741 { 742 auto& network = config.map["Network"].emplace_back(); 743 auto& lla = network["LinkLocalAddressing"]; 744 #ifdef LINK_LOCAL_AUTOCONFIGURATION 745 lla.emplace_back("yes"); 746 #else 747 lla.emplace_back("no"); 748 #endif 749 network["IPv6AcceptRA"].emplace_back(ipv6AcceptRA() ? "true" : "false"); 750 network["DHCP"].emplace_back(dhcp4() ? (dhcp6() ? "true" : "ipv4") 751 : (dhcp6() ? "ipv6" : "false")); 752 { 753 auto& vlans = network["VLAN"]; 754 for (const auto& [_, intf] : manager.interfaces) 755 { 756 if (intf->vlan && intf->vlan->parentIdx == ifIdx) 757 { 758 vlans.emplace_back(intf->interfaceName()); 759 } 760 } 761 } 762 { 763 auto& ntps = network["NTP"]; 764 for (const auto& ntp : EthernetInterfaceIntf::staticNTPServers()) 765 { 766 ntps.emplace_back(ntp); 767 } 768 } 769 { 770 auto& dnss = network["DNS"]; 771 for (const auto& dns : EthernetInterfaceIntf::staticNameServers()) 772 { 773 dnss.emplace_back(dns); 774 } 775 } 776 { 777 auto& address = network["Address"]; 778 for (const auto& addr : addrs) 779 { 780 if (originIsManuallyAssigned(addr.second->origin())) 781 { 782 address.emplace_back( 783 fmt::format("{}/{}", addr.second->address(), 784 addr.second->prefixLength())); 785 } 786 } 787 } 788 { 789 auto& gateways = network["Gateway"]; 790 if (!dhcp4()) 791 { 792 auto gateway = EthernetInterfaceIntf::defaultGateway(); 793 if (!gateway.empty()) 794 { 795 gateways.emplace_back(gateway); 796 } 797 } 798 799 if (!dhcp6()) 800 { 801 auto gateway6 = EthernetInterfaceIntf::defaultGateway6(); 802 if (!gateway6.empty()) 803 { 804 gateways.emplace_back(gateway6); 805 } 806 } 807 } 808 } 809 config.map["IPv6AcceptRA"].emplace_back()["DHCPv6Client"].emplace_back( 810 dhcp6() ? "true" : "false"); 811 { 812 auto& neighbors = config.map["Neighbor"]; 813 for (const auto& sneighbor : staticNeighbors) 814 { 815 auto& neighbor = neighbors.emplace_back(); 816 neighbor["Address"].emplace_back(sneighbor.second->ipAddress()); 817 neighbor["MACAddress"].emplace_back(sneighbor.second->macAddress()); 818 } 819 } 820 { 821 auto& dhcp = config.map["DHCP"].emplace_back(); 822 dhcp["ClientIdentifier"].emplace_back("mac"); 823 if (manager.getDHCPConf()) 824 { 825 const auto& conf = *manager.getDHCPConf(); 826 auto dns_enabled = conf.dnsEnabled() ? "true" : "false"; 827 dhcp["UseDNS"].emplace_back(dns_enabled); 828 dhcp["UseDomains"].emplace_back(dns_enabled); 829 dhcp["UseNTP"].emplace_back(conf.ntpEnabled() ? "true" : "false"); 830 dhcp["UseHostname"].emplace_back(conf.hostNameEnabled() ? "true" 831 : "false"); 832 dhcp["SendHostname"].emplace_back( 833 conf.sendHostNameEnabled() ? "true" : "false"); 834 } 835 } 836 auto path = config::pathForIntfConf(manager.getConfDir(), interfaceName()); 837 config.writeFile(path); 838 auto msg = fmt::format("Wrote networkd file: {}", path.native()); 839 log<level::INFO>(msg.c_str(), entry("FILE=%s", path.c_str())); 840 } 841 842 std::string EthernetInterface::macAddress([[maybe_unused]] std::string value) 843 { 844 if (vlan) 845 { 846 log<level::ERR>("Tried to set MAC address on VLAN"); 847 elog<InternalFailure>(); 848 } 849 #ifdef PERSIST_MAC 850 ether_addr newMAC; 851 try 852 { 853 newMAC = ToAddr<ether_addr>{}(value); 854 } 855 catch (const std::invalid_argument&) 856 { 857 log<level::ERR>("MACAddress is not valid.", 858 entry("MAC=%s", value.c_str())); 859 elog<InvalidArgument>(Argument::ARGUMENT_NAME("MACAddress"), 860 Argument::ARGUMENT_VALUE(value.c_str())); 861 } 862 if (!mac_address::isUnicast(newMAC)) 863 { 864 log<level::ERR>("MACAddress is not valid.", 865 entry("MAC=%s", value.c_str())); 866 elog<InvalidArgument>(Argument::ARGUMENT_NAME("MACAddress"), 867 Argument::ARGUMENT_VALUE(value.c_str())); 868 } 869 870 auto interface = interfaceName(); 871 auto validMAC = std::to_string(newMAC); 872 873 // We don't need to update the system if the address is unchanged 874 ether_addr oldMAC = ToAddr<ether_addr>{}(MacAddressIntf::macAddress()); 875 if (newMAC != oldMAC) 876 { 877 // Update everything that depends on the MAC value 878 for (const auto& [_, intf] : manager.interfaces) 879 { 880 if (intf->vlan && intf->vlan->parentIdx == ifIdx) 881 { 882 intf->MacAddressIntf::macAddress(validMAC); 883 } 884 } 885 MacAddressIntf::macAddress(validMAC); 886 887 writeConfigurationFile(); 888 manager.addReloadPreHook([interface]() { 889 // The MAC and LLADDRs will only update if the NIC is already down 890 system::setNICUp(interface, false); 891 }); 892 manager.reloadConfigs(); 893 } 894 895 #ifdef HAVE_UBOOT_ENV 896 // Ensure that the valid address is stored in the u-boot-env 897 auto envVar = interfaceToUbootEthAddr(interface); 898 if (envVar) 899 { 900 // Trimming MAC addresses that are out of range. eg: AA:FF:FF:FF:FF:100; 901 // and those having more than 6 bytes. eg: AA:AA:AA:AA:AA:AA:BB 902 execute("/sbin/fw_setenv", "fw_setenv", envVar->c_str(), 903 validMAC.c_str()); 904 } 905 #endif // HAVE_UBOOT_ENV 906 907 return value; 908 #else 909 elog<NotAllowed>( 910 NotAllowedArgument::REASON("Writing MAC address is not allowed")); 911 #endif // PERSIST_MAC 912 } 913 914 void EthernetInterface::deleteAll() 915 { 916 // clear all the ip on the interface 917 addrs.clear(); 918 919 writeConfigurationFile(); 920 manager.reloadConfigs(); 921 } 922 923 std::string EthernetInterface::defaultGateway(std::string gateway) 924 { 925 try 926 { 927 if (!gateway.empty()) 928 { 929 gateway = std::to_string(ToAddr<in_addr>{}(gateway)); 930 } 931 } 932 catch (const std::exception& e) 933 { 934 auto msg = fmt::format("Invalid v4 GW `{}`: {}", gateway, e.what()); 935 log<level::ERR>(msg.c_str(), entry("GATEWAY=%s", gateway.c_str())); 936 elog<InvalidArgument>(Argument::ARGUMENT_NAME("GATEWAY"), 937 Argument::ARGUMENT_VALUE(gateway.c_str())); 938 } 939 940 if (EthernetInterfaceIntf::defaultGateway() == gateway) 941 { 942 return gateway; 943 } 944 EthernetInterfaceIntf::defaultGateway(gateway); 945 946 writeConfigurationFile(); 947 manager.reloadConfigs(); 948 949 return gateway; 950 } 951 952 std::string EthernetInterface::defaultGateway6(std::string gateway) 953 { 954 try 955 { 956 if (!gateway.empty()) 957 { 958 gateway = std::to_string(ToAddr<in6_addr>{}(gateway)); 959 } 960 } 961 catch (const std::exception& e) 962 { 963 auto msg = fmt::format("Invalid v6 GW `{}`: {}", gateway, e.what()); 964 log<level::ERR>(msg.c_str(), entry("GATEWAY=%s", gateway.c_str())); 965 elog<InvalidArgument>(Argument::ARGUMENT_NAME("GATEWAY"), 966 Argument::ARGUMENT_VALUE(gateway.c_str())); 967 } 968 969 if (EthernetInterfaceIntf::defaultGateway6() == gateway) 970 { 971 return gateway; 972 } 973 EthernetInterfaceIntf::defaultGateway6(gateway); 974 975 writeConfigurationFile(); 976 manager.reloadConfigs(); 977 978 return gateway; 979 } 980 981 EthernetInterface::VlanProperties::VlanProperties( 982 sdbusplus::bus_t& bus, stdplus::const_zstring objPath, 983 const system::InterfaceInfo& info, EthernetInterface& eth, 984 bool emitSignal) : 985 VlanIfaces(bus, objPath.c_str(), 986 emitSignal ? VlanIfaces::action::defer_emit 987 : VlanIfaces::action::emit_no_signals), 988 parentIdx(*info.parent_idx), eth(eth) 989 { 990 VlanIntf::id(*info.vlan_id); 991 if (emitSignal) 992 { 993 this->emit_object_added(); 994 } 995 } 996 997 void EthernetInterface::VlanProperties::delete_() 998 { 999 auto intf = eth.interfaceName(); 1000 1001 // Remove all configs for the current interface 1002 const auto& confDir = eth.manager.getConfDir(); 1003 std::error_code ec; 1004 std::filesystem::remove(config::pathForIntfConf(confDir, intf), ec); 1005 std::filesystem::remove(config::pathForIntfDev(confDir, intf), ec); 1006 1007 // Write an updated parent interface since it has a VLAN entry 1008 for (const auto& [_, intf] : eth.manager.interfaces) 1009 { 1010 if (intf->ifIdx == parentIdx) 1011 { 1012 intf->writeConfigurationFile(); 1013 } 1014 } 1015 1016 // We need to forcibly delete the interface as systemd does not 1017 deleteInterface(intf); 1018 1019 if (eth.ifIdx > 0) 1020 { 1021 eth.manager.interfacesByIdx.erase(eth.ifIdx); 1022 } 1023 eth.manager.interfaces.erase(intf); 1024 } 1025 1026 } // namespace network 1027 } // namespace phosphor 1028