From 88bcaba5e77be2df9536438d5c672d92a7c42ac9 Mon Sep 17 00:00:00 2001 From: "K. Lange" Date: Fri, 17 Sep 2021 12:39:04 +0900 Subject: [PATCH] ping: shoddy implementation --- apps/ping.c | 152 ++++++++++++++++++++++++++++++++++ base/usr/include/sys/socket.h | 1 + kernel/net/ipv4.c | 84 ++++++++++++++++++- 3 files changed, 234 insertions(+), 3 deletions(-) create mode 100644 apps/ping.c diff --git a/apps/ping.c b/apps/ping.c new file mode 100644 index 00000000..3454fbc3 --- /dev/null +++ b/apps/ping.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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; +} diff --git a/base/usr/include/sys/socket.h b/base/usr/include/sys/socket.h index a527795b..e062ebe3 100644 --- a/base/usr/include/sys/socket.h +++ b/base/usr/include/sys/socket.h @@ -14,6 +14,7 @@ _Begin_C_Header #define SOCK_DGRAM 2 #define SOCK_RAW 3 +#define IPPROTO_ICMP 1 #define IPPROTO_TCP 6 #define IPPROTO_UDP 17 diff --git a/kernel/net/ipv4.c b/kernel/net/ipv4.c index 24c8f89d..a9aca671 100644 --- a/kernel/net/ipv4.c +++ b/kernel/net/ipv4.c @@ -30,6 +30,7 @@ #define DEFAULT_TCP_WINDOW_SIZE 65535 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) { 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; } +static sock_t * icmp_handler = NULL; + 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; + + /* Is this a PING request? */ if (header->type == 8 && header->code == 0) { printf("net: ping with %d bytes of payload\n", ntohs(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... */ net_ipv4_send(response,nic); 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 { 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 * 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 int next_port = 12345; @@ -841,7 +915,11 @@ long net_ipv4_socket(int type, int protocol) { /* Ignore protocol, make socket for 'type' only... */ switch (type) { case SOCK_DGRAM: - return udp_socket(); + if (!protocol || protocol == IPPROTO_UDP) + return udp_socket(); + if (protocol == IPPROTO_ICMP) + return icmp_socket(); + return -EINVAL; case SOCK_STREAM: return tcp_socket(); default: