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