ping: shoddy implementation
This commit is contained in:
parent
2c07ec50c3
commit
88bcaba5e7
152
apps/ping.c
Normal file
152
apps/ping.c
Normal 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;
|
||||
}
|
@ -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
|
||||
|
||||
|
@ -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:
|
||||
|
Loading…
Reference in New Issue
Block a user