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