blob: 283188c57afc90fb5cc2f75e784a7700b6af8dbc [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"
Patrick Venturec7c1c3c2017-11-15 14:29:18 -08005#include "net.hpp"
Patrick Venture5794fcf2017-10-26 11:11:14 -07006
7#include <string>
8#include <arpa/inet.h>
9
10#include <phosphor-logging/log.hpp>
11#include <phosphor-logging/elog-errors.hpp>
12#include "xyz/openbmc_project/Common/error.hpp"
13
Patrick Venture5794fcf2017-10-26 11:11:14 -070014constexpr auto ipv4Protocol = "xyz.openbmc_project.Network.IP.Protocol.IPv4";
15
16using namespace phosphor::logging;
17using namespace sdbusplus::xyz::openbmc_project::Common::Error;
18
Patrick Venturec7c1c3c2017-11-15 14:29:18 -080019/** @struct SetChannelAccessRequest
20 *
21 * IPMI payload for Set Channel access command request.
22 */
23struct SetChannelAccessRequest
24{
25 uint8_t channelNumber; //!< Channel number.
26 uint8_t setting; //!< The setting values.
27 uint8_t privilegeLevelLimit; //!< The Privilege Level Limit
28} __attribute__((packed));
29
Patrick Venture5794fcf2017-10-26 11:11:14 -070030/** @struct GetChannelAccessRequest
31 *
32 * IPMI payload for Get Channel access command request.
33 */
34struct GetChannelAccessRequest
35{
36 uint8_t channelNumber; //!< Channel number.
37 uint8_t volatileSetting; //!< Get non-volatile or the volatile setting.
38} __attribute__((packed));
39
40/** @struct GetChannelAccessResponse
41 *
42 * IPMI payload for Get Channel access command response.
43 */
44struct GetChannelAccessResponse
45{
46 uint8_t settings; //!< Channel settings.
47 uint8_t privilegeLimit; //!< Channel privilege level limit.
48} __attribute__((packed));
49
50ipmi_ret_t ipmi_set_channel_access(ipmi_netfn_t netfn,
51 ipmi_cmd_t cmd,
52 ipmi_request_t request,
53 ipmi_response_t response,
54 ipmi_data_len_t data_len,
55 ipmi_context_t context)
56{
57 ipmi_ret_t rc = IPMI_CC_OK;
58
59 std::string ipaddress;
60 std::string gateway;
61 uint8_t prefix {};
62 uint32_t vlanID {};
63 std::string networkInterfacePath;
64 ipmi::DbusObjectInfo ipObject;
65 ipmi::DbusObjectInfo systemObject;
66
Patrick Venturec7c1c3c2017-11-15 14:29:18 -080067 if (*data_len < sizeof(SetChannelAccessRequest))
68 {
69 return IPMI_CC_INVALID;
70 }
71
72 auto requestData = reinterpret_cast<const SetChannelAccessRequest*>
73 (request);
74 int channel = requestData->channelNumber & CHANNEL_MASK;
75 auto ethdevice = ipmi::network::ChanneltoEthernet(channel);
76 if (ethdevice.empty())
77 {
78 return IPMI_CC_INVALID_FIELD_REQUEST;
79 }
80 auto ethIp = ethdevice + "/" + ipmi::network::IP_TYPE;
81 auto channelConf = getChannelConfig(channel);
82
Patrick Venture5794fcf2017-10-26 11:11:14 -070083 // Todo: parse the request data if needed.
84 // Using Set Channel cmd to apply changes of Set Lan Cmd.
85 try
86 {
87 sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
88
89 log<level::INFO>("Network data from Cache",
Patrick Venturec7c1c3c2017-11-15 14:29:18 -080090 entry("PREFIX=%s", channelConf->netmask.c_str()),
91 entry("ADDRESS=%s", channelConf->ipaddr.c_str()),
92 entry("GATEWAY=%s", channelConf->gateway.c_str()),
93 entry("VLAN=%d", channelConf->vlanID),
94 entry("IPSRC=%d", channelConf->ipsrc));
Patrick Venture5794fcf2017-10-26 11:11:14 -070095
Patrick Venturec7c1c3c2017-11-15 14:29:18 -080096 if (channelConf->vlanID != ipmi::network::VLAN_ID_MASK)
Patrick Venture5794fcf2017-10-26 11:11:14 -070097 {
98 //get the first twelve bits which is vlan id
99 //not interested in rest of the bits.
Patrick Venturec7c1c3c2017-11-15 14:29:18 -0800100 channelConf->vlanID = le32toh(channelConf->vlanID);
101 vlanID = channelConf->vlanID & ipmi::network::VLAN_ID_MASK;
Patrick Venture5794fcf2017-10-26 11:11:14 -0700102 }
103
104 // if the asked ip src is DHCP then not interested in
105 // any given data except vlan.
Patrick Venturec7c1c3c2017-11-15 14:29:18 -0800106 if (channelConf->ipsrc != ipmi::network::IPOrigin::DHCP)
Patrick Venture5794fcf2017-10-26 11:11:14 -0700107 {
108 // always get the system object
109 systemObject = ipmi::getDbusObject(
110 bus,
111 ipmi::network::SYSTEMCONFIG_INTERFACE,
112 ipmi::network::ROOT);
113
114 // the below code is to determine the mode of the interface
115 // as the handling is same, if the system is configured with
116 // DHCP or user has given all the data.
117 try
118 {
119 ipmi::ObjectTree ancestorMap;
120
121 ipmi::InterfaceList interfaces {
122 ipmi::network::ETHERNET_INTERFACE };
123
124 // if the system is having ip object,then
125 // get the IP object.
Patrick Venturec7c1c3c2017-11-15 14:29:18 -0800126 ipObject = ipmi::getDbusObject(bus,
127 ipmi::network::IP_INTERFACE,
128 ipmi::network::ROOT,
129 ethIp);
Patrick Venture5794fcf2017-10-26 11:11:14 -0700130
131 // Get the parent interface of the IP object.
132 try
133 {
134 ancestorMap = ipmi::getAllAncestors(bus,
135 ipObject.first,
136 std::move(interfaces));
137 }
138 catch (InternalFailure& e)
139 {
140 // if unable to get the parent interface
141 // then commit the error and return.
142 log<level::ERR>("Unable to get the parent interface",
143 entry("PATH=%s", ipObject.first.c_str()),
144 entry("INTERFACE=%s",
145 ipmi::network::ETHERNET_INTERFACE));
146 commit<InternalFailure>();
147 rc = IPMI_CC_UNSPECIFIED_ERROR;
Patrick Venturec7c1c3c2017-11-15 14:29:18 -0800148 channelConf->clear();
Patrick Venture5794fcf2017-10-26 11:11:14 -0700149 return rc;
150 }
151
152 networkInterfacePath = ancestorMap.begin()->first;
153 }
154 catch (InternalFailure& e)
155 {
156 // TODO Currently IPMI supports single interface,need to handle
157 // Multiple interface through
158 // https://github.com/openbmc/openbmc/issues/2138
159
160 // if there is no ip configured on the system,then
161 // get the network interface object.
162 auto networkInterfaceObject = ipmi::getDbusObject(
163 bus,
164 ipmi::network::ETHERNET_INTERFACE,
165 ipmi::network::ROOT,
Patrick Venturec7c1c3c2017-11-15 14:29:18 -0800166 ethdevice);
Patrick Venture5794fcf2017-10-26 11:11:14 -0700167
168 networkInterfacePath = std::move(networkInterfaceObject.first);
169 }
170
171 // get the configured mode on the system.
172 auto enableDHCP = ipmi::getDbusProperty(
173 bus,
174 ipmi::network::SERVICE,
175 networkInterfacePath,
176 ipmi::network::ETHERNET_INTERFACE,
177 "DHCPEnabled").get<bool>();
178
Ratan Guptadfd84692017-12-19 18:03:23 +0530179 // if ip address source is not given then get the ip source mode
180 // from the system so that it can be applied later.
181 if (channelConf->ipsrc == ipmi::network::IPOrigin::UNSPECIFIED)
182 {
183 channelConf->ipsrc = (enableDHCP) ?
184 ipmi::network::IPOrigin::DHCP :
185 ipmi::network::IPOrigin::STATIC;
186 }
187
Patrick Venture5794fcf2017-10-26 11:11:14 -0700188 // check whether user has given all the data
189 // or the configured system interface is dhcp enabled,
190 // in both of the cases get the values from the cache.
Patrick Venturec7c1c3c2017-11-15 14:29:18 -0800191 if ((!channelConf->ipaddr.empty() &&
192 !channelConf->netmask.empty() &&
193 !channelConf->gateway.empty()) ||
Patrick Venture5794fcf2017-10-26 11:11:14 -0700194 (enableDHCP)) // configured system interface mode = DHCP
195 {
196 //convert mask into prefix
Patrick Venturec7c1c3c2017-11-15 14:29:18 -0800197 ipaddress = channelConf->ipaddr;
198 prefix = ipmi::network::toPrefix(AF_INET, channelConf->netmask);
199 gateway = channelConf->gateway;
Patrick Venture5794fcf2017-10-26 11:11:14 -0700200 }
201 else // asked ip src = static and configured system src = static
202 // or partially given data.
203 {
204 // We have partial filled cache so get the remaining
205 // info from the system.
206
207 // Get the network data from the system as user has
208 // not given all the data then use the data fetched from the
209 // system but it is implementation dependent,IPMI spec doesn't
210 // force it.
211
212 // if system is not having any ip object don't throw error,
213 try
214 {
215 auto properties = ipmi::getAllDbusProperties(
216 bus,
217 ipObject.second,
218 ipObject.first,
219 ipmi::network::IP_INTERFACE);
220
Patrick Venturec7c1c3c2017-11-15 14:29:18 -0800221 ipaddress = channelConf->ipaddr.empty() ?
Ratan Guptadd646202017-11-21 17:46:59 +0530222 properties["Address"].get<std::string>() :
Patrick Venturec7c1c3c2017-11-15 14:29:18 -0800223 channelConf->ipaddr;
Patrick Venture5794fcf2017-10-26 11:11:14 -0700224
Patrick Venturec7c1c3c2017-11-15 14:29:18 -0800225 prefix = channelConf->netmask.empty() ?
Patrick Venture5794fcf2017-10-26 11:11:14 -0700226 properties["PrefixLength"].get<uint8_t>() :
227 ipmi::network::toPrefix(AF_INET,
Patrick Venturec7c1c3c2017-11-15 14:29:18 -0800228 channelConf->netmask);
Patrick Venture5794fcf2017-10-26 11:11:14 -0700229 }
230 catch (InternalFailure& e)
231 {
232 log<level::INFO>("Failed to get IP object which matches",
233 entry("INTERFACE=%s", ipmi::network::IP_INTERFACE),
Patrick Venturec7c1c3c2017-11-15 14:29:18 -0800234 entry("MATCH=%s", ethIp));
Patrick Venture5794fcf2017-10-26 11:11:14 -0700235 }
236
237 auto systemProperties = ipmi::getAllDbusProperties(
238 bus,
239 systemObject.second,
240 systemObject.first,
241 ipmi::network::SYSTEMCONFIG_INTERFACE);
242
Patrick Venturec7c1c3c2017-11-15 14:29:18 -0800243 gateway = channelConf->gateway.empty() ?
Patrick Venture5794fcf2017-10-26 11:11:14 -0700244 systemProperties["DefaultGateway"].get<std::string>() :
Patrick Venturec7c1c3c2017-11-15 14:29:18 -0800245 channelConf->gateway;
Patrick Venture5794fcf2017-10-26 11:11:14 -0700246 }
247 }
248
249 // Currently network manager doesn't support purging of all the
250 // ip addresses and the vlan interfaces from the parent interface,
251 // TODO once the support is there, will make the change here.
252 // https://github.com/openbmc/openbmc/issues/2141.
253
254 // TODO Currently IPMI supports single interface,need to handle
255 // Multiple interface through
256 // https://github.com/openbmc/openbmc/issues/2138
257
258 // instead of deleting all the vlan interfaces and
259 // all the ipv4 address,we will call reset method.
260 //delete all the vlan interfaces
261
262 ipmi::deleteAllDbusObjects(bus,
263 ipmi::network::ROOT,
264 ipmi::network::VLAN_INTERFACE);
265
266 // set the interface mode to static
267 auto networkInterfaceObject = ipmi::getDbusObject(
268 bus,
269 ipmi::network::ETHERNET_INTERFACE,
270 ipmi::network::ROOT,
Patrick Venturec7c1c3c2017-11-15 14:29:18 -0800271 ethdevice);
Patrick Venture5794fcf2017-10-26 11:11:14 -0700272
273 // setting the physical interface mode to static.
274 ipmi::setDbusProperty(bus,
275 ipmi::network::SERVICE,
276 networkInterfaceObject.first,
277 ipmi::network::ETHERNET_INTERFACE,
278 "DHCPEnabled",
279 false);
280
281 networkInterfacePath = networkInterfaceObject.first;
282
283 //delete all the ipv4 addresses
284 ipmi::deleteAllDbusObjects(bus,
285 ipmi::network::ROOT,
286 ipmi::network::IP_INTERFACE,
Patrick Venturec7c1c3c2017-11-15 14:29:18 -0800287 ethIp);
Patrick Venture5794fcf2017-10-26 11:11:14 -0700288
289 if (vlanID)
290 {
291 ipmi::network::createVLAN(bus,
292 ipmi::network::SERVICE,
293 ipmi::network::ROOT,
Patrick Venturec7c1c3c2017-11-15 14:29:18 -0800294 ethdevice,
Patrick Venture5794fcf2017-10-26 11:11:14 -0700295 vlanID);
296
297 auto networkInterfaceObject = ipmi::getDbusObject(
298 bus,
299 ipmi::network::VLAN_INTERFACE,
300 ipmi::network::ROOT);
301
302 networkInterfacePath = networkInterfaceObject.first;
303 }
304
Patrick Venturec7c1c3c2017-11-15 14:29:18 -0800305 if (channelConf->ipsrc == ipmi::network::IPOrigin::DHCP)
Patrick Venture5794fcf2017-10-26 11:11:14 -0700306 {
307 ipmi::setDbusProperty(bus,
308 ipmi::network::SERVICE,
309 networkInterfacePath,
310 ipmi::network::ETHERNET_INTERFACE,
311 "DHCPEnabled",
312 true);
313 }
314 else
315 {
316 //change the mode to static
317 ipmi::setDbusProperty(bus,
318 ipmi::network::SERVICE,
319 networkInterfacePath,
320 ipmi::network::ETHERNET_INTERFACE,
321 "DHCPEnabled",
322 false);
323
324 if (!ipaddress.empty())
325 {
326 ipmi::network::createIP(bus,
327 ipmi::network::SERVICE,
328 networkInterfacePath,
329 ipv4Protocol,
330 ipaddress,
331 prefix);
332 }
333
334 if (!gateway.empty())
335 {
336 ipmi::setDbusProperty(bus,
337 systemObject.second,
338 systemObject.first,
339 ipmi::network::SYSTEMCONFIG_INTERFACE,
340 "DefaultGateway",
341 std::string(gateway));
342 }
343 }
344
345 }
346 catch (InternalFailure& e)
347 {
348 log<level::ERR>("Failed to set network data",
349 entry("PREFIX=%d", prefix),
350 entry("ADDRESS=%s", ipaddress.c_str()),
351 entry("GATEWAY=%s", gateway.c_str()),
352 entry("VLANID=%d", vlanID),
Patrick Venturec7c1c3c2017-11-15 14:29:18 -0800353 entry("IPSRC=%d", channelConf->ipsrc));
Patrick Venture5794fcf2017-10-26 11:11:14 -0700354
355 commit<InternalFailure>();
356 rc = IPMI_CC_UNSPECIFIED_ERROR;
357 }
358
Patrick Venturec7c1c3c2017-11-15 14:29:18 -0800359 channelConf->clear();
Patrick Venture5794fcf2017-10-26 11:11:14 -0700360 return rc;
361}
362
363ipmi_ret_t ipmi_get_channel_access(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
364 ipmi_request_t request, ipmi_response_t response,
365 ipmi_data_len_t data_len, ipmi_context_t context)
366{
367 auto requestData = reinterpret_cast<const GetChannelAccessRequest*>
368 (request);
369 std::vector<uint8_t> outPayload(sizeof(GetChannelAccessResponse));
370 auto responseData = reinterpret_cast<GetChannelAccessResponse*>
371 (outPayload.data());
372
Patrick Venture5794fcf2017-10-26 11:11:14 -0700373 /*
374 * The value Eh is used as a way to identify the current channel that
375 * the command is being received from.
376 */
377 constexpr auto channelE = 0x0E;
Patrick Venturec7c1c3c2017-11-15 14:29:18 -0800378 int channel = requestData->channelNumber;
379 auto ethdevice = ipmi::network::ChanneltoEthernet(channel);
Patrick Venture5794fcf2017-10-26 11:11:14 -0700380
Patrick Venturec7c1c3c2017-11-15 14:29:18 -0800381 if (channel != channelE && ethdevice.empty())
Patrick Venture5794fcf2017-10-26 11:11:14 -0700382 {
383 *data_len = 0;
384 return IPMI_CC_INVALID_FIELD_REQUEST;
385 }
386
387 /*
388 * [7:6] - reserved
389 * [5] - 1b = Alerting disabled
390 * [4] - 1b = per message authentication disabled
391 * [3] - 0b = User level authentication enabled
392 * [2:0] - 2h = always available
393 */
394 constexpr auto channelSetting = 0x32;
395
396 responseData->settings = channelSetting;
397 //Defaulting the channel privilege to administrator level.
398 responseData->privilegeLimit = PRIVILEGE_ADMIN;
399
400 *data_len = outPayload.size();
401 memcpy(response, outPayload.data(), *data_len);
402
403 return IPMI_CC_OK;
404}
405
406// ATTENTION: This ipmi function is very hardcoded on purpose
407// OpenBMC does not fully support IPMI. This command is useful
408// to have around because it enables testing of interfaces with
409// the IPMI tool.
410#define GET_CHANNEL_INFO_CHANNEL_OFFSET 0
411// IPMI Table 6-2
412#define IPMI_CHANNEL_TYPE_IPMB 1
413// IPMI Table 6-3
414#define IPMI_CHANNEL_MEDIUM_TYPE_OTHER 6
415
416ipmi_ret_t ipmi_app_channel_info(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
417 ipmi_request_t request, ipmi_response_t response,
418 ipmi_data_len_t data_len, ipmi_context_t context)
419{
420 ipmi_ret_t rc = IPMI_CC_OK;
421 uint8_t resp[] = {
422 1,
423 IPMI_CHANNEL_MEDIUM_TYPE_OTHER,
424 IPMI_CHANNEL_TYPE_IPMB,
425 1,0x41,0xA7,0x00,0,0};
426 uint8_t *p = (uint8_t*) request;
Patrick Venturec7c1c3c2017-11-15 14:29:18 -0800427 int channel = (*p) & CHANNEL_MASK;
428 std::string ethdevice = ipmi::network::ChanneltoEthernet(channel);
Patrick Venture5794fcf2017-10-26 11:11:14 -0700429
430 printf("IPMI APP GET CHANNEL INFO\n");
431
Patrick Venturec7c1c3c2017-11-15 14:29:18 -0800432 // The supported channels numbers are those which are configured.
Patrick Venture5794fcf2017-10-26 11:11:14 -0700433 // Channel Number E is used as way to identify the current channel
434 // that the command is being is received from.
Patrick Venturec7c1c3c2017-11-15 14:29:18 -0800435 if (channel != 0xe && ethdevice.empty()) {
Patrick Venture5794fcf2017-10-26 11:11:14 -0700436 rc = IPMI_CC_PARM_OUT_OF_RANGE;
437 *data_len = 0;
Patrick Venturec7c1c3c2017-11-15 14:29:18 -0800438 } else {
439 *data_len = sizeof(resp);
440 memcpy(response, resp, *data_len);
Patrick Venture5794fcf2017-10-26 11:11:14 -0700441 }
442
443 return rc;
444}