1 /* 2 // Copyright (c) 2018 Intel Corporation 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 */ 16 #pragma once 17 18 #include "bmcweb_config.h" 19 20 #include "app.hpp" 21 #include "dbus_singleton.hpp" 22 #include "dbus_utility.hpp" 23 #include "error_messages.hpp" 24 #include "health.hpp" 25 #include "human_sort.hpp" 26 #include "query.hpp" 27 #include "registries/privilege_registry.hpp" 28 #include "utils/ip_utils.hpp" 29 #include "utils/json_utils.hpp" 30 31 #include <boost/url/format.hpp> 32 33 #include <array> 34 #include <optional> 35 #include <ranges> 36 #include <regex> 37 #include <string_view> 38 #include <vector> 39 40 namespace redfish 41 { 42 43 enum class LinkType 44 { 45 Local, 46 Global 47 }; 48 49 /** 50 * Structure for keeping IPv4 data required by Redfish 51 */ 52 struct IPv4AddressData 53 { 54 std::string id; 55 std::string address; 56 std::string domain; 57 std::string gateway; 58 std::string netmask; 59 std::string origin; 60 LinkType linktype{}; 61 bool isActive{}; 62 }; 63 64 /** 65 * Structure for keeping IPv6 data required by Redfish 66 */ 67 struct IPv6AddressData 68 { 69 std::string id; 70 std::string address; 71 std::string origin; 72 uint8_t prefixLength = 0; 73 }; 74 /** 75 * Structure for keeping basic single Ethernet Interface information 76 * available from DBus 77 */ 78 struct EthernetInterfaceData 79 { 80 uint32_t speed; 81 size_t mtuSize; 82 bool autoNeg; 83 bool dnsv4Enabled; 84 bool dnsv6Enabled; 85 bool domainv4Enabled; 86 bool domainv6Enabled; 87 bool ntpv4Enabled; 88 bool ntpv6Enabled; 89 bool hostNamev4Enabled; 90 bool hostNamev6Enabled; 91 bool linkUp; 92 bool nicEnabled; 93 bool ipv6AcceptRa; 94 std::string dhcpEnabled; 95 std::string operatingMode; 96 std::string hostName; 97 std::string defaultGateway; 98 std::string ipv6DefaultGateway; 99 std::string macAddress; 100 std::optional<uint32_t> vlanId; 101 std::vector<std::string> nameServers; 102 std::vector<std::string> staticNameServers; 103 std::vector<std::string> domainnames; 104 }; 105 106 struct DHCPParameters 107 { 108 std::optional<bool> dhcpv4Enabled; 109 std::optional<bool> useDnsServers; 110 std::optional<bool> useNtpServers; 111 std::optional<bool> useDomainName; 112 std::optional<std::string> dhcpv6OperatingMode; 113 }; 114 115 // Helper function that changes bits netmask notation (i.e. /24) 116 // into full dot notation 117 inline std::string getNetmask(unsigned int bits) 118 { 119 uint32_t value = 0xffffffff << (32 - bits); 120 std::string netmask = std::to_string((value >> 24) & 0xff) + "." + 121 std::to_string((value >> 16) & 0xff) + "." + 122 std::to_string((value >> 8) & 0xff) + "." + 123 std::to_string(value & 0xff); 124 return netmask; 125 } 126 127 inline bool translateDhcpEnabledToBool(const std::string& inputDHCP, 128 bool isIPv4) 129 { 130 if (isIPv4) 131 { 132 return ( 133 (inputDHCP == 134 "xyz.openbmc_project.Network.EthernetInterface.DHCPConf.v4") || 135 (inputDHCP == 136 "xyz.openbmc_project.Network.EthernetInterface.DHCPConf.both")); 137 } 138 return ((inputDHCP == 139 "xyz.openbmc_project.Network.EthernetInterface.DHCPConf.v6") || 140 (inputDHCP == 141 "xyz.openbmc_project.Network.EthernetInterface.DHCPConf.both")); 142 } 143 144 inline std::string getDhcpEnabledEnumeration(bool isIPv4, bool isIPv6) 145 { 146 if (isIPv4 && isIPv6) 147 { 148 return "xyz.openbmc_project.Network.EthernetInterface.DHCPConf.both"; 149 } 150 if (isIPv4) 151 { 152 return "xyz.openbmc_project.Network.EthernetInterface.DHCPConf.v4"; 153 } 154 if (isIPv6) 155 { 156 return "xyz.openbmc_project.Network.EthernetInterface.DHCPConf.v6"; 157 } 158 return "xyz.openbmc_project.Network.EthernetInterface.DHCPConf.none"; 159 } 160 161 inline std::string 162 translateAddressOriginDbusToRedfish(const std::string& inputOrigin, 163 bool isIPv4) 164 { 165 if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.Static") 166 { 167 return "Static"; 168 } 169 if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal") 170 { 171 if (isIPv4) 172 { 173 return "IPv4LinkLocal"; 174 } 175 return "LinkLocal"; 176 } 177 if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP") 178 { 179 if (isIPv4) 180 { 181 return "DHCP"; 182 } 183 return "DHCPv6"; 184 } 185 if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.SLAAC") 186 { 187 return "SLAAC"; 188 } 189 return ""; 190 } 191 192 inline bool extractEthernetInterfaceData( 193 const std::string& ethifaceId, 194 const dbus::utility::ManagedObjectType& dbusData, 195 EthernetInterfaceData& ethData) 196 { 197 bool idFound = false; 198 for (const auto& objpath : dbusData) 199 { 200 for (const auto& ifacePair : objpath.second) 201 { 202 if (objpath.first == "/xyz/openbmc_project/network/" + ethifaceId) 203 { 204 idFound = true; 205 if (ifacePair.first == "xyz.openbmc_project.Network.MACAddress") 206 { 207 for (const auto& propertyPair : ifacePair.second) 208 { 209 if (propertyPair.first == "MACAddress") 210 { 211 const std::string* mac = 212 std::get_if<std::string>(&propertyPair.second); 213 if (mac != nullptr) 214 { 215 ethData.macAddress = *mac; 216 } 217 } 218 } 219 } 220 else if (ifacePair.first == "xyz.openbmc_project.Network.VLAN") 221 { 222 for (const auto& propertyPair : ifacePair.second) 223 { 224 if (propertyPair.first == "Id") 225 { 226 const uint32_t* id = 227 std::get_if<uint32_t>(&propertyPair.second); 228 if (id != nullptr) 229 { 230 ethData.vlanId = *id; 231 } 232 } 233 } 234 } 235 else if (ifacePair.first == 236 "xyz.openbmc_project.Network.EthernetInterface") 237 { 238 for (const auto& propertyPair : ifacePair.second) 239 { 240 if (propertyPair.first == "AutoNeg") 241 { 242 const bool* autoNeg = 243 std::get_if<bool>(&propertyPair.second); 244 if (autoNeg != nullptr) 245 { 246 ethData.autoNeg = *autoNeg; 247 } 248 } 249 else if (propertyPair.first == "Speed") 250 { 251 const uint32_t* speed = 252 std::get_if<uint32_t>(&propertyPair.second); 253 if (speed != nullptr) 254 { 255 ethData.speed = *speed; 256 } 257 } 258 else if (propertyPair.first == "MTU") 259 { 260 const size_t* mtuSize = 261 std::get_if<size_t>(&propertyPair.second); 262 if (mtuSize != nullptr) 263 { 264 ethData.mtuSize = *mtuSize; 265 } 266 } 267 else if (propertyPair.first == "LinkUp") 268 { 269 const bool* linkUp = 270 std::get_if<bool>(&propertyPair.second); 271 if (linkUp != nullptr) 272 { 273 ethData.linkUp = *linkUp; 274 } 275 } 276 else if (propertyPair.first == "NICEnabled") 277 { 278 const bool* nicEnabled = 279 std::get_if<bool>(&propertyPair.second); 280 if (nicEnabled != nullptr) 281 { 282 ethData.nicEnabled = *nicEnabled; 283 } 284 } 285 else if (propertyPair.first == "IPv6AcceptRA") 286 { 287 const bool* ipv6AcceptRa = 288 std::get_if<bool>(&propertyPair.second); 289 if (ipv6AcceptRa != nullptr) 290 { 291 ethData.ipv6AcceptRa = *ipv6AcceptRa; 292 } 293 } 294 else if (propertyPair.first == "Nameservers") 295 { 296 const std::vector<std::string>* nameservers = 297 std::get_if<std::vector<std::string>>( 298 &propertyPair.second); 299 if (nameservers != nullptr) 300 { 301 ethData.nameServers = *nameservers; 302 } 303 } 304 else if (propertyPair.first == "StaticNameServers") 305 { 306 const std::vector<std::string>* staticNameServers = 307 std::get_if<std::vector<std::string>>( 308 &propertyPair.second); 309 if (staticNameServers != nullptr) 310 { 311 ethData.staticNameServers = *staticNameServers; 312 } 313 } 314 else if (propertyPair.first == "DHCPEnabled") 315 { 316 const std::string* dhcpEnabled = 317 std::get_if<std::string>(&propertyPair.second); 318 if (dhcpEnabled != nullptr) 319 { 320 ethData.dhcpEnabled = *dhcpEnabled; 321 } 322 } 323 else if (propertyPair.first == "DomainName") 324 { 325 const std::vector<std::string>* domainNames = 326 std::get_if<std::vector<std::string>>( 327 &propertyPair.second); 328 if (domainNames != nullptr) 329 { 330 ethData.domainnames = *domainNames; 331 } 332 } 333 else if (propertyPair.first == "DefaultGateway") 334 { 335 const std::string* defaultGateway = 336 std::get_if<std::string>(&propertyPair.second); 337 if (defaultGateway != nullptr) 338 { 339 std::string defaultGatewayStr = *defaultGateway; 340 if (defaultGatewayStr.empty()) 341 { 342 ethData.defaultGateway = "0.0.0.0"; 343 } 344 else 345 { 346 ethData.defaultGateway = defaultGatewayStr; 347 } 348 } 349 } 350 else if (propertyPair.first == "DefaultGateway6") 351 { 352 const std::string* defaultGateway6 = 353 std::get_if<std::string>(&propertyPair.second); 354 if (defaultGateway6 != nullptr) 355 { 356 std::string defaultGateway6Str = 357 *defaultGateway6; 358 if (defaultGateway6Str.empty()) 359 { 360 ethData.ipv6DefaultGateway = 361 "0:0:0:0:0:0:0:0"; 362 } 363 else 364 { 365 ethData.ipv6DefaultGateway = 366 defaultGateway6Str; 367 } 368 } 369 } 370 } 371 } 372 } 373 374 sdbusplus::message::object_path path( 375 "/xyz/openbmc_project/network"); 376 sdbusplus::message::object_path dhcp4Path = path / ethifaceId / 377 "dhcp4"; 378 379 if (sdbusplus::message::object_path(objpath.first) == dhcp4Path) 380 { 381 if (ifacePair.first == 382 "xyz.openbmc_project.Network.DHCPConfiguration") 383 { 384 for (const auto& propertyPair : ifacePair.second) 385 { 386 if (propertyPair.first == "DNSEnabled") 387 { 388 const bool* dnsEnabled = 389 std::get_if<bool>(&propertyPair.second); 390 if (dnsEnabled != nullptr) 391 { 392 ethData.dnsv4Enabled = *dnsEnabled; 393 } 394 } 395 else if (propertyPair.first == "DomainEnabled") 396 { 397 const bool* domainEnabled = 398 std::get_if<bool>(&propertyPair.second); 399 if (domainEnabled != nullptr) 400 { 401 ethData.domainv4Enabled = *domainEnabled; 402 } 403 } 404 else if (propertyPair.first == "NTPEnabled") 405 { 406 const bool* ntpEnabled = 407 std::get_if<bool>(&propertyPair.second); 408 if (ntpEnabled != nullptr) 409 { 410 ethData.ntpv4Enabled = *ntpEnabled; 411 } 412 } 413 else if (propertyPair.first == "HostNameEnabled") 414 { 415 const bool* hostNameEnabled = 416 std::get_if<bool>(&propertyPair.second); 417 if (hostNameEnabled != nullptr) 418 { 419 ethData.hostNamev4Enabled = *hostNameEnabled; 420 } 421 } 422 } 423 } 424 } 425 426 sdbusplus::message::object_path dhcp6Path = path / ethifaceId / 427 "dhcp6"; 428 429 if (sdbusplus::message::object_path(objpath.first) == dhcp6Path) 430 { 431 if (ifacePair.first == 432 "xyz.openbmc_project.Network.DHCPConfiguration") 433 { 434 for (const auto& propertyPair : ifacePair.second) 435 { 436 if (propertyPair.first == "DNSEnabled") 437 { 438 const bool* dnsEnabled = 439 std::get_if<bool>(&propertyPair.second); 440 if (dnsEnabled != nullptr) 441 { 442 ethData.dnsv6Enabled = *dnsEnabled; 443 } 444 } 445 if (propertyPair.first == "DomainEnabled") 446 { 447 const bool* domainEnabled = 448 std::get_if<bool>(&propertyPair.second); 449 if (domainEnabled != nullptr) 450 { 451 ethData.domainv6Enabled = *domainEnabled; 452 } 453 } 454 else if (propertyPair.first == "NTPEnabled") 455 { 456 const bool* ntpEnabled = 457 std::get_if<bool>(&propertyPair.second); 458 if (ntpEnabled != nullptr) 459 { 460 ethData.ntpv6Enabled = *ntpEnabled; 461 } 462 } 463 else if (propertyPair.first == "HostNameEnabled") 464 { 465 const bool* hostNameEnabled = 466 std::get_if<bool>(&propertyPair.second); 467 if (hostNameEnabled != nullptr) 468 { 469 ethData.hostNamev6Enabled = *hostNameEnabled; 470 } 471 } 472 } 473 } 474 } 475 // System configuration shows up in the global namespace, so no need 476 // to check eth number 477 if (ifacePair.first == 478 "xyz.openbmc_project.Network.SystemConfiguration") 479 { 480 for (const auto& propertyPair : ifacePair.second) 481 { 482 if (propertyPair.first == "HostName") 483 { 484 const std::string* hostname = 485 std::get_if<std::string>(&propertyPair.second); 486 if (hostname != nullptr) 487 { 488 ethData.hostName = *hostname; 489 } 490 } 491 } 492 } 493 } 494 } 495 return idFound; 496 } 497 498 // Helper function that extracts data for single ethernet ipv6 address 499 inline void extractIPV6Data(const std::string& ethifaceId, 500 const dbus::utility::ManagedObjectType& dbusData, 501 std::vector<IPv6AddressData>& ipv6Config) 502 { 503 const std::string ipPathStart = "/xyz/openbmc_project/network/" + 504 ethifaceId; 505 506 // Since there might be several IPv6 configurations aligned with 507 // single ethernet interface, loop over all of them 508 for (const auto& objpath : dbusData) 509 { 510 // Check if proper pattern for object path appears 511 if (objpath.first.str.starts_with(ipPathStart + "/")) 512 { 513 for (const auto& interface : objpath.second) 514 { 515 if (interface.first == "xyz.openbmc_project.Network.IP") 516 { 517 auto type = std::ranges::find_if(interface.second, 518 [](const auto& property) { 519 return property.first == "Type"; 520 }); 521 if (type == interface.second.end()) 522 { 523 continue; 524 } 525 526 const std::string* typeStr = 527 std::get_if<std::string>(&type->second); 528 529 if (typeStr == nullptr || 530 (*typeStr != 531 "xyz.openbmc_project.Network.IP.Protocol.IPv6")) 532 { 533 continue; 534 } 535 536 // Instance IPv6AddressData structure, and set as 537 // appropriate 538 IPv6AddressData& ipv6Address = ipv6Config.emplace_back(); 539 ipv6Address.id = 540 objpath.first.str.substr(ipPathStart.size()); 541 for (const auto& property : interface.second) 542 { 543 if (property.first == "Address") 544 { 545 const std::string* address = 546 std::get_if<std::string>(&property.second); 547 if (address != nullptr) 548 { 549 ipv6Address.address = *address; 550 } 551 } 552 else if (property.first == "Origin") 553 { 554 const std::string* origin = 555 std::get_if<std::string>(&property.second); 556 if (origin != nullptr) 557 { 558 ipv6Address.origin = 559 translateAddressOriginDbusToRedfish(*origin, 560 false); 561 } 562 } 563 else if (property.first == "PrefixLength") 564 { 565 const uint8_t* prefix = 566 std::get_if<uint8_t>(&property.second); 567 if (prefix != nullptr) 568 { 569 ipv6Address.prefixLength = *prefix; 570 } 571 } 572 else if (property.first == "Type" || 573 property.first == "Gateway") 574 { 575 // Type & Gateway is not used 576 } 577 else 578 { 579 BMCWEB_LOG_ERROR( 580 "Got extra property: {} on the {} object", 581 property.first, objpath.first.str); 582 } 583 } 584 } 585 } 586 } 587 } 588 } 589 590 // Helper function that extracts data for single ethernet ipv4 address 591 inline void extractIPData(const std::string& ethifaceId, 592 const dbus::utility::ManagedObjectType& dbusData, 593 std::vector<IPv4AddressData>& ipv4Config) 594 { 595 const std::string ipPathStart = "/xyz/openbmc_project/network/" + 596 ethifaceId; 597 598 // Since there might be several IPv4 configurations aligned with 599 // single ethernet interface, loop over all of them 600 for (const auto& objpath : dbusData) 601 { 602 // Check if proper pattern for object path appears 603 if (objpath.first.str.starts_with(ipPathStart + "/")) 604 { 605 for (const auto& interface : objpath.second) 606 { 607 if (interface.first == "xyz.openbmc_project.Network.IP") 608 { 609 auto type = std::ranges::find_if(interface.second, 610 [](const auto& property) { 611 return property.first == "Type"; 612 }); 613 if (type == interface.second.end()) 614 { 615 continue; 616 } 617 618 const std::string* typeStr = 619 std::get_if<std::string>(&type->second); 620 621 if (typeStr == nullptr || 622 (*typeStr != 623 "xyz.openbmc_project.Network.IP.Protocol.IPv4")) 624 { 625 continue; 626 } 627 628 // Instance IPv4AddressData structure, and set as 629 // appropriate 630 IPv4AddressData& ipv4Address = ipv4Config.emplace_back(); 631 ipv4Address.id = 632 objpath.first.str.substr(ipPathStart.size()); 633 for (const auto& property : interface.second) 634 { 635 if (property.first == "Address") 636 { 637 const std::string* address = 638 std::get_if<std::string>(&property.second); 639 if (address != nullptr) 640 { 641 ipv4Address.address = *address; 642 } 643 } 644 else if (property.first == "Origin") 645 { 646 const std::string* origin = 647 std::get_if<std::string>(&property.second); 648 if (origin != nullptr) 649 { 650 ipv4Address.origin = 651 translateAddressOriginDbusToRedfish(*origin, 652 true); 653 } 654 } 655 else if (property.first == "PrefixLength") 656 { 657 const uint8_t* mask = 658 std::get_if<uint8_t>(&property.second); 659 if (mask != nullptr) 660 { 661 // convert it to the string 662 ipv4Address.netmask = getNetmask(*mask); 663 } 664 } 665 else if (property.first == "Type" || 666 property.first == "Gateway") 667 { 668 // Type & Gateway is not used 669 } 670 else 671 { 672 BMCWEB_LOG_ERROR( 673 "Got extra property: {} on the {} object", 674 property.first, objpath.first.str); 675 } 676 } 677 // Check if given address is local, or global 678 ipv4Address.linktype = 679 ipv4Address.address.starts_with("169.254.") 680 ? LinkType::Local 681 : LinkType::Global; 682 } 683 } 684 } 685 } 686 } 687 688 /** 689 * @brief Deletes given IPv4 interface 690 * 691 * @param[in] ifaceId Id of interface whose IP should be deleted 692 * @param[in] ipHash DBus Hash id of IP that should be deleted 693 * @param[io] asyncResp Response object that will be returned to client 694 * 695 * @return None 696 */ 697 inline void deleteIPAddress(const std::string& ifaceId, 698 const std::string& ipHash, 699 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 700 { 701 crow::connections::systemBus->async_method_call( 702 [asyncResp](const boost::system::error_code& ec) { 703 if (ec) 704 { 705 messages::internalError(asyncResp->res); 706 } 707 }, 708 "xyz.openbmc_project.Network", 709 "/xyz/openbmc_project/network/" + ifaceId + ipHash, 710 "xyz.openbmc_project.Object.Delete", "Delete"); 711 } 712 713 inline void updateIPv4DefaultGateway( 714 const std::string& ifaceId, const std::string& gateway, 715 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 716 { 717 sdbusplus::asio::setProperty( 718 *crow::connections::systemBus, "xyz.openbmc_project.Network", 719 "/xyz/openbmc_project/network/" + ifaceId, 720 "xyz.openbmc_project.Network.EthernetInterface", "DefaultGateway", 721 gateway, [asyncResp](const boost::system::error_code& ec) { 722 if (ec) 723 { 724 messages::internalError(asyncResp->res); 725 return; 726 } 727 asyncResp->res.result(boost::beast::http::status::no_content); 728 }); 729 } 730 /** 731 * @brief Creates a static IPv4 entry 732 * 733 * @param[in] ifaceId Id of interface upon which to create the IPv4 entry 734 * @param[in] prefixLength IPv4 prefix syntax for the subnet mask 735 * @param[in] gateway IPv4 address of this interfaces gateway 736 * @param[in] address IPv4 address to assign to this interface 737 * @param[io] asyncResp Response object that will be returned to client 738 * 739 * @return None 740 */ 741 inline void createIPv4(const std::string& ifaceId, uint8_t prefixLength, 742 const std::string& gateway, const std::string& address, 743 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 744 { 745 auto createIpHandler = [asyncResp, ifaceId, 746 gateway](const boost::system::error_code& ec) { 747 if (ec) 748 { 749 messages::internalError(asyncResp->res); 750 return; 751 } 752 updateIPv4DefaultGateway(ifaceId, gateway, asyncResp); 753 }; 754 755 crow::connections::systemBus->async_method_call( 756 std::move(createIpHandler), "xyz.openbmc_project.Network", 757 "/xyz/openbmc_project/network/" + ifaceId, 758 "xyz.openbmc_project.Network.IP.Create", "IP", 759 "xyz.openbmc_project.Network.IP.Protocol.IPv4", address, prefixLength, 760 gateway); 761 } 762 763 /** 764 * @brief Deletes the IPv6 entry for this interface and creates a replacement 765 * static IPv6 entry 766 * 767 * @param[in] ifaceId Id of interface upon which to create the IPv6 entry 768 * @param[in] id The unique hash entry identifying the DBus entry 769 * @param[in] prefixLength IPv6 prefix syntax for the subnet mask 770 * @param[in] address IPv6 address to assign to this interface 771 * @param[io] asyncResp Response object that will be returned to client 772 * 773 * @return None 774 */ 775 776 enum class IpVersion 777 { 778 IpV4, 779 IpV6 780 }; 781 782 inline void deleteAndCreateIPAddress( 783 IpVersion version, const std::string& ifaceId, const std::string& id, 784 uint8_t prefixLength, const std::string& address, 785 const std::string& gateway, 786 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 787 { 788 crow::connections::systemBus->async_method_call( 789 [asyncResp, version, ifaceId, address, prefixLength, 790 gateway](const boost::system::error_code& ec) { 791 if (ec) 792 { 793 messages::internalError(asyncResp->res); 794 } 795 std::string protocol = "xyz.openbmc_project.Network.IP.Protocol."; 796 protocol += version == IpVersion::IpV4 ? "IPv4" : "IPv6"; 797 crow::connections::systemBus->async_method_call( 798 [asyncResp](const boost::system::error_code& ec2) { 799 if (ec2) 800 { 801 messages::internalError(asyncResp->res); 802 } 803 }, 804 "xyz.openbmc_project.Network", 805 "/xyz/openbmc_project/network/" + ifaceId, 806 "xyz.openbmc_project.Network.IP.Create", "IP", protocol, address, 807 prefixLength, gateway); 808 }, 809 "xyz.openbmc_project.Network", 810 "/xyz/openbmc_project/network/" + ifaceId + id, 811 "xyz.openbmc_project.Object.Delete", "Delete"); 812 } 813 814 /** 815 * @brief Creates IPv6 with given data 816 * 817 * @param[in] ifaceId Id of interface whose IP should be added 818 * @param[in] prefixLength Prefix length that needs to be added 819 * @param[in] address IP address that needs to be added 820 * @param[io] asyncResp Response object that will be returned to client 821 * 822 * @return None 823 */ 824 inline void createIPv6(const std::string& ifaceId, uint8_t prefixLength, 825 const std::string& address, 826 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 827 { 828 auto createIpHandler = [asyncResp, 829 address](const boost::system::error_code& ec) { 830 if (ec) 831 { 832 if (ec == boost::system::errc::io_error) 833 { 834 messages::propertyValueFormatError(asyncResp->res, address, 835 "Address"); 836 } 837 else 838 { 839 messages::internalError(asyncResp->res); 840 } 841 } 842 }; 843 // Passing null for gateway, as per redfish spec IPv6StaticAddresses object 844 // does not have associated gateway property 845 crow::connections::systemBus->async_method_call( 846 std::move(createIpHandler), "xyz.openbmc_project.Network", 847 "/xyz/openbmc_project/network/" + ifaceId, 848 "xyz.openbmc_project.Network.IP.Create", "IP", 849 "xyz.openbmc_project.Network.IP.Protocol.IPv6", address, prefixLength, 850 ""); 851 } 852 853 /** 854 * Function that retrieves all properties for given Ethernet Interface 855 * Object 856 * from EntityManager Network Manager 857 * @param ethiface_id a eth interface id to query on DBus 858 * @param callback a function that shall be called to convert Dbus output 859 * into JSON 860 */ 861 template <typename CallbackFunc> 862 void getEthernetIfaceData(const std::string& ethifaceId, 863 CallbackFunc&& callback) 864 { 865 sdbusplus::message::object_path path("/xyz/openbmc_project/network"); 866 dbus::utility::getManagedObjects( 867 "xyz.openbmc_project.Network", path, 868 [ethifaceId{std::string{ethifaceId}}, 869 callback{std::forward<CallbackFunc>(callback)}]( 870 const boost::system::error_code& ec, 871 const dbus::utility::ManagedObjectType& resp) { 872 EthernetInterfaceData ethData{}; 873 std::vector<IPv4AddressData> ipv4Data; 874 std::vector<IPv6AddressData> ipv6Data; 875 876 if (ec) 877 { 878 callback(false, ethData, ipv4Data, ipv6Data); 879 return; 880 } 881 882 bool found = extractEthernetInterfaceData(ethifaceId, resp, ethData); 883 if (!found) 884 { 885 callback(false, ethData, ipv4Data, ipv6Data); 886 return; 887 } 888 889 extractIPData(ethifaceId, resp, ipv4Data); 890 // Fix global GW 891 for (IPv4AddressData& ipv4 : ipv4Data) 892 { 893 if (((ipv4.linktype == LinkType::Global) && 894 (ipv4.gateway == "0.0.0.0")) || 895 (ipv4.origin == "DHCP") || (ipv4.origin == "Static")) 896 { 897 ipv4.gateway = ethData.defaultGateway; 898 } 899 } 900 901 extractIPV6Data(ethifaceId, resp, ipv6Data); 902 // Finally make a callback with useful data 903 callback(true, ethData, ipv4Data, ipv6Data); 904 }); 905 } 906 907 /** 908 * Function that retrieves all Ethernet Interfaces available through Network 909 * Manager 910 * @param callback a function that shall be called to convert Dbus output 911 * into JSON. 912 */ 913 template <typename CallbackFunc> 914 void getEthernetIfaceList(CallbackFunc&& callback) 915 { 916 sdbusplus::message::object_path path("/xyz/openbmc_project/network"); 917 dbus::utility::getManagedObjects( 918 "xyz.openbmc_project.Network", path, 919 [callback{std::forward<CallbackFunc>(callback)}]( 920 const boost::system::error_code& ec, 921 const dbus::utility::ManagedObjectType& resp) { 922 // Callback requires vector<string> to retrieve all available 923 // ethernet interfaces 924 std::vector<std::string> ifaceList; 925 ifaceList.reserve(resp.size()); 926 if (ec) 927 { 928 callback(false, ifaceList); 929 return; 930 } 931 932 // Iterate over all retrieved ObjectPaths. 933 for (const auto& objpath : resp) 934 { 935 // And all interfaces available for certain ObjectPath. 936 for (const auto& interface : objpath.second) 937 { 938 // If interface is 939 // xyz.openbmc_project.Network.EthernetInterface, this is 940 // what we're looking for. 941 if (interface.first == 942 "xyz.openbmc_project.Network.EthernetInterface") 943 { 944 std::string ifaceId = objpath.first.filename(); 945 if (ifaceId.empty()) 946 { 947 continue; 948 } 949 // and put it into output vector. 950 ifaceList.emplace_back(ifaceId); 951 } 952 } 953 } 954 955 std::ranges::sort(ifaceList, AlphanumLess<std::string>()); 956 957 // Finally make a callback with useful data 958 callback(true, ifaceList); 959 }); 960 } 961 962 inline void 963 handleHostnamePatch(const std::string& hostname, 964 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 965 { 966 // SHOULD handle host names of up to 255 characters(RFC 1123) 967 if (hostname.length() > 255) 968 { 969 messages::propertyValueFormatError(asyncResp->res, hostname, 970 "HostName"); 971 return; 972 } 973 sdbusplus::asio::setProperty( 974 *crow::connections::systemBus, "xyz.openbmc_project.Network", 975 "/xyz/openbmc_project/network/config", 976 "xyz.openbmc_project.Network.SystemConfiguration", "HostName", hostname, 977 [asyncResp](const boost::system::error_code& ec) { 978 if (ec) 979 { 980 messages::internalError(asyncResp->res); 981 } 982 }); 983 } 984 985 inline void 986 handleMTUSizePatch(const std::string& ifaceId, const size_t mtuSize, 987 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 988 { 989 sdbusplus::message::object_path objPath = "/xyz/openbmc_project/network/" + 990 ifaceId; 991 sdbusplus::asio::setProperty( 992 *crow::connections::systemBus, "xyz.openbmc_project.Network", objPath, 993 "xyz.openbmc_project.Network.EthernetInterface", "MTU", mtuSize, 994 [asyncResp](const boost::system::error_code& ec) { 995 if (ec) 996 { 997 messages::internalError(asyncResp->res); 998 } 999 }); 1000 } 1001 1002 inline void 1003 handleDomainnamePatch(const std::string& ifaceId, 1004 const std::string& domainname, 1005 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 1006 { 1007 std::vector<std::string> vectorDomainname = {domainname}; 1008 sdbusplus::asio::setProperty( 1009 *crow::connections::systemBus, "xyz.openbmc_project.Network", 1010 "/xyz/openbmc_project/network/" + ifaceId, 1011 "xyz.openbmc_project.Network.EthernetInterface", "DomainName", 1012 vectorDomainname, [asyncResp](const boost::system::error_code& ec) { 1013 if (ec) 1014 { 1015 messages::internalError(asyncResp->res); 1016 } 1017 }); 1018 } 1019 1020 inline bool isHostnameValid(const std::string& hostname) 1021 { 1022 // A valid host name can never have the dotted-decimal form (RFC 1123) 1023 if (std::ranges::all_of(hostname, ::isdigit)) 1024 { 1025 return false; 1026 } 1027 // Each label(hostname/subdomains) within a valid FQDN 1028 // MUST handle host names of up to 63 characters (RFC 1123) 1029 // labels cannot start or end with hyphens (RFC 952) 1030 // labels can start with numbers (RFC 1123) 1031 const static std::regex pattern( 1032 "^[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9]$"); 1033 1034 return std::regex_match(hostname, pattern); 1035 } 1036 1037 inline bool isDomainnameValid(const std::string& domainname) 1038 { 1039 // Can have multiple subdomains 1040 // Top Level Domain's min length is 2 character 1041 const static std::regex pattern( 1042 "^([A-Za-z0-9][a-zA-Z0-9\\-]{1,61}|[a-zA-Z0-9]{1,30}\\.)*[a-zA-Z]{2,}$"); 1043 1044 return std::regex_match(domainname, pattern); 1045 } 1046 1047 inline void handleFqdnPatch(const std::string& ifaceId, const std::string& fqdn, 1048 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 1049 { 1050 // Total length of FQDN must not exceed 255 characters(RFC 1035) 1051 if (fqdn.length() > 255) 1052 { 1053 messages::propertyValueFormatError(asyncResp->res, fqdn, "FQDN"); 1054 return; 1055 } 1056 1057 size_t pos = fqdn.find('.'); 1058 if (pos == std::string::npos) 1059 { 1060 messages::propertyValueFormatError(asyncResp->res, fqdn, "FQDN"); 1061 return; 1062 } 1063 1064 std::string hostname; 1065 std::string domainname; 1066 domainname = (fqdn).substr(pos + 1); 1067 hostname = (fqdn).substr(0, pos); 1068 1069 if (!isHostnameValid(hostname) || !isDomainnameValid(domainname)) 1070 { 1071 messages::propertyValueFormatError(asyncResp->res, fqdn, "FQDN"); 1072 return; 1073 } 1074 1075 handleHostnamePatch(hostname, asyncResp); 1076 handleDomainnamePatch(ifaceId, domainname, asyncResp); 1077 } 1078 1079 inline void 1080 handleMACAddressPatch(const std::string& ifaceId, 1081 const std::string& macAddress, 1082 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 1083 { 1084 static constexpr std::string_view dbusNotAllowedError = 1085 "xyz.openbmc_project.Common.Error.NotAllowed"; 1086 1087 sdbusplus::asio::setProperty( 1088 *crow::connections::systemBus, "xyz.openbmc_project.Network", 1089 "/xyz/openbmc_project/network/" + ifaceId, 1090 "xyz.openbmc_project.Network.MACAddress", "MACAddress", macAddress, 1091 [asyncResp](const boost::system::error_code& ec, 1092 const sdbusplus::message_t& msg) { 1093 if (ec) 1094 { 1095 const sd_bus_error* err = msg.get_error(); 1096 if (err == nullptr) 1097 { 1098 messages::internalError(asyncResp->res); 1099 return; 1100 } 1101 if (err->name == dbusNotAllowedError) 1102 { 1103 messages::propertyNotWritable(asyncResp->res, "MACAddress"); 1104 return; 1105 } 1106 messages::internalError(asyncResp->res); 1107 return; 1108 } 1109 }); 1110 } 1111 1112 inline void setDHCPEnabled(const std::string& ifaceId, 1113 const std::string& propertyName, const bool v4Value, 1114 const bool v6Value, 1115 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 1116 { 1117 const std::string dhcp = getDhcpEnabledEnumeration(v4Value, v6Value); 1118 sdbusplus::asio::setProperty( 1119 *crow::connections::systemBus, "xyz.openbmc_project.Network", 1120 "/xyz/openbmc_project/network/" + ifaceId, 1121 "xyz.openbmc_project.Network.EthernetInterface", propertyName, dhcp, 1122 [asyncResp](const boost::system::error_code& ec) { 1123 if (ec) 1124 { 1125 BMCWEB_LOG_ERROR("D-Bus responses error: {}", ec); 1126 messages::internalError(asyncResp->res); 1127 return; 1128 } 1129 messages::success(asyncResp->res); 1130 }); 1131 } 1132 1133 inline void setEthernetInterfaceBoolProperty( 1134 const std::string& ifaceId, const std::string& propertyName, 1135 const bool& value, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 1136 { 1137 sdbusplus::asio::setProperty( 1138 *crow::connections::systemBus, "xyz.openbmc_project.Network", 1139 "/xyz/openbmc_project/network/" + ifaceId, 1140 "xyz.openbmc_project.Network.EthernetInterface", propertyName, value, 1141 [asyncResp](const boost::system::error_code& ec) { 1142 if (ec) 1143 { 1144 BMCWEB_LOG_ERROR("D-Bus responses error: {}", ec); 1145 messages::internalError(asyncResp->res); 1146 return; 1147 } 1148 }); 1149 } 1150 1151 enum class NetworkType 1152 { 1153 dhcp4, 1154 dhcp6 1155 }; 1156 1157 inline void setDHCPConfig(const std::string& propertyName, const bool& value, 1158 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1159 const std::string& ethifaceId, NetworkType type) 1160 { 1161 BMCWEB_LOG_DEBUG("{} = {}", propertyName, value); 1162 sdbusplus::message::object_path path("/xyz/openbmc_project/network/"); 1163 path /= ethifaceId; 1164 1165 if (type == NetworkType::dhcp4) 1166 { 1167 path /= "dhcp4"; 1168 } 1169 else 1170 { 1171 path /= "dhcp6"; 1172 } 1173 1174 sdbusplus::asio::setProperty( 1175 *crow::connections::systemBus, "xyz.openbmc_project.Network", path, 1176 "xyz.openbmc_project.Network.DHCPConfiguration", propertyName, value, 1177 [asyncResp](const boost::system::error_code& ec) { 1178 if (ec) 1179 { 1180 BMCWEB_LOG_ERROR("D-Bus responses error: {}", ec); 1181 messages::internalError(asyncResp->res); 1182 return; 1183 } 1184 }); 1185 } 1186 1187 inline void handleSLAACAutoConfigPatch( 1188 const std::string& ifaceId, bool ipv6AutoConfigEnabled, 1189 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 1190 { 1191 sdbusplus::message::object_path path("/xyz/openbmc_project/network"); 1192 path /= ifaceId; 1193 sdbusplus::asio::setProperty( 1194 *crow::connections::systemBus, "xyz.openbmc_project.Network", path, 1195 "xyz.openbmc_project.Network.EthernetInterface", "IPv6AcceptRA", 1196 ipv6AutoConfigEnabled, 1197 [asyncResp](const boost::system::error_code& ec) { 1198 if (ec) 1199 { 1200 BMCWEB_LOG_ERROR("D-Bus responses error: {}", ec); 1201 messages::internalError(asyncResp->res); 1202 return; 1203 } 1204 messages::success(asyncResp->res); 1205 }); 1206 } 1207 1208 inline void handleDHCPPatch(const std::string& ifaceId, 1209 const EthernetInterfaceData& ethData, 1210 const DHCPParameters& v4dhcpParms, 1211 const DHCPParameters& v6dhcpParms, 1212 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 1213 { 1214 bool ipv4Active = translateDhcpEnabledToBool(ethData.dhcpEnabled, true); 1215 bool ipv6Active = translateDhcpEnabledToBool(ethData.dhcpEnabled, false); 1216 1217 bool nextv4DHCPState = 1218 v4dhcpParms.dhcpv4Enabled ? *v4dhcpParms.dhcpv4Enabled : ipv4Active; 1219 1220 bool nextv6DHCPState{}; 1221 if (v6dhcpParms.dhcpv6OperatingMode) 1222 { 1223 if ((*v6dhcpParms.dhcpv6OperatingMode != "Enabled") && 1224 (*v6dhcpParms.dhcpv6OperatingMode != "Disabled")) 1225 { 1226 messages::propertyValueFormatError(asyncResp->res, 1227 *v6dhcpParms.dhcpv6OperatingMode, 1228 "OperatingMode"); 1229 return; 1230 } 1231 nextv6DHCPState = (*v6dhcpParms.dhcpv6OperatingMode == "Enabled"); 1232 } 1233 else 1234 { 1235 nextv6DHCPState = ipv6Active; 1236 } 1237 1238 bool nextDNSv4 = ethData.dnsv4Enabled; 1239 bool nextDNSv6 = ethData.dnsv6Enabled; 1240 if (v4dhcpParms.useDnsServers) 1241 { 1242 nextDNSv4 = *v4dhcpParms.useDnsServers; 1243 } 1244 if (v6dhcpParms.useDnsServers) 1245 { 1246 nextDNSv6 = *v6dhcpParms.useDnsServers; 1247 } 1248 1249 bool nextNTPv4 = ethData.ntpv4Enabled; 1250 bool nextNTPv6 = ethData.ntpv6Enabled; 1251 if (v4dhcpParms.useNtpServers) 1252 { 1253 nextNTPv4 = *v4dhcpParms.useNtpServers; 1254 } 1255 if (v6dhcpParms.useNtpServers) 1256 { 1257 nextNTPv6 = *v6dhcpParms.useNtpServers; 1258 } 1259 1260 bool nextUsev4Domain = ethData.domainv4Enabled; 1261 bool nextUsev6Domain = ethData.domainv6Enabled; 1262 if (v4dhcpParms.useDomainName) 1263 { 1264 nextUsev4Domain = *v4dhcpParms.useDomainName; 1265 } 1266 if (v6dhcpParms.useDomainName) 1267 { 1268 nextUsev6Domain = *v6dhcpParms.useDomainName; 1269 } 1270 1271 BMCWEB_LOG_DEBUG("set DHCPEnabled..."); 1272 setDHCPEnabled(ifaceId, "DHCPEnabled", nextv4DHCPState, nextv6DHCPState, 1273 asyncResp); 1274 BMCWEB_LOG_DEBUG("set DNSEnabled..."); 1275 setDHCPConfig("DNSEnabled", nextDNSv4, asyncResp, ifaceId, 1276 NetworkType::dhcp4); 1277 BMCWEB_LOG_DEBUG("set NTPEnabled..."); 1278 setDHCPConfig("NTPEnabled", nextNTPv4, asyncResp, ifaceId, 1279 NetworkType::dhcp4); 1280 BMCWEB_LOG_DEBUG("set DomainEnabled..."); 1281 setDHCPConfig("DomainEnabled", nextUsev4Domain, asyncResp, ifaceId, 1282 NetworkType::dhcp4); 1283 BMCWEB_LOG_DEBUG("set DNSEnabled for dhcp6..."); 1284 setDHCPConfig("DNSEnabled", nextDNSv6, asyncResp, ifaceId, 1285 NetworkType::dhcp6); 1286 BMCWEB_LOG_DEBUG("set NTPEnabled for dhcp6..."); 1287 setDHCPConfig("NTPEnabled", nextNTPv6, asyncResp, ifaceId, 1288 NetworkType::dhcp6); 1289 BMCWEB_LOG_DEBUG("set DomainEnabled for dhcp6..."); 1290 setDHCPConfig("DomainEnabled", nextUsev6Domain, asyncResp, ifaceId, 1291 NetworkType::dhcp6); 1292 } 1293 1294 inline std::vector<IPv4AddressData>::const_iterator getNextStaticIpEntry( 1295 const std::vector<IPv4AddressData>::const_iterator& head, 1296 const std::vector<IPv4AddressData>::const_iterator& end) 1297 { 1298 return std::find_if(head, end, [](const IPv4AddressData& value) { 1299 return value.origin == "Static"; 1300 }); 1301 } 1302 1303 inline std::vector<IPv6AddressData>::const_iterator getNextStaticIpEntry( 1304 const std::vector<IPv6AddressData>::const_iterator& head, 1305 const std::vector<IPv6AddressData>::const_iterator& end) 1306 { 1307 return std::find_if(head, end, [](const IPv6AddressData& value) { 1308 return value.origin == "Static"; 1309 }); 1310 } 1311 1312 inline void 1313 handleIPv4StaticPatch(const std::string& ifaceId, 1314 nlohmann::json::array_t& input, 1315 const std::vector<IPv4AddressData>& ipv4Data, 1316 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 1317 { 1318 if (input.empty()) 1319 { 1320 messages::propertyValueTypeError(asyncResp->res, input, 1321 "IPv4StaticAddresses"); 1322 return; 1323 } 1324 1325 unsigned entryIdx = 1; 1326 // Find the first static IP address currently active on the NIC and 1327 // match it to the first JSON element in the IPv4StaticAddresses array. 1328 // Match each subsequent JSON element to the next static IP programmed 1329 // into the NIC. 1330 std::vector<IPv4AddressData>::const_iterator nicIpEntry = 1331 getNextStaticIpEntry(ipv4Data.cbegin(), ipv4Data.cend()); 1332 1333 for (nlohmann::json& thisJson : input) 1334 { 1335 std::string pathString = "IPv4StaticAddresses/" + 1336 std::to_string(entryIdx); 1337 1338 if (!thisJson.is_null() && !thisJson.empty()) 1339 { 1340 std::optional<std::string> address; 1341 std::optional<std::string> subnetMask; 1342 std::optional<std::string> gateway; 1343 1344 if (!json_util::readJson(thisJson, asyncResp->res, "Address", 1345 address, "SubnetMask", subnetMask, 1346 "Gateway", gateway)) 1347 { 1348 messages::propertyValueFormatError(asyncResp->res, thisJson, 1349 pathString); 1350 return; 1351 } 1352 1353 // Find the address/subnet/gateway values. Any values that are 1354 // not explicitly provided are assumed to be unmodified from the 1355 // current state of the interface. Merge existing state into the 1356 // current request. 1357 if (address) 1358 { 1359 if (!ip_util::ipv4VerifyIpAndGetBitcount(*address)) 1360 { 1361 messages::propertyValueFormatError(asyncResp->res, *address, 1362 pathString + "/Address"); 1363 return; 1364 } 1365 } 1366 else if (nicIpEntry != ipv4Data.cend()) 1367 { 1368 address = (nicIpEntry->address); 1369 } 1370 else 1371 { 1372 messages::propertyMissing(asyncResp->res, 1373 pathString + "/Address"); 1374 return; 1375 } 1376 1377 uint8_t prefixLength = 0; 1378 if (subnetMask) 1379 { 1380 if (!ip_util::ipv4VerifyIpAndGetBitcount(*subnetMask, 1381 &prefixLength)) 1382 { 1383 messages::propertyValueFormatError( 1384 asyncResp->res, *subnetMask, 1385 pathString + "/SubnetMask"); 1386 return; 1387 } 1388 } 1389 else if (nicIpEntry != ipv4Data.cend()) 1390 { 1391 if (!ip_util::ipv4VerifyIpAndGetBitcount(nicIpEntry->netmask, 1392 &prefixLength)) 1393 { 1394 messages::propertyValueFormatError( 1395 asyncResp->res, nicIpEntry->netmask, 1396 pathString + "/SubnetMask"); 1397 return; 1398 } 1399 } 1400 else 1401 { 1402 messages::propertyMissing(asyncResp->res, 1403 pathString + "/SubnetMask"); 1404 return; 1405 } 1406 1407 if (gateway) 1408 { 1409 if (!ip_util::ipv4VerifyIpAndGetBitcount(*gateway)) 1410 { 1411 messages::propertyValueFormatError(asyncResp->res, *gateway, 1412 pathString + "/Gateway"); 1413 return; 1414 } 1415 } 1416 else if (nicIpEntry != ipv4Data.cend()) 1417 { 1418 gateway = nicIpEntry->gateway; 1419 } 1420 else 1421 { 1422 messages::propertyMissing(asyncResp->res, 1423 pathString + "/Gateway"); 1424 return; 1425 } 1426 1427 if (nicIpEntry != ipv4Data.cend()) 1428 { 1429 deleteAndCreateIPAddress(IpVersion::IpV4, ifaceId, 1430 nicIpEntry->id, prefixLength, *address, 1431 *gateway, asyncResp); 1432 nicIpEntry = getNextStaticIpEntry(++nicIpEntry, 1433 ipv4Data.cend()); 1434 } 1435 else 1436 { 1437 createIPv4(ifaceId, prefixLength, *gateway, *address, 1438 asyncResp); 1439 } 1440 entryIdx++; 1441 } 1442 else 1443 { 1444 if (nicIpEntry == ipv4Data.cend()) 1445 { 1446 // Requesting a DELETE/DO NOT MODIFY action for an item 1447 // that isn't present on the eth(n) interface. Input JSON is 1448 // in error, so bail out. 1449 if (thisJson.is_null()) 1450 { 1451 messages::resourceCannotBeDeleted(asyncResp->res); 1452 return; 1453 } 1454 messages::propertyValueFormatError(asyncResp->res, thisJson, 1455 pathString); 1456 return; 1457 } 1458 1459 if (thisJson.is_null()) 1460 { 1461 deleteIPAddress(ifaceId, nicIpEntry->id, asyncResp); 1462 } 1463 if (nicIpEntry != ipv4Data.cend()) 1464 { 1465 nicIpEntry = getNextStaticIpEntry(++nicIpEntry, 1466 ipv4Data.cend()); 1467 } 1468 entryIdx++; 1469 } 1470 } 1471 } 1472 1473 inline void handleStaticNameServersPatch( 1474 const std::string& ifaceId, 1475 const std::vector<std::string>& updatedStaticNameServers, 1476 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 1477 { 1478 sdbusplus::asio::setProperty( 1479 *crow::connections::systemBus, "xyz.openbmc_project.Network", 1480 "/xyz/openbmc_project/network/" + ifaceId, 1481 "xyz.openbmc_project.Network.EthernetInterface", "StaticNameServers", 1482 updatedStaticNameServers, 1483 [asyncResp](const boost::system::error_code& ec) { 1484 if (ec) 1485 { 1486 messages::internalError(asyncResp->res); 1487 return; 1488 } 1489 }); 1490 } 1491 1492 inline void handleIPv6StaticAddressesPatch( 1493 const std::string& ifaceId, const nlohmann::json::array_t& input, 1494 const std::vector<IPv6AddressData>& ipv6Data, 1495 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 1496 { 1497 if (input.empty()) 1498 { 1499 messages::propertyValueTypeError(asyncResp->res, input, 1500 "IPv6StaticAddresses"); 1501 return; 1502 } 1503 size_t entryIdx = 1; 1504 std::vector<IPv6AddressData>::const_iterator nicIpEntry = 1505 getNextStaticIpEntry(ipv6Data.cbegin(), ipv6Data.cend()); 1506 for (const nlohmann::json& thisJson : input) 1507 { 1508 std::string pathString = "IPv6StaticAddresses/" + 1509 std::to_string(entryIdx); 1510 1511 if (!thisJson.is_null() && !thisJson.empty()) 1512 { 1513 std::optional<std::string> address; 1514 std::optional<uint8_t> prefixLength; 1515 nlohmann::json thisJsonCopy = thisJson; 1516 if (!json_util::readJson(thisJsonCopy, asyncResp->res, "Address", 1517 address, "PrefixLength", prefixLength)) 1518 { 1519 messages::propertyValueFormatError(asyncResp->res, thisJson, 1520 pathString); 1521 return; 1522 } 1523 1524 const std::string* addr = nullptr; 1525 uint8_t prefix = 0; 1526 1527 // Find the address and prefixLength values. Any values that are 1528 // not explicitly provided are assumed to be unmodified from the 1529 // current state of the interface. Merge existing state into the 1530 // current request. 1531 if (address) 1532 { 1533 addr = &(*address); 1534 } 1535 else if (nicIpEntry != ipv6Data.end()) 1536 { 1537 addr = &(nicIpEntry->address); 1538 } 1539 else 1540 { 1541 messages::propertyMissing(asyncResp->res, 1542 pathString + "/Address"); 1543 return; 1544 } 1545 1546 if (prefixLength) 1547 { 1548 prefix = *prefixLength; 1549 } 1550 else if (nicIpEntry != ipv6Data.end()) 1551 { 1552 prefix = nicIpEntry->prefixLength; 1553 } 1554 else 1555 { 1556 messages::propertyMissing(asyncResp->res, 1557 pathString + "/PrefixLength"); 1558 return; 1559 } 1560 1561 if (nicIpEntry != ipv6Data.end()) 1562 { 1563 deleteAndCreateIPAddress(IpVersion::IpV6, ifaceId, 1564 nicIpEntry->id, prefix, *addr, "", 1565 asyncResp); 1566 nicIpEntry = getNextStaticIpEntry(++nicIpEntry, 1567 ipv6Data.cend()); 1568 } 1569 else 1570 { 1571 createIPv6(ifaceId, *prefixLength, *addr, asyncResp); 1572 } 1573 entryIdx++; 1574 } 1575 else 1576 { 1577 if (nicIpEntry == ipv6Data.end()) 1578 { 1579 // Requesting a DELETE/DO NOT MODIFY action for an item 1580 // that isn't present on the eth(n) interface. Input JSON is 1581 // in error, so bail out. 1582 if (thisJson.is_null()) 1583 { 1584 messages::resourceCannotBeDeleted(asyncResp->res); 1585 return; 1586 } 1587 messages::propertyValueFormatError(asyncResp->res, thisJson, 1588 pathString); 1589 return; 1590 } 1591 1592 if (thisJson.is_null()) 1593 { 1594 deleteIPAddress(ifaceId, nicIpEntry->id, asyncResp); 1595 } 1596 if (nicIpEntry != ipv6Data.cend()) 1597 { 1598 nicIpEntry = getNextStaticIpEntry(++nicIpEntry, 1599 ipv6Data.cend()); 1600 } 1601 entryIdx++; 1602 } 1603 } 1604 } 1605 1606 inline std::string extractParentInterfaceName(const std::string& ifaceId) 1607 { 1608 std::size_t pos = ifaceId.find('_'); 1609 return ifaceId.substr(0, pos); 1610 } 1611 1612 inline void 1613 parseInterfaceData(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1614 const std::string& ifaceId, 1615 const EthernetInterfaceData& ethData, 1616 const std::vector<IPv4AddressData>& ipv4Data, 1617 const std::vector<IPv6AddressData>& ipv6Data) 1618 { 1619 nlohmann::json& jsonResponse = asyncResp->res.jsonValue; 1620 jsonResponse["Id"] = ifaceId; 1621 jsonResponse["@odata.id"] = boost::urls::format( 1622 "/redfish/v1/Managers/bmc/EthernetInterfaces/{}", ifaceId); 1623 jsonResponse["InterfaceEnabled"] = ethData.nicEnabled; 1624 1625 if constexpr (bmcwebEnableHealthPopulate) 1626 { 1627 constexpr std::array<std::string_view, 1> inventoryForEthernet = { 1628 "xyz.openbmc_project.Inventory.Item.Ethernet"}; 1629 auto health = std::make_shared<HealthPopulate>(asyncResp); 1630 dbus::utility::getSubTreePaths( 1631 "/", 0, inventoryForEthernet, 1632 [health](const boost::system::error_code& ec, 1633 const dbus::utility::MapperGetSubTreePathsResponse& resp) { 1634 if (ec) 1635 { 1636 return; 1637 } 1638 1639 health->inventory = resp; 1640 }); 1641 1642 health->populate(); 1643 } 1644 1645 if (ethData.nicEnabled) 1646 { 1647 jsonResponse["LinkStatus"] = ethData.linkUp ? "LinkUp" : "LinkDown"; 1648 jsonResponse["Status"]["State"] = "Enabled"; 1649 } 1650 else 1651 { 1652 jsonResponse["LinkStatus"] = "NoLink"; 1653 jsonResponse["Status"]["State"] = "Disabled"; 1654 } 1655 1656 jsonResponse["SpeedMbps"] = ethData.speed; 1657 jsonResponse["MTUSize"] = ethData.mtuSize; 1658 jsonResponse["MACAddress"] = ethData.macAddress; 1659 jsonResponse["DHCPv4"]["DHCPEnabled"] = 1660 translateDhcpEnabledToBool(ethData.dhcpEnabled, true); 1661 jsonResponse["DHCPv4"]["UseNTPServers"] = ethData.ntpv4Enabled; 1662 jsonResponse["DHCPv4"]["UseDNSServers"] = ethData.dnsv4Enabled; 1663 jsonResponse["DHCPv4"]["UseDomainName"] = ethData.hostNamev4Enabled; 1664 jsonResponse["DHCPv6"]["OperatingMode"] = 1665 translateDhcpEnabledToBool(ethData.dhcpEnabled, false) ? "Enabled" 1666 : "Disabled"; 1667 jsonResponse["DHCPv6"]["UseNTPServers"] = ethData.ntpv6Enabled; 1668 jsonResponse["DHCPv6"]["UseDNSServers"] = ethData.dnsv6Enabled; 1669 jsonResponse["DHCPv6"]["UseDomainName"] = ethData.hostNamev6Enabled; 1670 jsonResponse["StatelessAddressAutoConfig"]["IPv6AutoConfigEnabled"] = 1671 ethData.ipv6AcceptRa; 1672 1673 if (!ethData.hostName.empty()) 1674 { 1675 jsonResponse["HostName"] = ethData.hostName; 1676 1677 // When domain name is empty then it means, that it is a network 1678 // without domain names, and the host name itself must be treated as 1679 // FQDN 1680 std::string fqdn = ethData.hostName; 1681 if (!ethData.domainnames.empty()) 1682 { 1683 fqdn += "." + ethData.domainnames[0]; 1684 } 1685 jsonResponse["FQDN"] = fqdn; 1686 } 1687 1688 if (ethData.vlanId) 1689 { 1690 jsonResponse["EthernetInterfaceType"] = "Virtual"; 1691 jsonResponse["VLAN"]["VLANEnable"] = true; 1692 jsonResponse["VLAN"]["VLANId"] = *ethData.vlanId; 1693 jsonResponse["VLAN"]["Tagged"] = true; 1694 1695 nlohmann::json::array_t relatedInterfaces; 1696 nlohmann::json& parentInterface = relatedInterfaces.emplace_back(); 1697 parentInterface["@odata.id"] = 1698 boost::urls::format("/redfish/v1/Managers/bmc/EthernetInterfaces", 1699 extractParentInterfaceName(ifaceId)); 1700 jsonResponse["Links"]["RelatedInterfaces"] = 1701 std::move(relatedInterfaces); 1702 } 1703 else 1704 { 1705 jsonResponse["EthernetInterfaceType"] = "Physical"; 1706 } 1707 1708 jsonResponse["NameServers"] = ethData.nameServers; 1709 jsonResponse["StaticNameServers"] = ethData.staticNameServers; 1710 1711 nlohmann::json& ipv4Array = jsonResponse["IPv4Addresses"]; 1712 nlohmann::json& ipv4StaticArray = jsonResponse["IPv4StaticAddresses"]; 1713 ipv4Array = nlohmann::json::array(); 1714 ipv4StaticArray = nlohmann::json::array(); 1715 for (const auto& ipv4Config : ipv4Data) 1716 { 1717 std::string gatewayStr = ipv4Config.gateway; 1718 if (gatewayStr.empty()) 1719 { 1720 gatewayStr = "0.0.0.0"; 1721 } 1722 nlohmann::json::object_t ipv4; 1723 ipv4["AddressOrigin"] = ipv4Config.origin; 1724 ipv4["SubnetMask"] = ipv4Config.netmask; 1725 ipv4["Address"] = ipv4Config.address; 1726 ipv4["Gateway"] = gatewayStr; 1727 1728 if (ipv4Config.origin == "Static") 1729 { 1730 ipv4StaticArray.push_back(ipv4); 1731 } 1732 1733 ipv4Array.emplace_back(std::move(ipv4)); 1734 } 1735 1736 std::string ipv6GatewayStr = ethData.ipv6DefaultGateway; 1737 if (ipv6GatewayStr.empty()) 1738 { 1739 ipv6GatewayStr = "0:0:0:0:0:0:0:0"; 1740 } 1741 1742 jsonResponse["IPv6DefaultGateway"] = ipv6GatewayStr; 1743 1744 nlohmann::json& ipv6Array = jsonResponse["IPv6Addresses"]; 1745 nlohmann::json& ipv6StaticArray = jsonResponse["IPv6StaticAddresses"]; 1746 ipv6Array = nlohmann::json::array(); 1747 ipv6StaticArray = nlohmann::json::array(); 1748 nlohmann::json& ipv6AddrPolicyTable = 1749 jsonResponse["IPv6AddressPolicyTable"]; 1750 ipv6AddrPolicyTable = nlohmann::json::array(); 1751 for (const auto& ipv6Config : ipv6Data) 1752 { 1753 nlohmann::json::object_t ipv6; 1754 ipv6["Address"] = ipv6Config.address; 1755 ipv6["PrefixLength"] = ipv6Config.prefixLength; 1756 ipv6["AddressOrigin"] = ipv6Config.origin; 1757 1758 ipv6Array.emplace_back(std::move(ipv6)); 1759 if (ipv6Config.origin == "Static") 1760 { 1761 nlohmann::json::object_t ipv6Static; 1762 ipv6Static["Address"] = ipv6Config.address; 1763 ipv6Static["PrefixLength"] = ipv6Config.prefixLength; 1764 ipv6StaticArray.emplace_back(std::move(ipv6Static)); 1765 } 1766 } 1767 } 1768 1769 inline void afterDelete(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1770 const std::string& ifaceId, 1771 const boost::system::error_code& ec, 1772 const sdbusplus::message_t& m) 1773 { 1774 if (!ec) 1775 { 1776 return; 1777 } 1778 const sd_bus_error* dbusError = m.get_error(); 1779 if (dbusError == nullptr) 1780 { 1781 messages::internalError(asyncResp->res); 1782 return; 1783 } 1784 BMCWEB_LOG_DEBUG("DBus error: {}", dbusError->name); 1785 1786 if (std::string_view("org.freedesktop.DBus.Error.UnknownObject") == 1787 dbusError->name) 1788 { 1789 messages::resourceNotFound(asyncResp->res, "EthernetInterface", 1790 ifaceId); 1791 return; 1792 } 1793 if (std::string_view("org.freedesktop.DBus.Error.UnknownMethod") == 1794 dbusError->name) 1795 { 1796 messages::resourceCannotBeDeleted(asyncResp->res); 1797 return; 1798 } 1799 messages::internalError(asyncResp->res); 1800 } 1801 1802 inline void afterVlanCreate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1803 const std::string& parentInterfaceUri, 1804 const std::string& vlanInterface, 1805 const boost::system::error_code& ec, 1806 const sdbusplus::message_t& m 1807 1808 ) 1809 { 1810 if (ec) 1811 { 1812 const sd_bus_error* dbusError = m.get_error(); 1813 if (dbusError == nullptr) 1814 { 1815 messages::internalError(asyncResp->res); 1816 return; 1817 } 1818 BMCWEB_LOG_DEBUG("DBus error: {}", dbusError->name); 1819 1820 if (std::string_view( 1821 "xyz.openbmc_project.Common.Error.ResourceNotFound") == 1822 dbusError->name) 1823 { 1824 messages::propertyValueNotInList( 1825 asyncResp->res, parentInterfaceUri, 1826 "Links/RelatedInterfaces/0/@odata.id"); 1827 return; 1828 } 1829 if (std::string_view( 1830 "xyz.openbmc_project.Common.Error.InvalidArgument") == 1831 dbusError->name) 1832 { 1833 messages::resourceAlreadyExists(asyncResp->res, "EthernetInterface", 1834 "Id", vlanInterface); 1835 return; 1836 } 1837 messages::internalError(asyncResp->res); 1838 return; 1839 } 1840 1841 const boost::urls::url vlanInterfaceUri = boost::urls::format( 1842 "/redfish/v1/Managers/bmc/EthernetInterfaces/{}", vlanInterface); 1843 asyncResp->res.addHeader("Location", vlanInterfaceUri.buffer()); 1844 } 1845 1846 inline void requestEthernetInterfacesRoutes(App& app) 1847 { 1848 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/") 1849 .privileges(redfish::privileges::getEthernetInterfaceCollection) 1850 .methods(boost::beast::http::verb::get)( 1851 [&app](const crow::Request& req, 1852 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 1853 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1854 { 1855 return; 1856 } 1857 1858 asyncResp->res.jsonValue["@odata.type"] = 1859 "#EthernetInterfaceCollection.EthernetInterfaceCollection"; 1860 asyncResp->res.jsonValue["@odata.id"] = 1861 "/redfish/v1/Managers/bmc/EthernetInterfaces"; 1862 asyncResp->res.jsonValue["Name"] = 1863 "Ethernet Network Interface Collection"; 1864 asyncResp->res.jsonValue["Description"] = 1865 "Collection of EthernetInterfaces for this Manager"; 1866 1867 // Get eth interface list, and call the below callback for JSON 1868 // preparation 1869 getEthernetIfaceList( 1870 [asyncResp](const bool& success, 1871 const std::vector<std::string>& ifaceList) { 1872 if (!success) 1873 { 1874 messages::internalError(asyncResp->res); 1875 return; 1876 } 1877 1878 nlohmann::json& ifaceArray = asyncResp->res.jsonValue["Members"]; 1879 ifaceArray = nlohmann::json::array(); 1880 for (const std::string& ifaceItem : ifaceList) 1881 { 1882 nlohmann::json::object_t iface; 1883 iface["@odata.id"] = boost::urls::format( 1884 "/redfish/v1/Managers/bmc/EthernetInterfaces/{}", 1885 ifaceItem); 1886 ifaceArray.push_back(std::move(iface)); 1887 } 1888 1889 asyncResp->res.jsonValue["Members@odata.count"] = ifaceArray.size(); 1890 asyncResp->res.jsonValue["@odata.id"] = 1891 "/redfish/v1/Managers/bmc/EthernetInterfaces"; 1892 }); 1893 }); 1894 1895 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/") 1896 .privileges(redfish::privileges::postEthernetInterfaceCollection) 1897 .methods(boost::beast::http::verb::post)( 1898 [&app](const crow::Request& req, 1899 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 1900 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1901 { 1902 return; 1903 } 1904 1905 bool vlanEnable = false; 1906 uint32_t vlanId = 0; 1907 nlohmann::json::array_t relatedInterfaces; 1908 1909 if (!json_util::readJsonPatch(req, asyncResp->res, "VLAN/VLANEnable", 1910 vlanEnable, "VLAN/VLANId", vlanId, 1911 "Links/RelatedInterfaces", 1912 relatedInterfaces)) 1913 { 1914 return; 1915 } 1916 1917 if (relatedInterfaces.size() != 1) 1918 { 1919 messages::arraySizeTooLong(asyncResp->res, 1920 "Links/RelatedInterfaces", 1921 relatedInterfaces.size()); 1922 return; 1923 } 1924 1925 std::string parentInterfaceUri; 1926 if (!json_util::readJson(relatedInterfaces[0], asyncResp->res, 1927 "@odata.id", parentInterfaceUri)) 1928 { 1929 messages::propertyMissing(asyncResp->res, 1930 "Links/RelatedInterfaces/0/@odata.id"); 1931 return; 1932 } 1933 BMCWEB_LOG_INFO("Parent Interface URI: {}", parentInterfaceUri); 1934 1935 boost::system::result<boost::urls::url_view> parsedUri = 1936 boost::urls::parse_relative_ref(parentInterfaceUri); 1937 if (!parsedUri) 1938 { 1939 messages::propertyValueFormatError( 1940 asyncResp->res, parentInterfaceUri, 1941 "Links/RelatedInterfaces/0/@odata.id"); 1942 return; 1943 } 1944 1945 std::string parentInterface; 1946 if (!crow::utility::readUrlSegments( 1947 *parsedUri, "redfish", "v1", "Managers", "bmc", 1948 "EthernetInterfaces", std::ref(parentInterface))) 1949 { 1950 messages::propertyValueNotInList( 1951 asyncResp->res, parentInterfaceUri, 1952 "Links/RelatedInterfaces/0/@odata.id"); 1953 return; 1954 } 1955 1956 if (!vlanEnable) 1957 { 1958 // In OpenBMC implementation, VLANEnable cannot be false on 1959 // create 1960 messages::propertyValueIncorrect(asyncResp->res, "VLAN/VLANEnable", 1961 "false"); 1962 return; 1963 } 1964 1965 std::string vlanInterface = parentInterface + "_" + 1966 std::to_string(vlanId); 1967 crow::connections::systemBus->async_method_call( 1968 [asyncResp, parentInterfaceUri, 1969 vlanInterface](const boost::system::error_code& ec, 1970 const sdbusplus::message_t& m) { 1971 afterVlanCreate(asyncResp, parentInterfaceUri, vlanInterface, ec, 1972 m); 1973 }, 1974 "xyz.openbmc_project.Network", "/xyz/openbmc_project/network", 1975 "xyz.openbmc_project.Network.VLAN.Create", "VLAN", parentInterface, 1976 vlanId); 1977 }); 1978 1979 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/") 1980 .privileges(redfish::privileges::getEthernetInterface) 1981 .methods(boost::beast::http::verb::get)( 1982 [&app](const crow::Request& req, 1983 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1984 const std::string& ifaceId) { 1985 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1986 { 1987 return; 1988 } 1989 getEthernetIfaceData( 1990 ifaceId, 1991 [asyncResp, ifaceId](const bool& success, 1992 const EthernetInterfaceData& ethData, 1993 const std::vector<IPv4AddressData>& ipv4Data, 1994 const std::vector<IPv6AddressData>& ipv6Data) { 1995 if (!success) 1996 { 1997 // TODO(Pawel)consider distinguish between non 1998 // existing object, and other errors 1999 messages::resourceNotFound(asyncResp->res, "EthernetInterface", 2000 ifaceId); 2001 return; 2002 } 2003 2004 asyncResp->res.jsonValue["@odata.type"] = 2005 "#EthernetInterface.v1_9_0.EthernetInterface"; 2006 asyncResp->res.jsonValue["Name"] = "Manager Ethernet Interface"; 2007 asyncResp->res.jsonValue["Description"] = 2008 "Management Network Interface"; 2009 2010 parseInterfaceData(asyncResp, ifaceId, ethData, ipv4Data, ipv6Data); 2011 }); 2012 }); 2013 2014 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/") 2015 .privileges(redfish::privileges::patchEthernetInterface) 2016 .methods(boost::beast::http::verb::patch)( 2017 [&app](const crow::Request& req, 2018 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2019 const std::string& ifaceId) { 2020 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2021 { 2022 return; 2023 } 2024 std::optional<std::string> hostname; 2025 std::optional<std::string> fqdn; 2026 std::optional<std::string> macAddress; 2027 std::optional<std::string> ipv6DefaultGateway; 2028 std::optional<nlohmann::json::array_t> ipv4StaticAddresses; 2029 std::optional<nlohmann::json::array_t> ipv6StaticAddresses; 2030 std::optional<std::vector<std::string>> staticNameServers; 2031 std::optional<nlohmann::json> dhcpv4; 2032 std::optional<nlohmann::json> dhcpv6; 2033 std::optional<bool> ipv6AutoConfigEnabled; 2034 std::optional<bool> interfaceEnabled; 2035 std::optional<size_t> mtuSize; 2036 DHCPParameters v4dhcpParms; 2037 DHCPParameters v6dhcpParms; 2038 // clang-format off 2039 if (!json_util::readJsonPatch( 2040 req, asyncResp->res, 2041 "DHCPv4", dhcpv4, 2042 "DHCPv6", dhcpv6, 2043 "FQDN", fqdn, 2044 "HostName", hostname, 2045 "IPv4StaticAddresses", ipv4StaticAddresses, 2046 "IPv6DefaultGateway", ipv6DefaultGateway, 2047 "IPv6StaticAddresses", ipv6StaticAddresses, 2048 "InterfaceEnabled", interfaceEnabled, 2049 "MACAddress", macAddress, 2050 "MTUSize", mtuSize, 2051 "StatelessAddressAutoConfig/IPv6AutoConfigEnabled", ipv6AutoConfigEnabled, 2052 "StaticNameServers", staticNameServers 2053 ) 2054 ) 2055 { 2056 return; 2057 } 2058 //clang-format on 2059 if (dhcpv4) 2060 { 2061 if (!json_util::readJson(*dhcpv4, asyncResp->res, "DHCPEnabled", 2062 v4dhcpParms.dhcpv4Enabled, "UseDNSServers", 2063 v4dhcpParms.useDnsServers, "UseNTPServers", 2064 v4dhcpParms.useNtpServers, "UseDomainName", 2065 v4dhcpParms.useDomainName)) 2066 { 2067 return; 2068 } 2069 } 2070 2071 if (dhcpv6) 2072 { 2073 if (!json_util::readJson(*dhcpv6, asyncResp->res, "OperatingMode", 2074 v6dhcpParms.dhcpv6OperatingMode, 2075 "UseDNSServers", v6dhcpParms.useDnsServers, 2076 "UseNTPServers", v6dhcpParms.useNtpServers, 2077 "UseDomainName", 2078 v6dhcpParms.useDomainName)) 2079 { 2080 return; 2081 } 2082 } 2083 2084 // Get single eth interface data, and call the below callback 2085 // for JSON preparation 2086 getEthernetIfaceData( 2087 ifaceId, 2088 [asyncResp, ifaceId, hostname = std::move(hostname), 2089 fqdn = std::move(fqdn), macAddress = std::move(macAddress), 2090 ipv4StaticAddresses = std::move(ipv4StaticAddresses), 2091 ipv6DefaultGateway = std::move(ipv6DefaultGateway), 2092 ipv6StaticAddresses = std::move(ipv6StaticAddresses), 2093 staticNameServers = std::move(staticNameServers), 2094 dhcpv4 = std::move(dhcpv4), dhcpv6 = std::move(dhcpv6), mtuSize, 2095 ipv6AutoConfigEnabled, v4dhcpParms = std::move(v4dhcpParms), 2096 v6dhcpParms = std::move(v6dhcpParms), interfaceEnabled]( 2097 const bool& success, const EthernetInterfaceData& ethData, 2098 const std::vector<IPv4AddressData>& ipv4Data, 2099 const std::vector<IPv6AddressData>& ipv6Data) { 2100 if (!success) 2101 { 2102 // ... otherwise return error 2103 // TODO(Pawel)consider distinguish between non 2104 // existing object, and other errors 2105 messages::resourceNotFound(asyncResp->res, "EthernetInterface", 2106 ifaceId); 2107 return; 2108 } 2109 2110 if (dhcpv4 || dhcpv6) 2111 { 2112 handleDHCPPatch(ifaceId, ethData, v4dhcpParms, v6dhcpParms, 2113 asyncResp); 2114 } 2115 2116 if (hostname) 2117 { 2118 handleHostnamePatch(*hostname, asyncResp); 2119 } 2120 2121 if (ipv6AutoConfigEnabled) 2122 { 2123 handleSLAACAutoConfigPatch(ifaceId, *ipv6AutoConfigEnabled, 2124 asyncResp); 2125 } 2126 2127 if (fqdn) 2128 { 2129 handleFqdnPatch(ifaceId, *fqdn, asyncResp); 2130 } 2131 2132 if (macAddress) 2133 { 2134 handleMACAddressPatch(ifaceId, *macAddress, asyncResp); 2135 } 2136 2137 if (ipv4StaticAddresses) 2138 { 2139 // TODO(ed) for some reason the capture of 2140 // ipv4Addresses above is returning a const value, 2141 // not a non-const value. This doesn't really work 2142 // for us, as we need to be able to efficiently move 2143 // out the intermedia nlohmann::json objects. This 2144 // makes a copy of the structure, and operates on 2145 // that, but could be done more efficiently 2146 nlohmann::json::array_t ipv4Static = *ipv4StaticAddresses; 2147 handleIPv4StaticPatch(ifaceId, ipv4Static, ipv4Data, asyncResp); 2148 } 2149 2150 if (staticNameServers) 2151 { 2152 handleStaticNameServersPatch(ifaceId, *staticNameServers, 2153 asyncResp); 2154 } 2155 2156 if (ipv6DefaultGateway) 2157 { 2158 messages::propertyNotWritable(asyncResp->res, 2159 "IPv6DefaultGateway"); 2160 } 2161 2162 if (ipv6StaticAddresses) 2163 { 2164 handleIPv6StaticAddressesPatch(ifaceId, *ipv6StaticAddresses, 2165 ipv6Data, asyncResp); 2166 } 2167 2168 if (interfaceEnabled) 2169 { 2170 setEthernetInterfaceBoolProperty(ifaceId, "NICEnabled", 2171 *interfaceEnabled, asyncResp); 2172 } 2173 2174 if (mtuSize) 2175 { 2176 handleMTUSizePatch(ifaceId, *mtuSize, asyncResp); 2177 } 2178 }); 2179 }); 2180 2181 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/") 2182 .privileges(redfish::privileges::deleteEthernetInterface) 2183 .methods(boost::beast::http::verb::delete_)( 2184 [&app](const crow::Request& req, 2185 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2186 const std::string& ifaceId) { 2187 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2188 { 2189 return; 2190 } 2191 2192 crow::connections::systemBus->async_method_call( 2193 [asyncResp, ifaceId](const boost::system::error_code& ec, 2194 const sdbusplus::message_t& m) { 2195 afterDelete(asyncResp, ifaceId, ec, m); 2196 }, 2197 "xyz.openbmc_project.Network", 2198 std::string("/xyz/openbmc_project/network/") + ifaceId, 2199 "xyz.openbmc_project.Object.Delete", "Delete"); 2200 }); 2201 } 2202 2203 } // namespace redfish 2204