blob: 72f1571c68a4e5185e4acd3f630db449ac874e47 [file] [log] [blame]
Patrick Venture5794fcf2017-10-26 11:11:14 -07001#include "channel.hpp"
2#include "types.hpp"
3#include "transporthandler.hpp"
4#include "utils.hpp"
5
6#include <string>
7#include <arpa/inet.h>
8
9#include <phosphor-logging/log.hpp>
10#include <phosphor-logging/elog-errors.hpp>
11#include "xyz/openbmc_project/Common/error.hpp"
12
13extern struct ChannelConfig_t channelConfig;
14
15constexpr auto ipv4Protocol = "xyz.openbmc_project.Network.IP.Protocol.IPv4";
16
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{
26 uint8_t channelNumber; //!< Channel number.
27 uint8_t volatileSetting; //!< Get non-volatile or the volatile setting.
28} __attribute__((packed));
29
30/** @struct GetChannelAccessResponse
31 *
32 * IPMI payload for Get Channel access command response.
33 */
34struct GetChannelAccessResponse
35{
36 uint8_t settings; //!< Channel settings.
37 uint8_t privilegeLimit; //!< Channel privilege level limit.
38} __attribute__((packed));
39
40ipmi_ret_t ipmi_set_channel_access(ipmi_netfn_t netfn,
41 ipmi_cmd_t cmd,
42 ipmi_request_t request,
43 ipmi_response_t response,
44 ipmi_data_len_t data_len,
45 ipmi_context_t context)
46{
47 ipmi_ret_t rc = IPMI_CC_OK;
48
49 std::string ipaddress;
50 std::string gateway;
51 uint8_t prefix {};
52 uint32_t vlanID {};
53 std::string networkInterfacePath;
54 ipmi::DbusObjectInfo ipObject;
55 ipmi::DbusObjectInfo systemObject;
56
57 // Todo: parse the request data if needed.
58 // Using Set Channel cmd to apply changes of Set Lan Cmd.
59 try
60 {
61 sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
62
63 log<level::INFO>("Network data from Cache",
64 entry("PREFIX=%s", channelConfig.netmask.c_str()),
65 entry("ADDRESS=%s", channelConfig.ipaddr.c_str()),
66 entry("GATEWAY=%s", channelConfig.gateway.c_str()),
67 entry("VLAN=%d", channelConfig.vlanID),
68 entry("IPSRC=%d", channelConfig.ipsrc));
69
70 if (channelConfig.vlanID != ipmi::network::VLAN_ID_MASK)
71 {
72 //get the first twelve bits which is vlan id
73 //not interested in rest of the bits.
74 channelConfig.vlanID = le32toh(channelConfig.vlanID);
75 vlanID = channelConfig.vlanID & ipmi::network::VLAN_ID_MASK;
76 }
77
78 // if the asked ip src is DHCP then not interested in
79 // any given data except vlan.
80 if (channelConfig.ipsrc != ipmi::network::IPOrigin::DHCP)
81 {
82 // always get the system object
83 systemObject = ipmi::getDbusObject(
84 bus,
85 ipmi::network::SYSTEMCONFIG_INTERFACE,
86 ipmi::network::ROOT);
87
88 // the below code is to determine the mode of the interface
89 // as the handling is same, if the system is configured with
90 // DHCP or user has given all the data.
91 try
92 {
93 ipmi::ObjectTree ancestorMap;
94
95 ipmi::InterfaceList interfaces {
96 ipmi::network::ETHERNET_INTERFACE };
97
98 // if the system is having ip object,then
99 // get the IP object.
100 ipObject = ipmi::getDbusObject(bus,
101 ipmi::network::IP_INTERFACE,
102 ipmi::network::ROOT,
103 ipmi::network::IP_TYPE);
104
105 // Get the parent interface of the IP object.
106 try
107 {
108 ancestorMap = ipmi::getAllAncestors(bus,
109 ipObject.first,
110 std::move(interfaces));
111 }
112 catch (InternalFailure& e)
113 {
114 // if unable to get the parent interface
115 // then commit the error and return.
116 log<level::ERR>("Unable to get the parent interface",
117 entry("PATH=%s", ipObject.first.c_str()),
118 entry("INTERFACE=%s",
119 ipmi::network::ETHERNET_INTERFACE));
120 commit<InternalFailure>();
121 rc = IPMI_CC_UNSPECIFIED_ERROR;
122 channelConfig.clear();
123 return rc;
124 }
125
126 networkInterfacePath = ancestorMap.begin()->first;
127 }
128 catch (InternalFailure& e)
129 {
130 // TODO Currently IPMI supports single interface,need to handle
131 // Multiple interface through
132 // https://github.com/openbmc/openbmc/issues/2138
133
134 // if there is no ip configured on the system,then
135 // get the network interface object.
136 auto networkInterfaceObject = ipmi::getDbusObject(
137 bus,
138 ipmi::network::ETHERNET_INTERFACE,
139 ipmi::network::ROOT,
140 ipmi::network::INTERFACE);
141
142 networkInterfacePath = std::move(networkInterfaceObject.first);
143 }
144
145 // get the configured mode on the system.
146 auto enableDHCP = ipmi::getDbusProperty(
147 bus,
148 ipmi::network::SERVICE,
149 networkInterfacePath,
150 ipmi::network::ETHERNET_INTERFACE,
151 "DHCPEnabled").get<bool>();
152
153 // check whether user has given all the data
154 // or the configured system interface is dhcp enabled,
155 // in both of the cases get the values from the cache.
156 if ((!channelConfig.ipaddr.empty() &&
157 !channelConfig.netmask.empty() &&
158 !channelConfig.gateway.empty()) ||
159 (enableDHCP)) // configured system interface mode = DHCP
160 {
161 //convert mask into prefix
162 ipaddress = channelConfig.ipaddr;
163 prefix = ipmi::network::toPrefix(AF_INET, channelConfig.netmask);
164 gateway = channelConfig.gateway;
165 if (channelConfig.vlanID != ipmi::network::VLAN_ID_MASK)
166 {
167 //get the first twelve bits which is vlan id
168 //not interested in rest of the bits.
169 channelConfig.vlanID = le32toh(channelConfig.vlanID);
170 vlanID = channelConfig.vlanID & ipmi::network::VLAN_ID_MASK;
171 }
172 else
173 {
174 vlanID = ipmi::network::getVLAN(networkInterfacePath);
175 }
176
177 }
178 else // asked ip src = static and configured system src = static
179 // or partially given data.
180 {
181 // We have partial filled cache so get the remaining
182 // info from the system.
183
184 // Get the network data from the system as user has
185 // not given all the data then use the data fetched from the
186 // system but it is implementation dependent,IPMI spec doesn't
187 // force it.
188
189 // if system is not having any ip object don't throw error,
190 try
191 {
192 auto properties = ipmi::getAllDbusProperties(
193 bus,
194 ipObject.second,
195 ipObject.first,
196 ipmi::network::IP_INTERFACE);
197
198 ipaddress = channelConfig.ipaddr.empty() ?
199 ipmi::getIPAddress(bus,
200 ipmi::network::IP_INTERFACE,
201 ipmi::network::ROOT,
202 ipmi::network::IP_TYPE) :
203 channelConfig.ipaddr;
204
205 prefix = channelConfig.netmask.empty() ?
206 properties["PrefixLength"].get<uint8_t>() :
207 ipmi::network::toPrefix(AF_INET,
208 channelConfig.netmask);
209
210 }
211 catch (InternalFailure& e)
212 {
213 log<level::INFO>("Failed to get IP object which matches",
214 entry("INTERFACE=%s", ipmi::network::IP_INTERFACE),
215 entry("MATCH=%s", ipmi::network::IP_TYPE));
216 }
217
218 auto systemProperties = ipmi::getAllDbusProperties(
219 bus,
220 systemObject.second,
221 systemObject.first,
222 ipmi::network::SYSTEMCONFIG_INTERFACE);
223
224 gateway = channelConfig.gateway.empty() ?
225 systemProperties["DefaultGateway"].get<std::string>() :
226 channelConfig.gateway;
227
228 }
229 }
230
231 // Currently network manager doesn't support purging of all the
232 // ip addresses and the vlan interfaces from the parent interface,
233 // TODO once the support is there, will make the change here.
234 // https://github.com/openbmc/openbmc/issues/2141.
235
236 // TODO Currently IPMI supports single interface,need to handle
237 // Multiple interface through
238 // https://github.com/openbmc/openbmc/issues/2138
239
240 // instead of deleting all the vlan interfaces and
241 // all the ipv4 address,we will call reset method.
242 //delete all the vlan interfaces
243
244 ipmi::deleteAllDbusObjects(bus,
245 ipmi::network::ROOT,
246 ipmi::network::VLAN_INTERFACE);
247
248 // set the interface mode to static
249 auto networkInterfaceObject = ipmi::getDbusObject(
250 bus,
251 ipmi::network::ETHERNET_INTERFACE,
252 ipmi::network::ROOT,
253 ipmi::network::INTERFACE);
254
255 // setting the physical interface mode to static.
256 ipmi::setDbusProperty(bus,
257 ipmi::network::SERVICE,
258 networkInterfaceObject.first,
259 ipmi::network::ETHERNET_INTERFACE,
260 "DHCPEnabled",
261 false);
262
263 networkInterfacePath = networkInterfaceObject.first;
264
265 //delete all the ipv4 addresses
266 ipmi::deleteAllDbusObjects(bus,
267 ipmi::network::ROOT,
268 ipmi::network::IP_INTERFACE,
269 ipmi::network::IP_TYPE);
270
271 if (vlanID)
272 {
273 ipmi::network::createVLAN(bus,
274 ipmi::network::SERVICE,
275 ipmi::network::ROOT,
276 ipmi::network::INTERFACE,
277 vlanID);
278
279 auto networkInterfaceObject = ipmi::getDbusObject(
280 bus,
281 ipmi::network::VLAN_INTERFACE,
282 ipmi::network::ROOT);
283
284 networkInterfacePath = networkInterfaceObject.first;
285 }
286
287 if (channelConfig.ipsrc == ipmi::network::IPOrigin::DHCP)
288 {
289 ipmi::setDbusProperty(bus,
290 ipmi::network::SERVICE,
291 networkInterfacePath,
292 ipmi::network::ETHERNET_INTERFACE,
293 "DHCPEnabled",
294 true);
295 }
296 else
297 {
298 //change the mode to static
299 ipmi::setDbusProperty(bus,
300 ipmi::network::SERVICE,
301 networkInterfacePath,
302 ipmi::network::ETHERNET_INTERFACE,
303 "DHCPEnabled",
304 false);
305
306 if (!ipaddress.empty())
307 {
308 ipmi::network::createIP(bus,
309 ipmi::network::SERVICE,
310 networkInterfacePath,
311 ipv4Protocol,
312 ipaddress,
313 prefix);
314 }
315
316 if (!gateway.empty())
317 {
318 ipmi::setDbusProperty(bus,
319 systemObject.second,
320 systemObject.first,
321 ipmi::network::SYSTEMCONFIG_INTERFACE,
322 "DefaultGateway",
323 std::string(gateway));
324 }
325 }
326
327 }
328 catch (InternalFailure& e)
329 {
330 log<level::ERR>("Failed to set network data",
331 entry("PREFIX=%d", prefix),
332 entry("ADDRESS=%s", ipaddress.c_str()),
333 entry("GATEWAY=%s", gateway.c_str()),
334 entry("VLANID=%d", vlanID),
335 entry("IPSRC=%d", channelConfig.ipsrc));
336
337 commit<InternalFailure>();
338 rc = IPMI_CC_UNSPECIFIED_ERROR;
339 }
340
341 channelConfig.clear();
342 return rc;
343}
344
345ipmi_ret_t ipmi_get_channel_access(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
346 ipmi_request_t request, ipmi_response_t response,
347 ipmi_data_len_t data_len, ipmi_context_t context)
348{
349 auto requestData = reinterpret_cast<const GetChannelAccessRequest*>
350 (request);
351 std::vector<uint8_t> outPayload(sizeof(GetChannelAccessResponse));
352 auto responseData = reinterpret_cast<GetChannelAccessResponse*>
353 (outPayload.data());
354
355 // Channel 1 is arbitrarily assigned to ETH0 channel
356 constexpr auto channelOne = 0x01;
357
358 /*
359 * The value Eh is used as a way to identify the current channel that
360 * the command is being received from.
361 */
362 constexpr auto channelE = 0x0E;
363
364 if (requestData->channelNumber != channelOne &&
365 requestData->channelNumber != channelE)
366 {
367 *data_len = 0;
368 return IPMI_CC_INVALID_FIELD_REQUEST;
369 }
370
371 /*
372 * [7:6] - reserved
373 * [5] - 1b = Alerting disabled
374 * [4] - 1b = per message authentication disabled
375 * [3] - 0b = User level authentication enabled
376 * [2:0] - 2h = always available
377 */
378 constexpr auto channelSetting = 0x32;
379
380 responseData->settings = channelSetting;
381 //Defaulting the channel privilege to administrator level.
382 responseData->privilegeLimit = PRIVILEGE_ADMIN;
383
384 *data_len = outPayload.size();
385 memcpy(response, outPayload.data(), *data_len);
386
387 return IPMI_CC_OK;
388}
389
390// ATTENTION: This ipmi function is very hardcoded on purpose
391// OpenBMC does not fully support IPMI. This command is useful
392// to have around because it enables testing of interfaces with
393// the IPMI tool.
394#define GET_CHANNEL_INFO_CHANNEL_OFFSET 0
395// IPMI Table 6-2
396#define IPMI_CHANNEL_TYPE_IPMB 1
397// IPMI Table 6-3
398#define IPMI_CHANNEL_MEDIUM_TYPE_OTHER 6
399
400ipmi_ret_t ipmi_app_channel_info(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
401 ipmi_request_t request, ipmi_response_t response,
402 ipmi_data_len_t data_len, ipmi_context_t context)
403{
404 ipmi_ret_t rc = IPMI_CC_OK;
405 uint8_t resp[] = {
406 1,
407 IPMI_CHANNEL_MEDIUM_TYPE_OTHER,
408 IPMI_CHANNEL_TYPE_IPMB,
409 1,0x41,0xA7,0x00,0,0};
410 uint8_t *p = (uint8_t*) request;
411
412 printf("IPMI APP GET CHANNEL INFO\n");
413
414 // The supported channels numbers are 1 and 8.
415 // Channel Number E is used as way to identify the current channel
416 // that the command is being is received from.
417 if (*p == 0xe || *p == 1 || *p == 8) {
418
419 *data_len = sizeof(resp);
420 memcpy(response, resp, *data_len);
421
422 } else {
423 rc = IPMI_CC_PARM_OUT_OF_RANGE;
424 *data_len = 0;
425 }
426
427 return rc;
428}