2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
2N/A * Use is subject to license terms.
2N/A */
2N/A
2N/A#include <stdlib.h>
2N/A#include <stdio.h>
2N/A#include <assert.h>
2N/A#include <inttypes.h>
2N/A#include <string.h>
2N/A#include <sys/types.h>
2N/A#include <sys/socket.h>
2N/A#include <netinet/in.h>
2N/A#include <arpa/inet.h>
2N/A#include <errno.h>
2N/A#include <unistd.h>
2N/A#include <netdb.h>
2N/A#include <fcntl.h>
2N/A
2N/A#include "libipmi.h"
2N/A#include "ipmi_lan.h"
2N/A#include "ipmi_impl.h"
2N/A
2N/A#define DEF_IPMI_LAN_TIMEOUT 3 /* seconds */
2N/A#define DEF_IPMI_LAN_NUM_RETRIES 5
2N/A#define IPMI_LAN_CHANNEL_E 0x0e
2N/A
2N/Atypedef struct ipmi_rs {
2N/A uint8_t ir_data[IPMI_BUF_SIZE];
2N/A int ir_dlen;
2N/A ipmi_msg_hdr_t ir_ihdr;
2N/A uint8_t ir_ccode;
2N/A} ipmi_rs_t;
2N/A
2N/Astatic ipmi_rs_t *ipmi_lan_poll_recv(ipmi_handle_t *);
2N/A
2N/Atypedef struct ipmi_rq_entry {
2N/A ipmi_list_t ire_list;
2N/A ipmi_cmd_t ire_req;
2N/A uint8_t ire_target_cmd;
2N/A uint8_t ire_rq_seq;
2N/A uint8_t *ire_msg_data;
2N/A int ire_msg_len;
2N/A} ipmi_rq_entry_t;
2N/A
2N/Aipmi_rq_entry_t *ipmi_req_entries = NULL;
2N/A
2N/A/*
2N/A * LAN transport-specific data
2N/A */
2N/Atypedef struct ipmi_lan {
2N/A ipmi_handle_t *il_ihp;
2N/A char il_host[MAXHOSTNAMELEN + 1];
2N/A uint16_t il_port;
2N/A char il_user[17];
2N/A char il_authcode[IPMI_AUTHCODE_BUF_SIZE + 1];
2N/A uint8_t il_challenge[16];
2N/A uint32_t il_session_id;
2N/A int il_sd;
2N/A boolean_t il_send_authcode;
2N/A boolean_t il_session_active;
2N/A uint8_t il_authtype;
2N/A uint8_t il_privlvl;
2N/A uint8_t il_num_retries;
2N/A uint32_t il_in_seq;
2N/A uint32_t il_timeout;
2N/A struct sockaddr_in il_addr;
2N/A socklen_t il_addrlen;
2N/A} ipmi_lan_t;
2N/A
2N/A/*
2N/A * Calculate and returns IPMI checksum
2N/A *
2N/A * Checksum algorithm is described in Section 13.8
2N/A *
2N/A * d: buffer to check
2N/A * s: position in buffer to start checksum from
2N/A */
2N/Astatic uint8_t
2N/Aipmi_csum(uint8_t *d, int s)
2N/A{
2N/A uint8_t c = 0;
2N/A for (; s > 0; s--, d++)
2N/A c += *d;
2N/A return (-c);
2N/A}
2N/A
2N/Astatic ipmi_rq_entry_t *
2N/Aipmi_req_add_entry(ipmi_handle_t *ihp, ipmi_cmd_t *req)
2N/A{
2N/A ipmi_rq_entry_t *e;
2N/A
2N/A if ((e = ipmi_zalloc(ihp, sizeof (ipmi_rq_entry_t))) == NULL)
2N/A return (NULL);
2N/A
2N/A (void) memcpy(&e->ire_req, req, sizeof (ipmi_cmd_t));
2N/A ipmi_list_append(&ipmi_req_entries->ire_list, e);
2N/A
2N/A return (e);
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/Astatic ipmi_rq_entry_t *
2N/Aipmi_req_lookup_entry(ipmi_handle_t *ihp, uint8_t seq, uint8_t cmd)
2N/A{
2N/A ipmi_rq_entry_t *e;
2N/A
2N/A for (e = ipmi_list_next(&ipmi_req_entries->ire_list); e != NULL;
2N/A e = ipmi_list_next(e))
2N/A if (e->ire_rq_seq == seq && e->ire_req.ic_cmd == cmd)
2N/A return (e);
2N/A
2N/A return (NULL);
2N/A}
2N/A
2N/Astatic void
2N/Aipmi_req_remove_entry(ipmi_handle_t *ihp, uint8_t seq, uint8_t cmd)
2N/A{
2N/A ipmi_rq_entry_t *e;
2N/A
2N/A e = ipmi_req_lookup_entry(ihp, seq, cmd);
2N/A
2N/A if (e) {
2N/A ipmi_list_delete(&ipmi_req_entries->ire_list, e);
2N/A ipmi_free(ihp, e->ire_msg_data);
2N/A ipmi_free(ihp, e);
2N/A }
2N/A}
2N/A
2N/Astatic void
2N/Aipmi_req_clear_entries(ipmi_handle_t *ihp)
2N/A{
2N/A ipmi_rq_entry_t *e;
2N/A
2N/A while ((e = ipmi_list_next(&ipmi_req_entries->ire_list)) != NULL) {
2N/A ipmi_list_delete(&ipmi_req_entries->ire_list, e);
2N/A ipmi_free(ihp, e);
2N/A }
2N/A}
2N/A
2N/Astatic int
2N/Aget_random(void *buf, uint_t len)
2N/A{
2N/A int fd;
2N/A
2N/A assert(buf != NULL && len > 0);
2N/A if ((fd = open("/dev/urandom", O_RDONLY)) < 0)
2N/A return (-1);
2N/A
2N/A if (read(fd, buf, len) < 0) {
2N/A (void) close(fd);
2N/A return (-1);
2N/A }
2N/A (void) close(fd);
2N/A return (0);
2N/A}
2N/A
2N/Astatic int
2N/Aipmi_lan_send_packet(ipmi_handle_t *ihp, uint8_t *data, int dlen)
2N/A{
2N/A ipmi_lan_t *ilp = (ipmi_lan_t *)ihp->ih_tdata;
2N/A
2N/A return (send(ilp->il_sd, data, dlen, 0));
2N/A}
2N/A
2N/Astatic ipmi_rs_t *
2N/Aipmi_lan_recv_packet(ipmi_handle_t *ihp)
2N/A{
2N/A static ipmi_rs_t rsp;
2N/A fd_set read_set, err_set;
2N/A struct timeval tmout;
2N/A ipmi_lan_t *ilp = (ipmi_lan_t *)ihp->ih_tdata;
2N/A int ret;
2N/A
2N/A FD_ZERO(&read_set);
2N/A FD_SET(ilp->il_sd, &read_set);
2N/A
2N/A FD_ZERO(&err_set);
2N/A FD_SET(ilp->il_sd, &err_set);
2N/A
2N/A tmout.tv_sec = ilp->il_timeout;
2N/A tmout.tv_usec = 0;
2N/A
2N/A ret = select(ilp->il_sd + 1, &read_set, NULL, &err_set, &tmout);
2N/A if (ret < 0 || FD_ISSET(ilp->il_sd, &err_set) ||
2N/A !FD_ISSET(ilp->il_sd, &read_set))
2N/A return (NULL);
2N/A
2N/A /*
2N/A * The first read may return ECONNREFUSED because the rmcp ping
2N/A * packet--sent to UDP port 623--will be processed by both the
2N/A * BMC and the OS.
2N/A *
2N/A * The problem with this is that the ECONNREFUSED takes
2N/A * priority over any other received datagram; that means that
2N/A * the Connection Refused shows up _before_ the response packet,
2N/A * regardless of the order they were sent out. (unless the
2N/A * response is read before the connection refused is returned)
2N/A */
2N/A ret = recv(ilp->il_sd, &rsp.ir_data, IPMI_BUF_SIZE, 0);
2N/A
2N/A if (ret < 0) {
2N/A FD_ZERO(&read_set);
2N/A FD_SET(ilp->il_sd, &read_set);
2N/A
2N/A FD_ZERO(&err_set);
2N/A FD_SET(ilp->il_sd, &err_set);
2N/A
2N/A tmout.tv_sec = ilp->il_timeout;
2N/A tmout.tv_usec = 0;
2N/A
2N/A ret = select(ilp->il_sd + 1, &read_set, NULL, &err_set, &tmout);
2N/A if (ret < 0) {
2N/A if (FD_ISSET(ilp->il_sd, &err_set) ||
2N/A !FD_ISSET(ilp->il_sd, &read_set))
2N/A return (NULL);
2N/A
2N/A ret = recv(ilp->il_sd, &rsp.ir_data, IPMI_BUF_SIZE, 0);
2N/A if (ret < 0)
2N/A return (NULL);
2N/A }
2N/A }
2N/A
2N/A if (ret == 0)
2N/A return (NULL);
2N/A
2N/A rsp.ir_data[ret] = '\0';
2N/A rsp.ir_dlen = ret;
2N/A
2N/A return (&rsp);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * ASF/RMCP Pong Message
2N/A *
2N/A * See section 13.2.4
2N/A */
2N/Astruct rmcp_pong {
2N/A rmcp_hdr_t rp_rmcp;
2N/A asf_hdr_t rp_asf;
2N/A uint32_t rp_iana;
2N/A uint32_t rp_oem;
2N/A uint8_t rp_sup_entities;
2N/A uint8_t rp_sup_interact;
2N/A uint8_t rp_reserved[6];
2N/A};
2N/A
2N/A/*
2N/A * parse response RMCP "pong" packet
2N/A *
2N/A * return -1 if ping response not received
2N/A * returns 0 if IPMI is NOT supported
2N/A * returns 1 if IPMI is supported
2N/A */
2N/A/*ARGSUSED*/
2N/Astatic int
2N/Aipmi_handle_pong(ipmi_handle_t *ihp, ipmi_rs_t *rsp)
2N/A{
2N/A struct rmcp_pong *pong;
2N/A
2N/A if (rsp == NULL)
2N/A return (-1);
2N/A
2N/A /*LINTED: E_BAD_PTR_CAST_ALIGN*/
2N/A pong = (struct rmcp_pong *)rsp->ir_data;
2N/A
2N/A return ((pong->rp_sup_entities & 0x80) ? 1 : 0);
2N/A}
2N/A
2N/A/*
2N/A * Build and send RMCP presence ping message
2N/A */
2N/Astatic int
2N/Aipmi_lan_ping(ipmi_handle_t *ihp)
2N/A{
2N/A rmcp_hdr_t rmcp_ping;
2N/A asf_hdr_t asf_ping;
2N/A uint8_t *data;
2N/A int rv, dlen = sizeof (rmcp_ping) + sizeof (asf_ping);
2N/A
2N/A (void) memset(&rmcp_ping, 0, sizeof (rmcp_ping));
2N/A rmcp_ping.rh_version = RMCP_VERSION_1;
2N/A rmcp_ping.rh_msg_class = RMCP_CLASS_ASF;
2N/A rmcp_ping.rh_seq = 0xff;
2N/A
2N/A (void) memset(&asf_ping, 0, sizeof (asf_ping));
2N/A asf_ping.ah_iana = htonl(ASF_RMCP_IANA);
2N/A asf_ping.ah_msg_type = ASF_TYPE_PING;
2N/A
2N/A if ((data = ipmi_zalloc(ihp, dlen)) == NULL)
2N/A return (-1);
2N/A
2N/A (void) memcpy(data, &rmcp_ping, sizeof (rmcp_ping));
2N/A (void) memcpy(data + sizeof (rmcp_ping), &asf_ping, sizeof (asf_ping));
2N/A
2N/A rv = ipmi_lan_send_packet(ihp, data, dlen);
2N/A
2N/A ipmi_free(ihp, data);
2N/A
2N/A if (rv < 0)
2N/A return (ipmi_set_error(ihp, EIPMI_LAN_PING_FAILED, NULL));
2N/A
2N/A if (ipmi_lan_poll_recv(ihp) == NULL)
2N/A return (ipmi_set_error(ihp, EIPMI_LAN_PING_FAILED, NULL));
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Astatic ipmi_rs_t *
2N/Aipmi_lan_poll_recv(ipmi_handle_t *ihp)
2N/A{
2N/A rmcp_hdr_t rmcp_rsp;
2N/A ipmi_rs_t *rsp;
2N/A ipmi_rq_entry_t *entry;
2N/A int off = 0, rv;
2N/A ipmi_lan_t *ilp = (ipmi_lan_t *)ihp->ih_tdata;
2N/A uint8_t rsp_authtype;
2N/A
2N/A rsp = ipmi_lan_recv_packet(ihp);
2N/A
2N/A while (rsp != NULL) {
2N/A
2N/A /* parse response headers */
2N/A (void) memcpy(&rmcp_rsp, rsp->ir_data, 4);
2N/A
2N/A switch (rmcp_rsp.rh_msg_class) {
2N/A case RMCP_CLASS_ASF:
2N/A /* ping response packet */
2N/A rv = ipmi_handle_pong(ihp, rsp);
2N/A return ((rv <= 0) ? NULL : rsp);
2N/A case RMCP_CLASS_IPMI:
2N/A /* handled by rest of function */
2N/A break;
2N/A default:
2N/A /* Invalid RMCP class */
2N/A rsp = ipmi_lan_recv_packet(ihp);
2N/A continue;
2N/A }
2N/A
2N/A off = sizeof (rmcp_hdr_t);
2N/A rsp_authtype = rsp->ir_data[off];
2N/A if (ilp->il_send_authcode && (rsp_authtype || ilp->il_authtype))
2N/A off += 26;
2N/A else
2N/A off += 10;
2N/A
2N/A (void) memcpy(&rsp->ir_ihdr, (void *)(rsp->ir_data + off),
2N/A sizeof (rsp->ir_ihdr));
2N/A rsp->ir_ihdr.imh_seq = rsp->ir_ihdr.imh_seq >> 2;
2N/A off += sizeof (rsp->ir_ihdr);
2N/A rsp->ir_ccode = rsp->ir_data[off++];
2N/A
2N/A entry = ipmi_req_lookup_entry(ihp, rsp->ir_ihdr.imh_seq,
2N/A rsp->ir_ihdr.imh_cmd);
2N/A if (entry) {
2N/A ipmi_req_remove_entry(ihp, rsp->ir_ihdr.imh_seq,
2N/A rsp->ir_ihdr.imh_cmd);
2N/A } else {
2N/A rsp = ipmi_lan_recv_packet(ihp);
2N/A continue;
2N/A }
2N/A break;
2N/A }
2N/A
2N/A /* shift response data to start of array */
2N/A if (rsp && rsp->ir_dlen > off) {
2N/A rsp->ir_dlen -= off + 1;
2N/A (void) memmove(rsp->ir_data, rsp->ir_data + off, rsp->ir_dlen);
2N/A (void) memset(rsp->ir_data + rsp->ir_dlen, 0,
2N/A IPMI_BUF_SIZE - rsp->ir_dlen);
2N/A }
2N/A return (rsp);
2N/A}
2N/A
2N/A/*
2N/A * IPMI LAN Request Message Format
2N/A *
2N/A * See section 13.8
2N/A *
2N/A * +---------------------+
2N/A * | rmcp_hdr_t | 4 bytes
2N/A * +---------------------+
2N/A * | v15_session_hdr_t | 9 bytes
2N/A * +---------------------+
2N/A * | [authcode] | 16 bytes (if AUTHTYPE != none)
2N/A * +---------------------+
2N/A * | msg length | 1 byte
2N/A * +---------------------+
2N/A * | ipmi_msg_hdr_t | 6 bytes
2N/A * +---------------------+
2N/A * | [msg data] | variable
2N/A * +---------------------+
2N/A * | msg data checksum | 1 byte
2N/A * +---------------------+
2N/A */
2N/Astatic ipmi_rq_entry_t *
2N/Aipmi_lan_build_cmd(ipmi_handle_t *ihp, ipmi_cmd_t *req)
2N/A{
2N/A ipmi_lan_t *ilp = (ipmi_lan_t *)ihp->ih_tdata;
2N/A rmcp_hdr_t rmcp_hdr;
2N/A v15_session_hdr_t session_hdr;
2N/A ipmi_msg_hdr_t msg_hdr;
2N/A uint8_t *msg;
2N/A int cs, tmp, off = 0, len;
2N/A ipmi_rq_entry_t *entry;
2N/A static int curr_seq = 0;
2N/A
2N/A if (curr_seq >= 64)
2N/A curr_seq = 0;
2N/A
2N/A if ((entry = ipmi_req_add_entry(ihp, req)) == NULL)
2N/A return (NULL);
2N/A
2N/A len = req->ic_dlen + 29;
2N/A if (ilp->il_send_authcode && ilp->il_authtype)
2N/A len += 16;
2N/A
2N/A if ((msg = ipmi_zalloc(ihp, len)) == NULL)
2N/A /* ipmi_errno set */
2N/A return (NULL);
2N/A
2N/A /* RMCP header */
2N/A (void) memset(&rmcp_hdr, 0, sizeof (rmcp_hdr));
2N/A rmcp_hdr.rh_version = RMCP_VERSION_1;
2N/A rmcp_hdr.rh_msg_class = RMCP_CLASS_IPMI;
2N/A rmcp_hdr.rh_seq = 0xff;
2N/A (void) memcpy(msg, &rmcp_hdr, sizeof (rmcp_hdr));
2N/A off = sizeof (rmcp_hdr);
2N/A
2N/A /* IPMI session header */
2N/A (void) memset(&session_hdr, 0, sizeof (session_hdr));
2N/A if (! ilp->il_send_authcode)
2N/A session_hdr.sh_authtype = 0x00;
2N/A else
2N/A /* hardcode passwd authentication */
2N/A session_hdr.sh_authtype = 0x04;
2N/A
2N/A (void) memcpy(&session_hdr.sh_seq, &ilp->il_in_seq, sizeof (uint32_t));
2N/A (void) memcpy(&session_hdr.sh_id, &ilp->il_session_id,
2N/A sizeof (uint32_t));
2N/A
2N/A (void) memcpy(msg + off, &session_hdr, sizeof (session_hdr));
2N/A off += sizeof (session_hdr);
2N/A
2N/A /* IPMI session authcode */
2N/A if (ilp->il_send_authcode && ilp->il_authtype) {
2N/A (void) memcpy(msg + off, ilp->il_authcode, 16);
2N/A off += 16;
2N/A }
2N/A
2N/A /* message length */
2N/A msg[off++] = req->ic_dlen + 7;
2N/A cs = off;
2N/A
2N/A /* IPMI message header */
2N/A (void) memset(&msg_hdr, 0, sizeof (msg_hdr));
2N/A msg_hdr.imh_addr1 = IPMI_BMC_SLAVE_ADDR;
2N/A msg_hdr.imh_lun = req->ic_lun;
2N/A msg_hdr.imh_netfn = req->ic_netfn;
2N/A tmp = off - cs;
2N/A msg_hdr.imh_csum = ipmi_csum(msg + cs, tmp);
2N/A cs = off;
2N/A msg_hdr.imh_addr2 = IPMI_BMC_SLAVE_ADDR;
2N/A entry->ire_rq_seq = curr_seq++;
2N/A msg_hdr.imh_seq = entry->ire_rq_seq << 2;
2N/A msg_hdr.imh_cmd = req->ic_cmd;
2N/A (void) memcpy(msg + off, &msg_hdr, sizeof (msg_hdr));
2N/A off += sizeof (msg_hdr);
2N/A
2N/A /* message data */
2N/A if (req->ic_dlen != 0) {
2N/A (void) memcpy(msg + off, req->ic_data, req->ic_dlen);
2N/A off += req->ic_dlen;
2N/A }
2N/A
2N/A /* message data checksum */
2N/A tmp = off - cs;
2N/A msg[off++] = ipmi_csum(msg + cs, tmp);
2N/A
2N/A if (ilp->il_in_seq) {
2N/A ilp->il_in_seq++;
2N/A if (ilp->il_in_seq == 0)
2N/A ilp->il_in_seq++;
2N/A }
2N/A
2N/A entry->ire_msg_len = off;
2N/A entry->ire_msg_data = msg;
2N/A
2N/A return (entry);
2N/A}
2N/A
2N/Astatic int
2N/Aipmi_lan_send(void *data, ipmi_cmd_t *cmd, ipmi_cmd_t *response,
2N/A int *completion)
2N/A{
2N/A ipmi_lan_t *ilp = (ipmi_lan_t *)data;
2N/A ipmi_rq_entry_t *entry = NULL;
2N/A ipmi_rs_t *rsp = NULL;
2N/A uint_t try = 0;
2N/A
2N/A for (;;) {
2N/A if ((entry = ipmi_lan_build_cmd(ilp->il_ihp, cmd)) == NULL)
2N/A return (-1);
2N/A
2N/A if (ipmi_lan_send_packet(ilp->il_ihp, entry->ire_msg_data,
2N/A entry->ire_msg_len) < 0) {
2N/A if (++try >= ilp->il_num_retries)
2N/A return (-1);
2N/A (void) usleep(5000);
2N/A continue;
2N/A }
2N/A
2N/A (void) usleep(100);
2N/A
2N/A if ((rsp = ipmi_lan_poll_recv(ilp->il_ihp)) != NULL)
2N/A break;
2N/A
2N/A (void) usleep(5000);
2N/A ipmi_req_remove_entry(ilp->il_ihp, entry->ire_rq_seq,
2N/A entry->ire_req.ic_cmd);
2N/A
2N/A if (++try >= ilp->il_num_retries)
2N/A return (-1);
2N/A }
2N/A response->ic_netfn = rsp->ir_ihdr.imh_netfn;
2N/A response->ic_lun = rsp->ir_ihdr.imh_lun;
2N/A response->ic_cmd = rsp->ir_ihdr.imh_cmd;
2N/A if (rsp->ir_ccode != 0) {
2N/A *completion = rsp->ir_ccode;
2N/A response->ic_dlen = 0;
2N/A response->ic_data = NULL;
2N/A } else {
2N/A *completion = 0;
2N/A response->ic_dlen = rsp->ir_dlen;
2N/A response->ic_data = rsp->ir_data;
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * IPMI Get Session Challenge Command
2N/A *
2N/A * Copies the returned session ID and 16-byte challenge string to the supplied
2N/A * buffers
2N/A *
2N/A * See section 22.16
2N/A */
2N/Astatic int
2N/Aipmi_get_session_challenge_cmd(ipmi_handle_t *ihp, uint32_t *session_id,
2N/A uint8_t *challenge)
2N/A{
2N/A ipmi_cmd_t cmd, resp;
2N/A ipmi_lan_t *ilp = (ipmi_lan_t *)ihp->ih_tdata;
2N/A char msg_data[17];
2N/A int ccode;
2N/A
2N/A (void) memset(msg_data, 0, 17);
2N/A
2N/A switch (ilp->il_authtype) {
2N/A case IPMI_SESSION_AUTHTYPE_NONE:
2N/A msg_data[0] = 0x00;
2N/A break;
2N/A case IPMI_SESSION_AUTHTYPE_MD2:
2N/A msg_data[0] = 0x01;
2N/A break;
2N/A case IPMI_SESSION_AUTHTYPE_MD5:
2N/A msg_data[0] = 0x02;
2N/A break;
2N/A case IPMI_SESSION_AUTHTYPE_PASSWORD:
2N/A msg_data[0] = 0x04;
2N/A break;
2N/A case IPMI_SESSION_AUTHTYPE_OEM:
2N/A msg_data[0] = 0x05;
2N/A break;
2N/A }
2N/A (void) memcpy(msg_data + 1, ilp->il_user, 16);
2N/A
2N/A cmd.ic_netfn = IPMI_NETFN_APP;
2N/A cmd.ic_lun = 0;
2N/A cmd.ic_cmd = IPMI_CMD_GET_SESSION_CHALLENGE;
2N/A cmd.ic_data = msg_data;
2N/A cmd.ic_dlen = 17;
2N/A
2N/A if (ipmi_lan_send(ilp, &cmd, &resp, &ccode) != 0 || ccode)
2N/A return (ipmi_set_error(ihp, EIPMI_LAN_CHALLENGE, NULL));
2N/A
2N/A (void) memcpy(session_id, resp.ic_data, 4);
2N/A (void) memcpy(challenge, (uint8_t *)resp.ic_data + 4, 16);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * IPMI Activate Session Command
2N/A *
2N/A * See section 22.17
2N/A */
2N/Astatic int
2N/Aipmi_activate_session_cmd(ipmi_handle_t *ihp)
2N/A{
2N/A ipmi_cmd_t cmd, resp;
2N/A ipmi_lan_t *ilp = (ipmi_lan_t *)ihp->ih_tdata;
2N/A uint8_t msg_data[22], *resp_data;
2N/A int ccode;
2N/A
2N/A cmd.ic_netfn = IPMI_NETFN_APP;
2N/A cmd.ic_lun = 0;
2N/A cmd.ic_cmd = IPMI_CMD_ACTIVATE_SESSION;
2N/A
2N/A switch (ilp->il_authtype) {
2N/A case IPMI_SESSION_AUTHTYPE_NONE:
2N/A msg_data[0] = 0x00;
2N/A break;
2N/A case IPMI_SESSION_AUTHTYPE_MD2:
2N/A msg_data[0] = 0x01;
2N/A break;
2N/A case IPMI_SESSION_AUTHTYPE_MD5:
2N/A msg_data[0] = 0x02;
2N/A break;
2N/A case IPMI_SESSION_AUTHTYPE_PASSWORD:
2N/A msg_data[0] = 0x04;
2N/A break;
2N/A case IPMI_SESSION_AUTHTYPE_OEM:
2N/A msg_data[0] = 0x05;
2N/A break;
2N/A }
2N/A msg_data[1] = ilp->il_privlvl;
2N/A
2N/A (void) memcpy(msg_data + 2, ilp->il_challenge, 16);
2N/A
2N/A /* setup initial outbound sequence number */
2N/A (void) get_random(msg_data + 18, 4);
2N/A
2N/A cmd.ic_data = msg_data;
2N/A cmd.ic_dlen = 22;
2N/A
2N/A ilp->il_send_authcode = B_TRUE;
2N/A
2N/A if (ipmi_lan_send(ilp, &cmd, &resp, &ccode) != 0 || ccode) {
2N/A ilp->il_send_authcode = B_FALSE;
2N/A return (ipmi_set_error(ihp, EIPMI_LAN_SESSION, NULL));
2N/A }
2N/A
2N/A resp_data = (uint8_t *)resp.ic_data;
2N/A (void) memcpy(&ilp->il_session_id, resp_data + 1, 4);
2N/A ilp->il_in_seq = resp_data[8] << 24 | resp_data[7] << 16 |
2N/A resp_data[6] << 8 | resp_data[5];
2N/A if (ilp->il_in_seq == 0)
2N/A ++ilp->il_in_seq;
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * See section 22.18
2N/A *
2N/A * returns privilege level or -1 on error
2N/A */
2N/Astatic int
2N/Aipmi_set_session_privlvl_cmd(ipmi_handle_t *ihp, uint8_t privlvl)
2N/A{
2N/A ipmi_cmd_t cmd, resp;
2N/A int ret = 0, ccode;
2N/A
2N/A if (privlvl > IPMI_SESSION_PRIV_OEM)
2N/A return (ipmi_set_error(ihp, EIPMI_BADPARAM, NULL));
2N/A
2N/A cmd.ic_netfn = IPMI_NETFN_APP;
2N/A cmd.ic_lun = 0;
2N/A cmd.ic_cmd = IPMI_CMD_SET_SESSION_PRIVLVL;
2N/A cmd.ic_data = &privlvl;
2N/A cmd.ic_dlen = 1;
2N/A
2N/A if (ipmi_lan_send(ihp->ih_tdata, &cmd, &resp, &ccode) != 0)
2N/A ret = ipmi_set_error(ihp, EIPMI_LAN_SETPRIV, NULL);
2N/A
2N/A return (ret);
2N/A}
2N/A
2N/A/*
2N/A * See section 22.19
2N/A */
2N/Astatic int
2N/Aipmi_close_session_cmd(ipmi_handle_t *ihp)
2N/A{
2N/A ipmi_lan_t *ilp = (ipmi_lan_t *)ihp->ih_tdata;
2N/A ipmi_cmd_t cmd, resp;
2N/A uint8_t msg_data[4];
2N/A int ret = 0, ccode;
2N/A
2N/A if (! ilp->il_session_active)
2N/A return (-1);
2N/A
2N/A (void) memcpy(&msg_data, &ilp->il_session_id, 4);
2N/A
2N/A cmd.ic_netfn = IPMI_NETFN_APP;
2N/A cmd.ic_lun = 0;
2N/A cmd.ic_cmd = IPMI_CMD_CLOSE_SESSION;
2N/A cmd.ic_data = msg_data;
2N/A cmd.ic_dlen = 4;
2N/A
2N/A if (ipmi_lan_send(ilp, &cmd, &resp, &ccode) != 0)
2N/A ret = -1;
2N/A
2N/A return (ret);
2N/A}
2N/A
2N/A/*
2N/A * IPMI LAN Session Activation
2N/A *
2N/A * See section 13.14
2N/A *
2N/A * 1. send "RMCP Presence Ping" message, response message will
2N/A * indicate whether the platform supports IPMI
2N/A * 2. send "Get Channel Authentication Capabilities" command
2N/A * with AUTHTYPE = none, response packet will contain information
2N/A * about supported challenge/response authentication types
2N/A * 3. send "Get Session Challenge" command with AUTHTYPE = none
2N/A * and indicate the authentication type in the message, response
2N/A * packet will contain challenge string and temporary session ID.
2N/A * 4. send "Activate Session" command, authenticated with AUTHTYPE
2N/A * sent in previous message. Also sends the initial value for
2N/A * the outbound sequence number for BMC.
2N/A * 5. BMC returns response confirming session activation and
2N/A * session ID for this session and initial inbound sequence.
2N/A */
2N/Astatic int
2N/Aipmi_lan_activate_session(ipmi_handle_t *ihp)
2N/A{
2N/A ipmi_lan_t *ilp = (ipmi_lan_t *)ihp->ih_tdata;
2N/A ipmi_channel_auth_caps_t *ac;
2N/A
2N/A if (ipmi_lan_ping(ihp) != 0)
2N/A return (-1);
2N/A
2N/A if ((ac = ipmi_get_channel_auth_caps(ihp, IPMI_LAN_CHANNEL_E,
2N/A ilp->il_privlvl)) == NULL)
2N/A return (-1);
2N/A
2N/A /*
2N/A * For the sake of simplicity, we're just supporting basic password
2N/A * authentication. If this authentication type is not supported then
2N/A * we'll bail here.
2N/A */
2N/A if (!(ac->cap_authtype & IPMI_SESSION_AUTHTYPE_PASSWORD)) {
2N/A free(ac);
2N/A return (ipmi_set_error(ihp, EIPMI_LAN_PASSWD_NOTSUP, NULL));
2N/A }
2N/A free(ac);
2N/A
2N/A if (ipmi_get_session_challenge_cmd(ihp, &ilp->il_session_id,
2N/A ilp->il_challenge) != 0)
2N/A return (-1);
2N/A
2N/A if (ipmi_activate_session_cmd(ihp) != 0)
2N/A return (-1);
2N/A
2N/A ilp->il_session_active = B_TRUE;
2N/A
2N/A if (ipmi_set_session_privlvl_cmd(ihp, ilp->il_privlvl) != 0)
2N/A return (-1);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Astatic void
2N/Aipmi_lan_close(void *data)
2N/A{
2N/A ipmi_lan_t *ilp = (ipmi_lan_t *)data;
2N/A
2N/A if (ilp->il_session_active)
2N/A (void) ipmi_close_session_cmd(ilp->il_ihp);
2N/A
2N/A if (ilp->il_sd >= 0)
2N/A (void) close(ilp->il_sd);
2N/A
2N/A ipmi_req_clear_entries(ilp->il_ihp);
2N/A ipmi_free(ilp->il_ihp, ipmi_req_entries);
2N/A ipmi_free(ilp->il_ihp, ilp);
2N/A}
2N/A
2N/Astatic void *
2N/Aipmi_lan_open(ipmi_handle_t *ihp, nvlist_t *params)
2N/A{
2N/A int rc;
2N/A struct hostent *host;
2N/A ipmi_lan_t *ilp;
2N/A char *hostname, *user, *authcode;
2N/A
2N/A if ((ilp = ipmi_zalloc(ihp, sizeof (ipmi_lan_t))) == NULL) {
2N/A /* ipmi errno set */
2N/A return (NULL);
2N/A }
2N/A ilp->il_ihp = ihp;
2N/A ihp->ih_tdata = ilp;
2N/A
2N/A /*
2N/A * Parse the parameters passed in the params nvlist. The following
2N/A * parameters are required
2N/A * IPMI_LAN_HOST, IPMI_LAN_USER and IPMI_LAN_PASSWD
2N/A *
2N/A * If any of these were not specified then we abort
2N/A */
2N/A if (nvlist_lookup_string(params, IPMI_LAN_HOST, &hostname) ||
2N/A nvlist_lookup_string(params, IPMI_LAN_USER, &user) ||
2N/A nvlist_lookup_string(params, IPMI_LAN_PASSWD, &authcode)) {
2N/A ipmi_free(ihp, ilp);
2N/A (void) ipmi_set_error(ihp, EIPMI_BADPARAM, NULL);
2N/A return (NULL);
2N/A }
2N/A (void) strncpy(ilp->il_host, hostname, MAXHOSTNAMELEN);
2N/A (void) strncpy(ilp->il_user, user, 16);
2N/A (void) strncpy(ilp->il_authcode, authcode, 16);
2N/A
2N/A /*
2N/A * IPMI_LAN_PORT is an optional parameter and defaults to port 623
2N/A * IPMI_LAN_PRIVLVL is also optional and defaults to admin
2N/A * IPMI_LAN_TIMEOUT is optional and will default to 3 seconds
2N/A * IPMI_LAN_NUM_RETIES is optional and will default to 5
2N/A */
2N/A if (nvlist_lookup_uint16(params, IPMI_LAN_PORT, &ilp->il_port))
2N/A ilp->il_port = RMCP_UDP_PORT;
2N/A
2N/A if (nvlist_lookup_uint8(params, IPMI_LAN_PRIVLVL, &ilp->il_privlvl))
2N/A ilp->il_privlvl = IPMI_SESSION_PRIV_ADMIN;
2N/A
2N/A if (nvlist_lookup_uint32(params, IPMI_LAN_TIMEOUT, &ilp->il_timeout))
2N/A ilp->il_timeout = DEF_IPMI_LAN_TIMEOUT;
2N/A
2N/A if (nvlist_lookup_uint8(params, IPMI_LAN_NUM_RETRIES,
2N/A &ilp->il_num_retries))
2N/A ilp->il_num_retries = DEF_IPMI_LAN_NUM_RETRIES;
2N/A
2N/A ilp->il_authtype = IPMI_SESSION_AUTHTYPE_PASSWORD;
2N/A
2N/A /*
2N/A * Open up and connect a UDP socket between us and the service
2N/A * processor
2N/A */
2N/A ilp->il_addr.sin_family = AF_INET;
2N/A ilp->il_addr.sin_port = htons(ilp->il_port);
2N/A
2N/A rc = inet_pton(AF_INET, (const char *)ilp->il_host,
2N/A &ilp->il_addr.sin_addr);
2N/A if (rc <= 0) {
2N/A if ((host = gethostbyname((const char *)ilp->il_host))
2N/A == NULL) {
2N/A ipmi_free(ihp, ilp);
2N/A (void) ipmi_set_error(ihp, EIPMI_LAN_OPEN_FAILED, NULL);
2N/A return (NULL);
2N/A }
2N/A ilp->il_addr.sin_family = host->h_addrtype;
2N/A (void) memcpy(&ilp->il_addr.sin_addr, host->h_addr,
2N/A host->h_length);
2N/A }
2N/A
2N/A if ((ilp->il_sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
2N/A ipmi_free(ihp, ilp);
2N/A (void) ipmi_set_error(ihp, EIPMI_LAN_OPEN_FAILED, NULL);
2N/A return (NULL);
2N/A }
2N/A if (connect(ilp->il_sd, (struct sockaddr *)&ilp->il_addr,
2N/A sizeof (struct sockaddr_in)) < 0) {
2N/A ipmi_lan_close(ilp);
2N/A (void) ipmi_set_error(ihp, EIPMI_LAN_OPEN_FAILED, NULL);
2N/A return (NULL);
2N/A }
2N/A
2N/A if ((ipmi_req_entries = ipmi_zalloc(ihp, sizeof (ipmi_rq_entry_t)))
2N/A == NULL)
2N/A return (NULL);
2N/A
2N/A /*
2N/A * Finally we start up the IPMI LAN session
2N/A */
2N/A if ((rc = ipmi_lan_activate_session(ihp)) < 0) {
2N/A ipmi_lan_close(ilp);
2N/A return (NULL);
2N/A }
2N/A
2N/A return (ilp);
2N/A}
2N/A
2N/Aipmi_transport_t ipmi_transport_lan = {
2N/A ipmi_lan_open,
2N/A ipmi_lan_close,
2N/A ipmi_lan_send
2N/A};