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 auto ifname = interfaceName(); 788 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 = std::move(ifname), eifSocket = std::move(eifSocket)]() { 796 setNICAdminState(eifSocket.sock, ifname.c_str(), false); 797 }); 798 } 799 EthernetInterfaceIntf::nicEnabled(value); 800 manager.reloadConfigs(); 801 802 return value; 803 } 804 805 ServerList EthernetInterface::nameservers(ServerList /*value*/) 806 { 807 elog<NotAllowed>(NotAllowedArgument::REASON("ReadOnly Property")); 808 return EthernetInterfaceIntf::nameservers(); 809 } 810 811 ServerList EthernetInterface::staticNameServers(ServerList value) 812 { 813 for (const auto& nameserverip : value) 814 { 815 if (!isValidIP(AF_INET, nameserverip) && 816 !isValidIP(AF_INET6, nameserverip)) 817 { 818 log<level::ERR>("Not a valid IP address"), 819 entry("ADDRESS=%s", nameserverip.c_str()); 820 elog<InvalidArgument>( 821 Argument::ARGUMENT_NAME("StaticNameserver"), 822 Argument::ARGUMENT_VALUE(nameserverip.c_str())); 823 } 824 } 825 try 826 { 827 EthernetInterfaceIntf::staticNameServers(value); 828 829 writeConfigurationFile(); 830 manager.reloadConfigs(); 831 } 832 catch (const InternalFailure& e) 833 { 834 log<level::ERR>("Exception processing DNS entries"); 835 } 836 return EthernetInterfaceIntf::staticNameServers(); 837 } 838 839 void EthernetInterface::loadNameServers() 840 { 841 EthernetInterfaceIntf::nameservers(getNameServerFromResolvd()); 842 EthernetInterfaceIntf::staticNameServers(getstaticNameServerFromConf()); 843 } 844 845 ServerList EthernetInterface::getstaticNameServerFromConf() 846 { 847 fs::path confPath = manager.getConfDir(); 848 849 std::string fileName = systemd::config::networkFilePrefix + 850 interfaceName() + systemd::config::networkFileSuffix; 851 confPath /= fileName; 852 ServerList servers; 853 config::Parser parser(confPath.string()); 854 auto rc = config::ReturnCode::SUCCESS; 855 856 std::tie(rc, servers) = parser.getValues("Network", "DNS"); 857 if (rc != config::ReturnCode::SUCCESS) 858 { 859 log<level::DEBUG>("Unable to get the value for network[DNS]", 860 entry("RC=%d", rc)); 861 } 862 return servers; 863 } 864 865 ServerList EthernetInterface::getNameServerFromResolvd() 866 { 867 ServerList servers; 868 std::string OBJ_PATH = RESOLVED_SERVICE_PATH + std::to_string(ifIndex()); 869 870 /* 871 The DNS property under org.freedesktop.resolve1.Link interface contains 872 an array containing all DNS servers currently used by resolved. It 873 contains similar information as the DNS server data written to 874 /run/systemd/resolve/resolv.conf. 875 876 Each structure in the array consists of a numeric network interface index, 877 an address family, and a byte array containing the DNS server address 878 (either 4 bytes in length for IPv4 or 16 bytes in lengths for IPv6). 879 The array contains DNS servers configured system-wide, including those 880 possibly read from a foreign /etc/resolv.conf or the DNS= setting in 881 /etc/systemd/resolved.conf, as well as per-interface DNS server 882 information either retrieved from systemd-networkd or configured by 883 external software via SetLinkDNS(). 884 */ 885 886 using type = std::vector<std::tuple<int32_t, std::vector<uint8_t>>>; 887 std::variant<type> name; // Variable to capture the DNS property 888 auto method = bus.new_method_call(RESOLVED_SERVICE, OBJ_PATH.c_str(), 889 PROPERTY_INTERFACE, METHOD_GET); 890 891 method.append(RESOLVED_INTERFACE, "DNS"); 892 auto reply = bus.call(method); 893 894 try 895 { 896 reply.read(name); 897 } 898 catch (const sdbusplus::exception::exception& e) 899 { 900 log<level::ERR>("Failed to get DNS information from Systemd-Resolved"); 901 } 902 auto tupleVector = std::get_if<type>(&name); 903 for (auto i = tupleVector->begin(); i != tupleVector->end(); ++i) 904 { 905 int addressFamily = std::get<0>(*i); 906 std::vector<uint8_t>& ipaddress = std::get<1>(*i); 907 908 switch (addressFamily) 909 { 910 case AF_INET: 911 if (ipaddress.size() == sizeof(struct in_addr)) 912 { 913 servers.push_back(toString( 914 *reinterpret_cast<struct in_addr*>(ipaddress.data()))); 915 } 916 else 917 { 918 log<level::ERR>( 919 "Invalid data recived from Systemd-Resolved"); 920 } 921 break; 922 923 case AF_INET6: 924 if (ipaddress.size() == sizeof(struct in6_addr)) 925 { 926 servers.push_back(toString( 927 *reinterpret_cast<struct in6_addr*>(ipaddress.data()))); 928 } 929 else 930 { 931 log<level::ERR>( 932 "Invalid data recived from Systemd-Resolved"); 933 } 934 break; 935 936 default: 937 log<level::ERR>( 938 "Unsupported address family in DNS from Systemd-Resolved"); 939 break; 940 } 941 } 942 return servers; 943 } 944 945 void EthernetInterface::loadVLAN(VlanId id) 946 { 947 std::string vlanInterfaceName = interfaceName() + "." + std::to_string(id); 948 std::string path = objPath; 949 path += "_" + std::to_string(id); 950 951 DHCPConf dhcpEnabled = 952 getDHCPValue(manager.getConfDir().string(), vlanInterfaceName); 953 auto vlanIntf = std::make_unique<phosphor::network::VlanInterface>( 954 bus, path.c_str(), dhcpEnabled, EthernetInterfaceIntf::nicEnabled(), id, 955 *this, manager); 956 957 // Fetch the ip address from the system 958 // and create the dbus object. 959 vlanIntf->createIPAddressObjects(); 960 vlanIntf->createStaticNeighborObjects(); 961 vlanIntf->loadNameServers(); 962 963 this->vlanInterfaces.emplace(std::move(vlanInterfaceName), 964 std::move(vlanIntf)); 965 } 966 967 ObjectPath EthernetInterface::createVLAN(VlanId id) 968 { 969 std::string vlanInterfaceName = interfaceName() + "." + std::to_string(id); 970 std::string path = objPath; 971 path += "_" + std::to_string(id); 972 973 // Pass the parents nicEnabled property, so that the child 974 // VLAN interface can inherit. 975 976 auto vlanIntf = std::make_unique<phosphor::network::VlanInterface>( 977 bus, path.c_str(), EthernetInterface::DHCPConf::none, 978 EthernetInterfaceIntf::nicEnabled(), id, *this, manager); 979 980 // write the device file for the vlan interface. 981 vlanIntf->writeDeviceFile(); 982 983 this->vlanInterfaces.emplace(vlanInterfaceName, std::move(vlanIntf)); 984 985 writeConfigurationFile(); 986 manager.reloadConfigs(); 987 988 return path; 989 } 990 991 bool EthernetInterface::getIPv6AcceptRAFromConf() 992 { 993 fs::path confPath = manager.getConfDir(); 994 995 std::string fileName = systemd::config::networkFilePrefix + 996 interfaceName() + systemd::config::networkFileSuffix; 997 confPath /= fileName; 998 config::ValueList values; 999 config::Parser parser(confPath.string()); 1000 auto rc = config::ReturnCode::SUCCESS; 1001 std::tie(rc, values) = parser.getValues("Network", "IPv6AcceptRA"); 1002 if (rc != config::ReturnCode::SUCCESS) 1003 { 1004 log<level::DEBUG>("Unable to get the value for Network[IPv6AcceptRA]", 1005 entry("rc=%d", rc)); 1006 return false; 1007 } 1008 return (values[0] == "true"); 1009 } 1010 1011 ServerList EthernetInterface::getNTPServersFromConf() 1012 { 1013 fs::path confPath = manager.getConfDir(); 1014 1015 std::string fileName = systemd::config::networkFilePrefix + 1016 interfaceName() + systemd::config::networkFileSuffix; 1017 confPath /= fileName; 1018 1019 ServerList servers; 1020 config::Parser parser(confPath.string()); 1021 auto rc = config::ReturnCode::SUCCESS; 1022 1023 std::tie(rc, servers) = parser.getValues("Network", "NTP"); 1024 if (rc != config::ReturnCode::SUCCESS) 1025 { 1026 log<level::DEBUG>("Unable to get the value for Network[NTP]", 1027 entry("rc=%d", rc)); 1028 } 1029 1030 return servers; 1031 } 1032 1033 ServerList EthernetInterface::ntpServers(ServerList servers) 1034 { 1035 auto ntpServers = EthernetInterfaceIntf::ntpServers(servers); 1036 1037 writeConfigurationFile(); 1038 manager.reloadConfigs(); 1039 1040 return ntpServers; 1041 } 1042 // Need to merge the below function with the code which writes the 1043 // config file during factory reset. 1044 // TODO openbmc/openbmc#1751 1045 1046 void EthernetInterface::writeConfigurationFile() 1047 { 1048 // write all the static ip address in the systemd-network conf file 1049 1050 using namespace std::string_literals; 1051 namespace fs = std::filesystem; 1052 1053 // if there is vlan interafce then write the configuration file 1054 // for vlan also. 1055 1056 for (const auto& intf : vlanInterfaces) 1057 { 1058 intf.second->writeConfigurationFile(); 1059 } 1060 1061 fs::path confPath = manager.getConfDir(); 1062 1063 std::string fileName = systemd::config::networkFilePrefix + 1064 interfaceName() + systemd::config::networkFileSuffix; 1065 confPath /= fileName; 1066 std::fstream stream; 1067 1068 stream.open(confPath.c_str(), std::fstream::out); 1069 if (!stream.is_open()) 1070 { 1071 log<level::ERR>("Unable to open the file", 1072 entry("FILE=%s", confPath.c_str())); 1073 elog<InternalFailure>(); 1074 } 1075 1076 // Write the device 1077 stream << "[Match]\n"; 1078 stream << "Name=" << interfaceName() << "\n"; 1079 1080 auto addrs = getAddresses(); 1081 1082 // Write the link section 1083 stream << "[Link]\n"; 1084 auto mac = MacAddressIntf::macAddress(); 1085 if (!mac.empty()) 1086 { 1087 stream << "MACAddress=" << mac << "\n"; 1088 } 1089 1090 if (!EthernetInterfaceIntf::nicEnabled()) 1091 { 1092 stream << "Unmanaged=yes\n"; 1093 } 1094 1095 // write the network section 1096 stream << "[Network]\n"; 1097 #ifdef LINK_LOCAL_AUTOCONFIGURATION 1098 stream << "LinkLocalAddressing=yes\n"; 1099 #else 1100 stream << "LinkLocalAddressing=no\n"; 1101 #endif 1102 stream << std::boolalpha 1103 << "IPv6AcceptRA=" << EthernetInterfaceIntf::ipv6AcceptRA() << "\n"; 1104 1105 // Add the VLAN entry 1106 for (const auto& intf : vlanInterfaces) 1107 { 1108 stream << "VLAN=" << intf.second->EthernetInterface::interfaceName() 1109 << "\n"; 1110 } 1111 // Add the NTP server 1112 for (const auto& ntp : EthernetInterfaceIntf::ntpServers()) 1113 { 1114 stream << "NTP=" << ntp << "\n"; 1115 } 1116 1117 // Add the DNS entry 1118 for (const auto& dns : EthernetInterfaceIntf::staticNameServers()) 1119 { 1120 stream << "DNS=" << dns << "\n"; 1121 } 1122 1123 // Add the DHCP entry 1124 stream << "DHCP="s + 1125 mapDHCPToSystemd[EthernetInterfaceIntf::dhcpEnabled()] + "\n"; 1126 1127 stream << "[IPv6AcceptRA]\n"; 1128 stream << "DHCPv6Client="; 1129 stream << (dhcpIsEnabled(IP::Protocol::IPv6) ? "true" : "false"); 1130 stream << "\n"; 1131 1132 // Static IP addresses 1133 for (const auto& addr : addrs) 1134 { 1135 if (originIsManuallyAssigned(addr.second->origin()) && 1136 !dhcpIsEnabled(addr.second->type())) 1137 { 1138 // Process all static addresses 1139 std::string address = addr.second->address() + "/" + 1140 std::to_string(addr.second->prefixLength()); 1141 1142 // build the address entries. Do not use [Network] shortcuts to 1143 // insert address entries. 1144 stream << "[Address]\n"; 1145 stream << "Address=" << address << "\n"; 1146 } 1147 } 1148 1149 if (!dhcpIsEnabled(IP::Protocol::IPv4)) 1150 { 1151 auto gateway = EthernetInterfaceIntf::defaultGateway(); 1152 if (!gateway.empty()) 1153 { 1154 stream << "[Route]\n"; 1155 stream << "Gateway=" << gateway << "\n"; 1156 } 1157 } 1158 1159 if (!dhcpIsEnabled(IP::Protocol::IPv6)) 1160 { 1161 auto gateway6 = EthernetInterfaceIntf::defaultGateway6(); 1162 if (!gateway6.empty()) 1163 { 1164 stream << "[Route]\n"; 1165 stream << "Gateway=" << gateway6 << "\n"; 1166 } 1167 } 1168 1169 // Write the neighbor sections 1170 for (const auto& neighbor : staticNeighbors) 1171 { 1172 stream << "[Neighbor]" 1173 << "\n"; 1174 stream << "Address=" << neighbor.second->ipAddress() << "\n"; 1175 stream << "MACAddress=" << neighbor.second->macAddress() << "\n"; 1176 } 1177 1178 // Write the dhcp section irrespective of whether DHCP is enabled or not 1179 writeDHCPSection(stream); 1180 1181 stream.close(); 1182 } 1183 1184 void EthernetInterface::writeDHCPSection(std::fstream& stream) 1185 { 1186 using namespace std::string_literals; 1187 // write the dhcp section 1188 stream << "[DHCP]\n"; 1189 1190 // Hardcoding the client identifier to mac, to address below issue 1191 // https://github.com/openbmc/openbmc/issues/1280 1192 stream << "ClientIdentifier=mac\n"; 1193 if (manager.getDHCPConf()) 1194 { 1195 auto value = manager.getDHCPConf()->dnsEnabled() ? "true"s : "false"s; 1196 stream << "UseDNS="s + value + "\n"; 1197 1198 value = manager.getDHCPConf()->ntpEnabled() ? "true"s : "false"s; 1199 stream << "UseNTP="s + value + "\n"; 1200 1201 value = manager.getDHCPConf()->hostNameEnabled() ? "true"s : "false"s; 1202 stream << "UseHostname="s + value + "\n"; 1203 1204 value = 1205 manager.getDHCPConf()->sendHostNameEnabled() ? "true"s : "false"s; 1206 stream << "SendHostname="s + value + "\n"; 1207 } 1208 } 1209 1210 std::string EthernetInterface::macAddress(std::string value) 1211 { 1212 ether_addr newMAC; 1213 try 1214 { 1215 newMAC = mac_address::fromString(value); 1216 } 1217 catch (const std::invalid_argument&) 1218 { 1219 log<level::ERR>("MACAddress is not valid.", 1220 entry("MAC=%s", value.c_str())); 1221 elog<InvalidArgument>(Argument::ARGUMENT_NAME("MACAddress"), 1222 Argument::ARGUMENT_VALUE(value.c_str())); 1223 } 1224 if (!mac_address::isUnicast(newMAC)) 1225 { 1226 log<level::ERR>("MACAddress is not valid.", 1227 entry("MAC=%s", value.c_str())); 1228 elog<InvalidArgument>(Argument::ARGUMENT_NAME("MACAddress"), 1229 Argument::ARGUMENT_VALUE(value.c_str())); 1230 } 1231 1232 auto interface = interfaceName(); 1233 std::string validMAC = mac_address::toString(newMAC); 1234 1235 // We don't need to update the system if the address is unchanged 1236 ether_addr oldMAC = mac_address::fromString(MacAddressIntf::macAddress()); 1237 if (!stdplus::raw::equal(newMAC, oldMAC)) 1238 { 1239 // Update everything that depends on the MAC value 1240 for (const auto& [name, intf] : vlanInterfaces) 1241 { 1242 intf->MacAddressIntf::macAddress(validMAC); 1243 } 1244 MacAddressIntf::macAddress(validMAC); 1245 1246 writeConfigurationFile(); 1247 manager.addReloadPreHook([interface]() { 1248 // The MAC and LLADDRs will only update if the NIC is already down 1249 EthernetIntfSocket eifSocket(PF_INET, SOCK_DGRAM, IPPROTO_IP); 1250 setNICAdminState(eifSocket.sock, interface.c_str(), false); 1251 }); 1252 manager.reloadConfigs(); 1253 } 1254 1255 #ifdef HAVE_UBOOT_ENV 1256 // Ensure that the valid address is stored in the u-boot-env 1257 auto envVar = interfaceToUbootEthAddr(interface.c_str()); 1258 if (envVar) 1259 { 1260 // Trimming MAC addresses that are out of range. eg: AA:FF:FF:FF:FF:100; 1261 // and those having more than 6 bytes. eg: AA:AA:AA:AA:AA:AA:BB 1262 execute("/sbin/fw_setenv", "fw_setenv", envVar->c_str(), 1263 validMAC.c_str()); 1264 } 1265 #endif // HAVE_UBOOT_ENV 1266 1267 return value; 1268 } 1269 1270 void EthernetInterface::deleteAll() 1271 { 1272 // clear all the ip on the interface 1273 addrs.clear(); 1274 1275 writeConfigurationFile(); 1276 manager.reloadConfigs(); 1277 } 1278 1279 std::string EthernetInterface::defaultGateway(std::string gateway) 1280 { 1281 auto gw = EthernetInterfaceIntf::defaultGateway(); 1282 if (gw == gateway) 1283 { 1284 return gw; 1285 } 1286 1287 if (!isValidIP(AF_INET, gateway)) 1288 { 1289 log<level::ERR>("Not a valid v4 Gateway", 1290 entry("GATEWAY=%s", gateway.c_str())); 1291 elog<InvalidArgument>(Argument::ARGUMENT_NAME("GATEWAY"), 1292 Argument::ARGUMENT_VALUE(gateway.c_str())); 1293 } 1294 gw = EthernetInterfaceIntf::defaultGateway(gateway); 1295 1296 writeConfigurationFile(); 1297 manager.reloadConfigs(); 1298 1299 return gw; 1300 } 1301 1302 std::string EthernetInterface::defaultGateway6(std::string gateway) 1303 { 1304 auto gw = EthernetInterfaceIntf::defaultGateway6(); 1305 if (gw == gateway) 1306 { 1307 return gw; 1308 } 1309 1310 if (!isValidIP(AF_INET6, gateway)) 1311 { 1312 log<level::ERR>("Not a valid v6 Gateway", 1313 entry("GATEWAY=%s", gateway.c_str())); 1314 elog<InvalidArgument>(Argument::ARGUMENT_NAME("GATEWAY"), 1315 Argument::ARGUMENT_VALUE(gateway.c_str())); 1316 } 1317 gw = EthernetInterfaceIntf::defaultGateway6(gateway); 1318 1319 writeConfigurationFile(); 1320 manager.reloadConfigs(); 1321 1322 return gw; 1323 } 1324 } // namespace network 1325 } // namespace phosphor 1326