PA: 83 - Revised IPv6 patch

Author: Holger Liebig
Deduplicated socket connection shared among lan and lanplus.
Allows IPv6 address and tries to pick correct scope ID.
This commit is contained in:
Ales Ledvinka 2013-11-14 12:56:49 +00:00
parent d3ca7cb090
commit f6cabfb089
7 changed files with 218 additions and 121 deletions

View File

@ -29,7 +29,7 @@ AC_C_BIGENDIAN
AC_FUNC_MALLOC
AC_FUNC_SELECT_ARGTYPES
AC_FUNC_STRTOD
AC_CHECK_FUNCS([alarm gethostbyname socket select])
AC_CHECK_FUNCS([alarm gethostbyname getaddrinfo getifaddrs socket select])
AC_CHECK_FUNCS([memmove memset strchr strdup strerror])
AC_CHECK_FUNCS([getpassphrase])
@ -39,6 +39,8 @@ AM_PROG_LIBTOOL
LIBTOOL="$LIBTOOL --silent"
AC_SEARCH_LIBS([gethostbyname], [nsl])
AC_SEARCH_LIBS([getaddrinfo], [nsl])
AC_SEARCH_LIBS([getifaddrs], [nsl])
AC_SEARCH_LIBS([socket], [socket], [],
[AC_CHECK_LIB([nsl], [socket],
[LIBS="$LIBS -lsocket -lnsl"], [], [-lsocket])])

View File

@ -89,8 +89,9 @@ struct ipmi_session {
uint32_t out_seq;
uint32_t timeout;
struct sockaddr_in addr;
struct sockaddr_storage addr;
socklen_t addrlen;
int ai_family; /* Protocol family for socket. */
/*
* This struct holds state data specific to IPMI v2 / RMCP+ sessions
@ -211,4 +212,7 @@ void ipmi_intf_session_set_timeout(struct ipmi_intf * intf, uint32_t timeout);
void ipmi_intf_session_set_retry(struct ipmi_intf * intf, int retry);
void ipmi_cleanup(struct ipmi_intf * intf);
#if defined(IPMI_INTF_LAN) || defined (IPMI_INTF_LANPLUS)
int ipmi_intf_socket_connect(struct ipmi_intf * intf);
#endif
#endif /* IPMI_INTF_H */

View File

@ -481,44 +481,14 @@ ipmi_dcmi_prnt_oobDiscover(struct ipmi_intf * intf)
intf->abort = 1;
intf->session->sol_data.sequence_number = 1;
/* open port to BMC */
memset(&s->addr, 0, sizeof(struct sockaddr_in));
s->addr.sin_family = AF_INET;
s->addr.sin_port = htons(s->port);
rc = inet_pton(AF_INET, (const char *)s->hostname, &s->addr.sin_addr);
if (rc <= 0) {
struct hostent *host = gethostbyname((const char *)s->hostname);
if (host == NULL) {
lprintf(LOG_ERR, "Address lookup for %s failed",
s->hostname);
return -1;
}
if (host->h_addrtype != AF_INET) {
lprintf(LOG_ERR,
"Address lookup for %s failed. Got %s, expected IPv4 address.",
s->hostname,
(host->h_addrtype == AF_INET6) ? "IPv6" : "Unknown");
return (-1);
}
s->addr.sin_family = host->h_addrtype;
memcpy(&s->addr.sin_addr, host->h_addr, host->h_length);
}
lprintf(LOG_DEBUG, "IPMI LAN host %s port %d",
s->hostname, ntohs(s->addr.sin_port));
intf->fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (intf->fd < 0) {
lperror(LOG_ERR, "Socket failed");
if (ipmi_intf_socket_connect (intf) == -1) {
lprintf(LOG_ERR, "Could not open socket!");
return -1;
}
/* connect to UDP socket so we get async errors */
rc = connect(intf->fd, (struct sockaddr *)&s->addr,
sizeof(struct sockaddr_in));
if (rc < 0) {
lperror(LOG_ERR, "Connect failed");
if (intf->fd < 0) {
lperror(LOG_ERR, "Connect to %s failed",
s->hostname);
intf->close(intf);
return -1;
}

View File

@ -381,7 +381,7 @@ int
ipmi_tsol_main(struct ipmi_intf * intf, int argc, char ** argv)
{
struct pollfd fds_wait[3], fds_data_wait[3], *fds;
struct sockaddr_in sin, myaddr;
struct sockaddr_in sin, myaddr, *sa_in;
socklen_t mylen;
char *recvip = NULL;
char out_buff[IPMI_BUF_SIZE * 8], in_buff[IPMI_BUF_SIZE];
@ -398,8 +398,11 @@ ipmi_tsol_main(struct ipmi_intf * intf, int argc, char ** argv)
}
for (i = 0; i<argc; i++) {
if (sscanf(argv[i], "%d.%d.%d.%d", &ip1, &ip2, &ip3, &ip4) == 4)
recvip = strdup(argv[i]);
if (sscanf(argv[i], "%d.%d.%d.%d", &ip1, &ip2, &ip3, &ip4) == 4) {
/* not free'd ...*/
/* recvip = strdup(argv[i]); */
recvip = argv[i];
}
else if (sscanf(argv[i], "port=%d", &ip1) == 1)
port = ip1;
else if (sscanf(argv[i], "rows=%d", &ip1) == 1)
@ -427,8 +430,9 @@ ipmi_tsol_main(struct ipmi_intf * intf, int argc, char ** argv)
sin.sin_family = AF_INET;
sin.sin_port = htons(port);
sa_in = (struct sockaddr_in *)&intf->session->addr;
result = inet_pton(AF_INET, (const char *)intf->session->hostname,
&intf->session->addr.sin_addr);
&sa_in->sin_addr);
if (result <= 0) {
struct hostent *host = gethostbyname((const char *)intf->session->hostname);
@ -444,8 +448,8 @@ ipmi_tsol_main(struct ipmi_intf * intf, int argc, char ** argv)
(host->h_addrtype == AF_INET6) ? "IPv6" : "Unknown");
return (-1);
}
intf->session->addr.sin_family = host->h_addrtype;
memcpy(&intf->session->addr.sin_addr, host->h_addr, host->h_length);
sa_in->sin_family = host->h_addrtype;
memcpy(&sa_in->sin_addr, host->h_addr, host->h_length);
}
fd_socket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
@ -455,6 +459,7 @@ ipmi_tsol_main(struct ipmi_intf * intf, int argc, char ** argv)
}
if (-1 == bind(fd_socket, (struct sockaddr *)&sin, sizeof(sin))) {
lprintf(LOG_ERR, "Failed to bind socket.");
close(fd_socket);
return -1;
}

View File

@ -36,6 +36,18 @@
#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>
@ -321,3 +333,170 @@ 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

View File

@ -2032,44 +2032,14 @@ ipmi_lan_open(struct ipmi_intf * intf)
intf->session->sol_data.sequence_number = 1;
/* open port to BMC */
memset(&s->addr, 0, sizeof(struct sockaddr_in));
s->addr.sin_family = AF_INET;
s->addr.sin_port = htons(s->port);
rc = inet_pton(AF_INET, (const char *)s->hostname, &s->addr.sin_addr);
if (rc <= 0) {
struct hostent *host = gethostbyname((const char *)s->hostname);
if (host == NULL) {
lprintf(LOG_ERR, "Address lookup for %s failed",
s->hostname);
return -1;
}
if (host->h_addrtype != AF_INET) {
lprintf(LOG_ERR,
"Address lookup for %s failed. Got %s, expected IPv4 address.",
s->hostname,
(host->h_addrtype == AF_INET6) ? "IPv6" : "Unknown");
return (-1);
}
s->addr.sin_family = host->h_addrtype;
memcpy(&s->addr.sin_addr, host->h_addr, host->h_length);
}
lprintf(LOG_DEBUG, "IPMI LAN host %s port %d",
s->hostname, ntohs(s->addr.sin_port));
intf->fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (intf->fd < 0) {
lperror(LOG_ERR, "Socket failed");
if (ipmi_intf_socket_connect (intf) == -1) {
lprintf(LOG_ERR, "Could not open socket!");
return -1;
}
/* connect to UDP socket so we get async errors */
rc = connect(intf->fd, (struct sockaddr *)&s->addr,
sizeof(struct sockaddr_in));
if (rc < 0) {
lperror(LOG_ERR, "Connect failed");
if (intf->fd < 0) {
lperror(LOG_ERR, "Connect to %s failed",
s->hostname);
intf->close(intf);
return -1;
}

View File

@ -3334,7 +3334,6 @@ ipmi_lanplus_open(struct ipmi_intf * intf)
{
int rc;
struct get_channel_auth_cap_rsp auth_cap;
struct sockaddr_in addr;
struct ipmi_session *session;
if (!intf || !intf->session)
@ -3373,46 +3372,14 @@ ipmi_lanplus_open(struct ipmi_intf * intf)
/* Kg is set in ipmi_intf */
//memset(session->v2_data.kg, 0, IPMI_KG_BUFFER_SIZE);
/* open port to BMC */
memset(&addr, 0, sizeof(struct sockaddr_in));
addr.sin_family = AF_INET;
addr.sin_port = htons(session->port);
rc = inet_pton(AF_INET, (const char *)session->hostname, &addr.sin_addr);
if (rc <= 0) {
struct hostent *host = gethostbyname((const char *)session->hostname);
if (host == NULL) {
lprintf(LOG_ERR, "Address lookup for %s failed",
session->hostname);
return -1;
}
if (host->h_addrtype != AF_INET) {
lprintf(LOG_ERR,
"Address lookup for %s failed. Got %s, expected IPv4 address.",
session->hostname,
(host->h_addrtype == AF_INET6) ? "IPv6" : "Unknown");
return (-1);
}
addr.sin_family = host->h_addrtype;
memcpy(&addr.sin_addr, host->h_addr, host->h_length);
}
lprintf(LOG_DEBUG, "IPMI LAN host %s port %d",
session->hostname, ntohs(addr.sin_port));
intf->fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (intf->fd < 0) {
lperror(LOG_ERR, "Socket failed");
if (ipmi_intf_socket_connect (intf) == -1) {
lprintf(LOG_ERR, "Could not open socket!");
return -1;
}
/* connect to UDP socket so we get async errors */
rc = connect(intf->fd,
(struct sockaddr *)&addr, sizeof(struct sockaddr_in));
if (rc < 0) {
lperror(LOG_ERR, "Connect failed");
if (intf->fd < 0) {
lperror(LOG_ERR, "Connect to %s failed",
session->hostname);
intf->close(intf);
return -1;
}