diff options
author | Tom Smeding <tom.smeding@gmail.com> | 2018-07-23 21:44:07 +0200 |
---|---|---|
committer | Tom Smeding <tom.smeding@gmail.com> | 2018-07-25 18:07:49 +0200 |
commit | 9f2fccfdc2eae83efbde1e3ae94a2cc220537983 (patch) | |
tree | 16a19a1877d7cfdd39767de0cfaf312adc398d1b /icmp.c | |
parent | b582cce69853d0a562dd0171914426887e854966 (diff) |
Better ICMP code encapsulation
Diffstat (limited to 'icmp.c')
-rw-r--r-- | icmp.c | 119 |
1 files changed, 96 insertions, 23 deletions
@@ -1,24 +1,28 @@ #include <stdlib.h> +#include <stdbool.h> #include <stddef.h> #include <string.h> #include <assert.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> +#include <netinet/ip.h> #include <netinet/ip_icmp.h> #include <arpa/inet.h> -#include "icmp.h" +#include "icmp_client.h" +#include "icmp_server.h" #include "util.h" -#define PROT_ICMP 1 - - -static void make_sockaddr(struct sockaddr_in *dst, const char *ip_address) { +static void make_sockaddr_u32(struct sockaddr_in *dst, uint32_t addr) { memset(dst, 0, sizeof *dst); dst->sin_family = AF_INET; dst->sin_port = htons(0); - dst->sin_addr.s_addr = inet_addr(ip_address); + dst->sin_addr.s_addr = addr; +} + +static void make_sockaddr(struct sockaddr_in *dst, const char *ip_address) { + make_sockaddr_u32(dst, inet_addr(ip_address)); } static uint16_t compute_checksum(const void *buf_, size_t buflen) { @@ -39,18 +43,27 @@ static uint16_t compute_checksum(const void *buf_, size_t buflen) { return res; } -int icmp_open_socket(void) { - return socket(PF_INET, SOCK_DGRAM, PROT_ICMP); +int icmp_client_open_socket(void) { + return socket(PF_INET, SOCK_DGRAM, IPPROTO_ICMP); } -struct icmp_reply icmp_communicate(int sock, const char *ip_address, int seqnum, const void *data_, size_t length) { +int icmp_server_open_socket(void) { + return socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); +} + +struct icmp_client_incoming icmp_client_communicate( + int sock, const char *ip_address, + int id, int seqnum, + const void *data_, size_t length) { + static uint8_t buffer[ICMP_MAX_PAYLOAD_LENGTH]; const uint8_t *data = (const uint8_t*)data_; assert(length <= ICMP_MAX_PAYLOAD_LENGTH); - struct icmp_reply errret = (struct icmp_reply){.data = NULL, .length = 0, .seqnum = 0}; + struct icmp_client_incoming errret = + (struct icmp_client_incoming){.data = NULL, .length = 0, .id = 0, .seqnum = 0}; struct sockaddr_in addr; make_sockaddr(&addr, ip_address); @@ -59,7 +72,8 @@ struct icmp_reply icmp_communicate(int sock, const char *ip_address, int seqnum, memset(&msg, 0, sizeof msg); msg.type = ICMP_ECHO; msg.code = 0; - msg.seqnum = seqnum; + msg.id = htons(id); + msg.seqnum = htons(seqnum); memcpy(msg.payload, data, length); @@ -90,25 +104,90 @@ struct icmp_reply icmp_communicate(int sock, const char *ip_address, int seqnum, memcpy(buffer, msg.payload, payloadlen); - return (struct icmp_reply){ + return (struct icmp_client_incoming){ .data = buffer, .length = payloadlen, - .seqnum = msg.seqnum + .id = ntohs(msg.id), + .seqnum = ntohs(msg.seqnum) }; } -int icmp_send_echo_reply(const char *ip_address, int id, int seqnum, const void *data_, size_t length) { +struct icmp_server_incoming icmp_server_receive(int sock) { + static uint8_t buffer[ICMP_MAX_PAYLOAD_LENGTH]; + + struct icmp_server_incoming errret = + (struct icmp_server_incoming){.data = NULL, .length = 0, .id = 0, .seqnum = 0, .source_addr = 0}; + + char buf[MAX_IP_PACKET_SIZE]; + + struct iovec iov1; + memset(&iov1, 0, sizeof iov1); + iov1.iov_base = buf; + iov1.iov_len = sizeof buf; + + struct msghdr recv_msghdr; + memset(&recv_msghdr, 0, sizeof recv_msghdr); + recv_msghdr.msg_name = NULL; + recv_msghdr.msg_iov = &iov1; + recv_msghdr.msg_iovlen = 1; + + // TODO: check whether the above stuff needs re-initialisation + while (true) { + recv_msghdr.msg_flags = 0; + + ssize_t nr = recvmsg(sock, &recv_msghdr, 0); + if (nr < 0) return errret; + + // buf now contains received data starting at the IP header + + // printf("Full packet received:\n"); + // xxd(buf, nr); + + struct iphdr *hdr = (struct iphdr*)buf; + int hdr_len = hdr->ihl * 4; + uint32_t saddr = hdr->saddr; + + struct icmp_echo *msg = (struct icmp_echo*)(buf + hdr_len); + int msg_len = nr - hdr_len; + int payloadlen = msg_len - offsetof(struct icmp_echo, payload); + // printf("Received: type %u code %u id %hu seqnum %hu payload:\n", + // (unsigned)msg->type, (unsigned)msg->code, msg->id, msg->seqnum); + // xxd(msg->payload, payloadlen); + + if (msg->type != ICMP_ECHO && msg->type != ICMP_ECHOREPLY) { + // printf("Not an ICMP_ECHO or ICMP_ECHOREPLY, ignoring\n"); + continue; + } else { + memcpy(buffer, msg->payload, payloadlen); + + return (struct icmp_server_incoming){ + .data = buffer, + .length = payloadlen, + .type = msg->type, + .id = ntohs(msg->id), + .seqnum = ntohs(msg->seqnum), + .source_addr = saddr + }; + } + } +} + +int icmp_server_send_reply( + int sock, uint32_t addr_u32, + int id, int seqnum, + const void *data_, size_t length) { + const uint8_t *data = (const uint8_t*)data_; struct sockaddr_in addr; - make_sockaddr(&addr, ip_address); + make_sockaddr_u32(&addr, addr_u32); struct icmp_echo msg; memset(&msg, 0, sizeof msg); msg.type = ICMP_ECHOREPLY; msg.code = 0; - msg.id = id; - msg.seqnum = seqnum; + msg.id = htons(id); + msg.seqnum = htons(seqnum); msg.checksum = 0; memcpy(msg.payload, data, length); @@ -117,13 +196,7 @@ int icmp_send_echo_reply(const char *ip_address, int id, int seqnum, const void msg.checksum = ~compute_checksum(&msg, total_length); - int sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); - // The below is only necessary for sending on IPPROTO_RAW sockets - // int zero = 0; - // assert(setsockopt(sock, IPPROTO_IP, IP_HDRINCL, &zero, sizeof zero) >= 0); - int ret = sendto(sock, &msg, ICMP_PAYLOAD_OFFSET + length, 0, (struct sockaddr*)&addr, sizeof addr); - close(sock); if (ret < 0) return -1; return 0; |