1
0
mirror of https://code.semirocket.science/wrapsix synced 2024-11-10 00:01:01 +02:00

Full processing of ICMP packets (v4 and v6); NDP

IPv4 header and pseudoheader
Basic preprocessing of IPv4 packets
This commit is contained in:
Michal Zima 2012-04-02 13:44:14 +02:00
parent 4b6a373019
commit 1196c33bee
8 changed files with 541 additions and 8 deletions

View File

@ -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 \

345
src/icmp.c Normal file
View File

@ -0,0 +1,345 @@
/*
* WrapSix
* Copyright (C) 2008-2012 Michal Zima <xhire@mujmalysvet.cz>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <net/ethernet.h> /* ETHERTYPE_* */
#include <netinet/in.h> /* htons */
#include <stdio.h>
#include <stdlib.h> /* malloc */
#include <string.h> /* 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;
}

83
src/icmp.h Normal file
View File

@ -0,0 +1,83 @@
/*
* WrapSix
* Copyright (C) 2008-2012 Michal Zima <xhire@mujmalysvet.cz>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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 */

70
src/ipv4.c Normal file
View File

@ -0,0 +1,70 @@
/*
* WrapSix
* Copyright (C) 2008-2012 Michal Zima <xhire@mujmalysvet.cz>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <netinet/in.h> /* IPPROTO_* */
#include <stdio.h>
#include <string.h> /* 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;
}

View File

@ -1,6 +1,6 @@
/*
* WrapSix
* Copyright (C) 2008-2010 Michal Zima <xhire@mujmalysvet.cz>
* Copyright (C) 2008-2012 Michal Zima <xhire@mujmalysvet.cz>
*
* 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 */

View File

@ -20,6 +20,7 @@
#include <stdio.h>
#include <string.h> /* 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",

View File

@ -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 */

View File

@ -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);