diff options
author | Andreas Baumann <mail@andreasbaumann.cc> | 2023-04-29 09:27:08 +0200 |
---|---|---|
committer | Andreas Baumann <mail@andreasbaumann.cc> | 2023-04-29 09:27:08 +0200 |
commit | 86c5a55a4f1722452e26a05ec15121c1f93d177b (patch) | |
tree | b46f643ec1b1381c6fad92d2e9103b0f7de1db4c | |
parent | 296512eaab30ec5bcba041d4f3db52b12e10fe2d (diff) | |
download | abase-86c5a55a4f1722452e26a05ec15121c1f93d177b.tar.gz abase-86c5a55a4f1722452e26a05ec15121c1f93d177b.tar.bz2 |
finished a first ping version (which still requires root)
-rw-r--r-- | Makefile | 3 | ||||
-rw-r--r-- | README | 11 | ||||
-rw-r--r-- | ping.1 | 12 | ||||
-rw-r--r-- | ping.c | 211 |
4 files changed, 235 insertions, 2 deletions
@@ -16,7 +16,8 @@ LIB = $(LIBUTF) $(LIBUTIL) BIN =\ more\ hexdump\ - ifconfig + ifconfig\ + ping LIBUTILOBJ = $(LIBUTILSRC:.c=.o) OBJ = $(BIN:=.o) $(LIBUTFOBJ) $(LIBUTILOBJ) @@ -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/ @@ -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 @@ -0,0 +1,211 @@ +/* See LICENSE file for copyright and license details. */ +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <netinet/ip_icmp.h> +#include <netdb.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <signal.h> +#include <time.h> + +#include "util.h" + +static volatile int terminate = 0; + +static void +usage(void) +{ + eprintf("usage: %s <destination>\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; +} |