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