Ping: Add -s argument to specify the payload size of the ping

Change the static buffers to ByteBuffers to deal with the dynamic
size of the incoming and outgoing packets. Use sizeof(struct ip) rather
than the magic number '20' for the IPv4 header size.
Report the size of the reply packet to the console.
This commit is contained in:
Thomas Wagenveld 2021-08-02 19:45:34 +02:00 committed by Andreas Kling
parent 8b61667924
commit 4181c0330c

View file

@ -5,11 +5,13 @@
*/
#include <AK/Assertions.h>
#include <AK/ByteBuffer.h>
#include <LibCore/ArgsParser.h>
#include <arpa/inet.h>
#include <errno.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <serenity.h>
#include <signal.h>
@ -27,6 +29,7 @@ static uint32_t total_ms;
static int min_ms;
static int max_ms;
static const char* host;
static int payload_size = -1;
static void closing_statistics()
{
@ -58,8 +61,14 @@ int main(int argc, char** argv)
Core::ArgsParser args_parser;
args_parser.add_positional_argument(host, "Host to ping", "host");
args_parser.add_option(count, "Stop after sending specified number of ECHO_REQUEST packets.", "count", 'c', "count");
args_parser.add_option(payload_size, "Amount of bytes to send as payload in the ECHO_REQUEST packets.", "size", 's', "size");
args_parser.parse(argc, argv);
if (payload_size < 0) {
// Use the default.
payload_size = 32 - sizeof(struct icmphdr);
}
int fd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
if (fd < 0) {
perror("socket");
@ -106,18 +115,6 @@ int main(int argc, char** argv)
peer_address.sin_addr.s_addr = *(const in_addr_t*)hostent->h_addr_list[0];
struct PingPacket {
struct icmphdr header;
char msg[64 - sizeof(struct icmphdr)];
};
struct PongPacket {
// FIXME: IPv4 headers are not actually fixed-size, handle other sizes.
char ip_header[20];
struct icmphdr header;
char msg[64 - sizeof(struct icmphdr)];
};
uint16_t seq = 1;
sighandler_t ret = signal(SIGINT, [](int) {
@ -130,24 +127,24 @@ int main(int argc, char** argv)
}
for (;;) {
PingPacket ping_packet;
memset(&ping_packet, 0, sizeof(PingPacket));
ByteBuffer ping_packet = ByteBuffer::create_zeroed(sizeof(struct icmphdr) + payload_size);
struct icmphdr* ping_hdr = reinterpret_cast<struct icmphdr*>(ping_packet.data());
ping_hdr->type = ICMP_ECHO;
ping_hdr->code = 0;
ping_hdr->un.echo.id = htons(pid);
ping_hdr->un.echo.sequence = htons(seq++);
ping_packet.header.type = ICMP_ECHO;
ping_packet.header.code = 0;
ping_packet.header.un.echo.id = htons(pid);
ping_packet.header.un.echo.sequence = htons(seq++);
// Fill payload
for (int i = 0; i < payload_size; i++) {
ping_packet[i + sizeof(struct icmphdr)] = i & 0xFF;
}
bool fits = String("Hello there!\n").copy_characters_to_buffer(ping_packet.msg, sizeof(ping_packet.msg));
// It's a constant string, we can be sure that it fits.
VERIFY(fits);
ping_packet.header.checksum = internet_checksum(&ping_packet, sizeof(PingPacket));
ping_hdr->checksum = internet_checksum(ping_packet.data(), ping_packet.size());
struct timeval tv_send;
gettimeofday(&tv_send, nullptr);
rc = sendto(fd, &ping_packet, sizeof(PingPacket), 0, (const struct sockaddr*)&peer_address, sizeof(sockaddr_in));
rc = sendto(fd, ping_packet.data(), ping_packet.size(), 0, (const struct sockaddr*)&peer_address, sizeof(sockaddr_in));
if (rc < 0) {
perror("sendto");
return 1;
@ -159,23 +156,25 @@ int main(int argc, char** argv)
total_pings++;
for (;;) {
PongPacket pong_packet;
// FIXME: IPv4 headers are not actually fixed-size, handle other sizes.
ByteBuffer pong_packet = ByteBuffer::create_uninitialized(sizeof(struct ip) + sizeof(struct icmphdr) + payload_size);
struct icmphdr* pong_hdr = reinterpret_cast<struct icmphdr*>(pong_packet.data() + sizeof(struct ip));
socklen_t peer_address_size = sizeof(peer_address);
rc = recvfrom(fd, &pong_packet, sizeof(PongPacket), 0, (struct sockaddr*)&peer_address, &peer_address_size);
rc = recvfrom(fd, pong_packet.data(), pong_packet.size(), 0, (struct sockaddr*)&peer_address, &peer_address_size);
if (rc < 0) {
if (errno == EAGAIN) {
outln("Request (seq={}) timed out.", ntohs(ping_packet.header.un.echo.sequence));
outln("Request (seq={}) timed out.", ntohs(ping_hdr->un.echo.sequence));
break;
}
perror("recvfrom");
return 1;
}
if (pong_packet.header.type != ICMP_ECHOREPLY)
if (pong_hdr->type != ICMP_ECHOREPLY)
continue;
if (pong_packet.header.code != 0)
if (pong_hdr->code != 0)
continue;
if (ntohs(pong_packet.header.un.echo.id) != pid)
if (ntohs(pong_hdr->un.echo.id) != pid)
continue;
struct timeval tv_receive;
@ -186,7 +185,7 @@ int main(int argc, char** argv)
int ms = tv_diff.tv_sec * 1000 + tv_diff.tv_usec / 1000;
successful_pings++;
int seq_dif = ntohs(ping_packet.header.un.echo.sequence) - ntohs(pong_packet.header.un.echo.sequence);
int seq_dif = ntohs(ping_hdr->un.echo.sequence) - ntohs(pong_hdr->un.echo.sequence);
// Approximation about the timeout of the out of order packet
if (seq_dif)
@ -201,15 +200,15 @@ int main(int argc, char** argv)
max_ms = ms;
char addr_buf[INET_ADDRSTRLEN];
outln("Pong from {}: id={}, seq={}{}, time={}ms",
outln("Pong from {}: id={}, seq={}{}, time={}ms, size={}",
inet_ntop(AF_INET, &peer_address.sin_addr, addr_buf, sizeof(addr_buf)),
ntohs(pong_packet.header.un.echo.id),
ntohs(pong_packet.header.un.echo.sequence),
pong_packet.header.un.echo.sequence != ping_packet.header.un.echo.sequence ? "(!)" : "",
ms);
ntohs(pong_hdr->un.echo.id),
ntohs(pong_hdr->un.echo.sequence),
pong_hdr->un.echo.sequence != ping_hdr->un.echo.sequence ? "(!)" : "",
ms, rc);
// If this was a response to an earlier packet, we still need to wait for the current one.
if (pong_packet.header.un.echo.sequence != ping_packet.header.un.echo.sequence)
if (pong_hdr->un.echo.sequence != ping_hdr->un.echo.sequence)
continue;
break;
}