From 1196c33bee8c72d283dbffa7d4502bd66f80024e Mon Sep 17 00:00:00 2001 From: Michal Zima Date: Mon, 2 Apr 2012 13:44:14 +0200 Subject: [PATCH] Full processing of ICMP packets (v4 and v6); NDP IPv4 header and pseudoheader Basic preprocessing of IPv4 packets --- src/Makefile.am | 3 +- src/icmp.c | 345 ++++++++++++++++++++++++++++++++++++++++++++++++ src/icmp.h | 83 ++++++++++++ src/ipv4.c | 70 ++++++++++ src/ipv4.h | 36 ++++- src/ipv6.c | 5 +- src/ipv6.h | 5 +- src/wrapper.c | 2 +- 8 files changed, 541 insertions(+), 8 deletions(-) create mode 100644 src/icmp.c create mode 100644 src/icmp.h create mode 100644 src/ipv4.c diff --git a/src/Makefile.am b/src/Makefile.am index 80b9083..2f1a6d7 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -3,8 +3,9 @@ wrapsix_dnsproxy_SOURCES = dnsproxy.c wrapsix_wrapper_SOURCES = \ arp.c arp.h \ checksum.c checksum.h \ + icmp.c icmp.h \ + ipv4.c ipv4.h \ ipv6.c ipv6.h \ - ipv4.h \ nat.c nat.h \ radixtree.c radixtree.h \ transmitter.c transmitter.h \ diff --git a/src/icmp.c b/src/icmp.c new file mode 100644 index 0000000..644da95 --- /dev/null +++ b/src/icmp.c @@ -0,0 +1,345 @@ +/* + * WrapSix + * Copyright (C) 2008-2012 Michal Zima + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include /* ETHERTYPE_* */ +#include /* htons */ +#include +#include /* malloc */ +#include /* memcpy, memset */ + +#include "checksum.h" +#include "icmp.h" +#include "ipv6.h" +#include "nat.h" +#include "transmitter.h" +#include "wrapper.h" + +/** + * Processing of incoming ICMPv4 packets. Directly sends translated ICMPv6 + * packets. + * + * @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 + * + * @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) +{ + struct s_icmp *icmp; + unsigned char *icmp_data; + struct s_nat *connection; + unsigned short orig_checksum; + unsigned char *packet; + + struct s_icmp_echo *echo; + struct s_ethernet *eth6; + struct s_ipv6 *ip6; + + icmp = (struct s_icmp *) payload; + icmp_data = (unsigned char *) (payload + sizeof(struct s_icmp)); + + /* ICMP checksum recheck */ + orig_checksum = icmp->checksum; + icmp->checksum = 0; + icmp->checksum = checksum((unsigned char *) icmp, payload_size); + + if (icmp->checksum != orig_checksum) { + /* packet is corrupted and shouldn't be processed */ + printf("[Debug] Wrong checksum\n"); + return 1; + } + + switch (icmp->type) { + case ICMPV4_ECHO_REQUEST: + /* this is pretty non-sense situation */ + return 1; + + case ICMPV4_ECHO_REPLY: + echo = (struct s_icmp_echo *) icmp_data; + + connection = nat_in(nat4_icmp, ip4->ip_src, + 0, echo->id); + + if (connection == NULL) { + printf("[Debug] Incoming connection wasn't " + "found in NAT\n"); + return 1; + } + + echo->id = connection->ipv6_port_src; + + /* override information in original ICMP header */ + icmp->type = ICMPV6_ECHO_REPLY; + + break; + + default: + printf("[Debug] ICMPv4 Type: unknown [%d/0x%x]\n", + icmp->type, icmp->type); + return 1; + } + + /* allocate memory for translated packet */ + if ((packet = (unsigned char *) malloc(sizeof(struct s_ethernet) + + sizeof(struct s_ipv6) + + payload_size)) == NULL) { + fprintf(stderr, "[Error] Lack of free memory\n"); + return 1; + } + eth6 = (struct s_ethernet *) packet; + ip6 = (struct s_ipv6 *) (packet + sizeof(struct s_ethernet)); + + /* build ethernet header */ + eth6->dest = connection->mac; + eth6->src = mac; + eth6->type = htons(ETHERTYPE_IPV6); + + /* build IPv6 packet */ + ip6->ver = 0x60; + ip6->traffic_class = 0x0; + ip6->flow_label = 0x0; + ip6->len = htons(payload_size); + ip6->next_header = IPPROTO_ICMPV6; + ip6->hop_limit = ip4->ttl; + ipv4_to_ipv6(&ip4->ip_src, &ip6->ip_src); + memcpy(&ip6->ip_dest, &connection->ipv6, sizeof(struct s_ipv6_addr)); + + /* compute ICMP checksum */ + icmp->checksum = 0x0; + icmp->checksum = checksum_ipv6(ip6->ip_src, ip6->ip_dest, payload_size, + IPPROTO_ICMPV6, (unsigned char *) icmp); + + /* copy the payload data (with new checksum) */ + memcpy(packet + sizeof(struct s_ethernet) + sizeof(struct s_ipv6), payload, + payload_size); + + /* send translated packet */ + transmit_raw(packet, sizeof(struct s_ethernet) + sizeof(struct s_ipv6) + + payload_size); + + /* clean-up */ + free(packet); + + return 0; +} + +/** + * Processing of outgoing ICMPv6 packets. Directly sends translated ICMPv4 + * packets. + * + * @param eth6 Ethernet header + * @param ip6 IPv6 header + * @param payload ICMPv6 data + * + * @return 0 for success + * @return 1 for failure + */ +int icmp_ipv6(struct s_ethernet *eth6, struct s_ipv6 *ip6, char *payload) +{ + struct s_icmp *icmp; + unsigned char *icmp_data; + struct s_nat *connection; + unsigned short orig_checksum; + unsigned char *packet; + + struct s_icmp_echo *echo; + struct s_ipv4 *ip4; + + icmp = (struct s_icmp *) payload; + icmp_data = (unsigned char *) (payload + sizeof(struct s_icmp)); + + /* checksum recheck */ + orig_checksum = icmp->checksum; + icmp->checksum = 0; + icmp->checksum = checksum_ipv6(ip6->ip_src, ip6->ip_dest, + htons(ip6->len), IPPROTO_ICMPV6, + (unsigned char *) icmp); + + if (icmp->checksum != orig_checksum) { + /* packet is corrupted and shouldn't be processed */ + printf("[Debug] Wrong checksum\n"); + return 1; + } + + /* decide the type of the ICMP packet */ + switch (icmp->type) { + case ICMPV6_ECHO_REQUEST: + echo = (struct s_icmp_echo *) icmp_data; + + connection = nat_out(nat6_icmp, nat4_icmp, + eth6->src, + ip6->ip_src, ip6->ip_dest, + echo->id, 0); + + if (connection == NULL) { + printf("[Debug] Error! Outgoing connection " + "wasn't found/created in NAT!\n"); + return 1; + } + + echo->id = connection->ipv4_port_src; + + /* override information in original ICMP header */ + icmp->type = ICMPV4_ECHO_REQUEST; + + break; + + case ICMPV6_ECHO_REPLY: + /* this is pretty non-sense situation */ + return 1; + + case ICMPV6_NDP_NS: + return icmp_ndp(eth6, ip6, + (struct s_icmp_ndp_ns *) icmp_data); + + default: + printf("[Debug] ICMPv6 Type: unknown [%d/0x%x]\n", + icmp->type, icmp->type); + return 1; + } + + /* allocate memory for translated packet */ + if ((packet = (unsigned char *) malloc(sizeof(struct s_ipv4) + + htons(ip6->len))) == NULL) { + fprintf(stderr, "[Error] Lack of free memory\n"); + return 1; + } + ip4 = (struct s_ipv4 *) packet; + + /* build IPv4 packet */ + ip4->ver_hdrlen = 0x45; /* ver 4, header length 20 B */ + ip4->tos = 0x0; + ip4->len = htons(sizeof(struct s_ipv4) + htons(ip6->len)); + ip4->id = 0x0; + ip4->flags_offset = htons(IPV4_FLAG_DONT_FRAGMENT); + ip4->ttl = ip6->hop_limit; + ip4->proto = IPPROTO_ICMP; + ipv6_to_ipv4(&ip6->ip_dest, &ip4->ip_dest); + memcpy(&ip4->ip_src, &wrapsix_ipv4_addr, sizeof(struct s_ipv4_addr)); + + /* compute ICMP checksum */ + icmp->checksum = 0x0; + icmp->checksum = checksum((unsigned char *) icmp, htons(ip6->len)); + + /* copy the payload data (with new checksum) */ + memcpy(packet + sizeof(struct s_ipv4), payload, htons(ip6->len)); + + /* compute IPv4 checksum */ + ip4->checksum = checksum_ipv4(ip4->ip_src, ip4->ip_dest, + htons(ip4->len), IPPROTO_ICMP, + (unsigned char *) icmp); + + /* send translated packet */ + printf("[Debug] transmitting\n"); + transmit_ipv4(&ip4->ip_dest, packet, htons(ip4->len)); + + /* clean-up */ + free(packet); + + return 0; +} + +/** + * Processes NDP NS packets and sends NDP NA. + * + * @param ethq Ethernet header + * @param ipq IPv6 header + * @param ndp_ns NDP NS data + * + * @return 0 for success + * @return 1 for failure + */ +int icmp_ndp(struct s_ethernet *ethq, struct s_ipv6 *ipq, + struct s_icmp_ndp_ns *ndp_ns) +{ + unsigned char *packet; + struct s_ethernet *ethr; + struct s_ipv6 *ipr; + struct s_icmp *icmp; + struct s_icmp_ndp_na *ndp_na; + + /* first check whether the request belongs to us */ + if (memcmp(&wrapsix_ipv6_prefix, &ndp_ns->target, 12) != 0) { + printf("[Debug] [NDP] This is unfamiliar packet\n"); + return 1; + } + + /* allocate memory for reply packet */ + #define NDP_PACKET_SIZE sizeof(struct s_ethernet) + \ + sizeof(struct s_ipv6) + \ + sizeof(struct s_icmp) + \ + sizeof(struct s_icmp_ndp_na) + if ((packet = (unsigned char *) malloc(NDP_PACKET_SIZE)) == NULL) { + fprintf(stderr, "[Error] Lack of free memory\n"); + return 1; + } + memset(packet, 0x0, NDP_PACKET_SIZE); + + /* divide reply packet into parts */ + ethr = (struct s_ethernet *) packet; + ipr = (struct s_ipv6 *) (packet + sizeof(struct s_ethernet)); + icmp = (struct s_icmp *) (packet + sizeof(struct s_ethernet) + + sizeof(struct s_ipv6)); + ndp_na = (struct s_icmp_ndp_na *) (packet + sizeof(struct s_ethernet) + + sizeof(struct s_ipv6) + + sizeof(struct s_icmp)); + + /* ethernet */ + ethr->dest = ethq->src; + ethr->src = mac; + ethr->type = ethq->type; + + /* IPv6 */ + ipr->ver = 0x60; + ipr->len = htons(sizeof(struct s_icmp) + sizeof(struct s_icmp_ndp_na)); + ipr->next_header = IPPROTO_ICMPV6; + /* hop limit 255 is required by RFC 4861, section 7.1.2. */ + ipr->hop_limit = 255; + ipr->ip_src = ndp_ns->target; + ipr->ip_dest = ipq->ip_src; + + /* ICMP */ + icmp->type = ICMPV6_NDP_NA; + icmp->code = 0; + icmp->checksum = 0; + + /* NDP NA */ + ndp_na->flags = INNAF_S; + ndp_na->target = ndp_ns->target; + ndp_na->opt_type = 2; + ndp_na->opt_len = 1; + ndp_na->opt_tlla = ethr->src; + + /* compute ICMP checksum */ + icmp->checksum = checksum_ipv6(ipr->ip_src, ipr->ip_dest, + htons(ipr->len), IPPROTO_ICMPV6, + (unsigned char *) icmp); + + /* send NDP reply */ + transmit_raw(packet, NDP_PACKET_SIZE); + + /* clean-up */ + free(packet); + + return 0; +} diff --git a/src/icmp.h b/src/icmp.h new file mode 100644 index 0000000..82ca9f7 --- /dev/null +++ b/src/icmp.h @@ -0,0 +1,83 @@ +/* + * WrapSix + * Copyright (C) 2008-2012 Michal Zima + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#ifndef ICMP_H +#define ICMP_H + +#include "ipv4.h" +#include "ipv6.h" + +/* ICMP types */ +#define ICMPV4_ECHO_REPLY 0x0 +#define ICMPV4_ECHO_REQUEST 0x8 + +/* ICMPv6 types */ +#define ICMPV6_DST_UNREACHABLE 0x1 +#define ICMPV6_PKT_TOO_BIG 0x2 +#define ICMPV6_TIME_EXCEEDED 0x3 +#define ICMPV6_PARAM_PROBLEM 0x4 +#define ICMPV6_ECHO_REQUEST 0x80 +#define ICMPV6_ECHO_REPLY 0x81 +#define ICMPV6_NDP_RS 0x85 +#define ICMPV6_NDP_RA 0x86 +#define ICMPV6_NDP_NS 0x87 +#define ICMPV6_NDP_NA 0x88 +#define ICMPV6_NDP_RM 0x89 + +/* ICMP NDP NA Flag (INNAF) */ +#define INNAF_R 0x80 /* router flag */ +#define INNAF_S 0x40 /* solicited flag */ +#define INNAF_O 0x20 /* override flag */ + +/* ICMP header structure */ +struct s_icmp { + unsigned char type; /* 8 b; ICMP type */ + unsigned char code; /* 8 b; subtype of ICMP type */ + unsigned short checksum; /* 16 b */ +}; + +/* ICMP echo structure */ +struct s_icmp_echo { + unsigned short id; /* 16 b; ID value */ + unsigned short seq; /* 16 b; sequence number */ +}; + +/* ICMP NDP NS structure */ +struct s_icmp_ndp_ns { + unsigned int zeros; /* 32 b; reserved section */ + struct s_ipv6_addr target; /* 128 b; target IP address */ +}; + +/* ICMP NDP NA structure */ +struct s_icmp_ndp_na { + unsigned char flags; /* 8 b; 3 flags */ + unsigned int zeros:24; /* 24 b; reserved section */ + struct s_ipv6_addr target; /* 128 b; target IP address */ + unsigned char opt_type; /* 8 b; option -- type */ + unsigned char opt_len; /* 8 b; option -- length */ + struct s_mac_addr opt_tlla; /* 48 b; option -- target + link-layer address */ +}; + +int icmp_ipv4(struct s_ethernet *eth, struct s_ipv4 *ip4, char *payload, + unsigned short payload_size); +int icmp_ipv6(struct s_ethernet *eth, struct s_ipv6 *ip6, char *payload); +int icmp_ndp(struct s_ethernet *ethq, struct s_ipv6 *ipq, + struct s_icmp_ndp_ns *ndp_ns); + +#endif /* ICMP_H */ diff --git a/src/ipv4.c b/src/ipv4.c new file mode 100644 index 0000000..d84e18e --- /dev/null +++ b/src/ipv4.c @@ -0,0 +1,70 @@ +/* + * WrapSix + * Copyright (C) 2008-2012 Michal Zima + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include /* IPPROTO_* */ +#include +#include /* memcmp */ + +#include "icmp.h" +#include "ipv4.h" +#include "wrapper.h" + +int ipv4(struct s_ethernet *eth, char *packet) +{ + struct s_ipv4 *ip; + char *payload; + unsigned short header_size; + unsigned short data_size; + + /* load IP header */ + ip = (struct s_ipv4 *) packet; + + /* test if this packet belongs to us */ + if (memcmp(&wrapsix_ipv4_addr, &ip->ip_dest, 4) != 0) { + printf("[Debug] [IPv4] This is unfamiliar packet\n"); + return 1; + } + + /* TODO: verify checksum */ + + /* compute sizes and get payload */ + header_size = (ip->ver_hdrlen & 0x0f) * 4; /* # of 4 byte words */ + data_size = htons(ip->len) - header_size; + payload = packet + header_size; + + switch (ip->proto) { + case IPPROTO_TCP: + printf("[Debug] IPv4 Protocol: TCP\n"); + /*ipv4_tcp(eth, ip, payload, data_size);*/ + break; + case IPPROTO_UDP: + printf("[Debug] IPv4 Protocol: UDP\n"); + /*ipv4_udp(eth, ip, payload, data_size);*/ + break; + case IPPROTO_ICMP: + printf("[Debug] IPv4 Protocol: ICMP\n"); + icmp_ipv4(eth, ip, payload, data_size); + break; + default: + printf("[Debug] IPv4 Protocol: unknown [%d/0x%x]\n", + ip->proto, ip->proto); + return 1; + } + + return 0; +} diff --git a/src/ipv4.h b/src/ipv4.h index 0d2e00a..03d4bb3 100644 --- a/src/ipv4.h +++ b/src/ipv4.h @@ -1,6 +1,6 @@ /* * WrapSix - * Copyright (C) 2008-2010 Michal Zima + * Copyright (C) 2008-2012 Michal Zima * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -19,9 +19,43 @@ #ifndef IPV4_H #define IPV4_H +#include "ethernet.h" + +/* IPv4 flags */ +#define IPV4_FLAG_DONT_FRAGMENT 0x4000 +#define IPV4_FLAG_MORE_FRAGMENTS 0x2000 + /* IPv4 address structure */ struct s_ipv4_addr { unsigned char addr[4]; } __attribute__ ((__packed__)); +/* IPv4 header structure */ +struct s_ipv4 { + unsigned char ver_hdrlen; /* 4 b; version, + 4 b; header length in 4 B */ + unsigned char tos; /* 8 b; type of service */ + unsigned short len; /* 16 b; total packet length */ + unsigned short id; /* 16 b; id of the packet + (for fragmentation) */ + unsigned short flags_offset; /* 3 b; flags, + 13 b; fragment offset in B */ + unsigned char ttl; /* 8 b; time to live */ + unsigned char proto; /* 8 b; protocol in payload */ + unsigned short checksum; /* 16 b */ + struct s_ipv4_addr ip_src; /* 32 b; source address */ + struct s_ipv4_addr ip_dest; /* 32 b; destination address */ +} __attribute__ ((__packed__)); + +/* IPv4 pseudoheader structure for checksum */ +struct s_ipv4_pseudo { + struct s_ipv4_addr ip_src; /* 32 b; source address */ + struct s_ipv4_addr ip_dest; /* 32 b; destination address */ + unsigned char zeros; /* 8 b */ + unsigned char proto; /* 8 b; protocol in payload */ + unsigned int len; /* 32 b; payload length */ +} __attribute__ ((__packed__)); + +int ipv4(struct s_ethernet *eth, char *packet); + #endif /* IPV4_H */ diff --git a/src/ipv6.c b/src/ipv6.c index 4a84a1e..96d6610 100644 --- a/src/ipv6.c +++ b/src/ipv6.c @@ -20,6 +20,7 @@ #include #include /* memcmp */ +#include "icmp.h" #include "ipv6.h" #include "wrapper.h" @@ -35,7 +36,7 @@ int ipv6(struct s_ethernet *eth, char *packet) /* 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) { - printf("[Debug] This is unfamiliar packet\n"); + printf("[Debug] [IPv6] This is unfamiliar packet\n"); return 1; } @@ -50,7 +51,7 @@ int ipv6(struct s_ethernet *eth, char *packet) break; case IPPROTO_ICMPV6: printf("[Debug] IPv6 Protocol: ICMP\n"); - /*ipv6_icmp(eth, ip, payload);*/ + icmp_ipv6(eth, ip, payload); break; default: printf("[Debug] IPv6 Protocol: unknown [%d/0x%x]\n", diff --git a/src/ipv6.h b/src/ipv6.h index 273f07b..f20e2cf 100644 --- a/src/ipv6.h +++ b/src/ipv6.h @@ -30,10 +30,10 @@ struct s_ipv6_addr { struct s_ipv6 { unsigned char ver; /* 4 b; version */ unsigned char traffic_class; /* 8 b; traffic class */ - unsigned short flow_label; /* 20 b; flow label (qos) */ + unsigned short flow_label; /* 20 b; flow label (QOS) */ unsigned short len; /* 16 b; payload length */ unsigned char next_header; /* 8 b; next header */ - unsigned char hop_limit; /* 8 b; hop limit (replaces ttl) */ + unsigned char hop_limit; /* 8 b; hop limit (aka TTL) */ struct s_ipv6_addr ip_src; /* 128 b; source address */ struct s_ipv6_addr ip_dest; /* 128 b; destination address */ } __attribute__ ((__packed__)); @@ -47,7 +47,6 @@ struct s_ipv6_pseudo { unsigned char next_header; /* 8 b; next header */ } __attribute__ ((__packed__)); - int ipv6(struct s_ethernet *eth, char *packet); #endif /* IPV6_H */ diff --git a/src/wrapper.c b/src/wrapper.c index 63b9be5..d5fbcf5 100644 --- a/src/wrapper.c +++ b/src/wrapper.c @@ -161,7 +161,7 @@ int process(char *packet) switch (htons(eth->type)) { case ETHERTYPE_IP: printf("[Debug] HW Protocol: IPv4\n"); - return -1; + return ipv4(eth, payload); case ETHERTYPE_IPV6: printf("[Debug] HW Protocol: IPv6\n"); return ipv6(eth, payload);