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