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