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