blob: e5d6770dccbccd205aaeb30c43adb7235c4f404a [file] [log] [blame]
Patrick Venture5794fcf2017-10-26 11:11:14 -07001#include "channel.hpp"
Patrick Venture5794fcf2017-10-26 11:11:14 -07002
Patrick Venture0b02be92018-08-31 11:55:55 -07003#include "net.hpp"
4#include "transporthandler.hpp"
5#include "types.hpp"
6#include "utils.hpp"
7
Patrick Venture5794fcf2017-10-26 11:11:14 -07008#include <arpa/inet.h>
9
Tom Joseph13227682018-08-10 01:05:21 +053010#include <boost/process/child.hpp>
Patrick Venture0b02be92018-08-31 11:55:55 -070011#include <fstream>
Patrick Venture5794fcf2017-10-26 11:11:14 -070012#include <phosphor-logging/elog-errors.hpp>
Patrick Venture0b02be92018-08-31 11:55:55 -070013#include <phosphor-logging/log.hpp>
Richard Marian Thomaiyarf301f042019-01-16 15:56:16 +053014#include <set>
Patrick Venture0b02be92018-08-31 11:55:55 -070015#include <string>
16#include <xyz/openbmc_project/Common/error.hpp>
Patrick Venture5794fcf2017-10-26 11:11:14 -070017
18using namespace phosphor::logging;
19using namespace sdbusplus::xyz::openbmc_project::Common::Error;
20
21/** @struct GetChannelAccessRequest
22 *
23 * IPMI payload for Get Channel access command request.
24 */
25struct GetChannelAccessRequest
26{
Patrick Venture0b02be92018-08-31 11:55:55 -070027 uint8_t channelNumber; //!< Channel number.
28 uint8_t volatileSetting; //!< Get non-volatile or the volatile setting.
Patrick Venture5794fcf2017-10-26 11:11:14 -070029} __attribute__((packed));
30
31/** @struct GetChannelAccessResponse
32 *
33 * IPMI payload for Get Channel access command response.
34 */
35struct GetChannelAccessResponse
36{
Patrick Venture0b02be92018-08-31 11:55:55 -070037 uint8_t settings; //!< Channel settings.
38 uint8_t privilegeLimit; //!< Channel privilege level limit.
Patrick Venture5794fcf2017-10-26 11:11:14 -070039} __attribute__((packed));
40
Patrick Venture5794fcf2017-10-26 11:11:14 -070041ipmi_ret_t ipmi_get_channel_access(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -070042 ipmi_request_t request,
43 ipmi_response_t response,
44 ipmi_data_len_t data_len,
45 ipmi_context_t context)
Patrick Venture5794fcf2017-10-26 11:11:14 -070046{
Patrick Venture0b02be92018-08-31 11:55:55 -070047 auto requestData =
48 reinterpret_cast<const GetChannelAccessRequest*>(request);
Patrick Venture5794fcf2017-10-26 11:11:14 -070049 std::vector<uint8_t> outPayload(sizeof(GetChannelAccessResponse));
Patrick Venture0b02be92018-08-31 11:55:55 -070050 auto responseData =
51 reinterpret_cast<GetChannelAccessResponse*>(outPayload.data());
Patrick Venture5794fcf2017-10-26 11:11:14 -070052
Patrick Venture5794fcf2017-10-26 11:11:14 -070053 /*
54 * The value Eh is used as a way to identify the current channel that
55 * the command is being received from.
56 */
57 constexpr auto channelE = 0x0E;
Patrick Venturec7c1c3c2017-11-15 14:29:18 -080058 int channel = requestData->channelNumber;
59 auto ethdevice = ipmi::network::ChanneltoEthernet(channel);
Patrick Venture5794fcf2017-10-26 11:11:14 -070060
Patrick Venturec7c1c3c2017-11-15 14:29:18 -080061 if (channel != channelE && ethdevice.empty())
Patrick Venture5794fcf2017-10-26 11:11:14 -070062 {
63 *data_len = 0;
64 return IPMI_CC_INVALID_FIELD_REQUEST;
65 }
66
67 /*
68 * [7:6] - reserved
69 * [5] - 1b = Alerting disabled
70 * [4] - 1b = per message authentication disabled
71 * [3] - 0b = User level authentication enabled
72 * [2:0] - 2h = always available
73 */
74 constexpr auto channelSetting = 0x32;
75
76 responseData->settings = channelSetting;
Patrick Venture0b02be92018-08-31 11:55:55 -070077 // Defaulting the channel privilege to administrator level.
Patrick Venture5794fcf2017-10-26 11:11:14 -070078 responseData->privilegeLimit = PRIVILEGE_ADMIN;
79
80 *data_len = outPayload.size();
81 memcpy(response, outPayload.data(), *data_len);
82
83 return IPMI_CC_OK;
84}
85
86// ATTENTION: This ipmi function is very hardcoded on purpose
87// OpenBMC does not fully support IPMI. This command is useful
88// to have around because it enables testing of interfaces with
89// the IPMI tool.
90#define GET_CHANNEL_INFO_CHANNEL_OFFSET 0
91// IPMI Table 6-2
92#define IPMI_CHANNEL_TYPE_IPMB 1
93// IPMI Table 6-3
94#define IPMI_CHANNEL_MEDIUM_TYPE_OTHER 6
95
96ipmi_ret_t ipmi_app_channel_info(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -070097 ipmi_request_t request,
98 ipmi_response_t response,
99 ipmi_data_len_t data_len,
100 ipmi_context_t context)
Patrick Venture5794fcf2017-10-26 11:11:14 -0700101{
102 ipmi_ret_t rc = IPMI_CC_OK;
Patrick Venturec49daa32018-10-17 15:56:43 -0700103 auto* p = static_cast<uint8_t*>(request);
Patrick Venturec7c1c3c2017-11-15 14:29:18 -0800104 int channel = (*p) & CHANNEL_MASK;
105 std::string ethdevice = ipmi::network::ChanneltoEthernet(channel);
Patrick Venture5794fcf2017-10-26 11:11:14 -0700106
Patrick Venturec7c1c3c2017-11-15 14:29:18 -0800107 // The supported channels numbers are those which are configured.
Patrick Venture5794fcf2017-10-26 11:11:14 -0700108 // Channel Number E is used as way to identify the current channel
109 // that the command is being is received from.
Patrick Venture0b02be92018-08-31 11:55:55 -0700110 if (channel != 0xe && ethdevice.empty())
111 {
Patrick Venture5794fcf2017-10-26 11:11:14 -0700112 rc = IPMI_CC_PARM_OUT_OF_RANGE;
113 *data_len = 0;
Patrick Venture0b02be92018-08-31 11:55:55 -0700114 }
115 else
116 {
Patrick Venture4491a462018-10-13 13:00:42 -0700117 uint8_t resp[] = {1,
118 IPMI_CHANNEL_MEDIUM_TYPE_OTHER,
119 IPMI_CHANNEL_TYPE_IPMB,
120 1,
121 0x41,
122 0xA7,
123 0x00,
124 0,
125 0};
126
Patrick Venturec7c1c3c2017-11-15 14:29:18 -0800127 *data_len = sizeof(resp);
128 memcpy(response, resp, *data_len);
Patrick Venture5794fcf2017-10-26 11:11:14 -0700129 }
130
131 return rc;
132}
Tom Joseph7cbe2282018-03-21 21:17:33 +0530133
134namespace cipher
135{
136
137/** @brief Get the supported Cipher records
138 *
Richard Marian Thomaiyarf301f042019-01-16 15:56:16 +0530139 * The cipher records are read from the JSON file and converted into
140 * 1. cipher suite record format mentioned in the IPMI specification. The
141 * records can be either OEM or standard cipher. Each json entry is parsed and
142 * converted into the cipher record format and pushed into the vector.
143 * 2. Algorithms listed in vector format
Tom Joseph7cbe2282018-03-21 21:17:33 +0530144 *
Richard Marian Thomaiyarf301f042019-01-16 15:56:16 +0530145 * @return pair of vector containing 1. all the cipher suite records. 2.
146 * Algorithms supported
Tom Joseph7cbe2282018-03-21 21:17:33 +0530147 *
148 */
Richard Marian Thomaiyarf301f042019-01-16 15:56:16 +0530149std::pair<std::vector<uint8_t>, std::vector<uint8_t>> getCipherRecords()
Tom Joseph7cbe2282018-03-21 21:17:33 +0530150{
Richard Marian Thomaiyarf301f042019-01-16 15:56:16 +0530151 std::vector<uint8_t> cipherRecords;
152 std::vector<uint8_t> supportedAlgorithmRecords;
153 // create set to get the unique supported algorithms
154 std::set<uint8_t> supportedAlgorithmSet;
Tom Joseph7cbe2282018-03-21 21:17:33 +0530155
156 std::ifstream jsonFile(configFile);
157 if (!jsonFile.is_open())
158 {
159 log<level::ERR>("Channel Cipher suites file not found");
160 elog<InternalFailure>();
161 }
162
163 auto data = Json::parse(jsonFile, nullptr, false);
164 if (data.is_discarded())
165 {
166 log<level::ERR>("Parsing channel cipher suites JSON failed");
167 elog<InternalFailure>();
168 }
169
170 for (const auto& record : data)
171 {
172 if (record.find(oem) != record.end())
173 {
174 // OEM cipher suite - 0xC1
Richard Marian Thomaiyarf301f042019-01-16 15:56:16 +0530175 cipherRecords.push_back(oemCipherSuite);
Tom Joseph7cbe2282018-03-21 21:17:33 +0530176 // Cipher Suite ID
Richard Marian Thomaiyarf301f042019-01-16 15:56:16 +0530177 cipherRecords.push_back(record.value(cipher, 0));
Tom Joseph7cbe2282018-03-21 21:17:33 +0530178 // OEM IANA - 3 bytes
Richard Marian Thomaiyarf301f042019-01-16 15:56:16 +0530179 cipherRecords.push_back(record.value(oem, 0));
180 cipherRecords.push_back(record.value(oem, 0) >> 8);
181 cipherRecords.push_back(record.value(oem, 0) >> 16);
Tom Joseph7cbe2282018-03-21 21:17:33 +0530182 }
183 else
184 {
Richard Marian Thomaiyarf301f042019-01-16 15:56:16 +0530185 // Standard cipher suite - 0xC0
186 cipherRecords.push_back(stdCipherSuite);
Tom Joseph7cbe2282018-03-21 21:17:33 +0530187 // Cipher Suite ID
Richard Marian Thomaiyarf301f042019-01-16 15:56:16 +0530188 cipherRecords.push_back(record.value(cipher, 0));
Tom Joseph7cbe2282018-03-21 21:17:33 +0530189 }
190
191 // Authentication algorithm number
Richard Marian Thomaiyarf301f042019-01-16 15:56:16 +0530192 cipherRecords.push_back(record.value(auth, 0));
193 supportedAlgorithmSet.insert(record.value(auth, 0));
194
Tom Joseph7cbe2282018-03-21 21:17:33 +0530195 // Integrity algorithm number
Richard Marian Thomaiyarf301f042019-01-16 15:56:16 +0530196 cipherRecords.push_back(record.value(integrity, 0) | integrityTag);
197 supportedAlgorithmSet.insert(record.value(integrity, 0) | integrityTag);
198
Tom Joseph7cbe2282018-03-21 21:17:33 +0530199 // Confidentiality algorithm number
Richard Marian Thomaiyarf301f042019-01-16 15:56:16 +0530200 cipherRecords.push_back(record.value(conf, 0) | confTag);
201 supportedAlgorithmSet.insert(record.value(conf, 0) | confTag);
Tom Joseph7cbe2282018-03-21 21:17:33 +0530202 }
203
Richard Marian Thomaiyarf301f042019-01-16 15:56:16 +0530204 // copy the set to supportedAlgorithmRecord which is vector based.
205 std::copy(supportedAlgorithmSet.begin(), supportedAlgorithmSet.end(),
206 std::back_inserter(supportedAlgorithmRecords));
207
208 return std::make_pair(cipherRecords, supportedAlgorithmRecords);
Tom Joseph7cbe2282018-03-21 21:17:33 +0530209}
210
Patrick Venture0b02be92018-08-31 11:55:55 -0700211} // namespace cipher
Tom Joseph7cbe2282018-03-21 21:17:33 +0530212
Patrick Venture0b02be92018-08-31 11:55:55 -0700213ipmi_ret_t getChannelCipherSuites(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Tom Joseph7cbe2282018-03-21 21:17:33 +0530214 ipmi_request_t request,
215 ipmi_response_t response,
216 ipmi_data_len_t data_len,
217 ipmi_context_t context)
218{
Richard Marian Thomaiyarf301f042019-01-16 15:56:16 +0530219 static std::vector<uint8_t> cipherRecords;
220 static std::vector<uint8_t> supportedAlgorithms;
Tom Joseph7cbe2282018-03-21 21:17:33 +0530221 static auto recordInit = false;
222
223 auto requestData =
224 reinterpret_cast<const GetChannelCipherRequest*>(request);
225
Tom Joseph7cbe2282018-03-21 21:17:33 +0530226 if (*data_len < sizeof(GetChannelCipherRequest))
227 {
228 *data_len = 0;
229 return IPMI_CC_REQ_DATA_LEN_INVALID;
230 }
231
232 *data_len = 0;
233
Tom Joseph7cbe2282018-03-21 21:17:33 +0530234 if (!recordInit)
235 {
236 try
237 {
Richard Marian Thomaiyarf301f042019-01-16 15:56:16 +0530238 std::tie(cipherRecords, supportedAlgorithms) =
239 cipher::getCipherRecords();
Tom Joseph7cbe2282018-03-21 21:17:33 +0530240 recordInit = true;
241 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700242 catch (const std::exception& e)
Tom Joseph7cbe2282018-03-21 21:17:33 +0530243 {
244 return IPMI_CC_UNSPECIFIED_ERROR;
245 }
246 }
247
Richard Marian Thomaiyarf301f042019-01-16 15:56:16 +0530248 const auto& records = (cipher::listCipherSuite ==
249 (requestData->listIndex & cipher::listTypeMask))
250 ? cipherRecords
251 : supportedAlgorithms;
252
Tom Joseph7cbe2282018-03-21 21:17:33 +0530253 // List index(00h-3Fh), 0h selects the first set of 16, 1h selects the next
254 // set of 16 and so on.
Patrick Venture0b02be92018-08-31 11:55:55 -0700255 auto index =
256 static_cast<size_t>(requestData->listIndex & cipher::listIndexMask);
Tom Joseph7cbe2282018-03-21 21:17:33 +0530257
258 // Calculate the number of record data bytes to be returned.
259 auto start = std::min(index * cipher::respSize, records.size());
Patrick Venture0b02be92018-08-31 11:55:55 -0700260 auto end =
261 std::min((index * cipher::respSize) + cipher::respSize, records.size());
Tom Joseph7cbe2282018-03-21 21:17:33 +0530262 auto size = end - start;
263
Patrick Venture0b02be92018-08-31 11:55:55 -0700264 auto responseData = reinterpret_cast<GetChannelCipherRespHeader*>(response);
Tom Joseph7cbe2282018-03-21 21:17:33 +0530265 responseData->channelNumber = cipher::defaultChannelNumber;
266
267 if (!size)
268 {
269 *data_len = sizeof(GetChannelCipherRespHeader);
270 }
271 else
272 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700273 std::copy_n(records.data() + start, size,
Tom Joseph7cbe2282018-03-21 21:17:33 +0530274 static_cast<uint8_t*>(response) + 1);
275 *data_len = size + sizeof(GetChannelCipherRespHeader);
276 }
277
278 return IPMI_CC_OK;
279}
Tom Joseph13227682018-08-10 01:05:21 +0530280
281template <typename... ArgTypes>
282static int executeCmd(const char* path, ArgTypes&&... tArgs)
283{
284 boost::process::child execProg(path, const_cast<char*>(tArgs)...);
285 execProg.wait();
286 return execProg.exit_code();
287}
288
289/** @brief Enable the network IPMI service on the specified ethernet interface.
290 *
291 * @param[in] intf - ethernet interface on which to enable IPMI
292 */
293void enableNetworkIPMI(const std::string& intf)
294{
295 // Check if there is a iptable filter to drop IPMI packets for the
296 // interface.
297 auto retCode =
298 executeCmd("/usr/sbin/iptables", "-C", "INPUT", "-p", "udp", "-i",
299 intf.c_str(), "--dport", "623", "-j", "DROP");
300
301 // If the iptable filter exists, delete the filter.
302 if (!retCode)
303 {
304 auto response =
305 executeCmd("/usr/sbin/iptables", "-D", "INPUT", "-p", "udp", "-i",
306 intf.c_str(), "--dport", "623", "-j", "DROP");
307
308 if (response)
309 {
310 log<level::ERR>("Dropping the iptables filter failed",
311 entry("INTF=%s", intf.c_str()),
312 entry("RETURN_CODE:%d", response));
313 }
314 }
315}
316
317/** @brief Disable the network IPMI service on the specified ethernet interface.
318 *
319 * @param[in] intf - ethernet interface on which to disable IPMI
320 */
321void disableNetworkIPMI(const std::string& intf)
322{
323 // Check if there is a iptable filter to drop IPMI packets for the
324 // interface.
325 auto retCode =
326 executeCmd("/usr/sbin/iptables", "-C", "INPUT", "-p", "udp", "-i",
327 intf.c_str(), "--dport", "623", "-j", "DROP");
328
329 // If the iptable filter does not exist, add filter to drop network IPMI
330 // packets
331 if (retCode)
332 {
333 auto response =
334 executeCmd("/usr/sbin/iptables", "-I", "INPUT", "-p", "udp", "-i",
335 intf.c_str(), "--dport", "623", "-j", "DROP");
336
337 if (response)
338 {
339 log<level::ERR>("Inserting iptables filter failed",
340 entry("INTF=%s", intf.c_str()),
341 entry("RETURN_CODE:%d", response));
342 }
343 }
344}
345
346/** @struct SetChannelAccessRequest
347 *
348 * IPMI payload for Set Channel access command request.
349 */
350struct SetChannelAccessRequest
351{
352 uint8_t channelNumber; //!< Channel number
353 uint8_t accessMode; //!< Access mode for IPMI messaging
354 uint8_t privLevel; //!< Channel Privilege Level
355} __attribute__((packed));
356
357ipmi_ret_t ipmi_set_channel_access(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
358 ipmi_request_t request,
359 ipmi_response_t response,
360 ipmi_data_len_t data_len,
361 ipmi_context_t context)
362{
363 auto requestData =
364 reinterpret_cast<const SetChannelAccessRequest*>(request);
365
366 int channel = requestData->channelNumber;
367 // Validate the channel number corresponds to any of the network channel.
368 auto ethdevice = ipmi::network::ChanneltoEthernet(channel);
369 if (ethdevice.empty())
370 {
371 *data_len = 0;
372 return IPMI_CC_INVALID_FIELD_REQUEST;
373 }
374
375 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
376 // Bits[2:0] indicates the Access Mode, this mask field will extract the
377 // access mode from the command data.
378 static constexpr auto accessModeMask = 0x07;
379 auto accessMode = requestData->accessMode & accessModeMask;
380 static constexpr auto disabled = 0;
381 static constexpr auto enabled = 2;
382
383 try
384 {
385 if (accessMode == enabled)
386 {
387 enableNetworkIPMI(ethdevice);
388 }
389 else if (accessMode == disabled)
390 {
391 disableNetworkIPMI(ethdevice);
392 }
393 }
394 catch (const sdbusplus::exception::SdBusError& e)
395 {
396 log<level::ERR>(e.what());
397 *data_len = 0;
398 return IPMI_CC_UNSPECIFIED_ERROR;
399 }
400
401 return IPMI_CC_OK;
402}