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