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;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int yes = 1;
|
||||||
|
setsockopt(sock, IPPROTO_IP, IP_RECVTTL, &yes, sizeof(int));
|
||||||
|
|
||||||
signal(SIGINT, sig_break_loop);
|
signal(SIGINT, sig_break_loop);
|
||||||
|
|
||||||
struct sockaddr_in dest;
|
struct sockaddr_in dest;
|
||||||
@ -118,14 +121,40 @@ int main(int argc, char * argv[]) {
|
|||||||
|
|
||||||
if (ret > 0) {
|
if (ret > 0) {
|
||||||
char data[4096];
|
char data[4096];
|
||||||
|
char control[4096];
|
||||||
struct sockaddr_in source;
|
struct sockaddr_in source;
|
||||||
socklen_t source_size = sizeof(struct sockaddr_in);
|
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();
|
unsigned long rcvd_at = clocktime();
|
||||||
if (len > 0) {
|
if (len > 0) {
|
||||||
/* Is it actually a PING response ? */
|
/* Is it actually a PING response ? */
|
||||||
|
|
||||||
struct ICMP_Header * icmp = (void*)data;
|
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) {
|
if (icmp->type == 0) {
|
||||||
/* How much data, minus the header? */
|
/* How much data, minus the header? */
|
||||||
@ -133,7 +162,7 @@ int main(int argc, char * argv[]) {
|
|||||||
char * from = inet_ntoa(source.sin_addr);
|
char * from = inet_ntoa(source.sin_addr);
|
||||||
int time_taken = (rcvd_at - sent_at);
|
int time_taken = (rcvd_at - sent_at);
|
||||||
printf("%zd bytes from %s: icmp_seq=%d ttl=%d time=%d",
|
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);
|
time_taken / 1000);
|
||||||
if (time_taken < 1000) {
|
if (time_taken < 1000) {
|
||||||
printf(".%03d", time_taken % 1000);
|
printf(".%03d", time_taken % 1000);
|
||||||
|
@ -19,7 +19,7 @@ struct icmp_header {
|
|||||||
uint8_t type;
|
uint8_t type;
|
||||||
uint8_t code;
|
uint8_t code;
|
||||||
uint16_t csum;
|
uint16_t csum;
|
||||||
uint16_t rest_of_header;
|
uint16_t identifier;
|
||||||
uint8_t data[];
|
uint8_t data[];
|
||||||
} __attribute__((packed)) __attribute__((aligned(2)));
|
} __attribute__((packed)) __attribute__((aligned(2)));
|
||||||
|
|
||||||
|
@ -21,4 +21,7 @@ struct sockaddr_in {
|
|||||||
in_addr_t inet_addr(const char *cp);
|
in_addr_t inet_addr(const char *cp);
|
||||||
char *inet_ntoa(struct in_addr in);
|
char *inet_ntoa(struct in_addr in);
|
||||||
|
|
||||||
|
#define IP_TTL 2
|
||||||
|
#define IP_RECVTTL 12
|
||||||
|
|
||||||
_End_C_Header
|
_End_C_Header
|
||||||
|
@ -21,7 +21,7 @@ _Begin_C_Header
|
|||||||
#define IPPROTO_TCP 6
|
#define IPPROTO_TCP 6
|
||||||
#define IPPROTO_UDP 17
|
#define IPPROTO_UDP 17
|
||||||
|
|
||||||
#define SOL_SOCKET 0
|
#define SOL_SOCKET 1
|
||||||
|
|
||||||
#define SO_KEEPALIVE 1
|
#define SO_KEEPALIVE 1
|
||||||
#define SO_REUSEADDR 2
|
#define SO_REUSEADDR 2
|
||||||
@ -65,6 +65,15 @@ struct sockaddr_storage {
|
|||||||
char _ss_pad[128];
|
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_
|
#ifndef _KERNEL_
|
||||||
extern ssize_t recv(int sockfd, void *buf, size_t len, int flags);
|
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);
|
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;
|
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) {
|
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! */
|
/* TODO: This should be routing, with a _hint_ about the interface, not the actual nic to send from! */
|
||||||
struct EthernetDevice * enic = nic->device;
|
struct EthernetDevice * enic = nic->device;
|
||||||
@ -152,8 +162,6 @@ int net_ipv4_send(struct ipv4_packet * response, fs_node_t * nic) {
|
|||||||
return 0;
|
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) {
|
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;
|
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);
|
net_ipv4_send(response,nic);
|
||||||
free(response);
|
free(response);
|
||||||
} else if (header->type == 0 && header->code == 0) {
|
} else if (header->type == 0 && header->code == 0) {
|
||||||
printf("net: ping reply\n");
|
|
||||||
/* Did we have a client waiting for this? */
|
/* Did we have a client waiting for this? */
|
||||||
if (icmp_handler) {
|
sock_t * handler = hashmap_get(icmp_sockets, (void*)(uintptr_t)ntohs(header->identifier));
|
||||||
net_sock_add(icmp_handler, packet, ntohs(packet->length));
|
if (handler) {
|
||||||
|
net_sock_add(handler, packet, ntohs(packet->length));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
printf("net: ipv4: %s: %s -> %s ICMP %d (code = %d)\n", nic->name, src, dest, header->type, header->code);
|
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) {
|
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) {
|
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_family = AF_INET;
|
||||||
((struct sockaddr_in*)msg->msg_name)->sin_port = 0;
|
((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_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);
|
memcpy(msg->msg_iov[0].iov_base, src->payload, packet_size);
|
||||||
free(packet);
|
free(packet);
|
||||||
return packet_size;
|
return packet_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
static long sock_icmp_send(sock_t * sock, const struct msghdr *msg, int flags) {
|
static long sock_icmp_send(sock_t * sock, const struct msghdr *msg, int flags) {
|
||||||
if (msg->msg_iovlen > 1) {
|
if (msg->msg_iovlen > 1) return -ENOTSUP;
|
||||||
return -ENOTSUP;
|
|
||||||
}
|
|
||||||
if (msg->msg_iovlen == 0) return 0;
|
if (msg->msg_iovlen == 0) return 0;
|
||||||
if (msg->msg_namelen != sizeof(struct sockaddr_in)) {
|
if (msg->msg_namelen != sizeof(struct sockaddr_in)) return -EINVAL;
|
||||||
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;
|
struct sockaddr_in * name = msg->msg_name;
|
||||||
fs_node_t * nic = net_if_route(name->sin_addr.s_addr);
|
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));
|
response->checksum = htons(calculate_ipv4_checksum(response));
|
||||||
|
|
||||||
memcpy(response->payload, msg->msg_iov[0].iov_base, msg->msg_iov[0].iov_len);
|
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);
|
net_ipv4_send(response,nic);
|
||||||
free(response);
|
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) {
|
static int icmp_socket(void) {
|
||||||
printf("icmp socket...\n");
|
if (hashmap_has(icmp_sockets, (void*)(uintptr_t)this_core->current_process->id)) return -EINVAL;
|
||||||
if (icmp_handler) return -EINVAL;
|
|
||||||
sock_t * sock = net_sock_create();
|
sock_t * sock = net_sock_create();
|
||||||
sock->sock_recv = sock_icmp_recv;
|
sock->sock_recv = sock_icmp_recv;
|
||||||
sock->sock_send = sock_icmp_send;
|
sock->sock_send = sock_icmp_send;
|
||||||
sock->sock_close = sock_icmp_close;
|
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);
|
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_FIN (1 << 0)
|
||||||
#define TCP_FLAGS_SYN (1 << 1)
|
#define TCP_FLAGS_SYN (1 << 1)
|
||||||
#define TCP_FLAGS_RST (1 << 2)
|
#define TCP_FLAGS_RST (1 << 2)
|
||||||
@ -1034,3 +1051,15 @@ long net_ipv4_socket(int type, int protocol) {
|
|||||||
return -EINVAL;
|
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) {
|
static inline int is_socket(int sockfd) {
|
||||||
if (!FD_CHECK(sockfd)) return -EBADF;
|
if (!FD_CHECK(sockfd)) return -EBADF;
|
||||||
fs_node_t * node = FD_ENTRY(sockfd);
|
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) {
|
switch (level) {
|
||||||
case SOL_SOCKET:
|
case SOL_SOCKET:
|
||||||
return net_so_socket(node,optname,optval,optlen);
|
return net_so_socket(node,optname,optval,optlen);
|
||||||
|
case IPPROTO_IP:
|
||||||
|
return net_so_ipv4_socket(node,optname,optval,optlen);
|
||||||
default:
|
default:
|
||||||
return -ENOPROTOOPT;
|
return -ENOPROTOOPT;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user