Added IPMB dual bridge support (no need for driver support)

This commit is contained in:
Francois Isabelle 2010-01-20 14:52:29 +00:00
parent 50c514e336
commit e41a6aacb3
4 changed files with 336 additions and 122 deletions

View File

@ -131,6 +131,109 @@ no problems. The IPMIv1.5 interface will attempt to use OpenSSL for MD5
hash function at compile time but if that is not found it will use an hash function at compile time but if that is not found it will use an
internal library. internal library.
IPMB Dual Bridging in IPMITOOL
-------------------------------
IPMI offers a standard messaging interface.
The following concepts are related to this messaging interface:
Channel type : Communication channel type (SMS/KCS, IPMB, LAN)
Channel number : Channel descritor
Requester : Address of the requester
Responder : Address of the responder
NetFN : The logical function for the request/response.
Command : The command number
Sequence : An ID identifiying the request/response pair
Message tracking : The ability to match request/response pair.
When a communication is issued through any of the channels, an application
formats a request and expect a response.
Direct Command
--------------
The simplest form of communication is a "direct command" using SMS/KCS
Example:
ipmitool raw 6 4
55 00
This send raw command 4 (selftest) from netfn 6(application) to KCS, the driver
takes care of 'message tracking' and provides the answer.
Hopefuly, the application also includes a "human readable" instance of the API:
ipmitool mc selftest
Selftest: passed
Bridged Command
---------------
One slightly more complicated communication mode is the so-called
"bridged command" using IPMB.
Example:
ipmitool -m 0x94 -t 0x9a raw 6 4
55 00
or
ipmitool -m 0x94 -t 0x9a mc selftest
Selftest: passed
This still sends the same command 4 (selftest) from netfn 6(application) to
the target. However, to do so, the command is encapsulated (by the driver) and
sent using the command 0x34 (send message) from netfn 6(application) to KCS.
Then KCS is polled by the driver until a message has been received, then the
driver uses command 0x33 (get message). The driver also tracks the message
and makes sure the response matches the request. Then it decapsultates the
message and gives the response back to the application.
Dual Bridged Command
--------------------
Things get a little more ugly when the application needs to reach a management
controller sitting on an interface (or channel) not directly connected to the
BMC/IPMC. In the case the application must encapsulate its message itself and
request the IPMC to deal with message tracking itself.
Its been working well with IPMITOOL on the LAN interface with:
ipmitool -H <ip> -U <user> -P <password> -B 0 -T 0x8a -m 0x20 -t 0x7a -b 7
mc selftest
However, trying to dual bridge commands locally with :
ipmitool -B 0 -T 0x9a -m 0x94 -t 0x7a -b 7 mc selftest didn't work
(it returned the same data as ipmitool -m 0x20 -t 0x7a -b 7 mc selftest )
The reason was that the "openipmi" interface pluging didn't
encapsulate/decapsulate the message and didn't even detect the intent
to double bridge the request.
./src/ipmitool -B 0 -T 0x8a -m 0x94 -t 0x7a -b 7 mc selftest
-B 0 : transit channel for first bridge level (channel 0: IPMB-0)
-T 0x8a : transit destination address (remote IPMC address)
-m 0x94 : source address (local IPMC address on IPMB-0)
-t 0x7a : remote target (AMC IPMB-L address)
-b 7 : remote channel (channel 7: IPMB-L)
The transit source address (remote IPMC address on remote channel) is
automatically assigned by the remote IPMC.
Payload Size Limit
------------------
Vecause some commands return a lot of data (fru read/get sdr) and because 2
levels of encapsulation are used, some command will fail.
For instance this works.
ipmitool -H <ip> -U <user> -P <password> -B 0 -T 0x8a -m 0x94 -t 0x7a -b 7
mc selftest
but this does not:
ipmitool -H <ip> -U <user> -P <password> -B 0 -T 0x8a -m 0x94 -t 0x7a -b 7
fru print.
Usage Usage
===== =====

View File

@ -67,6 +67,7 @@
#include <ipmitool/ipmi_pef.h> #include <ipmitool/ipmi_pef.h>
#include <ipmitool/ipmi_oem.h> #include <ipmitool/ipmi_oem.h>
#include <ipmitool/ipmi_ekanalyzer.h> #include <ipmitool/ipmi_ekanalyzer.h>
#include <ipmitool/ipmi_picmg.h>
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
# include <config.h> # include <config.h>
@ -242,6 +243,7 @@ ipmi_option_usage(const char * progname, struct ipmi_cmd * cmdlist, struct ipmi_
lprintf(LOG_NOTICE, " -m address Set local IPMB address"); lprintf(LOG_NOTICE, " -m address Set local IPMB address");
lprintf(LOG_NOTICE, " -b channel Set destination channel for bridged request"); lprintf(LOG_NOTICE, " -b channel Set destination channel for bridged request");
lprintf(LOG_NOTICE, " -t address Bridge request to remote target address"); lprintf(LOG_NOTICE, " -t address Bridge request to remote target address");
lprintf(LOG_NOTICE, " -M address Set transit local address for bridge request(dual bridge)");
lprintf(LOG_NOTICE, " -B channel Set transit channel for bridged request (dual bridge)"); lprintf(LOG_NOTICE, " -B channel Set transit channel for bridged request (dual bridge)");
lprintf(LOG_NOTICE, " -T address Set transit address for bridge request (dual bridge)"); lprintf(LOG_NOTICE, " -T address Set transit address for bridge request (dual bridge)");
lprintf(LOG_NOTICE, " -l lun Set destination lun for raw commands"); lprintf(LOG_NOTICE, " -l lun Set destination lun for raw commands");
@ -355,6 +357,7 @@ ipmi_main(int argc, char ** argv,
int privlvl = 0; int privlvl = 0;
uint8_t target_addr = 0; uint8_t target_addr = 0;
uint8_t target_channel = 0; uint8_t target_channel = 0;
uint8_t transit_addr = 0; uint8_t transit_addr = 0;
uint8_t transit_channel = 0; uint8_t transit_channel = 0;
uint8_t target_lun = 0; uint8_t target_lun = 0;
@ -721,9 +724,72 @@ ipmi_main(int argc, char ** argv,
ipmi_main_intf->devnum = devnum; ipmi_main_intf->devnum = devnum;
/* setup IPMB local and target address if given */ /* setup IPMB local and target address if given */
ipmi_main_intf->my_addr = my_addr ? : IPMI_BMC_SLAVE_ADDR; if( my_addr ) {
if (target_addr > 0) { ipmi_main_intf->my_addr = my_addr;
}
else {
/* Check if PICMG extension is available to use the function GetDeviceLocator
* to retreive i2c address PICMG hack to set right IPMB address,
* If extension is not supported, should not give any problems
* PICMG Extension Version 2.0 (PICMG 3.0 Revision 1.0 ATCA) to
* PICMG Extension Version 2.3 (PICMG 3.0 Revision 3.0 ATCA)
*/
/* First, check if PICMG extension is available and supported */
struct ipmi_rq req;
struct ipmi_rs *rsp;
char msg_data;
unsigned char version_accepted = 0;
lprintf(LOG_INFO, "Running PICMG GetDeviceLocator" );
memset(&req, 0, sizeof(req));
req.msg.netfn = IPMI_NETFN_PICMG;
req.msg.cmd = PICMG_GET_PICMG_PROPERTIES_CMD;
msg_data = 0x00;
req.msg.data = &msg_data;
req.msg.data_len = 1;
msg_data = 0;
rsp = ipmi_main_intf->sendrecv(ipmi_main_intf, &req);
if (rsp && !rsp->ccode) {
if
(
(rsp->data[0] == 0)
&&
((rsp->data[1] & 0x0F ) == PICMG_ATCA_MAJOR_VERSION )
){
version_accepted = 1;
lprintf(LOG_INFO, "Discovered PICMG Extension %d.%d",
(rsp->data[1] & 0x0f),(rsp->data[1] >> 4) );
}
}
if(version_accepted == 1){
lprintf(LOG_DEBUG, "Running PICMG GetDeviceLocator" );
memset(&req, 0, sizeof(req));
req.msg.netfn = IPMI_NETFN_PICMG;
req.msg.cmd = PICMG_GET_ADDRESS_INFO_CMD;
msg_data = 0x00;
req.msg.data = &msg_data;
req.msg.data_len = 1;
msg_data = 0;
rsp = ipmi_main_intf->sendrecv(ipmi_main_intf, &req);
if (rsp && !rsp->ccode) {
ipmi_main_intf->my_addr = rsp->data[2];
ipmi_main_intf->target_addr = ipmi_main_intf->my_addr;
lprintf(LOG_INFO, "Discovered IPMB address = 0x%x", ipmi_main_intf->my_addr);
}
}
else{
lprintf(LOG_INFO,
"No PICMG Extenstion discovered, keeping IPMB address 0x20");
}
}
if ( target_addr > 0 && (target_addr != my_addr) ) {
/* need to open the interface first */ /* need to open the interface first */
if (ipmi_main_intf->open != NULL) if (ipmi_main_intf->open != NULL)
ipmi_main_intf->open(ipmi_main_intf); ipmi_main_intf->open(ipmi_main_intf);

View File

@ -44,7 +44,6 @@
#include <ipmitool/ipmi_intf.h> #include <ipmitool/ipmi_intf.h>
#include <ipmitool/helper.h> #include <ipmitool/helper.h>
#include <ipmitool/log.h> #include <ipmitool/log.h>
#include <ipmitool/ipmi_picmg.h>
#if defined(HAVE_CONFIG_H) #if defined(HAVE_CONFIG_H)
# include <config.h> # include <config.h>
@ -68,9 +67,7 @@ static int
ipmi_openipmi_open(struct ipmi_intf * intf) ipmi_openipmi_open(struct ipmi_intf * intf)
{ {
int i = 0; int i = 0;
struct ipmi_rq req;
struct ipmi_rs *rsp;
char msg_data;
char ipmi_dev[16]; char ipmi_dev[16];
char ipmi_devfs[16]; char ipmi_devfs[16];
char ipmi_devfs2[16]; char ipmi_devfs2[16];
@ -101,84 +98,21 @@ ipmi_openipmi_open(struct ipmi_intf * intf)
lperror(LOG_ERR, "Could not enable event receiver"); lperror(LOG_ERR, "Could not enable event receiver");
return -1; return -1;
} }
intf->opened = 1; intf->opened = 1;
/* Check if PICMG extension is available to use the function GetDeviceLocator /* This is never set to 0, the default is IPMI_BMC_SLAVE_ADDR */
* to retreive i2c address PICMG hack to set right IPMB address,
* If extension is not supported, should not give any problems
* PICMG Extension Version 2.0 (PICMG 3.0 Revision 1.0 ATCA) to
* PICMG Extension Version 2.3 (PICMG 3.0 Revision 3.0 ATCA)
*/
if (intf->my_addr == IPMI_BMC_SLAVE_ADDR) {
/* First, check if PICMG extension is available and supported */
unsigned char version_accepted = 0;
lprintf(LOG_DEBUG, "Running PICMG GetDeviceLocator" );
memset(&req, 0, sizeof(req));
req.msg.netfn = IPMI_NETFN_PICMG;
req.msg.cmd = PICMG_GET_PICMG_PROPERTIES_CMD;
msg_data = 0x00;
req.msg.data = &msg_data;
req.msg.data_len = 1;
msg_data = 0;
rsp = intf->sendrecv(intf, &req);
if (rsp && !rsp->ccode) {
if
(
(rsp->data[0] == 0)
&&
(
(rsp->data[1] == 0x02)
||
(rsp->data[1] == 0x12)
||
(rsp->data[1] == 0x22)
||
(rsp->data[1] == 0x32)
)
){
version_accepted = 1;
lprintf(LOG_DEBUG, "Discovered PICMG Extension %d.%d",
(rsp->data[1] & 0x0f),(rsp->data[1] >> 4) );
}
}
if(version_accepted == 1){
lprintf(LOG_DEBUG, "Running PICMG GetDeviceLocator" );
memset(&req, 0, sizeof(req));
req.msg.netfn = IPMI_NETFN_PICMG;
req.msg.cmd = PICMG_GET_ADDRESS_INFO_CMD;
msg_data = 0x00;
req.msg.data = &msg_data;
req.msg.data_len = 1;
msg_data = 0;
rsp = intf->sendrecv(intf, &req);
if (rsp && !rsp->ccode) {
intf->my_addr = rsp->data[2];
intf->target_addr = intf->my_addr;
lprintf(LOG_DEBUG, "Discovered IPMB address = 0x%x", intf->my_addr);
}
}
else{
lprintf(LOG_DEBUG,
"No PICMG Extenstion discovered, keeping IPMB address 0x20");
}
}
if (intf->my_addr != 0) { if (intf->my_addr != 0) {
unsigned int a = intf->my_addr; unsigned int a = intf->my_addr;
if (ioctl(intf->fd, IPMICTL_SET_MY_ADDRESS_CMD, &a) < 0) { if (ioctl(intf->fd, IPMICTL_SET_MY_ADDRESS_CMD, &a) < 0) {
lperror(LOG_ERR, "Could not set IPMB address"); lperror(LOG_ERR, "Could not set IPMB address");
return -1; return -1;
} }
lprintf(LOG_DEBUG, "Set IPMB address to 0x%x", lprintf(LOG_DEBUG, "Set IPMB address to 0x%x",
intf->my_addr); intf->my_addr );
} }
return intf->fd; return intf->fd;
} }
@ -211,8 +145,13 @@ ipmi_openipmi_send_cmd(struct ipmi_intf * intf, struct ipmi_rq * req)
static int curr_seq = 0; static int curr_seq = 0;
fd_set rset; fd_set rset;
uint8_t * data = NULL;
int data_len = 0;
if (intf == NULL || req == NULL) if (intf == NULL || req == NULL)
return NULL; return NULL;
if (intf->opened == 0 && intf->open != NULL) if (intf->opened == 0 && intf->open != NULL)
if (intf->open(intf) < 0) if (intf->open(intf) < 0)
return NULL; return NULL;
@ -231,56 +170,130 @@ ipmi_openipmi_send_cmd(struct ipmi_intf * intf, struct ipmi_rq * req)
intf->target_addr != intf->my_addr) { intf->target_addr != intf->my_addr) {
/* use IPMB address if needed */ /* use IPMB address if needed */
ipmb_addr.slave_addr = intf->target_addr; ipmb_addr.slave_addr = intf->target_addr;
ipmb_addr.lun = req->msg.lun; ipmb_addr.lun = req->msg.lun;
lprintf(LOG_DEBUG, "Sending request to " lprintf(LOG_DEBUG, "Sending request to "
"IPMB target @ 0x%x (from 0x%x)", intf->target_addr,intf->my_addr); "IPMB target @ 0x%x (from 0x%x)", intf->target_addr,intf->my_addr);
#ifdef ENABLE_INTF_OPEN_DUAL_BRIDGE
if(intf->transit_addr != 0 && if(intf->transit_addr != 0 && intf->transit_addr != intf->my_addr) {
intf->transit_addr != intf->my_addr) { uint8_t index = 0;
ipmb_addr.transit_slave_addr = intf->transit_addr;
lprintf(LOG_DEBUG, "Sending through transit " lprintf(LOG_DEBUG, "Encapsulating data sent to "
"IPMB target @ 0x%x", intf->transit_addr); "end target [0x%02x,0x%02x] using transit [0x%02x,0x%02x] from 0x%x ",
} (0x40 | intf->target_channel),
#endif intf->target_addr,
intf->transit_channel,
intf->transit_addr,
intf->my_addr
);
/* Convert Message to 'Send Message' */
/* Supplied req : req , internal req : _req */
if (verbose > 4) {
fprintf(stderr, "Converting message:\n");
fprintf(stderr, " netfn = 0x%x\n", req->msg.netfn );
fprintf(stderr, " cmd = 0x%x\n", req->msg.cmd);
if (recv.msg.data && recv.msg.data_len) {
fprintf(stderr, " data_len = %d\n", req->msg.data_len);
fprintf(stderr, " data = %s\n",
buf2str(req->msg.data,req->msg.data_len));
}
}
/* Modify target address to use 'transit' instead */
ipmb_addr.slave_addr = intf->transit_addr;
ipmb_addr.channel = intf->transit_channel;
/* FIXME backup "My address" */
data_len = req->msg.data_len + 8;
data = malloc(data_len);
if (data == NULL) {
lprintf(LOG_ERR, "ipmitool: malloc failure");
return NULL;
}
memset(data, 0, data_len);
data[index++] = (0x40|intf->target_channel);
data[index++] = intf->target_addr;
data[index++] = ( req->msg.netfn << 2 ) | req->msg.lun ;
data[index++] = ipmi_csum(data+1, 2);
data[index++] = 0xFF; /* normally 0x20 , overwritten by IPMC */
data[index++] = ( (0) << 2) | 0 ; /* FIXME */
data[index++] = req->msg.cmd;
memcpy( (data+index) , req->msg.data, req->msg.data_len);
index += req->msg.data_len;
data[index++] = ipmi_csum( (data+4),(req->msg.data_len + 3) );
if (verbose > 4) {
fprintf(stderr, "Encapsulated message:\n");
fprintf(stderr, " netfn = 0x%x\n", IPMI_NETFN_APP );
fprintf(stderr, " cmd = 0x%x\n", 0x34 );
if (data && data_len) {
fprintf(stderr, " data_len = %d\n", data_len);
fprintf(stderr, " data = %s\n",
buf2str(data,data_len));
}
}
}
_req.addr = (unsigned char *) &ipmb_addr; _req.addr = (unsigned char *) &ipmb_addr;
_req.addr_len = sizeof(ipmb_addr); _req.addr_len = sizeof(ipmb_addr);
} else { } else {
/* otherwise use system interface */ /* otherwise use system interface */
lprintf(LOG_DEBUG+2, "Sending request to " lprintf(LOG_DEBUG+2, "Sending request to "
"System Interface"); "System Interface");
bmc_addr.lun = req->msg.lun; bmc_addr.lun = req->msg.lun;
_req.addr = (unsigned char *) &bmc_addr; _req.addr = (unsigned char *) &bmc_addr;
_req.addr_len = sizeof(bmc_addr); _req.addr_len = sizeof(bmc_addr);
} }
_req.msgid = curr_seq++; _req.msgid = curr_seq++;
_req.msg.netfn = req->msg.netfn;
_req.msg.cmd = req->msg.cmd;
_req.msg.data = req->msg.data;
_req.msg.data_len = req->msg.data_len;
/* In case of a bridge request */
if( data != NULL && data_len != 0 ) {
_req.msg.data = data;
_req.msg.data_len = data_len;
_req.msg.netfn = IPMI_NETFN_APP;
_req.msg.cmd = 0x34;
} else {
_req.msg.data = req->msg.data;
_req.msg.data_len = req->msg.data_len;
_req.msg.netfn = req->msg.netfn;
_req.msg.cmd = req->msg.cmd;
}
if (ioctl(intf->fd, IPMICTL_SEND_COMMAND, &_req) < 0) { if (ioctl(intf->fd, IPMICTL_SEND_COMMAND, &_req) < 0) {
lperror(LOG_ERR, "Unable to send command"); lperror(LOG_ERR, "Unable to send command");
return NULL; if (data != NULL)
free(data);
return NULL;
} }
/* /*
* wait for and retrieve response * wait for and retrieve response
*/ */
if (intf->noanswer) if (intf->noanswer) {
return NULL; if (data != NULL)
free(data);
return NULL;
}
FD_ZERO(&rset); FD_ZERO(&rset);
FD_SET(intf->fd, &rset); FD_SET(intf->fd, &rset);
if (select(intf->fd+1, &rset, NULL, NULL, NULL) < 0) { if (select(intf->fd+1, &rset, NULL, NULL, NULL) < 0) {
lperror(LOG_ERR, "I/O Error"); lperror(LOG_ERR, "I/O Error");
return NULL; if (data != NULL)
free(data);
return NULL;
} }
if (FD_ISSET(intf->fd, &rset) == 0) { if (FD_ISSET(intf->fd, &rset) == 0) {
lprintf(LOG_ERR, "No data available"); lprintf(LOG_ERR, "No data available");
return NULL; if (data != NULL)
free(data);
return NULL;
} }
recv.addr = (unsigned char *) &addr; recv.addr = (unsigned char *) &addr;
@ -290,23 +303,56 @@ ipmi_openipmi_send_cmd(struct ipmi_intf * intf, struct ipmi_rq * req)
/* get data */ /* get data */
if (ioctl(intf->fd, IPMICTL_RECEIVE_MSG_TRUNC, &recv) < 0) { if (ioctl(intf->fd, IPMICTL_RECEIVE_MSG_TRUNC, &recv) < 0) {
lperror(LOG_ERR, "Error receiving message"); lperror(LOG_ERR, "Error receiving message");
if (errno != EMSGSIZE) if (errno != EMSGSIZE) {
return NULL; if (data != NULL)
free(data);
return NULL;
}
} }
if (verbose > 4) { if (verbose > 4) {
fprintf(stderr, "Got message:"); fprintf(stderr, "Got message:");
fprintf(stderr, " type = %d\n", recv.recv_type); fprintf(stderr, " type = %d\n", recv.recv_type);
fprintf(stderr, " channel = 0x%x\n", addr.channel); fprintf(stderr, " channel = 0x%x\n", addr.channel);
fprintf(stderr, " msgid = %ld\n", recv.msgid); fprintf(stderr, " msgid = %ld\n", recv.msgid);
fprintf(stderr, " netfn = 0x%x\n", recv.msg.netfn); fprintf(stderr, " netfn = 0x%x\n", recv.msg.netfn);
fprintf(stderr, " cmd = 0x%x\n", recv.msg.cmd); fprintf(stderr, " cmd = 0x%x\n", recv.msg.cmd);
if (recv.msg.data && recv.msg.data_len) { if (recv.msg.data && recv.msg.data_len) {
fprintf(stderr, " data_len = %d\n", recv.msg.data_len); fprintf(stderr, " data_len = %d\n", recv.msg.data_len);
fprintf(stderr, " data = %s\n", fprintf(stderr, " data = %s\n",
buf2str(recv.msg.data, recv.msg.data_len)); buf2str(recv.msg.data, recv.msg.data_len));
} }
}
if(intf->transit_addr != 0 && intf->transit_addr != intf->my_addr) {
uint8_t index = 0;
/* ipmb_addr.transit_slave_addr = intf->transit_addr; */
lprintf(LOG_DEBUG, "Decapsulating data received from transit "
"IPMB target @ 0x%x", intf->transit_addr);
/* comp code */
/* Check data */
if( recv.msg.data[0] == 0 ) {
recv.msg.netfn = recv.msg.data[2] >> 2;
recv.msg.cmd = recv.msg.data[6];
recv.msg.data = memmove(recv.msg.data ,recv.msg.data+7 , recv.msg.data_len - 7);
recv.msg.data_len -=8;
if (verbose > 4) {
fprintf(stderr, "Decapsulated message:\n");
fprintf(stderr, " netfn = 0x%x\n", recv.msg.netfn );
fprintf(stderr, " cmd = 0x%x\n", recv.msg.cmd);
if (recv.msg.data && recv.msg.data_len) {
fprintf(stderr, " data_len = %d\n", recv.msg.data_len);
fprintf(stderr, " data = %s\n",
buf2str(recv.msg.data,recv.msg.data_len));
}
}
}
} }
/* save completion code */ /* save completion code */
@ -315,10 +361,13 @@ ipmi_openipmi_send_cmd(struct ipmi_intf * intf, struct ipmi_rq * req)
/* save response data for caller */ /* save response data for caller */
if (rsp.ccode == 0 && rsp.data_len > 0) { if (rsp.ccode == 0 && rsp.data_len > 0) {
memmove(rsp.data, rsp.data + 1, rsp.data_len); memmove(rsp.data, rsp.data + 1, rsp.data_len);
rsp.data[recv.msg.data_len] = 0; rsp.data[recv.msg.data_len] = 0;
} }
if (data != NULL)
free(data);
return &rsp; return &rsp;
} }

View File

@ -89,10 +89,6 @@ struct ipmi_ipmb_addr {
short channel; short channel;
unsigned char slave_addr; unsigned char slave_addr;
unsigned char lun; unsigned char lun;
#ifdef ENABLE_INTF_OPEN_DUAL_BRIDGE
short transit_channel;
unsigned char transit_slave_addr;
#endif
}; };
#define IPMI_IOC_MAGIC 'i' #define IPMI_IOC_MAGIC 'i'