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