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