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