From 961fcb1716228df738ed10b98492ac5a01367873 Mon Sep 17 00:00:00 2001 From: Albert Chu Date: Thu, 22 Dec 2016 14:58:55 -0800 Subject: [PATCH] Add IPv6 Support in libipmiconsole --- libipmiconsole/ipmiconsole.c | 113 ++++++++--------- libipmiconsole/ipmiconsole_ctx.c | 159 ++++++++++++------------ libipmiconsole/ipmiconsole_defs.h | 15 ++- libipmiconsole/ipmiconsole_engine.c | 45 +++++-- libipmiconsole/ipmiconsole_processing.c | 14 ++- 5 files changed, 187 insertions(+), 159 deletions(-) diff --git a/libipmiconsole/ipmiconsole.c b/libipmiconsole/ipmiconsole.c index 3414c4a06..b464637a4 100644 --- a/libipmiconsole/ipmiconsole.c +++ b/libipmiconsole/ipmiconsole.c @@ -69,6 +69,7 @@ #include "freeipmi-portability.h" #include "conffile.h" +#include "fi_hostlist.h" #include "parse-common.h" #include "secure.h" @@ -836,10 +837,13 @@ ipmiconsole_engine_submit (ipmiconsole_ctx_t c, callback_arg) < 0) goto cleanup; - if (ipmiconsole_ctx_connection_setup (c) < 0) + /* session setup required before connection setup, so + * connection knows if IPv4 or IPv6 used + */ + if (ipmiconsole_ctx_session_setup (c) < 0) goto cleanup; - if (ipmiconsole_ctx_session_setup (c) < 0) + if (ipmiconsole_ctx_connection_setup (c) < 0) goto cleanup; if (ipmiconsole_engine_submit_ctx (c) < 0) @@ -1068,10 +1072,13 @@ ipmiconsole_engine_submit_block (ipmiconsole_ctx_t c) /* Set to success, so we know if an IPMI error occurred later */ ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_SUCCESS); - if (ipmiconsole_ctx_connection_setup (c) < 0) + /* session setup required before connection setup, so + * connection knows if IPv4 or IPv6 used + */ + if (ipmiconsole_ctx_session_setup (c) < 0) goto cleanup; - if (ipmiconsole_ctx_session_setup (c) < 0) + if (ipmiconsole_ctx_connection_setup (c) < 0) goto cleanup; if (_ipmiconsole_blocking_notification_setup (c) < 0) @@ -1153,9 +1160,12 @@ ipmiconsole_ctx_create (const char *hostname, struct ipmiconsole_engine_config *engine_config) { ipmiconsole_ctx_t c = NULL; - char hostnamebuf[MAXHOSTNAMELEN_WITH_PORT + 1]; - char *hostname_ptr; + char *hostname_copy = NULL; + char *port_copy = NULL; + const char *hostname_ptr = NULL; + const char *port_ptr = NULL; uint16_t port = RMCP_PRIMARY_RMCP_PORT; + int ret; if (!hostname || !ipmi_config @@ -1184,62 +1194,38 @@ ipmiconsole_ctx_create (const char *hostname, return (NULL); } - if (strchr (hostname, ':')) + /* Check for host:port or [Ipv6]:port format */ + if ((ret = fi_host_is_host_with_port (hostname, + &hostname_copy, + &port_copy)) < 0) { - char *ptr; - - if (strlen (hostname) > MAXHOSTNAMELEN_WITH_PORT) - { - IPMICONSOLE_DEBUG (("invalid input parameters")); - errno = EINVAL; - return (NULL); - } - - memset (hostnamebuf, '\0', MAXHOSTNAMELEN_WITH_PORT + 1); - strcpy (hostnamebuf, hostname); - - if ((ptr = strchr (hostnamebuf, ':'))) - { - char *endptr; - int tmp; - - *ptr = '\0'; - ptr++; - - if (strlen (hostnamebuf) > MAXHOSTNAMELEN) - { - IPMICONSOLE_DEBUG (("invalid input parameters")); - errno = EINVAL; - return (NULL); - } - - errno = 0; - tmp = strtol (ptr, &endptr, 0); - if (errno - || endptr[0] != '\0' - || tmp <= 0 - || tmp > USHRT_MAX) - { - IPMICONSOLE_DEBUG (("invalid input parameters")); - errno = EINVAL; - return (NULL); - } - - port = tmp; - } + IPMICONSOLE_DEBUG (("fi_host_is_host_with_port: %s", errno)); + errno = EINVAL; + return (NULL); + } - hostname_ptr = hostnamebuf; + if (ret) + { + hostname_ptr = hostname_copy; + port_ptr = port_copy; } else + hostname_ptr = hostname; + + if ((ret = fi_host_is_valid (hostname_ptr, + port_ptr, + &port)) < 0) { - if (strlen (hostname) > MAXHOSTNAMELEN) - { - IPMICONSOLE_DEBUG (("invalid input parameters")); - errno = EINVAL; - return (NULL); - } + IPMICONSOLE_DEBUG (("fi_host_is_valid: %s", errno)); + errno = EINVAL; + goto free_cleanup; + } - hostname_ptr = (char *)hostname; + if (!ret) + { + IPMICONSOLE_DEBUG (("invalid input parameters")); + errno = EINVAL; + goto free_cleanup; } /* If engine is not setup, the default_config is not yet known */ @@ -1247,7 +1233,7 @@ ipmiconsole_ctx_create (const char *hostname, { IPMICONSOLE_DEBUG (("engine not initialized")); errno = EAGAIN; - return (NULL); + goto free_cleanup; } if ((engine_config->engine_flags != IPMICONSOLE_ENGINE_DEFAULT @@ -1257,7 +1243,7 @@ ipmiconsole_ctx_create (const char *hostname, if (!(c = (ipmiconsole_ctx_t)secure_malloc (sizeof (struct ipmiconsole_ctx)))) { errno = ENOMEM; - return (NULL); + goto free_cleanup; } } else @@ -1265,7 +1251,7 @@ ipmiconsole_ctx_create (const char *hostname, if (!(c = (ipmiconsole_ctx_t)malloc (sizeof (struct ipmiconsole_ctx)))) { errno = ENOMEM; - return (NULL); + goto free_cleanup; } } @@ -1299,6 +1285,9 @@ ipmiconsole_ctx_create (const char *hostname, c->session_submitted = 0; + free (hostname_copy); + free (port_copy); + ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_SUCCESS); return (c); @@ -1321,6 +1310,12 @@ ipmiconsole_ctx_create (const char *hostname, secure_free (c, sizeof (struct ipmiconsole_ctx)); else free (c); + + free_cleanup: + + free (hostname_copy); + free (port_copy); + return (NULL); } diff --git a/libipmiconsole/ipmiconsole_ctx.c b/libipmiconsole/ipmiconsole_ctx.c index 0c44646e7..0f234c26d 100644 --- a/libipmiconsole/ipmiconsole_ctx.c +++ b/libipmiconsole/ipmiconsole_ctx.c @@ -74,8 +74,6 @@ #include "freeipmi-portability.h" -#define GETHOSTBYNAME_AUX_BUFLEN 1024 - extern List console_engine_ctxs_to_destroy; extern pthread_mutex_t console_engine_ctxs_to_destroy_mutex; extern struct ipmiconsole_ctx_config default_config; @@ -506,7 +504,11 @@ ipmiconsole_ctx_blocking_cleanup (ipmiconsole_ctx_t c) int ipmiconsole_ctx_connection_setup (ipmiconsole_ctx_t c) { - struct sockaddr_in srcaddr; + struct sockaddr *srcaddr; + socklen_t srcaddr_len; + struct sockaddr_in srcaddr4; + struct sockaddr_in6 srcaddr6; + int domain; int sv[2]; int secure_malloc_flag; @@ -514,6 +516,9 @@ ipmiconsole_ctx_connection_setup (ipmiconsole_ctx_t c) assert (c->magic == IPMICONSOLE_CTX_MAGIC); assert (!(c->session_submitted)); + /* session info must be setup first so we know how to setup IPv4 vs IPv6 */ + assert (c->session.session_info_setup); + memset (&(c->connection), '\0', sizeof (struct ipmiconsole_ctx_connection)); c->connection.user_fd = -1; c->connection.ipmiconsole_fd = -1; @@ -568,7 +573,26 @@ ipmiconsole_ctx_connection_setup (ipmiconsole_ctx_t c) /* Connection Data */ - if ((c->connection.ipmi_fd = socket (AF_INET, SOCK_DGRAM, 0)) < 0) + if (c->session.addr->sa_family == AF_INET) + { + memset (&srcaddr4, '\0', sizeof (struct sockaddr_in)); + srcaddr4.sin_family = AF_INET; + srcaddr4.sin_port = htons (0); + srcaddr = (struct sockaddr *)&srcaddr4; + srcaddr_len = sizeof (struct sockaddr_in); + domain = AF_INET; /* to remove dereference warning on srcaddr below */ + } + else + { + memset (&srcaddr6, '\0', sizeof (struct sockaddr_in6)); + srcaddr6.sin6_family = AF_INET6; + srcaddr6.sin6_port = htons (0); + srcaddr = (struct sockaddr *)&srcaddr6; + srcaddr_len = sizeof (struct sockaddr_in6); + domain = AF_INET6; /* to remove dereference warning on srcaddr below */ + } + + if ((c->connection.ipmi_fd = socket (domain, SOCK_DGRAM, 0)) < 0) { IPMICONSOLE_DEBUG (("socket: %s", strerror (errno))); if (errno == EMFILE) @@ -584,12 +608,7 @@ ipmiconsole_ctx_connection_setup (ipmiconsole_ctx_t c) goto cleanup; } - memset (&srcaddr, '\0', sizeof (struct sockaddr_in)); - srcaddr.sin_family = AF_INET; - srcaddr.sin_port = htons (0); - srcaddr.sin_addr.s_addr = htonl (INADDR_ANY); - - if (bind (c->connection.ipmi_fd, (struct sockaddr *)&srcaddr, sizeof (struct sockaddr_in)) < 0) + if (bind (c->connection.ipmi_fd, srcaddr, srcaddr_len) < 0) { IPMICONSOLE_DEBUG (("bind: %s", strerror (errno))); ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_SYSTEM_ERROR); @@ -1148,25 +1167,16 @@ ipmiconsole_ctx_connection_cleanup_session_not_submitted (ipmiconsole_ctx_t c) int ipmiconsole_ctx_session_setup (ipmiconsole_ctx_t c) { - struct hostent hent; - int h_errnop; - char buf[GETHOSTBYNAME_AUX_BUFLEN]; -#if defined(HAVE_FUNC_GETHOSTBYNAME_R_6) - struct hostent *hptr; -#elif defined(HAVE_FUNC_GETHOSTBYNAME_R_5) -#else /* !HAVE_FUNC_GETHOSTBYNAME_R */ - struct hostent *hptr; -#endif /* !HAVE_FUNC_GETHOSTBYNAME_R */ + struct addrinfo ai_hints, *ai_res = NULL, *ai = NULL; + char port_str[MAXPORTBUFLEN + 1]; + int ret; + int rv = -1; assert (c); assert (c->magic == IPMICONSOLE_CTX_MAGIC); c->session.console_port = c->config.port; - memset (&(c->session.addr), '\0', sizeof (struct sockaddr_in)); - c->session.addr.sin_family = AF_INET; - c->session.addr.sin_port = htons (c->session.console_port); - timeval_clear (&(c->session.last_ipmi_packet_sent)); /* Note: @@ -1180,7 +1190,7 @@ ipmiconsole_ctx_session_setup (ipmiconsole_ctx_t c) { IPMICONSOLE_DEBUG (("gettimeofday: %s", strerror (errno))); ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_SYSTEM_ERROR); - return (-1); + goto cleanup; } timeval_clear (&(c->session.last_keepalive_packet_sent)); @@ -1189,65 +1199,49 @@ ipmiconsole_ctx_session_setup (ipmiconsole_ctx_t c) { IPMICONSOLE_DEBUG (("gettimeofday: %s", strerror (errno))); ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_SYSTEM_ERROR); - return (-1); + goto cleanup; } - memset (&hent, '\0', sizeof (struct hostent)); -#if defined(HAVE_FUNC_GETHOSTBYNAME_R_6) - if (gethostbyname_r (c->config.hostname, - &hent, - buf, - GETHOSTBYNAME_AUX_BUFLEN, - &hptr, - &h_errnop)) -#elif defined(HAVE_FUNC_GETHOSTBYNAME_R_5) - /* Jan Forch - Solaris gethostbyname_r returns ptr, not integer */ - if (!gethostbyname_r (c->config.hostname, - &hent, - buf, - GETHOSTBYNAME_AUX_BUFLEN, - &h_errnop)) -#else /* !HAVE_FUNC_GETHOSTBYNAME_R */ - if (freeipmi_gethostbyname_r (c->config.hostname, - &hent, - buf, - GETHOSTBYNAME_AUX_BUFLEN, - &hptr, - &h_errnop)) -#endif /* !HAVE_FUNC_GETHOSTBYNAME_R */ - { - if (h_errnop == HOST_NOT_FOUND - || h_errnop == NO_ADDRESS - || h_errnop == NO_DATA) - { - ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_HOSTNAME_INVALID); - return (-1); - } -#if HAVE_HSTRERROR - IPMICONSOLE_DEBUG (("gethostbyname_r: %s", hstrerror (h_errnop))); -#else /* !HAVE_HSTRERROR */ - IPMICONSOLE_DEBUG (("gethostbyname_r: h_errno = %d", h_errnop)); -#endif /* !HAVE_HSTRERROR */ - ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_SYSTEM_ERROR); - return (-1); - } + memset (port_str, '\0', MAXPORTBUFLEN + 1); + snprintf (port_str, MAXPORTBUFLEN, "%d", c->session.console_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 defined(HAVE_FUNC_GETHOSTBYNAME_R_6) - if (!hptr) + if ((ret = getaddrinfo (c->config.hostname, port_str, &ai_hints, &ai_res))) { + IPMICONSOLE_DEBUG (("getaddrinfo: %s", gai_strerror (ret))); ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_HOSTNAME_INVALID); - return (-1); + goto cleanup; + } + + /* Try all of the different answers we got, until we succeed. */ + for (ai = ai_res; ai != NULL; ai = ai->ai_next) + { + if (ai->ai_family == AF_INET) + { + memcpy (&(c->session.addr4), ai->ai_addr, ai->ai_addrlen); + c->session.addr = (struct sockaddr *)&(c->session.addr4); + c->session.addr_len = sizeof (struct sockaddr_in); + } + else if (ai->ai_family == AF_INET6) + { + memcpy (&(c->session.addr6), ai->ai_addr, ai->ai_addrlen); + c->session.addr = (struct sockaddr *)&(c->session.addr6); + c->session.addr_len = sizeof (struct sockaddr_in6); + } + else + continue; + break; } -#elif defined(HAVE_FUNC_GETHOSTBYNAME_R_5) -#else /* !HAVE_FUNC_GETHOSTBYNAME_R */ - if (!hptr) + + if (!ai) { + IPMICONSOLE_DEBUG (("getaddrinfo: no entry found")); ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_HOSTNAME_INVALID); - return (-1); + goto cleanup; } -#endif /* !HAVE_FUNC_GETHOSTBYNAME_R */ - - c->session.addr.sin_addr = *((struct in_addr *)hent.h_addr); c->session.protocol_state = IPMICONSOLE_PROTOCOL_STATE_START; c->session.close_session_flag = 0; @@ -1269,7 +1263,7 @@ ipmiconsole_ctx_session_setup (ipmiconsole_ctx_t c) { IPMICONSOLE_DEBUG (("ipmi_check_session_sequence_number_2_0_init: %s", strerror (errno))); ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_INTERNAL_ERROR); - return (-1); + goto cleanup; } if (ipmi_get_random (&(c->session.message_tag), @@ -1277,14 +1271,14 @@ ipmiconsole_ctx_session_setup (ipmiconsole_ctx_t c) { IPMICONSOLE_DEBUG (("ipmi_get_random: %s", strerror (errno))); ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_INTERNAL_ERROR); - return (-1); + goto cleanup; } if (ipmi_get_random (&(c->session.requester_sequence_number), sizeof (c->session.requester_sequence_number)) < 0) { IPMICONSOLE_DEBUG (("ipmi_get_random: %s", strerror (errno))); ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_INTERNAL_ERROR); - return (-1); + goto cleanup; } c->session.requester_sequence_number %= (IPMI_LAN_REQUESTER_SEQUENCE_NUMBER_MAX + 1); @@ -1299,7 +1293,7 @@ ipmiconsole_ctx_session_setup (ipmiconsole_ctx_t c) { IPMICONSOLE_DEBUG (("ipmi_get_random: %s", strerror (errno))); ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_INTERNAL_ERROR); - return (-1); + goto cleanup; } } while (!c->session.remote_console_session_id); @@ -1308,7 +1302,7 @@ ipmiconsole_ctx_session_setup (ipmiconsole_ctx_t c) { IPMICONSOLE_DEBUG (("ipmi_get_random: %s", strerror (errno))); ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_INTERNAL_ERROR); - return (-1); + goto cleanup; } /* Keys and ptrs will be calculated during session setup. We just @@ -1352,7 +1346,12 @@ ipmiconsole_ctx_session_setup (ipmiconsole_ctx_t c) c->session.last_sol_output_packet_sequence_number = 0; c->session.last_sol_output_accepted_character_count = 0; - return (0); + c->session.session_info_setup = 1; + + rv = 0; + cleanup: + freeaddrinfo (ai_res); + return (rv); } void diff --git a/libipmiconsole/ipmiconsole_defs.h b/libipmiconsole/ipmiconsole_defs.h index b55de4b73..07820006e 100644 --- a/libipmiconsole/ipmiconsole_defs.h +++ b/libipmiconsole/ipmiconsole_defs.h @@ -61,8 +61,7 @@ #define MAXHOSTNAMELEN 64 #endif /* MAXHOSTNAMELEN */ -/* +5 for digits (max 65535) and +1 for colon ':' */ -#define MAXHOSTNAMELEN_WITH_PORT (MAXHOSTNAMELEN + 6) +#define MAXPORTBUFLEN 16 #ifndef MAXPATHLEN #define MAXPATHLEN 4096 @@ -347,7 +346,10 @@ struct ipmiconsole_ctx_connection { struct ipmiconsole_ctx_session { int16_t console_port; - struct sockaddr_in addr; + struct sockaddr *addr; + socklen_t addr_len; + struct sockaddr_in addr4; + struct sockaddr_in6 addr6; /* Session timeout, retransmission timeout, keepalive timeout maintenance */ struct timeval last_ipmi_packet_sent; @@ -424,6 +426,9 @@ struct ipmiconsole_ctx_session { /* SOL Output (BMC to remote console) */ uint8_t last_sol_output_packet_sequence_number; uint8_t last_sol_output_accepted_character_count; + + /* Flag indicating session info is setup */ + int session_info_setup; }; /* Context debug stuff */ @@ -552,10 +557,10 @@ struct ipmiconsole_ctx { struct ipmiconsole_ctx_blocking blocking; - struct ipmiconsole_ctx_connection connection; - struct ipmiconsole_ctx_session session; + struct ipmiconsole_ctx_connection connection; + struct ipmiconsole_ctx_fds fds; /* session_submitted - flag indicates context submitted to engine diff --git a/libipmiconsole/ipmiconsole_engine.c b/libipmiconsole/ipmiconsole_engine.c index 6f2a99209..9e00f4ad4 100644 --- a/libipmiconsole/ipmiconsole_engine.c +++ b/libipmiconsole/ipmiconsole_engine.c @@ -478,8 +478,9 @@ static int _ipmi_recvfrom (ipmiconsole_ctx_t c) { char buffer[IPMICONSOLE_PACKET_BUFLEN]; - struct sockaddr_in from; - unsigned int fromlen = sizeof (struct sockaddr_in); + struct sockaddr_in6 from6; + struct sockaddr *from = (struct sockaddr *)&from6; + unsigned int fromlen = sizeof (struct sockaddr_in6); ssize_t len; int n, dropped = 0; int secure_malloc_flag; @@ -503,7 +504,7 @@ _ipmi_recvfrom (ipmiconsole_ctx_t c) buffer, IPMICONSOLE_PACKET_BUFLEN, 0, - (struct sockaddr *)&from, + from, &fromlen); } while (len < 0 && errno == EINTR); @@ -559,12 +560,32 @@ _ipmi_recvfrom (ipmiconsole_ctx_t c) } /* Sanity Check */ - if (from.sin_family != AF_INET - || from.sin_addr.s_addr != c->session.addr.sin_addr.s_addr) + if (from6.sin6_family == AF_INET6) { - IPMICONSOLE_CTX_DEBUG (c, ("received from invalid address")); - /* Note: Not a fatal error, just return */ - return (0); + if (memcmp (&from6.sin6_addr, + &(c->session.addr6.sin6_addr), + sizeof (from6.sin6_addr))) + { + IPMICONSOLE_CTX_DEBUG (c, ("received from invalid address")); + /* Note: Not a fatal error, just return */ + return (0); + } + } + else + { + /* memcpy hacks to avoid warnings, i.e. + * warning: dereferencing pointer 'X' does break strict-aliasing rules + */ + struct sockaddr_in from4; + + memcpy (&from4, from, fromlen); + + if (from4.sin_addr.s_addr != c->session.addr4.sin_addr.s_addr) + { + IPMICONSOLE_CTX_DEBUG (c, ("received from invalid address")); + /* Note: Not a fatal error, just return */ + return (0); + } } /* Empty the scbuf if it's not empty */ @@ -634,8 +655,8 @@ _ipmi_sendto (ipmiconsole_ctx_t c) buffer, n, 0, - (struct sockaddr *)&(c->session.addr), - sizeof (struct sockaddr_in)); + c->session.addr, + c->session.addr_len); } while (len < 0 && errno == EINTR); } else @@ -646,8 +667,8 @@ _ipmi_sendto (ipmiconsole_ctx_t c) buffer, n, 0, - (struct sockaddr *)&(c->session.addr), - sizeof (struct sockaddr_in)); + c->session.addr, + c->session.addr_len); } while (len < 0 && errno == EINTR); } diff --git a/libipmiconsole/ipmiconsole_processing.c b/libipmiconsole/ipmiconsole_processing.c index 6142e4214..252d731ef 100644 --- a/libipmiconsole/ipmiconsole_processing.c +++ b/libipmiconsole/ipmiconsole_processing.c @@ -3926,9 +3926,17 @@ _process_protocol_state_close_session_sent (ipmiconsole_ctx_t c) /* now reset up w/ new console port */ c->session.console_port = console_port; - memset (&(c->session.addr), '\0', sizeof (struct sockaddr_in)); - c->session.addr.sin_family = AF_INET; - c->session.addr.sin_port = htons (c->session.console_port); + if (c->session.addr_len == sizeof (struct sockaddr_in)) + c->session.addr4.sin_port = htons (c->session.console_port); + else if (c->session.addr_len == sizeof (struct sockaddr_in6)) + c->session.addr6.sin6_port = htons (c->session.console_port); + else + { + /* Shouldn't be possible to reach here */ + IPMICONSOLE_CTX_DEBUG (c, ("reset port logic bug")); + ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_INTERNAL_ERROR); + return (-1); + } IPMICONSOLE_CTX_DEBUG (c, ("trying new port: %Xh", c->session.console_port));