Add ping(8).

This commit is contained in:
Jonas 'Sortie' Termansen 2017-03-09 22:24:40 +01:00
parent be563c165e
commit f884034e21
6 changed files with 227 additions and 1 deletions

View File

@ -24,6 +24,7 @@ kblayout \
kblayout-compiler \
login \
mkinitrd \
ping \
regress \
rw \
sf \

1
ping/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
ping

29
ping/Makefile Normal file
View File

@ -0,0 +1,29 @@
SOFTWARE_MEANT_FOR_SORTIX=1
include ../build-aux/platform.mak
include ../build-aux/compiler.mak
include ../build-aux/version.mak
include ../build-aux/dirs.mak
OPTLEVEL?=$(DEFAULT_OPTLEVEL)
CFLAGS?=$(OPTLEVEL)
CFLAGS += -Wall -Wextra
BINARIES = ping
MANPAGES8 = ping.8
all: $(BINARIES)
.PHONY: all install clean
install: all
mkdir -p $(DESTDIR)$(SBINDIR)
install $(BINARIES) $(DESTDIR)$(SBINDIR)
mkdir -p $(DESTDIR)$(MANDIR)/man8
install $(MANPAGES8) $(DESTDIR)$(MANDIR)/man8
%: %.c
$(CC) -std=gnu11 $(CFLAGS) $(CPPFLAGS) $< -o $@
clean:
rm -f $(BINARIES)

40
ping/ping.8 Normal file
View File

@ -0,0 +1,40 @@
.Dd February 23, 2023
.Dt PING 8
.Os
.Sh NAME
.Nm ping
.Nd internet control message protocol echo
.Sh SYNOPSIS
.Nm
.Op Fl 46
.Ar host
.Sh DESCRIPTION
.Nm
tests connectivity to the remote
.Ar host
by sending an Internet Control Message Protocol
.Xr ( icmp 4 )
ECHO
message and measures how quickly it responds to the ping and how many packets
are lost.
.Pp
The options are as follows:
.Bl -tag -width "12345678"
.It Fl 4
Ping using the Internet Prototocol version 4
.Xr ( ip 4 ) .
.It Fl 6
Ping using the Internet Prototocol version 6
.Xr ( ip6 4 ) .
.El
.Sh EXIT STATUS
.Nm
runs until terminated or it exits non-zero upon a fatal error.
.Sh SEE ALSO
.Xr icmp 4 ,
.Xr ip 4 ,
.Xr ip6 4 ,
.Xr ping 4 ,
.Xr ifconfig 8
.Sh BUGS
IPv6 is not yet implemented in the kernel.

154
ping/ping.c Normal file
View File

@ -0,0 +1,154 @@
/*
* Copyright (c) 2017, 2023 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* ping.c
* Internet Control Message Protocol Echo.
*/
#include <sys/socket.h>
#include <err.h>
#include <netdb.h>
#include <netinet/in.h>
#include <poll.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <timespec.h>
#include <unistd.h>
#define PAYLOAD_SIZE 56
int main(int argc, char* argv[])
{
bool ipv4 = false;
bool ipv6 = false;
int opt;
while ( (opt = getopt(argc, argv, "46")) != -1 )
{
switch ( opt )
{
case '4': ipv4 = true; break;
case '6': ipv6 = true; break;
default: return 1;
}
}
if ( argc - optind < 1 )
errx(1, "No host given");
if ( argc - optind > 1 )
errx(1, "Unexpected extra operand: %s", argv[optind + 1]);
const char* host = argv[optind];
if ( 1 < ipv4 + ipv6 )
errx(1, "The -4 and -6 options are mutually incompatible");
struct addrinfo hint;
memset(&hint, 0, sizeof(hint));
if ( ipv4 )
hint.ai_family = AF_INET;
if ( ipv6 )
hint.ai_family = AF_INET6;
hint.ai_socktype = SOCK_DGRAM;
hint.ai_protocol = IPPROTO_PING;
struct addrinfo* res0 = NULL;
int status = getaddrinfo(host, NULL, &hint, &res0);
if ( status == EAI_SYSTEM )
err(1, "%s", host);
if ( status )
errx(1, "%s: %s", host, gai_strerror(status));
if ( !res0 )
errx(1, "%s: %s", host, gai_strerror(EAI_NONAME));
char host_address[NI_MAXHOST];
int fd;
for ( struct addrinfo* res = res0; res; res = res->ai_next )
{
if ( (fd = socket(res->ai_family, res->ai_socktype,
res->ai_protocol)) < 0 )
{
if ( res->ai_next )
continue;
err(1, "socket");
}
if ( connect(fd, res->ai_addr, res->ai_addrlen) < 0 )
{
close(fd);
if ( res->ai_next )
continue;
err(1, "connect: %s", host);
}
if ( getnameinfo(res->ai_addr, res->ai_addrlen, host_address,
sizeof(host_address), NULL, 0, NI_NUMERICHOST) < 0 )
strlcpy(host_address, "unknown", sizeof(host_address));
break;
}
freeaddrinfo(res0);
printf("PING %s (%s) %zu bytes of data.\n",
host, host_address, (size_t) (PAYLOAD_SIZE + 8));
while ( true )
{
unsigned char expected[PAYLOAD_SIZE];
arc4random_buf(expected, sizeof(expected));
struct timespec begun;
clock_gettime(CLOCK_MONOTONIC, &begun);
// TODO: Don't fail on network errors.
if ( send(fd, expected, sizeof(expected), 0) < 0 )
err(1, "send");
struct timespec timeout = timespec_add(timespec_make(1, 0), begun);
while ( true )
{
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
struct timespec remaining = timespec_sub(timeout, now);
if ( remaining.tv_sec < 0 )
break;
struct pollfd pfd = { 0 };
pfd.fd = fd;
pfd.events = POLLIN;
if ( ppoll(&pfd, 1, &remaining, NULL) <= 0 )
break;
unsigned char gotten[PAYLOAD_SIZE];
ssize_t amount = recv(fd, gotten, sizeof(gotten), 0);
struct timespec end;
clock_gettime(CLOCK_MONOTONIC, &end);
// TODO: Don't fail on network errors.
if ( amount < 0 )
err(1, "recv");
if ( amount == PAYLOAD_SIZE &&
memcmp(expected, gotten, PAYLOAD_SIZE) == 0 )
{
// TODO: Reverse DNS.
uint16_t sequence = gotten[0] << 8 | gotten[1] << 0;
struct timespec duration = timespec_sub(end, begun);
uintmax_t ms = (uintmax_t) duration.tv_sec * (uintmax_t) 1000 +
(uintmax_t) ((duration.tv_nsec / 1000) / 1000);
unsigned int us = (duration.tv_nsec / 1000) % 1000;
printf("%zu bytes from %s (%s): icmp_seq=%u time=%ju.%03u ms\n",
(size_t) (PAYLOAD_SIZE + 8), host, host_address,
sequence, ms, us);
}
}
}
return 0;
}

View File

@ -474,7 +474,8 @@ socket options was attempted to be set to a non-zero value.
.Xr if 4 ,
.Xr inet 4 ,
.Xr ip 4 ,
.Xr kernel 7
.Xr kernel 7 ,
.Xr ping 8
.Sh STANDARDS
.Rs
.%A J. Postel (ed.)