📄 ipmi_con_lan.cpp
字号:
m_session_id = 0; m_working_auth = eIpmiAuthTypeNone; m_recv_msg_map = 0; m_inbound_seq_num = 0; // start seq with 0 m_current_seq = 0; SaErrorT rv = AuthCap(); if ( rv != SA_OK ) return -1; rv = Challange(); if ( rv != SA_OK ) return -1; rv = ActiveSession(); if ( rv != SA_OK ) return -1; rv = SetSessionPriv(); if ( rv != SA_OK ) return rv; assert( m_num_outstanding == 0 ); // reset seq m_current_seq = 0; stdlog << "RMCP session is up.\n"; return SA_OK;}// Send a final "close session" to shut down the connection.voidcIpmiConLan::SendCloseSession(){ cIpmiAddr si( eIpmiAddrTypeSystemInterface ); cIpmiMsg msg( eIpmiNetfnApp, eIpmiCmdCloseSession ); IpmiSetUint32( msg.m_data, m_session_id ); msg.m_data_len = 4; cIpmiRequest r( si, msg ); r.m_seq = 1; IfSendCmd( &r );}voidcIpmiConLan::IfClose(){ if ( m_fd >= 0 ) { SendCloseSession(); close( m_fd ); m_fd = -1; if ( m_auth_method ) { delete m_auth_method; m_auth_method = 0; } }}voidcIpmiConLan::Reconnect(){ stdlog << "RMCP reconnection in progress.\n"; RequeueOutstanding(); GList *list = m_queue; m_queue = 0; while( true ) { // send a ping SendPing(); if ( WaitForPong( m_timeout ) == false ) continue; stdlog << "create new RMCP session.\n"; if ( CreateSession() == SA_OK ) break; } m_queue = list; stdlog << "RMCP reconnection done.\n";}SaErrorTcIpmiConLan::IfSendCmd( cIpmiRequest *r ){ IfAddrToSendAddr( r->m_addr, r->m_send_addr ); unsigned char data[dIpmiMaxLanLen]; unsigned char *tmsg; int pos; int msgstart; switch( r->m_send_addr.m_type ) { case eIpmiAddrTypeSystemInterface: case eIpmiAddrTypeIpmb: case eIpmiAddrTypeIpmbBroadcast: break; default: assert( 0 ); return SA_ERR_HPI_INVALID_PARAMS; } data[0] = 6; // RMCP version 1.0. data[1] = 0; data[2] = 0xff; data[3] = 0x07; data[4] = m_working_auth; IpmiSetUint32( data+5, m_outbound_seq_num ); IpmiSetUint32( data+9, m_session_id ); if ( m_working_auth == 0 ) tmsg = data + 14; else tmsg = data + 30; if ( r->m_send_addr.m_type == eIpmiAddrTypeSystemInterface ) { // It's a message straight to the BMC. tmsg[0] = 0x20; // To the BMC. tmsg[1] = (r->m_msg.m_netfn << 2) | r->m_send_addr.m_lun; tmsg[2] = Checksum( tmsg, 2 ); tmsg[3] = 0x81; // Remote console IPMI Software ID tmsg[4] = r->m_seq << 2; tmsg[5] = r->m_msg.m_cmd; memcpy( tmsg + 6, r->m_msg.m_data, r->m_msg.m_data_len ); pos = r->m_msg.m_data_len + 6; tmsg[pos] = Checksum( tmsg + 3, pos - 3 ); pos++; } else { // It's an IPMB address, route it using a send message // command. pos = 0; tmsg[pos++] = 0x20; // BMC is the bridge. tmsg[pos++] = (eIpmiNetfnApp << 2) | 0; tmsg[pos++] = Checksum( tmsg, 2 ); tmsg[pos++] = 0x81; // Remote console IPMI Software ID tmsg[pos++] = r->m_seq << 2; tmsg[pos++] = eIpmiCmdSendMsg; tmsg[pos++] = r->m_send_addr.m_channel & 0xf | (1 << 6); // Turn on tracking if ( r->m_send_addr.m_type == eIpmiAddrTypeIpmbBroadcast ) tmsg[pos++] = 0; // Do a broadcast. msgstart = pos; tmsg[pos++] = r->m_send_addr.m_slave_addr; tmsg[pos++] = (r->m_msg.m_netfn << 2) | r->m_send_addr.m_lun; tmsg[pos++] = Checksum( tmsg + msgstart, 2 ); msgstart = pos; tmsg[pos++] = 0x20; tmsg[pos++] = (r->m_seq << 2) | 2; // Add 2 as the SMS Lun tmsg[pos++] = r->m_msg.m_cmd; memcpy( tmsg + pos, r->m_msg.m_data, r->m_msg.m_data_len ); pos += r->m_msg.m_data_len; tmsg[pos] = Checksum( tmsg + msgstart, pos - msgstart ); pos++; tmsg[pos] = Checksum( tmsg + 3, pos - 3 ); pos++; } if ( m_working_auth == 0 ) { // No authentication, so no authcode. data[13] = pos; pos += 14; // Convert to pos in data } else { data[29] = pos; int rv = AuthGen( data+13, data+9, data+5, tmsg, pos ); if ( rv ) return SA_ERR_HPI_INVALID_PARAMS; pos += 30; // Convert to pos in data } // Increment the outbound number, but make sure it's not zero. If // it's already zero, ignore it, we are in pre-setup. if ( m_outbound_seq_num != 0 ) { m_outbound_seq_num++; if ( m_outbound_seq_num == 0 ) m_outbound_seq_num++; } int rv = sendto( m_fd, data, pos, 0, (struct sockaddr *)&m_ip_addr, sizeof(struct sockaddr_in) ); if ( rv == -1 ) return SA_ERR_HPI_NOT_PRESENT; return SA_OK;}cIpmiConLan::tResponseTypecIpmiConLan::ReadResponse( int &seq, cIpmiAddr &addr, cIpmiMsg &msg ){ unsigned char data[dIpmiMaxLanLen]; struct sockaddr ipaddrd; struct sockaddr_in *ipaddr; int len; socklen_t from_len; uint32_t sess_id; unsigned char *tmsg; unsigned int data_len; from_len = sizeof( ipaddrd ); len = recvfrom( m_fd, data, dIpmiMaxLanLen, 0, &ipaddrd, &from_len ); if ( len < 0 ) return eResponseTypeError; //stdlog << "rmcp msg: " ); //IpmiLogHex( data, len ); // Make sure the source IP matches what we expect the other end to // be. ipaddr = (struct sockaddr_in *)(void *)&ipaddrd; if ( (ipaddr->sin_port != m_ip_addr.sin_port) || (ipaddr->sin_addr.s_addr != m_ip_addr.sin_addr.s_addr) ) { stdlog << "Dropped message due to invalid IP !\n"; return eResponseTypeError; } // Validate the length first, so we know that all the data in the // buffer we will deal with is valid. if ( len < 21 ) { // Minimum size of an IPMI msg. stdlog << "Dropped message because too small(1)\n"; return eResponseTypeError; } // Validate the RMCP portion of the message. if ( data[0] != 6 || data[2] != 0xff ) { stdlog << "Dropped message not valid IPMI/RMCP !\n"; return eResponseTypeError; } if ( data[3] == 0x06 ) { unsigned int asf_iana = IpmiGetUint32( data+4 ); if ( asf_iana != dAsfIana || data[8] != 0x40 ) { stdlog.Log( "Dropped message not valid RMCP pong message %04x, %04x, %02x !\n", asf_iana, dAsfIana, data[8] ); return eResponseTypeError; } m_ping_count--; stdlog << "reading RMCP pong.\n"; return eResponseTypePong; } if ( data[3] != 0x07 ) { stdlog << "Dropped message not valid IPMI/RMCP\n"; return eResponseTypeError; } if ( data[4] == 0 ) { // No authentication. if ( len < data[13] + 14 ) { // Not enough data was supplied, reject the message. stdlog << "Dropped message because too small(2)\n"; return eResponseTypeError; } data_len = data[13]; } else { if ( len < 37 ) { // Minimum size of an authenticated IPMI msg. stdlog << "Dropped message because too small(3)\n"; return eResponseTypeError; } // authcode in message, add 16 to the above checks. if ( len < data[29] + 30 ) { // Not enough data was supplied, reject the message. stdlog << "Dropped message because too small(4)\n"; return eResponseTypeError; } data_len = data[29]; } // Drop if the authtypes are incompatible. if ( m_working_auth != data[4] ) { stdlog << "Dropped message not valid authtype\n"; return eResponseTypeError; } // Drop if sessions ID's don't match. sess_id = IpmiGetUint32( data+9 ); if ( sess_id != m_session_id ) { stdlog << "Dropped message not valid session id " << sess_id << " != " << m_session_id << " !\n"; return eResponseTypeError; } seq = IpmiGetUint32( data+5 ); if ( data[4] != 0 ) { // Validate the message's authcode. Do this before checking // the session seq num so we know the data is valid. int rv = AuthCheck( data+9, data+5, data+30, data[29], data+13 ); if ( rv ) { stdlog << "Dropped message auth fail !\n"; return eResponseTypeError; } tmsg = data + 30; } else tmsg = data + 14; // Check the sequence number. if ( seq - m_inbound_seq_num <= 8 ) { // It's after the current sequence number, but within 8. We // move the sequence number forward. m_recv_msg_map <<= seq - m_inbound_seq_num; m_recv_msg_map |= 1; m_inbound_seq_num = seq; } else if ( m_inbound_seq_num - seq <= 8 ) { // It's before the current sequence number, but within 8. uint8_t bit = 1 << (m_inbound_seq_num - seq); if ( m_recv_msg_map & bit ) { stdlog << "Dropped message duplicate\n"; return eResponseTypeError; } m_recv_msg_map |= bit; } else { // It's outside the current sequence number range, discard // the packet. stdlog << "Dropped message out of seq range\n"; return eResponseTypeError; } // Now we have an authentic in-sequence message. // We don't check the checksums, because the network layer should // validate all this for us. if ( tmsg[5] == eIpmiCmdReadEventMsgBuffer && (tmsg[1] >> 2) == eIpmiNetfnAppRsp ) { // event if ( tmsg[6] != 0 ) { // An error getting the events, just ignore it. stdlog << "Dropped message err getting event\n"; return eResponseTypeError; } addr.m_type = eIpmiAddrTypeIpmb; addr.m_slave_addr = tmsg[3]; addr.m_lun = tmsg[4] & 0x3; addr.m_channel = 0; msg.m_netfn = (tIpmiNetfn)(tmsg[1] >> 2); msg.m_cmd = (tIpmiCmd)tmsg[5]; msg.m_data_len = data_len - 6 - 2; /* Remove completion code and checksum */ memcpy( msg.m_data, tmsg + 6 + 1, msg.m_data_len ); return eResponseTypeEvent; } seq = tmsg[4] >> 2; if ( m_outstanding[seq] == 0 ) { stdlog << "Dropped message seq not in use: " << (unsigned char)seq << " !\n"; return eResponseTypeError; } if ( tmsg[5] == eIpmiCmdSendMsg && (tmsg[1] >> 2) == eIpmiNetfnAppRsp ) { // It's a response to a sent message. // FIXME - this entire thing is a cheap hack. if ( tmsg[6] != 0 ) { // Got an error from the send message. We don't have any // IPMB information to work with, so just extract it from // the message. addr = m_outstanding[seq]->m_send_addr; // Just in case it's a broadcast. addr.m_type = eIpmiAddrTypeIpmb; msg.m_netfn = (tIpmiNetfn)(m_outstanding[seq]->m_msg.m_netfn | 1); msg.m_cmd = m_outstanding[seq]->m_msg.m_cmd; msg.m_data[0] = tmsg[6]; msg.m_data_len = 1; } else { if ( data_len < 15 ) return eResponseTypeError; if ( tmsg[10] == m_slave_addr ) addr.Si(); else { addr.m_type = eIpmiAddrTypeIpmb; addr.m_channel = m_outstanding[seq]->m_send_addr.m_channel; addr.m_slave_addr = tmsg[10]; } addr.m_lun = tmsg[11] & 0x3; msg.m_netfn = (tIpmiNetfn)(tmsg[8] >> 2); msg.m_cmd = (tIpmiCmd)tmsg[12]; msg.m_data_len = data_len - 15; memcpy( msg.m_data, tmsg + 13, msg.m_data_len ); } } else if ( m_outstanding[seq]->m_send_addr.m_type == eIpmiAddrTypeSystemInterface && tmsg[3] == m_slave_addr ) { // In some cases, a message from the IPMB looks like it came // from the BMC itself, IMHO a misinterpretation of the // errata. IPMIv1_5_rev1_1_0926 markup, section 6.12.4, // didn't clear things up at all. Some manufacturers have // interpreted it this way, but IMHO it is incorrect. addr = m_outstanding[seq]->m_send_addr; msg.m_netfn = (tIpmiNetfn)(tmsg[1] >> 2); msg.m_cmd = (tIpmiCmd)tmsg[5]; msg.m_data_len = data_len - 7; memcpy( msg.m_data, tmsg+6, msg.m_data_len ); } else { // It's not encapsulated in a send message response. if ( tmsg[3] == m_slave_addr ) { // It's directly from the BMC, so it's a system interface // message. addr.Si(); addr.m_lun = tmsg[1] & 3; } else { // A message from the IPMB. addr.m_type = eIpmiAddrTypeIpmb; // This is a hack, but the channel does not come back in the // message. So we use the channel from the original // instead. addr.m_channel = m_outstanding[seq]->m_send_addr.m_channel; addr.m_slave_addr = tmsg[3]; addr.m_lun = tmsg[4] & 0x3; } msg.m_netfn = (tIpmiNetfn)(tmsg[1] >> 2); msg.m_cmd = (tIpmiCmd)tmsg[5]; msg.m_data_len = data_len - 6 - 1; // Remove the checksum memcpy( msg.m_data, tmsg+6, msg.m_data_len ); } if ( (tIpmiNetfn)(m_outstanding[seq]->m_msg.m_netfn | 1) != msg.m_netfn || m_outstanding[seq]->m_msg.m_cmd != msg.m_cmd ) { stdlog << "Message mismatch seq " << (unsigned char)seq << ":\n" << "read "; IpmiLogDataMsg( addr, msg ); stdlog << "\n"; stdlog << "expt "; IpmiLogDataMsg( m_outstanding[seq]->m_send_addr, m_outstanding[seq]->m_msg ); stdlog << "\n"; stdlog.Hex( data, len ); stdlog << "len " << len << ", m_num_outstanding " << m_num_outstanding << ", m_queue " << (m_queue ? "full" : "empty") << "\n"; assert( 0 ); return eResponseTypeError; } if ( m_outstanding[seq]->m_send_addr != m_outstanding[seq]->m_addr ) addr = m_outstanding[seq]->m_addr; return eResponseTypeMessage;}voidcIpmiConLan::IfReadResponse(){ int seq; cIpmiAddr addr; cIpmiMsg msg; tResponseType rt = ReadResponse( seq, addr, msg ); switch( rt ) { case eResponseTypeError: break; case eResponseTypePong: stdlog << "connection seems to be ok.\n"; HandleCheckConnection( true ); break; case eResponseTypeTimeout: break; case eResponseTypeMessage: HandleResponse( seq, addr, msg ); break; case eResponseTypeEvent: HandleEvent( addr, msg ); break; }}boolcIpmiConLan::IfCheckConnection( cTime &timeout ){ stdlog << "check connection.\n"; SendPing(); timeout = cTime::Now(); timeout += m_timeout; return true;}voidcIpmiConLan::IfCheckConnectionTimeout(){ stdlog << "connection timeout !\n"; m_queue_lock.Lock(); Reconnect(); m_queue_lock.Unlock();}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -