ping: shoddy implementation

This commit is contained in:
K. Lange 2021-09-17 12:39:04 +09:00
parent 2c07ec50c3
commit 88bcaba5e7
3 changed files with 234 additions and 3 deletions

152
apps/ping.c Normal file
View File

@ -0,0 +1,152 @@
/**
* @brief Send ICMP pings
*
* @copyright
* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE.md
* Copyright (C) 2021 K. Lange
*/
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <getopt.h>
#include <time.h>
#include <termios.h>
#include <unistd.h>
#include <poll.h>
#include <sys/times.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <syscall.h>
#define BYTES_TO_SEND 56
struct IPV4_Header {
uint8_t version_ihl;
uint8_t dscp_ecn;
uint16_t length;
uint16_t ident;
uint16_t flags_fragment;
uint8_t ttl;
uint8_t protocol;
uint16_t checksum;
uint32_t source;
uint32_t destination;
uint8_t payload[];
};
struct ICMP_Header {
uint8_t type, code;
uint16_t checksum;
uint16_t identifier;
uint16_t sequence_number;
uint8_t payload[];
};
static uint16_t icmp_checksum(char * payload, size_t len) {
uint32_t sum = 0;
uint16_t * s = (uint16_t *)payload;
for (size_t i = 0; i < (len) / 2; ++i) {
sum += ntohs(s[i]);
}
if (sum > 0xFFFF) {
sum = (sum >> 16) + (sum & 0xFFFF);
}
return ~(sum & 0xFFFF) & 0xFFFF;
}
int main(int argc, char * argv[]) {
if (argc < 2) return 1;
int pings_to_send = 4;
struct hostent * host = gethostbyname(argv[1]);
if (!host) {
fprintf(stderr, "%s: not found\n", argv[1]);
return 1;
}
char * addr = inet_ntoa(*(struct in_addr*)host->h_addr_list[0]);
int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
if (sock < 0) {
fprintf(stderr, "%s: No socket: %s\n", argv[1], strerror(errno));
return 1;
}
struct sockaddr_in dest;
dest.sin_family = AF_INET;
memcpy(&dest.sin_addr.s_addr, host->h_addr, host->h_length);
printf("PING %s (%s) %d data bytes\n", argv[1], addr, BYTES_TO_SEND);
struct ICMP_Header * ping = malloc(BYTES_TO_SEND);
ping->type = 8; /* request */
ping->code = 0;
ping->identifier = 0;
ping->sequence_number = 0;
/* Fill in data */
for (int i = 0; i < BYTES_TO_SEND - 8; ++i) {
ping->payload[i] = i;
}
int responses_received = 0;
for (int i = 0; i < pings_to_send; ++i) {
ping->sequence_number = htons(i+1);
ping->checksum = 0;
ping->checksum = htons(icmp_checksum((void*)ping, BYTES_TO_SEND));
/* Send it and wait */
clock_t sent_at = times(NULL);
if (sendto(sock, (void*)ping, BYTES_TO_SEND, 0, (struct sockaddr*)&dest, sizeof(struct sockaddr_in)) < 0) {
fprintf(stderr, "sendto: %s\n", strerror(errno));
}
struct pollfd fds[1];
fds[0].fd = sock;
fds[0].events = POLLIN;
int ret = poll(fds,1,1000);
if (ret > 0) {
char data[4096];
ssize_t len = recv(sock, data, 4096, 0);
clock_t rcvd_at = times(NULL);
if (len > 0) {
/* Is it actually a PING response ? */
struct IPV4_Header * ipv4 = (void*)data;
struct ICMP_Header * icmp = (void*)ipv4->payload;
if (icmp->type == 0) {
/* How much data, minus the header? */
size_t len = ntohs(ipv4->length) - sizeof(struct IPV4_Header);
/* Get the address */
char * from = inet_ntoa(*(struct in_addr*)&ipv4->source);
printf("%zd bytes from %s: icmp_seq=%d ttl=%d time=%zd ms\n",
len, from, ntohs(icmp->sequence_number), ipv4->ttl,
(rcvd_at - sent_at) / 1000);
responses_received++;
}
}
}
if (i + 1 != pings_to_send) {
syscall_sleep(1,0);
}
}
printf("%d packets transmitted, %d received, %d%% packet loss\n",
pings_to_send, responses_received, 100*(pings_to_send-responses_received)/pings_to_send);
return 0;
}

View File

@ -14,6 +14,7 @@ _Begin_C_Header
#define SOCK_DGRAM 2 #define SOCK_DGRAM 2
#define SOCK_RAW 3 #define SOCK_RAW 3
#define IPPROTO_ICMP 1
#define IPPROTO_TCP 6 #define IPPROTO_TCP 6
#define IPPROTO_UDP 17 #define IPPROTO_UDP 17

View File

@ -30,6 +30,7 @@
#define DEFAULT_TCP_WINDOW_SIZE 65535 #define DEFAULT_TCP_WINDOW_SIZE 65535
static int _debug __attribute__((unused)) = 0; static int _debug __attribute__((unused)) = 0;
extern fs_node_t * net_if_any(void);
static void ip_ntoa(const uint32_t src_addr, char * out) { static void ip_ntoa(const uint32_t src_addr, char * out) {
snprintf(out, 16, "%d.%d.%d.%d", snprintf(out, 16, "%d.%d.%d.%d",
@ -149,8 +150,12 @@ int net_ipv4_send(struct ipv4_packet * response, fs_node_t * nic) {
return 0; return 0;
} }
static sock_t * icmp_handler = NULL;
static void icmp_handle(struct ipv4_packet * packet, const char * src, const char * dest, fs_node_t * nic) { static void icmp_handle(struct ipv4_packet * packet, const char * src, const char * dest, fs_node_t * nic) {
struct icmp_header * header = (void*)&packet->payload; struct icmp_header * header = (void*)&packet->payload;
/* Is this a PING request? */
if (header->type == 8 && header->code == 0) { if (header->type == 8 && header->code == 0) {
printf("net: ping with %d bytes of payload\n", ntohs(packet->length)); printf("net: ping with %d bytes of payload\n", ntohs(packet->length));
if (ntohs(packet->length) & 1) packet->length++; if (ntohs(packet->length) & 1) packet->length++;
@ -177,11 +182,82 @@ static void icmp_handle(struct ipv4_packet * packet, const char * src, const cha
/* send ipv4... */ /* send ipv4... */
net_ipv4_send(response,nic); net_ipv4_send(response,nic);
free(response); free(response);
} else if (header->type == 0 && header->code == 0) {
printf("net: ping reply\n");
/* Did we have a client waiting for this? */
if (icmp_handler) {
net_sock_add(icmp_handler, packet, ntohs(packet->length));
}
} else { } else {
printf("net: ipv4: %s: %s -> %s ICMP %d (code = %d)\n", nic->name, src, dest, header->type, header->code); printf("net: ipv4: %s: %s -> %s ICMP %d (code = %d)\n", nic->name, src, dest, header->type, header->code);
} }
} }
static void sock_icmp_close(sock_t * sock) {
icmp_handler = NULL;
}
static long sock_icmp_recv(sock_t * sock, struct msghdr * msg, int flags) {
if (msg->msg_iovlen > 1) {
return -ENOTSUP;
}
if (msg->msg_iovlen == 0) return 0;
struct ipv4_packet * data = net_sock_get(sock);
if (!data) {
return -EINTR;
}
long resp = ntohs(data->length);
memcpy(msg->msg_iov[0].iov_base, data, resp);
free(data);
return resp;
}
static long sock_icmp_send(sock_t * sock, const struct msghdr *msg, int flags) {
if (msg->msg_iovlen > 1) {
return -ENOTSUP;
}
if (msg->msg_iovlen == 0) return 0;
if (msg->msg_namelen != sizeof(struct sockaddr_in)) {
return -EINVAL;
}
struct sockaddr_in * name = msg->msg_name;
fs_node_t * nic = net_if_any();
size_t total_length = sizeof(struct ipv4_packet) + msg->msg_iov[0].iov_len;
struct ipv4_packet * response = malloc(total_length);
response->length = htons(total_length);
response->destination = name->sin_addr.s_addr;
response->source = ((struct EthernetDevice*)nic->device)->ipv4_addr;
response->ttl = 64;
response->protocol = 1;
response->ident = 0;
response->flags_fragment = htons(0x4000);
response->version_ihl = 0x45;
response->dscp_ecn = 0;
response->checksum = 0;
response->checksum = htons(calculate_ipv4_checksum(response));
memcpy(response->payload, msg->msg_iov[0].iov_base, msg->msg_iov[0].iov_len);
net_ipv4_send(response,nic);
free(response);
return 0;
}
static int icmp_socket(void) {
printf("icmp socket...\n");
if (icmp_handler) return -EINVAL;
sock_t * sock = net_sock_create();
sock->sock_recv = sock_icmp_recv;
sock->sock_send = sock_icmp_send;
sock->sock_close = sock_icmp_close;
icmp_handler = sock;
return process_append_fd((process_t *)this_core->current_process, (fs_node_t *)sock);
}
static hashmap_t * udp_sockets = NULL; static hashmap_t * udp_sockets = NULL;
static hashmap_t * tcp_sockets = NULL; static hashmap_t * tcp_sockets = NULL;
@ -355,8 +431,6 @@ void net_ipv4_handle(struct ipv4_packet * packet, fs_node_t * nic) {
} }
} }
extern fs_node_t * net_if_any(void);
static spin_lock_t udp_port_lock = {0}; static spin_lock_t udp_port_lock = {0};
static int next_port = 12345; static int next_port = 12345;
@ -841,7 +915,11 @@ long net_ipv4_socket(int type, int protocol) {
/* Ignore protocol, make socket for 'type' only... */ /* Ignore protocol, make socket for 'type' only... */
switch (type) { switch (type) {
case SOCK_DGRAM: case SOCK_DGRAM:
if (!protocol || protocol == IPPROTO_UDP)
return udp_socket(); return udp_socket();
if (protocol == IPPROTO_ICMP)
return icmp_socket();
return -EINVAL;
case SOCK_STREAM: case SOCK_STREAM:
return tcp_socket(); return tcp_socket();
default: default: