diff options
author | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2014-07-06 18:04:32 +0200 |
---|---|---|
committer | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2014-07-06 18:04:32 +0200 |
commit | a7f89980e5b3f4b9a74c70dbc5ffe8aabd28be28 (patch) | |
tree | 41c4deec1fdfbafd7821b4ca7a9772ac0abd92f5 /util/ipmilan.c |
Imported Upstream version 2.9.3upstream/2.9.3
Diffstat (limited to 'util/ipmilan.c')
-rw-r--r-- | util/ipmilan.c | 2430 |
1 files changed, 2430 insertions, 0 deletions
diff --git a/util/ipmilan.c b/util/ipmilan.c new file mode 100644 index 0000000..64b6425 --- /dev/null +++ b/util/ipmilan.c @@ -0,0 +1,2430 @@ +/*M* +// PVCS: +// $Workfile: ipmilan.c $ +// $Revision: 1.0 $ +// $Modtime: 2 Feb 2006 15:31:14 $ +// $Author: arcress at users.sourceforge.net $ +// +// This implements support for the IPMI LAN interface natively. +// +// 02/02/06 ARC - created. +// 05/16/06 ARC - more added. +// 06/19/06 ARCress - fixed sequence numbers for 1.7.1 +// 08/08/06 ARCress - fix AUTHTYPE masks +// 08/11/06 ARCress - added lan command retries +// 10/17/06 ARCress - special case for no MsgAuth headers +// 11/28/06 ARCress - added ipmi_cmd_ipmb routine +// 05/08/07 ARCress - added 1.5 SOL data packet format to _send_lan_cmd, +// not working yet. +// 08/21/07 ARCress - handle Dell 1855 blades that return different authcode +// 04/17/08 ARCress - check FD_ISSET in fd_wait + *M*/ +/*----------------------------------------------------------------------* +The BSD License + +Copyright (c) 2005-2006, Intel Corporation +Copyright (c) 2009 Kontron America, Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + a.. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + b.. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + c.. Neither the name of Intel Corporation nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *----------------------------------------------------------------------*/ + +#ifdef WIN32 +#include <windows.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <io.h> +#include <time.h> +#include <signal.h> + +//#define HAVE_IPV6 1 +#ifdef HAVE_IPV6 +#include <winsock2.h> +//#include <ws2tcpip.h> +//#include <tpipv6.h> +#else +#include <winsock.h> +#define MSG_WAITALL 0x100 /* Wait for a full request */ +#endif + +#define INET_ADDRSTRLEN 16 +#define MSG_NONE 0x000 +#define int32_t int +#define u_int32_t unsigned int +#define uint32 unsigned int +#define uchar unsigned char +#define RECV_MSG_FLAGS MSG_NONE +typedef unsigned int socklen_t; + +#elif defined(DOS) +#include <dos.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#undef HAVE_LANPLUS + +#else /* Linux */ +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/select.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <net/if.h> +#include <netdb.h> +#include <sys/time.h> +#include <time.h> +#include <signal.h> +#define RECV_MSG_FLAGS MSG_WAITALL +#endif +#include "ipmicmd.h" +#include "ipmilan.h" + +#if defined(HAVE_CONFIG_H) +#include "config.h" +#else +#if defined(MACOS) +typedef unsigned int socklen_t; +#endif +#endif + +#if defined(LINUX) +/* TODO: fixups in BSD/Solaris for ipv6 method */ +#define HAVE_IPV6 1 +#endif + +#if defined(CROSS_COMPILE) +#undef HAVE_IPV6 +static struct hostent *xgethostbyname(const char *nstr) +{ + static struct hostent hptr; + if (StrIsIp(nstr)) { /* the string is an IP address */ + hptr.h_name = (char *)nstr; + inet_aton(nstr,(struct in_addr *)&hptr.h_addr); + return(&hptr); + } else { /*the string is a node name */ + return(NULL); + } +} +#else +#define xgethostbyname gethostbyname +#endif + +// #define TEST_LAN 1 /*++++TEST_LAN for DEBUG++++*/ +#if defined(ALLOW_GNU) +#define MD2OK 1 /*use md2.h GPL code*/ +#else +/* if here, ALLOW_GPL is not defined, check for lanplus libcrypto. */ +#if defined(HAVE_LANPLUS) +#define MD2OK 1 /*use MD2 version from lanplus libcrypto */ +#endif +/* if libcrypto does not have EVP_md2, then skip MD2. */ +#if defined(SKIP_MD2) +#undef MD2OK +#endif +#endif + +/* Connection States: + * 0 = init, 1 = socket() complete, 2 = bind/gethost complete, + * 3 = ping sent, 4 = pong received, 5 = session activated. + * see also conn_state_str[] below */ +#define CONN_STATE_INIT 0 +#define CONN_STATE_SOCK 1 +#define CONN_STATE_BIND 2 +#define CONN_STATE_PING 3 +#define CONN_STATE_PONG 4 +#define CONN_STATE_ACTIVE 5 + +#define SOL_DATA 0xFD /*SOL Data command*/ +#define SOL_MSG 0x10000000 /*SOL message type*/ +#define SOL_HLEN 14 +// SZGNODE == 80 +#define SZ_CMD_HDR 4 /*cmd, netfn/lun, sa*/ +#define SWID_REMOTE 0x81 +#define SWID_SMSOS 0x41 + +#pragma pack(1) +typedef struct { /*first 30 bytes conform to IPMI header format*/ + uchar rmcp_ver; + uchar rmcp_res; + uchar rmcp_seq; + uchar rmcp_type; + uchar auth_type; + uint32 seq_num; /*outgoing seq*/ + uint32 sess_id; + uchar auth_code[16]; + uchar msg_len; /* size here = 30 bytes = RQ_HDR_LEN */ + uchar swid; /* usu SWID_REMOTE. From here down, order is changeable */ + uchar swseq; /* outgoing swseq */ + uchar swlun; + uchar priv_level; + uint32 iseq_num; /*incoming seq */ + uchar bmc_addr; /*usu BMC_SA*/ + uchar target_addr; + uchar target_chan; + uchar target_lun; + uchar target_cmd; + uchar target_netfn; + uchar transit_addr; + uchar transit_chan; + uchar bridge_level; + uchar password[16]; + uchar challenge[16]; + } IPMI_HDR; +#pragma pack() + +typedef struct { + int type; + int len; + char *data; + } SOL_RSP_PKT; + +/* extern void atoip(uchar *array,char *instr); *subs.c*/ +extern FILE *open_log(char *mname); /*ipmicmd.c*/ +extern char * get_iana_str(int mfg); /*subs.c*/ + +#define dbglog printf +#define dbg_dump dump_buf +extern FILE *fperr; /*defined in ipmicmd.c, usu stderr */ +extern FILE *fpdbg; /*defined in ipmicmd.c, usu stdout */ +extern int gshutdown; /* from ipmicmd.c */ +extern char gnodename[]; /* from ipmicmd.c */ +extern char *gnode; /* from ipmicmd.c */ +extern char *guser; /* from ipmicmd.c */ +extern char *gpswd; /* from ipmicmd.c */ +extern int fauth_type_set; /* from ipmicmd.c */ +extern int gauth_type; /* from ipmicmd.c */ +extern int gpriv_level; /* from ipmicmd.c */ +extern ipmi_cmd_t ipmi_cmds[NCMDS]; + +static IPMI_HDR ipmi_hdr = { 0x06, 0, 0xFF, 0x07, 0x00, 0, 0, + /*auth_code*/{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, 0, /*msg_len*/ + /*swid*/SWID_REMOTE, 1,0,0,0, BMC_SA,0,0,0,0,0,0,0,0 /*bridge_level*/ + }; +// static IPMI_HDR *phdr; +static uchar bmc_sa = BMC_SA; /*usu 0x20*/ +static uchar sms_swid = SWID_REMOTE; /*usu 0x81*/ +static int vend_id = 0; +static int prod_id = 0; + +#if defined(DOS) || defined(EFI) +int ipmi_open_lan(char *node, char *user, char *pswd, int fdebugcmd) +{ + printf("IPMI LAN is not supported under DOS.\n"); + return(-1); +} +int ipmi_close_lan(char *node) +{ + printf("IPMI LAN is not supported under DOS.\n"); + return(-1); +} +int ipmi_cmd_lan(char *node, ushort cmd, uchar *pdata, int sdata, + uchar *presp, int *sresp, uchar *pcc, char fdebugcmd) +{ + printf("IPMI LAN is not supported under DOS.\n"); + return(-1); +} +int ipmi_cmdraw_lan(char *node, uchar cmd, uchar netfn, uchar lun, uchar sa, + uchar bus, uchar *pdata, int sdata, uchar *presp, int *sresp, + uchar *pcc, char fdebugcmd) +{ + printf("IPMI LAN is not supported under DOS.\n"); + return(-1); +} +int ipmicmd_lan(char *node, uchar cmd, uchar netfn, uchar lun, uchar sa, + uchar bus, uchar *pdata, int sdata, uchar *presp, int *sresp, + uchar *pcc, char fdebugcmd) +{ + printf("IPMI LAN is not supported under DOS.\n"); + return(-1); +} +#else +/* All other OSs can support IPMI LAN */ + +/* + * These variables pertain to ipmilan, for the node given at open time. + * The assumption here is that these are utilities, so no more than one + * node will be open at a given time. + * See also gnode, guser, gpswd in ipmicmd.c + */ +typedef struct { + int connect_state; /*=CONN_STATE_INIT*/ + SockType sockfd; + int finsession; + uint32 session_id; + uint32 in_seq; /*=1*/ + uint32 start_out_seq; /*=1*/ + uchar fMsgAuth; /*0=AuthNone 1=PerMsgAuth 2=UserLevelAuth*/ + uchar auth_type; /*=AUTHTYPE_INIT*/ + } LAN_CONN; /*see also IPMI_HDR below*/ + // static int connect_state = CONN_STATE_INIT; + // static int sockfd = 0; + // static int finsession = 0; + // static uint32 session_id = 0; + // static uint32 in_seq = 0x01; /* inbound sequence num */ + // static uint32 start_out_seq = 0x01; /* initial outbound sequence num */ + // static uchar fMsgAuth = 1; + static uchar auth_type = AUTHTYPE_INIT; /*initial value, not set*/ + static char nodename[SZGNODE+1] = ""; +#if defined(AI_NUMERICSERV) +static int my_ai_flags = AI_NUMERICSERV; /*0x0400 Dont use name resolution NEW*/ +// static int my_ai_flags = AI_NUMERICHOST; /*0x0004 Dont use name resolution*/ +#else +#undef HAVE_IPV6 +#endif + +#ifdef HAVE_IPV6 +#define SOCKADDR_T struct sockaddr_storage +#else +#define SOCKADDR_T struct sockaddr_in +static char _dest_ip[INET_ADDRSTRLEN+1]; +// static char _dest[MAXHOSTNAMELEN+1]; +#endif +static SOCKADDR_T _srcaddr; +static SOCKADDR_T _destaddr; +static int _destaddr_len = 0; +#ifdef TEST_LAN +static int fdebuglan = 3; +#else +static int fdebuglan = 0; +#endif +static int fdoping = 0; /* =1 do ping first, set to 0 b/c no added value*/ +static int fdopoke1 = 0; +static int fdopoke2 = 0; +static int frequireping = 0; /*=1 if ping is required, =0 ignore ping error */ +static LAN_CONN conn = {CONN_STATE_INIT,0,0,0,1,1,1}; +static LAN_CONN *pconn = &conn; +static char *authcode = NULL; +static int authcode_len = 0; +static int ping_timeout = 1; /* timeout: 1 sec */ +static int ipmi_timeout = 2; /* timeout: 10 sec -> 2 sec */ +static int ipmi_try = 4; /* retries: 4 */ +static uchar bridgePossible = 0; +static char *conn_state_str[6] = { + "init state", "socket complete", "bind complete", + "ping sent", "pong received", "session activated" }; +int lasterr = 0; + +static uchar sol_op = 0x80; /* encrypted/not */ +static uchar sol_snd_seq = 0; /* valid if non-zero*/ +static uchar sol_rcv_seq = 0; +static uchar sol_rcv_cnt = 0; +static uchar sol_rcv_ctl = 0x00; +// static uchar sol_offset = 0; +static uchar sol_seed_cnt = 0x01; /* set after activate response */ +static char sol_Encryption = 0; /*for SOL 1.5*/ +static uint32 g_Seed[ 16 ]; /*for SOL 1.5*/ +static uchar g_Cipher[ 16 ][ 16 ]; /*SeedCount x CipherHash for SOL 1.5*/ +#define BUSY_MAX 10 /*for Dell FS12-TY Node Busy errors*/ + +#ifdef WIN32 +int econnrefused = WSAECONNREFUSED; /*=10061.*/ +#else +int econnrefused = ECONNREFUSED; /*=111. from Linux asm/errno.h */ +#endif + +#ifdef WIN32 +WSADATA lan_ws; + +/* See http://msdn2.microsoft.com/en-us/library/ms740668.aspx + * or doc/winsockerr.txt */ +#define NWINERRS 21 +static struct { int err; char *desc; } winerrs[NWINERRS] = { + WSAEINTR /*10004*/, "Interrupted function call", + WSAEBADF /*10009*/, "File handle is not valid", + WSAEACCES /*10013*/, "Permission denied", + WSAEFAULT /*10014*/, "Bad address", + WSAEINVAL /*10022*/, "Invalid argument", + WSAEMFILE /*10024*/, "Too many open files", + WSAENOTSOCK /*10038*/, "Socket operation on nonsocket", + WSAEDESTADDRREQ /*10039*/, "Destination address required", + WSAEMSGSIZE /*10040*/, "Message too long", + WSAEOPNOTSUPP /*10045*/, "Operation not supported", + WSAEADDRINUSE /*10048*/, "Address already in use", + WSAEADDRNOTAVAIL /*10049*/, "Cannot assign requested address", + WSAENETDOWN /*10050*/, "Network is down", + WSAENETUNREACH /*10051*/, "Network is unreachable", + WSAENETRESET /*10052*/, "Network dropped connection on reset", + WSAECONNABORTED /*10053*/, "Software caused connection abort", + WSAECONNRESET /*10054*/, "Connection reset by peer", + WSAENOTCONN /*10057*/, "Socket is not connected", + WSAECONNREFUSED /*10061*/, "Connection refused", + WSAEHOSTDOWN /*10064*/, "Host is down", + WSAEHOSTUNREACH /*10065*/, "No route to host" +}; + +char * strlasterr(int rv) +{ + char *desc; + int i; + for (i = 0; i < NWINERRS; i++) { + if (winerrs[i].err == rv) { + desc = winerrs[i].desc; + break; + } + } + if (i >= NWINERRS) desc = ""; + + return(desc); +} +#endif + +#ifdef MD2OK +extern void md2_sum(uchar *string, int len, uchar *mda); /*from md2.c*/ +#endif +extern void md5_sum(uchar *string, int len, uchar *mda); /*from md5.c*/ + +int _ipmilan_cmd(SockType s, struct sockaddr *to, int tolen, + uchar cmd, uchar netfn, uchar lun, uchar sa, uchar bus, + uchar *sdata, int slen, uchar *rdata, int *rlen, + int fdebugcmd); +static int _send_lan_cmd(SockType s, uchar *pcmd, int scmd, uchar *presp, + int *sresp, struct sockaddr *to, int tolen); +static int ipmilan_open_session(SockType sfd, struct sockaddr *destaddr, + int destaddr_len, uchar auth_type, char *username, + char *authcode, int authcode_len, + uchar priv_level, uint32 init_out_seqnum, + uint32 *session_seqnum, uint32 *session_id); +static int ipmilan_close_session(SockType sfd, struct sockaddr *destaddr, + int destaddr_len, uint32 session_id); +uchar cksum(const uchar *buf, register int len); +char *decode_rv(int rv); /*moved to ipmicmd.c*/ + +void show_LastError(char *tag, int err) +{ +#ifdef WIN32 + fprintf(fperr,"%s LastError = %d %s\n",tag,err,strlasterr(err)); +#else + char *s; + s = strerror(err); + if (s == NULL) s = "error"; + fprintf(fperr,"%s errno = %d, %s\n",tag,err,s); +#endif +} + +int get_LastError( void ) +{ +#ifdef WIN32 + lasterr = WSAGetLastError(); +#else + lasterr = errno; +#endif + return(lasterr); +} + +static void cc_challenge(int cc) +{ + switch(cc) { + case 0x81: + printf("GetSessChallenge: Invalid user name\n"); + break; + case 0x82: + printf("GetSessChallenge: Null user name not enabled\n"); + break; + default: + printf("GetSessChallenge: %s\n",decode_cc((ushort)0,cc)); + break; + } +} + +static void cc_session(int cc) +{ + switch(cc) { + case 0x81: + printf("ActivateSession: No session slots available from BMC\n"); + break; + case 0x82: + printf("ActivateSession: No sessions available for this user\n"); + break; + case 0x83: + printf("ActivateSession: No sessions for this user/privilege\n"); + break; + case 0x84: + printf("ActivateSession: Session sequence number out of range\n"); + break; + case 0x85: + printf("ActivateSession: Invalid session ID in request\n"); + break; + case 0x86: + printf("ActivateSession: Privilege level exceeds user/channel limit\n"); + break; + default: + printf("%s\n",decode_cc((ushort)0,cc)); + break; + } + return; +} + +int StrIsIp(char *str) +{ + int i, j, n; + char ipchars[11] = "0123456789."; + int ndot = 0; + int rv = 0; + /* checks if the string looks like an IP address. */ + if (str == NULL) return(rv); + n = (int)strlen(str); + for (i = 0; i < n; i++) { + for (j = 0; j < 11; j++) + if (str[i] == ipchars[j]) break; + if (j >= 11) break; /*some other char, not valid*/ + if (str[i] == '.') ndot++; + } + if ((i == n) && (ndot == 3)) rv = 1; /*valid*/ + return(rv); +} + +void close_sockfd(SockType sfd); +void close_sockfd(SockType sfd) +{ + if (sfd == 0) return; +#ifdef WIN32 + // shutdown(sfd,SD_SEND); /*done sending*/ + closesocket(sfd); /*close lan socket */ + WSACleanup(); +#else + alarm(0); + signal(SIGALRM,SIG_DFL); + signal(SIGINT,SIG_DFL); + close(sfd); /*close lan socket */ +#endif + pconn->sockfd = 0; /*set global to zero */ +} + +int open_sockfd(char *node, SockType *sfd, SOCKADDR_T *daddr, + int *daddr_len, int foutput); +int open_sockfd(char *node, SockType *sfd, SOCKADDR_T *daddr, + int *daddr_len, int foutput) +{ + int rv = 0; + SockType s, _sockfd = -1; +#ifdef HAVE_IPV6 + struct addrinfo hints; + struct addrinfo *res, *res0; + char service[NI_MAXSERV]; +#else + struct hostent *hptr; +#endif + +#ifdef WIN32 + DWORD rvl; + + if (sfd == NULL || daddr == NULL || daddr_len == NULL) + return(-3); /* invalid pointer */ + rvl = WSAStartup(0x0202,&lan_ws); + if (rvl != 0) { + fprintf(fperr,"lan, WSAStartup(2.2) error %ld, try 1.1\n", rvl); + WSACleanup(); + rvl = WSAStartup(0x0101,&lan_ws); + if (rvl != 0) { + fprintf(fperr,"lan, WSAStartup(1.1) error %ld\n", rvl); + WSACleanup(); + return((int)rvl); + } + } +#else + if (sfd == NULL || daddr == NULL || daddr_len == NULL) + return(-3); /* invalid pointer */ +#endif + pconn->connect_state = CONN_STATE_INIT; + +#ifdef HAVE_IPV6 + memset(&_srcaddr, 0, sizeof(_srcaddr)); + memset(daddr, 0, sizeof(_destaddr)); + sprintf(service, "%d", RMCP_PRI_RMCP_PORT); + /* Obtain address(es) matching host/port */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */ + hints.ai_flags = my_ai_flags; + hints.ai_protocol = IPPROTO_UDP; /* */ + rv = getaddrinfo(node, service, NULL, &res); + if (rv != 0) { + printf("Address lookup for %s failed, getaddrinfo error %d\n", + node,rv); + return rv; + } + + /* getaddrinfo() returns a list of address structures. + * Try each address until we successfully connect(2). + * If socket(2) (or connect(2)) fails, we (close the socket + * and) try the next address. + */ + for (res0 = res; res0 != NULL; res0 = res0->ai_next) { + s = socket(res0->ai_family, res0->ai_socktype, res0->ai_protocol); + if (s == SockInvalid) continue; + else _sockfd = s; + /* valid protocols are IPPROTO_UDP, IPPROTO_IPV6 */ + if (res0->ai_protocol == IPPROTO_TCP) continue; /*IPMI != TCP*/ + pconn->connect_state = CONN_STATE_SOCK; + rv = connect(_sockfd, res0->ai_addr, res0->ai_addrlen); + if (fdebuglan) printf("socket(%d,%d,%d), connect(%d) rv = %d\n", + res0->ai_family, res0->ai_socktype, + res0->ai_protocol, s,rv); + if (rv != -1) { + memcpy(daddr, res0->ai_addr, res0->ai_addrlen); + *daddr_len = res0->ai_addrlen; + break; /* Success */ + } + close_sockfd(_sockfd); + _sockfd = -1; + } + freeaddrinfo(res); /* Done with addrinfo */ + if (_sockfd < 0) { + printf("Connect to %s failed\n", node); + // ipmi_close_(); + rv = -1; + } +#else + /* Open lan interface (ipv4 method) */ + s = socket(AF_INET, SOCK_DGRAM, 0); + if (s == SockInvalid) return (-1); + else _sockfd = s; + + pconn->connect_state = CONN_STATE_SOCK; + memset(&_srcaddr, 0, sizeof(_srcaddr)); + _srcaddr.sin_family = AF_INET; + _srcaddr.sin_port = htons(0); + _srcaddr.sin_addr.s_addr = htonl(INADDR_ANY); + rv = bind(_sockfd, (struct sockaddr *)&_srcaddr, sizeof(_srcaddr)); + if (rv < 0) { + close_sockfd(_sockfd); + return (rv); + } + + memset(daddr, 0, sizeof(SOCKADDR_T)); + daddr->sin_family = AF_INET; + daddr->sin_port = htons(RMCP_PRI_RMCP_PORT); /*0x26f = 623.*/ + if (StrIsIp(node)) { /* the node string is an IP address */ + uchar in_ip[4]; + atoip(in_ip,node); + memcpy(&daddr->sin_addr.s_addr,in_ip,4); + if ((hptr = xgethostbyname(node)) == NULL) /*gethost error*/ + strncpy(gnodename,node,SZGNODE); /*but not fatal*/ + else strncpy(gnodename,hptr->h_name,SZGNODE); + } + else if ((hptr = xgethostbyname(node)) == NULL) { + if (foutput) { +#ifdef WIN32 + fprintf(fperr,"lan, gethostbyname(%s): errno=%d\n", node,get_errno()); +#elif SOLARIS + fprintf(fperr,"lan, gethostbyname(%s): errno=%d\n", node,get_errno()); +#else + fprintf(fperr,"lan, gethostbyname(%s): %s\n", node,hstrerror(errno)); +#endif + } + close_sockfd(_sockfd); + return(LAN_ERR_HOSTNAME); + } else { /*gethostbyname(name) succeeded*/ + daddr->sin_addr = *((struct in_addr *)hptr->h_addr); + strncpy(gnodename,hptr->h_name,SZGNODE); + } + *daddr_len = sizeof(SOCKADDR_T); +#endif + + *sfd = _sockfd; + return(rv); +} + +static void +sig_timeout(int sig) +{ +#ifndef WIN32 + alarm(0); + signal(SIGALRM,SIG_DFL); +#endif + fprintf(fpdbg,"ipmilan_cmd timeout, after %s\n",conn_state_str[pconn->connect_state]); + _exit(LAN_ERR_TIMEOUT); /*timeout signal*/ +} + +static void +sig_abort(int sig) +{ + static int sig_aborting = 0; + int rv; + // uchar buf_rs[4]; + // uchar *cmd_rs; + + if (sig_aborting == 0) { + sig_aborting = 1; + if (pconn->sockfd != 0) { /* socket is open */ + if (pconn->session_id != 0) { /* session is open */ + // cmd_rs = buf_rs; + rv = ipmilan_close_session(pconn->sockfd, + (struct sockaddr *)&_destaddr, + _destaddr_len, ipmi_hdr.sess_id); + } + close_sockfd(pconn->sockfd); + } + signal(SIGINT,SIG_DFL); + fprintf(fpdbg,"ipmilan_cmd interrupt, after %s\n", conn_state_str[pconn->connect_state]); + _exit(LAN_ERR_ABORT); /*abort signal*/ + } /*endif*/ +} /*end sig_abort*/ + +int fd_wait(SockType fd, int nsec, int usec) +{ + fd_set readfds; + struct timeval tv; + int rv; + + FD_ZERO(&readfds); + FD_SET(fd, &readfds); + tv.tv_sec = nsec; + tv.tv_usec = usec; + rv = select((int)(fd+1), &readfds, NULL, NULL, &tv); + if (rv <= 0 || !FD_ISSET(fd,&readfds)) return(-1); + else return(0); +} + +#ifndef WIN32 + /* LINUX, Solaris, BSD */ +/* do_sleep() is currently unused. */ +/* signal handlers + sleep(3) is a bad idea */ +int do_sleep(unsigned int sleep_len) +{ + struct timeval tv; + + if (sleep_len == 0) + return 0; + + tv.tv_sec = sleep_len; + tv.tv_usec = 0; + if (select(1, NULL, NULL, NULL, &tv) < 0) + { + if (errno != EINTR) return(errno); + } + return 0; +} +#endif + +static void h2net(uint h, uchar *net, int n) +{ + int i = 0; + net[i++] = h & 0xff; + net[i++] = (h >> 8) & 0xff; + if (n == 2) return; + net[i++] = (h >> 16) & 0xff; + net[i++] = (h >> 24) & 0xff; + return; +} + +static void net2h(uint *h, uchar *net, int n) +{ + uint v; + v = (net[1] << 8) | net[0]; + if (n == 2) { *h = v; return; } + v |= (net[3] << 24) | (net[2] << 16); + *h = v; + return; +} + +/* + * _ipmilan_cmd + * local routine to send & receive each command. + * called by global ipmicmd_lan() + */ +int _ipmilan_cmd(SockType sockfd, struct sockaddr *hostaddr, int hostaddr_len, + uchar cmd, uchar netfn, uchar lun, uchar sa, uchar bus, + uchar *sdata, int slen, uchar *rdata, int *rlen, int fdebugcmd) +{ + uchar cmd_rq[RQ_LEN_MAX+SZ_CMD_HDR]; + uchar cmd_rs[RS_LEN_MAX+SZ_CMD_HDR+1]; + int rv = 0; + int rs_len; + int clen; + uchar cc = 0; + +#ifndef TEST_LAN + fdebuglan = fdebugcmd; +#endif + if (sockfd == 0 || hostaddr == NULL || + sdata == NULL || rdata == NULL) + return(LAN_ERR_INVPARAM);; + + if (fdebuglan > 2) + dbglog("_ipmilan_cmd(%02x,%02x,%02x,%02x,%02x)\n", + cmd,netfn,lun,sa,bus); + clen = SZ_CMD_HDR; + cmd_rq[0] = cmd; + cmd_rq[1] = (netfn << 2) + (lun & 0x03); + cmd_rq[2] = sa; + cmd_rq[3] = bus; + memcpy(&cmd_rq[clen],sdata,slen); + rs_len = sizeof(cmd_rs); + memset(cmd_rs, 0, rs_len); + rv = _send_lan_cmd(sockfd, cmd_rq, slen+clen, cmd_rs, &rs_len, + hostaddr, hostaddr_len); + if (rv == 0 && rs_len == 0) cc = 0; + else cc = cmd_rs[0]; + if (fdebugcmd) fprintf(fpdbg,"_ipmilan_cmd[%02x]: rv = %d, cc=%x rs_len=%d\n", + cmd_rq[0],rv,cc,rs_len); + if (rv == 0 && cc != 0) { /* completion code error */ + if (fdebugcmd) { + dump_buf("cmd_rq",cmd_rq,slen+clen,0); + dump_buf("cmd_rs",cmd_rs,rs_len,0); + } + } + if (rv == 0) { + if (rs_len < 0) rs_len = 0; + if (*rlen <= 0) *rlen = 1; /*failsafe*/ + if (rs_len > 0) { + if (rs_len > *rlen) rs_len = *rlen; + memcpy(rdata,&cmd_rs[0],rs_len); + } else { /*(rs_len == 0)*/ + rv = LAN_ERR_TOO_SHORT; /*no completion code returned*/ + } + } else { + rdata[0] = cc; + if (rs_len < 1) rs_len = 1; + } + *rlen = rs_len; + + return (rv); +} /*end _ipmilan_cmd()*/ + + +static void hash(uchar *pwd, uchar *id, uchar *chaldata, int chlen, uint32 seq, + uchar *mda, uchar md) +{ + uchar pbuf[80]; /* 16 + 4 + 16 + 4 + 16 = 56 */ + int blen, n, i; + + blen = 0; n = 16; + memcpy(&pbuf[blen], pwd,n); /* password */ + blen += n; n = 4; + memcpy(&pbuf[blen],id,n); /* session id */ + blen += n; + memcpy(&pbuf[blen],chaldata,chlen); /* ipmi msg data, incl challenge */ + blen += chlen; n = 4; + h2net(seq,&pbuf[blen],n); /* in_seq num */ + blen += n; n = 16; + memcpy(&pbuf[blen],pwd,n); /* password */ + blen += n; + if (md == IPMI_SESSION_AUTHTYPE_MD2) i = 2; + else i = 5; +#ifdef TEST_AUTH + if (fdebuglan) { + fprintf(fpdbg,"hash: calling md%d_sum with seq %u\n",i,seq); + dump_buf("pbuf",pbuf,blen,0); + } +#endif +#ifdef MD2OK + if (md == IPMI_SESSION_AUTHTYPE_MD2) + md2_sum(pbuf,blen,mda); + else /* assume md5 */ +#endif + md5_sum(pbuf,blen,mda); +#ifdef TEST_AUTH + if (fdebuglan) { + fprintf(fpdbg,"Hashed MD%d AuthCode: \n",i); + dump_buf("AuthCode",mda,16,0); + } +#endif +} /* end hash() */ + +static void hash_special(uchar *pwd, uchar *chaldata, uchar *mda) +{ + uchar md_pwd[16]; + uchar challenge[16]; + int i; + /* Only used by SuperMidro, must be MD5 auth_type */ + memset(md_pwd, 0, 16); + md5_sum(pwd,16,md_pwd); + memset(challenge, 0, 16); + for (i = 0; i < 16; i++) + challenge[i] = chaldata[i] ^ md_pwd[i]; + memset(mda, 0, 16); + md5_sum(challenge,16,mda); +} + +static int ipmilan_sendto(SockType s, const void *msg, size_t len, int flags, + const struct sockaddr *to, int tolen) +{ + int fusepad = 0; + int n; + if (fdebuglan > 2) { + dbg_dump("ipmilan_sendto",(uchar *)msg,(int)len,0); + } + /* Check whether we need a pad byte */ + /* Note from Table 12-8, RMCP Packet for IPMI via Ethernet footnote. */ + if (len == 56 || len == 84 || len == 112 || len == 128 || len == 156) { + /* include pad byte at end, require input buffer to have one extra */ + fusepad = 1; + len += 1; + } + n = (int)sendto(s,msg,len,flags,to,(socklen_t)tolen); + if (fusepad && (n > 0)) n--; + return(n); +} + +static int ipmilan_recvfrom(SockType s, void *buf, size_t len, int flags, + struct sockaddr *from, int *fromlen) +{ + int rv; + rv = (int)recvfrom(s,buf,len,flags,from,(socklen_t *)fromlen); +#ifdef OLD + /* Sometimes the OS sends an ECONNREFUSED error, but + * retrying will catch the BMC's reply packet. */ + if (rv < 0) { + int err; + err = get_LastError(); + if (err == econnrefused) { + if (fdebuglan) + fprintf(fpdbg,"ipmilan_recvfrom rv=%d econnrefused, retry\n",rv); + rv = recvfrom(s,buf,len,flags,from,(socklen_t *)fromlen); + } + } +#endif + return(rv); +} + +static int ipmilan_poke1(SockType sfd, struct sockaddr *destaddr, int destlen) +{ + int rv; + uchar asfpkt[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x20, 0x18, 0xc8, 0xc2, 0x01, 0x01, 0x3c }; + if (fdebuglan) fprintf(fpdbg,"sending ipmilan poke1\n"); + rv = ipmilan_sendto(sfd, asfpkt, 16, 0, destaddr, destlen); + os_usleep(0,100); + return rv; +} + +static int ipmilan_poke2(SockType sfd, struct sockaddr *destaddr, int destlen) +{ + int rv; + uchar asfpkt[16] = "poke2"; /*any junk*/ + if (fdebuglan) fprintf(fpdbg,"sending ipmilan poke2\n"); + rv = ipmilan_sendto(sfd, asfpkt, 10, 0, destaddr, destlen); + os_usleep(0,100); + return rv; +} + + +static void do_hash(uchar *password, uchar *sessid, uchar *pdata, int sdata, + uint32 seq_num, uchar auth_type, uchar *auth_out) +{ + /* finish header with auth_code */ + if (auth_type != IPMI_SESSION_AUTHTYPE_NONE) { /*fdoauth==1*/ + if (auth_type == IPMI_SESSION_AUTHTYPE_MD5) + hash(password, sessid, pdata, sdata, seq_num, + auth_out,IPMI_SESSION_AUTHTYPE_MD5); +#ifdef MD2OK + else if (auth_type == IPMI_SESSION_AUTHTYPE_MD2) + hash(password, sessid, pdata, sdata, seq_num, + auth_out,IPMI_SESSION_AUTHTYPE_MD2); +#endif + else /* IPMI_SESSION_AUTHTYPE_PASSWORD */ + memcpy(auth_out,password,16); + } +} + +static uint32 inc_seq_num(uint32 seq) +{ + seq++; + if (seq == 0) seq = 1; + return(seq); +} + +static int inc_sol_seq(int seq) +{ + seq++; + if (seq > 0x0f) seq = 1; /*limit to 4 bits*/ + return(seq); +} + +void ipmi_lan_set_timeout(int ipmito, int tries, int pingto) +{ + ipmi_timeout = ipmito; /*default 2*/ + ipmi_try = tries; /*default 4*/ + ping_timeout = pingto; /*default 1*/ +} + +/* + * _send_lan_cmd + * Internal routine called by local _ipmilan_cmd() and by + * ipmilan_open_session(). + * Writes the data to the lan socket in IPMI LAN format. + * Authentication, sequence numbers, checksums are handled here. + * + * Input Parameters: + * s = socket descriptor for this session + * pcmd = buffer for the command and data + * Arbitrary pcmd format: + * cmd[0] = IPMI command + * cmd[1] = NETFN/LUN byte + * cmd[2] = Slave Address (usu 0x20) + * cmd[3] = Bus (usu 0x00) + * cmd[4-N] = command data + * scmd = size of command buffer (3+sdata) + * presp = pointer to existing response buffer + * sresp = On input, size of response buffer, + * On output, length of response data. + * to = sockaddr structure for to/destination + * tolen = length of to sockaddr + */ +static int _send_lan_cmd(SockType s, uchar *pcmd, int scmd, uchar *presp, + int *sresp, struct sockaddr *to, int tolen) +{ + uchar cbuf[SEND_BUF_SZ]; + uchar rbuf[RECV_BUF_SZ]; + // uint32 out_seq = 0; /* outbound */ + int clen, rlen, hlen, msglen; + int flags; + int sz, n, i, j; + int cs1, cs2, cs3 = 0, cs4 = 0; + uchar *pdata; + int sdata; + IPMI_HDR *phdr; + uchar *psessid; + uchar iauth[16]; + int fdoauth = 1; + uint32 sess_id_tmp; + int rv = 0; + int itry; + uchar fsentok; + + /* set up LAN req hdr */ + phdr = &ipmi_hdr; + hlen = RQ_HDR_LEN; + /* phdr->bmc_addr set in open_session */ + phdr->target_addr = pcmd[2]; + phdr->target_chan = pcmd[3]; + phdr->target_lun = (pcmd[1] & 0x03); + phdr->target_netfn = (pcmd[1] >> 2); + phdr->target_cmd = pcmd[0]; + + if (phdr->seq_num != 0) pconn->finsession = 1; + if ( ((phdr->target_cmd == CMD_ACTIVATE_SESSION) || + (phdr->target_cmd == CMD_SET_SESSION_PRIV)) && + (phdr->target_netfn == NETFN_APP) ) { + pconn->finsession = 1; /*so do seq_num*/ + fdoauth = 1; /*use msg auth*/ + } + if (phdr->auth_type == IPMI_SESSION_AUTHTYPE_NONE) fdoauth = 0; + else if (pconn->finsession && (pconn->fMsgAuth == 0)) { + /* Forcing the type may be necessary with IBM eServer 360S. */ + if (fauth_type_set) fdoauth = 1; /*user set it, so try anyway*/ + else fdoauth = 0; /*auth not supported*/ + } + if (fdoauth == 0) hlen = RQ_HDR_LEN - 16; + + /* copy command */ + if (scmd < SZ_CMD_HDR) return(LAN_ERR_INVPARAM); + sdata = scmd - SZ_CMD_HDR; /* scmd = 4 + datalen */ + msglen = 7 + sdata; + if (fdebuglan > 2) { + dbglog("_send_lan_cmd: cmd=%02x, hlen=%d, msglen=%02x, authtype=%02x\n", + pcmd[0], hlen, msglen, phdr->auth_type); + } + clen = hlen + msglen; + if (clen > sizeof(cbuf)) { + fprintf(fpdbg,"message size %d > buffer size %d\n",clen,sizeof(cbuf)); + return(LAN_ERR_TOO_SHORT); + } + + if ((phdr->target_cmd == SOL_DATA) && + (phdr->target_netfn == NETFN_SOL) ) /*SOL 1.5 data packet*/ + { + hlen = SOL_HDR_LEN; /*RMCP header + 26 */ + if (fdoauth == 0) hlen = SOL_HDR_LEN - 16; + msglen = sdata; + memcpy(&cbuf[0], phdr, 4); /* copy RMCP header to buffer */ + pdata = &cbuf[4]; + pdata[0] = phdr->auth_type; + memcpy(&pdata[1],&phdr->seq_num,4); + sess_id_tmp = phdr->sess_id | SOL_MSG; + memcpy(&pdata[5],&sess_id_tmp,4); + if (fdebuglan > 2) + dbglog("auth_type=%x/%x fdoauth=%d hlen=%d seq_num=%x\n", /*SOL*/ + phdr->auth_type,gauth_type,fdoauth,hlen,phdr->seq_num); + if (fdoauth) { + psessid = (uchar *)&sess_id_tmp; + do_hash(phdr->password, psessid, &cbuf[hlen],msglen, + phdr->seq_num, phdr->auth_type, iauth); + /* copy hashed authcode to header */ + memcpy(&pdata[9],iauth,16); + } + // pdata[hlen-1] = msglen; + pdata = &cbuf[hlen]; + memcpy(pdata,&pcmd[SZ_CMD_HDR],msglen); /*copy the data*/ + clen = hlen + msglen; + if (fdebuglan > 2) + dbg_dump("sol data, before",pdata,sdata,1); /*SOL TEST*/ + } else { /*not SOL packet, normal IPMI packet*/ + pdata = &cbuf[hlen]; + j = cs1 = 0; + if ((phdr->target_addr == phdr->bmc_addr) || !bridgePossible || + (phdr->target_addr == SWID_REMOTE) || + (phdr->target_addr == SWID_SMSOS)) { + phdr->bridge_level = 0; + msglen = sdata + 7; + clen = hlen + msglen; + } else { + phdr->bridge_level = 1; + if (phdr->transit_addr != 0 && phdr->transit_addr != phdr->bmc_addr) + { + msglen = sdata + 15 + 8; + phdr->bridge_level++; + } else { + msglen = sdata + 15; + } + if (fdebuglan > 2) { + dbglog("bridge_level=%d cmd=%02x target=%02x transit=%02x\n", + phdr->bridge_level, phdr->target_cmd, + phdr->target_addr, phdr->transit_addr); + dbglog("bridge target_ch=%x transit_ch=%x\n", + phdr->target_chan, phdr->transit_chan); + } + clen = hlen + msglen; + pdata[j++] = bmc_sa; /*[0]=sa */ + pdata[j++] = (NETFN_APP << 2); /*[1]=netfn/lun*/ + pdata[j] = cksum(&pdata[cs1],j-cs1); /*[2]=cksum1*/ + j++; + cs3 = j; /*start for cksum3*/ + pdata[j++] = phdr->swid; /*[3]=swid, usu SWID_REMOTE */ + pdata[j++] = (phdr->swseq << 2); /*[4]=swseq/lun*/ + pdata[j++] = CMD_SEND_MESSAGE; /*[5]=cmd*/ + if (phdr->bridge_level == 1) { + pdata[j++] = (0x40|phdr->target_chan); /*channel*/ + } else { /*double bridge*/ + pdata[j++] = (0x40|phdr->transit_chan); /*channel*/ + cs1 = j; + pdata[j++] = phdr->transit_addr; /*[0]=sa */ + pdata[j++] = (NETFN_APP << 2); /*[1]=netfn/lun*/ + pdata[j] = cksum(&pdata[cs1],j-cs1); /*[2]=cksum1*/ + j++; + cs4 = j; /*start for cksum4*/ + pdata[j++] = bmc_sa; /*[3]=swid */ + pdata[j++] = (phdr->swseq << 2); /*[4]=swseq/lun*/ + pdata[j++] = CMD_SEND_MESSAGE; /*[5]=cmd*/ + pdata[j++] = (0x40|phdr->target_chan); /*channel*/ + } + } /*end-else bridged*/ + /* normal IPMI LAN commmand packet */ + cs1 = j; /*start for cksum1*/ + pdata[j++] = pcmd[2]; /*[0]=sa (phdr->target_addr)*/ + pdata[j++] = pcmd[1]; /*[1]=netfn/lun*/ + pdata[j] = cksum(&pdata[cs1],j-cs1); /*[2]=cksum1*/ + j++; + cs2 = j; /*start for cksum2*/ + if (phdr->bridge_level == 0) + pdata[j++] = phdr->swid; /*[3]=swid*/ + else /*bridged message*/ + pdata[j++] = phdr->bmc_addr; /*[3]=swid*/ + pdata[j++] = (phdr->swseq << 2) + phdr->swlun; /*[4]=swseq/lun*/ + pdata[j++] = phdr->target_cmd; /*[5]=cmd (pcmd[0])*/ + if (sdata > 0) { + memcpy(&pdata[j],&pcmd[SZ_CMD_HDR],sdata); /*[6]=data*/ + j += sdata; + } + pdata[j] = cksum(&pdata[cs2],j-cs2); /*cksum2*/ + j++; + + if (phdr->bridge_level > 0) { + if (phdr->bridge_level > 1) { + pdata[j] = cksum(&pdata[cs4],j-cs4); /*cksum4*/ + j++; + } + pdata[j] = cksum(&pdata[cs3],j-cs3); /*cksum3*/ + j++; + } + if (fdebuglan && (msglen != j)) + fprintf(fpdbg,"warning: msglen(%d)!=j(%d), hlen=%d clen=%d\n", + msglen,j,hlen,clen); + + if (fdoauth) { + psessid = (uchar *)&phdr->sess_id; + do_hash(phdr->password, psessid, &cbuf[hlen],msglen, + phdr->seq_num, phdr->auth_type, iauth); + /* copy hashed authcode to header */ + memcpy(phdr->auth_code,iauth,16); + } + memcpy(&cbuf[0], phdr, hlen); /* copy header to buffer */ + } /*end-else normal IPMI */ + + if (fdoauth == 0 && phdr->auth_type != IPMI_SESSION_AUTHTYPE_NONE) { + /* force the packet auth type to NONE (0) */ + IPMI_HDR *pchdr; + pchdr = (IPMI_HDR *)&cbuf[0]; + pchdr->auth_type = IPMI_SESSION_AUTHTYPE_NONE; + // memset(phdr->auth_code,0,16); + } + cbuf[hlen-1] = (uchar)msglen; /* IPMI Message Length = 7 + data */ + if (fdebuglan > 2) { + if ((phdr->target_cmd == SOL_DATA) && + (phdr->target_netfn == NETFN_SOL) ) { + // phdr->sess_id = sess_id_sav; /*restore sess_id*/ + if (fdebuglan) fprintf(fpdbg,"sending lan sol data (len=%d)\n",clen); + } else { + if (fdebuglan) fprintf(fpdbg,"sending ipmi lan data (len=%d)\n",clen); + } + } + flags = 0; + rlen = 0; + + if ((fdebuglan > 2) && + (phdr->target_cmd == CMD_GET_CHAN_AUTH_CAP) && + (phdr->target_netfn == NETFN_APP) ) + dbg_dump("get_chan_auth_cap command",cbuf,clen,1); + memset(rbuf,0,sizeof(rbuf)); + fsentok = 0; + for (itry = 0; (itry < ipmi_try) && (rlen == 0); itry++) + { + if (fdebuglan > 2) + dbglog("ipmilan_cmd(seq=%x) fsentok=%d itry=%d\n", + phdr->seq_num, fsentok, itry); + if (fsentok == 0) { + if (fdebuglan) + fprintf(fpdbg,"ipmilan_sendto(seq=%x,clen=%d)\n", + phdr->seq_num, clen); + sz = ipmilan_sendto(s,cbuf,clen,flags,to,tolen); + if (sz < 1) { + lasterr = get_LastError(); + if (fdebuglan) show_LastError("ipmilan_sendto",lasterr); + rv = LAN_ERR_SEND_FAIL; + os_usleep(0,5000); + continue; /* retry */ + } + fsentok = 1; /*sent ok, no need to resend*/ + } + + /* receive the response */ + rv = fd_wait(s, ipmi_timeout,0); + if (rv != 0) { + if (fdebuglan) + fprintf(fpdbg,"ipmilan_cmd timeout, after request, seq=%x itry=%d\n", + phdr->seq_num, itry); + rv = LAN_ERR_RECV_FAIL; + if (fdopoke2) ipmilan_poke2(s, to, tolen); + os_usleep(0,5000); + continue; /* retry */ + } + flags = RECV_MSG_FLAGS; + rlen = ipmilan_recvfrom(s,rbuf,sizeof(rbuf),flags,to,&tolen); + if (rlen < 0) { + lasterr = get_LastError(); + if (fdebuglan) { + fprintf(fpdbg,"ipmilan_recvfrom rlen=%d, err=%d iseq=%x itry=%d\n", + rlen,lasterr,phdr->iseq_num,itry); + show_LastError("ipmilan_recvfrom",lasterr); + } + rv = rlen; /* -3 = LAN_ERR_RECV_FAIL */ + rlen = 0; + *sresp = rlen; + /* Sometimes the OS sends an ECONNREFUSED error, but + * retrying will catch the BMC's reply packet. */ + if (lasterr == econnrefused) continue; /*try again*/ + else break; /* goto EXIT; */ + } else { /* successful receive */ + net2h(&phdr->iseq_num,&rbuf[5],4); /*incoming seq_num from hdr*/ + if (fdebuglan) { + fprintf(fpdbg,"ipmilan_recvfrom rlen=%d, iseq=%x\n", + rlen,phdr->iseq_num); + if (fdebuglan > 2) + dbg_dump("ipmilan_recvfrom", rbuf,rlen,1); + } + + /* incoming auth_code may differ from request auth code, (Dell 1855)*/ + if (rbuf[4] == IPMI_SESSION_AUTHTYPE_NONE) { /* if AUTH_NONE */ + phdr->auth_type = IPMI_SESSION_AUTHTYPE_NONE; + hlen = RQ_HDR_LEN - 16; /*RMCP header + 10*/ + } else { + hlen = RQ_HDR_LEN; /*RMCP header + 26 */ + // memcpy(phdr->iauth_code,&rbuf[13],16); /*iauthcode*/ + } + + i = hlen + 6; + if (rlen <= i) rv = LAN_ERR_TOO_SHORT; + else { /* successful */ + n = rlen - i - 1; + if (bridgePossible && (phdr->target_addr != bmc_sa)) { + if (phdr->bridge_level && + ((rbuf[hlen+1] >> 2) == (NETFN_APP + 1)) && /*0x07*/ + rbuf[hlen+5] == CMD_SEND_MESSAGE) /*0x34*/ + { + phdr->bridge_level--; + if (n <= 1) { /* no data, wait for next */ + if (fdebuglan) + fprintf(fpdbg,"bridged response empty, cc=%x\n",rbuf[i]); + if (rbuf[i] == 0) { /* if sendmsg cc==0, recv again*/ + rlen = 0; + continue; + } /*else done*/ + } else { /* has data, copy it*/ + //if (fdebuglan) fprintf(fpdbg,"bridged response, n=%d\n",n); + // memmove(&rbuf[i-7],&rbuf[i],n); + phdr->swseq = rbuf[i-3] >> 2; /*needed?*/ + rbuf[i-8] -= 8; + n -= 8; + i -= 7; + if (fdebuglan) dump_buf("bridged response",&rbuf[i],n,1); + continue; /*recv again*/ + } + } + } /*endif bridge*/ + if (n > *sresp) n = *sresp; + memcpy(presp,&rbuf[i],n); + *sresp = n; + rv = 0; + } + } /*end else success*/ + } /*end for loop*/ +// EXIT: +#ifdef NOT + /* do not increment sequence numbers for SEND_MESSAGE command */ + if ((phdr->target_cmd == IPMB_SEND_MESSAGE) && + (phdr->target_netfn == NETFN_APP) ) + pconn->finsession = 0; +#endif + if (pconn->finsession) { + /* increment seqnum - even if error */ + phdr->seq_num = inc_seq_num( phdr->seq_num ); + if (rlen > 0) pconn->in_seq = phdr->iseq_num; + else pconn->in_seq = inc_seq_num(pconn->in_seq); + phdr->swseq = (uchar)inc_seq_num(phdr->swseq); + } + return(rv); +} /*end _send_lan_cmd*/ + +static char *auth_type_str(int authcode) +{ + char *pstr; + switch(authcode) { + case IPMI_SESSION_AUTHTYPE_MD5: pstr = "MD5"; break; + case IPMI_SESSION_AUTHTYPE_MD2: pstr = "MD2"; break; + case IPMI_SESSION_AUTHTYPE_PASSWORD: pstr = "PSWD"; break; + case IPMI_SESSION_AUTHTYPE_OEM: pstr = "OEM"; break; + case IPMI_SESSION_AUTHTYPE_NONE: pstr = "NONE"; break; + default: pstr = "Init"; break; /* AUTHTYPE_INIT, etc. */ + } + return (pstr); +} + +#ifdef NOT_USED +static uchar auth_type_mask(int authcode, uchar allowed) +{ + uchar mask; + switch(authcode) { + case IPMI_SESSION_AUTHTYPE_MD5: + mask = IPMI_MASK_AUTHTYPE_MD5; + break; + case IPMI_SESSION_AUTHTYPE_MD2: + mask = IPMI_MASK_AUTHTYPE_MD2; + break; + case IPMI_SESSION_AUTHTYPE_PASSWORD: + mask = IPMI_MASK_AUTHTYPE_PASSWORD; + break; + case IPMI_SESSION_AUTHTYPE_OEM: + mask = IPMI_MASK_AUTHTYPE_OEM; + break; + case IPMI_SESSION_AUTHTYPE_NONE: + mask = IPMI_MASK_AUTHTYPE_NONE; + break; + default: /* AUTHTYPE_INIT, etc. */ + mask = 0; + break; + } + return (mask); +} +#endif + +/* + * ipmilan_open_session + * Performs the various command/response sequence needed to + * initiate an IPMI LAN session. + */ +static int ipmilan_open_session(SockType sfd, struct sockaddr *destaddr, + int destaddr_len, uchar auth_type, char *username, + char *authcode, int authcode_len, + uchar priv_level, uint32 init_out_seqnum, + uint32 *session_seqnum, uint32 *session_id) +{ + int rv = 0; + uchar ibuf[RQ_LEN_MAX+3]; + uchar rbuf[RS_LEN_MAX+4]; + uint32 iseqn; + uchar ipasswd[16]; + uchar iauthtype; + uchar iauthcap, imsgauth; + int rlen, ilen; + IPMI_HDR *phdr; + uchar cc; + int busy_tries = 0; + + if (fdebuglan) + fprintf(fpdbg,"ipmilan_open_session(%d,%02x,%s,%02x,%x) called\n", + sfd,auth_type,username,priv_level,init_out_seqnum); + if (sfd == 0 || destaddr == NULL) return LAN_ERR_INVPARAM; + phdr = &ipmi_hdr; + /* Initialize ipmi_hdr fields */ + memset(phdr,0,sizeof(ipmi_hdr)); + phdr->rmcp_ver = 0x06; + phdr->rmcp_res = 0x00; + phdr->rmcp_seq = 0xFF; + phdr->rmcp_type = 0x07; + phdr->swid = sms_swid; + phdr->swseq = 1; + phdr->priv_level = priv_level; + + /* Get Channel Authentication */ + phdr->auth_type = IPMI_SESSION_AUTHTYPE_NONE; /*use none(0) at first*/ + ibuf[0] = 0x0e; /*this channel*/ + ibuf[1] = phdr->priv_level; + rlen = sizeof(rbuf); + if (fdebuglan) + fprintf(fpdbg,"GetChanAuth(sock %x, level %x) called\n",sfd,ibuf[1]); + rv = _ipmilan_cmd(sfd, destaddr, destaddr_len, CMD_GET_CHAN_AUTH_CAP, + NETFN_APP,BMC_LUN,bmc_sa, PUBLIC_BUS, + ibuf,2, rbuf,&rlen, fdebuglan); + if (rv != 0) { /*retry if error*/ + rv = _ipmilan_cmd(sfd, destaddr, destaddr_len, CMD_GET_CHAN_AUTH_CAP, + NETFN_APP,BMC_LUN,bmc_sa, PUBLIC_BUS, + ibuf,2, rbuf,&rlen, fdebuglan); + } + cc = rbuf[0]; + if (fdebuglan) + fprintf(fpdbg,"GetChanAuth rv = %d, cc=%x rbuf: %02x %02x %02x " + "%02x %02x %02x %02x\n", + rv, rbuf[0],rbuf[1],rbuf[2],rbuf[3], + rbuf[4],rbuf[5],rbuf[6],rbuf[7]); + if (rv != 0 || cc != 0) goto ERREXIT; + + /* Check Channel Auth params */ + if ((rbuf[2] & 0x80) != 0) { /*have IPMI 2.0 extended capab*/ + if ( ((rbuf[4]&0x02) != 0) && ((rbuf[4]&0x01) == 0) ) { + if (fdebuglan) + fprintf(fpdbg,"GetChanAuth reports only v2 capability\n"); + rv = LAN_ERR_V2; /*try v2 instead*/ + goto ERREXIT; + } + } + /* Check authentication support */ + imsgauth = rbuf[3]; + if ((imsgauth & 0x10) == 0) pconn->fMsgAuth = 1; /*per-message auth*/ + else if ((imsgauth & 0x08) == 0) pconn->fMsgAuth = 2; /*user-level auth*/ + else pconn->fMsgAuth = 0; /*no auth support*/ + iauthcap = rbuf[2] & 0x3f; + if (fauth_type_set) { + iauthtype = (uchar)gauth_type; // set by user + auth_type = iauthtype; + } else { + iauthtype = AUTHTYPE_INIT; /*initial value, not set*/ + } + if (auth_type != iauthtype) { + /* default of MD5 is preferred, but try what BMC allows */ + if (iauthcap & IPMI_MASK_AUTHTYPE_MD5) { + iauthtype = IPMI_SESSION_AUTHTYPE_MD5; + auth_type = iauthtype; +#ifdef MD2OK + } else if (iauthcap & IPMI_MASK_AUTHTYPE_MD2) { + iauthtype = IPMI_SESSION_AUTHTYPE_MD2; + auth_type = iauthtype; + if (fdebuglan) + fprintf(fpdbg,"auth_type set to MD2 (%02x)\n",iauthtype); +#endif + } else if (iauthcap & IPMI_MASK_AUTHTYPE_PASSWORD) { + iauthtype = IPMI_SESSION_AUTHTYPE_PASSWORD; + auth_type = iauthtype; + if (fdebuglan) + fprintf(fpdbg,"auth_type set to Password (%02x)\n",iauthtype); + } else { + if (fdebuglan) fprintf(fpdbg, + "auth_type set to %02x, using None\n",auth_type); + iauthtype = IPMI_SESSION_AUTHTYPE_NONE; + auth_type = iauthtype; + } + } + if (fdebuglan) fprintf(fpdbg, + "auth_type=%02x(%s) allow=%02x iauthtype=%02x msgAuth=%d(%02x)\n", + auth_type,auth_type_str(auth_type),iauthcap,iauthtype, + pconn->fMsgAuth,imsgauth); + + /* get session challenge */ + phdr->auth_type = IPMI_SESSION_AUTHTYPE_NONE; + memset(ibuf,0,17); + ibuf[0] = iauthtype; + if (username != NULL) + strncpy(&ibuf[1],username,16); + while (busy_tries < BUSY_MAX) { + rlen = sizeof(rbuf); + rv = _ipmilan_cmd(sfd, destaddr,destaddr_len, CMD_GET_SESSION_CHALLENGE, + NETFN_APP,BMC_LUN,bmc_sa, PUBLIC_BUS, + ibuf,17, rbuf,&rlen, fdebuglan); + cc = rbuf[0]; + if (rv != 0) break; + else if (cc == 0xc0) busy_tries++; + else break; + } + if (fdebuglan) { + if ((rv == 0) && (cc == 0)) + dump_buf("GetSessionChallenge rv=0, rbuf",rbuf,rlen,0); + else + fprintf(fpdbg,"GetSessionChallenge rv=%d cc=%x rlen=%d tries=%d\n", + rv, cc, rlen,busy_tries); + } + if (rv != 0) goto ERREXIT; + else if (cc != 0) { cc_challenge(cc); goto ERREXIT; } + + /* save challenge response data */ + memcpy(&phdr->sess_id, &rbuf[1], 4); + memcpy(phdr->challenge, &rbuf[5], 16); + + /* Save authtype/authcode in ipmi_hdr for later use in _send_lan_cmd. */ + phdr->bmc_addr = bmc_sa; + phdr->auth_type = iauthtype; + if (authcode_len > 16 || authcode_len < 0) authcode_len = 16; + memset(&ipasswd,0,16); + if (authcode != NULL && authcode_len > 0) + memcpy(&ipasswd,(uchar *)authcode,authcode_len); /* AuthCode=passwd */ + memcpy(phdr->password,&ipasswd,16); /* save password */ + + /* ActivateSession request */ + ibuf[0] = phdr->auth_type; + ibuf[1] = phdr->priv_level; + if (vend_id == VENDOR_SUPERMICRO) { + /* if supermicro, do special auth logic here */ + hash_special(phdr->password, phdr->challenge, ipasswd); + memcpy(phdr->password,ipasswd,16); + memset(&ibuf[2],0,16); /*zero challenge string here*/ + if (fdebuglan) printf("Using supermicro OEM challenge\n"); + } else { + memcpy(&ibuf[2],phdr->challenge,16); /*copy challenge string to data*/ + } + phdr->seq_num = 0; + iseqn = init_out_seqnum; + h2net(iseqn,&ibuf[18],4); /* write iseqn to buffer */ + ilen = 22; + if (fdebuglan) dump_buf("ActivateSession req",ibuf,ilen,0); + + rlen = sizeof(rbuf); + rv = _ipmilan_cmd(sfd,destaddr,destaddr_len,CMD_ACTIVATE_SESSION, + NETFN_APP,BMC_LUN,bmc_sa, PUBLIC_BUS, + ibuf, ilen, rbuf, &rlen, fdebuglan); + cc = rbuf[0]; + if (fdebuglan) { + if (rv > 0) fprintf(fpdbg,"ActivateSession rv = 0x%02x\n",rv); /*cc*/ + else fprintf(fpdbg,"ActivateSession rv = %d\n",rv); + } + if (rv != 0) goto ERREXIT; + else if (cc != 0) { cc_session(cc); goto ERREXIT; } + + if (pconn->fMsgAuth == 2) /*user-level auth*/ + phdr->auth_type = IPMI_SESSION_AUTHTYPE_NONE; + + memcpy(&phdr->sess_id,&rbuf[2],4); /* save new session id */ + net2h(&iseqn, &rbuf[6],4); /* save returned out_seq_num */ + if (iseqn == 0) iseqn = inc_seq_num(iseqn); /* was ++iseqn */ + phdr->seq_num = iseqn; /* new session seqn */ + if (fdebuglan) + fprintf(fpdbg,"sess_id=%x seq_num=%x priv_allow=%x priv_req=%x\n", + phdr->sess_id,phdr->seq_num,rbuf[10],phdr->priv_level); + + /* set session privileges (set_session_privilege_level) */ + ibuf[0] = phdr->priv_level; + rlen = sizeof(rbuf); + rv = _ipmilan_cmd(sfd, destaddr, destaddr_len, CMD_SET_SESSION_PRIV, + NETFN_APP,BMC_LUN,bmc_sa, PUBLIC_BUS, + ibuf,1, rbuf,&rlen, fdebuglan); + cc = rbuf[0]; + if (fdebuglan) fprintf(fpdbg,"SetSessionPriv(%x) rv = %d\n",ibuf[0], rv); + + bridgePossible = 1; + *session_id = phdr->sess_id; + *session_seqnum = phdr->seq_num; +ERREXIT: + if (rv == 0 && cc != 0) rv = cc; + return(rv); +} /*end ipmilan_open_session*/ + +/* + * ipmilan_close_session + */ +static int ipmilan_close_session(SockType sfd, struct sockaddr *destaddr, + int destaddr_len, uint32 session_id) +{ + uchar ibuf[RQ_LEN_MAX+3]; + uchar rbuf[RS_LEN_MAX+4]; + int rlen; + int rv = 0; + + if (session_id == 0) return(0); + /* send close session command */ + memcpy(ibuf,&session_id,4); + rlen = sizeof(rbuf); + bridgePossible = 0; + rv = _ipmilan_cmd(sfd, destaddr, destaddr_len, CMD_CLOSE_SESSION, + NETFN_APP,BMC_LUN,bmc_sa,PUBLIC_BUS, + ibuf,4, rbuf,&rlen, fdebuglan); + if (fdebuglan) fprintf(fpdbg,"CloseSession rv = %d, cc = %02x\n", + rv, rbuf[0]); + if (rbuf[0] != 0) rv = rbuf[0]; /*comp code*/ + if (rv == 0) pconn->session_id = 0; + ipmi_hdr.seq_num = 0; + ipmi_hdr.swseq = 1; + ipmi_hdr.iseq_num = 0; + ipmi_hdr.sess_id = 0; + pconn->finsession = 0; + return(rv); +} + + +int rmcp_ping(SockType sfd, struct sockaddr *saddr, int saddr_len, int foutput) +{ + /* The ASF spec says to use network byte order */ + uchar asf_pkt[40] = {06,0,0xFF,06,0x00,0x00,0x11,0xBE,0x80,0,0,0 }; + struct sockaddr from_addr; + int from_len; + int rv; + int iana; + + /* Send RMCP ASF ping to verify IPMI LAN connection. */ + asf_pkt[9] = 1; /*tag*/ + rv = ipmilan_sendto(sfd, asf_pkt, 12, 0, saddr, saddr_len); + if (foutput) + fprintf(fpdbg,"ipmilan ping, sendto len=%d\n",rv); + if (rv < 0) return(LAN_ERR_PING); + pconn->connect_state = CONN_STATE_PING; /*ping was sent ok*/ + + from_len = sizeof(struct sockaddr); + rv = fd_wait(sfd,ping_timeout,0); + if (rv != 0) { + fprintf(fpdbg,"ping timeout, after %s\n", + conn_state_str[pconn->connect_state]); + rv = LAN_ERR_CONNECT; + } else { + rv = ipmilan_recvfrom(sfd, asf_pkt, sizeof(asf_pkt), 0, + &from_addr,&from_len); + if (foutput) { + fprintf(fpdbg,"ipmilan pong, recvfrom len=%d\n", rv); + if (rv > 0) { + iana = asf_pkt[15] + (asf_pkt[14] << 8) + + (asf_pkt[13] << 16) + (asf_pkt[12] << 24); + dump_buf("ping response",asf_pkt, rv,0); + printf("ping IANA = %d (%s)\n",iana,get_iana_str(iana)); + } + } + if (rv < 0) return(LAN_ERR_CONNECT); + } + return(0); +} + +int ping_bmc(char *node, int fdebugcmd) +{ + SOCKADDR_T toaddr; + int toaddr_len; + SockType sfd; + int rv; + + rv = open_sockfd(node,&sfd, &toaddr, &toaddr_len, fdebugcmd); + if (rv != 0) return(rv); + + rv = rmcp_ping(sfd, (struct sockaddr *)&toaddr,toaddr_len, fdebugcmd); + + close_sockfd(sfd); + return(rv); +} + +static int get_rand(void *data, int len) +{ + int rv = 0; + int fd; + +#if defined(LINUX) + fd = open("/dev/urandom", O_RDONLY); + if (fd < 0 || len < 0) return errno; + rv = read(fd, data, len); + close(fd); +#else + fd = rand(); + if (fd == 0) fd = 1; + if (len > sizeof(int)) len = sizeof(int); + memcpy(data,&fd,len); +#endif + return rv; +} + +/* + * ipmi_open_lan + */ +int ipmi_open_lan(char *node, char *user, char *pswd, int fdebugcmd) +{ + char *username; + uchar priv_level; + int rv = -1; +#ifndef HAVE_IPV6 + char *temp; +#endif + +#ifndef TEST_LAN + fdebuglan = fdebugcmd; + if (fdebugcmd) fprintf(fpdbg,"ipmi_open_lan: fdebug = %d\n",fdebugcmd); +#endif + if (fdebugcmd > 2) fdoping = 1; + get_mfgid(&vend_id,&prod_id); + if (nodeislocal(node)) { + fprintf(fpdbg,"ipmi_open_lan: node %s is local!\n",node); + rv = LAN_ERR_INVPARAM; + goto EXIT; + } else { + + if ((gshutdown==0) || fdebugcmd) + fprintf(fpdbg,"Opening lan connection to node %s ...\n",node); + /* save nodename for sig_abort later */ + if (strlen(node) > SZGNODE) { + strncpy(nodename, node, SZGNODE); nodename[SZGNODE] = 0; + } else strcpy(nodename, node); + + rv = open_sockfd(node, &(pconn->sockfd), &_destaddr, &_destaddr_len, 1); + if (fdebugcmd) + printf("open_sockfd returned %d, fd=%d\n", rv, pconn->sockfd); + if (rv != 0) goto EXIT; + +#ifdef HAVE_IPV6 + strcpy(gnodename,nodename); + fprintf(fpdbg,"Connecting to node %s\n",gnodename); +#else +#ifdef WIN32 + /* check for ws2_32.lib(getnameinfo) resolution */ + gnodename[0] = 0; +/* + int getnameinfo( const struct sockaddr * sa, socklen_t salen, + char * host, DWORD hostlen, + char * serv, DWORD servlen, + int flags); + rv = getnameinfo((SOCKADDR *)&_destaddr, _destaddr_len, + gnodename,SZGNODE, NULL,0,0); +*/ +#else + rv = getnameinfo((struct sockaddr *)&_destaddr, _destaddr_len, + gnodename,SZGNODE, NULL,0,0); +#endif + if (rv != 0) { + if (fdebugcmd) + fprintf(fpdbg,"ipmi_open_lan: getnameinfo rv = %d\n",rv); + gnodename[0] = 0; + } + temp = inet_ntoa(_destaddr.sin_addr); + fprintf(fpdbg,"Connecting to node %s %s\n",gnodename, temp); + strncpy(_dest_ip, temp, INET_ADDRSTRLEN); + _dest_ip[INET_ADDRSTRLEN] = 0; +#endif + +#ifndef WIN32 + /* Linux: Set up signals to handle errors & timeouts. */ + signal(SIGINT,sig_abort); + signal(SIGALRM,sig_timeout); +#endif + + pconn->connect_state = CONN_STATE_BIND; + + if (fdoping) { + rv = rmcp_ping(pconn->sockfd,(struct sockaddr *)&_destaddr, + _destaddr_len, fdebugcmd); + if (fdopoke1 && rv != 0) { + /* May sometimes need a poke to free up the BMC (cant hurt) */ + ipmilan_poke1(pconn->sockfd,(struct sockaddr *)&_destaddr, + _destaddr_len); + } + + if (rv != 0) { + if (rv == LAN_ERR_CONNECT && frequireping == 0) { + /* keep going even if ping/pong failure */ + rv = 0; + } else { + close_sockfd(pconn->sockfd); + rv = LAN_ERR_CONNECT; + goto EXIT; + } + } + pconn->connect_state = CONN_STATE_PONG; + } + + { + auth_type = (uchar)gauth_type; + priv_level = (uchar)gpriv_level; + username = user; + authcode = pswd; + authcode_len = (pswd) ? strlen_(authcode) : 0; + if ((vend_id == VENDOR_INTEL) || (vend_id == VENDOR_IBM)) + pconn->start_out_seq = 1; + else { + if (fdebugcmd) + printf("calling get_rand(%d)\n", pconn->start_out_seq); + get_rand(&pconn->start_out_seq,sizeof(pconn->start_out_seq)); + } + } + rv = ipmilan_open_session(pconn->sockfd, (struct sockaddr *)&_destaddr, + _destaddr_len, auth_type, username, + authcode, authcode_len,priv_level, pconn->start_out_seq, + &pconn->in_seq, &pconn->session_id); + if (rv == 0) { /* successful (session active) */ + pconn->connect_state = CONN_STATE_ACTIVE; /*set connection state to active*/ + } else { /* open_session rv != 0 */ + if ((gshutdown==0) || fdebugcmd) { + if (rv < 0) + fprintf(fpdbg,"ipmilan_open_session error, rv = %d\n",rv); + else fprintf(fpdbg,"ipmilan_open_session error, rv = 0x%x\n",rv); + } + close_sockfd(pconn->sockfd); + } + } +EXIT: + if (rv != 0) { + // if ((gshutdown==0) || fdebugcmd) + printf("ipmilan %s\n",decode_rv(rv)); + if (rv == -1 && lasterr != 0) show_LastError("ipmilan",lasterr); + } + return(rv); +} + +int ipmi_flush_lan(char *node) +{ + int rv = 0; + /* could match node via pconn = find_conn(node); */ + if (!nodeislocal(node)) { /* ipmilan, need to close & cleanup */ + if (pconn->sockfd != 0) close_sockfd(pconn->sockfd); + } else { /* kcs cleanup */ +#ifndef WIN32 + alarm(0); + signal(SIGALRM,SIG_DFL); +#endif + } /* endif */ + pconn->connect_state = CONN_STATE_INIT; + pconn->finsession = 0; + pconn->session_id = 0; + pconn->sockfd = 0; + pconn->in_seq = 1; + pconn->start_out_seq = 1; + pconn->fMsgAuth = 1; /*1=PerMsgAuth*/ + pconn->auth_type = AUTHTYPE_INIT; + return (rv); +} + +int ipmi_close_lan(char *node) +{ + int rv = 0; + + /* could match node via pconn = find_conn(node); */ + if (!nodeislocal(node)) { /* ipmilan, need to close & cleanup */ + if (pconn->sockfd != 0) { /* socket is open */ + if (gshutdown) pconn->session_id = 0; + if (pconn->session_id != 0) { /* session is open */ + // cmd_rs = buf_rs; + rv = ipmilan_close_session(pconn->sockfd, + (struct sockaddr *)&_destaddr, + _destaddr_len, ipmi_hdr.sess_id); + /* flush session_id even if error, let it time out */ + pconn->session_id = 0; + } + close_sockfd(pconn->sockfd); + pconn->sockfd = 0; + } + pconn->connect_state = CONN_STATE_INIT; + pconn->finsession = 0; + } else { /* kcs cleanup */ +#ifndef WIN32 + alarm(0); + signal(SIGALRM,SIG_DFL); +#endif + } /* endif */ + return (rv); +} + +/* + * ipmicmd_lan + * This is called by ipmi_cmd_lan, all commands come through here. + */ +int ipmicmd_lan(char *node, + uchar cmd, uchar netfn, uchar lun, uchar sa, uchar bus, + uchar *pdata, int sdata, uchar *presp, int *sresp, + uchar *pcc, char fdebugcmd) +{ + uchar rq_data[RQ_LEN_MAX+3]; + uchar cmd_rs[RS_LEN_MAX+4]; + uchar cc = 0; + int rlen; + int rv = -1; + +#ifndef TEST_LAN + fdebuglan = fdebugcmd; +#endif + /* check sdata/sresp against MAX_ */ + if (sdata > RQ_LEN_MAX) { + if (fdebugcmd) printf("cmd %x sdata(%d) > RQ_LEN_MAX(%d)\n", + cmd,sdata,RQ_LEN_MAX); + return(LAN_ERR_BADLENGTH); + } + if (*sresp > RS_LEN_MAX) { + if (fdebugcmd) printf("cmd %x sresp(%d) > RS_LEN_MAX(%d), use less\n", + cmd,*sresp,RS_LEN_MAX); + /* This is ok, just receive up to the max. */ + *sresp = RS_LEN_MAX; + } + if (pdata == NULL) { pdata = rq_data; sdata = 0; } + rlen = *sresp; + + if (nodeislocal(node)) { /*local, use kcs*/ + fprintf(fpdbg,"ipmicmd_lan: node %s is local", node); + goto EXIT; + } else { /* ipmilan */ + if (pconn->sockfd == 0) { /* closed, do re-open */ + if (fdebugcmd) + fprintf(fpdbg,"sockfd==0, node %s needs re-open\n",node); + rv = ipmi_open_lan(gnode, guser, gpswd, fdebugcmd); + if (rv != 0) goto EXIT; + } + if (fdebugcmd) { + fprintf(fpdbg,"lan_cmd(seq=%x) %02x %02x %02x %02x, (dlen=%d): ", + ipmi_hdr.seq_num, cmd,netfn,lun,sa,sdata); + dump_buf("cmd data",pdata,sdata,0); + } + if (fdebuglan > 2) + dbglog("calling _ipmilan_cmd(%02x,%02x)\n",cmd,netfn); + rlen = sizeof(cmd_rs); + rv = _ipmilan_cmd(pconn->sockfd, (struct sockaddr *)&_destaddr, + _destaddr_len, cmd, netfn, lun, sa, bus, pdata, sdata, + cmd_rs, &rlen, fdebugcmd); + } + + cc = cmd_rs[0]; + if (rv == 0 && cc == 0) { /* success */ + if (fdebugcmd) { + fprintf(fpdbg,"lan_rsp rv=0 cc=0 (rlen=%d): ",rlen); + dump_buf("cmd rsp",cmd_rs,rlen,0); + } + rlen--; + if (rlen > *sresp) { /*received data > receive buffer*/ + if (fdebugcmd) + printf("rlen(%d) > sresp(%d), truncated\n",rlen,*sresp); + rlen = *sresp; + } + memcpy(presp,&cmd_rs[1],rlen); + *sresp = rlen; + } else { /* error */ + if (fdebugcmd) + fprintf(fpdbg,"ipmicmd_lan: cmd=%02x rv=%d, cc=%02x, rlen=%d\n", + cmd,rv,cc,rlen); + presp[0] = 0; /*memset(presp,0,*sresp);*/ + *sresp = 0; + } + +EXIT: + *pcc = cc; + return(rv); +} /*end ipmicmd_lan()*/ + +/* + * ipmi_cmd_lan + * This is the entry point, called from ipmicmd.c + */ +int ipmi_cmd_lan(char *node, ushort cmd, uchar *pdata, int sdata, + uchar *presp, int *sresp, uchar *pcc, char fdebugcmd) +{ + int rc, i; + uchar mycmd; + + for (i = 0; i < NCMDS; i++) { + if (ipmi_cmds[i].cmdtyp == cmd) break; + } + if (i >= NCMDS) { + fprintf(fperr, "ipmi_cmd_lan: Unknown command %x\n",cmd); + return(-1); + } + if (cmd >= CMDMASK) mycmd = (uchar)(cmd & CMDMASK); /* unmask it */ + else mycmd = (uchar)cmd; + if (fdebuglan > 2) + dbglog("ipmi_cmd_lan: cmd=%04x, mycmd=%02x\n",cmd,mycmd); + rc = ipmicmd_lan(node,mycmd,ipmi_cmds[i].netfn,ipmi_cmds[i].lun, + ipmi_cmds[i].sa, ipmi_cmds[i].bus, + pdata,sdata,presp,sresp,pcc,fdebugcmd); + return (rc); +} + +int ipmi_cmdraw_lan(char *node, uchar cmd, uchar netfn, uchar lun, uchar sa, + uchar bus, uchar *pdata, int sdata, uchar *presp, int *sresp, + uchar *pcc, char fdebugcmd) +{ + int rc; + if (fdebuglan > 2) + dbglog("ipmi_cmdraw_lan: cmd=%02x, netfn=%02x\n",cmd,netfn); + /* bus is not used for lan */ + rc = ipmicmd_lan(node, cmd, netfn, lun, sa, bus, + pdata,sdata,presp,sresp,pcc,fdebugcmd); + return (rc); +} + + +SockType lan_get_fd(void) +{ + return(pconn->sockfd); +} + +/* static SOL v1.5 encryption routines */ + +static void sol15_cipherinit( uchar SeedCount, char *password, uint32 out_seq) +{ + uchar temp[ 40 ]; /* 16 + 4 + 8 + 4 = 32 bytes */ + int i; + + i = SeedCount & 0x0f; + + srand((unsigned int)time(NULL)); + g_Seed[i] = (int32_t)rand(); + + if (password == NULL) + memset(&temp[0], 0, 16); + else memcpy(&temp[0], password,16); /*16-byte password*/ + h2net(g_Seed[i],&temp[16],4); /*4-byte seed */ + memset(&temp[20], 0, 8 ); /*8-byte pad*/ + h2net(out_seq,&temp[28],4); /*4-byte seq num */ + md5_sum(temp,32, g_Cipher[i]); +} + +#ifdef NOT_USED +static void sol15_encrypt( uchar *dst, uchar *src, int len, uchar SeedCount ) +{ + uchar cipher; + unsigned int val; + int i; + + SeedCount &= 0x0f; + if ( sol_Encryption ) { + for (i = 0; i < len; i++, dst++, src++) { + cipher = g_Cipher[ SeedCount ][ (i & 0x0f) ]; + val = (*src) << (cipher & 0x07); + *dst = (uchar)((val | (val >> 8)) ^ cipher); + } + } else { + memcpy( dst, src, len ); + } +} + +static void sol15_decrypt( uchar *dst, uchar *src, int len, uchar SeedCount ) +{ + uchar cipher; + unsigned int val; + int i; + SeedCount &= 0x0f; + if ( sol_Encryption ) { + for (i = 0; i < len; i++, dst++, src++) { + cipher = g_Cipher[ SeedCount ][ (i & 0x0f) ]; + val = ((*src) ^ cipher) << 8; + val >>= (cipher & 0x07); + *dst = (uchar)((val >> 8) | val); + } + } else { + memcpy( dst, src, len ); + } +} +#endif + +/* + * lan_get_sol_data + * Called before ACTIVATE_SOL1 command + */ +void lan_get_sol_data(uchar fEnc, uchar seed_cnt, uint32 *seed) +{ + if (seed_cnt != sol_seed_cnt && (seed_cnt < 16)) + sol_seed_cnt = seed_cnt; + pconn->start_out_seq = ipmi_hdr.seq_num; + sol_snd_seq = (uchar)pconn->start_out_seq; + sol15_cipherinit(sol_seed_cnt, gpswd, pconn->start_out_seq); + *seed = g_Seed[sol_seed_cnt]; + if (fdebuglan > 2) + dbglog("lan_get_sol_data: %02x %02x %02x\n", /*SOL*/ + fEnc, seed_cnt, ipmi_hdr.seq_num); +} + +/* + * lan_set_sol_data + * Called after ACTIVATE_SOL1 response received + */ +void lan_set_sol_data(uchar fenc, uchar auth, uchar seed_cnt, + int insize, int outsize) +{ + if (fdebuglan > 2) + dbglog("lan_set_sol_data: %02x %02x %02x %02x\n", /*SOL*/ + auth,seed_cnt,insize,outsize); + if (fenc || (auth & 0x07) == 1) { + sol_op = 0x80; /*OEM encryption*/ + sol_Encryption = 1; + } else { + sol_op = 0x00; /*no encryption*/ + sol_Encryption = 0; + } + if (seed_cnt != sol_seed_cnt && (seed_cnt < 16)) { + /* if seed count changed, re-init the cipher. */ + sol_seed_cnt = seed_cnt; + sol15_cipherinit(sol_seed_cnt, gpswd, pconn->start_out_seq); + } +} + +/* + * lan_send_sol + * buffer contains characters entered at console. + * build an SOL data frame, which ends up + * calling _send_lan_cmd(). + * + * SOL 1.5 Message Format + * 0 : Packet Sequence Number + * 1 : Packet ack/nak seq num (recvd) + * 2 : Character offset (of recvd) + * 3 : Seed count + * 4 : Operation/Status + */ +int lan_send_sol( uchar *buffer, int len, SOL_RSP_PKT *rsp) +{ + int rv = -1; + int ilen; + uchar idata[IPMI_REQBUF_SIZE]; /*=80 bytes*/ + uchar *pdata; + int hlen, msglen; + int sz; + IPMI_HDR *phdr; + int fdoauth = 1; + uchar iauth[16]; + uint32 sess_id_tmp; + uchar *psessid; + int flags; + + phdr = &ipmi_hdr; + hlen = 4 + 10 + 16; // was SOL_HLEN (14); + + pdata = &idata[0]; + memset(pdata,0,hlen); + memcpy(&pdata[0], phdr, 4); /* copy RMCP header to buffer */ + if (phdr->auth_type == IPMI_SESSION_AUTHTYPE_NONE) fdoauth = 0; + else fdoauth = 1; + if (fdoauth == 0 && phdr->auth_type != IPMI_SESSION_AUTHTYPE_NONE) + pdata[4] = IPMI_SESSION_AUTHTYPE_NONE; + else pdata[4] = phdr->auth_type; + memcpy(&pdata[5],&phdr->seq_num,4); /*session sequence number*/ + sess_id_tmp = phdr->sess_id | SOL_MSG; + memcpy(&pdata[9],&sess_id_tmp,4); /*session id*/ + if (fdoauth == 0) hlen -= 16; + + pdata = &idata[hlen]; + if (len == 0) { + pdata[0] = 0x00; /*ack for keepalive*/ + } else { + sol_snd_seq = (uchar)inc_sol_seq(sol_snd_seq); + pdata[0] = sol_snd_seq; + // sol15_encrypt(&pdata[5],buffer,len, sol_seed_cnt); + memcpy(&pdata[5],buffer,len); + } + pdata[1] = sol_rcv_seq; /* seq to ack*/ + pdata[2] = sol_rcv_cnt; /* num bytes ack'd */ + pdata[3] = sol_seed_cnt; + pdata[4] = 0x00; /*Operation/Status*/ + msglen = len + 5; + { + if (fdebuglan > 2) { /*SOL*/ + dbg_dump("lan_send_sol input", buffer,len,1); + dbglog("auth_type=%x/%x fdoauth=%d hlen=%d seq_num=%x enc=%d\n", + phdr->auth_type,gauth_type,fdoauth,hlen,phdr->seq_num, + sol_Encryption); + dbg_dump("send_sol buf", pdata,msglen,1); + } + if (fdoauth) { + psessid = (uchar *)&sess_id_tmp; + do_hash(phdr->password, psessid, &idata[hlen],msglen, + phdr->seq_num, phdr->auth_type, iauth); + /* copy hashed authcode to header */ + memcpy(&pdata[13],iauth,16); + } + } + idata[hlen-1] = (uchar)msglen; + ilen = hlen + msglen; + // rlen = sizeof(rdata);; + + if (fdebuglan > 2) + dbg_dump("lan_send_sol sendto",idata,ilen,1); + flags = 0; + sz = ipmilan_sendto(pconn->sockfd,idata,ilen,flags, + (struct sockaddr *)&_destaddr,_destaddr_len); + if (fdebuglan) dbglog("lan_send_sol, sent %d bytes\n",sz); + if (sz < 1) { + lasterr = get_LastError(); + if (fdebuglan) show_LastError("lan_send_sol",lasterr); + rv = LAN_ERR_SEND_FAIL; + os_usleep(0,5000); + } + else { + phdr->seq_num = inc_seq_num( phdr->seq_num ); + rv = 0; + } + + if (rsp != NULL) { + rsp->len = 0; +#ifdef RCV_EXTRA + if (rv == 0) { /*send was ok, try to receive*/ + rv = lan_recv_sol( rsp ); + if (fdebuglan) printf("lan_recv_sol ret = %d, len = %d\n",rv,rsp->len); + } +#endif + } + return(rv); +} + +int lan_keepalive( uchar bType ) +{ + int rv = 0; +#ifdef KAL_OK + if (bType == 2) { + /* don't try if no data sent yet */ + if (sol_snd_seq == 0) return(rv); + /* use lan_send_sol, but with 0 length data */ + rv = lan_send_sol(NULL,0,NULL); + } + else +#endif + { + uchar devrec[16]; + rv = ipmi_getdeviceid(devrec,16,0); + } + return(rv); +} + +int lan_recv_sol( SOL_RSP_PKT *rsp ) +{ + static uchar rsdata[MAX_BUFFER_SIZE]; /*LANplus allows 1024*/ + uchar rdata[MAX_BUFFER_SIZE]; + int rlen, hlen; + IPMI_HDR *phdr; + int fdoauth = 1; + uchar *pdata; + int itry; + int flags; + int rv = -1; + + phdr = &ipmi_hdr; + rsp->data = rsdata; + fdoauth = 0; + hlen = SOL_HLEN; + rlen = 0; + if (fdebuglan) + printf("lan_recv_sol, fdebug=%d, fpdbg=%p\n",fdebuglan,fpdbg); + // for (itry = 0; (itry < ipmi_try) && (rlen == 0); itry++) + for (itry = 0; (itry < 1) && (rlen == 0); itry++) + { + /* receive the response */ + rv = fd_wait(pconn->sockfd, ipmi_timeout,0); + if (rv != 0) { + if (fdebuglan) fprintf(fpdbg,"lan_recv_sol timeout\n"); + rv = LAN_ERR_RECV_FAIL; + os_usleep(0,5000); + continue; /* ok to retry */ + } + flags = RECV_MSG_FLAGS; + rlen = ipmilan_recvfrom(pconn->sockfd,rdata,sizeof(rdata),flags, + (struct sockaddr *)&_destaddr,&_destaddr_len); + if (rlen < 0) { + lasterr = get_LastError(); + if (fdebuglan) show_LastError("ipmilan_recvfrom",lasterr); + rv = rlen; /* -3 = LAN_ERR_RECV_FAIL */ + rlen = 0; + rsp->len = rlen; + if (lasterr == econnrefused) continue; /*try again*/ + else break; /* goto EXIT; */ + } else { /* successful receive */ + rv = 0; + if (fdebuglan) { + dump_buf("lan_recv_sol rdata",rdata,rlen,1); /*SOL*/ + } + if (rdata[4] == IPMI_SESSION_AUTHTYPE_NONE) { /* if AUTH_NONE */ + /* may have tried with auth, but denied, Dell 1855 */ + phdr->auth_type = IPMI_SESSION_AUTHTYPE_NONE; + hlen = SOL_HLEN; + } + net2h(&phdr->iseq_num,&rdata[5],4); /*incoming seq_num from hdr*/ + if (rlen >= hlen) { + pdata = &rdata[hlen]; + if (fdebuglan) + dump_buf("lan_recv_sol rsp",rdata,rlen,1); /*SOL*/ + rlen -= hlen; + if (rlen >= 5) { /*have some data*/ + /* 0=seq, 1=ack, 2=cnt, 3=ctl, 4=rsv */ + sol_rcv_seq = pdata[0]; + sol_rcv_ctl = pdata[3]; + pdata = &rdata[hlen+5]; + rlen -= 5; + /* rlen should match rdata[hlen-1] */ + sol_rcv_cnt = (uchar)rlen; + } + rsp->type = PAYLOAD_TYPE_SOL; + rsp->len = rlen; + // sol15_decrypt(rsp->data,pdata,rlen, sol_seed_cnt); + memcpy(rsp->data,pdata,rlen); + } else { + if (fdebuglan) + printf("lan_recv_sol rlen %d < %d\n",rlen,hlen); /*fpdbg*/ + rsp->type = PAYLOAD_TYPE_SOL; + rsp->len = 0; + } + break; + } + } /*end for*/ + return(rv); +} +#endif + +uchar +cksum(const uchar *buf, register int len) +{ + register uchar csum; + register int i; + + /* 8-bit 2s compliment checksum */ + csum = 0; + for (i = 0; i < len; i++) + csum = (csum + buf[i]) % 256; + csum = -csum; + return(csum); +} + +/* + * ipmi_cmd_ipmb() + * This is an entry point for IPMB indirect commands. + * Embed the command withn a SendMessage command. + * + * The iseq value needs to be the same as ipmi_hdr.swseq if + * the session is ipmilan, so this routine was moved here. + * However, ipmi_cmd_ipmb will also work ok via ipmi_cmdraw + * for local commands. + */ +int ipmi_cmd_ipmb(uchar cmd, uchar netfn, uchar sa, uchar bus, uchar lun, + uchar *pdata, int sdata, uchar *presp, + int *sresp, uchar *pcc, char fdebugcmd) +{ + int rc, i, j; + uchar idata[IPMI_REQBUF_SIZE]; + uchar rdata[MAX_BUFFER_SIZE]; + uchar ilen; + int rlen; + uchar iseq; + IPMI_HDR *phdr; + char *pstr; + uchar fneedclear = 0; + uchar cc; + + if (fdebugcmd) printf("ipmi_cmd_ipmb(%02x,%02x,%02x,%02x,%02x) sdata=%d\n", + cmd,netfn,sa,bus,lun,sdata); + phdr = &ipmi_hdr; + iseq = phdr->swseq; + i = 0; + idata[i++] = bus; + j = i; + idata[i++] = sa; /*rsAddr (target)*/ + idata[i++] = (netfn << 2) | (lun & 0x03); /*metFm/rsLUN*/ + idata[i++] = cksum(&idata[j],2); + j = i; + idata[i++] = bmc_sa; /* rqAddr (main sa) */ + idata[i++] = (iseq << 2) | SMS_LUN; /*rqSeq num, SMS Message LUN = 0x02*/ + idata[i++] = cmd; + if (sdata > 0) { + memcpy(&idata[i],pdata,sdata); + i += sdata; + } + idata[i] = cksum(&idata[j],(i - j)); + ilen = ++i; + rlen = sizeof(rdata); + rc = ipmi_cmdraw(IPMB_SEND_MESSAGE,NETFN_APP, bmc_sa,PUBLIC_BUS,BMC_LUN, + idata, ilen, rdata, &rlen, pcc, fdebugcmd); + if (rc == 0x83 || *pcc == 0x83) { /*retry*/ + rlen = sizeof(rdata); + rc = ipmi_cmdraw(IPMB_SEND_MESSAGE,NETFN_APP, bmc_sa,PUBLIC_BUS,BMC_LUN, + idata, ilen, rdata, &rlen, pcc, fdebugcmd); + } + if (fdebugcmd) { + if (rc == 0 && *pcc == 0) + dump_buf("ipmb sendmsg ok",rdata,rlen,0); + else { + if (*pcc == 0x80) { pstr = "Invalid session handle"; } + else if (*pcc == 0x81) pstr = "Lost Arbitration"; + else if (*pcc == 0x82) pstr = "Bus Error"; + else if (*pcc == 0x83) pstr = "NAK on Write"; + else pstr = ""; + fprintf(fpdbg,"ipmb sendmsg error %d, cc %x %s\n",rc,*pcc,pstr); + } + } + if (presp == NULL || sresp == NULL) return(LAN_ERR_INVPARAM); + if (rc != 0 || *pcc != 0) { *sresp = 0; return(rc); } + if (*sresp < 0) return(LAN_ERR_TOO_SHORT); + + /* sent ok, now issue a GET_MESSAGE command to get the response. */ + for (i = 0; i < 10; i++) + { + rlen = sizeof(rdata); + rc = ipmi_cmdraw(IPMB_GET_MESSAGE,NETFN_APP, bmc_sa,PUBLIC_BUS,BMC_LUN, + idata, 0, rdata, &rlen, pcc, fdebugcmd); + if (fdebugcmd) printf("ipmb get_message rc=%d cc=%x\n",rc,*pcc); + if (rc == 0x80 || *pcc == 0x80) /*empty, so retry*/; + else if (rc == 0x83 || *pcc == 0x83) /*busy, so retry*/; + else break; /* done w success or error */ +#ifdef DOS + ; /*short wait*/ +#else + fd_wait(0,0,10); /* wait 1 msec before retry */ +#endif + /* will retry up to 10 msec for a get_message response on ipmb */ + } + if (rc == 0 && *pcc == 0) { + if (fdebugcmd) + dump_buf("ipmb getmsg ok",rdata,rlen,0); + i = 0; + if (rlen >= 8) { /* strip out the getmsg header */ + *pcc = rdata[6]; + rlen -= 8; + i = 7; + } + /* copy the data */ + if (rlen > *sresp) rlen = *sresp; + memcpy(presp,&rdata[i],rlen); + } else { + if (*pcc == 0x80) pstr = "buffer empty"; + else { fneedclear = 1; pstr = ""; } + if (fdebugcmd) + fprintf(fpdbg,"ipmb getmsg[%d] error %d, cc %x %s\n",i,rc,*pcc,pstr); + if (fneedclear) { /* Clear pending message flags */ + idata[0] = 0x03; /* clear EvtMsgBuf & RecvMsgQueue */ + rlen = 16; + rc = ipmi_cmdraw(IPMB_CLEAR_MSGF, NETFN_APP, + bmc_sa, PUBLIC_BUS,BMC_LUN, + idata, 1, rdata, &rlen, &cc, fdebugcmd); + } + rlen = 0; + } + *sresp = rlen; + return(rc); +} +/* end ipmilan.c */ + |