From 4dbbcf7fc1fffda05095e9aa4fa95c1ea7983900 Mon Sep 17 00:00:00 2001 From: xHire Date: Sat, 21 Oct 2017 10:17:32 +0200 Subject: [PATCH] Propagation of packet/payload size More sanity checks Fixed expiration of TTL in IPv4 packets Optimisations Fixed subtle mistakes --- src/arp.c | 15 ++++++++--- src/arp.h | 4 +-- src/icmp.c | 67 ++++++++++++++++++++++++++++-------------------- src/ipv4.c | 26 +++++++++++++------ src/ipv4.h | 4 +-- src/ipv6.c | 17 ++++++++++--- src/ipv6.h | 4 +-- src/tcp.c | 70 +++++++++++++++++++++++++++++++-------------------- src/tcp.h | 5 ++-- src/udp.c | 29 +++++++++++++++------ src/udp.h | 5 ++-- src/wrapper.c | 56 ++++++++++++++++++++++++++++++----------- 12 files changed, 201 insertions(+), 101 deletions(-) diff --git a/src/arp.c b/src/arp.c index dc00a04..7087e22 100644 --- a/src/arp.c +++ b/src/arp.c @@ -25,18 +25,19 @@ #include "transmitter.h" #include "wrapper.h" -#define ARP_PACKET_SIZE sizeof(struct s_ethernet) + sizeof(struct s_arp) +#define ARP_PACKET_SIZE sizeof(struct s_ethernet) + sizeof(struct s_arp) /** * Process ARP packets and reply to them. * - * @param ethq Ethernet header of the packet - * @param payload Data of the packet + * @param ethq Ethernet header of the packet + * @param payload Data of the packet + * @param payload_size Size of the data payload * * @return 0 for success * @return 1 for failure */ -int arp(struct s_ethernet *ethq, char *payload) +int arp(struct s_ethernet *ethq, char *payload, unsigned short payload_size) { struct s_arp *arpq, *arpr; /* request and reply */ struct s_ethernet *ethr; @@ -50,6 +51,12 @@ int arp(struct s_ethernet *ethq, char *payload) return 1; } + /* sanity check (it's OK to do it here) */ + if (payload_size < sizeof(struct s_arp)) { + log_debug("Too short ARP packet"); + return 1; + } + /* test if this packet belongs to us */ if (memcmp(&wrapsix_ipv4_addr, &arpq->ip_dest, 4)) { log_debug("This is unfamiliar ARP packet"); diff --git a/src/arp.h b/src/arp.h index 3436497..9db354f 100644 --- a/src/arp.h +++ b/src/arp.h @@ -1,6 +1,6 @@ /* * WrapSix - * Copyright (C) 2008-2013 xHire + * Copyright (C) 2008-2017 xHire * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -46,6 +46,6 @@ struct s_arp { struct s_ipv4_addr ip_dest; /* 32 b; target protocol addr */ } __attribute__ ((__packed__)); -int arp(struct s_ethernet *ethq, char *payload); +int arp(struct s_ethernet *ethq, char *payload, unsigned short payload_size); #endif /* ARP_H */ diff --git a/src/icmp.c b/src/icmp.c index 1a9f67d..c6b4ac2 100644 --- a/src/icmp.c +++ b/src/icmp.c @@ -47,14 +47,13 @@ int sub_icmp6_error(struct s_ethernet *eth6, * @param eth4 Ethernet header * @param ip4 IPv4 header * @param payload ICMPv4 data - * @param payload_size Size of payload; needed because IPv4 header has - * dynamic length + * @param payload_size Size of the data payload * * @return 0 for success * @return 1 for failure */ -int icmp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, - char *payload, unsigned short payload_size) +int icmp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, char *payload, + unsigned short payload_size) { struct s_icmp *icmp; unsigned int *icmp_extra; @@ -86,7 +85,7 @@ int icmp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, /* ICMP checksum recheck */ orig_checksum = icmp->checksum; - icmp->checksum = 0; + icmp->checksum = 0x0; icmp->checksum = checksum((char *) icmp, payload_size); if (icmp->checksum != orig_checksum) { @@ -114,7 +113,7 @@ int icmp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, echo->id = connection->ipv6_port_src; - /* override information in original ICMP header */ + /* override information in the original ICMP header */ icmp->type = ICMPV6_ECHO_REPLY; /* copy the payload data */ @@ -164,18 +163,25 @@ int icmp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, } else if (icmp->code == 4) { icmp->type = ICMPV6_PKT_TOO_BIG; icmp->code = 0; + /* here to write new 4B MTU value */ icmp_extra = (unsigned int *) ((char *) icmp + sizeof(struct s_icmp)); + /* from here read original 2B MTU value + * after skipping 2 unused bytes */ icmp_extra_s = (unsigned short *) ((char *) icmp + sizeof(struct s_icmp) + 2); - if (ntohs(*icmp_extra_s) < 68) { + /* router supports path MTU discovery */ + if (ntohs(*icmp_extra_s) >= 68) { *icmp_extra = htonl( - *icmp_extra_s + 20 < - mtu ? (unsigned int) - *icmp_extra_s + 20 : + ntohs(*icmp_extra_s) + + 20 < mtu ? + (unsigned int) + (ntohs(*icmp_extra_s) + + 20) : (unsigned int) mtu); + /* does not => leverage packet length */ } else { /* RFC1191 */ /* NOTE: >= would cause infinite @@ -186,17 +192,19 @@ int icmp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, if (ntohs(eip4->len) > 1006) { *icmp_extra = - htonl(1006); + htonl(1006 + + 20); } else if (ntohs(eip4->len) > 508) { *icmp_extra = - htonl(508); + htonl(508 + 20); } else if (ntohs(eip4->len) > 296) { *icmp_extra = - htonl(296); + htonl(296 + 20); } else { - *icmp_extra = htonl(68); + *icmp_extra = + htonl(68 + 20); } } } else if (icmp->code == 3) { @@ -391,6 +399,8 @@ int icmp_ipv6(struct s_ethernet *eth6, struct s_ipv6 *ip6, char *payload) /* decide the type of the ICMP packet */ switch (icmp->type) { case ICMPV6_ECHO_REQUEST: + /* this option is already sanitized */ + echo = (struct s_icmp_echo *) icmp_data; connection = nat_out(nat6_icmp, nat4_icmp, eth6->src, @@ -437,7 +447,7 @@ int icmp_ipv6(struct s_ethernet *eth6, struct s_ipv6 *ip6, char *payload) /* sanity check */ if (payload_size < sizeof(struct s_icmp) + sizeof(struct s_icmp_ndp_ns)) { - log_debug("Too short NDP NS"); + log_debug("Too short packet for NDP NS"); return 1; } @@ -561,7 +571,8 @@ int icmp_ipv6(struct s_ethernet *eth6, struct s_ipv6 *ip6, char *payload) /* sanity check */ if (payload_size < sizeof(struct s_icmp) + 4 + sizeof(struct s_ipv6)) { - log_debug("Too short ICMPv6 packet 6"); + log_debug("Too short ICMPv6 packet" + "PacketTooBig"); return 1; } @@ -897,13 +908,13 @@ int sub_icmp4_error(char *payload, unsigned short payload_size, return 1; } payload_size -= eip4_hlen; - payload += eip4_hlen; /* define new inner IPv6 header */ /* new_len+4+4+40=102 < 1280 */ eip6 = (struct s_ipv6 *) (packet + *packet_len + sizeof(struct s_icmp) + 4); + /* we'll need this right now */ ipv4_to_ipv6(&eip4->ip_dest, &eip6->ip_dest); @@ -955,7 +966,8 @@ int sub_icmp4_error(char *payload, unsigned short payload_size, /* else: */ /* sanity check */ - if (payload_size < 8) { + if (payload_size < sizeof(struct s_icmp) + + sizeof(struct s_icmp_echo)) { log_debug("Too short ICMPv4 packet 4"); return 1; } @@ -986,7 +998,7 @@ int sub_icmp4_error(char *payload, unsigned short payload_size, } /* copy ICMP header to new packet */ - memcpy(packet + *packet_len, *icmp, sizeof(struct s_icmp) + 4); + memcpy(packet + *packet_len, (char *) *icmp, sizeof(struct s_icmp) + 4); *icmp = (struct s_icmp *) (packet + *packet_len); /* complete inner IPv6 header */ @@ -1074,7 +1086,7 @@ int sub_icmp6_error(struct s_ethernet *eth6, unsigned char skip_l4 = 0; - /* sanity check */ + /* sanity check; redundant only for rare 'Packet too big' */ if (payload_size < sizeof(struct s_ipv6)) { log_debug("Too short ICMPv6 packet 2"); return 1; @@ -1103,6 +1115,7 @@ int sub_icmp6_error(struct s_ethernet *eth6, eip4->id = htons(ntohl(eip6_frag->id)); if (ntohs(eip6_frag->offset_flag) & 0xfff8) { + /* this is not the first fragment */ skip_l4 = 1; } if (ntohs(eip6_frag->offset_flag) & IPV6_FLAG_MORE_FRAGMENTS) { @@ -1118,6 +1131,12 @@ int sub_icmp6_error(struct s_ethernet *eth6, payload += sizeof(struct s_ipv6_fragment); payload_size -= sizeof(struct s_ipv6_fragment); } else { + eip4->id = 0x0; + eip4->flags_offset = htons(IPV4_FLAG_DONT_FRAGMENT); + } + + /* look for the original connection */ + if (skip_l4 == 0) { /* sanity check */ /* 4 B -> L4 addrs */ if (payload_size < 4) { @@ -1125,12 +1144,6 @@ int sub_icmp6_error(struct s_ethernet *eth6, return 1; } - eip4->id = 0x0; - eip4->flags_offset = htons(IPV4_FLAG_DONT_FRAGMENT); - } - - /* look for the original connection */ - if (skip_l4 == 0) { switch (eip6->next_header) { case IPPROTO_TCP: etcp = (struct s_tcp *) payload; @@ -1216,7 +1229,7 @@ int sub_icmp6_error(struct s_ethernet *eth6, } } - /* copy ICMP header to new packet */ + /* copy ICMP header (with updated data) to new packet */ memcpy(packet + *packet_len, *icmp, sizeof(struct s_icmp) + 4); *icmp = (struct s_icmp *) (packet + *packet_len); diff --git a/src/ipv4.c b/src/ipv4.c index 48f1e51..7e238de 100644 --- a/src/ipv4.c +++ b/src/ipv4.c @@ -31,16 +31,16 @@ * * @param eth Ethernet header * @param packet Packet data + * @param length Packet data length * * @return 0 for success * @return 1 for failure */ -int ipv4(struct s_ethernet *eth, char *packet) +int ipv4(struct s_ethernet *eth, char *packet, unsigned short length) { struct s_ipv4 *ip; char *payload; unsigned short header_size; - unsigned short data_size; /* load IP header */ ip = (struct s_ipv4 *) packet; @@ -51,25 +51,34 @@ int ipv4(struct s_ethernet *eth, char *packet) } /* compute sizes and get payload */ - header_size = (ip->ver_hdrlen & 0x0f) * 4; /* # of 4 byte words */ - data_size = htons(ip->len) - header_size; + header_size = (ip->ver_hdrlen & 0x0f) * 4; /* # of 4-byte words */ + + /* sanity check */ + if (header_size > length || ntohs(ip->len) != length) { + log_debug("IPv4 packet of an inconsistent length [dropped]"); + return 1; + } + payload = packet + header_size; /* check and decrease TTL */ if (ip->ttl <= 1) { /* deny this error for ICMP (except ping/pong) * and for non-first fragments */ - if ((ip->proto != IPPROTO_ICMP || payload[0] & 0x0 || - payload[0] & 0x08) && !(ip->flags_offset & htons(0x1fff))) { - /* code 0 = ttl exceeded in transmit */ + if ((ip->proto != IPPROTO_ICMP || + payload[0] == ICMPV4_ECHO_REPLY || + payload[0] == ICMPV4_ECHO_REQUEST) && + !(ip->flags_offset & htons(0x1fff))) { + /* code 0 = TTL exceeded in transmit */ icmp4_error(ip->ip_src, ICMPV4_TIME_EXCEEDED, 0, - packet, htons(ip->len)); + packet, length); } return 1; } else { ip->ttl--; } + #define data_size length - header_size switch (ip->proto) { case IPPROTO_TCP: log_debug("IPv4 Protocol: TCP"); @@ -85,4 +94,5 @@ int ipv4(struct s_ethernet *eth, char *packet) ip->proto, ip->proto); return 1; } + #undef data_size } diff --git a/src/ipv4.h b/src/ipv4.h index da3bd80..3b543ba 100644 --- a/src/ipv4.h +++ b/src/ipv4.h @@ -1,6 +1,6 @@ /* * WrapSix - * Copyright (C) 2008-2013 xHire + * Copyright (C) 2008-2017 xHire * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -64,6 +64,6 @@ struct s_ipv4_pseudo_delta { address */ } __attribute__ ((__packed__)); -int ipv4(struct s_ethernet *eth, char *packet); +int ipv4(struct s_ethernet *eth, char *packet, unsigned short length); #endif /* IPV4_H */ diff --git a/src/ipv6.c b/src/ipv6.c index 8dc9f9b..01232a7 100644 --- a/src/ipv6.c +++ b/src/ipv6.c @@ -31,11 +31,12 @@ * * @param eth Ethernet header * @param packet Packet data + * @param length Packet data length * * @return 0 for success * @return 1 for failure */ -int ipv6(struct s_ethernet *eth, char *packet) +int ipv6(struct s_ethernet *eth, char *packet, unsigned short length) { struct s_ipv6 *ip; char *payload; @@ -44,6 +45,12 @@ int ipv6(struct s_ethernet *eth, char *packet) ip = (struct s_ipv6 *) packet; payload = packet + sizeof(struct s_ipv6); + /* sanity check; len is already covered */ + if (ntohs(ip->len) + sizeof(struct s_ipv6) != length) { + log_debug("IPv6 packet of an inconsistent length [dropped]"); + return 1; + } + /* test if this packet belongs to us */ if (memcmp(&wrapsix_ipv6_prefix, &ip->ip_dest, 12) != 0 && memcmp(&ndp_multicast_addr, &ip->ip_dest, 13) != 0) { @@ -64,19 +71,21 @@ int ipv6(struct s_ethernet *eth, char *packet) ip->hop_limit--; } + #define data_size length - sizeof(struct s_ipv6) switch (ip->next_header) { case IPPROTO_TCP: log_debug("IPv6 Protocol: TCP"); - return tcp_ipv6(eth, ip, payload); + return tcp_ipv6(eth, ip, payload, data_size); case IPPROTO_UDP: log_debug("IPv6 Protocol: UDP"); - return udp_ipv6(eth, ip, payload); + return udp_ipv6(eth, ip, payload, data_size); case IPPROTO_ICMPV6: log_debug("IPv6 Protocol: ICMP"); - return icmp_ipv6(eth, ip, payload); + return icmp_ipv6(eth, ip, payload, data_size); default: log_debug("IPv6 Protocol: unknown [%d/0x%x]", ip->next_header, ip->next_header); return 1; } + #undef data_size } diff --git a/src/ipv6.h b/src/ipv6.h index e310147..c8477f0 100644 --- a/src/ipv6.h +++ b/src/ipv6.h @@ -1,6 +1,6 @@ /* * WrapSix - * Copyright (C) 2008-2013 xHire + * Copyright (C) 2008-2017 xHire * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -68,6 +68,6 @@ struct s_ipv6_pseudo_delta { address */ } __attribute__ ((__packed__)); -int ipv6(struct s_ethernet *eth, char *packet); +int ipv6(struct s_ethernet *eth, char *packet, unsigned short length); #endif /* IPV6_H */ diff --git a/src/tcp.c b/src/tcp.c index d3091ed..44f8148 100644 --- a/src/tcp.c +++ b/src/tcp.c @@ -36,11 +36,13 @@ * Processing of incoming TCPv4 packets. Directly sends translated TCPv6 * packets. * + * The IPv4 packet, although split into several parts, is expected to be stored + * in a continuous memory. + * * @param eth4 Ethernet header * @param ip4 IPv4 header * @param payload TCPv4 data - * @param payload_size Size of payload; needed because IPv4 header has - * dynamic length + * @param payload_size Size of the data payload * * @return 0 for success * @return 1 for failure @@ -67,8 +69,13 @@ int tcp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, char *payload, if ((ip4->flags_offset | htons(IPV4_FLAG_DONT_FRAGMENT)) == htons(IPV4_FLAG_DONT_FRAGMENT) || ((ip4->flags_offset & htons(IPV4_FLAG_MORE_FRAGMENTS)) && - (ip4->flags_offset & htons(0x1fff)) == 0x0000 && - payload_size >= sizeof(struct s_tcp))) { + (ip4->flags_offset & htons(0x1fff)) == 0x0000)) { + /* sanity check */ + if (payload_size < sizeof(struct s_tcp)) { + log_debug("Too short TCPv4 packet"); + return 1; + } + /* parse TCP header */ tcp = (struct s_tcp *) payload; @@ -84,7 +91,7 @@ int tcp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, char *payload, if (tcp->checksum != tmp_short) { /* packet is corrupted and shouldn't be * processed */ - log_debug("Wrong checksum"); + log_debug("Wrong TCPv4 checksum"); return 1; } } @@ -94,7 +101,8 @@ int tcp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, char *payload, tcp->port_src, tcp->port_dest); if (connection == NULL) { - log_debug("Incoming connection wasn't found in NAT"); + log_debug("Incoming TCP connection wasn't found in " + "NAT"); return 1; } @@ -170,13 +178,12 @@ int tcp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, char *payload, /* check if there are any saved fragments */ if (frag_conn->queue != NULL) { - log_debug("Processing TCP fragments of %d", + log_debug("Processing TCPv4 fragments of %d", ip4->id); llnode = frag_conn->queue->first.next; while (llnode->next != NULL) { llnode = llnode->next; - memcpy(&tmp_short, llnode->prev->data, - sizeof(unsigned short)); + /* first is fragment size */ tcp_ipv4((struct s_ethernet *) ( (char *) llnode->prev->data + sizeof(unsigned short)), @@ -188,7 +195,8 @@ int tcp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, char *payload, sizeof(unsigned short) + sizeof(struct s_ethernet) + sizeof(struct s_ipv4), - tmp_short); + *((unsigned short *) + llnode->prev->data)); free(llnode->prev->data); linkedlist_delete(frag_conn->queue, llnode->prev); @@ -289,7 +297,7 @@ int tcp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, char *payload, } if (frag_conn->connection == NULL) { - log_debug("Incoming connection wasn't found in " + log_debug("Incoming TCPv4 connection wasn't found in " "fragments table -- saving it"); if ((saved_packet = (char *) malloc( @@ -312,10 +320,9 @@ int tcp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, char *payload, memcpy(saved_packet, &payload_size, sizeof(unsigned short)); memcpy(saved_packet + sizeof(unsigned short), - (char *) eth4, sizeof(struct s_ethernet)); - memcpy(saved_packet + sizeof(unsigned short) + - sizeof(struct s_ethernet), - (char *) ip4, sizeof(struct s_ipv4)); + (char *) eth4, sizeof(struct s_ethernet) + + sizeof(struct s_ipv4)); + /* just in case the original IPv4 header had options */ memcpy(saved_packet + sizeof(unsigned short) + sizeof(struct s_ethernet) + sizeof(struct s_ipv4), payload, payload_size); @@ -349,7 +356,7 @@ int tcp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, char *payload, /* build IPv6 fragment header */ frag->next_header = IPPROTO_TCP; frag->zeros = 0x0; - frag->id = htonl(htons(ip4->id)); + frag->id = htonl(ntohs(ip4->id)); /* fragment the fragment or not? */ if (payload_size > mtu - sizeof(struct s_ipv6) - @@ -359,8 +366,8 @@ int tcp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, char *payload, sizeof(struct s_ipv6_fragment)); /* fill in missing IPv6 fragment header fields */ - frag->offset_flag = htons((htons(ip4->flags_offset) << - 3) | + frag->offset_flag = htons((ntohs(ip4->flags_offset) << + 3) | IPV6_FLAG_MORE_FRAGMENTS); /* copy the payload data */ @@ -374,9 +381,9 @@ int tcp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, char *payload, ip6->len = htons(payload_size + sizeof(struct s_ipv6_fragment) - FRAGMENT_LEN); - frag->offset_flag = htons(((htons(ip4->flags_offset) & - 0x1fff) + - FRAGMENT_LEN / 8) << 3); + frag->offset_flag = htons(((ntohs(ip4->flags_offset) & + 0x1fff) + + FRAGMENT_LEN / 8) << 3); if (ip4->flags_offset & htons(IPV4_FLAG_MORE_FRAGMENTS)) { frag->offset_flag |= @@ -399,7 +406,7 @@ int tcp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, char *payload, sizeof(struct s_ipv6_fragment)); /* fill in missing IPv6 fragment header fields */ - frag->offset_flag = htons(htons(ip4->flags_offset) << + frag->offset_flag = htons(ntohs(ip4->flags_offset) << 3); if (ip4->flags_offset & htons(IPV4_FLAG_MORE_FRAGMENTS)) { @@ -430,11 +437,13 @@ int tcp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, char *payload, * @param eth6 Ethernet header * @param ip6 IPv6 header * @param payload TCPv6 data + * @param payload_size Size of the data payload (L4+) * * @return 0 for success * @return 1 for failure */ -int tcp_ipv6(struct s_ethernet *eth6, struct s_ipv6 *ip6, char *payload) +int tcp_ipv6(struct s_ethernet *eth6, struct s_ipv6 *ip6, char *payload, + unsigned short payload_size) { struct s_tcp *tcp; struct s_nat *connection; @@ -442,6 +451,12 @@ int tcp_ipv6(struct s_ethernet *eth6, struct s_ipv6 *ip6, char *payload) struct s_ipv4 *ip4; char packet[PACKET_BUFFER]; + /* sanity check */ + if (payload_size < sizeof(struct s_tcp)) { + log_debug("Too short TCPv6 packet"); + return 1; + } + /* parse TCP header */ tcp = (struct s_tcp *) payload; @@ -449,7 +464,7 @@ int tcp_ipv6(struct s_ethernet *eth6, struct s_ipv6 *ip6, char *payload) orig_checksum = tcp->checksum; tcp->checksum = 0; tcp->checksum = checksum_ipv6(ip6->ip_src, ip6->ip_dest, - htons(ip6->len), IPPROTO_TCP, + payload_size, IPPROTO_TCP, (char *) payload); if (tcp->checksum != orig_checksum) { @@ -540,7 +555,7 @@ int tcp_ipv6(struct s_ethernet *eth6, struct s_ipv6 *ip6, char *payload) ip4->ver_hdrlen = 0x45; /* ver 4, header length 20 B */ ip4->tos = ((ip6->ver & 0x0f) << 4) | ((ip6->traffic_class & 0xf0) >> 4); - ip4->len = htons(sizeof(struct s_ipv4) + htons(ip6->len)); + ip4->len = htons(sizeof(struct s_ipv4) + payload_size); ip4->id = 0x0; ip4->flags_offset = htons(IPV4_FLAG_DONT_FRAGMENT); ip4->ttl = ip6->hop_limit; @@ -559,14 +574,15 @@ int tcp_ipv6(struct s_ethernet *eth6, struct s_ipv6 *ip6, char *payload) connection->ipv4_port_src); /* copy the payload data (with new checksum) */ - memcpy(packet + sizeof(struct s_ipv4), payload, htons(ip6->len)); + memcpy(packet + sizeof(struct s_ipv4), payload, payload_size); /* compute IPv4 checksum */ ip4->checksum = 0x0; ip4->checksum = checksum(ip4, sizeof(struct s_ipv4)); /* send translated packet */ - transmit_ipv4(&ip4->ip_dest, packet, htons(ip4->len)); + transmit_ipv4(&ip4->ip_dest, packet, sizeof(struct s_ipv4) + + payload_size); return 0; } diff --git a/src/tcp.h b/src/tcp.h index 0f76cd7..4b943da 100644 --- a/src/tcp.h +++ b/src/tcp.h @@ -1,6 +1,6 @@ /* * WrapSix - * Copyright (C) 2008-2012 xHire + * Copyright (C) 2008-2017 xHire * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -45,6 +45,7 @@ struct s_tcp { int tcp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, char *payload, unsigned short payload_size); -int tcp_ipv6(struct s_ethernet *eth6, struct s_ipv6 *ip6, char *payload); +int tcp_ipv6(struct s_ethernet *eth6, struct s_ipv6 *ip6, char *payload, + unsigned short payload_size); #endif /* TCP_H */ diff --git a/src/udp.c b/src/udp.c index 3e70ea5..177bd7d 100644 --- a/src/udp.c +++ b/src/udp.c @@ -38,8 +38,7 @@ * @param eth4 Ethernet header * @param ip4 IPv4 header * @param payload UDPv4 data - * @param payload_size Size of payload; needed because IPv4 header has - * dynamic length + * @param payload_size Size of the data payload * * @return 0 for success * @return 1 for failure @@ -58,6 +57,13 @@ int udp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, char *payload, /* parse UDP header */ udp = (struct s_udp *) payload; + /* sanity check again; the second one is not strictly needed */ + if (payload_size < sizeof(struct s_udp) || + payload_size != ntohs(udp->len)) { + log_debug("Too short/malformed UDPv4 packet"); + return 1; + } + /* checksum recheck */ if (udp->checksum != 0x0000) { orig_checksum = udp->checksum; @@ -138,11 +144,13 @@ int udp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, char *payload, * @param eth6 Ethernet header * @param ip6 IPv6 header * @param payload UDPv6 data + * @param payload_size Size of the data payload * * @return 0 for success * @return 1 for failure */ -int udp_ipv6(struct s_ethernet *eth6, struct s_ipv6 *ip6, char *payload) +int udp_ipv6(struct s_ethernet *eth6, struct s_ipv6 *ip6, char *payload, + unsigned short payload_size) { struct s_udp *udp; struct s_nat *connection; @@ -154,11 +162,18 @@ int udp_ipv6(struct s_ethernet *eth6, struct s_ipv6 *ip6, char *payload) /* parse UDP header */ udp = (struct s_udp *) payload; + /* sanity check again; the second one is not strictly needed */ + if (payload_size < sizeof(struct s_udp) || + payload_size != ntohs(udp->len)) { + log_debug("Too short/malformed UDPv6 packet"); + return 1; + } + /* checksum recheck */ orig_checksum = udp->checksum; udp->checksum = 0; - udp->checksum = checksum_ipv6(ip6->ip_src, ip6->ip_dest, - htons(ip6->len), IPPROTO_UDP, payload); + udp->checksum = checksum_ipv6(ip6->ip_src, ip6->ip_dest, payload_size, + IPPROTO_UDP, payload); if (udp->checksum != orig_checksum) { /* packet is corrupted and shouldn't be processed */ @@ -185,7 +200,7 @@ int udp_ipv6(struct s_ethernet *eth6, struct s_ipv6 *ip6, char *payload) /* translated packet */ ip4 = (struct s_ipv4 *) packet; - packet_size = sizeof(struct s_ipv4) + htons(ip6->len); + packet_size = sizeof(struct s_ipv4) + payload_size; /* build IPv4 packet */ ip4->ver_hdrlen = 0x45; /* ver 4, header length 20 B */ @@ -210,7 +225,7 @@ int udp_ipv6(struct s_ethernet *eth6, struct s_ipv6 *ip6, char *payload) connection->ipv4_port_src); /* copy the payload data (with new checksum) */ - memcpy(packet + sizeof(struct s_ipv4), payload, htons(ip6->len)); + memcpy(packet + sizeof(struct s_ipv4), payload, payload_size); /* compute IPv4 checksum */ ip4->checksum = 0x0; diff --git a/src/udp.h b/src/udp.h index b46f2b3..3725aab 100644 --- a/src/udp.h +++ b/src/udp.h @@ -1,6 +1,6 @@ /* * WrapSix - * Copyright (C) 2008-2012 xHire + * Copyright (C) 2008-2017 xHire * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -29,6 +29,7 @@ struct s_udp { int udp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, char *payload, unsigned short payload_size); -int udp_ipv6(struct s_ethernet *eth6, struct s_ipv6 *ip6, char *payload); +int udp_ipv6(struct s_ethernet *eth6, struct s_ipv6 *ip6, char *payload, + unsigned short payload_size); #endif /* UDP_H */ diff --git a/src/wrapper.c b/src/wrapper.c index 034774b..754db71 100644 --- a/src/wrapper.c +++ b/src/wrapper.c @@ -24,9 +24,11 @@ #include /* struct ifreq */ #include /* htons */ #include /* struct packet_mreq, struct sockaddr_ll */ +#include /* perror */ #include /* srand */ #include /* strncpy */ #include /* ioctl, SIOCGIFINDEX */ +#include /* caddr_t */ #include /* time, time_t */ #include /* close */ @@ -36,6 +38,7 @@ #endif /* HAVE_CONFIG_H */ #include "config.h" #include "ethernet.h" +#include "icmp.h" #include "ipv4.h" #include "ipv6.h" #include "log.h" @@ -53,7 +56,7 @@ struct s_ipv4_addr wrapsix_ipv4_addr; struct s_ipv6_addr host_ipv6_addr; struct s_ipv4_addr host_ipv4_addr; -int process(char *packet); +static int process(char *packet, unsigned short length); int main(int argc, char **argv) { @@ -177,13 +180,21 @@ int main(int argc, char **argv) /* sniff! :c) */ for (i = 1;; i++) { - if ((length = recv(sniff_sock, buffer, PACKET_BUFFER, 0)) == - -1) { + length = recv(sniff_sock, buffer, PACKET_BUFFER, MSG_TRUNC); + if (length == -1) { + perror("recv"); log_error("Unable to retrieve data from socket"); return 1; } - process((char *) &buffer); + if (length > PACKET_BUFFER) { + log_error("Received packet is too big (%d B). Please " + "tune NIC offloading features and report " + "this issue to " PACKAGE_BUGREPORT, length); + continue; + } + + process(buffer, length); if (i % 250000) { curtime = time(NULL); @@ -217,29 +228,46 @@ int main(int argc, char **argv) return 0; } -int process(char *packet) +/** + * Decide what to do with a packet and pass it for further processing. + * + * @param packet Packet data + * @param length Packet data length + * + * @return 0 for success + * @return 1 for failure + */ +static int process(char *packet, unsigned short length) { - struct s_ethernet *eth; /* the ethernet header */ - char *payload; /* the IP header + packet - payload */ + struct s_ethernet *eth; + + /* sanity check: out of every combination this is the smallest one */ + if (length < sizeof(struct s_ethernet) + sizeof(struct s_ipv4) + + sizeof(struct s_icmp)) { + return 1; + } /* parse ethernet header */ - eth = (struct s_ethernet *) (packet); - payload = packet + sizeof(struct s_ethernet); + eth = (struct s_ethernet *) packet; + + #define payload packet + sizeof(struct s_ethernet) + #define payload_length length - sizeof(struct s_ethernet) switch (htons(eth->type)) { case ETHERTYPE_IP: - return ipv4(eth, payload); + return ipv4(eth, payload, payload_length); case ETHERTYPE_IPV6: - return ipv6(eth, payload); + return ipv6(eth, payload, payload_length); case ETHERTYPE_ARP: - log_debug("HW Protocol: ARP"); - return arp(eth, payload); + return arp(eth, payload, payload_length); default: log_debug("HW Protocol: unknown [%d/0x%04x]", htons(eth->type), htons(eth->type)); return 1; } + + #undef payload_length + #undef payload } /**