summaryrefslogtreecommitdiff
path: root/src/plugins/ipmi_intf.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/ipmi_intf.c')
-rw-r--r--src/plugins/ipmi_intf.c651
1 files changed, 651 insertions, 0 deletions
diff --git a/src/plugins/ipmi_intf.c b/src/plugins/ipmi_intf.c
new file mode 100644
index 0000000..0fa76be
--- /dev/null
+++ b/src/plugins/ipmi_intf.c
@@ -0,0 +1,651 @@
+/*
+ * Copyright (c) 2003 Sun Microsystems, 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:
+ *
+ * Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * Redistribution 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.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any kind.
+ * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
+ * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
+ * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE
+ * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
+ * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL
+ * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA,
+ * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR
+ * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF
+ * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
+ * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#if defined(HAVE_CONFIG_H)
+# include <config.h>
+#endif
+
+#if defined(IPMI_INTF_LAN) || defined (IPMI_INTF_LANPLUS)
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <ifaddrs.h>
+#include <unistd.h>
+#include <netdb.h>
+#endif
+
+
+#include <ipmitool/ipmi_intf.h>
+#include <ipmitool/ipmi.h>
+#include <ipmitool/ipmi_sdr.h>
+#include <ipmitool/log.h>
+
+#define IPMI_DEFAULT_PAYLOAD_SIZE 25
+
+#ifdef IPMI_INTF_OPEN
+extern struct ipmi_intf ipmi_open_intf;
+#endif
+#ifdef IPMI_INTF_IMB
+extern struct ipmi_intf ipmi_imb_intf;
+#endif
+#ifdef IPMI_INTF_LIPMI
+extern struct ipmi_intf ipmi_lipmi_intf;
+#endif
+#ifdef IPMI_INTF_BMC
+extern struct ipmi_intf ipmi_bmc_intf;
+#endif
+#ifdef IPMI_INTF_LAN
+extern struct ipmi_intf ipmi_lan_intf;
+#endif
+#ifdef IPMI_INTF_LANPLUS
+extern struct ipmi_intf ipmi_lanplus_intf;
+#endif
+#ifdef IPMI_INTF_FREE
+extern struct ipmi_intf ipmi_free_intf;
+#endif
+#ifdef IPMI_INTF_SERIAL
+extern struct ipmi_intf ipmi_serial_term_intf;
+extern struct ipmi_intf ipmi_serial_bm_intf;
+#endif
+#ifdef IPMI_INTF_DUMMY
+extern struct ipmi_intf ipmi_dummy_intf;
+#endif
+
+struct ipmi_intf * ipmi_intf_table[] = {
+#ifdef IPMI_INTF_OPEN
+ &ipmi_open_intf,
+#endif
+#ifdef IPMI_INTF_IMB
+ &ipmi_imb_intf,
+#endif
+#ifdef IPMI_INTF_LIPMI
+ &ipmi_lipmi_intf,
+#endif
+#ifdef IPMI_INTF_BMC
+ &ipmi_bmc_intf,
+#endif
+#ifdef IPMI_INTF_LAN
+ &ipmi_lan_intf,
+#endif
+#ifdef IPMI_INTF_LANPLUS
+ &ipmi_lanplus_intf,
+#endif
+#ifdef IPMI_INTF_FREE
+ &ipmi_free_intf,
+#endif
+#ifdef IPMI_INTF_SERIAL
+ &ipmi_serial_term_intf,
+ &ipmi_serial_bm_intf,
+#endif
+#ifdef IPMI_INTF_DUMMY
+ &ipmi_dummy_intf,
+#endif
+ NULL
+};
+
+/* ipmi_intf_print - Print list of interfaces
+ *
+ * no meaningful return code
+ */
+void ipmi_intf_print(struct ipmi_intf_support * intflist)
+{
+ struct ipmi_intf ** intf;
+ struct ipmi_intf_support * sup;
+ int def = 1;
+ int found;
+
+ lprintf(LOG_NOTICE, "Interfaces:");
+
+ for (intf = ipmi_intf_table; intf && *intf; intf++) {
+
+ if (intflist != NULL) {
+ found = 0;
+ for (sup=intflist; sup->name != NULL; sup++) {
+ if (strncmp(sup->name, (*intf)->name, strlen(sup->name)) == 0 &&
+ strncmp(sup->name, (*intf)->name, strlen((*intf)->name)) == 0 &&
+ sup->supported == 1)
+ found = 1;
+ }
+ if (found == 0)
+ continue;
+ }
+
+ lprintf(LOG_NOTICE, "\t%-12s %s %s",
+ (*intf)->name, (*intf)->desc,
+ def ? "[default]" : "");
+ def = 0;
+ }
+ lprintf(LOG_NOTICE, "");
+}
+
+/* ipmi_intf_load - Load an interface from the interface table above
+ * If no interface name is given return first entry
+ *
+ * @name: interface name to try and load
+ *
+ * returns pointer to inteface structure if found
+ * returns NULL on error
+ */
+struct ipmi_intf * ipmi_intf_load(char * name)
+{
+ struct ipmi_intf ** intf;
+ struct ipmi_intf * i;
+
+ if (name == NULL) {
+ i = ipmi_intf_table[0];
+ if (i->setup != NULL && (i->setup(i) < 0)) {
+ lprintf(LOG_ERR, "Unable to setup "
+ "interface %s", name);
+ return NULL;
+ }
+ return i;
+ }
+
+ for (intf = ipmi_intf_table;
+ ((intf != NULL) && (*intf != NULL));
+ intf++) {
+ i = *intf;
+ if (strncmp(name, i->name, strlen(name)) == 0) {
+ if (i->setup != NULL && (i->setup(i) < 0)) {
+ lprintf(LOG_ERR, "Unable to setup "
+ "interface %s", name);
+ return NULL;
+ }
+ return i;
+ }
+ }
+
+ return NULL;
+}
+
+void
+ipmi_intf_session_set_hostname(struct ipmi_intf * intf, char * hostname)
+{
+ if (intf->session == NULL)
+ return;
+
+ memset(intf->session->hostname, 0, 16);
+
+ if (hostname != NULL) {
+ memcpy(intf->session->hostname, hostname,
+ __min(strlen(hostname), 64));
+ }
+}
+
+void
+ipmi_intf_session_set_username(struct ipmi_intf * intf, char * username)
+{
+ if (intf->session == NULL)
+ return;
+
+ memset(intf->session->username, 0, 17);
+
+ if (username == NULL)
+ return;
+
+ memcpy(intf->session->username, username, __min(strlen(username), 16));
+}
+
+void
+ipmi_intf_session_set_password(struct ipmi_intf * intf, char * password)
+{
+ if (intf->session == NULL)
+ return;
+
+ memset(intf->session->authcode, 0, IPMI_AUTHCODE_BUFFER_SIZE);
+
+ if (password == NULL) {
+ intf->session->password = 0;
+ return;
+ }
+
+ intf->session->password = 1;
+ memcpy(intf->session->authcode, password,
+ __min(strlen(password), IPMI_AUTHCODE_BUFFER_SIZE));
+}
+
+void
+ipmi_intf_session_set_privlvl(struct ipmi_intf * intf, uint8_t level)
+{
+ if (intf->session == NULL)
+ return;
+
+ intf->session->privlvl = level;
+}
+
+void
+ipmi_intf_session_set_lookupbit(struct ipmi_intf * intf, uint8_t lookupbit)
+{
+ if (intf->session == NULL)
+ return;
+
+ intf->session->v2_data.lookupbit = lookupbit;
+}
+
+void
+ipmi_intf_session_set_cipher_suite_id(struct ipmi_intf * intf, uint8_t cipher_suite_id)
+{
+ if (intf->session == NULL)
+ return;
+
+ intf->session->cipher_suite_id = cipher_suite_id;
+}
+
+void
+ipmi_intf_session_set_sol_escape_char(struct ipmi_intf * intf, char sol_escape_char)
+{
+ if (intf->session == NULL)
+ return;
+
+ intf->session->sol_escape_char = sol_escape_char;
+}
+
+void
+ipmi_intf_session_set_kgkey(struct ipmi_intf * intf, char * kgkey)
+{
+ if (intf->session == NULL)
+ return;
+
+ memset(intf->session->v2_data.kg, 0, IPMI_KG_BUFFER_SIZE);
+
+ if (kgkey == NULL)
+ return;
+
+ memcpy(intf->session->v2_data.kg, kgkey,
+ __min(strlen(kgkey), IPMI_KG_BUFFER_SIZE));
+}
+
+void
+ipmi_intf_session_set_port(struct ipmi_intf * intf, int port)
+{
+ if (intf->session == NULL)
+ return;
+
+ intf->session->port = port;
+}
+
+void
+ipmi_intf_session_set_authtype(struct ipmi_intf * intf, uint8_t authtype)
+{
+ if (intf->session == NULL)
+ return;
+
+ /* clear password field if authtype NONE specified */
+ if (authtype == IPMI_SESSION_AUTHTYPE_NONE) {
+ memset(intf->session->authcode, 0, IPMI_AUTHCODE_BUFFER_SIZE);
+ intf->session->password = 0;
+ }
+
+ intf->session->authtype_set = authtype;
+}
+
+void
+ipmi_intf_session_set_timeout(struct ipmi_intf * intf, uint32_t timeout)
+{
+ if (intf->session == NULL)
+ return;
+
+ intf->session->timeout = timeout;
+}
+
+void
+ipmi_intf_session_set_retry(struct ipmi_intf * intf, int retry)
+{
+ if (intf->session == NULL)
+ return;
+
+ intf->session->retry = retry;
+}
+
+void
+ipmi_cleanup(struct ipmi_intf * intf)
+{
+ ipmi_sdr_list_empty(intf);
+}
+
+#if defined(IPMI_INTF_LAN) || defined (IPMI_INTF_LANPLUS)
+int
+ipmi_intf_socket_connect(struct ipmi_intf * intf)
+{
+ struct ipmi_session *session;
+
+ struct sockaddr_storage addr;
+ struct addrinfo hints;
+ struct addrinfo *rp0 = NULL, *rp;
+ char service[NI_MAXSERV];
+ int rc;
+
+ if (!intf || intf->session == NULL) {
+ return -1;
+ }
+
+ session = intf->session;
+
+ if (session->hostname == NULL || strlen((const char *)session->hostname) == 0) {
+ lprintf(LOG_ERR, "No hostname specified!");
+ return -1;
+ }
+
+ /* open port to BMC */
+ memset(&addr, 0, sizeof(addr));
+
+ sprintf(service, "%d", session->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 = 0; /* use AI_NUMERICSERV for no name resolution */
+ hints.ai_protocol = IPPROTO_UDP; /* */
+
+ if (getaddrinfo(session->hostname, service, &hints, &rp0) != 0) {
+ lprintf(LOG_ERR, "Address lookup for %s failed",
+ session->hostname);
+ return -1;
+ }
+
+ /* 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.
+ */
+
+ session->ai_family = AF_UNSPEC;
+ for (rp = rp0; rp != NULL; rp = rp->ai_next) {
+ /* We are only interested in IPv4 and IPv6 */
+ if ((rp->ai_family != AF_INET6) && (rp->ai_family != AF_INET)) {
+ continue;
+ }
+
+ intf->fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+ if (intf->fd == -1) {
+ continue;
+ }
+
+ if (rp->ai_family == AF_INET) {
+ if (connect(intf->fd, rp->ai_addr, rp->ai_addrlen) != -1) {
+ memcpy(&session->addr, rp->ai_addr, rp->ai_addrlen);
+ session->addrlen = rp->ai_addrlen;
+ session->ai_family = rp->ai_family;
+ break; /* Success */
+ }
+ } else if (rp->ai_family == AF_INET6) {
+ struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)rp->ai_addr;
+ char hbuf[NI_MAXHOST];
+ socklen_t len;
+
+ /* The scope was specified on the command line e.g. with -H FE80::219:99FF:FEA0:BD95%eth0 */
+ if (addr6->sin6_scope_id != 0) {
+ len = sizeof(struct sockaddr_in6);
+ if (getnameinfo((struct sockaddr *)addr6, len, hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST) == 0) {
+ lprintf(LOG_DEBUG, "Trying address: %s scope=%d",
+ hbuf,
+ addr6->sin6_scope_id);
+ }
+ if (connect(intf->fd, rp->ai_addr, rp->ai_addrlen) != -1) {
+ memcpy(&session->addr, rp->ai_addr, rp->ai_addrlen);
+ session->addrlen = rp->ai_addrlen;
+ session->ai_family = rp->ai_family;
+ break; /* Success */
+ }
+ } else {
+ /* No scope specified, try to get this from the list of interfaces */
+ struct ifaddrs *ifaddrs = NULL;
+ struct ifaddrs *ifa = NULL;
+
+ if (getifaddrs(&ifaddrs) < 0) {
+ lprintf(LOG_ERR, "Interface address lookup for %s failed",
+ session->hostname);
+ break;
+ }
+
+ for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
+ if (ifa->ifa_addr == NULL) {
+ continue;
+ }
+
+ if (ifa->ifa_addr->sa_family == AF_INET6) {
+ struct sockaddr_in6 *tmp6 = (struct sockaddr_in6 *)ifa->ifa_addr;
+
+ /* Skip unwanted addresses */
+ if (IN6_IS_ADDR_MULTICAST(&tmp6->sin6_addr)) {
+ continue;
+ }
+ if (IN6_IS_ADDR_LOOPBACK(&tmp6->sin6_addr)) {
+ continue;
+ }
+ len = sizeof(struct sockaddr_in6);
+ if ( getnameinfo((struct sockaddr *)tmp6, len, hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST) == 0) {
+ lprintf(LOG_DEBUG, "Testing %s interface address: %s scope=%d",
+ ifa->ifa_name != NULL ? ifa->ifa_name : "???",
+ hbuf,
+ tmp6->sin6_scope_id);
+ }
+
+ if (tmp6->sin6_scope_id != 0) {
+ addr6->sin6_scope_id = tmp6->sin6_scope_id;
+ } else {
+ /*
+ * No scope information in interface address information
+ * On some OS'es, getifaddrs() is returning out the 'kernel' representation
+ * of scoped addresses which stores the scope in the 3rd and 4th
+ * byte. See also this page:
+ * http://www.freebsd.org/doc/en/books/developers-handbook/ipv6.html
+ */
+ if (IN6_IS_ADDR_LINKLOCAL(&tmp6->sin6_addr)
+ && (tmp6->sin6_addr.s6_addr16[1] != 0)) {
+ addr6->sin6_scope_id = ntohs(tmp6->sin6_addr.s6_addr16[1]);
+ }
+ }
+
+ /* OK, now try to connect with the scope id from this interface address */
+ if (addr6->sin6_scope_id != 0) {
+ if (connect(intf->fd, rp->ai_addr, rp->ai_addrlen) != -1) {
+ memcpy(&session->addr, rp->ai_addr, rp->ai_addrlen);
+ session->addrlen = rp->ai_addrlen;
+ session->ai_family = rp->ai_family;
+ lprintf(LOG_DEBUG, "Successful connected on %s interface with scope id %d", ifa->ifa_name, tmp6->sin6_scope_id);
+ break; /* Success */
+ }
+ }
+ }
+ }
+ freeifaddrs(ifaddrs);
+ }
+ }
+ if (session->ai_family != AF_UNSPEC) {
+ break;
+ }
+ close(intf->fd);
+ intf->fd = -1;
+ }
+
+ /* No longer needed */
+ freeaddrinfo(rp0);
+
+ return ((intf->fd != -1) ? 0 : -1);
+}
+#endif
+
+uint16_t
+ipmi_intf_get_max_request_data_size(struct ipmi_intf * intf)
+{
+ int16_t size;
+
+ size = intf->max_request_data_size;
+
+ /* check if request size is not specified */
+ if (!size) {
+ /*
+ * The IPMB standard overall message length for ‘non -bridging’
+ * messages is specified as 32 bytes, maximum, including slave
+ * address. This sets the upper limit for typical IPMI messages.
+ * With the exception of messages used for bridging messages to
+ * other busses or interfaces (e.g. Master Write-Read and Send Message)
+ * IPMI messages should be designed to fit within this 32-byte maximum.
+ * In order to support bridging, the Master Write -Read and Send Message
+ * commands are allowed to exceed the 32-byte maximum transaction on IPMB
+ */
+
+ size = IPMI_DEFAULT_PAYLOAD_SIZE;
+
+ /* check if message is forwarded */
+ if (intf->target_addr && intf->target_addr != intf->my_addr) {
+ /* add Send Message request size */
+ size += 8;
+ }
+ }
+
+ /* check if message is forwarded */
+ if (intf->target_addr && intf->target_addr != intf->my_addr) {
+ /* subtract send message request size */
+ size -= 8;
+
+ /*
+ * Check that forwarded request size is not greater
+ * than the default payload size.
+ */
+ if (size > IPMI_DEFAULT_PAYLOAD_SIZE) {
+ size = IPMI_DEFAULT_PAYLOAD_SIZE;
+ }
+
+ /* check for double bridging */
+ if (intf->transit_addr && intf->transit_addr != intf->target_addr) {
+ /* subtract inner send message request size */
+ size -= 8;
+ }
+ }
+
+ /* check for underflow */
+ if (size < 0) {
+ return 0;
+ }
+
+ return size;
+}
+
+uint16_t
+ipmi_intf_get_max_response_data_size(struct ipmi_intf * intf)
+{
+ int16_t size;
+
+ size = intf->max_response_data_size;
+
+ /* check if response size is not specified */
+ if (!size) {
+ /*
+ * The IPMB standard overall message length for ‘non -bridging’
+ * messages is specified as 32 bytes, maximum, including slave
+ * address. This sets the upper limit for typical IPMI messages.
+ * With the exception of messages used for bridging messages to
+ * other busses or interfaces (e.g. Master Write-Read and Send Message)
+ * IPMI messages should be designed to fit within this 32-byte maximum.
+ * In order to support bridging, the Master Write -Read and Send Message
+ * commands are allowed to exceed the 32-byte maximum transaction on IPMB
+ */
+
+ size = IPMI_DEFAULT_PAYLOAD_SIZE; /* response length with subtracted header and checksum byte */
+
+ /* check if message is forwarded */
+ if (intf->target_addr && intf->target_addr != intf->my_addr) {
+ /* add Send Message header size */
+ size += 7;
+ }
+ }
+
+ /* check if message is forwarded */
+ if (intf->target_addr && intf->target_addr != intf->my_addr) {
+ /*
+ * Some IPMI controllers like PICMG AMC Carriers embed responses
+ * to the forwarded messages into the Send Message response.
+ * In order to be sure that the response is not truncated,
+ * subtract the internal message header size.
+ */
+ size -= 8;
+
+ /*
+ * Check that forwarded response is not greater
+ * than the default payload size.
+ */
+ if (size > IPMI_DEFAULT_PAYLOAD_SIZE) {
+ size = IPMI_DEFAULT_PAYLOAD_SIZE;
+ }
+
+ /* check for double bridging */
+ if (intf->transit_addr && intf->transit_addr != intf->target_addr) {
+ /* subtract inner send message header size */
+ size -= 8;
+ }
+ }
+
+ /* check for underflow */
+ if (size < 0) {
+ return 0;
+ }
+
+ return size;
+}
+
+void
+ipmi_intf_set_max_request_data_size(struct ipmi_intf * intf, uint16_t size)
+{
+ if (size < IPMI_DEFAULT_PAYLOAD_SIZE) {
+ lprintf(LOG_ERR, "Request size is too small (%d), leave default size",
+ size);
+ return;
+ }
+
+ if (intf->set_max_request_data_size) {
+ intf->set_max_request_data_size(intf, size);
+ } else {
+ intf->max_request_data_size = size;
+ }
+}
+
+void
+ipmi_intf_set_max_response_data_size(struct ipmi_intf * intf, uint16_t size)
+{
+ if (size < IPMI_DEFAULT_PAYLOAD_SIZE - 1) {
+ lprintf(LOG_ERR, "Response size is too small (%d), leave default size",
+ size);
+ return;
+ }
+
+ if (intf->set_max_response_data_size) {
+ intf->set_max_response_data_size(intf, size);
+ } else {
+ intf->max_response_data_size = size;
+ }
+}