net: allow multiple icmp sockets; autocalculate checksum; support IP_RECVTTL?

This commit is contained in:
K. Lange 2024-02-09 14:54:07 +09:00
parent 55c0a2ab0d
commit df74cb6b55
6 changed files with 102 additions and 28 deletions

View File

@ -78,6 +78,9 @@ int main(int argc, char * argv[]) {
return 1;
}
int yes = 1;
setsockopt(sock, IPPROTO_IP, IP_RECVTTL, &yes, sizeof(int));
signal(SIGINT, sig_break_loop);
struct sockaddr_in dest;
@ -118,14 +121,40 @@ int main(int argc, char * argv[]) {
if (ret > 0) {
char data[4096];
char control[4096];
struct sockaddr_in source;
socklen_t source_size = sizeof(struct sockaddr_in);
ssize_t len = recvfrom(sock, data, 4096, 0, (struct sockaddr*)&source, &source_size);
struct iovec _iovec = {
data, 4096
};
struct msghdr msg = {
&source,
source_size,
&_iovec,
1,
control,
4096,
0
};
ssize_t len = recvmsg(sock, &msg, 0);
unsigned long rcvd_at = clocktime();
if (len > 0) {
/* Is it actually a PING response ? */
struct ICMP_Header * icmp = (void*)data;
unsigned char ttl = 0;
if (msg.msg_controllen) {
char * control_msg = control;
while (control_msg - control + sizeof(struct cmsghdr) <= msg.msg_controllen) {
struct cmsghdr * cmsg = (void*)control_msg;
if (cmsg->cmsg_level == IPPROTO_IP && (cmsg->cmsg_type == IP_RECVTTL || cmsg->cmsg_type == IP_TTL)) {
memcpy(&ttl, CMSG_DATA(cmsg), 1);
break;
}
control_msg += cmsg->cmsg_len;
}
}
if (icmp->type == 0) {
/* How much data, minus the header? */
@ -133,7 +162,7 @@ int main(int argc, char * argv[]) {
char * from = inet_ntoa(source.sin_addr);
int time_taken = (rcvd_at - sent_at);
printf("%zd bytes from %s: icmp_seq=%d ttl=%d time=%d",
len, from, ntohs(icmp->sequence_number), (unsigned char)source.sin_zero[0], /* we hide the ttl in here */
len, from, ntohs(icmp->sequence_number), (unsigned char)ttl,
time_taken / 1000);
if (time_taken < 1000) {
printf(".%03d", time_taken % 1000);

View File

@ -19,7 +19,7 @@ struct icmp_header {
uint8_t type;
uint8_t code;
uint16_t csum;
uint16_t rest_of_header;
uint16_t identifier;
uint8_t data[];
} __attribute__((packed)) __attribute__((aligned(2)));

View File

@ -21,4 +21,7 @@ struct sockaddr_in {
in_addr_t inet_addr(const char *cp);
char *inet_ntoa(struct in_addr in);
#define IP_TTL 2
#define IP_RECVTTL 12
_End_C_Header

View File

@ -21,7 +21,7 @@ _Begin_C_Header
#define IPPROTO_TCP 6
#define IPPROTO_UDP 17
#define SOL_SOCKET 0
#define SOL_SOCKET 1
#define SO_KEEPALIVE 1
#define SO_REUSEADDR 2
@ -65,6 +65,15 @@ struct sockaddr_storage {
char _ss_pad[128];
};
struct cmsghdr {
socklen_t cmsg_len;
int cmsg_level;
int cmsg_type;
unsigned char cmsg_data[];
};
#define CMSG_DATA(cmsg) (&((struct cmsghdr*)(cmsg))->cmsg_data)
#ifndef _KERNEL_
extern ssize_t recv(int sockfd, void *buf, size_t len, int flags);
extern ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);

View File

@ -117,6 +117,16 @@ uint16_t calculate_tcp_checksum(struct tcp_check_header * p, struct tcp_header *
return ~(sum & 0xFFFF) & 0xFFFF;
}
static hashmap_t * udp_sockets = NULL;
static hashmap_t * tcp_sockets = NULL;
static hashmap_t * icmp_sockets = NULL;
void ipv4_install(void) {
udp_sockets = hashmap_create_int(10);
tcp_sockets = hashmap_create_int(10);
icmp_sockets = hashmap_create_int(10);
}
int net_ipv4_send(struct ipv4_packet * response, fs_node_t * nic) {
/* TODO: This should be routing, with a _hint_ about the interface, not the actual nic to send from! */
struct EthernetDevice * enic = nic->device;
@ -152,8 +162,6 @@ 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;
@ -187,10 +195,10 @@ static void icmp_handle(struct ipv4_packet * packet, const char * src, const cha
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));
sock_t * handler = hashmap_get(icmp_sockets, (void*)(uintptr_t)ntohs(header->identifier));
if (handler) {
net_sock_add(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);
@ -198,7 +206,7 @@ static void icmp_handle(struct ipv4_packet * packet, const char * src, const cha
}
static void sock_icmp_close(sock_t * sock) {
icmp_handler = NULL;
hashmap_remove(icmp_sockets, (void*)(uintptr_t)sock->priv32[0]);
}
static long sock_icmp_recv(sock_t * sock, struct msghdr * msg, int flags) {
@ -225,23 +233,35 @@ static long sock_icmp_recv(sock_t * sock, struct msghdr * msg, int flags) {
((struct sockaddr_in*)msg->msg_name)->sin_family = AF_INET;
((struct sockaddr_in*)msg->msg_name)->sin_port = 0;
((struct sockaddr_in*)msg->msg_name)->sin_addr.s_addr = src->source;
((struct sockaddr_in*)msg->msg_name)->sin_zero[0] = src->ttl;
}
}
/* TODO should check flag IP_RECVTTL ? Also this should be common to all IPPROTO_IP sockets */
if (sock->priv32[2] && msg->msg_controllen > sizeof(struct cmsghdr) + 1) {
struct cmsghdr * out = msg->msg_control;
out->cmsg_len = sizeof(struct cmsghdr) + 1;
out->cmsg_level = IPPROTO_IP;
out->cmsg_type = IP_RECVTTL;
out->cmsg_data[0] = src->ttl;
msg->msg_controllen = sizeof(struct cmsghdr) + 1;
} else {
msg->msg_controllen = 0;
}
memcpy(msg->msg_iov[0].iov_base, src->payload, packet_size);
free(packet);
return packet_size;
}
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 > 1) return -ENOTSUP;
if (msg->msg_iovlen == 0) return 0;
if (msg->msg_namelen != sizeof(struct sockaddr_in)) {
return -EINVAL;
}
if (msg->msg_namelen != sizeof(struct sockaddr_in)) return -EINVAL;
if (msg->msg_iov[0].iov_len < sizeof(struct icmp_header)) return -EINVAL;
struct icmp_header * icmp = msg->msg_iov[0].iov_base;
if (icmp->type != 8 || icmp->code != 0) return -EINVAL;
if (icmp->identifier != 0) return -EINVAL;
struct sockaddr_in * name = msg->msg_name;
fs_node_t * nic = net_if_route(name->sin_addr.s_addr);
@ -262,6 +282,11 @@ static long sock_icmp_send(sock_t * sock, const struct msghdr *msg, int flags) {
response->checksum = htons(calculate_ipv4_checksum(response));
memcpy(response->payload, msg->msg_iov[0].iov_base, msg->msg_iov[0].iov_len);
struct icmp_header * micmp = (struct icmp_header*)response->payload;
micmp->identifier = htons(sock->priv32[0]);
micmp->csum = 0;
micmp->csum = htons(icmp_checksum(response));
net_ipv4_send(response,nic);
free(response);
@ -269,24 +294,16 @@ static long sock_icmp_send(sock_t * sock, const struct msghdr *msg, int flags) {
}
static int icmp_socket(void) {
printf("icmp socket...\n");
if (icmp_handler) return -EINVAL;
if (hashmap_has(icmp_sockets, (void*)(uintptr_t)this_core->current_process->id)) 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;
sock->priv32[0] = this_core->current_process->id;
hashmap_set(icmp_sockets, (void*)(uintptr_t)sock->priv32[0], 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;
void ipv4_install(void) {
udp_sockets = hashmap_create_int(10);
tcp_sockets = hashmap_create_int(10);
}
#define TCP_FLAGS_FIN (1 << 0)
#define TCP_FLAGS_SYN (1 << 1)
#define TCP_FLAGS_RST (1 << 2)
@ -1034,3 +1051,15 @@ long net_ipv4_socket(int type, int protocol) {
return -EINVAL;
}
}
long net_so_ipv4_socket(struct SockData * sock, int optname, const void *optval, socklen_t optlen) {
switch (optname) {
case IP_RECVTTL:
if (optlen != sizeof(int)) return -EINVAL;
/* TODO ugh bad */
sock->priv32[2] = *(int*)optval;
return 0;
default:
return -ENOPROTOOPT;
}
}

View File

@ -215,6 +215,8 @@ long net_so_socket(struct SockData * sock, int optname, const void *optval, sock
}
}
extern long net_so_ipv4_socket(struct SockData * sock, int optname, const void *optval, socklen_t optlen);
static inline int is_socket(int sockfd) {
if (!FD_CHECK(sockfd)) return -EBADF;
fs_node_t * node = FD_ENTRY(sockfd);
@ -241,6 +243,8 @@ long net_setsockopt(int sockfd, int level, int optname, const void *optval, sock
switch (level) {
case SOL_SOCKET:
return net_so_socket(node,optname,optval,optlen);
case IPPROTO_IP:
return net_so_ipv4_socket(node,optname,optval,optlen);
default:
return -ENOPROTOOPT;
}