net: allow multiple icmp sockets; autocalculate checksum; support IP_RECVTTL?
This commit is contained in:
parent
55c0a2ab0d
commit
df74cb6b55
33
apps/ping.c
33
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);
|
||||
|
@ -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)));
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user