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 (auto& ip : value) 567 { 568 try 569 { 570 ip = std::to_string(ToAddr<InAddrAny>{}(ip)); 571 } 572 catch (const std::exception& e) 573 { 574 auto msg = 575 fmt::format("Not a valid IP address `{}`: {}", ip, e.what()); 576 log<level::ERR>(msg.c_str()), entry("ADDRESS=%s", ip.c_str()); 577 elog<InvalidArgument>(Argument::ARGUMENT_NAME("StaticNameserver"), 578 Argument::ARGUMENT_VALUE(ip.c_str())); 579 } 580 } 581 try 582 { 583 EthernetInterfaceIntf::staticNameServers(value); 584 585 writeConfigurationFile(); 586 manager.reloadConfigs(); 587 } 588 catch (const InternalFailure& e) 589 { 590 log<level::ERR>("Exception processing DNS entries"); 591 } 592 return EthernetInterfaceIntf::staticNameServers(); 593 } 594 595 void EthernetInterface::loadNTPServers(const config::Parser& config) 596 { 597 EthernetInterfaceIntf::ntpServers(getNTPServerFromTimeSyncd()); 598 EthernetInterfaceIntf::staticNTPServers( 599 config.map.getValueStrings("Network", "NTP")); 600 } 601 602 void EthernetInterface::loadNameServers(const config::Parser& config) 603 { 604 EthernetInterfaceIntf::nameservers(getNameServerFromResolvd()); 605 EthernetInterfaceIntf::staticNameServers( 606 config.map.getValueStrings("Network", "DNS")); 607 } 608 609 ServerList EthernetInterface::getNTPServerFromTimeSyncd() 610 { 611 ServerList servers; // Variable to capture the NTP Server IPs 612 auto method = bus.new_method_call(TIMESYNCD_SERVICE, TIMESYNCD_SERVICE_PATH, 613 PROPERTY_INTERFACE, METHOD_GET); 614 615 method.append(TIMESYNCD_INTERFACE, "LinkNTPServers"); 616 617 try 618 { 619 auto reply = bus.call(method); 620 std::variant<ServerList> response; 621 reply.read(response); 622 servers = std::get<ServerList>(response); 623 } 624 catch (const sdbusplus::exception::SdBusError& e) 625 { 626 log<level::ERR>( 627 "Failed to get NTP server information from Systemd-Timesyncd"); 628 } 629 630 return servers; 631 } 632 633 ServerList EthernetInterface::getNameServerFromResolvd() 634 { 635 ServerList servers; 636 auto OBJ_PATH = fmt::format("{}{}", RESOLVED_SERVICE_PATH, ifIdx); 637 638 /* 639 The DNS property under org.freedesktop.resolve1.Link interface contains 640 an array containing all DNS servers currently used by resolved. It 641 contains similar information as the DNS server data written to 642 /run/systemd/resolve/resolv.conf. 643 644 Each structure in the array consists of a numeric network interface index, 645 an address family, and a byte array containing the DNS server address 646 (either 4 bytes in length for IPv4 or 16 bytes in lengths for IPv6). 647 The array contains DNS servers configured system-wide, including those 648 possibly read from a foreign /etc/resolv.conf or the DNS= setting in 649 /etc/systemd/resolved.conf, as well as per-interface DNS server 650 information either retrieved from systemd-networkd or configured by 651 external software via SetLinkDNS(). 652 */ 653 654 using type = std::vector<std::tuple<int32_t, std::vector<uint8_t>>>; 655 std::variant<type> name; // Variable to capture the DNS property 656 auto method = bus.new_method_call(RESOLVED_SERVICE, OBJ_PATH.c_str(), 657 PROPERTY_INTERFACE, METHOD_GET); 658 659 method.append(RESOLVED_INTERFACE, "DNS"); 660 661 try 662 { 663 auto reply = bus.call(method); 664 reply.read(name); 665 } 666 catch (const sdbusplus::exception_t& e) 667 { 668 log<level::ERR>("Failed to get DNS information from Systemd-Resolved"); 669 } 670 auto tupleVector = std::get_if<type>(&name); 671 for (auto i = tupleVector->begin(); i != tupleVector->end(); ++i) 672 { 673 int addressFamily = std::get<0>(*i); 674 std::vector<uint8_t>& ipaddress = std::get<1>(*i); 675 servers.push_back(std::to_string( 676 addrFromBuf(addressFamily, stdplus::raw::asView<char>(ipaddress)))); 677 } 678 return servers; 679 } 680 681 ObjectPath EthernetInterface::createVLAN(uint16_t id) 682 { 683 auto intfName = fmt::format(FMT_COMPILE("{}.{}"), interfaceName(), id); 684 auto idStr = std::to_string(id); 685 if (manager.interfaces.find(intfName) != manager.interfaces.end()) 686 { 687 log<level::ERR>("VLAN already exists", entry("VLANID=%u", id)); 688 elog<InvalidArgument>(Argument::ARGUMENT_NAME("VLANId"), 689 Argument::ARGUMENT_VALUE(idStr.c_str())); 690 } 691 692 auto objRoot = std::string_view(objPath).substr(0, objPath.rfind('/')); 693 auto macStr = MacAddressIntf::macAddress(); 694 std::optional<ether_addr> mac; 695 if (!macStr.empty()) 696 { 697 mac.emplace(ToAddr<ether_addr>{}(macStr)); 698 } 699 auto info = system::InterfaceInfo{ 700 .idx = 0, // TODO: Query the correct value after creation 701 .flags = 0, 702 .name = intfName, 703 .mac = std::move(mac), 704 .mtu = mtu(), 705 .parent_idx = ifIdx, 706 .vlan_id = id, 707 }; 708 709 // Pass the parents nicEnabled property, so that the child 710 // VLAN interface can inherit. 711 auto vlanIntf = std::make_unique<EthernetInterface>( 712 bus, manager, info, objRoot, config::Parser(), /*emit=*/true, 713 nicEnabled()); 714 ObjectPath ret = vlanIntf->objPath; 715 716 manager.interfaces.emplace(intfName, std::move(vlanIntf)); 717 718 // write the device file for the vlan interface. 719 config::Parser config; 720 auto& netdev = config.map["NetDev"].emplace_back(); 721 netdev["Name"].emplace_back(intfName); 722 netdev["Kind"].emplace_back("vlan"); 723 config.map["VLAN"].emplace_back()["Id"].emplace_back(std::move(idStr)); 724 config.writeFile(config::pathForIntfDev(manager.getConfDir(), intfName)); 725 726 writeConfigurationFile(); 727 manager.reloadConfigs(); 728 729 return objPath; 730 } 731 732 ServerList EthernetInterface::staticNTPServers(ServerList value) 733 { 734 try 735 { 736 EthernetInterfaceIntf::staticNTPServers(value); 737 738 writeConfigurationFile(); 739 manager.reloadConfigs(); 740 } 741 catch (InternalFailure& e) 742 { 743 log<level::ERR>("Exception processing NTP entries"); 744 } 745 return EthernetInterfaceIntf::staticNTPServers(); 746 } 747 748 ServerList EthernetInterface::ntpServers(ServerList /*servers*/) 749 { 750 elog<NotAllowed>(NotAllowedArgument::REASON("ReadOnly Property")); 751 } 752 // Need to merge the below function with the code which writes the 753 // config file during factory reset. 754 // TODO openbmc/openbmc#1751 755 756 void EthernetInterface::writeConfigurationFile() 757 { 758 config::Parser config; 759 config.map["Match"].emplace_back()["Name"].emplace_back(interfaceName()); 760 { 761 auto& link = config.map["Link"].emplace_back(); 762 #ifdef PERSIST_MAC 763 auto mac = MacAddressIntf::macAddress(); 764 if (!mac.empty()) 765 { 766 link["MACAddress"].emplace_back(mac); 767 } 768 #endif 769 if (!EthernetInterfaceIntf::nicEnabled()) 770 { 771 link["Unmanaged"].emplace_back("yes"); 772 } 773 } 774 { 775 auto& network = config.map["Network"].emplace_back(); 776 auto& lla = network["LinkLocalAddressing"]; 777 #ifdef LINK_LOCAL_AUTOCONFIGURATION 778 lla.emplace_back("yes"); 779 #else 780 lla.emplace_back("no"); 781 #endif 782 network["IPv6AcceptRA"].emplace_back(ipv6AcceptRA() ? "true" : "false"); 783 network["DHCP"].emplace_back(dhcp4() ? (dhcp6() ? "true" : "ipv4") 784 : (dhcp6() ? "ipv6" : "false")); 785 { 786 auto& vlans = network["VLAN"]; 787 for (const auto& [_, intf] : manager.interfaces) 788 { 789 if (intf->vlan && intf->vlan->parentIdx == ifIdx) 790 { 791 vlans.emplace_back(intf->interfaceName()); 792 } 793 } 794 } 795 { 796 auto& ntps = network["NTP"]; 797 for (const auto& ntp : EthernetInterfaceIntf::staticNTPServers()) 798 { 799 ntps.emplace_back(ntp); 800 } 801 } 802 { 803 auto& dnss = network["DNS"]; 804 for (const auto& dns : EthernetInterfaceIntf::staticNameServers()) 805 { 806 dnss.emplace_back(dns); 807 } 808 } 809 { 810 auto& address = network["Address"]; 811 for (const auto& addr : addrs) 812 { 813 if (originIsManuallyAssigned(addr.second->origin())) 814 { 815 address.emplace_back( 816 fmt::format("{}/{}", addr.second->address(), 817 addr.second->prefixLength())); 818 } 819 } 820 } 821 { 822 auto& gateways = network["Gateway"]; 823 if (!dhcp4()) 824 { 825 auto gateway = EthernetInterfaceIntf::defaultGateway(); 826 if (!gateway.empty()) 827 { 828 gateways.emplace_back(gateway); 829 } 830 } 831 832 if (!dhcp6()) 833 { 834 auto gateway6 = EthernetInterfaceIntf::defaultGateway6(); 835 if (!gateway6.empty()) 836 { 837 gateways.emplace_back(gateway6); 838 } 839 } 840 } 841 } 842 config.map["IPv6AcceptRA"].emplace_back()["DHCPv6Client"].emplace_back( 843 dhcp6() ? "true" : "false"); 844 { 845 auto& neighbors = config.map["Neighbor"]; 846 for (const auto& sneighbor : staticNeighbors) 847 { 848 auto& neighbor = neighbors.emplace_back(); 849 neighbor["Address"].emplace_back(sneighbor.second->ipAddress()); 850 neighbor["MACAddress"].emplace_back(sneighbor.second->macAddress()); 851 } 852 } 853 { 854 auto& dhcp = config.map["DHCP"].emplace_back(); 855 dhcp["ClientIdentifier"].emplace_back("mac"); 856 if (manager.getDHCPConf()) 857 { 858 const auto& conf = *manager.getDHCPConf(); 859 auto dns_enabled = conf.dnsEnabled() ? "true" : "false"; 860 dhcp["UseDNS"].emplace_back(dns_enabled); 861 dhcp["UseDomains"].emplace_back(dns_enabled); 862 dhcp["UseNTP"].emplace_back(conf.ntpEnabled() ? "true" : "false"); 863 dhcp["UseHostname"].emplace_back(conf.hostNameEnabled() ? "true" 864 : "false"); 865 dhcp["SendHostname"].emplace_back( 866 conf.sendHostNameEnabled() ? "true" : "false"); 867 } 868 } 869 auto path = config::pathForIntfConf(manager.getConfDir(), interfaceName()); 870 config.writeFile(path); 871 auto msg = fmt::format("Wrote networkd file: {}", path.native()); 872 log<level::INFO>(msg.c_str(), entry("FILE=%s", path.c_str())); 873 } 874 875 std::string EthernetInterface::macAddress([[maybe_unused]] std::string value) 876 { 877 if (vlan) 878 { 879 log<level::ERR>("Tried to set MAC address on VLAN"); 880 elog<InternalFailure>(); 881 } 882 #ifdef PERSIST_MAC 883 ether_addr newMAC; 884 try 885 { 886 newMAC = ToAddr<ether_addr>{}(value); 887 } 888 catch (const std::invalid_argument&) 889 { 890 log<level::ERR>("MACAddress is not valid.", 891 entry("MAC=%s", value.c_str())); 892 elog<InvalidArgument>(Argument::ARGUMENT_NAME("MACAddress"), 893 Argument::ARGUMENT_VALUE(value.c_str())); 894 } 895 if (!mac_address::isUnicast(newMAC)) 896 { 897 log<level::ERR>("MACAddress is not valid.", 898 entry("MAC=%s", value.c_str())); 899 elog<InvalidArgument>(Argument::ARGUMENT_NAME("MACAddress"), 900 Argument::ARGUMENT_VALUE(value.c_str())); 901 } 902 903 auto interface = interfaceName(); 904 auto validMAC = std::to_string(newMAC); 905 906 // We don't need to update the system if the address is unchanged 907 ether_addr oldMAC = ToAddr<ether_addr>{}(MacAddressIntf::macAddress()); 908 if (newMAC != oldMAC) 909 { 910 // Update everything that depends on the MAC value 911 for (const auto& [_, intf] : manager.interfaces) 912 { 913 if (intf->vlan && intf->vlan->parentIdx == ifIdx) 914 { 915 intf->MacAddressIntf::macAddress(validMAC); 916 } 917 } 918 MacAddressIntf::macAddress(validMAC); 919 920 writeConfigurationFile(); 921 manager.addReloadPreHook([interface]() { 922 // The MAC and LLADDRs will only update if the NIC is already down 923 system::setNICUp(interface, false); 924 }); 925 manager.reloadConfigs(); 926 } 927 928 #ifdef HAVE_UBOOT_ENV 929 // Ensure that the valid address is stored in the u-boot-env 930 auto envVar = interfaceToUbootEthAddr(interface); 931 if (envVar) 932 { 933 // Trimming MAC addresses that are out of range. eg: AA:FF:FF:FF:FF:100; 934 // and those having more than 6 bytes. eg: AA:AA:AA:AA:AA:AA:BB 935 execute("/sbin/fw_setenv", "fw_setenv", envVar->c_str(), 936 validMAC.c_str()); 937 } 938 #endif // HAVE_UBOOT_ENV 939 940 return value; 941 #else 942 elog<NotAllowed>( 943 NotAllowedArgument::REASON("Writing MAC address is not allowed")); 944 #endif // PERSIST_MAC 945 } 946 947 void EthernetInterface::deleteAll() 948 { 949 // clear all the ip on the interface 950 addrs.clear(); 951 952 writeConfigurationFile(); 953 manager.reloadConfigs(); 954 } 955 956 std::string EthernetInterface::defaultGateway(std::string gateway) 957 { 958 try 959 { 960 if (!gateway.empty()) 961 { 962 gateway = std::to_string(ToAddr<in_addr>{}(gateway)); 963 } 964 } 965 catch (const std::exception& e) 966 { 967 auto msg = fmt::format("Invalid v4 GW `{}`: {}", gateway, e.what()); 968 log<level::ERR>(msg.c_str(), entry("GATEWAY=%s", gateway.c_str())); 969 elog<InvalidArgument>(Argument::ARGUMENT_NAME("GATEWAY"), 970 Argument::ARGUMENT_VALUE(gateway.c_str())); 971 } 972 973 if (EthernetInterfaceIntf::defaultGateway() == gateway) 974 { 975 return gateway; 976 } 977 EthernetInterfaceIntf::defaultGateway(gateway); 978 979 writeConfigurationFile(); 980 manager.reloadConfigs(); 981 982 return gateway; 983 } 984 985 std::string EthernetInterface::defaultGateway6(std::string gateway) 986 { 987 try 988 { 989 if (!gateway.empty()) 990 { 991 gateway = std::to_string(ToAddr<in6_addr>{}(gateway)); 992 } 993 } 994 catch (const std::exception& e) 995 { 996 auto msg = fmt::format("Invalid v6 GW `{}`: {}", gateway, e.what()); 997 log<level::ERR>(msg.c_str(), entry("GATEWAY=%s", gateway.c_str())); 998 elog<InvalidArgument>(Argument::ARGUMENT_NAME("GATEWAY"), 999 Argument::ARGUMENT_VALUE(gateway.c_str())); 1000 } 1001 1002 if (EthernetInterfaceIntf::defaultGateway6() == gateway) 1003 { 1004 return gateway; 1005 } 1006 EthernetInterfaceIntf::defaultGateway6(gateway); 1007 1008 writeConfigurationFile(); 1009 manager.reloadConfigs(); 1010 1011 return gateway; 1012 } 1013 1014 EthernetInterface::VlanProperties::VlanProperties( 1015 sdbusplus::bus_t& bus, stdplus::const_zstring objPath, 1016 const system::InterfaceInfo& info, EthernetInterface& eth, 1017 bool emitSignal) : 1018 VlanIfaces(bus, objPath.c_str(), 1019 emitSignal ? VlanIfaces::action::defer_emit 1020 : VlanIfaces::action::emit_no_signals), 1021 parentIdx(*info.parent_idx), eth(eth) 1022 { 1023 VlanIntf::id(*info.vlan_id); 1024 if (emitSignal) 1025 { 1026 this->emit_object_added(); 1027 } 1028 } 1029 1030 void EthernetInterface::VlanProperties::delete_() 1031 { 1032 auto intf = eth.interfaceName(); 1033 1034 // Remove all configs for the current interface 1035 const auto& confDir = eth.manager.getConfDir(); 1036 std::error_code ec; 1037 std::filesystem::remove(config::pathForIntfConf(confDir, intf), ec); 1038 std::filesystem::remove(config::pathForIntfDev(confDir, intf), ec); 1039 1040 // Write an updated parent interface since it has a VLAN entry 1041 for (const auto& [_, intf] : eth.manager.interfaces) 1042 { 1043 if (intf->ifIdx == parentIdx) 1044 { 1045 intf->writeConfigurationFile(); 1046 } 1047 } 1048 1049 // We need to forcibly delete the interface as systemd does not 1050 deleteInterface(intf); 1051 1052 if (eth.ifIdx > 0) 1053 { 1054 eth.manager.interfacesByIdx.erase(eth.ifIdx); 1055 } 1056 eth.manager.interfaces.erase(intf); 1057 } 1058 1059 } // namespace network 1060 } // namespace phosphor 1061