From f6cabfb089025be170d08fd7402801663447282e Mon Sep 17 00:00:00 2001 From: Ales Ledvinka Date: Thu, 14 Nov 2013 12:56:49 +0000 Subject: [PATCH] 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. --- ipmitool/configure.in | 4 +- ipmitool/include/ipmitool/ipmi_intf.h | 6 +- ipmitool/lib/ipmi_dcmi.c | 40 +----- ipmitool/lib/ipmi_tsol.c | 17 ++- ipmitool/src/plugins/ipmi_intf.c | 179 +++++++++++++++++++++++++ ipmitool/src/plugins/lan/lan.c | 40 +----- ipmitool/src/plugins/lanplus/lanplus.c | 53 ++------ 7 files changed, 218 insertions(+), 121 deletions(-) diff --git a/ipmitool/configure.in b/ipmitool/configure.in index b815e90..21d666a 100644 --- a/ipmitool/configure.in +++ b/ipmitool/configure.in @@ -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])]) diff --git a/ipmitool/include/ipmitool/ipmi_intf.h b/ipmitool/include/ipmitool/ipmi_intf.h index 7ab2b13..774c6b2 100644 --- a/ipmitool/include/ipmitool/ipmi_intf.h +++ b/ipmitool/include/ipmitool/ipmi_intf.h @@ -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 */ diff --git a/ipmitool/lib/ipmi_dcmi.c b/ipmitool/lib/ipmi_dcmi.c index ff9646c..ce33d75 100755 --- a/ipmitool/lib/ipmi_dcmi.c +++ b/ipmitool/lib/ipmi_dcmi.c @@ -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; } diff --git a/ipmitool/lib/ipmi_tsol.c b/ipmitool/lib/ipmi_tsol.c index be53236..94ea284 100644 --- a/ipmitool/lib/ipmi_tsol.c +++ b/ipmitool/lib/ipmi_tsol.c @@ -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; isession->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; } diff --git a/ipmitool/src/plugins/ipmi_intf.c b/ipmitool/src/plugins/ipmi_intf.c index 086077d..1ea6fbe 100644 --- a/ipmitool/src/plugins/ipmi_intf.c +++ b/ipmitool/src/plugins/ipmi_intf.c @@ -36,6 +36,18 @@ #if defined(HAVE_CONFIG_H) # include #endif + +#if defined(IPMI_INTF_LAN) || defined (IPMI_INTF_LANPLUS) +#include +#include +#include +#include +#include +#include +#include +#endif + + #include #include #include @@ -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 + diff --git a/ipmitool/src/plugins/lan/lan.c b/ipmitool/src/plugins/lan/lan.c index e088479..fc90000 100644 --- a/ipmitool/src/plugins/lan/lan.c +++ b/ipmitool/src/plugins/lan/lan.c @@ -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; } diff --git a/ipmitool/src/plugins/lanplus/lanplus.c b/ipmitool/src/plugins/lanplus/lanplus.c index acf2410..1f8169e 100644 --- a/ipmitool/src/plugins/lanplus/lanplus.c +++ b/ipmitool/src/plugins/lanplus/lanplus.c @@ -84,14 +84,14 @@ static struct ipmi_rs * ipmi_lanplus_send_ipmi_cmd(struct ipmi_intf * intf, stru static struct ipmi_rs * ipmi_lanplus_send_payload(struct ipmi_intf * intf, struct ipmi_v2_payload * payload); static void getIpmiPayloadWireRep( - struct ipmi_intf * intf, - struct ipmi_v2_payload * payload, /* in */ + struct ipmi_intf * intf, + struct ipmi_v2_payload * payload, /* in */ uint8_t * out, struct ipmi_rq * req, uint8_t rq_seq, - uint8_t curr_seq); + uint8_t curr_seq); static void getSolPayloadWireRep( - struct ipmi_intf * intf, + struct ipmi_intf * intf, uint8_t * msg, struct ipmi_v2_payload * payload); static void read_open_session_response(struct ipmi_rs * rsp, int offset); @@ -113,7 +113,7 @@ static void ack_sol_packet( struct ipmi_intf * intf, struct ipmi_rs * rsp); -static uint8_t bridgePossible = 0; +static uint8_t bridgePossible = 0; struct ipmi_intf ipmi_lanplus_intf = { name: "lanplus", @@ -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; }