static void _recvfrom (cbuf_t cbuf, int fd, struct sockaddr_in *srcaddr) { int n, rv, dropped = 0; uint8_t buf[IPMIPOWER_PACKET_BUFLEN]; struct sockaddr_in from; unsigned int fromlen = sizeof (struct sockaddr_in); do { /* For receive side, ipmi_lan_recvfrom and * ipmi_rmcpplus_recvfrom are identical. So we just use * ipmi_lan_recvfrom for both. * * In event of future change, should use util functions * ipmi_is_ipmi_1_5_packet or ipmi_is_ipmi_2_0_packet * appropriately. */ rv = ipmi_lan_recvfrom (fd, buf, IPMIPOWER_PACKET_BUFLEN, 0, (struct sockaddr *)&from, &fromlen); } while (rv < 0 && errno == EINTR); /* achu & hliebig: * * Premise from ipmitool (http://ipmitool.sourceforge.net/) * * On some OSes (it seems Unixes), the behavior is to not return * port denied errors up to the client for UDP responses (i.e. you * need to timeout). But on some OSes (it seems Windows), the * behavior is to return port denied errors up to the user for UDP * responses via ECONNRESET or ECONNREFUSED. * * If this were just the case, we could return or handle errors * properly and move on. However, it's not the case. * * According to Ipmitool, on some motherboards, both the OS and the * BMC are capable of responding to an IPMI request. That means you * can get an ECONNRESET or ECONNREFUSED, then later on, get your * real IPMI response. * * Our solution is copied from Ipmitool, we'll ignore some specific * errors and try to read again. * * If the ECONNREFUSED or ECONNRESET is from the OS, but we will get * an IPMI response later, the recvfrom later on gets the packet we * want. * * If the ECONNREFUSED or ECONNRESET is from the OS but there is no * BMC (or IPMI disabled, etc.), just do the recvfrom again to * eventually get a timeout, which is the behavior we'd like. */ if (rv < 0 && (errno == ECONNRESET || errno == ECONNREFUSED)) { IPMIPOWER_DEBUG (("ipmi_lan_recvfrom: connection refused: %s", strerror (errno))); return; } if (rv < 0) { IPMIPOWER_ERROR (("ipmi_lan_recvfrom: %s", strerror (errno))); exit (EXIT_FAILURE); } if (!rv) { IPMIPOWER_ERROR (("ipmi_lan_recvfrom: EOF")); exit (EXIT_FAILURE); } /* 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) return; /* cbuf should be empty, but if it isn't, empty it */ if (!cbuf_is_empty (cbuf)) { IPMIPOWER_DEBUG (("cbuf not empty, draining")); do { uint8_t tempbuf[IPMIPOWER_PACKET_BUFLEN]; if (cbuf_read (cbuf, tempbuf, IPMIPOWER_PACKET_BUFLEN) < 0) { IPMIPOWER_ERROR (("cbuf_read: %s", strerror (errno))); exit (EXIT_FAILURE); } } while(!cbuf_is_empty (cbuf)); } if ((n = cbuf_write (cbuf, buf, rv, &dropped)) < 0) { IPMIPOWER_ERROR (("cbuf_write: %s", strerror (errno))); exit (EXIT_FAILURE); } if (n != rv) { IPMIPOWER_ERROR (("cbuf_write: rv=%d n=%d", rv, n)); exit (EXIT_FAILURE); } if (dropped) IPMIPOWER_DEBUG (("cbuf_write: read dropped %d bytes", dropped)); }
static void _main_loop (Ipmi_Ping_CreatePacket create, Ipmi_Ping_ParsePacket parse, Ipmi_Ping_LatePacket late) { unsigned int sequence_number = 0; time_t last_send = 0; int ret; assert (create); assert (parse); assert (late); assert (pingtool_progname); assert (pingtool_end_result); if (pingtool_initial_sequence_number < 0) { int len; if ((len = ipmi_get_random (&pingtool_initial_sequence_number, sizeof (pingtool_initial_sequence_number))) < 0) ipmi_ping_err_exit ("ipmi_get_random: %s", strerror (errno)); if (len != sizeof (pingtool_initial_sequence_number)) ipmi_ping_err_exit ("ipmi_get_random: invalid len returned"); } sequence_number = pingtool_initial_sequence_number; printf ("%s %s (%s)\n", pingtool_progname, pingtool_dest, pingtool_dest_ip); while (pingtool_count == -1 || (pingtool_pkt_sent < pingtool_count)) { int rv, len, received = 0; uint8_t buf[IPMI_PING_MAX_PKT_LEN]; time_t now; /* wait if necessary */ now = time (NULL); if ((now - last_send) < pingtool_interval) { if (_sleep ((last_send + pingtool_interval - now)) < 0) continue; } if ((len = create (pingtool_dest, buf, IPMI_PING_MAX_PKT_LEN, sequence_number, pingtool_version, pingtool_debug)) < 0) ipmi_ping_err_exit ("_create failed: %s", strerror (errno)); rv = ipmi_lan_sendto (pingtool_sockfd, buf, len, 0, (struct sockaddr *)&pingtool_destaddr, sizeof (pingtool_destaddr)); if (rv < 0) ipmi_ping_err_exit ("ipmi_sendto: %s", strerror (errno)); if (rv != len) ipmi_ping_err_exit ("ipmi_sendto: wrong bytes written"); last_send = time (NULL); pingtool_pkt_sent++; while (((now = time (NULL)) - last_send) < pingtool_timeout) { fd_set rset; struct timeval tv; FD_ZERO (&rset); FD_SET (pingtool_sockfd, &rset); tv.tv_sec = (last_send + pingtool_timeout - now); tv.tv_usec = 0; if ((rv = select (pingtool_sockfd+1, &rset, NULL, NULL, &tv)) < 0) ipmi_ping_err_exit ("select: %s", strerror (errno)); if (rv == 1) { struct sockaddr_in from; socklen_t fromlen; fromlen = sizeof (from); len = ipmi_lan_recvfrom (pingtool_sockfd, buf, IPMI_PING_MAX_PKT_LEN, 0, (struct sockaddr *)&from, &fromlen); /* achu & hliebig: * * Premise from ipmitool (http://ipmitool.sourceforge.net/) * * On some OSes (it seems Unixes), the behavior is to not return * port denied errors up to the client for UDP responses (i.e. you * need to timeout). But on some OSes (it seems Windows), the * behavior is to return port denied errors up to the user for UDP * responses via ECONNRESET or ECONNREFUSED. * * If this were just the case, we could return or handle errors * properly and move on. However, it's not the case. * * According to Ipmitool, on some motherboards, both the OS and the * BMC are capable of responding to an IPMI request. That means you * can get an ECONNRESET or ECONNREFUSED, then later on, get your * real IPMI response. * * Our solution is copied from Ipmitool, we'll ignore some specific * errors and try to read again. * * If the ECONNREFUSED or ECONNRESET is from the OS, but we will get * an IPMI response later, the recvfrom later on gets the packet we * want. * * If the ECONNREFUSED or ECONNRESET is from the OS but there is no * BMC (or IPMI disabled, etc.), just do the recvfrom again to * eventually get a timeout, which is the behavior we'd like. */ if (len < 0 && (errno == ECONNRESET || errno == ECONNREFUSED)) continue; if (len < 0) ipmi_ping_err_exit ("ipmi_recvfrom: %s", strerror (errno)); if ((rv = parse (pingtool_dest, buf, len, inet_ntoa (from.sin_addr), sequence_number, pingtool_verbose, pingtool_version, pingtool_debug)) < 0) ipmi_ping_err_exit ("_parse failed: %s", strerror (errno)); /* If rv == 0, the sequence numbers don't match, so * we'll wait some more for the latest packet we sent * out. */ if (!rv) continue; received++; pingtool_pkt_recv++; break; } } if (!received) late (sequence_number); sequence_number++; } ret = pingtool_end_result (pingtool_progname, pingtool_dest, pingtool_pkt_sent, pingtool_pkt_recv); _cleanup (); exit (ret); }
static void _main_loop(Ipmi_Ping_CreatePacket _create, Ipmi_Ping_ParsePacket _parse, Ipmi_Ping_LatePacket _late) { unsigned int sequence_number = 0; time_t last_send = 0; int ret; assert(_create != NULL && _parse != NULL && _late != NULL && _progname != NULL && _end_result != NULL); if (_initial_sequence_number < 0) { int len; if ((len = ipmi_get_random((uint8_t *)&_initial_sequence_number, sizeof(_initial_sequence_number))) < 0) ipmi_ping_err_exit("ipmi_get_random: %s", strerror(errno)); if (len != sizeof(_initial_sequence_number)) ipmi_ping_err_exit("ipmi_get_random: invalid len returned"); } sequence_number = _initial_sequence_number; printf("%s %s (%s)\n", _progname, _dest, _dest_ip); while (_count == -1 || (_pkt_sent < _count)) { int rv, len, received = 0; uint8_t buffer[IPMI_PING_MAX_PKT_LEN]; time_t now; /* wait if necessary */ now = time(NULL); if ((now - last_send) < _interval) { if (_sleep((last_send + _interval - now)) < 0) continue; } if ((len = _create(_dest, (char *)buffer, IPMI_PING_MAX_PKT_LEN, sequence_number, _version, _debug)) < 0) ipmi_ping_err_exit("_create failed: %s", strerror(errno)); rv = ipmi_lan_sendto(_sockfd, buffer, len, 0, (struct sockaddr *)&_destaddr, sizeof(_destaddr)); if (rv < 0) ipmi_ping_err_exit("ipmi_sendto: %s", strerror(errno)); if (rv != len) ipmi_ping_err_exit("ipmi_sendto: wrong bytes written"); last_send = time(NULL); _pkt_sent++; while (((now = time(NULL)) - last_send) < _timeout) { fd_set rset; struct timeval tv; FD_ZERO(&rset); FD_SET(_sockfd, &rset); tv.tv_sec = (last_send + _timeout - now); tv.tv_usec = 0; if ((rv = select(_sockfd+1, &rset, NULL, NULL, &tv)) < 0) ipmi_ping_err_exit("select: %s", strerror(errno)); if (rv == 1) { struct sockaddr_in from; socklen_t fromlen; fromlen = sizeof(from); len = ipmi_lan_recvfrom(_sockfd, buffer, IPMI_PING_MAX_PKT_LEN, 0, (struct sockaddr *)&from, &fromlen); if (len < 0) ipmi_ping_err_exit("ipmi_recvfrom: %s", strerror(errno)); if ((rv = _parse(_dest, (char *)buffer, len, inet_ntoa(from.sin_addr), sequence_number, _verbose, _version, _debug)) < 0) ipmi_ping_err_exit("_parse failed: %s", strerror(errno)); /* If rv == 0, the sequence numbers don't match, so * we'll wait some more for the latest packet we sent * out. */ if (rv == 0) continue; received++; _pkt_recv++; break; } } if (received == 0) _late(sequence_number); sequence_number++; } ret = _end_result(_progname, _dest, _pkt_sent, _pkt_recv); _cleanup(); exit(ret); }
static void _receive_ping (int fd) { struct sockaddr_in from; struct ipmidetectd_info *info; uint8_t buf[IPMIDETECTD_BUFLEN]; int len; socklen_t fromlen = sizeof (struct sockaddr_in); char *tmpstr; /* We're happy as long as we receive something. We don't bother * checking sequence numbers or anything like that. */ len = ipmi_lan_recvfrom (fd, buf, IPMIDETECTD_BUFLEN, 0, (struct sockaddr *)&from, &fromlen); /* achu & hliebig: * * Premise from ipmitool (http://ipmitool.sourceforge.net/) * * On some OSes (it seems Unixes), the behavior is to not return * port denied errors up to the client for UDP responses (i.e. you * need to timeout). But on some OSes (it seems Windows), the * behavior is to return port denied errors up to the user for UDP * responses via ECONNRESET or ECONNREFUSED. * * If this were just the case, we could return or handle errors * properly and move on. However, it's not the case. * * According to Ipmitool, on some motherboards, both the OS and the * BMC are capable of responding to an IPMI request. That means you * can get an ECONNRESET or ECONNREFUSED, then later on, get your * real IPMI response. * * Our solution is copied from Ipmitool, we'll ignore some specific * errors and try to read again. * * If the ECONNREFUSED or ECONNRESET is from the OS, but we will get * an IPMI response later, the recvfrom later on gets the packet we * want. * * If the ECONNREFUSED or ECONNRESET is from the OS but there is no * BMC (or IPMI disabled, etc.), just do the recvfrom again to * eventually get a timeout, which is the behavior we'd like. */ if (len < 0 && (errno == ECONNRESET || errno == ECONNREFUSED)) return; if (len < 0) err_exit ("ipmi_lan_recvfrom: %s", strerror (errno)); if (!(tmpstr = inet_ntoa (from.sin_addr))) err_exit ("inet_ntoa: %s", strerror (errno)); /* strerror? */ if ((info = hash_find (nodes_index, tmpstr))) { if (gettimeofday (&(info->last_received), NULL) < 0) err_exit ("gettimeofday: %s", strerror (errno)); if (cmd_args.debug) fprintf (stderr, "Ping Reply from %s\n", info->hostname); } }
/* * Return 0 on success * Return -1 on fatal error */ static int _ipmi_recvfrom (ipmiconsole_ctx_t c) { char buffer[IPMICONSOLE_PACKET_BUFLEN]; struct sockaddr_in from; unsigned int fromlen = sizeof (struct sockaddr_in); ssize_t len; int n, dropped = 0; int secure_malloc_flag; assert (c); assert (c->magic == IPMICONSOLE_CTX_MAGIC); secure_malloc_flag = (c->config.engine_flags & IPMICONSOLE_ENGINE_LOCK_MEMORY) ? 1 : 0; do { /* For receive side, ipmi_lan_recvfrom and * ipmi_rmcpplus_recvfrom are identical. So we just use * ipmi_lan_recvfrom for both. * * In event of future change, should use util functions * ipmi_is_ipmi_1_5_packet or ipmi_is_ipmi_2_0_packet * appropriately. */ len = ipmi_lan_recvfrom (c->connection.ipmi_fd, buffer, IPMICONSOLE_PACKET_BUFLEN, 0, (struct sockaddr *)&from, &fromlen); } while (len < 0 && errno == EINTR); /* achu & hliebig: * * Premise from ipmitool (http://ipmitool.sourceforge.net/) * * On some OSes (it seems Unixes), the behavior is to not return * port denied errors up to the client for UDP responses (i.e. you * need to timeout). But on some OSes (it seems Windows), the * behavior is to return port denied errors up to the user for UDP * responses via ECONNRESET or ECONNREFUSED. * * If this were just the case, we could return or handle errors * properly and move on. However, it's not the case. * * According to Ipmitool, on some motherboards, both the OS and the * BMC are capable of responding to an IPMI request. That means you * can get an ECONNRESET or ECONNREFUSED, then later on, get your * real IPMI response. * * Our solution is copied from Ipmitool, we'll ignore some specific * errors and try to read again. * * If the ECONNREFUSED or ECONNRESET is from the OS, but we will get * an IPMI response later, the recvfrom later on gets the packet we * want. * * If the ECONNREFUSED or ECONNRESET is from the OS but there is no * BMC (or IPMI disabled, etc.), just do the recvfrom again to * eventually get a timeout, which is the behavior we'd like. */ if (len < 0 && (errno == ECONNRESET || errno == ECONNREFUSED)) { IPMICONSOLE_CTX_DEBUG (c, ("ipmi_lan_recvfrom: connection refused: %s", strerror (errno))); return (0); } if (len < 0) { IPMICONSOLE_CTX_DEBUG (c, ("ipmi_lan_recvfrom: %s", strerror (errno))); ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_SYSTEM_ERROR); return (-1); } if (!len) { IPMICONSOLE_CTX_DEBUG (c, ("ipmi_lan_recvfrom: no data", strerror (errno))); /* Note: Not a fatal error, just return*/ return (0); } /* Sanity Check */ if (from.sin_family != AF_INET || from.sin_addr.s_addr != c->session.addr.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 */ if (!scbuf_is_empty (c->connection.ipmi_from_bmc)) { IPMICONSOLE_CTX_DEBUG (c, ("ipmi_from_bmc not empty, draining")); do { char tempbuf[IPMICONSOLE_PACKET_BUFLEN]; if (scbuf_read (c->connection.ipmi_from_bmc, tempbuf, IPMICONSOLE_PACKET_BUFLEN) < 0) { IPMICONSOLE_CTX_DEBUG (c, ("scbuf_read: %s", strerror (errno))); break; } } while(!scbuf_is_empty (c->connection.ipmi_from_bmc)); } if ((n = scbuf_write (c->connection.ipmi_from_bmc, buffer, len, &dropped, secure_malloc_flag)) < 0) { IPMICONSOLE_CTX_DEBUG (c, ("scbuf_write: %s", strerror (errno))); ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_INTERNAL_ERROR); return (-1); } if (n != len) { IPMICONSOLE_CTX_DEBUG (c, ("scbuf_write: invalid bytes written; n=%d; len=%d", n, len)); ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_INTERNAL_ERROR); return (-1); } if (dropped) { IPMICONSOLE_CTX_DEBUG (c, ("scbuf_write: dropped data: dropped=%d", dropped)); ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_INTERNAL_ERROR); return (-1); } return (0); }