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/rtnetlink.h> 13 #include <net/if.h> 14 15 #include <algorithm> 16 #include <filesystem> 17 #include <phosphor-logging/elog-errors.hpp> 18 #include <phosphor-logging/log.hpp> 19 #include <stdplus/raw.hpp> 20 #include <stdplus/zstring.hpp> 21 #include <string> 22 #include <unordered_map> 23 #include <variant> 24 #include <xyz/openbmc_project/Common/error.hpp> 25 26 namespace phosphor 27 { 28 namespace network 29 { 30 31 using namespace phosphor::logging; 32 using namespace sdbusplus::xyz::openbmc_project::Common::Error; 33 using NotAllowed = sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed; 34 using NotAllowedArgument = xyz::openbmc_project::Common::NotAllowed; 35 using Argument = xyz::openbmc_project::Common::InvalidArgument; 36 using std::literals::string_view_literals::operator""sv; 37 constexpr auto RESOLVED_SERVICE = "org.freedesktop.resolve1"; 38 constexpr auto RESOLVED_INTERFACE = "org.freedesktop.resolve1.Link"; 39 constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties"; 40 constexpr auto RESOLVED_SERVICE_PATH = "/org/freedesktop/resolve1/link/"; 41 42 constexpr auto TIMESYNCD_SERVICE = "org.freedesktop.timesync1"; 43 constexpr auto TIMESYNCD_INTERFACE = "org.freedesktop.timesync1.Manager"; 44 constexpr auto TIMESYNCD_SERVICE_PATH = "/org/freedesktop/timesync1"; 45 46 constexpr auto METHOD_GET = "Get"; 47 48 template <typename Func> 49 inline decltype(std::declval<Func>()()) 50 ignoreError(std::string_view msg, stdplus::zstring_view intf, 51 decltype(std::declval<Func>()()) fallback, Func&& func) noexcept 52 { 53 try 54 { 55 return func(); 56 } 57 catch (const std::exception& e) 58 { 59 auto err = fmt::format("{} failed on {}: {}", msg, intf, e.what()); 60 log<level::ERR>(err.c_str(), entry("INTERFACE=%s", intf.c_str())); 61 } 62 return fallback; 63 } 64 65 static std::string makeObjPath(std::string_view root, std::string_view intf) 66 { 67 auto ret = fmt::format(FMT_COMPILE("{}/{}"), root, intf); 68 std::replace(ret.begin() + ret.size() - intf.size(), ret.end(), '.', '_'); 69 return ret; 70 } 71 72 EthernetInterface::EthernetInterface(sdbusplus::bus_t& bus, Manager& manager, 73 const InterfaceInfo& info, 74 std::string_view objRoot, 75 const config::Parser& config, 76 bool emitSignal, bool enabled) : 77 EthernetInterface(bus, manager, info, makeObjPath(objRoot, *info.name), 78 config, emitSignal, enabled) 79 { 80 } 81 82 EthernetInterface::EthernetInterface(sdbusplus::bus_t& bus, Manager& manager, 83 const InterfaceInfo& info, 84 std::string&& objPath, 85 const config::Parser& config, 86 bool emitSignal, bool enabled) : 87 Ifaces(bus, objPath.c_str(), 88 emitSignal ? Ifaces::action::defer_emit 89 : Ifaces::action::emit_no_signals), 90 manager(manager), bus(bus), objPath(std::move(objPath)), ifIdx(info.idx) 91 { 92 interfaceName(*info.name); 93 auto dhcpVal = getDHCPValue(config); 94 EthernetInterfaceIntf::dhcp4(dhcpVal.v4); 95 EthernetInterfaceIntf::dhcp6(dhcpVal.v6); 96 EthernetInterfaceIntf::ipv6AcceptRA(getIPv6AcceptRA(config)); 97 EthernetInterfaceIntf::nicEnabled(enabled); 98 { 99 const auto& gws = manager.getRouteTable().getDefaultGateway(); 100 auto it = gws.find(ifIdx); 101 if (it != gws.end()) 102 { 103 EthernetInterfaceIntf::defaultGateway(std::to_string(it->second)); 104 } 105 } 106 { 107 const auto& gws = manager.getRouteTable().getDefaultGateway6(); 108 auto it = gws.find(ifIdx); 109 if (it != gws.end()) 110 { 111 EthernetInterfaceIntf::defaultGateway6(std::to_string(it->second)); 112 } 113 } 114 115 EthernetInterfaceIntf::ntpServers( 116 config.map.getValueStrings("Network", "NTP")); 117 118 if (ifIdx > 0) 119 { 120 auto ethInfo = ignoreError("GetEthInfo", *info.name, {}, [&] { 121 return system::getEthInfo(*info.name); 122 }); 123 EthernetInterfaceIntf::autoNeg(ethInfo.autoneg); 124 EthernetInterfaceIntf::speed(ethInfo.speed); 125 } 126 127 updateInfo(info); 128 129 if (info.vlan_id) 130 { 131 if (!info.parent_idx) 132 { 133 std::runtime_error("Missing parent link"); 134 } 135 vlan.emplace(bus, this->objPath.c_str(), info, *this, emitSignal); 136 } 137 138 // Emit deferred signal. 139 if (emitSignal) 140 { 141 this->emit_object_added(); 142 } 143 } 144 145 void EthernetInterface::updateInfo(const InterfaceInfo& info) 146 { 147 EthernetInterfaceIntf::linkUp(info.flags & IFF_RUNNING); 148 if (info.mac) 149 { 150 MacAddressIntf::macAddress(std::to_string(*info.mac)); 151 } 152 if (info.mtu) 153 { 154 EthernetInterfaceIntf::mtu(*info.mtu); 155 } 156 } 157 158 bool EthernetInterface::originIsManuallyAssigned(IP::AddressOrigin origin) 159 { 160 return ( 161 #ifdef LINK_LOCAL_AUTOCONFIGURATION 162 (origin == IP::AddressOrigin::Static) 163 #else 164 (origin == IP::AddressOrigin::Static || 165 origin == IP::AddressOrigin::LinkLocal) 166 #endif 167 168 ); 169 } 170 171 void EthernetInterface::addAddr(const AddressInfo& info) 172 { 173 IP::AddressOrigin origin = IP::AddressOrigin::Static; 174 if (dhcpIsEnabled(info.ifaddr.getAddr())) 175 { 176 origin = IP::AddressOrigin::DHCP; 177 } 178 #ifdef LINK_LOCAL_AUTOCONFIGURATION 179 if (info.scope == RT_SCOPE_LINK) 180 { 181 origin = IP::AddressOrigin::LinkLocal; 182 } 183 #endif 184 185 auto it = addrs.find(info.ifaddr); 186 if (it == addrs.end()) 187 { 188 addrs.emplace(info.ifaddr, std::make_unique<IPAddress>( 189 bus, std::string_view(objPath), *this, 190 info.ifaddr, origin)); 191 } 192 else 193 { 194 it->second->IPIfaces::origin(origin); 195 } 196 } 197 198 void EthernetInterface::createIPAddressObjects() 199 { 200 addrs.clear(); 201 for (const auto& addr : system::getAddresses({.ifidx = ifIdx})) 202 { 203 manager.addAddress(addr); 204 } 205 } 206 207 void EthernetInterface::addStaticNeigh(const NeighborInfo& info) 208 { 209 if (!info.mac || !info.addr) 210 { 211 auto msg = fmt::format("Missing neighbor mac on {}\n", interfaceName()); 212 log<level::ERR>(msg.c_str()); 213 return; 214 } 215 staticNeighbors.emplace( 216 *info.addr, std::make_unique<Neighbor>(bus, std::string_view(objPath), 217 *this, *info.addr, *info.mac, 218 Neighbor::State::Permanent)); 219 } 220 221 void EthernetInterface::createStaticNeighborObjects() 222 { 223 staticNeighbors.clear(); 224 for (const auto& neighbor : system::getNeighbors({.ifidx = ifIdx})) 225 { 226 manager.addNeighbor(neighbor); 227 } 228 } 229 230 ObjectPath EthernetInterface::ip(IP::Protocol protType, std::string ipaddress, 231 uint8_t prefixLength, std::string) 232 { 233 InAddrAny addr; 234 try 235 { 236 switch (protType) 237 { 238 case IP::Protocol::IPv4: 239 addr = ToAddr<in_addr>{}(ipaddress); 240 break; 241 case IP::Protocol::IPv6: 242 addr = ToAddr<in6_addr>{}(ipaddress); 243 break; 244 default: 245 throw std::logic_error("Exhausted protocols"); 246 } 247 } 248 catch (const std::exception& e) 249 { 250 auto msg = fmt::format("Invalid IP `{}`: {}\n", ipaddress, e.what()); 251 log<level::ERR>(msg.c_str(), entry("ADDRESS=%s", ipaddress.c_str())); 252 elog<InvalidArgument>(Argument::ARGUMENT_NAME("ipaddress"), 253 Argument::ARGUMENT_VALUE(ipaddress.c_str())); 254 } 255 IfAddr ifaddr; 256 try 257 { 258 ifaddr = {addr, prefixLength}; 259 } 260 catch (const std::exception& e) 261 { 262 auto msg = fmt::format("Invalid prefix length `{}`: {}\n", prefixLength, 263 e.what()); 264 log<level::ERR>(msg.c_str(), 265 entry("PREFIXLENGTH=%" PRIu8, prefixLength)); 266 elog<InvalidArgument>( 267 Argument::ARGUMENT_NAME("prefixLength"), 268 Argument::ARGUMENT_VALUE(std::to_string(prefixLength).c_str())); 269 } 270 271 auto [it, _] = this->addrs.insert_or_assign( 272 ifaddr, 273 std::make_unique<IPAddress>(bus, std::string_view(objPath), *this, 274 ifaddr, IP::AddressOrigin::Static)); 275 276 writeConfigurationFile(); 277 manager.reloadConfigsNoRefresh(); 278 279 return it->second->getObjPath(); 280 } 281 282 ObjectPath EthernetInterface::neighbor(std::string ipAddress, 283 std::string macAddress) 284 { 285 InAddrAny addr; 286 try 287 { 288 addr = ToAddr<InAddrAny>{}(ipAddress); 289 } 290 catch (const std::exception& e) 291 { 292 auto msg = 293 fmt::format("Not a valid IP address `{}`: {}", ipAddress, e.what()); 294 log<level::ERR>(msg.c_str(), entry("ADDRESS=%s", ipAddress.c_str())); 295 elog<InvalidArgument>(Argument::ARGUMENT_NAME("ipAddress"), 296 Argument::ARGUMENT_VALUE(ipAddress.c_str())); 297 } 298 299 ether_addr lladdr; 300 try 301 { 302 lladdr = ToAddr<ether_addr>{}(macAddress); 303 } 304 catch (const std::exception& e) 305 { 306 auto msg = fmt::format("Not a valid MAC address `{}`: {}", macAddress, 307 e.what()); 308 log<level::ERR>(msg.c_str(), 309 entry("MACADDRESS=%s", macAddress.c_str())); 310 elog<InvalidArgument>(Argument::ARGUMENT_NAME("macAddress"), 311 Argument::ARGUMENT_VALUE(macAddress.c_str())); 312 } 313 314 auto [it, _] = staticNeighbors.emplace( 315 addr, 316 std::make_unique<Neighbor>(bus, std::string_view(objPath), *this, addr, 317 lladdr, Neighbor::State::Permanent)); 318 319 writeConfigurationFile(); 320 manager.reloadConfigsNoRefresh(); 321 322 return it->second->getObjPath(); 323 } 324 325 bool EthernetInterface::ipv6AcceptRA(bool value) 326 { 327 if (ipv6AcceptRA() != EthernetInterfaceIntf::ipv6AcceptRA(value)) 328 { 329 writeConfigurationFile(); 330 manager.reloadConfigsNoRefresh(); 331 } 332 return value; 333 } 334 335 bool EthernetInterface::dhcp4(bool value) 336 { 337 if (dhcp4() != EthernetInterfaceIntf::dhcp4(value)) 338 { 339 writeConfigurationFile(); 340 manager.reloadConfigsNoRefresh(); 341 } 342 return value; 343 } 344 345 bool EthernetInterface::dhcp6(bool value) 346 { 347 if (dhcp6() != EthernetInterfaceIntf::dhcp6(value)) 348 { 349 writeConfigurationFile(); 350 manager.reloadConfigsNoRefresh(); 351 } 352 return value; 353 } 354 355 EthernetInterface::DHCPConf EthernetInterface::dhcpEnabled(DHCPConf value) 356 { 357 auto old4 = EthernetInterfaceIntf::dhcp4(); 358 auto new4 = EthernetInterfaceIntf::dhcp4(value == DHCPConf::v4 || 359 value == DHCPConf::v4v6stateless || 360 value == DHCPConf::both); 361 auto old6 = EthernetInterfaceIntf::dhcp6(); 362 auto new6 = EthernetInterfaceIntf::dhcp6(value == DHCPConf::v6 || 363 value == DHCPConf::both); 364 auto oldra = EthernetInterfaceIntf::ipv6AcceptRA(); 365 auto newra = EthernetInterfaceIntf::ipv6AcceptRA( 366 value == DHCPConf::v6stateless || value == DHCPConf::v4v6stateless || 367 value == DHCPConf::v6 || value == DHCPConf::both); 368 369 if (old4 != new4 || old6 != new6 || oldra != newra) 370 { 371 writeConfigurationFile(); 372 manager.reloadConfigsNoRefresh(); 373 } 374 return value; 375 } 376 377 EthernetInterface::DHCPConf EthernetInterface::dhcpEnabled() const 378 { 379 if (dhcp6()) 380 { 381 return dhcp4() ? DHCPConf::both : DHCPConf::v6; 382 } 383 else if (dhcp4()) 384 { 385 return ipv6AcceptRA() ? DHCPConf::v4v6stateless : DHCPConf::v4; 386 } 387 return ipv6AcceptRA() ? DHCPConf::v6stateless : DHCPConf::none; 388 } 389 390 bool EthernetInterface::linkUp() const 391 { 392 if (ifIdx == 0) 393 { 394 return EthernetInterfaceIntf::linkUp(); 395 } 396 return system::intfIsRunning(interfaceName()); 397 } 398 399 size_t EthernetInterface::mtu() const 400 { 401 if (ifIdx == 0) 402 { 403 return EthernetInterfaceIntf::mtu(); 404 } 405 const auto ifname = interfaceName(); 406 return ignoreError("GetMTU", ifname, std::nullopt, 407 [&] { return system::getMTU(ifname); }) 408 .value_or(EthernetInterfaceIntf::mtu()); 409 } 410 411 size_t EthernetInterface::mtu(size_t value) 412 { 413 const size_t old = EthernetInterfaceIntf::mtu(); 414 if (value == old) 415 { 416 return value; 417 } 418 const auto ifname = interfaceName(); 419 return EthernetInterfaceIntf::mtu(ignoreError("SetMTU", ifname, old, [&] { 420 system::setMTU(ifname, value); 421 return value; 422 })); 423 } 424 425 bool EthernetInterface::nicEnabled(bool value) 426 { 427 if (value == EthernetInterfaceIntf::nicEnabled()) 428 { 429 return value; 430 } 431 432 EthernetInterfaceIntf::nicEnabled(value); 433 writeConfigurationFile(); 434 if (!value) 435 { 436 // We only need to bring down the interface, networkd will always bring 437 // up managed interfaces 438 manager.addReloadPreHook( 439 [ifname = interfaceName()]() { system::setNICUp(ifname, false); }); 440 } 441 manager.reloadConfigsNoRefresh(); 442 443 return value; 444 } 445 446 ServerList EthernetInterface::staticNameServers(ServerList value) 447 { 448 for (auto& ip : value) 449 { 450 try 451 { 452 ip = std::to_string(ToAddr<InAddrAny>{}(ip)); 453 } 454 catch (const std::exception& e) 455 { 456 auto msg = 457 fmt::format("Not a valid IP address `{}`: {}", ip, e.what()); 458 log<level::ERR>(msg.c_str()), entry("ADDRESS=%s", ip.c_str()); 459 elog<InvalidArgument>(Argument::ARGUMENT_NAME("StaticNameserver"), 460 Argument::ARGUMENT_VALUE(ip.c_str())); 461 } 462 } 463 try 464 { 465 EthernetInterfaceIntf::staticNameServers(value); 466 467 writeConfigurationFile(); 468 manager.reloadConfigsNoRefresh(); 469 } 470 catch (const InternalFailure& e) 471 { 472 log<level::ERR>("Exception processing DNS entries"); 473 } 474 return EthernetInterfaceIntf::staticNameServers(); 475 } 476 477 void EthernetInterface::loadNTPServers(const config::Parser& config) 478 { 479 EthernetInterfaceIntf::ntpServers(getNTPServerFromTimeSyncd()); 480 EthernetInterfaceIntf::staticNTPServers( 481 config.map.getValueStrings("Network", "NTP")); 482 } 483 484 void EthernetInterface::loadNameServers(const config::Parser& config) 485 { 486 EthernetInterfaceIntf::nameservers(getNameServerFromResolvd()); 487 EthernetInterfaceIntf::staticNameServers( 488 config.map.getValueStrings("Network", "DNS")); 489 } 490 491 ServerList EthernetInterface::getNTPServerFromTimeSyncd() 492 { 493 ServerList servers; // Variable to capture the NTP Server IPs 494 auto method = bus.new_method_call(TIMESYNCD_SERVICE, TIMESYNCD_SERVICE_PATH, 495 PROPERTY_INTERFACE, METHOD_GET); 496 497 method.append(TIMESYNCD_INTERFACE, "LinkNTPServers"); 498 499 try 500 { 501 auto reply = bus.call(method); 502 std::variant<ServerList> response; 503 reply.read(response); 504 servers = std::get<ServerList>(response); 505 } 506 catch (const sdbusplus::exception::SdBusError& e) 507 { 508 log<level::ERR>( 509 "Failed to get NTP server information from Systemd-Timesyncd"); 510 } 511 512 return servers; 513 } 514 515 ServerList EthernetInterface::getNameServerFromResolvd() 516 { 517 ServerList servers; 518 auto OBJ_PATH = fmt::format("{}{}", RESOLVED_SERVICE_PATH, ifIdx); 519 520 /* 521 The DNS property under org.freedesktop.resolve1.Link interface contains 522 an array containing all DNS servers currently used by resolved. It 523 contains similar information as the DNS server data written to 524 /run/systemd/resolve/resolv.conf. 525 526 Each structure in the array consists of a numeric network interface index, 527 an address family, and a byte array containing the DNS server address 528 (either 4 bytes in length for IPv4 or 16 bytes in lengths for IPv6). 529 The array contains DNS servers configured system-wide, including those 530 possibly read from a foreign /etc/resolv.conf or the DNS= setting in 531 /etc/systemd/resolved.conf, as well as per-interface DNS server 532 information either retrieved from systemd-networkd or configured by 533 external software via SetLinkDNS(). 534 */ 535 536 using type = std::vector<std::tuple<int32_t, std::vector<uint8_t>>>; 537 std::variant<type> name; // Variable to capture the DNS property 538 auto method = bus.new_method_call(RESOLVED_SERVICE, OBJ_PATH.c_str(), 539 PROPERTY_INTERFACE, METHOD_GET); 540 541 method.append(RESOLVED_INTERFACE, "DNS"); 542 543 try 544 { 545 auto reply = bus.call(method); 546 reply.read(name); 547 } 548 catch (const sdbusplus::exception_t& e) 549 { 550 log<level::ERR>("Failed to get DNS information from Systemd-Resolved"); 551 } 552 auto tupleVector = std::get_if<type>(&name); 553 for (auto i = tupleVector->begin(); i != tupleVector->end(); ++i) 554 { 555 int addressFamily = std::get<0>(*i); 556 std::vector<uint8_t>& ipaddress = std::get<1>(*i); 557 servers.push_back(std::to_string( 558 addrFromBuf(addressFamily, stdplus::raw::asView<char>(ipaddress)))); 559 } 560 return servers; 561 } 562 563 ObjectPath EthernetInterface::createVLAN(uint16_t id) 564 { 565 auto intfName = fmt::format(FMT_COMPILE("{}.{}"), interfaceName(), id); 566 auto idStr = std::to_string(id); 567 if (manager.interfaces.find(intfName) != manager.interfaces.end()) 568 { 569 log<level::ERR>("VLAN already exists", entry("VLANID=%u", id)); 570 elog<InvalidArgument>(Argument::ARGUMENT_NAME("VLANId"), 571 Argument::ARGUMENT_VALUE(idStr.c_str())); 572 } 573 574 auto objRoot = std::string_view(objPath).substr(0, objPath.rfind('/')); 575 auto macStr = MacAddressIntf::macAddress(); 576 std::optional<ether_addr> mac; 577 if (!macStr.empty()) 578 { 579 mac.emplace(ToAddr<ether_addr>{}(macStr)); 580 } 581 auto info = InterfaceInfo{ 582 .idx = 0, // TODO: Query the correct value after creation 583 .flags = 0, 584 .name = intfName, 585 .mac = std::move(mac), 586 .mtu = mtu(), 587 .parent_idx = ifIdx, 588 .vlan_id = id, 589 }; 590 591 // Pass the parents nicEnabled property, so that the child 592 // VLAN interface can inherit. 593 auto vlanIntf = std::make_unique<EthernetInterface>( 594 bus, manager, info, objRoot, config::Parser(), /*emit=*/true, 595 nicEnabled()); 596 ObjectPath ret = vlanIntf->objPath; 597 598 manager.interfaces.emplace(intfName, std::move(vlanIntf)); 599 600 // write the device file for the vlan interface. 601 config::Parser config; 602 auto& netdev = config.map["NetDev"].emplace_back(); 603 netdev["Name"].emplace_back(intfName); 604 netdev["Kind"].emplace_back("vlan"); 605 config.map["VLAN"].emplace_back()["Id"].emplace_back(std::move(idStr)); 606 config.writeFile(config::pathForIntfDev(manager.getConfDir(), intfName)); 607 608 writeConfigurationFile(); 609 manager.reloadConfigsNoRefresh(); 610 611 return ret; 612 } 613 614 ServerList EthernetInterface::staticNTPServers(ServerList value) 615 { 616 try 617 { 618 EthernetInterfaceIntf::staticNTPServers(value); 619 620 writeConfigurationFile(); 621 manager.reloadConfigsNoRefresh(); 622 } 623 catch (InternalFailure& e) 624 { 625 log<level::ERR>("Exception processing NTP entries"); 626 } 627 return EthernetInterfaceIntf::staticNTPServers(); 628 } 629 630 ServerList EthernetInterface::ntpServers(ServerList /*servers*/) 631 { 632 elog<NotAllowed>(NotAllowedArgument::REASON("ReadOnly Property")); 633 } 634 // Need to merge the below function with the code which writes the 635 // config file during factory reset. 636 // TODO openbmc/openbmc#1751 637 638 void EthernetInterface::writeConfigurationFile() 639 { 640 config::Parser config; 641 config.map["Match"].emplace_back()["Name"].emplace_back(interfaceName()); 642 { 643 auto& link = config.map["Link"].emplace_back(); 644 #ifdef PERSIST_MAC 645 auto mac = MacAddressIntf::macAddress(); 646 if (!mac.empty()) 647 { 648 link["MACAddress"].emplace_back(mac); 649 } 650 #endif 651 if (!EthernetInterfaceIntf::nicEnabled()) 652 { 653 link["Unmanaged"].emplace_back("yes"); 654 } 655 } 656 { 657 auto& network = config.map["Network"].emplace_back(); 658 auto& lla = network["LinkLocalAddressing"]; 659 #ifdef LINK_LOCAL_AUTOCONFIGURATION 660 lla.emplace_back("yes"); 661 #else 662 lla.emplace_back("no"); 663 #endif 664 network["IPv6AcceptRA"].emplace_back(ipv6AcceptRA() ? "true" : "false"); 665 network["DHCP"].emplace_back(dhcp4() ? (dhcp6() ? "true" : "ipv4") 666 : (dhcp6() ? "ipv6" : "false")); 667 { 668 auto& vlans = network["VLAN"]; 669 for (const auto& [_, intf] : manager.interfaces) 670 { 671 if (intf->vlan && intf->vlan->parentIdx == ifIdx) 672 { 673 vlans.emplace_back(intf->interfaceName()); 674 } 675 } 676 } 677 { 678 auto& ntps = network["NTP"]; 679 for (const auto& ntp : EthernetInterfaceIntf::staticNTPServers()) 680 { 681 ntps.emplace_back(ntp); 682 } 683 } 684 { 685 auto& dnss = network["DNS"]; 686 for (const auto& dns : EthernetInterfaceIntf::staticNameServers()) 687 { 688 dnss.emplace_back(dns); 689 } 690 } 691 { 692 auto& address = network["Address"]; 693 for (const auto& addr : addrs) 694 { 695 if (originIsManuallyAssigned(addr.second->origin())) 696 { 697 address.emplace_back( 698 fmt::format("{}/{}", addr.second->address(), 699 addr.second->prefixLength())); 700 } 701 } 702 } 703 { 704 auto& gateways = network["Gateway"]; 705 if (!dhcp4()) 706 { 707 auto gateway = EthernetInterfaceIntf::defaultGateway(); 708 if (!gateway.empty()) 709 { 710 gateways.emplace_back(gateway); 711 } 712 } 713 714 if (!dhcp6()) 715 { 716 auto gateway6 = EthernetInterfaceIntf::defaultGateway6(); 717 if (!gateway6.empty()) 718 { 719 gateways.emplace_back(gateway6); 720 } 721 } 722 } 723 } 724 config.map["IPv6AcceptRA"].emplace_back()["DHCPv6Client"].emplace_back( 725 dhcp6() ? "true" : "false"); 726 { 727 auto& neighbors = config.map["Neighbor"]; 728 for (const auto& sneighbor : staticNeighbors) 729 { 730 auto& neighbor = neighbors.emplace_back(); 731 neighbor["Address"].emplace_back(sneighbor.second->ipAddress()); 732 neighbor["MACAddress"].emplace_back(sneighbor.second->macAddress()); 733 } 734 } 735 { 736 auto& dhcp = config.map["DHCP"].emplace_back(); 737 dhcp["ClientIdentifier"].emplace_back("mac"); 738 if (manager.getDHCPConf()) 739 { 740 const auto& conf = *manager.getDHCPConf(); 741 auto dns_enabled = conf.dnsEnabled() ? "true" : "false"; 742 dhcp["UseDNS"].emplace_back(dns_enabled); 743 dhcp["UseDomains"].emplace_back(dns_enabled); 744 dhcp["UseNTP"].emplace_back(conf.ntpEnabled() ? "true" : "false"); 745 dhcp["UseHostname"].emplace_back(conf.hostNameEnabled() ? "true" 746 : "false"); 747 dhcp["SendHostname"].emplace_back( 748 conf.sendHostNameEnabled() ? "true" : "false"); 749 } 750 } 751 auto path = config::pathForIntfConf(manager.getConfDir(), interfaceName()); 752 config.writeFile(path); 753 auto msg = fmt::format("Wrote networkd file: {}", path.native()); 754 log<level::INFO>(msg.c_str(), entry("FILE=%s", path.c_str())); 755 } 756 757 std::string EthernetInterface::macAddress([[maybe_unused]] std::string value) 758 { 759 if (vlan) 760 { 761 log<level::ERR>("Tried to set MAC address on VLAN"); 762 elog<InternalFailure>(); 763 } 764 #ifdef PERSIST_MAC 765 ether_addr newMAC; 766 try 767 { 768 newMAC = ToAddr<ether_addr>{}(value); 769 } 770 catch (const std::invalid_argument&) 771 { 772 log<level::ERR>("MACAddress is not valid.", 773 entry("MAC=%s", value.c_str())); 774 elog<InvalidArgument>(Argument::ARGUMENT_NAME("MACAddress"), 775 Argument::ARGUMENT_VALUE(value.c_str())); 776 } 777 if (!mac_address::isUnicast(newMAC)) 778 { 779 log<level::ERR>("MACAddress is not valid.", 780 entry("MAC=%s", value.c_str())); 781 elog<InvalidArgument>(Argument::ARGUMENT_NAME("MACAddress"), 782 Argument::ARGUMENT_VALUE(value.c_str())); 783 } 784 785 auto interface = interfaceName(); 786 auto validMAC = std::to_string(newMAC); 787 788 // We don't need to update the system if the address is unchanged 789 ether_addr oldMAC = ToAddr<ether_addr>{}(MacAddressIntf::macAddress()); 790 if (newMAC != oldMAC) 791 { 792 // Update everything that depends on the MAC value 793 for (const auto& [_, intf] : manager.interfaces) 794 { 795 if (intf->vlan && intf->vlan->parentIdx == ifIdx) 796 { 797 intf->MacAddressIntf::macAddress(validMAC); 798 } 799 } 800 MacAddressIntf::macAddress(validMAC); 801 802 writeConfigurationFile(); 803 manager.addReloadPreHook([interface]() { 804 // The MAC and LLADDRs will only update if the NIC is already down 805 system::setNICUp(interface, false); 806 }); 807 manager.reloadConfigsNoRefresh(); 808 } 809 810 #ifdef HAVE_UBOOT_ENV 811 // Ensure that the valid address is stored in the u-boot-env 812 auto envVar = interfaceToUbootEthAddr(interface); 813 if (envVar) 814 { 815 // Trimming MAC addresses that are out of range. eg: AA:FF:FF:FF:FF:100; 816 // and those having more than 6 bytes. eg: AA:AA:AA:AA:AA:AA:BB 817 execute("/sbin/fw_setenv", "fw_setenv", envVar->c_str(), 818 validMAC.c_str()); 819 } 820 #endif // HAVE_UBOOT_ENV 821 822 return value; 823 #else 824 elog<NotAllowed>( 825 NotAllowedArgument::REASON("Writing MAC address is not allowed")); 826 #endif // PERSIST_MAC 827 } 828 829 void EthernetInterface::deleteAll() 830 { 831 // clear all the ip on the interface 832 addrs.clear(); 833 834 writeConfigurationFile(); 835 manager.reloadConfigsNoRefresh(); 836 } 837 838 std::string EthernetInterface::defaultGateway(std::string gateway) 839 { 840 try 841 { 842 if (!gateway.empty()) 843 { 844 gateway = std::to_string(ToAddr<in_addr>{}(gateway)); 845 } 846 } 847 catch (const std::exception& e) 848 { 849 auto msg = fmt::format("Invalid v4 GW `{}`: {}", gateway, e.what()); 850 log<level::ERR>(msg.c_str(), entry("GATEWAY=%s", gateway.c_str())); 851 elog<InvalidArgument>(Argument::ARGUMENT_NAME("GATEWAY"), 852 Argument::ARGUMENT_VALUE(gateway.c_str())); 853 } 854 855 if (EthernetInterfaceIntf::defaultGateway() == gateway) 856 { 857 return gateway; 858 } 859 EthernetInterfaceIntf::defaultGateway(gateway); 860 861 writeConfigurationFile(); 862 manager.reloadConfigsNoRefresh(); 863 864 return gateway; 865 } 866 867 std::string EthernetInterface::defaultGateway6(std::string gateway) 868 { 869 try 870 { 871 if (!gateway.empty()) 872 { 873 gateway = std::to_string(ToAddr<in6_addr>{}(gateway)); 874 } 875 } 876 catch (const std::exception& e) 877 { 878 auto msg = fmt::format("Invalid v6 GW `{}`: {}", gateway, e.what()); 879 log<level::ERR>(msg.c_str(), entry("GATEWAY=%s", gateway.c_str())); 880 elog<InvalidArgument>(Argument::ARGUMENT_NAME("GATEWAY"), 881 Argument::ARGUMENT_VALUE(gateway.c_str())); 882 } 883 884 if (EthernetInterfaceIntf::defaultGateway6() == gateway) 885 { 886 return gateway; 887 } 888 EthernetInterfaceIntf::defaultGateway6(gateway); 889 890 writeConfigurationFile(); 891 manager.reloadConfigsNoRefresh(); 892 893 return gateway; 894 } 895 896 EthernetInterface::VlanProperties::VlanProperties( 897 sdbusplus::bus_t& bus, stdplus::const_zstring objPath, 898 const InterfaceInfo& info, EthernetInterface& eth, bool emitSignal) : 899 VlanIfaces(bus, objPath.c_str(), 900 emitSignal ? VlanIfaces::action::defer_emit 901 : VlanIfaces::action::emit_no_signals), 902 parentIdx(*info.parent_idx), eth(eth) 903 { 904 VlanIntf::id(*info.vlan_id); 905 if (emitSignal) 906 { 907 this->emit_object_added(); 908 } 909 } 910 911 void EthernetInterface::VlanProperties::delete_() 912 { 913 auto intf = eth.interfaceName(); 914 915 // Remove all configs for the current interface 916 const auto& confDir = eth.manager.getConfDir(); 917 std::error_code ec; 918 std::filesystem::remove(config::pathForIntfConf(confDir, intf), ec); 919 std::filesystem::remove(config::pathForIntfDev(confDir, intf), ec); 920 921 // Write an updated parent interface since it has a VLAN entry 922 for (const auto& [_, intf] : eth.manager.interfaces) 923 { 924 if (intf->ifIdx == parentIdx) 925 { 926 intf->writeConfigurationFile(); 927 } 928 } 929 930 // We need to forcibly delete the interface as systemd does not 931 deleteInterface(intf); 932 933 if (eth.ifIdx > 0) 934 { 935 eth.manager.interfacesByIdx.erase(eth.ifIdx); 936 } 937 eth.manager.interfaces.erase(intf); 938 } 939 940 } // namespace network 941 } // namespace phosphor 942