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 basic single Ethernet Interface information 75 * available from DBus 76 */ 77 struct EthernetInterfaceData 78 { 79 uint32_t speed; 80 bool auto_neg; 81 bool DHCPEnabled; 82 std::string hostname; 83 std::string default_gateway; 84 std::string ipv6_default_gateway; 85 std::string mac_address; 86 std::vector<std::uint32_t> vlan_id; 87 std::vector<std::string> nameservers; 88 }; 89 90 // Helper function that changes bits netmask notation (i.e. /24) 91 // into full dot notation 92 inline std::string getNetmask(unsigned int bits) 93 { 94 uint32_t value = 0xffffffff << (32 - bits); 95 std::string netmask = std::to_string((value >> 24) & 0xff) + "." + 96 std::to_string((value >> 16) & 0xff) + "." + 97 std::to_string((value >> 8) & 0xff) + "." + 98 std::to_string(value & 0xff); 99 return netmask; 100 } 101 102 inline std::string 103 translateAddressOriginDbusToRedfish(const std::string &inputOrigin, 104 bool isIPv4) 105 { 106 if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.Static") 107 { 108 return "Static"; 109 } 110 if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal") 111 { 112 if (isIPv4) 113 { 114 return "IPv4LinkLocal"; 115 } 116 else 117 { 118 return "LinkLocal"; 119 } 120 } 121 if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP") 122 { 123 if (isIPv4) 124 { 125 return "DHCP"; 126 } 127 else 128 { 129 return "DHCPv6"; 130 } 131 } 132 if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.SLAAC") 133 { 134 return "SLAAC"; 135 } 136 return ""; 137 } 138 139 inline std::string 140 translateAddressOriginRedfishToDbus(const std::string &inputOrigin) 141 { 142 if (inputOrigin == "Static") 143 { 144 return "xyz.openbmc_project.Network.IP.AddressOrigin.Static"; 145 } 146 if (inputOrigin == "DHCP" || inputOrigin == "DHCPv6") 147 { 148 return "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP"; 149 } 150 if (inputOrigin == "IPv4LinkLocal" || inputOrigin == "LinkLocal") 151 { 152 return "xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal"; 153 } 154 if (inputOrigin == "SLAAC") 155 { 156 return "xyz.openbmc_project.Network.IP.AddressOrigin.SLAAC"; 157 } 158 return ""; 159 } 160 161 inline void extractEthernetInterfaceData(const std::string ðiface_id, 162 const GetManagedObjects &dbus_data, 163 EthernetInterfaceData ðData) 164 { 165 for (const auto &objpath : dbus_data) 166 { 167 for (const auto &ifacePair : objpath.second) 168 { 169 if (objpath.first == "/xyz/openbmc_project/network/" + ethiface_id) 170 { 171 if (ifacePair.first == "xyz.openbmc_project.Network.MACAddress") 172 { 173 for (const auto &propertyPair : ifacePair.second) 174 { 175 if (propertyPair.first == "MACAddress") 176 { 177 const std::string *mac = 178 std::get_if<std::string>(&propertyPair.second); 179 if (mac != nullptr) 180 { 181 ethData.mac_address = *mac; 182 } 183 } 184 } 185 } 186 else if (ifacePair.first == "xyz.openbmc_project.Network.VLAN") 187 { 188 for (const auto &propertyPair : ifacePair.second) 189 { 190 if (propertyPair.first == "Id") 191 { 192 const uint32_t *id = 193 std::get_if<uint32_t>(&propertyPair.second); 194 if (id != nullptr) 195 { 196 ethData.vlan_id.push_back(*id); 197 } 198 } 199 } 200 } 201 else if (ifacePair.first == 202 "xyz.openbmc_project.Network.EthernetInterface") 203 { 204 for (const auto &propertyPair : ifacePair.second) 205 { 206 if (propertyPair.first == "AutoNeg") 207 { 208 const bool *auto_neg = 209 std::get_if<bool>(&propertyPair.second); 210 if (auto_neg != nullptr) 211 { 212 ethData.auto_neg = *auto_neg; 213 } 214 } 215 else if (propertyPair.first == "Speed") 216 { 217 const uint32_t *speed = 218 std::get_if<uint32_t>(&propertyPair.second); 219 if (speed != nullptr) 220 { 221 ethData.speed = *speed; 222 } 223 } 224 else if (propertyPair.first == "Nameservers") 225 { 226 const std::vector<std::string> *nameservers = 227 sdbusplus::message::variant_ns::get_if< 228 std::vector<std::string>>( 229 &propertyPair.second); 230 if (nameservers != nullptr) 231 { 232 ethData.nameservers = std::move(*nameservers); 233 } 234 } 235 else if (propertyPair.first == "DHCPEnabled") 236 { 237 const bool *DHCPEnabled = 238 std::get_if<bool>(&propertyPair.second); 239 if (DHCPEnabled != nullptr) 240 { 241 ethData.DHCPEnabled = *DHCPEnabled; 242 } 243 } 244 } 245 } 246 } 247 // System configuration shows up in the global namespace, so no need 248 // to check eth number 249 if (ifacePair.first == 250 "xyz.openbmc_project.Network.SystemConfiguration") 251 { 252 for (const auto &propertyPair : ifacePair.second) 253 { 254 if (propertyPair.first == "HostName") 255 { 256 const std::string *hostname = 257 sdbusplus::message::variant_ns::get_if<std::string>( 258 &propertyPair.second); 259 if (hostname != nullptr) 260 { 261 ethData.hostname = *hostname; 262 } 263 } 264 else if (propertyPair.first == "DefaultGateway") 265 { 266 const std::string *defaultGateway = 267 sdbusplus::message::variant_ns::get_if<std::string>( 268 &propertyPair.second); 269 if (defaultGateway != nullptr) 270 { 271 ethData.default_gateway = *defaultGateway; 272 } 273 } 274 else if (propertyPair.first == "DefaultGateway6") 275 { 276 const std::string *defaultGateway6 = 277 sdbusplus::message::variant_ns::get_if<std::string>( 278 &propertyPair.second); 279 if (defaultGateway6 != nullptr) 280 { 281 ethData.ipv6_default_gateway = *defaultGateway6; 282 } 283 } 284 } 285 } 286 } 287 } 288 } 289 290 // Helper function that extracts data for single ethernet ipv4 address 291 inline void 292 extractIPData(const std::string ðiface_id, 293 const GetManagedObjects &dbus_data, 294 boost::container::flat_set<IPv4AddressData> &ipv4_config) 295 { 296 const std::string ipv4PathStart = 297 "/xyz/openbmc_project/network/" + ethiface_id + "/ipv4/"; 298 299 // Since there might be several IPv4 configurations aligned with 300 // single ethernet interface, loop over all of them 301 for (const auto &objpath : dbus_data) 302 { 303 // Check if proper pattern for object path appears 304 if (boost::starts_with(objpath.first.str, ipv4PathStart)) 305 { 306 for (auto &interface : objpath.second) 307 { 308 if (interface.first == "xyz.openbmc_project.Network.IP") 309 { 310 // Instance IPv4AddressData structure, and set as 311 // appropriate 312 std::pair< 313 boost::container::flat_set<IPv4AddressData>::iterator, 314 bool> 315 it = ipv4_config.insert( 316 {objpath.first.str.substr(ipv4PathStart.size())}); 317 IPv4AddressData &ipv4_address = *it.first; 318 for (auto &property : interface.second) 319 { 320 if (property.first == "Address") 321 { 322 const std::string *address = 323 std::get_if<std::string>(&property.second); 324 if (address != nullptr) 325 { 326 ipv4_address.address = *address; 327 } 328 } 329 else if (property.first == "Gateway") 330 { 331 const std::string *gateway = 332 std::get_if<std::string>(&property.second); 333 if (gateway != nullptr) 334 { 335 ipv4_address.gateway = *gateway; 336 } 337 } 338 else if (property.first == "Origin") 339 { 340 const std::string *origin = 341 std::get_if<std::string>(&property.second); 342 if (origin != nullptr) 343 { 344 ipv4_address.origin = 345 translateAddressOriginDbusToRedfish(*origin, 346 true); 347 } 348 } 349 else if (property.first == "PrefixLength") 350 { 351 const uint8_t *mask = 352 std::get_if<uint8_t>(&property.second); 353 if (mask != nullptr) 354 { 355 // convert it to the string 356 ipv4_address.netmask = getNetmask(*mask); 357 } 358 } 359 else 360 { 361 BMCWEB_LOG_ERROR 362 << "Got extra property: " << property.first 363 << " on the " << objpath.first.str << " object"; 364 } 365 } 366 // Check if given address is local, or global 367 ipv4_address.linktype = 368 boost::starts_with(ipv4_address.address, "169.254.") 369 ? LinkType::Global 370 : LinkType::Local; 371 } 372 } 373 } 374 } 375 } 376 377 /** 378 * @brief Sets given Id on the given VLAN interface through D-Bus 379 * 380 * @param[in] ifaceId Id of VLAN interface that should be modified 381 * @param[in] inputVlanId New ID of the VLAN 382 * @param[in] callback Function that will be called after the operation 383 * 384 * @return None. 385 */ 386 template <typename CallbackFunc> 387 void changeVlanId(const std::string &ifaceId, const uint32_t &inputVlanId, 388 CallbackFunc &&callback) 389 { 390 crow::connections::systemBus->async_method_call( 391 callback, "xyz.openbmc_project.Network", 392 std::string("/xyz/openbmc_project/network/") + ifaceId, 393 "org.freedesktop.DBus.Properties", "Set", 394 "xyz.openbmc_project.Network.VLAN", "Id", 395 std::variant<uint32_t>(inputVlanId)); 396 } 397 398 /** 399 * @brief Helper function that verifies IP address to check if it is in 400 * proper format. If bits pointer is provided, also calculates active 401 * bit count for Subnet Mask. 402 * 403 * @param[in] ip IP that will be verified 404 * @param[out] bits Calculated mask in bits notation 405 * 406 * @return true in case of success, false otherwise 407 */ 408 inline bool ipv4VerifyIpAndGetBitcount(const std::string &ip, 409 uint8_t *bits = nullptr) 410 { 411 std::vector<std::string> bytesInMask; 412 413 boost::split(bytesInMask, ip, boost::is_any_of(".")); 414 415 static const constexpr int ipV4AddressSectionsCount = 4; 416 if (bytesInMask.size() != ipV4AddressSectionsCount) 417 { 418 return false; 419 } 420 421 if (bits != nullptr) 422 { 423 *bits = 0; 424 } 425 426 char *endPtr; 427 long previousValue = 255; 428 bool firstZeroInByteHit; 429 for (const std::string &byte : bytesInMask) 430 { 431 if (byte.empty()) 432 { 433 return false; 434 } 435 436 // Use strtol instead of stroi to avoid exceptions 437 long value = std::strtol(byte.c_str(), &endPtr, 10); 438 439 // endPtr should point to the end of the string, otherwise given string 440 // is not 100% number 441 if (*endPtr != '\0') 442 { 443 return false; 444 } 445 446 // Value should be contained in byte 447 if (value < 0 || value > 255) 448 { 449 return false; 450 } 451 452 if (bits != nullptr) 453 { 454 // Mask has to be continuous between bytes 455 if (previousValue != 255 && value != 0) 456 { 457 return false; 458 } 459 460 // Mask has to be continuous inside bytes 461 firstZeroInByteHit = false; 462 463 // Count bits 464 for (int bitIdx = 7; bitIdx >= 0; bitIdx--) 465 { 466 if (value & (1 << bitIdx)) 467 { 468 if (firstZeroInByteHit) 469 { 470 // Continuity not preserved 471 return false; 472 } 473 else 474 { 475 (*bits)++; 476 } 477 } 478 else 479 { 480 firstZeroInByteHit = true; 481 } 482 } 483 } 484 485 previousValue = value; 486 } 487 488 return true; 489 } 490 491 /** 492 * @brief Changes IPv4 address type property (Address, Gateway) 493 * 494 * @param[in] ifaceId Id of interface whose IP should be modified 495 * @param[in] ipIdx Index of IP in input array that should be modified 496 * @param[in] ipHash DBus Hash id of modified IP 497 * @param[in] name Name of field in JSON representation 498 * @param[in] newValue New value that should be written 499 * @param[io] asyncResp Response object that will be returned to client 500 * 501 * @return true if give IP is valid and has been sent do D-Bus, false 502 * otherwise 503 */ 504 inline void changeIPv4AddressProperty( 505 const std::string &ifaceId, int ipIdx, const std::string &ipHash, 506 const std::string &name, const std::string &newValue, 507 const std::shared_ptr<AsyncResp> asyncResp) 508 { 509 auto callback = [asyncResp, ipIdx, name{std::string(name)}, 510 newValue{std::move(newValue)}]( 511 const boost::system::error_code ec) { 512 if (ec) 513 { 514 messages::internalError(asyncResp->res); 515 } 516 else 517 { 518 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx][name] = newValue; 519 } 520 }; 521 522 crow::connections::systemBus->async_method_call( 523 std::move(callback), "xyz.openbmc_project.Network", 524 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash, 525 "org.freedesktop.DBus.Properties", "Set", 526 "xyz.openbmc_project.Network.IP", name, 527 std::variant<std::string>(newValue)); 528 } 529 530 /** 531 * @brief Changes IPv4 address origin property 532 * 533 * @param[in] ifaceId Id of interface whose IP should be modified 534 * @param[in] ipIdx Index of IP in input array that should be 535 * modified 536 * @param[in] ipHash DBus Hash id of modified IP 537 * @param[in] newValue New value in Redfish format 538 * @param[in] newValueDbus New value in D-Bus format 539 * @param[io] asyncResp Response object that will be returned to client 540 * 541 * @return true if give IP is valid and has been sent do D-Bus, false 542 * otherwise 543 */ 544 inline void changeIPv4Origin(const std::string &ifaceId, int ipIdx, 545 const std::string &ipHash, 546 const std::string &newValue, 547 const std::string &newValueDbus, 548 const std::shared_ptr<AsyncResp> asyncResp) 549 { 550 auto callback = [asyncResp, ipIdx, newValue{std::move(newValue)}]( 551 const boost::system::error_code ec) { 552 if (ec) 553 { 554 messages::internalError(asyncResp->res); 555 } 556 else 557 { 558 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx]["AddressOrigin"] = 559 newValue; 560 } 561 }; 562 563 crow::connections::systemBus->async_method_call( 564 std::move(callback), "xyz.openbmc_project.Network", 565 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash, 566 "org.freedesktop.DBus.Properties", "Set", 567 "xyz.openbmc_project.Network.IP", "Origin", 568 std::variant<std::string>(newValueDbus)); 569 } 570 571 /** 572 * @brief Modifies SubnetMask for given IP 573 * 574 * @param[in] ifaceId Id of interface whose IP should be modified 575 * @param[in] ipIdx Index of IP in input array that should be 576 * modified 577 * @param[in] ipHash DBus Hash id of modified IP 578 * @param[in] newValueStr Mask in dot notation as string 579 * @param[in] newValue Mask as PrefixLength in bitcount 580 * @param[io] asyncResp Response object that will be returned to client 581 * 582 * @return None 583 */ 584 inline void changeIPv4SubnetMaskProperty(const std::string &ifaceId, int ipIdx, 585 const std::string &ipHash, 586 const std::string &newValueStr, 587 uint8_t &newValue, 588 std::shared_ptr<AsyncResp> asyncResp) 589 { 590 auto callback = [asyncResp, ipIdx, newValueStr{std::move(newValueStr)}]( 591 const boost::system::error_code ec) { 592 if (ec) 593 { 594 messages::internalError(asyncResp->res); 595 } 596 else 597 { 598 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx]["SubnetMask"] = 599 newValueStr; 600 } 601 }; 602 603 crow::connections::systemBus->async_method_call( 604 std::move(callback), "xyz.openbmc_project.Network", 605 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash, 606 "org.freedesktop.DBus.Properties", "Set", 607 "xyz.openbmc_project.Network.IP", "PrefixLength", 608 std::variant<uint8_t>(newValue)); 609 } 610 611 /** 612 * @brief Deletes given IPv4 613 * 614 * @param[in] ifaceId Id of interface whose IP should be deleted 615 * @param[in] ipIdx Index of IP in input array that should be deleted 616 * @param[in] ipHash DBus Hash id of IP that should be deleted 617 * @param[io] asyncResp Response object that will be returned to client 618 * 619 * @return None 620 */ 621 inline void deleteIPv4(const std::string &ifaceId, const std::string &ipHash, 622 unsigned int ipIdx, 623 const std::shared_ptr<AsyncResp> asyncResp) 624 { 625 crow::connections::systemBus->async_method_call( 626 [ipIdx, asyncResp](const boost::system::error_code ec) { 627 if (ec) 628 { 629 messages::internalError(asyncResp->res); 630 } 631 else 632 { 633 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx] = nullptr; 634 } 635 }, 636 "xyz.openbmc_project.Network", 637 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash, 638 "xyz.openbmc_project.Object.Delete", "Delete"); 639 } 640 641 /** 642 * @brief Creates IPv4 with given data 643 * 644 * @param[in] ifaceId Id of interface whose IP should be deleted 645 * @param[in] ipIdx Index of IP in input array that should be deleted 646 * @param[in] ipHash DBus Hash id of IP that should be deleted 647 * @param[io] asyncResp Response object that will be returned to client 648 * 649 * @return None 650 */ 651 inline void createIPv4(const std::string &ifaceId, unsigned int ipIdx, 652 uint8_t subnetMask, const std::string &gateway, 653 const std::string &address, 654 std::shared_ptr<AsyncResp> asyncResp) 655 { 656 auto createIpHandler = [asyncResp](const boost::system::error_code ec) { 657 if (ec) 658 { 659 messages::internalError(asyncResp->res); 660 } 661 }; 662 663 crow::connections::systemBus->async_method_call( 664 std::move(createIpHandler), "xyz.openbmc_project.Network", 665 "/xyz/openbmc_project/network/" + ifaceId, 666 "xyz.openbmc_project.Network.IP.Create", "IP", 667 "xyz.openbmc_project.Network.IP.Protocol.IPv4", address, subnetMask, 668 gateway); 669 } 670 using GetAllPropertiesType = 671 boost::container::flat_map<std::string, sdbusplus::message::variant<bool>>; 672 673 inline void getDHCPConfigData(const std::shared_ptr<AsyncResp> asyncResp) 674 { 675 auto getConfig = [asyncResp](const boost::system::error_code error_code, 676 const GetAllPropertiesType &dbus_data) { 677 if (error_code) 678 { 679 BMCWEB_LOG_ERROR << "D-Bus response error: " << error_code; 680 messages::internalError(asyncResp->res); 681 return; 682 } 683 nlohmann::json &DHCPConfigTypeJson = asyncResp->res.jsonValue["DHCPv4"]; 684 for (const auto &property : dbus_data) 685 { 686 auto value = 687 sdbusplus::message::variant_ns::get_if<bool>(&property.second); 688 689 if (value == nullptr) 690 { 691 continue; 692 } 693 if (property.first == "DNSEnabled") 694 { 695 DHCPConfigTypeJson["UseDNSServers"] = *value; 696 } 697 else if (property.first == "HostNameEnabled") 698 { 699 DHCPConfigTypeJson["UseDomainName"] = *value; 700 } 701 else if (property.first == "NTPEnabled") 702 { 703 DHCPConfigTypeJson["UseNTPServers"] = *value; 704 } 705 } 706 }; 707 crow::connections::systemBus->async_method_call( 708 std::move(getConfig), "xyz.openbmc_project.Network", 709 "/xyz/openbmc_project/network/config/dhcp", 710 "org.freedesktop.DBus.Properties", "GetAll", 711 "xyz.openbmc_project.Network.DHCPConfiguration"); 712 } 713 714 /** 715 * Function that retrieves all properties for given Ethernet Interface 716 * Object 717 * from EntityManager Network Manager 718 * @param ethiface_id a eth interface id to query on DBus 719 * @param callback a function that shall be called to convert Dbus output 720 * into JSON 721 */ 722 template <typename CallbackFunc> 723 void getEthernetIfaceData(const std::string ðiface_id, 724 CallbackFunc &&callback) 725 { 726 crow::connections::systemBus->async_method_call( 727 [ethiface_id{std::string{ethiface_id}}, callback{std::move(callback)}]( 728 const boost::system::error_code error_code, 729 const GetManagedObjects &resp) { 730 EthernetInterfaceData ethData{}; 731 boost::container::flat_set<IPv4AddressData> ipv4Data; 732 733 if (error_code) 734 { 735 callback(false, ethData, ipv4Data); 736 return; 737 } 738 739 extractEthernetInterfaceData(ethiface_id, resp, ethData); 740 extractIPData(ethiface_id, resp, ipv4Data); 741 742 // Fix global GW 743 for (IPv4AddressData &ipv4 : ipv4Data) 744 { 745 if ((ipv4.linktype == LinkType::Global) && 746 (ipv4.gateway == "0.0.0.0")) 747 { 748 ipv4.gateway = ethData.default_gateway; 749 } 750 } 751 752 // Finally make a callback with usefull data 753 callback(true, ethData, ipv4Data); 754 }, 755 "xyz.openbmc_project.Network", "/xyz/openbmc_project/network", 756 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 757 }; 758 759 /** 760 * Function that retrieves all Ethernet Interfaces available through Network 761 * Manager 762 * @param callback a function that shall be called to convert Dbus output 763 * into JSON. 764 */ 765 template <typename CallbackFunc> 766 void getEthernetIfaceList(CallbackFunc &&callback) 767 { 768 crow::connections::systemBus->async_method_call( 769 [callback{std::move(callback)}]( 770 const boost::system::error_code error_code, 771 GetManagedObjects &resp) { 772 // Callback requires vector<string> to retrieve all available 773 // ethernet interfaces 774 std::vector<std::string> iface_list; 775 iface_list.reserve(resp.size()); 776 if (error_code) 777 { 778 callback(false, iface_list); 779 return; 780 } 781 782 // Iterate over all retrieved ObjectPaths. 783 for (const auto &objpath : resp) 784 { 785 // And all interfaces available for certain ObjectPath. 786 for (const auto &interface : objpath.second) 787 { 788 // If interface is 789 // xyz.openbmc_project.Network.EthernetInterface, this is 790 // what we're looking for. 791 if (interface.first == 792 "xyz.openbmc_project.Network.EthernetInterface") 793 { 794 // Cut out everyting until last "/", ... 795 const std::string &iface_id = objpath.first.str; 796 std::size_t last_pos = iface_id.rfind("/"); 797 if (last_pos != std::string::npos) 798 { 799 // and put it into output vector. 800 iface_list.emplace_back( 801 iface_id.substr(last_pos + 1)); 802 } 803 } 804 } 805 } 806 // Finally make a callback with useful data 807 callback(true, iface_list); 808 }, 809 "xyz.openbmc_project.Network", "/xyz/openbmc_project/network", 810 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 811 }; 812 813 /** 814 * EthernetCollection derived class for delivering Ethernet Collection Schema 815 */ 816 class EthernetCollection : public Node 817 { 818 public: 819 template <typename CrowApp> 820 EthernetCollection(CrowApp &app) : 821 Node(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/") 822 { 823 entityPrivileges = { 824 {boost::beast::http::verb::get, {{"Login"}}}, 825 {boost::beast::http::verb::head, {{"Login"}}}, 826 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 827 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 828 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 829 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 830 } 831 832 private: 833 /** 834 * Functions triggers appropriate requests on DBus 835 */ 836 void doGet(crow::Response &res, const crow::Request &req, 837 const std::vector<std::string> ¶ms) override 838 { 839 res.jsonValue["@odata.type"] = 840 "#EthernetInterfaceCollection.EthernetInterfaceCollection"; 841 res.jsonValue["@odata.context"] = 842 "/redfish/v1/" 843 "$metadata#EthernetInterfaceCollection.EthernetInterfaceCollection"; 844 res.jsonValue["@odata.id"] = 845 "/redfish/v1/Managers/bmc/EthernetInterfaces"; 846 res.jsonValue["Name"] = "Ethernet Network Interface Collection"; 847 res.jsonValue["Description"] = 848 "Collection of EthernetInterfaces for this Manager"; 849 850 // Get eth interface list, and call the below callback for JSON 851 // preparation 852 getEthernetIfaceList( 853 [&res](const bool &success, 854 const std::vector<std::string> &iface_list) { 855 if (!success) 856 { 857 messages::internalError(res); 858 res.end(); 859 return; 860 } 861 862 nlohmann::json &iface_array = res.jsonValue["Members"]; 863 iface_array = nlohmann::json::array(); 864 std::string tag = "_"; 865 for (const std::string &iface_item : iface_list) 866 { 867 std::size_t found = iface_item.find(tag); 868 if (found == std::string::npos) 869 { 870 iface_array.push_back( 871 {{"@odata.id", 872 "/redfish/v1/Managers/bmc/EthernetInterfaces/" + 873 iface_item}}); 874 } 875 } 876 877 res.jsonValue["Members@odata.count"] = iface_array.size(); 878 res.jsonValue["@odata.id"] = 879 "/redfish/v1/Managers/bmc/EthernetInterfaces"; 880 res.end(); 881 }); 882 } 883 }; 884 885 /** 886 * EthernetInterface derived class for delivering Ethernet Schema 887 */ 888 class EthernetInterface : public Node 889 { 890 public: 891 /* 892 * Default Constructor 893 */ 894 template <typename CrowApp> 895 EthernetInterface(CrowApp &app) : 896 Node(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/", 897 std::string()) 898 { 899 entityPrivileges = { 900 {boost::beast::http::verb::get, {{"Login"}}}, 901 {boost::beast::http::verb::head, {{"Login"}}}, 902 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 903 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 904 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 905 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 906 } 907 908 private: 909 void handleHostnamePatch(const std::string &hostname, 910 const std::shared_ptr<AsyncResp> asyncResp) 911 { 912 asyncResp->res.jsonValue["HostName"] = hostname; 913 crow::connections::systemBus->async_method_call( 914 [asyncResp](const boost::system::error_code ec) { 915 if (ec) 916 { 917 messages::internalError(asyncResp->res); 918 } 919 }, 920 "xyz.openbmc_project.Network", 921 "/xyz/openbmc_project/network/config", 922 "org.freedesktop.DBus.Properties", "Set", 923 "xyz.openbmc_project.Network.SystemConfiguration", "HostName", 924 std::variant<std::string>(hostname)); 925 } 926 927 void handleMACAddressPatch(const std::string &ifaceId, 928 const std::string &macAddress, 929 const std::shared_ptr<AsyncResp> &asyncResp) 930 { 931 crow::connections::systemBus->async_method_call( 932 [asyncResp, macAddress](const boost::system::error_code ec) { 933 if (ec) 934 { 935 messages::internalError(asyncResp->res); 936 return; 937 } 938 asyncResp->res.jsonValue["MACAddress"] = std::move(macAddress); 939 }, 940 "xyz.openbmc_project.Network", 941 "/xyz/openbmc_project/network/" + ifaceId, 942 "org.freedesktop.DBus.Properties", "Set", 943 "xyz.openbmc_project.Network.MACAddress", "MACAddress", 944 std::variant<std::string>(macAddress)); 945 } 946 947 void handleIPv4Patch( 948 const std::string &ifaceId, nlohmann::json &input, 949 const boost::container::flat_set<IPv4AddressData> &ipv4Data, 950 const std::shared_ptr<AsyncResp> asyncResp) 951 { 952 if (!input.is_array()) 953 { 954 messages::propertyValueTypeError(asyncResp->res, input.dump(), 955 "IPv4Addresses"); 956 return; 957 } 958 959 int entryIdx = 0; 960 boost::container::flat_set<IPv4AddressData>::const_iterator thisData = 961 ipv4Data.begin(); 962 for (nlohmann::json &thisJson : input) 963 { 964 std::string pathString = 965 "IPv4Addresses/" + std::to_string(entryIdx); 966 967 if (thisJson.is_null()) 968 { 969 if (thisData != ipv4Data.end()) 970 { 971 deleteIPv4(ifaceId, thisData->id, entryIdx, asyncResp); 972 thisData++; 973 } 974 else 975 { 976 messages::propertyValueFormatError( 977 asyncResp->res, input.dump(), pathString); 978 return; 979 // TODO(ratagupt) Not sure about the property where value is 980 // list and if unable to update one of the 981 // list value then should we proceed further or 982 // break there, would ask in the redfish forum 983 // till then we stop processing the next list item. 984 } 985 entryIdx++; 986 continue; // not an error as per the redfish spec. 987 } 988 989 if (thisJson.empty()) 990 { 991 if (thisData != ipv4Data.end()) 992 { 993 thisData++; 994 } 995 else 996 { 997 messages::propertyMissing(asyncResp->res, 998 pathString + "/Address"); 999 return; 1000 // TODO(ratagupt) Not sure about the property where value is 1001 // list and if unable to update one of the 1002 // list value then should we proceed further or 1003 // break there, would ask in the redfish forum 1004 // till then we stop processing the next list item. 1005 } 1006 entryIdx++; 1007 continue; // not an error as per the redfish spec. 1008 } 1009 1010 std::optional<std::string> address; 1011 std::optional<std::string> addressOrigin; 1012 std::optional<std::string> subnetMask; 1013 std::optional<std::string> gateway; 1014 1015 if (!json_util::readJson(thisJson, asyncResp->res, "Address", 1016 address, "AddressOrigin", addressOrigin, 1017 "SubnetMask", subnetMask, "Gateway", 1018 gateway)) 1019 { 1020 return; 1021 } 1022 1023 if (address) 1024 { 1025 if (!ipv4VerifyIpAndGetBitcount(*address)) 1026 { 1027 messages::propertyValueFormatError(asyncResp->res, *address, 1028 pathString + "/Address"); 1029 return; 1030 } 1031 } 1032 1033 uint8_t prefixLength = 0; 1034 if (subnetMask) 1035 { 1036 if (!ipv4VerifyIpAndGetBitcount(*subnetMask, &prefixLength)) 1037 { 1038 messages::propertyValueFormatError( 1039 asyncResp->res, *subnetMask, 1040 pathString + "/SubnetMask"); 1041 return; 1042 } 1043 } 1044 std::string addressOriginInDBusFormat; 1045 if (addressOrigin) 1046 { 1047 // Get Address origin in proper format 1048 addressOriginInDBusFormat = 1049 translateAddressOriginRedfishToDbus(*addressOrigin); 1050 if (addressOriginInDBusFormat.empty()) 1051 { 1052 messages::propertyValueNotInList( 1053 asyncResp->res, *addressOrigin, 1054 pathString + "/AddressOrigin"); 1055 return; 1056 } 1057 } 1058 1059 if (gateway) 1060 { 1061 if (!ipv4VerifyIpAndGetBitcount(*gateway)) 1062 { 1063 messages::propertyValueFormatError(asyncResp->res, *gateway, 1064 pathString + "/Gateway"); 1065 return; 1066 } 1067 } 1068 1069 // if IP address exist then modify it. 1070 if (thisData != ipv4Data.end()) 1071 { 1072 // Apply changes 1073 if (address) 1074 { 1075 auto callback = [asyncResp, entryIdx, 1076 address{std::string(*address)}]( 1077 const boost::system::error_code ec) { 1078 if (ec) 1079 { 1080 messages::internalError(asyncResp->res); 1081 return; 1082 } 1083 asyncResp->res 1084 .jsonValue["IPv4Addresses"][entryIdx]["Address"] = 1085 std::move(address); 1086 }; 1087 1088 crow::connections::systemBus->async_method_call( 1089 std::move(callback), "xyz.openbmc_project.Network", 1090 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + 1091 thisData->id, 1092 "org.freedesktop.DBus.Properties", "Set", 1093 "xyz.openbmc_project.Network.IP", "Address", 1094 std::variant<std::string>(*address)); 1095 } 1096 1097 if (subnetMask) 1098 { 1099 changeIPv4SubnetMaskProperty(ifaceId, entryIdx, 1100 thisData->id, *subnetMask, 1101 prefixLength, asyncResp); 1102 } 1103 1104 if (addressOrigin) 1105 { 1106 changeIPv4Origin(ifaceId, entryIdx, thisData->id, 1107 *addressOrigin, addressOriginInDBusFormat, 1108 asyncResp); 1109 } 1110 1111 if (gateway) 1112 { 1113 auto callback = [asyncResp, entryIdx, 1114 gateway{std::string(*gateway)}]( 1115 const boost::system::error_code ec) { 1116 if (ec) 1117 { 1118 messages::internalError(asyncResp->res); 1119 return; 1120 } 1121 asyncResp->res 1122 .jsonValue["IPv4Addresses"][entryIdx]["Gateway"] = 1123 std::move(gateway); 1124 }; 1125 1126 crow::connections::systemBus->async_method_call( 1127 std::move(callback), "xyz.openbmc_project.Network", 1128 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + 1129 thisData->id, 1130 "org.freedesktop.DBus.Properties", "Set", 1131 "xyz.openbmc_project.Network.IP", "Gateway", 1132 std::variant<std::string>(*gateway)); 1133 } 1134 1135 thisData++; 1136 } 1137 else 1138 { 1139 // Create IPv4 with provided data 1140 if (!gateway) 1141 { 1142 messages::propertyMissing(asyncResp->res, 1143 pathString + "/Gateway"); 1144 continue; 1145 } 1146 1147 if (!address) 1148 { 1149 messages::propertyMissing(asyncResp->res, 1150 pathString + "/Address"); 1151 continue; 1152 } 1153 1154 if (!subnetMask) 1155 { 1156 messages::propertyMissing(asyncResp->res, 1157 pathString + "/SubnetMask"); 1158 continue; 1159 } 1160 1161 createIPv4(ifaceId, entryIdx, prefixLength, *gateway, *address, 1162 asyncResp); 1163 1164 nlohmann::json &ipv4AddressJson = 1165 asyncResp->res.jsonValue["IPv4Addresses"][entryIdx]; 1166 ipv4AddressJson["Address"] = *address; 1167 ipv4AddressJson["SubnetMask"] = *subnetMask; 1168 ipv4AddressJson["Gateway"] = *gateway; 1169 } 1170 entryIdx++; 1171 } 1172 } 1173 1174 void handleStaticNameServersPatch( 1175 const std::string &ifaceId, 1176 const std::vector<std::string> &updatedStaticNameServers, 1177 const std::shared_ptr<AsyncResp> &asyncResp) 1178 { 1179 crow::connections::systemBus->async_method_call( 1180 [asyncResp, 1181 updatedStaticNameServers](const boost::system::error_code ec) { 1182 if (ec) 1183 { 1184 messages::internalError(asyncResp->res); 1185 return; 1186 } 1187 asyncResp->res.jsonValue["NameServers"] = 1188 updatedStaticNameServers; 1189 asyncResp->res.jsonValue["StaticNameServers"] = 1190 updatedStaticNameServers; 1191 }, 1192 "xyz.openbmc_project.Network", 1193 "/xyz/openbmc_project/network/" + ifaceId, 1194 "org.freedesktop.DBus.Properties", "Set", 1195 "xyz.openbmc_project.Network.EthernetInterface", "Nameservers", 1196 std::variant<std::vector<std::string>>{updatedStaticNameServers}); 1197 } 1198 1199 void parseInterfaceData( 1200 nlohmann::json &json_response, const std::string &iface_id, 1201 const EthernetInterfaceData ðData, 1202 const boost::container::flat_set<IPv4AddressData> &ipv4Data) 1203 { 1204 json_response["Id"] = iface_id; 1205 json_response["@odata.id"] = 1206 "/redfish/v1/Managers/bmc/EthernetInterfaces/" + iface_id; 1207 json_response["InterfaceEnabled"] = true; 1208 if (ethData.speed == 0) 1209 { 1210 json_response["LinkStatus"] = "NoLink"; 1211 json_response["Status"] = { 1212 {"Health", "OK"}, 1213 {"State", "Disabled"}, 1214 }; 1215 } 1216 else 1217 { 1218 json_response["LinkStatus"] = "LinkUp"; 1219 json_response["Status"] = { 1220 {"Health", "OK"}, 1221 {"State", "Enabled"}, 1222 }; 1223 } 1224 json_response["SpeedMbps"] = ethData.speed; 1225 json_response["MACAddress"] = ethData.mac_address; 1226 json_response["DHCPv4"]["DHCPEnabled"] = ethData.DHCPEnabled; 1227 1228 if (!ethData.hostname.empty()) 1229 { 1230 json_response["HostName"] = ethData.hostname; 1231 } 1232 1233 json_response["VLANs"] = { 1234 {"@odata.id", "/redfish/v1/Managers/bmc/EthernetInterfaces/" + 1235 iface_id + "/VLANs"}}; 1236 1237 json_response["NameServers"] = ethData.nameservers; 1238 json_response["StaticNameServers"] = ethData.nameservers; 1239 1240 if (ipv4Data.size() > 0) 1241 { 1242 nlohmann::json &ipv4_array = json_response["IPv4Addresses"]; 1243 ipv4_array = nlohmann::json::array(); 1244 for (auto &ipv4_config : ipv4Data) 1245 { 1246 1247 std::string gatewayStr = ipv4_config.gateway; 1248 if (gatewayStr.empty()) 1249 { 1250 gatewayStr = "0.0.0.0"; 1251 } 1252 1253 ipv4_array.push_back({{"AddressOrigin", ipv4_config.origin}, 1254 {"SubnetMask", ipv4_config.netmask}, 1255 {"Address", ipv4_config.address}, 1256 {"Gateway", gatewayStr}}); 1257 } 1258 } 1259 json_response["IPv6DefaultGateway"] = ethData.ipv6_default_gateway; 1260 } 1261 1262 /** 1263 * Functions triggers appropriate requests on DBus 1264 */ 1265 void doGet(crow::Response &res, const crow::Request &req, 1266 const std::vector<std::string> ¶ms) override 1267 { 1268 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 1269 if (params.size() != 1) 1270 { 1271 messages::internalError(asyncResp->res); 1272 return; 1273 } 1274 1275 getEthernetIfaceData( 1276 params[0], 1277 [this, asyncResp, iface_id{std::string(params[0])}]( 1278 const bool &success, const EthernetInterfaceData ðData, 1279 const boost::container::flat_set<IPv4AddressData> &ipv4Data) { 1280 if (!success) 1281 { 1282 // TODO(Pawel)consider distinguish between non existing 1283 // object, and other errors 1284 messages::resourceNotFound(asyncResp->res, 1285 "EthernetInterface", iface_id); 1286 return; 1287 } 1288 asyncResp->res.jsonValue["@odata.type"] = 1289 "#EthernetInterface.v1_4_1.EthernetInterface"; 1290 asyncResp->res.jsonValue["@odata.context"] = 1291 "/redfish/v1/$metadata#EthernetInterface.EthernetInterface"; 1292 asyncResp->res.jsonValue["Name"] = "Manager Ethernet Interface"; 1293 asyncResp->res.jsonValue["Description"] = 1294 "Management Network Interface"; 1295 1296 parseInterfaceData(asyncResp->res.jsonValue, iface_id, ethData, 1297 ipv4Data); 1298 }); 1299 getDHCPConfigData(asyncResp); 1300 } 1301 1302 void doPatch(crow::Response &res, const crow::Request &req, 1303 const std::vector<std::string> ¶ms) override 1304 { 1305 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 1306 if (params.size() != 1) 1307 { 1308 messages::internalError(asyncResp->res); 1309 return; 1310 } 1311 1312 const std::string &iface_id = params[0]; 1313 1314 std::optional<std::string> hostname; 1315 std::optional<std::string> macAddress; 1316 std::optional<std::string> ipv6DefaultGateway; 1317 std::optional<nlohmann::json> ipv4Addresses; 1318 std::optional<nlohmann::json> ipv6Addresses; 1319 std::optional<std::vector<std::string>> staticNameServers; 1320 1321 if (!json_util::readJson( 1322 req, res, "HostName", hostname, "IPv4Addresses", ipv4Addresses, 1323 "IPv6Addresses", ipv6Addresses, "MACAddress", macAddress, 1324 "StaticNameServers", staticNameServers, "IPv6DefaultGateway", 1325 ipv6DefaultGateway)) 1326 { 1327 return; 1328 } 1329 1330 // Get single eth interface data, and call the below callback for JSON 1331 // preparation 1332 getEthernetIfaceData( 1333 iface_id, 1334 [this, asyncResp, iface_id, hostname = std::move(hostname), 1335 macAddress = std::move(macAddress), 1336 ipv4Addresses = std::move(ipv4Addresses), 1337 ipv6Addresses = std::move(ipv6Addresses), 1338 ipv6DefaultGateway = std::move(ipv6DefaultGateway), 1339 staticNameServers = std::move(staticNameServers)]( 1340 const bool &success, const EthernetInterfaceData ðData, 1341 const boost::container::flat_set<IPv4AddressData> &ipv4Data) { 1342 if (!success) 1343 { 1344 // ... otherwise return error 1345 // TODO(Pawel)consider distinguish between non existing 1346 // object, and other errors 1347 messages::resourceNotFound(asyncResp->res, 1348 "Ethernet Interface", iface_id); 1349 return; 1350 } 1351 1352 parseInterfaceData(asyncResp->res.jsonValue, iface_id, ethData, 1353 ipv4Data); 1354 1355 if (hostname) 1356 { 1357 handleHostnamePatch(*hostname, asyncResp); 1358 } 1359 1360 if (macAddress) 1361 { 1362 handleMACAddressPatch(iface_id, *macAddress, asyncResp); 1363 } 1364 1365 if (ipv4Addresses) 1366 { 1367 // TODO(ed) for some reason the capture of ipv4Addresses 1368 // above is returning a const value, not a non-const value. 1369 // This doesn't really work for us, as we need to be able to 1370 // efficiently move out the intermedia nlohmann::json 1371 // objects. This makes a copy of the structure, and operates 1372 // on that, but could be done more efficiently 1373 nlohmann::json ipv4 = std::move(*ipv4Addresses); 1374 handleIPv4Patch(iface_id, ipv4, ipv4Data, asyncResp); 1375 } 1376 1377 if (ipv6Addresses) 1378 { 1379 // TODO(kkowalsk) IPv6 Not supported on D-Bus yet 1380 messages::propertyNotWritable(asyncResp->res, 1381 "IPv6Addresses"); 1382 } 1383 1384 if (staticNameServers) 1385 { 1386 handleStaticNameServersPatch(iface_id, *staticNameServers, 1387 asyncResp); 1388 } 1389 1390 if (ipv6DefaultGateway) 1391 { 1392 messages::propertyNotWritable(asyncResp->res, 1393 "IPv6DefaultGateway"); 1394 } 1395 }); 1396 } 1397 }; 1398 1399 /** 1400 * VlanNetworkInterface derived class for delivering VLANNetworkInterface 1401 * Schema 1402 */ 1403 class VlanNetworkInterface : public Node 1404 { 1405 public: 1406 /* 1407 * Default Constructor 1408 */ 1409 template <typename CrowApp> 1410 VlanNetworkInterface(CrowApp &app) : 1411 Node(app, 1412 "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/VLANs/<str>", 1413 std::string(), std::string()) 1414 { 1415 entityPrivileges = { 1416 {boost::beast::http::verb::get, {{"Login"}}}, 1417 {boost::beast::http::verb::head, {{"Login"}}}, 1418 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 1419 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 1420 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 1421 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 1422 } 1423 1424 private: 1425 void parseInterfaceData( 1426 nlohmann::json &json_response, const std::string &parent_iface_id, 1427 const std::string &iface_id, const EthernetInterfaceData ðData, 1428 const boost::container::flat_set<IPv4AddressData> &ipv4Data) 1429 { 1430 // Fill out obvious data... 1431 json_response["Id"] = iface_id; 1432 json_response["@odata.id"] = 1433 "/redfish/v1/Managers/bmc/EthernetInterfaces/" + parent_iface_id + 1434 "/VLANs/" + iface_id; 1435 1436 json_response["VLANEnable"] = true; 1437 if (!ethData.vlan_id.empty()) 1438 { 1439 json_response["VLANId"] = ethData.vlan_id.back(); 1440 } 1441 } 1442 1443 bool verifyNames(const std::string &parent, const std::string &iface) 1444 { 1445 if (!boost::starts_with(iface, parent + "_")) 1446 { 1447 return false; 1448 } 1449 else 1450 { 1451 return true; 1452 } 1453 } 1454 1455 /** 1456 * Functions triggers appropriate requests on DBus 1457 */ 1458 void doGet(crow::Response &res, const crow::Request &req, 1459 const std::vector<std::string> ¶ms) override 1460 { 1461 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 1462 // TODO(Pawel) this shall be parameterized call (two params) to get 1463 // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'. 1464 // Check if there is required param, truly entering this shall be 1465 // impossible. 1466 if (params.size() != 2) 1467 { 1468 messages::internalError(res); 1469 res.end(); 1470 return; 1471 } 1472 1473 const std::string &parent_iface_id = params[0]; 1474 const std::string &iface_id = params[1]; 1475 res.jsonValue["@odata.type"] = 1476 "#VLanNetworkInterface.v1_1_0.VLanNetworkInterface"; 1477 res.jsonValue["@odata.context"] = 1478 "/redfish/v1/$metadata#VLanNetworkInterface.VLanNetworkInterface"; 1479 res.jsonValue["Name"] = "VLAN Network Interface"; 1480 1481 if (!verifyNames(parent_iface_id, iface_id)) 1482 { 1483 return; 1484 } 1485 1486 // Get single eth interface data, and call the below callback for JSON 1487 // preparation 1488 getEthernetIfaceData( 1489 params[1], 1490 [this, asyncResp, parent_iface_id{std::string(params[0])}, 1491 iface_id{std::string(params[1])}]( 1492 const bool &success, const EthernetInterfaceData ðData, 1493 const boost::container::flat_set<IPv4AddressData> &ipv4Data) { 1494 if (success && ethData.vlan_id.size() != 0) 1495 { 1496 parseInterfaceData(asyncResp->res.jsonValue, 1497 parent_iface_id, iface_id, ethData, 1498 ipv4Data); 1499 } 1500 else 1501 { 1502 // ... otherwise return error 1503 // TODO(Pawel)consider distinguish between non existing 1504 // object, and other errors 1505 messages::resourceNotFound( 1506 asyncResp->res, "VLAN Network Interface", iface_id); 1507 } 1508 }); 1509 } 1510 1511 void doPatch(crow::Response &res, const crow::Request &req, 1512 const std::vector<std::string> ¶ms) override 1513 { 1514 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 1515 if (params.size() != 2) 1516 { 1517 messages::internalError(asyncResp->res); 1518 return; 1519 } 1520 1521 const std::string &parentIfaceId = params[0]; 1522 const std::string &ifaceId = params[1]; 1523 1524 if (!verifyNames(parentIfaceId, ifaceId)) 1525 { 1526 messages::resourceNotFound(asyncResp->res, "VLAN Network Interface", 1527 ifaceId); 1528 return; 1529 } 1530 1531 bool vlanEnable = false; 1532 uint64_t vlanId = 0; 1533 1534 if (!json_util::readJson(req, res, "VLANEnable", vlanEnable, "VLANId", 1535 vlanId)) 1536 { 1537 return; 1538 } 1539 1540 // Get single eth interface data, and call the below callback for JSON 1541 // preparation 1542 getEthernetIfaceData(params[1], [this, asyncResp, 1543 parentIfaceId{std::string(params[0])}, 1544 ifaceId{std::string(params[1])}, 1545 &vlanEnable, &vlanId]( 1546 const bool &success, 1547 const EthernetInterfaceData 1548 ðData, 1549 const boost::container::flat_set< 1550 IPv4AddressData> &ipv4Data) { 1551 if (success && !ethData.vlan_id.empty()) 1552 { 1553 parseInterfaceData(asyncResp->res.jsonValue, parentIfaceId, 1554 ifaceId, ethData, ipv4Data); 1555 auto callback = 1556 [asyncResp](const boost::system::error_code ec) { 1557 if (ec) 1558 { 1559 messages::internalError(asyncResp->res); 1560 } 1561 }; 1562 1563 if (vlanEnable == true) 1564 { 1565 crow::connections::systemBus->async_method_call( 1566 std::move(callback), "xyz.openbmc_project.Network", 1567 "/xyz/openbmc_project/network/" + ifaceId, 1568 "org.freedesktop.DBus.Properties", "Set", 1569 "xyz.openbmc_project.Network.VLAN", "Id", 1570 std::variant<uint32_t>(vlanId)); 1571 } 1572 else 1573 { 1574 BMCWEB_LOG_DEBUG 1575 << "vlanEnable is false. Deleting the vlan interface"; 1576 crow::connections::systemBus->async_method_call( 1577 std::move(callback), "xyz.openbmc_project.Network", 1578 std::string("/xyz/openbmc_project/network/") + ifaceId, 1579 "xyz.openbmc_project.Object.Delete", "Delete"); 1580 } 1581 } 1582 else 1583 { 1584 // TODO(Pawel)consider distinguish between non existing 1585 // object, and other errors 1586 messages::resourceNotFound(asyncResp->res, 1587 "VLAN Network Interface", ifaceId); 1588 return; 1589 } 1590 }); 1591 } 1592 1593 void doDelete(crow::Response &res, const crow::Request &req, 1594 const std::vector<std::string> ¶ms) override 1595 { 1596 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 1597 if (params.size() != 2) 1598 { 1599 messages::internalError(asyncResp->res); 1600 return; 1601 } 1602 1603 const std::string &parentIfaceId = params[0]; 1604 const std::string &ifaceId = params[1]; 1605 1606 if (!verifyNames(parentIfaceId, ifaceId)) 1607 { 1608 messages::resourceNotFound(asyncResp->res, "VLAN Network Interface", 1609 ifaceId); 1610 return; 1611 } 1612 1613 // Get single eth interface data, and call the below callback for JSON 1614 // preparation 1615 getEthernetIfaceData( 1616 params[1], 1617 [this, asyncResp, parentIfaceId{std::string(params[0])}, 1618 ifaceId{std::string(params[1])}]( 1619 const bool &success, const EthernetInterfaceData ðData, 1620 const boost::container::flat_set<IPv4AddressData> &ipv4Data) { 1621 if (success && !ethData.vlan_id.empty()) 1622 { 1623 parseInterfaceData(asyncResp->res.jsonValue, parentIfaceId, 1624 ifaceId, ethData, ipv4Data); 1625 1626 auto callback = 1627 [asyncResp](const boost::system::error_code ec) { 1628 if (ec) 1629 { 1630 messages::internalError(asyncResp->res); 1631 } 1632 }; 1633 crow::connections::systemBus->async_method_call( 1634 std::move(callback), "xyz.openbmc_project.Network", 1635 std::string("/xyz/openbmc_project/network/") + ifaceId, 1636 "xyz.openbmc_project.Object.Delete", "Delete"); 1637 } 1638 else 1639 { 1640 // ... otherwise return error 1641 // TODO(Pawel)consider distinguish between non existing 1642 // object, and other errors 1643 messages::resourceNotFound( 1644 asyncResp->res, "VLAN Network Interface", ifaceId); 1645 } 1646 }); 1647 } 1648 }; 1649 1650 /** 1651 * VlanNetworkInterfaceCollection derived class for delivering 1652 * VLANNetworkInterface Collection Schema 1653 */ 1654 class VlanNetworkInterfaceCollection : public Node 1655 { 1656 public: 1657 template <typename CrowApp> 1658 VlanNetworkInterfaceCollection(CrowApp &app) : 1659 Node(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/VLANs/", 1660 std::string()) 1661 { 1662 entityPrivileges = { 1663 {boost::beast::http::verb::get, {{"Login"}}}, 1664 {boost::beast::http::verb::head, {{"Login"}}}, 1665 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 1666 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 1667 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 1668 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 1669 } 1670 1671 private: 1672 /** 1673 * Functions triggers appropriate requests on DBus 1674 */ 1675 void doGet(crow::Response &res, const crow::Request &req, 1676 const std::vector<std::string> ¶ms) override 1677 { 1678 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 1679 if (params.size() != 1) 1680 { 1681 // This means there is a problem with the router 1682 messages::internalError(asyncResp->res); 1683 return; 1684 } 1685 1686 const std::string &rootInterfaceName = params[0]; 1687 1688 // Get eth interface list, and call the below callback for JSON 1689 // preparation 1690 getEthernetIfaceList( 1691 [asyncResp, rootInterfaceName{std::string(rootInterfaceName)}]( 1692 const bool &success, 1693 const std::vector<std::string> &iface_list) { 1694 if (!success) 1695 { 1696 messages::internalError(asyncResp->res); 1697 return; 1698 } 1699 asyncResp->res.jsonValue["@odata.type"] = 1700 "#VLanNetworkInterfaceCollection." 1701 "VLanNetworkInterfaceCollection"; 1702 asyncResp->res.jsonValue["@odata.context"] = 1703 "/redfish/v1/$metadata" 1704 "#VLanNetworkInterfaceCollection." 1705 "VLanNetworkInterfaceCollection"; 1706 asyncResp->res.jsonValue["Name"] = 1707 "VLAN Network Interface Collection"; 1708 1709 nlohmann::json iface_array = nlohmann::json::array(); 1710 1711 for (const std::string &iface_item : iface_list) 1712 { 1713 if (boost::starts_with(iface_item, rootInterfaceName + "_")) 1714 { 1715 iface_array.push_back( 1716 {{"@odata.id", 1717 "/redfish/v1/Managers/bmc/EthernetInterfaces/" + 1718 rootInterfaceName + "/VLANs/" + iface_item}}); 1719 } 1720 } 1721 1722 asyncResp->res.jsonValue["Members@odata.count"] = 1723 iface_array.size(); 1724 asyncResp->res.jsonValue["Members"] = std::move(iface_array); 1725 asyncResp->res.jsonValue["@odata.id"] = 1726 "/redfish/v1/Managers/bmc/EthernetInterfaces/" + 1727 rootInterfaceName + "/VLANs"; 1728 }); 1729 } 1730 1731 void doPost(crow::Response &res, const crow::Request &req, 1732 const std::vector<std::string> ¶ms) override 1733 { 1734 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 1735 if (params.size() != 1) 1736 { 1737 messages::internalError(asyncResp->res); 1738 return; 1739 } 1740 bool vlanEnable = false; 1741 uint32_t vlanId = 0; 1742 if (!json_util::readJson(req, res, "VLANId", vlanId, "VLANEnable", 1743 vlanEnable)) 1744 { 1745 return; 1746 } 1747 // Need both vlanId and vlanEnable to service this request 1748 if (!vlanId) 1749 { 1750 messages::propertyMissing(asyncResp->res, "VLANId"); 1751 } 1752 if (!vlanEnable) 1753 { 1754 messages::propertyMissing(asyncResp->res, "VLANEnable"); 1755 } 1756 if (static_cast<bool>(vlanId) ^ static_cast<bool>(vlanEnable)) 1757 { 1758 return; 1759 } 1760 1761 const std::string &rootInterfaceName = params[0]; 1762 auto callback = [asyncResp](const boost::system::error_code ec) { 1763 if (ec) 1764 { 1765 // TODO(ed) make more consistent error messages based on 1766 // phosphor-network responses 1767 messages::internalError(asyncResp->res); 1768 return; 1769 } 1770 messages::created(asyncResp->res); 1771 }; 1772 crow::connections::systemBus->async_method_call( 1773 std::move(callback), "xyz.openbmc_project.Network", 1774 "/xyz/openbmc_project/network", 1775 "xyz.openbmc_project.Network.VLAN.Create", "VLAN", 1776 rootInterfaceName, vlanId); 1777 } 1778 }; 1779 } // namespace redfish 1780