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