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