blob: 9db9ea84e5acff3630fe2b231cbe9c099f71fa40 [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>
14#include <string>
15#include <xyz/openbmc_project/Common/error.hpp>
Patrick Venture5794fcf2017-10-26 11:11:14 -070016
17using namespace phosphor::logging;
18using namespace sdbusplus::xyz::openbmc_project::Common::Error;
19
20/** @struct GetChannelAccessRequest
21 *
22 * IPMI payload for Get Channel access command request.
23 */
24struct GetChannelAccessRequest
25{
Patrick Venture0b02be92018-08-31 11:55:55 -070026 uint8_t channelNumber; //!< Channel number.
27 uint8_t volatileSetting; //!< Get non-volatile or the volatile setting.
Patrick Venture5794fcf2017-10-26 11:11:14 -070028} __attribute__((packed));
29
30/** @struct GetChannelAccessResponse
31 *
32 * IPMI payload for Get Channel access command response.
33 */
34struct GetChannelAccessResponse
35{
Patrick Venture0b02be92018-08-31 11:55:55 -070036 uint8_t settings; //!< Channel settings.
37 uint8_t privilegeLimit; //!< Channel privilege level limit.
Patrick Venture5794fcf2017-10-26 11:11:14 -070038} __attribute__((packed));
39
Patrick Venture5794fcf2017-10-26 11:11:14 -070040ipmi_ret_t ipmi_get_channel_access(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -070041 ipmi_request_t request,
42 ipmi_response_t response,
43 ipmi_data_len_t data_len,
44 ipmi_context_t context)
Patrick Venture5794fcf2017-10-26 11:11:14 -070045{
Patrick Venture0b02be92018-08-31 11:55:55 -070046 auto requestData =
47 reinterpret_cast<const GetChannelAccessRequest*>(request);
Patrick Venture5794fcf2017-10-26 11:11:14 -070048 std::vector<uint8_t> outPayload(sizeof(GetChannelAccessResponse));
Patrick Venture0b02be92018-08-31 11:55:55 -070049 auto responseData =
50 reinterpret_cast<GetChannelAccessResponse*>(outPayload.data());
Patrick Venture5794fcf2017-10-26 11:11:14 -070051
Patrick Venture5794fcf2017-10-26 11:11:14 -070052 /*
53 * The value Eh is used as a way to identify the current channel that
54 * the command is being received from.
55 */
56 constexpr auto channelE = 0x0E;
Patrick Venturec7c1c3c2017-11-15 14:29:18 -080057 int channel = requestData->channelNumber;
58 auto ethdevice = ipmi::network::ChanneltoEthernet(channel);
Patrick Venture5794fcf2017-10-26 11:11:14 -070059
Patrick Venturec7c1c3c2017-11-15 14:29:18 -080060 if (channel != channelE && ethdevice.empty())
Patrick Venture5794fcf2017-10-26 11:11:14 -070061 {
62 *data_len = 0;
63 return IPMI_CC_INVALID_FIELD_REQUEST;
64 }
65
66 /*
67 * [7:6] - reserved
68 * [5] - 1b = Alerting disabled
69 * [4] - 1b = per message authentication disabled
70 * [3] - 0b = User level authentication enabled
71 * [2:0] - 2h = always available
72 */
73 constexpr auto channelSetting = 0x32;
74
75 responseData->settings = channelSetting;
Patrick Venture0b02be92018-08-31 11:55:55 -070076 // Defaulting the channel privilege to administrator level.
Patrick Venture5794fcf2017-10-26 11:11:14 -070077 responseData->privilegeLimit = PRIVILEGE_ADMIN;
78
79 *data_len = outPayload.size();
80 memcpy(response, outPayload.data(), *data_len);
81
82 return IPMI_CC_OK;
83}
84
85// ATTENTION: This ipmi function is very hardcoded on purpose
86// OpenBMC does not fully support IPMI. This command is useful
87// to have around because it enables testing of interfaces with
88// the IPMI tool.
89#define GET_CHANNEL_INFO_CHANNEL_OFFSET 0
90// IPMI Table 6-2
91#define IPMI_CHANNEL_TYPE_IPMB 1
92// IPMI Table 6-3
93#define IPMI_CHANNEL_MEDIUM_TYPE_OTHER 6
94
95ipmi_ret_t ipmi_app_channel_info(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -070096 ipmi_request_t request,
97 ipmi_response_t response,
98 ipmi_data_len_t data_len,
99 ipmi_context_t context)
Patrick Venture5794fcf2017-10-26 11:11:14 -0700100{
101 ipmi_ret_t rc = IPMI_CC_OK;
Patrick Venturec49daa32018-10-17 15:56:43 -0700102 auto* p = static_cast<uint8_t*>(request);
Patrick Venturec7c1c3c2017-11-15 14:29:18 -0800103 int channel = (*p) & CHANNEL_MASK;
104 std::string ethdevice = ipmi::network::ChanneltoEthernet(channel);
Patrick Venture5794fcf2017-10-26 11:11:14 -0700105
Patrick Venturec7c1c3c2017-11-15 14:29:18 -0800106 // The supported channels numbers are those which are configured.
Patrick Venture5794fcf2017-10-26 11:11:14 -0700107 // Channel Number E is used as way to identify the current channel
108 // that the command is being is received from.
Patrick Venture0b02be92018-08-31 11:55:55 -0700109 if (channel != 0xe && ethdevice.empty())
110 {
Patrick Venture5794fcf2017-10-26 11:11:14 -0700111 rc = IPMI_CC_PARM_OUT_OF_RANGE;
112 *data_len = 0;
Patrick Venture0b02be92018-08-31 11:55:55 -0700113 }
114 else
115 {
Patrick Venture4491a462018-10-13 13:00:42 -0700116 uint8_t resp[] = {1,
117 IPMI_CHANNEL_MEDIUM_TYPE_OTHER,
118 IPMI_CHANNEL_TYPE_IPMB,
119 1,
120 0x41,
121 0xA7,
122 0x00,
123 0,
124 0};
125
Patrick Venturec7c1c3c2017-11-15 14:29:18 -0800126 *data_len = sizeof(resp);
127 memcpy(response, resp, *data_len);
Patrick Venture5794fcf2017-10-26 11:11:14 -0700128 }
129
130 return rc;
131}
Tom Joseph7cbe2282018-03-21 21:17:33 +0530132
133namespace cipher
134{
135
136/** @brief Get the supported Cipher records
137 *
138 * The cipher records are read from the JSON file and converted into cipher
139 * suite record format mentioned in the IPMI specification. The records can be
140 * either OEM or standard cipher. Each json entry is parsed and converted into
141 * the cipher record format and pushed into the vector.
142 *
143 * @return vector containing all the cipher suite records.
144 *
145 */
146std::vector<uint8_t> getCipherRecords()
147{
148 std::vector<uint8_t> records;
149
150 std::ifstream jsonFile(configFile);
151 if (!jsonFile.is_open())
152 {
153 log<level::ERR>("Channel Cipher suites file not found");
154 elog<InternalFailure>();
155 }
156
157 auto data = Json::parse(jsonFile, nullptr, false);
158 if (data.is_discarded())
159 {
160 log<level::ERR>("Parsing channel cipher suites JSON failed");
161 elog<InternalFailure>();
162 }
163
164 for (const auto& record : data)
165 {
166 if (record.find(oem) != record.end())
167 {
168 // OEM cipher suite - 0xC1
169 records.push_back(oemCipherSuite);
170 // Cipher Suite ID
171 records.push_back(record.value(cipher, 0));
172 // OEM IANA - 3 bytes
173 records.push_back(record.value(oem, 0));
174 records.push_back(record.value(oem, 0) >> 8);
175 records.push_back(record.value(oem, 0) >> 16);
Tom Joseph7cbe2282018-03-21 21:17:33 +0530176 }
177 else
178 {
179 // OEM cipher suite - 0xC0
180 records.push_back(stdCipherSuite);
181 // Cipher Suite ID
182 records.push_back(record.value(cipher, 0));
183 }
184
185 // Authentication algorithm number
186 records.push_back(record.value(auth, 0));
187 // Integrity algorithm number
188 records.push_back(record.value(integrity, 0) | integrityTag);
189 // Confidentiality algorithm number
190 records.push_back(record.value(conf, 0) | confTag);
191 }
192
193 return records;
194}
195
Patrick Venture0b02be92018-08-31 11:55:55 -0700196} // namespace cipher
Tom Joseph7cbe2282018-03-21 21:17:33 +0530197
Patrick Venture0b02be92018-08-31 11:55:55 -0700198ipmi_ret_t getChannelCipherSuites(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Tom Joseph7cbe2282018-03-21 21:17:33 +0530199 ipmi_request_t request,
200 ipmi_response_t response,
201 ipmi_data_len_t data_len,
202 ipmi_context_t context)
203{
204 static std::vector<uint8_t> records;
205 static auto recordInit = false;
206
207 auto requestData =
208 reinterpret_cast<const GetChannelCipherRequest*>(request);
209
Tom Joseph7cbe2282018-03-21 21:17:33 +0530210 if (*data_len < sizeof(GetChannelCipherRequest))
211 {
212 *data_len = 0;
213 return IPMI_CC_REQ_DATA_LEN_INVALID;
214 }
215
216 *data_len = 0;
217
218 // Support only for list algorithms by cipher suite
219 if (cipher::listCipherSuite !=
Patrick Venture0b02be92018-08-31 11:55:55 -0700220 (requestData->listIndex & cipher::listTypeMask))
Tom Joseph7cbe2282018-03-21 21:17:33 +0530221 {
222 return IPMI_CC_INVALID_FIELD_REQUEST;
223 }
224
225 if (!recordInit)
226 {
227 try
228 {
229 records = cipher::getCipherRecords();
230 recordInit = true;
231 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700232 catch (const std::exception& e)
Tom Joseph7cbe2282018-03-21 21:17:33 +0530233 {
234 return IPMI_CC_UNSPECIFIED_ERROR;
235 }
236 }
237
238 // List index(00h-3Fh), 0h selects the first set of 16, 1h selects the next
239 // set of 16 and so on.
Patrick Venture0b02be92018-08-31 11:55:55 -0700240 auto index =
241 static_cast<size_t>(requestData->listIndex & cipher::listIndexMask);
Tom Joseph7cbe2282018-03-21 21:17:33 +0530242
243 // Calculate the number of record data bytes to be returned.
244 auto start = std::min(index * cipher::respSize, records.size());
Patrick Venture0b02be92018-08-31 11:55:55 -0700245 auto end =
246 std::min((index * cipher::respSize) + cipher::respSize, records.size());
Tom Joseph7cbe2282018-03-21 21:17:33 +0530247 auto size = end - start;
248
Patrick Venture0b02be92018-08-31 11:55:55 -0700249 auto responseData = reinterpret_cast<GetChannelCipherRespHeader*>(response);
Tom Joseph7cbe2282018-03-21 21:17:33 +0530250 responseData->channelNumber = cipher::defaultChannelNumber;
251
252 if (!size)
253 {
254 *data_len = sizeof(GetChannelCipherRespHeader);
255 }
256 else
257 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700258 std::copy_n(records.data() + start, size,
Tom Joseph7cbe2282018-03-21 21:17:33 +0530259 static_cast<uint8_t*>(response) + 1);
260 *data_len = size + sizeof(GetChannelCipherRespHeader);
261 }
262
263 return IPMI_CC_OK;
264}
Tom Joseph13227682018-08-10 01:05:21 +0530265
266template <typename... ArgTypes>
267static int executeCmd(const char* path, ArgTypes&&... tArgs)
268{
269 boost::process::child execProg(path, const_cast<char*>(tArgs)...);
270 execProg.wait();
271 return execProg.exit_code();
272}
273
274/** @brief Enable the network IPMI service on the specified ethernet interface.
275 *
276 * @param[in] intf - ethernet interface on which to enable IPMI
277 */
278void enableNetworkIPMI(const std::string& intf)
279{
280 // Check if there is a iptable filter to drop IPMI packets for the
281 // interface.
282 auto retCode =
283 executeCmd("/usr/sbin/iptables", "-C", "INPUT", "-p", "udp", "-i",
284 intf.c_str(), "--dport", "623", "-j", "DROP");
285
286 // If the iptable filter exists, delete the filter.
287 if (!retCode)
288 {
289 auto response =
290 executeCmd("/usr/sbin/iptables", "-D", "INPUT", "-p", "udp", "-i",
291 intf.c_str(), "--dport", "623", "-j", "DROP");
292
293 if (response)
294 {
295 log<level::ERR>("Dropping the iptables filter failed",
296 entry("INTF=%s", intf.c_str()),
297 entry("RETURN_CODE:%d", response));
298 }
299 }
300}
301
302/** @brief Disable the network IPMI service on the specified ethernet interface.
303 *
304 * @param[in] intf - ethernet interface on which to disable IPMI
305 */
306void disableNetworkIPMI(const std::string& intf)
307{
308 // Check if there is a iptable filter to drop IPMI packets for the
309 // interface.
310 auto retCode =
311 executeCmd("/usr/sbin/iptables", "-C", "INPUT", "-p", "udp", "-i",
312 intf.c_str(), "--dport", "623", "-j", "DROP");
313
314 // If the iptable filter does not exist, add filter to drop network IPMI
315 // packets
316 if (retCode)
317 {
318 auto response =
319 executeCmd("/usr/sbin/iptables", "-I", "INPUT", "-p", "udp", "-i",
320 intf.c_str(), "--dport", "623", "-j", "DROP");
321
322 if (response)
323 {
324 log<level::ERR>("Inserting iptables filter failed",
325 entry("INTF=%s", intf.c_str()),
326 entry("RETURN_CODE:%d", response));
327 }
328 }
329}
330
331/** @struct SetChannelAccessRequest
332 *
333 * IPMI payload for Set Channel access command request.
334 */
335struct SetChannelAccessRequest
336{
337 uint8_t channelNumber; //!< Channel number
338 uint8_t accessMode; //!< Access mode for IPMI messaging
339 uint8_t privLevel; //!< Channel Privilege Level
340} __attribute__((packed));
341
342ipmi_ret_t ipmi_set_channel_access(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
343 ipmi_request_t request,
344 ipmi_response_t response,
345 ipmi_data_len_t data_len,
346 ipmi_context_t context)
347{
348 auto requestData =
349 reinterpret_cast<const SetChannelAccessRequest*>(request);
350
351 int channel = requestData->channelNumber;
352 // Validate the channel number corresponds to any of the network channel.
353 auto ethdevice = ipmi::network::ChanneltoEthernet(channel);
354 if (ethdevice.empty())
355 {
356 *data_len = 0;
357 return IPMI_CC_INVALID_FIELD_REQUEST;
358 }
359
360 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
361 // Bits[2:0] indicates the Access Mode, this mask field will extract the
362 // access mode from the command data.
363 static constexpr auto accessModeMask = 0x07;
364 auto accessMode = requestData->accessMode & accessModeMask;
365 static constexpr auto disabled = 0;
366 static constexpr auto enabled = 2;
367
368 try
369 {
370 if (accessMode == enabled)
371 {
372 enableNetworkIPMI(ethdevice);
373 }
374 else if (accessMode == disabled)
375 {
376 disableNetworkIPMI(ethdevice);
377 }
378 }
379 catch (const sdbusplus::exception::SdBusError& e)
380 {
381 log<level::ERR>(e.what());
382 *data_len = 0;
383 return IPMI_CC_UNSPECIFIED_ERROR;
384 }
385
386 return IPMI_CC_OK;
387}