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