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