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.context"] = 1009 "/redfish/v1/" 1010 "$metadata#EthernetInterfaceCollection.EthernetInterfaceCollection"; 1011 res.jsonValue["@odata.id"] = 1012 "/redfish/v1/Managers/bmc/EthernetInterfaces"; 1013 res.jsonValue["Name"] = "Ethernet Network Interface Collection"; 1014 res.jsonValue["Description"] = 1015 "Collection of EthernetInterfaces for this Manager"; 1016 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 1017 // Get eth interface list, and call the below callback for JSON 1018 // preparation 1019 getEthernetIfaceList( 1020 [asyncResp]( 1021 const bool &success, 1022 const boost::container::flat_set<std::string> &iface_list) { 1023 if (!success) 1024 { 1025 messages::internalError(asyncResp->res); 1026 return; 1027 } 1028 1029 nlohmann::json &iface_array = 1030 asyncResp->res.jsonValue["Members"]; 1031 iface_array = nlohmann::json::array(); 1032 std::string tag = "_"; 1033 for (const std::string &iface_item : iface_list) 1034 { 1035 std::size_t found = iface_item.find(tag); 1036 if (found == std::string::npos) 1037 { 1038 iface_array.push_back( 1039 {{"@odata.id", 1040 "/redfish/v1/Managers/bmc/EthernetInterfaces/" + 1041 iface_item}}); 1042 } 1043 } 1044 1045 asyncResp->res.jsonValue["Members@odata.count"] = 1046 iface_array.size(); 1047 asyncResp->res.jsonValue["@odata.id"] = 1048 "/redfish/v1/Managers/bmc/EthernetInterfaces"; 1049 }); 1050 } 1051 }; 1052 1053 /** 1054 * EthernetInterface derived class for delivering Ethernet Schema 1055 */ 1056 class EthernetInterface : public Node 1057 { 1058 public: 1059 /* 1060 * Default Constructor 1061 */ 1062 template <typename CrowApp> 1063 EthernetInterface(CrowApp &app) : 1064 Node(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/", 1065 std::string()) 1066 { 1067 entityPrivileges = { 1068 {boost::beast::http::verb::get, {{"Login"}}}, 1069 {boost::beast::http::verb::head, {{"Login"}}}, 1070 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 1071 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 1072 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 1073 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 1074 } 1075 1076 private: 1077 void handleHostnamePatch(const std::string &hostname, 1078 const std::shared_ptr<AsyncResp> asyncResp) 1079 { 1080 asyncResp->res.jsonValue["HostName"] = hostname; 1081 crow::connections::systemBus->async_method_call( 1082 [asyncResp](const boost::system::error_code ec) { 1083 if (ec) 1084 { 1085 messages::internalError(asyncResp->res); 1086 } 1087 }, 1088 "xyz.openbmc_project.Network", 1089 "/xyz/openbmc_project/network/config", 1090 "org.freedesktop.DBus.Properties", "Set", 1091 "xyz.openbmc_project.Network.SystemConfiguration", "HostName", 1092 std::variant<std::string>(hostname)); 1093 } 1094 1095 void handleMACAddressPatch(const std::string &ifaceId, 1096 const std::string &macAddress, 1097 const std::shared_ptr<AsyncResp> &asyncResp) 1098 { 1099 crow::connections::systemBus->async_method_call( 1100 [asyncResp, macAddress](const boost::system::error_code ec) { 1101 if (ec) 1102 { 1103 messages::internalError(asyncResp->res); 1104 return; 1105 } 1106 }, 1107 "xyz.openbmc_project.Network", 1108 "/xyz/openbmc_project/network/" + ifaceId, 1109 "org.freedesktop.DBus.Properties", "Set", 1110 "xyz.openbmc_project.Network.MACAddress", "MACAddress", 1111 std::variant<std::string>(macAddress)); 1112 } 1113 1114 void setDHCPEnabled(const std::string &ifaceId, 1115 const std::string &propertyName, const bool v4Value, 1116 const bool v6Value, 1117 const std::shared_ptr<AsyncResp> asyncResp) 1118 { 1119 const std::string dhcp = GetDHCPEnabledEnumeration(v4Value, v6Value); 1120 crow::connections::systemBus->async_method_call( 1121 [asyncResp](const boost::system::error_code ec) { 1122 if (ec) 1123 { 1124 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec; 1125 messages::internalError(asyncResp->res); 1126 return; 1127 } 1128 }, 1129 "xyz.openbmc_project.Network", 1130 "/xyz/openbmc_project/network/" + ifaceId, 1131 "org.freedesktop.DBus.Properties", "Set", 1132 "xyz.openbmc_project.Network.EthernetInterface", propertyName, 1133 std::variant<std::string>{dhcp}); 1134 } 1135 1136 void setDHCPv4Config(const std::string &propertyName, const bool &value, 1137 const std::shared_ptr<AsyncResp> asyncResp) 1138 { 1139 BMCWEB_LOG_DEBUG << propertyName << " = " << value; 1140 crow::connections::systemBus->async_method_call( 1141 [asyncResp](const boost::system::error_code ec) { 1142 if (ec) 1143 { 1144 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec; 1145 messages::internalError(asyncResp->res); 1146 return; 1147 } 1148 }, 1149 "xyz.openbmc_project.Network", 1150 "/xyz/openbmc_project/network/config/dhcp", 1151 "org.freedesktop.DBus.Properties", "Set", 1152 "xyz.openbmc_project.Network.DHCPConfiguration", propertyName, 1153 std::variant<bool>{value}); 1154 } 1155 1156 void handleDHCPPatch(const std::string &ifaceId, 1157 const EthernetInterfaceData ðData, 1158 DHCPParameters v4dhcpParms, DHCPParameters v6dhcpParms, 1159 const std::shared_ptr<AsyncResp> asyncResp) 1160 { 1161 bool ipv4Active = translateDHCPEnabledToBool(ethData.DHCPEnabled, true); 1162 bool ipv6Active = 1163 translateDHCPEnabledToBool(ethData.DHCPEnabled, false); 1164 1165 bool nextv4DHCPState = 1166 v4dhcpParms.dhcpv4Enabled ? *v4dhcpParms.dhcpv4Enabled : ipv4Active; 1167 1168 bool nextv6DHCPState{}; 1169 if (v6dhcpParms.dhcpv6OperatingMode) 1170 { 1171 if ((*v6dhcpParms.dhcpv6OperatingMode != "Stateful") && 1172 (*v6dhcpParms.dhcpv6OperatingMode != "Stateless") && 1173 (*v6dhcpParms.dhcpv6OperatingMode != "Disabled")) 1174 { 1175 messages::propertyValueFormatError( 1176 asyncResp->res, *v6dhcpParms.dhcpv6OperatingMode, 1177 "OperatingMode"); 1178 return; 1179 } 1180 nextv6DHCPState = (*v6dhcpParms.dhcpv6OperatingMode == "Stateful"); 1181 } 1182 else 1183 { 1184 nextv6DHCPState = ipv6Active; 1185 } 1186 1187 bool nextDNS{}; 1188 if (v4dhcpParms.useDNSServers && v6dhcpParms.useDNSServers) 1189 { 1190 if (*v4dhcpParms.useDNSServers != *v6dhcpParms.useDNSServers) 1191 { 1192 messages::generalError(asyncResp->res); 1193 return; 1194 } 1195 nextDNS = *v4dhcpParms.useDNSServers; 1196 } 1197 else if (v4dhcpParms.useDNSServers) 1198 { 1199 nextDNS = *v4dhcpParms.useDNSServers; 1200 } 1201 else if (v6dhcpParms.useDNSServers) 1202 { 1203 nextDNS = *v6dhcpParms.useDNSServers; 1204 } 1205 else 1206 { 1207 nextDNS = ethData.DNSEnabled; 1208 } 1209 1210 bool nextNTP{}; 1211 if (v4dhcpParms.useNTPServers && v6dhcpParms.useNTPServers) 1212 { 1213 if (*v4dhcpParms.useNTPServers != *v6dhcpParms.useNTPServers) 1214 { 1215 messages::generalError(asyncResp->res); 1216 return; 1217 } 1218 nextNTP = *v4dhcpParms.useNTPServers; 1219 } 1220 else if (v4dhcpParms.useNTPServers) 1221 { 1222 nextNTP = *v4dhcpParms.useNTPServers; 1223 } 1224 else if (v6dhcpParms.useNTPServers) 1225 { 1226 nextNTP = *v6dhcpParms.useNTPServers; 1227 } 1228 else 1229 { 1230 nextNTP = ethData.NTPEnabled; 1231 } 1232 1233 bool nextUseDomain{}; 1234 if (v4dhcpParms.useUseDomainName && v6dhcpParms.useUseDomainName) 1235 { 1236 if (*v4dhcpParms.useUseDomainName != *v6dhcpParms.useUseDomainName) 1237 { 1238 messages::generalError(asyncResp->res); 1239 return; 1240 } 1241 nextUseDomain = *v4dhcpParms.useUseDomainName; 1242 } 1243 else if (v4dhcpParms.useUseDomainName) 1244 { 1245 nextUseDomain = *v4dhcpParms.useUseDomainName; 1246 } 1247 else if (v6dhcpParms.useUseDomainName) 1248 { 1249 nextUseDomain = *v6dhcpParms.useUseDomainName; 1250 } 1251 else 1252 { 1253 nextUseDomain = ethData.HostNameEnabled; 1254 } 1255 1256 BMCWEB_LOG_DEBUG << "set DHCPEnabled..."; 1257 setDHCPEnabled(ifaceId, "DHCPEnabled", nextv4DHCPState, nextv6DHCPState, 1258 asyncResp); 1259 BMCWEB_LOG_DEBUG << "set DNSEnabled..."; 1260 setDHCPv4Config("DNSEnabled", nextDNS, asyncResp); 1261 BMCWEB_LOG_DEBUG << "set NTPEnabled..."; 1262 setDHCPv4Config("NTPEnabled", nextNTP, asyncResp); 1263 BMCWEB_LOG_DEBUG << "set HostNameEnabled..."; 1264 setDHCPv4Config("HostNameEnabled", nextUseDomain, asyncResp); 1265 } 1266 1267 boost::container::flat_set<IPv4AddressData>::const_iterator 1268 GetNextStaticIPEntry( 1269 boost::container::flat_set<IPv4AddressData>::const_iterator head, 1270 boost::container::flat_set<IPv4AddressData>::const_iterator end) 1271 { 1272 for (; head != end; head++) 1273 { 1274 if (head->origin == "Static") 1275 { 1276 return head; 1277 } 1278 } 1279 return end; 1280 } 1281 1282 boost::container::flat_set<IPv6AddressData>::const_iterator 1283 GetNextStaticIPEntry( 1284 boost::container::flat_set<IPv6AddressData>::const_iterator head, 1285 boost::container::flat_set<IPv6AddressData>::const_iterator end) 1286 { 1287 for (; head != end; head++) 1288 { 1289 if (head->origin == "Static") 1290 { 1291 return head; 1292 } 1293 } 1294 return end; 1295 } 1296 1297 void handleIPv4StaticPatch( 1298 const std::string &ifaceId, nlohmann::json &input, 1299 const boost::container::flat_set<IPv4AddressData> &ipv4Data, 1300 const std::shared_ptr<AsyncResp> asyncResp) 1301 { 1302 if ((!input.is_array()) || input.empty()) 1303 { 1304 messages::propertyValueTypeError(asyncResp->res, input.dump(), 1305 "IPv4StaticAddresses"); 1306 return; 1307 } 1308 1309 unsigned entryIdx = 1; 1310 // Find the first static IP address currently active on the NIC and 1311 // match it to the first JSON element in the IPv4StaticAddresses array. 1312 // Match each subsequent JSON element to the next static IP programmed 1313 // into the NIC. 1314 boost::container::flat_set<IPv4AddressData>::const_iterator NICIPentry = 1315 GetNextStaticIPEntry(ipv4Data.cbegin(), ipv4Data.cend()); 1316 1317 for (nlohmann::json &thisJson : input) 1318 { 1319 std::string pathString = 1320 "IPv4StaticAddresses/" + std::to_string(entryIdx); 1321 1322 if (!thisJson.is_null() && !thisJson.empty()) 1323 { 1324 std::optional<std::string> address; 1325 std::optional<std::string> subnetMask; 1326 std::optional<std::string> gateway; 1327 1328 if (!json_util::readJson(thisJson, asyncResp->res, "Address", 1329 address, "SubnetMask", subnetMask, 1330 "Gateway", gateway)) 1331 { 1332 messages::propertyValueFormatError( 1333 asyncResp->res, thisJson.dump(), pathString); 1334 return; 1335 } 1336 1337 // Find the address/subnet/gateway values. Any values that are 1338 // not explicitly provided are assumed to be unmodified from the 1339 // current state of the interface. Merge existing state into the 1340 // current request. 1341 const std::string *addr = nullptr; 1342 const std::string *gw = nullptr; 1343 uint8_t prefixLength = 0; 1344 bool errorInEntry = false; 1345 if (address) 1346 { 1347 if (ipv4VerifyIpAndGetBitcount(*address)) 1348 { 1349 addr = &(*address); 1350 } 1351 else 1352 { 1353 messages::propertyValueFormatError( 1354 asyncResp->res, *address, pathString + "/Address"); 1355 errorInEntry = true; 1356 } 1357 } 1358 else if (NICIPentry != ipv4Data.cend()) 1359 { 1360 addr = &(NICIPentry->address); 1361 } 1362 else 1363 { 1364 messages::propertyMissing(asyncResp->res, 1365 pathString + "/Address"); 1366 errorInEntry = true; 1367 } 1368 1369 if (subnetMask) 1370 { 1371 if (!ipv4VerifyIpAndGetBitcount(*subnetMask, &prefixLength)) 1372 { 1373 messages::propertyValueFormatError( 1374 asyncResp->res, *subnetMask, 1375 pathString + "/SubnetMask"); 1376 errorInEntry = true; 1377 } 1378 } 1379 else if (NICIPentry != ipv4Data.cend()) 1380 { 1381 if (!ipv4VerifyIpAndGetBitcount(NICIPentry->netmask, 1382 &prefixLength)) 1383 { 1384 messages::propertyValueFormatError( 1385 asyncResp->res, NICIPentry->netmask, 1386 pathString + "/SubnetMask"); 1387 errorInEntry = true; 1388 } 1389 } 1390 else 1391 { 1392 messages::propertyMissing(asyncResp->res, 1393 pathString + "/SubnetMask"); 1394 errorInEntry = true; 1395 } 1396 1397 if (gateway) 1398 { 1399 if (ipv4VerifyIpAndGetBitcount(*gateway)) 1400 { 1401 gw = &(*gateway); 1402 } 1403 else 1404 { 1405 messages::propertyValueFormatError( 1406 asyncResp->res, *gateway, pathString + "/Gateway"); 1407 errorInEntry = true; 1408 } 1409 } 1410 else if (NICIPentry != ipv4Data.cend()) 1411 { 1412 gw = &NICIPentry->gateway; 1413 } 1414 else 1415 { 1416 messages::propertyMissing(asyncResp->res, 1417 pathString + "/Gateway"); 1418 errorInEntry = true; 1419 } 1420 1421 if (errorInEntry) 1422 { 1423 return; 1424 } 1425 1426 if (NICIPentry != ipv4Data.cend()) 1427 { 1428 if (gw != nullptr || addr != nullptr) 1429 { 1430 // Shouldn't be possible based on errorInEntry, but 1431 // it flags -wmaybe-uninitialized in the compiler, 1432 // so defend against that 1433 return; 1434 } 1435 deleteAndCreateIPv4(ifaceId, NICIPentry->id, prefixLength, 1436 *gw, *addr, asyncResp); 1437 NICIPentry = 1438 GetNextStaticIPEntry(++NICIPentry, ipv4Data.cend()); 1439 } 1440 else 1441 { 1442 createIPv4(ifaceId, entryIdx, prefixLength, *gateway, 1443 *address, asyncResp); 1444 } 1445 entryIdx++; 1446 } 1447 else 1448 { 1449 if (NICIPentry == ipv4Data.cend()) 1450 { 1451 // Requesting a DELETE/DO NOT MODIFY action for an item 1452 // that isn't present on the eth(n) interface. Input JSON is 1453 // in error, so bail out. 1454 if (thisJson.is_null()) 1455 { 1456 messages::resourceCannotBeDeleted(asyncResp->res); 1457 return; 1458 } 1459 else 1460 { 1461 messages::propertyValueFormatError( 1462 asyncResp->res, thisJson.dump(), pathString); 1463 return; 1464 } 1465 } 1466 1467 if (thisJson.is_null()) 1468 { 1469 deleteIPv4(ifaceId, NICIPentry->id, asyncResp); 1470 } 1471 if (NICIPentry != ipv4Data.cend()) 1472 { 1473 NICIPentry = 1474 GetNextStaticIPEntry(++NICIPentry, ipv4Data.cend()); 1475 } 1476 entryIdx++; 1477 } 1478 } 1479 } 1480 1481 void handleStaticNameServersPatch( 1482 const std::string &ifaceId, 1483 const std::vector<std::string> &updatedStaticNameServers, 1484 const std::shared_ptr<AsyncResp> &asyncResp) 1485 { 1486 crow::connections::systemBus->async_method_call( 1487 [asyncResp](const boost::system::error_code ec) { 1488 if (ec) 1489 { 1490 messages::internalError(asyncResp->res); 1491 return; 1492 } 1493 }, 1494 "xyz.openbmc_project.Network", 1495 "/xyz/openbmc_project/network/" + ifaceId, 1496 "org.freedesktop.DBus.Properties", "Set", 1497 "xyz.openbmc_project.Network.EthernetInterface", "Nameservers", 1498 std::variant<std::vector<std::string>>{updatedStaticNameServers}); 1499 } 1500 1501 void handleIPv6StaticAddressesPatch( 1502 const std::string &ifaceId, nlohmann::json &input, 1503 const boost::container::flat_set<IPv6AddressData> &ipv6Data, 1504 const std::shared_ptr<AsyncResp> asyncResp) 1505 { 1506 if (!input.is_array() || input.empty()) 1507 { 1508 messages::propertyValueTypeError(asyncResp->res, input.dump(), 1509 "IPv6StaticAddresses"); 1510 return; 1511 } 1512 size_t entryIdx = 1; 1513 boost::container::flat_set<IPv6AddressData>::const_iterator NICIPentry = 1514 GetNextStaticIPEntry(ipv6Data.cbegin(), ipv6Data.cend()); 1515 for (nlohmann::json &thisJson : input) 1516 { 1517 std::string pathString = 1518 "IPv6StaticAddresses/" + std::to_string(entryIdx); 1519 1520 if (!thisJson.is_null() && !thisJson.empty()) 1521 { 1522 std::optional<std::string> address; 1523 std::optional<uint8_t> prefixLength; 1524 1525 if (!json_util::readJson(thisJson, asyncResp->res, "Address", 1526 address, "PrefixLength", prefixLength)) 1527 { 1528 messages::propertyValueFormatError( 1529 asyncResp->res, thisJson.dump(), pathString); 1530 return; 1531 } 1532 1533 const std::string *addr; 1534 uint8_t prefix; 1535 1536 // Find the address and prefixLength values. Any values that are 1537 // not explicitly provided are assumed to be unmodified from the 1538 // current state of the interface. Merge existing state into the 1539 // current request. 1540 if (address) 1541 { 1542 addr = &(*address); 1543 } 1544 else if (NICIPentry != ipv6Data.end()) 1545 { 1546 addr = &(NICIPentry->address); 1547 } 1548 else 1549 { 1550 messages::propertyMissing(asyncResp->res, 1551 pathString + "/Address"); 1552 return; 1553 } 1554 1555 if (prefixLength) 1556 { 1557 prefix = *prefixLength; 1558 } 1559 else if (NICIPentry != ipv6Data.end()) 1560 { 1561 prefix = NICIPentry->prefixLength; 1562 } 1563 else 1564 { 1565 messages::propertyMissing(asyncResp->res, 1566 pathString + "/PrefixLength"); 1567 return; 1568 } 1569 1570 if (NICIPentry != ipv6Data.end()) 1571 { 1572 deleteAndCreateIPv6(ifaceId, NICIPentry->id, prefix, *addr, 1573 asyncResp); 1574 NICIPentry = 1575 GetNextStaticIPEntry(++NICIPentry, ipv6Data.cend()); 1576 } 1577 else 1578 { 1579 createIPv6(ifaceId, *prefixLength, *addr, asyncResp); 1580 } 1581 entryIdx++; 1582 } 1583 else 1584 { 1585 if (NICIPentry == ipv6Data.end()) 1586 { 1587 // Requesting a DELETE/DO NOT MODIFY action for an item 1588 // that isn't present on the eth(n) interface. Input JSON is 1589 // in error, so bail out. 1590 if (thisJson.is_null()) 1591 { 1592 messages::resourceCannotBeDeleted(asyncResp->res); 1593 return; 1594 } 1595 else 1596 { 1597 messages::propertyValueFormatError( 1598 asyncResp->res, thisJson.dump(), pathString); 1599 return; 1600 } 1601 } 1602 1603 if (thisJson.is_null()) 1604 { 1605 deleteIPv6(ifaceId, NICIPentry->id, asyncResp); 1606 } 1607 if (NICIPentry != ipv6Data.cend()) 1608 { 1609 NICIPentry = 1610 GetNextStaticIPEntry(++NICIPentry, ipv6Data.cend()); 1611 } 1612 entryIdx++; 1613 } 1614 } 1615 } 1616 1617 void parseInterfaceData( 1618 nlohmann::json &json_response, const std::string &iface_id, 1619 const EthernetInterfaceData ðData, 1620 const boost::container::flat_set<IPv4AddressData> &ipv4Data, 1621 const boost::container::flat_set<IPv6AddressData> &ipv6Data) 1622 { 1623 json_response["Id"] = iface_id; 1624 json_response["@odata.id"] = 1625 "/redfish/v1/Managers/bmc/EthernetInterfaces/" + iface_id; 1626 json_response["InterfaceEnabled"] = true; 1627 if (ethData.speed == 0) 1628 { 1629 json_response["Status"] = { 1630 {"Health", "OK"}, 1631 {"State", "Disabled"}, 1632 }; 1633 } 1634 else 1635 { 1636 json_response["Status"] = { 1637 {"Health", "OK"}, 1638 {"State", "Enabled"}, 1639 }; 1640 } 1641 1642 json_response["LinkStatus"] = ethData.linkUp ? "LinkUp" : "LinkDown"; 1643 json_response["SpeedMbps"] = ethData.speed; 1644 json_response["MACAddress"] = ethData.mac_address; 1645 json_response["DHCPv4"]["DHCPEnabled"] = 1646 translateDHCPEnabledToBool(ethData.DHCPEnabled, true); 1647 json_response["DHCPv4"]["UseNTPServers"] = ethData.NTPEnabled; 1648 json_response["DHCPv4"]["UseDNSServers"] = ethData.DNSEnabled; 1649 json_response["DHCPv4"]["UseDomainName"] = ethData.HostNameEnabled; 1650 1651 json_response["DHCPv6"]["OperatingMode"] = 1652 translateDHCPEnabledToBool(ethData.DHCPEnabled, false) ? "Stateful" 1653 : "Disabled"; 1654 json_response["DHCPv6"]["UseNTPServers"] = ethData.NTPEnabled; 1655 json_response["DHCPv6"]["UseDNSServers"] = ethData.DNSEnabled; 1656 json_response["DHCPv6"]["UseDomainName"] = ethData.HostNameEnabled; 1657 1658 if (!ethData.hostname.empty()) 1659 { 1660 json_response["HostName"] = ethData.hostname; 1661 if (!ethData.domainnames.empty()) 1662 { 1663 json_response["FQDN"] = 1664 ethData.hostname + "." + ethData.domainnames[0]; 1665 } 1666 } 1667 1668 json_response["VLANs"] = { 1669 {"@odata.id", "/redfish/v1/Managers/bmc/EthernetInterfaces/" + 1670 iface_id + "/VLANs"}}; 1671 1672 if (translateDHCPEnabledToBool(ethData.DHCPEnabled, true) && 1673 ethData.DNSEnabled) 1674 { 1675 json_response["StaticNameServers"] = nlohmann::json::array(); 1676 } 1677 else 1678 { 1679 json_response["StaticNameServers"] = ethData.nameservers; 1680 } 1681 1682 nlohmann::json &ipv4_array = json_response["IPv4Addresses"]; 1683 nlohmann::json &ipv4_static_array = 1684 json_response["IPv4StaticAddresses"]; 1685 ipv4_array = nlohmann::json::array(); 1686 ipv4_static_array = nlohmann::json::array(); 1687 for (auto &ipv4_config : ipv4Data) 1688 { 1689 1690 std::string gatewayStr = ipv4_config.gateway; 1691 if (gatewayStr.empty()) 1692 { 1693 gatewayStr = "0.0.0.0"; 1694 } 1695 1696 ipv4_array.push_back({{"AddressOrigin", ipv4_config.origin}, 1697 {"SubnetMask", ipv4_config.netmask}, 1698 {"Address", ipv4_config.address}, 1699 {"Gateway", gatewayStr}}); 1700 if (ipv4_config.origin == "Static") 1701 { 1702 ipv4_static_array.push_back( 1703 {{"AddressOrigin", ipv4_config.origin}, 1704 {"SubnetMask", ipv4_config.netmask}, 1705 {"Address", ipv4_config.address}, 1706 {"Gateway", gatewayStr}}); 1707 } 1708 } 1709 1710 json_response["IPv6DefaultGateway"] = ethData.ipv6_default_gateway; 1711 1712 nlohmann::json &ipv6_array = json_response["IPv6Addresses"]; 1713 nlohmann::json &ipv6_static_array = 1714 json_response["IPv6StaticAddresses"]; 1715 ipv6_array = nlohmann::json::array(); 1716 ipv6_static_array = nlohmann::json::array(); 1717 for (auto &ipv6_config : ipv6Data) 1718 { 1719 ipv6_array.push_back({{"Address", ipv6_config.address}, 1720 {"PrefixLength", ipv6_config.prefixLength}, 1721 {"AddressOrigin", ipv6_config.origin}}); 1722 if (ipv6_config.origin == "Static") 1723 { 1724 ipv6_static_array.push_back( 1725 {{"Address", ipv6_config.address}, 1726 {"PrefixLength", ipv6_config.prefixLength}, 1727 {"AddressOrigin", ipv6_config.origin}}); 1728 } 1729 } 1730 } 1731 1732 /** 1733 * Functions triggers appropriate requests on DBus 1734 */ 1735 void doGet(crow::Response &res, const crow::Request &req, 1736 const std::vector<std::string> ¶ms) override 1737 { 1738 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 1739 if (params.size() != 1) 1740 { 1741 messages::internalError(asyncResp->res); 1742 return; 1743 } 1744 1745 getEthernetIfaceData( 1746 params[0], 1747 [this, asyncResp, iface_id{std::string(params[0])}]( 1748 const bool &success, const EthernetInterfaceData ðData, 1749 const boost::container::flat_set<IPv4AddressData> &ipv4Data, 1750 const boost::container::flat_set<IPv6AddressData> &ipv6Data) { 1751 if (!success) 1752 { 1753 // TODO(Pawel)consider distinguish between non existing 1754 // object, and other errors 1755 messages::resourceNotFound(asyncResp->res, 1756 "EthernetInterface", iface_id); 1757 return; 1758 } 1759 1760 asyncResp->res.jsonValue["@odata.type"] = 1761 "#EthernetInterface.v1_4_1.EthernetInterface"; 1762 asyncResp->res.jsonValue["@odata.context"] = 1763 "/redfish/v1/" 1764 "$metadata#EthernetInterface.EthernetInterface"; 1765 asyncResp->res.jsonValue["Name"] = "Manager Ethernet Interface"; 1766 asyncResp->res.jsonValue["Description"] = 1767 "Management Network Interface"; 1768 1769 parseInterfaceData(asyncResp->res.jsonValue, iface_id, ethData, 1770 ipv4Data, ipv6Data); 1771 }); 1772 } 1773 1774 void doPatch(crow::Response &res, const crow::Request &req, 1775 const std::vector<std::string> ¶ms) override 1776 { 1777 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 1778 if (params.size() != 1) 1779 { 1780 messages::internalError(asyncResp->res); 1781 return; 1782 } 1783 1784 const std::string &iface_id = params[0]; 1785 1786 std::optional<std::string> hostname; 1787 std::optional<std::string> macAddress; 1788 std::optional<std::string> ipv6DefaultGateway; 1789 std::optional<nlohmann::json> ipv4StaticAddresses; 1790 std::optional<nlohmann::json> ipv6StaticAddresses; 1791 std::optional<std::vector<std::string>> staticNameServers; 1792 std::optional<nlohmann::json> dhcpv4; 1793 std::optional<nlohmann::json> dhcpv6; 1794 DHCPParameters v4dhcpParms; 1795 DHCPParameters v6dhcpParms; 1796 1797 if (!json_util::readJson( 1798 req, res, "HostName", hostname, "IPv4StaticAddresses", 1799 ipv4StaticAddresses, "MACAddress", macAddress, 1800 "StaticNameServers", staticNameServers, "IPv6DefaultGateway", 1801 ipv6DefaultGateway, "IPv6StaticAddresses", ipv6StaticAddresses, 1802 "DHCPv4", dhcpv4, "DHCPv6", dhcpv6)) 1803 { 1804 return; 1805 } 1806 if (dhcpv4) 1807 { 1808 if (!json_util::readJson(*dhcpv4, res, "DHCPEnabled", 1809 v4dhcpParms.dhcpv4Enabled, "UseDNSServers", 1810 v4dhcpParms.useDNSServers, "UseNTPServers", 1811 v4dhcpParms.useNTPServers, "UseDomainName", 1812 v4dhcpParms.useUseDomainName)) 1813 { 1814 return; 1815 } 1816 } 1817 1818 if (dhcpv6) 1819 { 1820 if (!json_util::readJson(*dhcpv6, res, "OperatingMode", 1821 v6dhcpParms.dhcpv6OperatingMode, 1822 "UseDNSServers", v6dhcpParms.useDNSServers, 1823 "UseNTPServers", v6dhcpParms.useNTPServers, 1824 "UseDomainName", 1825 v6dhcpParms.useUseDomainName)) 1826 { 1827 return; 1828 } 1829 } 1830 1831 // Get single eth interface data, and call the below callback for 1832 // JSON preparation 1833 getEthernetIfaceData( 1834 iface_id, 1835 [this, asyncResp, iface_id, hostname = std::move(hostname), 1836 macAddress = std::move(macAddress), 1837 ipv4StaticAddresses = std::move(ipv4StaticAddresses), 1838 ipv6DefaultGateway = std::move(ipv6DefaultGateway), 1839 ipv6StaticAddresses = std::move(ipv6StaticAddresses), 1840 staticNameServers = std::move(staticNameServers), 1841 dhcpv4 = std::move(dhcpv4), dhcpv6 = std::move(dhcpv6), 1842 v4dhcpParms = std::move(v4dhcpParms), 1843 v6dhcpParms = std::move(v6dhcpParms)]( 1844 const bool &success, const EthernetInterfaceData ðData, 1845 const boost::container::flat_set<IPv4AddressData> &ipv4Data, 1846 const boost::container::flat_set<IPv6AddressData> &ipv6Data) { 1847 if (!success) 1848 { 1849 // ... otherwise return error 1850 // TODO(Pawel)consider distinguish between non existing 1851 // object, and other errors 1852 messages::resourceNotFound(asyncResp->res, 1853 "Ethernet Interface", iface_id); 1854 return; 1855 } 1856 1857 if (dhcpv4 || dhcpv6) 1858 { 1859 handleDHCPPatch(iface_id, ethData, std::move(v4dhcpParms), 1860 std::move(v6dhcpParms), asyncResp); 1861 } 1862 1863 if (hostname) 1864 { 1865 handleHostnamePatch(*hostname, asyncResp); 1866 } 1867 1868 if (macAddress) 1869 { 1870 handleMACAddressPatch(iface_id, *macAddress, asyncResp); 1871 } 1872 1873 if (ipv4StaticAddresses) 1874 { 1875 // TODO(ed) for some reason the capture of ipv4Addresses 1876 // above is returning a const value, not a non-const 1877 // value. This doesn't really work for us, as we need to 1878 // be able to efficiently move out the intermedia 1879 // nlohmann::json objects. This makes a copy of the 1880 // structure, and operates on that, but could be done 1881 // more efficiently 1882 nlohmann::json ipv4Static = std::move(*ipv4StaticAddresses); 1883 handleIPv4StaticPatch(iface_id, ipv4Static, ipv4Data, 1884 asyncResp); 1885 } 1886 1887 if (staticNameServers) 1888 { 1889 handleStaticNameServersPatch(iface_id, *staticNameServers, 1890 asyncResp); 1891 } 1892 1893 if (ipv6DefaultGateway) 1894 { 1895 messages::propertyNotWritable(asyncResp->res, 1896 "IPv6DefaultGateway"); 1897 } 1898 1899 if (ipv6StaticAddresses) 1900 { 1901 nlohmann::json ipv6Static = std::move(*ipv6StaticAddresses); 1902 handleIPv6StaticAddressesPatch(iface_id, ipv6Static, 1903 ipv6Data, asyncResp); 1904 } 1905 }); 1906 } 1907 }; 1908 1909 /** 1910 * VlanNetworkInterface derived class for delivering VLANNetworkInterface 1911 * Schema 1912 */ 1913 class VlanNetworkInterface : public Node 1914 { 1915 public: 1916 /* 1917 * Default Constructor 1918 */ 1919 template <typename CrowApp> 1920 VlanNetworkInterface(CrowApp &app) : 1921 Node(app, 1922 "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/VLANs/<str>", 1923 std::string(), std::string()) 1924 { 1925 entityPrivileges = { 1926 {boost::beast::http::verb::get, {{"Login"}}}, 1927 {boost::beast::http::verb::head, {{"Login"}}}, 1928 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 1929 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 1930 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 1931 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 1932 } 1933 1934 private: 1935 void parseInterfaceData( 1936 nlohmann::json &json_response, const std::string &parent_iface_id, 1937 const std::string &iface_id, const EthernetInterfaceData ðData, 1938 const boost::container::flat_set<IPv4AddressData> &ipv4Data, 1939 const boost::container::flat_set<IPv6AddressData> &ipv6Data) 1940 { 1941 // Fill out obvious data... 1942 json_response["Id"] = iface_id; 1943 json_response["@odata.id"] = 1944 "/redfish/v1/Managers/bmc/EthernetInterfaces/" + parent_iface_id + 1945 "/VLANs/" + iface_id; 1946 1947 json_response["VLANEnable"] = true; 1948 if (!ethData.vlan_id.empty()) 1949 { 1950 json_response["VLANId"] = ethData.vlan_id.back(); 1951 } 1952 } 1953 1954 bool verifyNames(const std::string &parent, const std::string &iface) 1955 { 1956 if (!boost::starts_with(iface, parent + "_")) 1957 { 1958 return false; 1959 } 1960 else 1961 { 1962 return true; 1963 } 1964 } 1965 1966 /** 1967 * Functions triggers appropriate requests on DBus 1968 */ 1969 void doGet(crow::Response &res, const crow::Request &req, 1970 const std::vector<std::string> ¶ms) override 1971 { 1972 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 1973 // TODO(Pawel) this shall be parameterized call (two params) to get 1974 // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'. 1975 // Check if there is required param, truly entering this shall be 1976 // impossible. 1977 if (params.size() != 2) 1978 { 1979 messages::internalError(res); 1980 res.end(); 1981 return; 1982 } 1983 1984 const std::string &parent_iface_id = params[0]; 1985 const std::string &iface_id = params[1]; 1986 res.jsonValue["@odata.type"] = 1987 "#VLanNetworkInterface.v1_1_0.VLanNetworkInterface"; 1988 res.jsonValue["@odata.context"] = 1989 "/redfish/v1/" 1990 "$metadata#VLanNetworkInterface.VLanNetworkInterface"; 1991 res.jsonValue["Name"] = "VLAN Network Interface"; 1992 1993 if (!verifyNames(parent_iface_id, iface_id)) 1994 { 1995 return; 1996 } 1997 1998 // Get single eth interface data, and call the below callback for 1999 // JSON preparation 2000 getEthernetIfaceData( 2001 params[1], 2002 [this, asyncResp, parent_iface_id{std::string(params[0])}, 2003 iface_id{std::string(params[1])}]( 2004 const bool &success, const EthernetInterfaceData ðData, 2005 const boost::container::flat_set<IPv4AddressData> &ipv4Data, 2006 const boost::container::flat_set<IPv6AddressData> &ipv6Data) { 2007 if (success && ethData.vlan_id.size() != 0) 2008 { 2009 parseInterfaceData(asyncResp->res.jsonValue, 2010 parent_iface_id, iface_id, ethData, 2011 ipv4Data, ipv6Data); 2012 } 2013 else 2014 { 2015 // ... otherwise return error 2016 // TODO(Pawel)consider distinguish between non existing 2017 // object, and other errors 2018 messages::resourceNotFound( 2019 asyncResp->res, "VLAN Network Interface", iface_id); 2020 } 2021 }); 2022 } 2023 2024 void doPatch(crow::Response &res, const crow::Request &req, 2025 const std::vector<std::string> ¶ms) override 2026 { 2027 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 2028 if (params.size() != 2) 2029 { 2030 messages::internalError(asyncResp->res); 2031 return; 2032 } 2033 2034 const std::string &parentIfaceId = params[0]; 2035 const std::string &ifaceId = params[1]; 2036 2037 if (!verifyNames(parentIfaceId, ifaceId)) 2038 { 2039 messages::resourceNotFound(asyncResp->res, "VLAN Network Interface", 2040 ifaceId); 2041 return; 2042 } 2043 2044 bool vlanEnable = false; 2045 uint64_t vlanId = 0; 2046 2047 if (!json_util::readJson(req, res, "VLANEnable", vlanEnable, "VLANId", 2048 vlanId)) 2049 { 2050 return; 2051 } 2052 2053 // Get single eth interface data, and call the below callback for 2054 // JSON preparation 2055 getEthernetIfaceData( 2056 params[1], 2057 [asyncResp, parentIfaceId{std::string(params[0])}, 2058 ifaceId{std::string(params[1])}, &vlanEnable, &vlanId]( 2059 const bool &success, const EthernetInterfaceData ðData, 2060 const boost::container::flat_set<IPv4AddressData> &ipv4Data, 2061 const boost::container::flat_set<IPv6AddressData> &ipv6Data) { 2062 if (success && !ethData.vlan_id.empty()) 2063 { 2064 auto callback = 2065 [asyncResp](const boost::system::error_code ec) { 2066 if (ec) 2067 { 2068 messages::internalError(asyncResp->res); 2069 } 2070 }; 2071 2072 if (vlanEnable == true) 2073 { 2074 crow::connections::systemBus->async_method_call( 2075 std::move(callback), "xyz.openbmc_project.Network", 2076 "/xyz/openbmc_project/network/" + ifaceId, 2077 "org.freedesktop.DBus.Properties", "Set", 2078 "xyz.openbmc_project.Network.VLAN", "Id", 2079 std::variant<uint32_t>(vlanId)); 2080 } 2081 else 2082 { 2083 BMCWEB_LOG_DEBUG << "vlanEnable is false. Deleting the " 2084 "vlan interface"; 2085 crow::connections::systemBus->async_method_call( 2086 std::move(callback), "xyz.openbmc_project.Network", 2087 std::string("/xyz/openbmc_project/network/") + 2088 ifaceId, 2089 "xyz.openbmc_project.Object.Delete", "Delete"); 2090 } 2091 } 2092 else 2093 { 2094 // TODO(Pawel)consider distinguish between non existing 2095 // object, and other errors 2096 messages::resourceNotFound( 2097 asyncResp->res, "VLAN Network Interface", ifaceId); 2098 return; 2099 } 2100 }); 2101 } 2102 2103 void doDelete(crow::Response &res, const crow::Request &req, 2104 const std::vector<std::string> ¶ms) override 2105 { 2106 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 2107 if (params.size() != 2) 2108 { 2109 messages::internalError(asyncResp->res); 2110 return; 2111 } 2112 2113 const std::string &parentIfaceId = params[0]; 2114 const std::string &ifaceId = params[1]; 2115 2116 if (!verifyNames(parentIfaceId, ifaceId)) 2117 { 2118 messages::resourceNotFound(asyncResp->res, "VLAN Network Interface", 2119 ifaceId); 2120 return; 2121 } 2122 2123 // Get single eth interface data, and call the below callback for 2124 // JSON preparation 2125 getEthernetIfaceData( 2126 params[1], 2127 [asyncResp, parentIfaceId{std::string(params[0])}, 2128 ifaceId{std::string(params[1])}]( 2129 const bool &success, const EthernetInterfaceData ðData, 2130 const boost::container::flat_set<IPv4AddressData> &ipv4Data, 2131 const boost::container::flat_set<IPv6AddressData> &ipv6Data) { 2132 if (success && !ethData.vlan_id.empty()) 2133 { 2134 auto callback = 2135 [asyncResp](const boost::system::error_code ec) { 2136 if (ec) 2137 { 2138 messages::internalError(asyncResp->res); 2139 } 2140 }; 2141 crow::connections::systemBus->async_method_call( 2142 std::move(callback), "xyz.openbmc_project.Network", 2143 std::string("/xyz/openbmc_project/network/") + ifaceId, 2144 "xyz.openbmc_project.Object.Delete", "Delete"); 2145 } 2146 else 2147 { 2148 // ... otherwise return error 2149 // TODO(Pawel)consider distinguish between non existing 2150 // object, and other errors 2151 messages::resourceNotFound( 2152 asyncResp->res, "VLAN Network Interface", ifaceId); 2153 } 2154 }); 2155 } 2156 }; 2157 2158 /** 2159 * VlanNetworkInterfaceCollection derived class for delivering 2160 * VLANNetworkInterface Collection Schema 2161 */ 2162 class VlanNetworkInterfaceCollection : public Node 2163 { 2164 public: 2165 template <typename CrowApp> 2166 VlanNetworkInterfaceCollection(CrowApp &app) : 2167 Node(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/VLANs/", 2168 std::string()) 2169 { 2170 entityPrivileges = { 2171 {boost::beast::http::verb::get, {{"Login"}}}, 2172 {boost::beast::http::verb::head, {{"Login"}}}, 2173 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 2174 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 2175 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 2176 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 2177 } 2178 2179 private: 2180 /** 2181 * Functions triggers appropriate requests on DBus 2182 */ 2183 void doGet(crow::Response &res, const crow::Request &req, 2184 const std::vector<std::string> ¶ms) override 2185 { 2186 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 2187 if (params.size() != 1) 2188 { 2189 // This means there is a problem with the router 2190 messages::internalError(asyncResp->res); 2191 return; 2192 } 2193 2194 const std::string &rootInterfaceName = params[0]; 2195 2196 // Get eth interface list, and call the below callback for JSON 2197 // preparation 2198 getEthernetIfaceList( 2199 [asyncResp, rootInterfaceName{std::string(rootInterfaceName)}]( 2200 const bool &success, 2201 const boost::container::flat_set<std::string> &iface_list) { 2202 if (!success) 2203 { 2204 messages::internalError(asyncResp->res); 2205 return; 2206 } 2207 2208 if (iface_list.find(rootInterfaceName) == iface_list.end()) 2209 { 2210 messages::resourceNotFound(asyncResp->res, 2211 "VLanNetworkInterfaceCollection", 2212 rootInterfaceName); 2213 return; 2214 } 2215 2216 asyncResp->res.jsonValue["@odata.type"] = 2217 "#VLanNetworkInterfaceCollection." 2218 "VLanNetworkInterfaceCollection"; 2219 asyncResp->res.jsonValue["@odata.context"] = 2220 "/redfish/v1/$metadata" 2221 "#VLanNetworkInterfaceCollection." 2222 "VLanNetworkInterfaceCollection"; 2223 asyncResp->res.jsonValue["Name"] = 2224 "VLAN Network Interface Collection"; 2225 2226 nlohmann::json iface_array = nlohmann::json::array(); 2227 2228 for (const std::string &iface_item : iface_list) 2229 { 2230 if (boost::starts_with(iface_item, rootInterfaceName + "_")) 2231 { 2232 iface_array.push_back( 2233 {{"@odata.id", 2234 "/redfish/v1/Managers/bmc/EthernetInterfaces/" + 2235 rootInterfaceName + "/VLANs/" + iface_item}}); 2236 } 2237 } 2238 2239 asyncResp->res.jsonValue["Members@odata.count"] = 2240 iface_array.size(); 2241 asyncResp->res.jsonValue["Members"] = std::move(iface_array); 2242 asyncResp->res.jsonValue["@odata.id"] = 2243 "/redfish/v1/Managers/bmc/EthernetInterfaces/" + 2244 rootInterfaceName + "/VLANs"; 2245 }); 2246 } 2247 2248 void doPost(crow::Response &res, const crow::Request &req, 2249 const std::vector<std::string> ¶ms) override 2250 { 2251 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 2252 if (params.size() != 1) 2253 { 2254 messages::internalError(asyncResp->res); 2255 return; 2256 } 2257 bool vlanEnable = false; 2258 uint32_t vlanId = 0; 2259 if (!json_util::readJson(req, res, "VLANId", vlanId, "VLANEnable", 2260 vlanEnable)) 2261 { 2262 return; 2263 } 2264 // Need both vlanId and vlanEnable to service this request 2265 if (!vlanId) 2266 { 2267 messages::propertyMissing(asyncResp->res, "VLANId"); 2268 } 2269 if (!vlanEnable) 2270 { 2271 messages::propertyMissing(asyncResp->res, "VLANEnable"); 2272 } 2273 if (static_cast<bool>(vlanId) ^ vlanEnable) 2274 { 2275 return; 2276 } 2277 2278 const std::string &rootInterfaceName = params[0]; 2279 auto callback = [asyncResp](const boost::system::error_code ec) { 2280 if (ec) 2281 { 2282 // TODO(ed) make more consistent error messages based on 2283 // phosphor-network responses 2284 messages::internalError(asyncResp->res); 2285 return; 2286 } 2287 messages::created(asyncResp->res); 2288 }; 2289 crow::connections::systemBus->async_method_call( 2290 std::move(callback), "xyz.openbmc_project.Network", 2291 "/xyz/openbmc_project/network", 2292 "xyz.openbmc_project.Network.VLAN.Create", "VLAN", 2293 rootInterfaceName, vlanId); 2294 } 2295 }; 2296 } // namespace redfish 2297