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