| Patrick Venture | 4d49ae6 | 2018-09-17 11:35:32 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | * Copyright 2018 Google Inc. | 
|  | 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 |  | 
|  | 17 | #include "cable.hpp" | 
|  | 18 |  | 
|  | 19 | #include "main.hpp" | 
|  | 20 |  | 
|  | 21 | #include <cstdint> | 
| Patrick Venture | ce07ee0 | 2018-09-19 18:09:32 -0700 | [diff] [blame] | 22 | #include <cstring> | 
| Patrick Venture | 4d49ae6 | 2018-09-17 11:35:32 -0700 | [diff] [blame] | 23 | #include <experimental/filesystem> | 
|  | 24 | #include <fstream> | 
|  | 25 | #include <sstream> | 
|  | 26 | #include <string> | 
|  | 27 | #include <system_error> | 
|  | 28 |  | 
|  | 29 | namespace google | 
|  | 30 | { | 
|  | 31 | namespace ipmi | 
|  | 32 | { | 
|  | 33 | namespace fs = std::experimental::filesystem; | 
|  | 34 |  | 
|  | 35 | struct CableRequest | 
|  | 36 | { | 
|  | 37 | uint8_t subcommand; | 
|  | 38 | uint8_t if_name_len; | 
|  | 39 | uint8_t if_name[0]; | 
|  | 40 | } __attribute__((packed)); | 
|  | 41 |  | 
|  | 42 | struct CableReply | 
|  | 43 | { | 
|  | 44 | uint8_t subcommand; | 
|  | 45 | uint8_t value; | 
|  | 46 | } __attribute__((packed)); | 
|  | 47 |  | 
|  | 48 | ipmi_ret_t CableCheck(const uint8_t* reqBuf, uint8_t* replyBuf, size_t* dataLen) | 
|  | 49 | { | 
|  | 50 | // There is an IPMI LAN channel statistics command which could be used for | 
|  | 51 | // this type of check, however, we're not able to wait for the OpenBMC | 
|  | 52 | // implementation to stabilize related to the network management. | 
|  | 53 | // | 
|  | 54 | // There is a link status file, but it is "unknown" to start with... | 
|  | 55 | // The path we're checking: /sys/class/net/eth1/statistics/rx_packets | 
|  | 56 |  | 
|  | 57 | // This command is expecting: [0x00][len][if_name] | 
|  | 58 | if ((*dataLen) < sizeof(struct CableRequest) + sizeof(uint8_t)) | 
|  | 59 | { | 
| Patrick Venture | ce07ee0 | 2018-09-19 18:09:32 -0700 | [diff] [blame] | 60 | std::fprintf(stderr, "Invalid command length: %u\n", | 
|  | 61 | static_cast<uint32_t>(*dataLen)); | 
| Patrick Venture | fff9861 | 2018-11-12 09:05:54 -0800 | [diff] [blame] | 62 | return IPMI_CC_REQ_DATA_LEN_INVALID; | 
| Patrick Venture | 4d49ae6 | 2018-09-17 11:35:32 -0700 | [diff] [blame] | 63 | } | 
|  | 64 |  | 
|  | 65 | const auto request = | 
|  | 66 | reinterpret_cast<const struct CableRequest*>(&reqBuf[0]); | 
|  | 67 |  | 
|  | 68 | // Sanity check the object contents. | 
|  | 69 | if (request->if_name_len == 0) | 
|  | 70 | { | 
| Patrick Venture | ce07ee0 | 2018-09-19 18:09:32 -0700 | [diff] [blame] | 71 | std::fprintf(stderr, "Invalid string length: %d\n", | 
|  | 72 | request->if_name_len); | 
| Patrick Venture | fff9861 | 2018-11-12 09:05:54 -0800 | [diff] [blame] | 73 | return IPMI_CC_REQ_DATA_LEN_INVALID; | 
| Patrick Venture | 4d49ae6 | 2018-09-17 11:35:32 -0700 | [diff] [blame] | 74 | } | 
|  | 75 |  | 
|  | 76 | // Verify the request buffer contains the object and the string. | 
|  | 77 | if ((*dataLen) < (sizeof(struct CableRequest) + request->if_name_len)) | 
|  | 78 | { | 
| Patrick Venture | ce07ee0 | 2018-09-19 18:09:32 -0700 | [diff] [blame] | 79 | std::fprintf(stderr, "*dataLen too small: %u\n", | 
|  | 80 | static_cast<uint32_t>(*dataLen)); | 
| Patrick Venture | fff9861 | 2018-11-12 09:05:54 -0800 | [diff] [blame] | 81 | return IPMI_CC_REQ_DATA_LEN_INVALID; | 
| Patrick Venture | 4d49ae6 | 2018-09-17 11:35:32 -0700 | [diff] [blame] | 82 | } | 
|  | 83 |  | 
|  | 84 | // Maximum length one can specify, plus null terminator. | 
|  | 85 | char nameBuf[256] = {}; | 
|  | 86 | std::ostringstream opath; | 
|  | 87 |  | 
|  | 88 | // Copy the string out of the request buffer. | 
| Patrick Venture | ce07ee0 | 2018-09-19 18:09:32 -0700 | [diff] [blame] | 89 | std::memcpy(&nameBuf[0], request->if_name, request->if_name_len); | 
| Patrick Venture | 4d49ae6 | 2018-09-17 11:35:32 -0700 | [diff] [blame] | 90 | std::string name = nameBuf; | 
|  | 91 |  | 
|  | 92 | // Minor sanity & security check (of course, I'm less certain if unicode | 
|  | 93 | // comes into play here. | 
|  | 94 | // | 
|  | 95 | // Basically you can't easily inject ../ or /../ into the path below. | 
|  | 96 | if (name.find("/") != std::string::npos) | 
|  | 97 | { | 
| Patrick Venture | ce07ee0 | 2018-09-19 18:09:32 -0700 | [diff] [blame] | 98 | std::fprintf(stderr, "Invalid or illegal name: '%s'\n", nameBuf); | 
| Patrick Venture | fff9861 | 2018-11-12 09:05:54 -0800 | [diff] [blame] | 99 | return IPMI_CC_INVALID_FIELD_REQUEST; | 
| Patrick Venture | 4d49ae6 | 2018-09-17 11:35:32 -0700 | [diff] [blame] | 100 | } | 
|  | 101 |  | 
|  | 102 | opath << "/sys/class/net/" << name << "/statistics/rx_packets"; | 
|  | 103 | std::string path = opath.str(); | 
|  | 104 |  | 
|  | 105 | std::error_code ec; | 
|  | 106 | if (!fs::exists(path, ec)) | 
|  | 107 | { | 
| Patrick Venture | ce07ee0 | 2018-09-19 18:09:32 -0700 | [diff] [blame] | 108 | std::fprintf(stderr, "Path: '%s' doesn't exist.\n", path.c_str()); | 
| Patrick Venture | fff9861 | 2018-11-12 09:05:54 -0800 | [diff] [blame] | 109 | return IPMI_CC_INVALID_FIELD_REQUEST; | 
| Patrick Venture | 4d49ae6 | 2018-09-17 11:35:32 -0700 | [diff] [blame] | 110 | } | 
|  | 111 | // We're uninterested in the state of ec. | 
|  | 112 |  | 
|  | 113 | // Read the file and check the result. | 
|  | 114 | int64_t count = 0; | 
|  | 115 | std::ifstream ifs; | 
|  | 116 | ifs.exceptions(std::ifstream::failbit); | 
|  | 117 | try | 
|  | 118 | { | 
|  | 119 | ifs.open(path); | 
|  | 120 | ifs >> count; | 
|  | 121 | } | 
|  | 122 | catch (std::ios_base::failure& fail) | 
|  | 123 | { | 
| Patrick Venture | fff9861 | 2018-11-12 09:05:54 -0800 | [diff] [blame] | 124 | return IPMI_CC_UNSPECIFIED_ERROR; | 
| Patrick Venture | 4d49ae6 | 2018-09-17 11:35:32 -0700 | [diff] [blame] | 125 | } | 
|  | 126 |  | 
|  | 127 | struct CableReply reply; | 
|  | 128 | reply.subcommand = SysCableCheck; | 
| Patrick Venture | 4d49ae6 | 2018-09-17 11:35:32 -0700 | [diff] [blame] | 129 |  | 
|  | 130 | // If we have received packets then there is a cable present. | 
|  | 131 | reply.value = (count > 0) ? 1 : 0; | 
|  | 132 |  | 
|  | 133 | // Return the subcommand and the result. | 
| Patrick Venture | ce07ee0 | 2018-09-19 18:09:32 -0700 | [diff] [blame] | 134 | std::memcpy(&replyBuf[0], &reply, sizeof(struct CableReply)); | 
| Patrick Venture | 4d49ae6 | 2018-09-17 11:35:32 -0700 | [diff] [blame] | 135 | (*dataLen) = sizeof(struct CableReply); | 
|  | 136 |  | 
|  | 137 | return IPMI_CC_OK; | 
|  | 138 | } | 
|  | 139 |  | 
|  | 140 | } // namespace ipmi | 
|  | 141 | } // namespace google |