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