diff --git a/ChangeLog b/ChangeLog index 40430a8d5..d5c92396c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2016-12-06 LaMont Jones + + * ipmipower/: Initial pass at IPv6 support. + 2016-12-05 Albert Chu Support IPv6 addresses in FreeIPMI diff --git a/ipmipower/ipmipower.c b/ipmipower/ipmipower.c index 733816fbd..d30595de2 100644 --- a/ipmipower/ipmipower.c +++ b/ipmipower/ipmipower.c @@ -209,7 +209,7 @@ _eliminate_nodes (void) } static void -_sendto (cbuf_t cbuf, int fd, struct sockaddr_in *destaddr) +_sendto (cbuf_t cbuf, int fd, struct sockaddr_in6 *destaddr) { int n, rv; uint8_t buf[IPMIPOWER_PACKET_BUFLEN]; @@ -234,7 +234,7 @@ _sendto (cbuf_t cbuf, int fd, struct sockaddr_in *destaddr) n, 0, (struct sockaddr *)destaddr, - sizeof (struct sockaddr_in)); + sizeof (struct sockaddr_in6)); else { if (ipmi_is_ipmi_1_5_packet (buf, n)) @@ -243,14 +243,14 @@ _sendto (cbuf_t cbuf, int fd, struct sockaddr_in *destaddr) n, 0, (struct sockaddr *)destaddr, - sizeof (struct sockaddr_in)); + sizeof (struct sockaddr_in6)); else rv = ipmi_rmcpplus_sendto (fd, buf, n, 0, (struct sockaddr *)destaddr, - sizeof (struct sockaddr_in)); + sizeof (struct sockaddr_in6)); } } while (rv < 0 && errno == EINTR); @@ -269,12 +269,13 @@ _sendto (cbuf_t cbuf, int fd, struct sockaddr_in *destaddr) } static void -_recvfrom (cbuf_t cbuf, int fd, struct sockaddr_in *srcaddr) +_recvfrom (cbuf_t cbuf, int fd, struct sockaddr_in6 *srcaddr) { int n, rv, dropped = 0; uint8_t buf[IPMIPOWER_PACKET_BUFLEN]; - struct sockaddr_in from; - unsigned int fromlen = sizeof (struct sockaddr_in); + struct sockaddr_in6 from; + struct sockaddr_in *from4; + unsigned int fromlen = sizeof (struct sockaddr_in6); do { @@ -343,9 +344,11 @@ _recvfrom (cbuf_t cbuf, int fd, struct sockaddr_in *srcaddr) exit (EXIT_FAILURE); } + from4 = (struct sockaddr_in*)&from; /* Don't store if this packet is strange for some reason */ - if (from.sin_family != AF_INET - || from.sin_addr.s_addr != srcaddr->sin_addr.s_addr) + if (!(from4->sin_family == AF_INET + && from4->sin_addr.s_addr == ((struct sockaddr_in*)srcaddr)->sin_addr.s_addr) && + !(from.sin6_family == AF_INET6 && memcmp (&from.sin6_addr, &srcaddr->sin6_addr, sizeof(from.sin6_addr)) == 0)) return; /* cbuf should be empty, but if it isn't, empty it */ diff --git a/ipmipower/ipmipower.h b/ipmipower/ipmipower.h index fa6af8441..0cc35d4a5 100644 --- a/ipmipower/ipmipower.h +++ b/ipmipower/ipmipower.h @@ -467,7 +467,7 @@ struct ipmipower_connection char hostname[MAXHOSTNAMELEN+1]; /* for oem power types ; extra arg passed in via "+extra" at end of hostname */ struct ipmipower_connection_extra_arg *extra_args; - struct sockaddr_in destaddr; + struct sockaddr_in6 destaddr; /* for eliminate option */ int skip; diff --git a/ipmipower/ipmipower_connection.c b/ipmipower/ipmipower_connection.c index fe7106d11..b222993df 100644 --- a/ipmipower/ipmipower_connection.c +++ b/ipmipower/ipmipower_connection.c @@ -61,8 +61,6 @@ #include "cbuf.h" #include "fi_hostlist.h" -extern int h_errno; - extern cbuf_t ttyout; extern struct ipmipower_arguments cmd_args; @@ -128,13 +126,15 @@ ipmipower_connection_clear (struct ipmipower_connection *ic) static int _connection_setup (struct ipmipower_connection *ic, const char *hostname) { - struct sockaddr_in srcaddr; + struct sockaddr_in6 srcaddr; struct hostent *result; char *hostname_first_parse_copy = NULL; const char *hostname_first_parse_ptr = NULL; char *hostname_second_parse_copy = NULL; const char *hostname_second_parse_ptr = NULL; uint16_t port = RMCP_PRIMARY_RMCP_PORT; + char port_str[12]; + struct addrinfo ai_hints, *ai_res, *ai; int rv = -1; assert (ic); @@ -144,47 +144,6 @@ _connection_setup (struct ipmipower_connection *ic, const char *hostname) errno = 0; - if ((ic->ipmi_fd = socket (AF_INET, SOCK_DGRAM, 0)) < 0) - { - if (errno == EMFILE) - { - IPMIPOWER_DEBUG (("file descriptor limit reached")); - return (-1); - } - - IPMIPOWER_ERROR (("socket: %s", strerror (errno))); - exit (EXIT_FAILURE); - } - - if ((ic->ping_fd = socket (AF_INET, SOCK_DGRAM, 0)) < 0) - { - if (errno == EMFILE) - { - IPMIPOWER_DEBUG (("file descriptor limit reached")); - return (-1); - } - - IPMIPOWER_ERROR (("socket: %s", strerror (errno))); - exit (EXIT_FAILURE); - } - - /* Secure ephemeral ports */ - bzero (&srcaddr, sizeof (struct sockaddr_in)); - srcaddr.sin_family = AF_INET; - srcaddr.sin_port = htons (0); - srcaddr.sin_addr.s_addr = htonl (INADDR_ANY); - - if (bind (ic->ipmi_fd, &srcaddr, sizeof (struct sockaddr_in)) < 0) - { - IPMIPOWER_ERROR (("bind: %s", strerror (errno))); - exit (EXIT_FAILURE); - } - if (bind (ic->ping_fd, &srcaddr, sizeof (struct sockaddr_in)) < 0) - { - IPMIPOWER_ERROR (("bind: %s", strerror (errno))); - exit (EXIT_FAILURE); - } - if (!(ic->ipmi_in = cbuf_create (IPMIPOWER_MIN_CONNECTION_BUF, IPMIPOWER_MAX_CONNECTION_BUF))) { @@ -293,7 +252,45 @@ _connection_setup (struct ipmipower_connection *ic, const char *hostname) else hostname_first_parse_ptr = hostname; - if (strchr (hostname_first_parse_ptr, ':')) + if (hostname_first_parse_ptr[0] == '[' + && strchr (hostname_first_parse_ptr, ']')) + { + char *ptr; + + /* IPv6 address */ + if (!(hostname_second_parse_copy = strdup (hostname_first_parse_ptr+1))) + { + IPMIPOWER_ERROR (("strdup: %s", strerror (errno))); + exit (EXIT_FAILURE); + } + + hostname_second_parse_ptr = hostname_second_parse_copy; + ptr = strchr (hostname_second_parse_copy, ']'); + *ptr = '\0'; + ptr++; + if (*ptr == ':') + { + char *endptr; + int tmp; + + *ptr = '\0'; + ptr++; + + errno = 0; + tmp = strtol (ptr, &endptr, 0); + if (errno + || endptr[0] != '\0' + || tmp <= 0 + || tmp > USHRT_MAX) + { + ipmipower_output (IPMIPOWER_MSG_TYPE_HOSTNAME_INVALID, hostname_second_parse_ptr, NULL); + goto cleanup; + } + + port = tmp; + } + } + else if (strchr (hostname_first_parse_ptr, ':')) { char *ptr; @@ -335,29 +332,74 @@ _connection_setup (struct ipmipower_connection *ic, const char *hostname) strncpy (ic->hostname, hostname_second_parse_ptr, MAXHOSTNAMELEN); ic->hostname[MAXHOSTNAMELEN] = '\0'; - /* Determine the destination address */ - bzero (&(ic->destaddr), sizeof (struct sockaddr_in)); - ic->destaddr.sin_family = AF_INET; - ic->destaddr.sin_port = htons (port); - - if (!(result = gethostbyname (ic->hostname))) + snprintf(port_str, sizeof (port_str), "%d", port); + memset(&ai_hints, 0, sizeof (struct addrinfo)); + ai_hints.ai_family = AF_UNSPEC; + ai_hints.ai_socktype = SOCK_DGRAM; + ai_hints.ai_flags = (AI_V4MAPPED | AI_ADDRCONFIG); + if ((result = getaddrinfo (ic->hostname, port_str, &ai_hints, &ai_res )) != 0) { - if (h_errno == HOST_NOT_FOUND) + if (result == EAI_NODATA) ipmipower_output (IPMIPOWER_MSG_TYPE_HOSTNAME_INVALID, ic->hostname, NULL); else { -#if HAVE_HSTRERROR - IPMIPOWER_ERROR (("gethostbyname() %s: %s", ic->hostname, hstrerror (h_errno))); -#else /* !HAVE_HSTRERROR */ - IPMIPOWER_ERROR (("gethostbyname() %s: h_errno = %d", ic->hostname, h_errno)); -#endif /* !HAVE_HSTRERROR */ + IPMIPOWER_ERROR (("getaddrinfo() %s: %s", ic->hostname, gai_strerror (result))); exit (EXIT_FAILURE); } goto cleanup; } - ic->destaddr.sin_addr = *((struct in_addr *)result->h_addr); - - ic->skip = 0; + /* Try all of the different answers we got, until we succeed. */ + for (ai = ai_res; ai != NULL; ai = ai->ai_next) + { + if ((ic->ipmi_fd = socket (ai->ai_family, + ai->ai_socktype, ai->ai_protocol)) < 0) + { + if (errno == EMFILE) + { + IPMIPOWER_DEBUG (("file descriptor limit reached")); + return (-1); + } + } + + if ((ic->ping_fd = socket (ai->ai_family, + ai->ai_socktype, ai->ai_protocol)) < 0) + { + if (errno == EMFILE) + { + IPMIPOWER_DEBUG (("file descriptor limit reached")); + return (-1); + } + } + /* Secure ephemeral ports */ + bzero (&srcaddr, sizeof (struct sockaddr_in6)); + srcaddr.sin6_family = ai->ai_family; /* always the same place */ + /* All zero is otherwise correct. */ + + if ((bind (ic->ipmi_fd, &srcaddr, sizeof (struct sockaddr_in6)) < 0) + || (bind (ic->ping_fd, &srcaddr, sizeof (struct sockaddr_in6)) < 0)) + { + close(ic->ipmi_fd); + close(ic->ping_fd); + continue; + } + + /* Determine the destination address */ + if (ai->ai_addrlen > sizeof (struct sockaddr_in6)) + { + IPMIPOWER_ERROR (("getaddrinfo: unexpected address length %d", + ai->ai_addrlen)); + exit (EXIT_FAILURE); + } + memcpy (&(ic->destaddr), ai->ai_addr, ai->ai_addrlen); + ic->skip = 0; + break; + } + + if (!ai) + { + IPMIPOWER_ERROR (("socket/bind: %s", strerror (errno))); + exit (EXIT_FAILURE); + } rv = 0; cleanup: