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