blob: 6db987f991034dab1711d20ca6f8cbb163a58d0f [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 Venture0b02be92018-08-31 11:55:55 -0700102 uint8_t resp[] = {1,
103 IPMI_CHANNEL_MEDIUM_TYPE_OTHER,
104 IPMI_CHANNEL_TYPE_IPMB,
105 1,
106 0x41,
107 0xA7,
108 0x00,
109 0,
110 0};
111 uint8_t* p = (uint8_t*)request;
Patrick Venturec7c1c3c2017-11-15 14:29:18 -0800112 int channel = (*p) & CHANNEL_MASK;
113 std::string ethdevice = ipmi::network::ChanneltoEthernet(channel);
Patrick Venture5794fcf2017-10-26 11:11:14 -0700114
Patrick Venturec7c1c3c2017-11-15 14:29:18 -0800115 // The supported channels numbers are those which are configured.
Patrick Venture5794fcf2017-10-26 11:11:14 -0700116 // Channel Number E is used as way to identify the current channel
117 // that the command is being is received from.
Patrick Venture0b02be92018-08-31 11:55:55 -0700118 if (channel != 0xe && ethdevice.empty())
119 {
Patrick Venture5794fcf2017-10-26 11:11:14 -0700120 rc = IPMI_CC_PARM_OUT_OF_RANGE;
121 *data_len = 0;
Patrick Venture0b02be92018-08-31 11:55:55 -0700122 }
123 else
124 {
Patrick Venturec7c1c3c2017-11-15 14:29:18 -0800125 *data_len = sizeof(resp);
126 memcpy(response, resp, *data_len);
Patrick Venture5794fcf2017-10-26 11:11:14 -0700127 }
128
129 return rc;
130}
Tom Joseph7cbe2282018-03-21 21:17:33 +0530131
132namespace cipher
133{
134
135/** @brief Get the supported Cipher records
136 *
137 * The cipher records are read from the JSON file and converted into cipher
138 * suite record format mentioned in the IPMI specification. The records can be
139 * either OEM or standard cipher. Each json entry is parsed and converted into
140 * the cipher record format and pushed into the vector.
141 *
142 * @return vector containing all the cipher suite records.
143 *
144 */
145std::vector<uint8_t> getCipherRecords()
146{
147 std::vector<uint8_t> records;
148
149 std::ifstream jsonFile(configFile);
150 if (!jsonFile.is_open())
151 {
152 log<level::ERR>("Channel Cipher suites file not found");
153 elog<InternalFailure>();
154 }
155
156 auto data = Json::parse(jsonFile, nullptr, false);
157 if (data.is_discarded())
158 {
159 log<level::ERR>("Parsing channel cipher suites JSON failed");
160 elog<InternalFailure>();
161 }
162
163 for (const auto& record : data)
164 {
165 if (record.find(oem) != record.end())
166 {
167 // OEM cipher suite - 0xC1
168 records.push_back(oemCipherSuite);
169 // Cipher Suite ID
170 records.push_back(record.value(cipher, 0));
171 // OEM IANA - 3 bytes
172 records.push_back(record.value(oem, 0));
173 records.push_back(record.value(oem, 0) >> 8);
174 records.push_back(record.value(oem, 0) >> 16);
Tom Joseph7cbe2282018-03-21 21:17:33 +0530175 }
176 else
177 {
178 // OEM cipher suite - 0xC0
179 records.push_back(stdCipherSuite);
180 // Cipher Suite ID
181 records.push_back(record.value(cipher, 0));
182 }
183
184 // Authentication algorithm number
185 records.push_back(record.value(auth, 0));
186 // Integrity algorithm number
187 records.push_back(record.value(integrity, 0) | integrityTag);
188 // Confidentiality algorithm number
189 records.push_back(record.value(conf, 0) | confTag);
190 }
191
192 return records;
193}
194
Patrick Venture0b02be92018-08-31 11:55:55 -0700195} // namespace cipher
Tom Joseph7cbe2282018-03-21 21:17:33 +0530196
Patrick Venture0b02be92018-08-31 11:55:55 -0700197ipmi_ret_t getChannelCipherSuites(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Tom Joseph7cbe2282018-03-21 21:17:33 +0530198 ipmi_request_t request,
199 ipmi_response_t response,
200 ipmi_data_len_t data_len,
201 ipmi_context_t context)
202{
203 static std::vector<uint8_t> records;
204 static auto recordInit = false;
205
206 auto requestData =
207 reinterpret_cast<const GetChannelCipherRequest*>(request);
208
Tom Joseph7cbe2282018-03-21 21:17:33 +0530209 if (*data_len < sizeof(GetChannelCipherRequest))
210 {
211 *data_len = 0;
212 return IPMI_CC_REQ_DATA_LEN_INVALID;
213 }
214
215 *data_len = 0;
216
217 // Support only for list algorithms by cipher suite
218 if (cipher::listCipherSuite !=
Patrick Venture0b02be92018-08-31 11:55:55 -0700219 (requestData->listIndex & cipher::listTypeMask))
Tom Joseph7cbe2282018-03-21 21:17:33 +0530220 {
221 return IPMI_CC_INVALID_FIELD_REQUEST;
222 }
223
224 if (!recordInit)
225 {
226 try
227 {
228 records = cipher::getCipherRecords();
229 recordInit = true;
230 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700231 catch (const std::exception& e)
Tom Joseph7cbe2282018-03-21 21:17:33 +0530232 {
233 return IPMI_CC_UNSPECIFIED_ERROR;
234 }
235 }
236
237 // List index(00h-3Fh), 0h selects the first set of 16, 1h selects the next
238 // set of 16 and so on.
Patrick Venture0b02be92018-08-31 11:55:55 -0700239 auto index =
240 static_cast<size_t>(requestData->listIndex & cipher::listIndexMask);
Tom Joseph7cbe2282018-03-21 21:17:33 +0530241
242 // Calculate the number of record data bytes to be returned.
243 auto start = std::min(index * cipher::respSize, records.size());
Patrick Venture0b02be92018-08-31 11:55:55 -0700244 auto end =
245 std::min((index * cipher::respSize) + cipher::respSize, records.size());
Tom Joseph7cbe2282018-03-21 21:17:33 +0530246 auto size = end - start;
247
Patrick Venture0b02be92018-08-31 11:55:55 -0700248 auto responseData = reinterpret_cast<GetChannelCipherRespHeader*>(response);
Tom Joseph7cbe2282018-03-21 21:17:33 +0530249 responseData->channelNumber = cipher::defaultChannelNumber;
250
251 if (!size)
252 {
253 *data_len = sizeof(GetChannelCipherRespHeader);
254 }
255 else
256 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700257 std::copy_n(records.data() + start, size,
Tom Joseph7cbe2282018-03-21 21:17:33 +0530258 static_cast<uint8_t*>(response) + 1);
259 *data_len = size + sizeof(GetChannelCipherRespHeader);
260 }
261
262 return IPMI_CC_OK;
263}
Tom Joseph13227682018-08-10 01:05:21 +0530264
265template <typename... ArgTypes>
266static int executeCmd(const char* path, ArgTypes&&... tArgs)
267{
268 boost::process::child execProg(path, const_cast<char*>(tArgs)...);
269 execProg.wait();
270 return execProg.exit_code();
271}
272
273/** @brief Enable the network IPMI service on the specified ethernet interface.
274 *
275 * @param[in] intf - ethernet interface on which to enable IPMI
276 */
277void enableNetworkIPMI(const std::string& intf)
278{
279 // Check if there is a iptable filter to drop IPMI packets for the
280 // interface.
281 auto retCode =
282 executeCmd("/usr/sbin/iptables", "-C", "INPUT", "-p", "udp", "-i",
283 intf.c_str(), "--dport", "623", "-j", "DROP");
284
285 // If the iptable filter exists, delete the filter.
286 if (!retCode)
287 {
288 auto response =
289 executeCmd("/usr/sbin/iptables", "-D", "INPUT", "-p", "udp", "-i",
290 intf.c_str(), "--dport", "623", "-j", "DROP");
291
292 if (response)
293 {
294 log<level::ERR>("Dropping the iptables filter failed",
295 entry("INTF=%s", intf.c_str()),
296 entry("RETURN_CODE:%d", response));
297 }
298 }
299}
300
301/** @brief Disable the network IPMI service on the specified ethernet interface.
302 *
303 * @param[in] intf - ethernet interface on which to disable IPMI
304 */
305void disableNetworkIPMI(const std::string& intf)
306{
307 // Check if there is a iptable filter to drop IPMI packets for the
308 // interface.
309 auto retCode =
310 executeCmd("/usr/sbin/iptables", "-C", "INPUT", "-p", "udp", "-i",
311 intf.c_str(), "--dport", "623", "-j", "DROP");
312
313 // If the iptable filter does not exist, add filter to drop network IPMI
314 // packets
315 if (retCode)
316 {
317 auto response =
318 executeCmd("/usr/sbin/iptables", "-I", "INPUT", "-p", "udp", "-i",
319 intf.c_str(), "--dport", "623", "-j", "DROP");
320
321 if (response)
322 {
323 log<level::ERR>("Inserting iptables filter failed",
324 entry("INTF=%s", intf.c_str()),
325 entry("RETURN_CODE:%d", response));
326 }
327 }
328}
329
330/** @struct SetChannelAccessRequest
331 *
332 * IPMI payload for Set Channel access command request.
333 */
334struct SetChannelAccessRequest
335{
336 uint8_t channelNumber; //!< Channel number
337 uint8_t accessMode; //!< Access mode for IPMI messaging
338 uint8_t privLevel; //!< Channel Privilege Level
339} __attribute__((packed));
340
341ipmi_ret_t ipmi_set_channel_access(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
342 ipmi_request_t request,
343 ipmi_response_t response,
344 ipmi_data_len_t data_len,
345 ipmi_context_t context)
346{
347 auto requestData =
348 reinterpret_cast<const SetChannelAccessRequest*>(request);
349
350 int channel = requestData->channelNumber;
351 // Validate the channel number corresponds to any of the network channel.
352 auto ethdevice = ipmi::network::ChanneltoEthernet(channel);
353 if (ethdevice.empty())
354 {
355 *data_len = 0;
356 return IPMI_CC_INVALID_FIELD_REQUEST;
357 }
358
359 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
360 // Bits[2:0] indicates the Access Mode, this mask field will extract the
361 // access mode from the command data.
362 static constexpr auto accessModeMask = 0x07;
363 auto accessMode = requestData->accessMode & accessModeMask;
364 static constexpr auto disabled = 0;
365 static constexpr auto enabled = 2;
366
367 try
368 {
369 if (accessMode == enabled)
370 {
371 enableNetworkIPMI(ethdevice);
372 }
373 else if (accessMode == disabled)
374 {
375 disableNetworkIPMI(ethdevice);
376 }
377 }
378 catch (const sdbusplus::exception::SdBusError& e)
379 {
380 log<level::ERR>(e.what());
381 *data_len = 0;
382 return IPMI_CC_UNSPECIFIED_ERROR;
383 }
384
385 return IPMI_CC_OK;
386}