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