diff options
| author | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2014-07-23 15:03:01 +0200 | 
|---|---|---|
| committer | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2014-07-23 15:03:01 +0200 | 
| commit | 777af8a8761d05c30588abec7444b143fe7393f0 (patch) | |
| tree | 5a135c37eaa9ac94772819a28ce5beedd18e5c4a /src/plugins/ipmi_intf.c | |
| parent | c3445516ecd58e97de483cf4b7fafcc1104890d7 (diff) | |
| parent | b32d92e890caac903491116e9d817aa780c0323b (diff) | |
Merge tag 'upstream/1.8.14'
Upstream version 1.8.14
Diffstat (limited to 'src/plugins/ipmi_intf.c')
| -rw-r--r-- | src/plugins/ipmi_intf.c | 651 | 
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; +	} +} | 
