summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Baumann <mail@andreasbaumann.cc>2023-04-29 09:27:08 +0200
committerAndreas Baumann <mail@andreasbaumann.cc>2023-04-29 09:27:08 +0200
commit86c5a55a4f1722452e26a05ec15121c1f93d177b (patch)
treeb46f643ec1b1381c6fad92d2e9103b0f7de1db4c
parent296512eaab30ec5bcba041d4f3db52b12e10fe2d (diff)
downloadabase-86c5a55a4f1722452e26a05ec15121c1f93d177b.tar.gz
abase-86c5a55a4f1722452e26a05ec15121c1f93d177b.tar.bz2
finished a first ping version (which still requires root)
-rw-r--r--Makefile3
-rw-r--r--README11
-rw-r--r--ping.112
-rw-r--r--ping.c211
4 files changed, 235 insertions, 2 deletions
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 <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;
+}