1 #include "config.h" 2 3 #include "ethernet_interface.hpp" 4 5 #include "config_parser.hpp" 6 #include "network_manager.hpp" 7 #include "util.hpp" 8 9 #include <arpa/inet.h> 10 #include <fmt/compile.h> 11 #include <fmt/format.h> 12 #include <linux/ethtool.h> 13 #include <linux/rtnetlink.h> 14 #include <linux/sockios.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/fd/create.hpp> 23 #include <stdplus/raw.hpp> 24 #include <stdplus/zstring.hpp> 25 #include <string> 26 #include <unordered_map> 27 #include <variant> 28 #include <xyz/openbmc_project/Common/error.hpp> 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 static stdplus::Fd& getIFSock() 53 { 54 using namespace stdplus::fd; 55 static auto fd = 56 socket(SocketDomain::INet, SocketType::Datagram, SocketProto::IP); 57 return fd; 58 } 59 60 struct LinkInfo 61 { 62 bool autoneg; 63 uint16_t speed; 64 }; 65 66 static LinkInfo getLinkInfo(stdplus::zstring_view ifname) 67 { 68 LinkInfo ret; 69 try 70 { 71 ethtool_cmd edata = {}; 72 edata.cmd = ETHTOOL_GSET; 73 74 ifreq ifr = {}; 75 const auto copied = std::min<std::size_t>(ifname.size(), IFNAMSIZ - 1); 76 std::copy_n(ifname.begin(), copied, ifr.ifr_name); 77 ifr.ifr_data = reinterpret_cast<char*>(&edata); 78 79 getIFSock().ioctl(SIOCETHTOOL, &ifr); 80 81 ret.speed = edata.speed; 82 ret.autoneg = edata.autoneg; 83 } 84 catch (const std::system_error& e) 85 { 86 if (e.code() == std::errc::operation_not_supported) 87 { 88 auto msg = fmt::format("ETHTOOL not supported on {}", ifname); 89 log<level::NOTICE>(msg.c_str(), 90 entry("INTERFACE=%s", ifname.c_str())); 91 } 92 else 93 { 94 auto msg = 95 fmt::format("ETHTOOL failed on {}: {}", ifname, e.what()); 96 log<level::ERR>(msg.c_str(), entry("INTERFACE=%s", ifname.c_str())); 97 } 98 } 99 return ret; 100 } 101 102 EthernetInterface::EthernetInterface(sdbusplus::bus_t& bus, 103 stdplus::zstring_view objPath, 104 const config::Parser& config, 105 Manager& parent, bool emitSignal, 106 std::optional<bool> enabled) : 107 Ifaces(bus, objPath.c_str(), 108 emitSignal ? Ifaces::action::defer_emit 109 : Ifaces::action::emit_no_signals), 110 bus(bus), manager(parent), objPath(objPath.c_str()) 111 { 112 auto intfName = std::string(objPath.substr(objPath.rfind('/') + 1)); 113 std::replace(intfName.begin(), intfName.end(), '_', '.'); 114 interfaceName(intfName); 115 auto dhcpVal = getDHCPValue(config); 116 EthernetInterfaceIntf::dhcp4(dhcpVal.v4); 117 EthernetInterfaceIntf::dhcp6(dhcpVal.v6); 118 EthernetInterfaceIntf::ipv6AcceptRA(getIPv6AcceptRA(config)); 119 EthernetInterfaceIntf::nicEnabled(enabled ? *enabled : queryNicEnabled()); 120 const auto& gatewayList = manager.getRouteTable().getDefaultGateway(); 121 const auto& gateway6List = manager.getRouteTable().getDefaultGateway6(); 122 std::string defaultGateway; 123 std::string defaultGateway6; 124 125 for (const auto& gateway : gatewayList) 126 { 127 if (gateway.first == intfName) 128 { 129 defaultGateway = gateway.second; 130 break; 131 } 132 } 133 134 for (const auto& gateway6 : gateway6List) 135 { 136 if (gateway6.first == intfName) 137 { 138 defaultGateway6 = gateway6.second; 139 break; 140 } 141 } 142 143 EthernetInterfaceIntf::defaultGateway(defaultGateway); 144 EthernetInterfaceIntf::defaultGateway6(defaultGateway6); 145 // Don't get the mac address from the system as the mac address 146 // would be same as parent interface. 147 if (intfName.find(".") == std::string::npos) 148 { 149 try 150 { 151 MacAddressIntf::macAddress(getMACAddress(intfName)); 152 } 153 catch (const std::exception& e) 154 { 155 auto msg = 156 fmt::format("Failed to get MAC for {}: {}", intfName, e.what()); 157 log<level::ERR>(msg.c_str(), 158 entry("INTERFACE=%s", intfName.c_str())); 159 } 160 } 161 EthernetInterfaceIntf::ntpServers( 162 config.map.getValueStrings("Network", "NTP")); 163 164 EthernetInterfaceIntf::linkUp(linkUp()); 165 EthernetInterfaceIntf::mtu(mtu()); 166 167 auto info = getLinkInfo(intfName); 168 EthernetInterfaceIntf::autoNeg(info.autoneg); 169 EthernetInterfaceIntf::speed(info.speed); 170 171 // Emit deferred signal. 172 if (emitSignal) 173 { 174 this->emit_object_added(); 175 } 176 } 177 178 static IP::Protocol getProtocol(const InAddrAny& addr) 179 { 180 if (std::holds_alternative<in_addr>(addr)) 181 { 182 return IP::Protocol::IPv4; 183 } 184 else if (std::holds_alternative<in6_addr>(addr)) 185 { 186 return IP::Protocol::IPv6; 187 } 188 189 throw std::runtime_error("Invalid addr type"); 190 } 191 192 bool EthernetInterface::dhcpIsEnabled(IP::Protocol family) 193 { 194 switch (family) 195 { 196 case IP::Protocol::IPv6: 197 return dhcp6(); 198 case IP::Protocol::IPv4: 199 return dhcp4(); 200 } 201 throw std::logic_error("Unreachable"); 202 } 203 204 bool EthernetInterface::originIsManuallyAssigned(IP::AddressOrigin origin) 205 { 206 return ( 207 #ifdef LINK_LOCAL_AUTOCONFIGURATION 208 (origin == IP::AddressOrigin::Static) 209 #else 210 (origin == IP::AddressOrigin::Static || 211 origin == IP::AddressOrigin::LinkLocal) 212 #endif 213 214 ); 215 } 216 217 void EthernetInterface::createIPAddressObjects() 218 { 219 addrs.clear(); 220 221 AddressFilter filter; 222 filter.interface = ifIndex(); 223 auto currentAddrs = getCurrentAddresses(filter); 224 for (const auto& addr : currentAddrs) 225 { 226 if (addr.flags & IFA_F_DEPRECATED) 227 { 228 continue; 229 } 230 auto address = toString(addr.address); 231 IP::Protocol addressType = getProtocol(addr.address); 232 IP::AddressOrigin origin = IP::AddressOrigin::Static; 233 if (dhcpIsEnabled(addressType)) 234 { 235 origin = IP::AddressOrigin::DHCP; 236 } 237 #ifdef LINK_LOCAL_AUTOCONFIGURATION 238 if (addr.scope == RT_SCOPE_LINK) 239 { 240 origin = IP::AddressOrigin::LinkLocal; 241 } 242 #endif 243 244 auto ipAddressObjectPath = 245 generateObjectPath(addressType, address, addr.prefix, origin); 246 247 this->addrs.insert_or_assign( 248 address, std::make_unique<IPAddress>(bus, ipAddressObjectPath, 249 *this, addressType, address, 250 origin, addr.prefix)); 251 } 252 } 253 254 void EthernetInterface::createStaticNeighborObjects() 255 { 256 staticNeighbors.clear(); 257 258 NeighborFilter filter; 259 filter.interface = ifIndex(); 260 filter.state = NUD_PERMANENT; 261 auto neighbors = getCurrentNeighbors(filter); 262 for (const auto& neighbor : neighbors) 263 { 264 if (!neighbor.mac) 265 { 266 continue; 267 } 268 auto ip = toString(neighbor.address); 269 auto mac = mac_address::toString(*neighbor.mac); 270 auto objectPath = generateStaticNeighborObjectPath(ip, mac); 271 staticNeighbors.emplace( 272 ip, std::make_unique<Neighbor>(bus, objectPath, *this, ip, mac, 273 Neighbor::State::Permanent)); 274 } 275 } 276 277 unsigned EthernetInterface::ifIndex() const 278 { 279 unsigned idx = if_nametoindex(interfaceName().c_str()); 280 if (idx == 0) 281 { 282 throw std::system_error(errno, std::generic_category(), 283 "if_nametoindex"); 284 } 285 return idx; 286 } 287 288 ObjectPath EthernetInterface::ip(IP::Protocol protType, std::string ipaddress, 289 uint8_t prefixLength, std::string) 290 { 291 if (dhcpIsEnabled(protType)) 292 { 293 log<level::INFO>("DHCP enabled on the interface, disabling"), 294 entry("INTERFACE=%s", interfaceName().c_str()); 295 switch (protType) 296 { 297 case IP::Protocol::IPv4: 298 dhcp4(false); 299 break; 300 case IP::Protocol::IPv6: 301 dhcp6(false); 302 break; 303 } 304 // Delete the IP address object and that reloads the networkd 305 // to allow the same IP address to be set as Static IP 306 deleteObject(ipaddress); 307 } 308 309 IP::AddressOrigin origin = IP::AddressOrigin::Static; 310 311 int addressFamily = (protType == IP::Protocol::IPv4) ? AF_INET : AF_INET6; 312 313 if (!isValidIP(addressFamily, ipaddress)) 314 { 315 log<level::ERR>("Not a valid IP address"), 316 entry("ADDRESS=%s", ipaddress.c_str()); 317 elog<InvalidArgument>(Argument::ARGUMENT_NAME("ipaddress"), 318 Argument::ARGUMENT_VALUE(ipaddress.c_str())); 319 } 320 321 if (!isValidPrefix(addressFamily, prefixLength)) 322 { 323 log<level::ERR>("PrefixLength is not correct "), 324 entry("PREFIXLENGTH=%" PRIu8, prefixLength); 325 elog<InvalidArgument>( 326 Argument::ARGUMENT_NAME("prefixLength"), 327 Argument::ARGUMENT_VALUE(std::to_string(prefixLength).c_str())); 328 } 329 330 auto objectPath = 331 generateObjectPath(protType, ipaddress, prefixLength, origin); 332 this->addrs.insert_or_assign( 333 ipaddress, 334 std::make_unique<IPAddress>(bus, objectPath, *this, protType, ipaddress, 335 origin, prefixLength)); 336 337 writeConfigurationFile(); 338 manager.reloadConfigs(); 339 340 return objectPath; 341 } 342 343 ObjectPath EthernetInterface::neighbor(std::string ipAddress, 344 std::string macAddress) 345 { 346 if (!isValidIP(ipAddress)) 347 { 348 log<level::ERR>("Not a valid IP address", 349 entry("ADDRESS=%s", ipAddress.c_str())); 350 elog<InvalidArgument>(Argument::ARGUMENT_NAME("ipAddress"), 351 Argument::ARGUMENT_VALUE(ipAddress.c_str())); 352 } 353 if (!mac_address::isUnicast(mac_address::fromString(macAddress))) 354 { 355 log<level::ERR>("Not a valid MAC address", 356 entry("MACADDRESS=%s", ipAddress.c_str())); 357 elog<InvalidArgument>(Argument::ARGUMENT_NAME("macAddress"), 358 Argument::ARGUMENT_VALUE(macAddress.c_str())); 359 } 360 361 auto objectPath = generateStaticNeighborObjectPath(ipAddress, macAddress); 362 staticNeighbors.emplace( 363 ipAddress, 364 std::make_unique<Neighbor>(bus, objectPath, *this, ipAddress, 365 macAddress, Neighbor::State::Permanent)); 366 367 writeConfigurationFile(); 368 manager.reloadConfigs(); 369 370 return objectPath; 371 } 372 373 /** @brief get the mac address of the interface. 374 * @return macaddress on success 375 */ 376 377 std::string 378 EthernetInterface::getMACAddress(stdplus::const_zstring interfaceName) const 379 { 380 std::string activeMACAddr = MacAddressIntf::macAddress(); 381 382 ifreq ifr = {}; 383 std::strncpy(ifr.ifr_name, interfaceName.c_str(), IFNAMSIZ - 1); 384 try 385 { 386 getIFSock().ioctl(SIOCGIFHWADDR, &ifr); 387 } 388 catch (const std::exception& e) 389 { 390 log<level::ERR>("ioctl failed for SIOCGIFHWADDR:", 391 entry("ERROR=%s", e.what())); 392 elog<InternalFailure>(); 393 } 394 return mac_address::toString( 395 stdplus::raw::refFrom<ether_addr>(ifr.ifr_hwaddr.sa_data)); 396 } 397 398 void EthernetInterface::deleteObject(std::string_view ipaddress) 399 { 400 auto it = addrs.find(ipaddress); 401 if (it == addrs.end()) 402 { 403 log<level::ERR>("DeleteObject:Unable to find the object."); 404 return; 405 } 406 this->addrs.erase(it); 407 408 writeConfigurationFile(); 409 manager.reloadConfigs(); 410 } 411 412 void EthernetInterface::deleteStaticNeighborObject(std::string_view ipAddress) 413 { 414 auto it = staticNeighbors.find(ipAddress); 415 if (it == staticNeighbors.end()) 416 { 417 log<level::ERR>( 418 "DeleteStaticNeighborObject:Unable to find the object."); 419 return; 420 } 421 staticNeighbors.erase(it); 422 423 writeConfigurationFile(); 424 manager.reloadConfigs(); 425 } 426 427 void EthernetInterface::deleteVLANFromSystem(stdplus::zstring_view interface) 428 { 429 const auto& confDir = manager.getConfDir(); 430 auto networkFile = config::pathForIntfConf(confDir, interface); 431 auto deviceFile = config::pathForIntfDev(confDir, interface); 432 433 // delete the vlan network file 434 std::error_code ec; 435 std::filesystem::remove(networkFile, ec); 436 std::filesystem::remove(deviceFile, ec); 437 438 // TODO systemd doesn't delete the virtual network interface 439 // even after deleting all the related configuartion. 440 // https://github.com/systemd/systemd/issues/6600 441 try 442 { 443 deleteInterface(interface); 444 } 445 catch (const InternalFailure& e) 446 { 447 commit<InternalFailure>(); 448 } 449 } 450 451 void EthernetInterface::deleteVLANObject(stdplus::zstring_view interface) 452 { 453 auto it = vlanInterfaces.find(interface); 454 if (it == vlanInterfaces.end()) 455 { 456 log<level::ERR>("DeleteVLANObject:Unable to find the object", 457 entry("INTERFACE=%s", interface.c_str())); 458 return; 459 } 460 461 deleteVLANFromSystem(interface); 462 // delete the interface 463 vlanInterfaces.erase(it); 464 465 writeConfigurationFile(); 466 manager.reloadConfigs(); 467 } 468 469 std::string EthernetInterface::generateObjectPath( 470 IP::Protocol addressType, std::string_view ipAddress, uint8_t prefixLength, 471 IP::AddressOrigin origin) const 472 { 473 std::string_view type; 474 switch (addressType) 475 { 476 case IP::Protocol::IPv4: 477 type = "ipv4"sv; 478 break; 479 case IP::Protocol::IPv6: 480 type = "ipv6"sv; 481 break; 482 } 483 return fmt::format( 484 FMT_COMPILE("{}/{}/{:08x}"), objPath, type, 485 static_cast<uint32_t>(hash_multi( 486 ipAddress, prefixLength, 487 static_cast<std::underlying_type_t<IP::AddressOrigin>>(origin)))); 488 } 489 490 std::string EthernetInterface::generateStaticNeighborObjectPath( 491 std::string_view ipAddress, std::string_view macAddress) const 492 { 493 return fmt::format( 494 FMT_COMPILE("{}/static_neighbor/{:08x}"), objPath, 495 static_cast<uint32_t>(hash_multi(ipAddress, macAddress))); 496 } 497 498 bool EthernetInterface::ipv6AcceptRA(bool value) 499 { 500 if (ipv6AcceptRA() != EthernetInterfaceIntf::ipv6AcceptRA(value)) 501 { 502 writeConfigurationFile(); 503 manager.reloadConfigs(); 504 } 505 return value; 506 } 507 508 bool EthernetInterface::dhcp4(bool value) 509 { 510 if (dhcp4() != EthernetInterfaceIntf::dhcp4(value)) 511 { 512 writeConfigurationFile(); 513 manager.reloadConfigs(); 514 } 515 return value; 516 } 517 518 bool EthernetInterface::dhcp6(bool value) 519 { 520 if (dhcp6() != EthernetInterfaceIntf::dhcp6(value)) 521 { 522 writeConfigurationFile(); 523 manager.reloadConfigs(); 524 } 525 return value; 526 } 527 528 EthernetInterface::DHCPConf EthernetInterface::dhcpEnabled(DHCPConf value) 529 { 530 auto old4 = EthernetInterfaceIntf::dhcp4(); 531 auto new4 = EthernetInterfaceIntf::dhcp4(value == DHCPConf::v4 || 532 value == DHCPConf::v4v6stateless || 533 value == DHCPConf::both); 534 auto old6 = EthernetInterfaceIntf::dhcp6(); 535 auto new6 = EthernetInterfaceIntf::dhcp6(value == DHCPConf::v6 || 536 value == DHCPConf::both); 537 auto oldra = EthernetInterfaceIntf::ipv6AcceptRA(); 538 auto newra = EthernetInterfaceIntf::ipv6AcceptRA( 539 value == DHCPConf::v6stateless || value == DHCPConf::v4v6stateless || 540 value == DHCPConf::v6 || value == DHCPConf::both); 541 542 if (old4 != new4 || old6 != new6 || oldra != newra) 543 { 544 writeConfigurationFile(); 545 manager.reloadConfigs(); 546 } 547 return value; 548 } 549 550 EthernetInterface::DHCPConf EthernetInterface::dhcpEnabled() const 551 { 552 if (dhcp6()) 553 { 554 return dhcp4() ? DHCPConf::both : DHCPConf::v6; 555 } 556 else if (dhcp4()) 557 { 558 return ipv6AcceptRA() ? DHCPConf::v4v6stateless : DHCPConf::v4; 559 } 560 return ipv6AcceptRA() ? DHCPConf::v6stateless : DHCPConf::none; 561 } 562 563 bool EthernetInterface::linkUp() const 564 { 565 bool value = EthernetInterfaceIntf::linkUp(); 566 567 ifreq ifr = {}; 568 std::strncpy(ifr.ifr_name, interfaceName().c_str(), IF_NAMESIZE - 1); 569 try 570 { 571 getIFSock().ioctl(SIOCGIFFLAGS, &ifr); 572 value = static_cast<bool>(ifr.ifr_flags & IFF_RUNNING); 573 } 574 catch (const std::exception& e) 575 { 576 log<level::ERR>("ioctl failed for SIOCGIFFLAGS:", 577 entry("ERROR=%s", e.what())); 578 } 579 return value; 580 } 581 582 size_t EthernetInterface::mtu() const 583 { 584 size_t value = EthernetInterfaceIntf::mtu(); 585 586 ifreq ifr = {}; 587 std::strncpy(ifr.ifr_name, interfaceName().c_str(), IF_NAMESIZE - 1); 588 try 589 { 590 getIFSock().ioctl(SIOCGIFMTU, &ifr); 591 value = ifr.ifr_mtu; 592 } 593 catch (const std::exception& e) 594 { 595 log<level::ERR>("ioctl failed for SIOCGIFMTU:", 596 entry("ERROR=%s", e.what())); 597 } 598 return value; 599 } 600 601 size_t EthernetInterface::mtu(size_t value) 602 { 603 if (value == EthernetInterfaceIntf::mtu()) 604 { 605 return value; 606 } 607 else if (value == 0) 608 { 609 return EthernetInterfaceIntf::mtu(); 610 } 611 612 ifreq ifr = {}; 613 std::strncpy(ifr.ifr_name, interfaceName().c_str(), IF_NAMESIZE - 1); 614 ifr.ifr_mtu = value; 615 616 try 617 { 618 getIFSock().ioctl(SIOCSIFMTU, &ifr); 619 } 620 catch (const std::exception& e) 621 { 622 log<level::ERR>("ioctl failed for SIOCSIFMTU:", 623 entry("ERROR=%s", strerror(errno))); 624 return EthernetInterfaceIntf::mtu(); 625 } 626 627 EthernetInterfaceIntf::mtu(value); 628 return value; 629 } 630 631 bool EthernetInterface::queryNicEnabled() const 632 { 633 constexpr auto svc = "org.freedesktop.network1"; 634 constexpr auto intf = "org.freedesktop.network1.Link"; 635 constexpr auto prop = "AdministrativeState"; 636 char* rpath; 637 sd_bus_path_encode("/org/freedesktop/network1/link", 638 std::to_string(ifIndex()).c_str(), &rpath); 639 std::string path(rpath); 640 free(rpath); 641 642 // Store / Parser for the AdministrativeState return value 643 std::optional<bool> ret; 644 auto cb = [&](std::string_view state) { 645 if (state != "initialized") 646 { 647 ret = state != "unmanaged"; 648 } 649 }; 650 651 // Build a matcher before making the property call to ensure we 652 // can eventually get the value. 653 sdbusplus::bus::match_t match( 654 bus, 655 fmt::format("type='signal',sender='{}',path='{}',interface='{}',member=" 656 "'PropertiesChanged',arg0='{}',", 657 svc, path, PROPERTY_INTERFACE, intf) 658 .c_str(), 659 [&](sdbusplus::message_t& m) { 660 std::string intf; 661 std::unordered_map<std::string, std::variant<std::string>> values; 662 try 663 { 664 m.read(intf, values); 665 auto it = values.find(prop); 666 // Ignore properties that aren't AdministrativeState 667 if (it != values.end()) 668 { 669 cb(std::get<std::string>(it->second)); 670 } 671 } 672 catch (const std::exception& e) 673 { 674 log<level::ERR>( 675 fmt::format( 676 "AdministrativeState match parsing failed on {}: {}", 677 interfaceName(), e.what()) 678 .c_str(), 679 entry("INTERFACE=%s", interfaceName().c_str()), 680 entry("ERROR=%s", e.what())); 681 } 682 }); 683 684 // Actively call for the value in case the interface is already configured 685 auto method = 686 bus.new_method_call(svc, path.c_str(), PROPERTY_INTERFACE, METHOD_GET); 687 method.append(intf, prop); 688 try 689 { 690 auto reply = bus.call(method); 691 std::variant<std::string> state; 692 reply.read(state); 693 cb(std::get<std::string>(state)); 694 } 695 catch (const std::exception& e) 696 { 697 log<level::ERR>( 698 fmt::format("Failed to get AdministrativeState on {}: {}", 699 interfaceName(), e.what()) 700 .c_str(), 701 entry("INTERFACE=%s", interfaceName().c_str()), 702 entry("ERROR=%s", e.what())); 703 } 704 705 // The interface is not yet configured by systemd-networkd, wait until it 706 // signals us a valid state. 707 while (!ret) 708 { 709 bus.wait(); 710 bus.process_discard(); 711 } 712 713 return *ret; 714 } 715 716 static void setNICAdminState(stdplus::const_zstring intf, bool up) 717 { 718 ifreq ifr = {}; 719 std::strncpy(ifr.ifr_name, intf.data(), IF_NAMESIZE - 1); 720 getIFSock().ioctl(SIOCGIFFLAGS, &ifr); 721 722 ifr.ifr_flags &= ~IFF_UP; 723 ifr.ifr_flags |= up ? IFF_UP : 0; 724 getIFSock().ioctl(SIOCSIFFLAGS, &ifr); 725 } 726 727 bool EthernetInterface::nicEnabled(bool value) 728 { 729 if (value == EthernetInterfaceIntf::nicEnabled()) 730 { 731 return value; 732 } 733 734 EthernetInterfaceIntf::nicEnabled(value); 735 writeConfigurationFile(); 736 if (!value) 737 { 738 // We only need to bring down the interface, networkd will always bring 739 // up managed interfaces 740 manager.addReloadPreHook( 741 [ifname = interfaceName()]() { setNICAdminState(ifname, false); }); 742 } 743 manager.reloadConfigs(); 744 745 return value; 746 } 747 748 ServerList EthernetInterface::staticNameServers(ServerList value) 749 { 750 for (const auto& nameserverip : value) 751 { 752 if (!isValidIP(nameserverip)) 753 { 754 log<level::ERR>("Not a valid IP address"), 755 entry("ADDRESS=%s", nameserverip.c_str()); 756 elog<InvalidArgument>( 757 Argument::ARGUMENT_NAME("StaticNameserver"), 758 Argument::ARGUMENT_VALUE(nameserverip.c_str())); 759 } 760 } 761 try 762 { 763 EthernetInterfaceIntf::staticNameServers(value); 764 765 writeConfigurationFile(); 766 manager.reloadConfigs(); 767 } 768 catch (const InternalFailure& e) 769 { 770 log<level::ERR>("Exception processing DNS entries"); 771 } 772 return EthernetInterfaceIntf::staticNameServers(); 773 } 774 775 void EthernetInterface::loadNTPServers(const config::Parser& config) 776 { 777 EthernetInterfaceIntf::ntpServers(getNTPServerFromTimeSyncd()); 778 EthernetInterfaceIntf::staticNTPServers( 779 config.map.getValueStrings("Network", "NTP")); 780 } 781 782 void EthernetInterface::loadNameServers(const config::Parser& config) 783 { 784 EthernetInterfaceIntf::nameservers(getNameServerFromResolvd()); 785 EthernetInterfaceIntf::staticNameServers( 786 config.map.getValueStrings("Network", "DNS")); 787 } 788 789 ServerList EthernetInterface::getNTPServerFromTimeSyncd() 790 { 791 ServerList servers; // Variable to capture the NTP Server IPs 792 auto method = bus.new_method_call(TIMESYNCD_SERVICE, TIMESYNCD_SERVICE_PATH, 793 PROPERTY_INTERFACE, METHOD_GET); 794 795 method.append(TIMESYNCD_INTERFACE, "LinkNTPServers"); 796 797 try 798 { 799 auto reply = bus.call(method); 800 std::variant<ServerList> response; 801 reply.read(response); 802 servers = std::get<ServerList>(response); 803 } 804 catch (const sdbusplus::exception::SdBusError& e) 805 { 806 log<level::ERR>( 807 "Failed to get NTP server information from Systemd-Timesyncd"); 808 } 809 810 return servers; 811 } 812 813 ServerList EthernetInterface::getNameServerFromResolvd() 814 { 815 ServerList servers; 816 std::string OBJ_PATH = RESOLVED_SERVICE_PATH + std::to_string(ifIndex()); 817 818 /* 819 The DNS property under org.freedesktop.resolve1.Link interface contains 820 an array containing all DNS servers currently used by resolved. It 821 contains similar information as the DNS server data written to 822 /run/systemd/resolve/resolv.conf. 823 824 Each structure in the array consists of a numeric network interface index, 825 an address family, and a byte array containing the DNS server address 826 (either 4 bytes in length for IPv4 or 16 bytes in lengths for IPv6). 827 The array contains DNS servers configured system-wide, including those 828 possibly read from a foreign /etc/resolv.conf or the DNS= setting in 829 /etc/systemd/resolved.conf, as well as per-interface DNS server 830 information either retrieved from systemd-networkd or configured by 831 external software via SetLinkDNS(). 832 */ 833 834 using type = std::vector<std::tuple<int32_t, std::vector<uint8_t>>>; 835 std::variant<type> name; // Variable to capture the DNS property 836 auto method = bus.new_method_call(RESOLVED_SERVICE, OBJ_PATH.c_str(), 837 PROPERTY_INTERFACE, METHOD_GET); 838 839 method.append(RESOLVED_INTERFACE, "DNS"); 840 841 try 842 { 843 auto reply = bus.call(method); 844 reply.read(name); 845 } 846 catch (const sdbusplus::exception_t& e) 847 { 848 log<level::ERR>("Failed to get DNS information from Systemd-Resolved"); 849 } 850 auto tupleVector = std::get_if<type>(&name); 851 for (auto i = tupleVector->begin(); i != tupleVector->end(); ++i) 852 { 853 int addressFamily = std::get<0>(*i); 854 std::vector<uint8_t>& ipaddress = std::get<1>(*i); 855 856 switch (addressFamily) 857 { 858 case AF_INET: 859 if (ipaddress.size() == sizeof(struct in_addr)) 860 { 861 servers.push_back(toString( 862 *reinterpret_cast<struct in_addr*>(ipaddress.data()))); 863 } 864 else 865 { 866 log<level::ERR>( 867 "Invalid data recived from Systemd-Resolved"); 868 } 869 break; 870 871 case AF_INET6: 872 if (ipaddress.size() == sizeof(struct in6_addr)) 873 { 874 servers.push_back(toString( 875 *reinterpret_cast<struct in6_addr*>(ipaddress.data()))); 876 } 877 else 878 { 879 log<level::ERR>( 880 "Invalid data recived from Systemd-Resolved"); 881 } 882 break; 883 884 default: 885 log<level::ERR>( 886 "Unsupported address family in DNS from Systemd-Resolved"); 887 break; 888 } 889 } 890 return servers; 891 } 892 893 std::string EthernetInterface::vlanIntfName(VlanId id) const 894 { 895 return fmt::format(FMT_COMPILE("{}.{}"), interfaceName(), id); 896 } 897 898 std::string EthernetInterface::vlanObjPath(VlanId id) const 899 { 900 return fmt::format(FMT_COMPILE("{}_{}"), objPath, id); 901 } 902 903 void EthernetInterface::loadVLAN(VlanId id) 904 { 905 auto vlanInterfaceName = vlanIntfName(id); 906 auto path = vlanObjPath(id); 907 908 config::Parser config( 909 config::pathForIntfConf(manager.getConfDir(), vlanInterfaceName)); 910 911 auto vlanIntf = std::make_unique<phosphor::network::VlanInterface>( 912 bus, path.c_str(), config, EthernetInterfaceIntf::nicEnabled(), id, 913 *this, manager); 914 915 // Fetch the ip address from the system 916 // and create the dbus object. 917 vlanIntf->createIPAddressObjects(); 918 vlanIntf->createStaticNeighborObjects(); 919 vlanIntf->loadNameServers(config); 920 vlanIntf->loadNTPServers(config); 921 922 this->vlanInterfaces.emplace(std::move(vlanInterfaceName), 923 std::move(vlanIntf)); 924 } 925 926 ObjectPath EthernetInterface::createVLAN(VlanId id) 927 { 928 auto vlanInterfaceName = vlanIntfName(id); 929 if (this->vlanInterfaces.find(vlanInterfaceName) != 930 this->vlanInterfaces.end()) 931 { 932 log<level::ERR>("VLAN already exists", entry("VLANID=%u", id)); 933 elog<InvalidArgument>( 934 Argument::ARGUMENT_NAME("VLANId"), 935 Argument::ARGUMENT_VALUE(std::to_string(id).c_str())); 936 } 937 938 auto path = vlanObjPath(id); 939 940 // Pass the parents nicEnabled property, so that the child 941 // VLAN interface can inherit. 942 auto vlanIntf = std::make_unique<phosphor::network::VlanInterface>( 943 bus, path.c_str(), config::Parser(), 944 EthernetInterfaceIntf::nicEnabled(), id, *this, manager); 945 946 // write the device file for the vlan interface. 947 vlanIntf->writeDeviceFile(); 948 949 this->vlanInterfaces.emplace(vlanInterfaceName, std::move(vlanIntf)); 950 951 writeConfigurationFile(); 952 manager.reloadConfigs(); 953 954 return path; 955 } 956 957 ServerList EthernetInterface::staticNTPServers(ServerList value) 958 { 959 try 960 { 961 EthernetInterfaceIntf::staticNTPServers(value); 962 963 writeConfigurationFile(); 964 manager.reloadConfigs(); 965 } 966 catch (InternalFailure& e) 967 { 968 log<level::ERR>("Exception processing NTP entries"); 969 } 970 return EthernetInterfaceIntf::staticNTPServers(); 971 } 972 973 ServerList EthernetInterface::ntpServers(ServerList /*servers*/) 974 { 975 elog<NotAllowed>(NotAllowedArgument::REASON("ReadOnly Property")); 976 } 977 // Need to merge the below function with the code which writes the 978 // config file during factory reset. 979 // TODO openbmc/openbmc#1751 980 981 void EthernetInterface::writeConfigurationFile() 982 { 983 for (const auto& intf : vlanInterfaces) 984 { 985 intf.second->writeConfigurationFile(); 986 } 987 988 config::Parser config; 989 config.map["Match"].emplace_back()["Name"].emplace_back(interfaceName()); 990 { 991 auto& link = config.map["Link"].emplace_back(); 992 #ifdef PERSIST_MAC 993 auto mac = MacAddressIntf::macAddress(); 994 if (!mac.empty()) 995 { 996 link["MACAddress"].emplace_back(mac); 997 } 998 #endif 999 if (!EthernetInterfaceIntf::nicEnabled()) 1000 { 1001 link["Unmanaged"].emplace_back("yes"); 1002 } 1003 } 1004 { 1005 auto& network = config.map["Network"].emplace_back(); 1006 auto& lla = network["LinkLocalAddressing"]; 1007 #ifdef LINK_LOCAL_AUTOCONFIGURATION 1008 lla.emplace_back("yes"); 1009 #else 1010 lla.emplace_back("no"); 1011 #endif 1012 network["IPv6AcceptRA"].emplace_back(ipv6AcceptRA() ? "true" : "false"); 1013 network["DHCP"].emplace_back(dhcp4() ? (dhcp6() ? "true" : "ipv4") 1014 : (dhcp6() ? "ipv6" : "false")); 1015 { 1016 auto& vlans = network["VLAN"]; 1017 for (const auto& intf : vlanInterfaces) 1018 { 1019 vlans.emplace_back( 1020 intf.second->EthernetInterface::interfaceName()); 1021 } 1022 } 1023 { 1024 auto& ntps = network["NTP"]; 1025 for (const auto& ntp : EthernetInterfaceIntf::staticNTPServers()) 1026 { 1027 ntps.emplace_back(ntp); 1028 } 1029 } 1030 { 1031 auto& dnss = network["DNS"]; 1032 for (const auto& dns : EthernetInterfaceIntf::staticNameServers()) 1033 { 1034 dnss.emplace_back(dns); 1035 } 1036 } 1037 { 1038 auto& address = network["Address"]; 1039 for (const auto& addr : getAddresses()) 1040 { 1041 if (originIsManuallyAssigned(addr.second->origin()) && 1042 !dhcpIsEnabled(addr.second->type())) 1043 { 1044 address.emplace_back( 1045 fmt::format("{}/{}", addr.second->address(), 1046 addr.second->prefixLength())); 1047 } 1048 } 1049 } 1050 { 1051 auto& gateways = network["Gateway"]; 1052 if (!dhcp4()) 1053 { 1054 auto gateway = EthernetInterfaceIntf::defaultGateway(); 1055 if (!gateway.empty()) 1056 { 1057 gateways.emplace_back(gateway); 1058 } 1059 } 1060 1061 if (!dhcp6()) 1062 { 1063 auto gateway6 = EthernetInterfaceIntf::defaultGateway6(); 1064 if (!gateway6.empty()) 1065 { 1066 gateways.emplace_back(gateway6); 1067 } 1068 } 1069 } 1070 } 1071 config.map["IPv6AcceptRA"].emplace_back()["DHCPv6Client"].emplace_back( 1072 dhcp6() ? "true" : "false"); 1073 { 1074 auto& neighbors = config.map["Neighbor"]; 1075 for (const auto& sneighbor : staticNeighbors) 1076 { 1077 auto& neighbor = neighbors.emplace_back(); 1078 neighbor["Address"].emplace_back(sneighbor.second->ipAddress()); 1079 neighbor["MACAddress"].emplace_back(sneighbor.second->macAddress()); 1080 } 1081 } 1082 { 1083 auto& dhcp = config.map["DHCP"].emplace_back(); 1084 dhcp["ClientIdentifier"].emplace_back("mac"); 1085 if (manager.getDHCPConf()) 1086 { 1087 const auto& conf = *manager.getDHCPConf(); 1088 auto dns_enabled = conf.dnsEnabled() ? "true" : "false"; 1089 dhcp["UseDNS"].emplace_back(dns_enabled); 1090 dhcp["UseDomains"].emplace_back(dns_enabled); 1091 dhcp["UseNTP"].emplace_back(conf.ntpEnabled() ? "true" : "false"); 1092 dhcp["UseHostname"].emplace_back(conf.hostNameEnabled() ? "true" 1093 : "false"); 1094 dhcp["SendHostname"].emplace_back( 1095 conf.sendHostNameEnabled() ? "true" : "false"); 1096 } 1097 } 1098 auto path = config::pathForIntfConf(manager.getConfDir(), interfaceName()); 1099 config.writeFile(path); 1100 auto msg = fmt::format("Wrote networkd file: {}", path.native()); 1101 log<level::INFO>(msg.c_str(), entry("FILE=%s", path.c_str())); 1102 } 1103 1104 std::string EthernetInterface::macAddress([[maybe_unused]] std::string value) 1105 { 1106 #ifdef PERSIST_MAC 1107 ether_addr newMAC; 1108 try 1109 { 1110 newMAC = mac_address::fromString(value); 1111 } 1112 catch (const std::invalid_argument&) 1113 { 1114 log<level::ERR>("MACAddress is not valid.", 1115 entry("MAC=%s", value.c_str())); 1116 elog<InvalidArgument>(Argument::ARGUMENT_NAME("MACAddress"), 1117 Argument::ARGUMENT_VALUE(value.c_str())); 1118 } 1119 if (!mac_address::isUnicast(newMAC)) 1120 { 1121 log<level::ERR>("MACAddress is not valid.", 1122 entry("MAC=%s", value.c_str())); 1123 elog<InvalidArgument>(Argument::ARGUMENT_NAME("MACAddress"), 1124 Argument::ARGUMENT_VALUE(value.c_str())); 1125 } 1126 1127 auto interface = interfaceName(); 1128 std::string validMAC = mac_address::toString(newMAC); 1129 1130 // We don't need to update the system if the address is unchanged 1131 ether_addr oldMAC = mac_address::fromString(MacAddressIntf::macAddress()); 1132 if (!stdplus::raw::equal(newMAC, oldMAC)) 1133 { 1134 // Update everything that depends on the MAC value 1135 for (const auto& [name, intf] : vlanInterfaces) 1136 { 1137 intf->MacAddressIntf::macAddress(validMAC); 1138 } 1139 MacAddressIntf::macAddress(validMAC); 1140 1141 writeConfigurationFile(); 1142 manager.addReloadPreHook([interface]() { 1143 // The MAC and LLADDRs will only update if the NIC is already down 1144 setNICAdminState(interface, false); 1145 }); 1146 manager.reloadConfigs(); 1147 } 1148 1149 #ifdef HAVE_UBOOT_ENV 1150 // Ensure that the valid address is stored in the u-boot-env 1151 auto envVar = interfaceToUbootEthAddr(interface); 1152 if (envVar) 1153 { 1154 // Trimming MAC addresses that are out of range. eg: AA:FF:FF:FF:FF:100; 1155 // and those having more than 6 bytes. eg: AA:AA:AA:AA:AA:AA:BB 1156 execute("/sbin/fw_setenv", "fw_setenv", envVar->c_str(), 1157 validMAC.c_str()); 1158 } 1159 #endif // HAVE_UBOOT_ENV 1160 1161 return value; 1162 #else 1163 elog<NotAllowed>( 1164 NotAllowedArgument::REASON("Writing MAC address is not allowed")); 1165 #endif // PERSIST_MAC 1166 } 1167 1168 void EthernetInterface::deleteAll() 1169 { 1170 // clear all the ip on the interface 1171 addrs.clear(); 1172 1173 writeConfigurationFile(); 1174 manager.reloadConfigs(); 1175 } 1176 1177 std::string EthernetInterface::defaultGateway(std::string gateway) 1178 { 1179 auto gw = EthernetInterfaceIntf::defaultGateway(); 1180 if (gw == gateway) 1181 { 1182 return gw; 1183 } 1184 1185 if (!isValidIP(AF_INET, gateway) && !gateway.empty()) 1186 { 1187 log<level::ERR>("Not a valid v4 Gateway", 1188 entry("GATEWAY=%s", gateway.c_str())); 1189 elog<InvalidArgument>(Argument::ARGUMENT_NAME("GATEWAY"), 1190 Argument::ARGUMENT_VALUE(gateway.c_str())); 1191 } 1192 gw = EthernetInterfaceIntf::defaultGateway(gateway); 1193 1194 writeConfigurationFile(); 1195 manager.reloadConfigs(); 1196 1197 return gw; 1198 } 1199 1200 std::string EthernetInterface::defaultGateway6(std::string gateway) 1201 { 1202 auto gw = EthernetInterfaceIntf::defaultGateway6(); 1203 if (gw == gateway) 1204 { 1205 return gw; 1206 } 1207 1208 if (!isValidIP(AF_INET6, gateway) && !gateway.empty()) 1209 { 1210 log<level::ERR>("Not a valid v6 Gateway", 1211 entry("GATEWAY=%s", gateway.c_str())); 1212 elog<InvalidArgument>(Argument::ARGUMENT_NAME("GATEWAY"), 1213 Argument::ARGUMENT_VALUE(gateway.c_str())); 1214 } 1215 gw = EthernetInterfaceIntf::defaultGateway6(gateway); 1216 1217 writeConfigurationFile(); 1218 manager.reloadConfigs(); 1219 1220 return gw; 1221 } 1222 } // namespace network 1223 } // namespace phosphor 1224