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