From df74cb6b55c6d56e52c40c3a0a78fc43a47b3bb6 Mon Sep 17 00:00:00 2001 From: "K. Lange" Date: Fri, 9 Feb 2024 14:54:07 +0900 Subject: [PATCH] net: allow multiple icmp sockets; autocalculate checksum; support IP_RECVTTL? --- apps/ping.c | 33 ++++++++++++- base/usr/include/kernel/net/ipv4.h | 2 +- base/usr/include/netinet/in.h | 3 ++ base/usr/include/sys/socket.h | 11 ++++- kernel/net/ipv4.c | 77 ++++++++++++++++++++---------- kernel/net/socket.c | 4 ++ 6 files changed, 102 insertions(+), 28 deletions(-) diff --git a/apps/ping.c b/apps/ping.c index cff924fe..31feac2f 100644 --- a/apps/ping.c +++ b/apps/ping.c @@ -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); diff --git a/base/usr/include/kernel/net/ipv4.h b/base/usr/include/kernel/net/ipv4.h index ca600601..fb1c09f7 100644 --- a/base/usr/include/kernel/net/ipv4.h +++ b/base/usr/include/kernel/net/ipv4.h @@ -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))); diff --git a/base/usr/include/netinet/in.h b/base/usr/include/netinet/in.h index 8d0f3b63..7efef140 100644 --- a/base/usr/include/netinet/in.h +++ b/base/usr/include/netinet/in.h @@ -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 diff --git a/base/usr/include/sys/socket.h b/base/usr/include/sys/socket.h index 9386825b..0e052424 100644 --- a/base/usr/include/sys/socket.h +++ b/base/usr/include/sys/socket.h @@ -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); diff --git a/kernel/net/ipv4.c b/kernel/net/ipv4.c index 6870af15..c6543444 100644 --- a/kernel/net/ipv4.c +++ b/kernel/net/ipv4.c @@ -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; + } +} diff --git a/kernel/net/socket.c b/kernel/net/socket.c index fe55d3e8..0db20472 100644 --- a/kernel/net/socket.c +++ b/kernel/net/socket.c @@ -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; }