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