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