From 86c5a55a4f1722452e26a05ec15121c1f93d177b Mon Sep 17 00:00:00 2001 From: Andreas Baumann Date: Sat, 29 Apr 2023 09:27:08 +0200 Subject: finished a first ping version (which still requires root) --- Makefile | 3 +- README | 11 +++- ping.1 | 12 ++++ ping.c | 211 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 235 insertions(+), 2 deletions(-) create mode 100644 ping.1 create mode 100644 ping.c diff --git a/Makefile b/Makefile index a33b419..83f8a68 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,8 @@ LIB = $(LIBUTF) $(LIBUTIL) BIN =\ more\ hexdump\ - ifconfig + ifconfig\ + ping LIBUTILOBJ = $(LIBUTILSRC:.c=.o) OBJ = $(BIN:=.o) $(LIBUTFOBJ) $(LIBUTILOBJ) diff --git a/README b/README index 24f0c06..99ae704 100644 --- a/README +++ b/README @@ -11,6 +11,8 @@ Currently it contains the following: - a 'ifconfig': show and configure simple interfaces - a 'ping': test a network connection - a 'netstat': see list of active connections of a machine +- 'mkswap' +- 'fsck.ext4', 'mkfs.ext4': if ext4 is the only file system we support Note: abase is a shameless copy of sbase/ubase in certain areas like the Makefile, libutil, etc. This might also make an integration into @@ -48,5 +50,12 @@ Bugs and Questions - net-3-tools - nosh - netlink api or ioctls - + +References +---------- + +- ping: + - https://www.geeksforgeeks.org/ping-in-c/ + - Stevens Network Programming, Vol. 1 + [1] http://www.musl-libc.org/ diff --git a/ping.1 b/ping.1 new file mode 100644 index 0000000..c892f6f --- /dev/null +++ b/ping.1 @@ -0,0 +1,12 @@ +.Dd 2023-04-24 +.Dt PING 1 +.Os abase +.Sh NAME +.Nm ping +.Nd tests reachability of a host on the network +.Sh SYNOPSIS +.Nm +.Sh DESCRIPTION +.Nm +.Sh OPTIONS +.sp diff --git a/ping.c b/ping.c new file mode 100644 index 0000000..2431d52 --- /dev/null +++ b/ping.c @@ -0,0 +1,211 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "util.h" + +static volatile int terminate = 0; + +static void +usage(void) +{ + eprintf("usage: %s \n", argv0); +} + +static void +terminateHandler(int sig) +{ + terminate = 1; +} + +/* as per RFC 1035 section 2.3.4 it is reasonable to assume host names + * are not longer than 255 characters */ +#define FQDN_LEN 255 + +#define PING_PACKET_SIZE 64 +struct ping_packet { + struct icmphdr header; + char msg[PING_PACKET_SIZE-sizeof(struct icmphdr)]; +}; + +/* see Stevens Unix Networking Programming Vol. 1, p. 672 */ +static +unsigned short checksum(void *b, int buflen) +{ + unsigned short *buf = b; + unsigned int sum = 0; + unsigned short result = 0; + + for (sum = 0; buflen > 1; buflen -= 2) { + sum += *buf++; + } + if (buflen == 1) { + sum += *(unsigned char*)buf; + } + sum = (sum >> 16) + (sum & 0xFFFF); + sum += (sum >>16); + result = ~sum; + + return result; +} + +int +main(int argc, char *argv[]) +{ + struct addrinfo hints; + char *host; + struct addrinfo *addresses, *address; + struct sockaddr *ping_address; + struct sockaddr_in sending_address; + int res, fd; + struct timespec time_start, time_end; + + ARGBEGIN { + default: + usage(); + } ARGEND + + if (argc != 1) { + weprintf("Expecting exactly one 'destination' to ping\n"); + usage(); + } + host = argv[0]; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; /* IPv4 for now only */ + hints.ai_socktype = SOCK_RAW; + hints.ai_flags = AI_CANONNAME; + hints.ai_protocol = IPPROTO_ICMP; + hints.ai_canonname = NULL; + hints.ai_addr = NULL; + hints.ai_next = NULL; + if ((res=getaddrinfo(host, NULL, &hints, &addresses)) != 0) { + weprintf("error calling getaddrinfo for '%s': %s\n", host, gai_strerror(res)); + return 1; + } + + char ip_addr[INET_ADDRSTRLEN]; + char canon_host[FQDN_LEN]; + ping_address = NULL; + for (address = addresses; address != NULL; address = address->ai_next) { + struct sockaddr_in* addr = (struct sockaddr_in*)address->ai_addr; + inet_ntop(AF_INET, &addr->sin_addr, ip_addr, INET_ADDRSTRLEN); + strncpy(canon_host, address->ai_canonname, sizeof(canon_host)); + + fd = socket(address->ai_family, address->ai_socktype, address->ai_protocol); + if (fd == -1) { + if (errno == EPERM) { + weprintf("Permission denied, are you root?\n"); + continue; + } + weprintf("socket failed: %s\n", strerror(errno)); + continue; + } else { + ping_address = address->ai_addr; + } + + break; + } + + if (ping_address == NULL) { + return 1; + } + + signal(SIGINT, terminateHandler); + + printf("PING %s (%s)\n", canon_host, ip_addr); + + int ttl_val = 255; + if (setsockopt(fd, SOL_IP, IP_TTL, &ttl_val, sizeof(ttl_val)) < 0) { + freeaddrinfo(addresses); + close(fd); + eprintf("Unable to set TTL on socket: %s\n", host, strerror(errno)); + } + + struct timeval tv_out; + tv_out.tv_sec = 1; + tv_out.tv_usec = 0; + if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv_out, sizeof(tv_out)) < 0) { + freeaddrinfo(addresses); + close(fd); + eprintf("Unable to set receive time on socket: %s\n", host, strerror(errno)); + } + + int counter = 0; + int sent = 0; + int received = 0; + while (!terminate) { + struct ping_packet packet; + + memset(&packet, 0, sizeof(packet)); + packet.header.type = ICMP_ECHO; + packet.header.code = 0; /* ECHO */ + packet.header.un.echo.id = getpid(); + packet.header.un.echo.sequence = counter; + for (int i = 0; i < sizeof(packet.msg)-1; i++) { + packet.msg[i] = counter+i; + } + packet.header.checksum = 0; /* set to 0 for checksum computation */ + packet.header.checksum = checksum(&packet, sizeof(packet)); + counter++; + + sleep(1); + + clock_gettime(CLOCK_MONOTONIC, &time_start); + + if (sendto(fd, &packet, sizeof(packet), 0, ping_address, sizeof(*ping_address)) < 0) { + weprintf("Unable to send packet to '%s': %s\n", host, strerror(errno)); + sent = 0; + } else { + sent = 1; + } + + int address_len = sizeof(sending_address); + if (recvfrom(fd, &packet, sizeof(packet), 0, (struct sockaddr *)&sending_address, &address_len) <= 0) { + freeaddrinfo(addresses); + close(fd); + eprintf("Unable to receive packet from '%s': %s\n", host, strerror(errno)); + } else { + if (sent) { + if (packet.header.type != 69 || packet.header.code != 0) { + freeaddrinfo(addresses); + close(fd); + eprintf("Received unknown ICMP package with code=%d and type=%d from '%s'", + packet.header.code, packet.header.type, host); + } else { + char sending_ip_addr[INET_ADDRSTRLEN]; + //~ struct sockaddr_in* addr = (struct sockaddr_in*)&sending_address; + inet_ntop(AF_INET, &sending_address.sin_addr, ip_addr, INET_ADDRSTRLEN); + + clock_gettime(CLOCK_MONOTONIC, &time_end); + double timeElapsed = ((double)(time_end.tv_nsec - time_start.tv_nsec)) / 1000000.0; + long double rtt_msec = (time_end.tv_sec - time_start.tv_sec) * 1000.0 + timeElapsed; + + printf("%d bytes from %s (%s): icmp_seq=%d ttl=%d time=%0.3Lf ms\n", + sizeof(packet), host, ip_addr, counter, ttl_val, rtt_msec); + received++; + } + } + } + } + + printf("--- %s ping statistics ---\n" + "%d packets transmitted, %d received , %0.0f%% packet loss\n", + host, counter, received, + ((counter - received)/counter) * 100.0); + + freeaddrinfo(addresses); + + close(fd); + + return 0; +} -- cgit v1.2.3-54-g00ecf