1
0
mirror of https://code.semirocket.science/wrapsix synced 2024-09-19 23:11:04 +03:00

Propagation of packet/payload size

More sanity checks
Fixed expiration of TTL in IPv4 packets
Optimisations
Fixed subtle mistakes
This commit is contained in:
xHire 2017-10-21 10:17:32 +02:00
parent 8d68658e3f
commit 4dbbcf7fc1
12 changed files with 201 additions and 101 deletions

View File

@ -25,18 +25,19 @@
#include "transmitter.h" #include "transmitter.h"
#include "wrapper.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. * Process ARP packets and reply to them.
* *
* @param ethq Ethernet header of the packet * @param ethq Ethernet header of the packet
* @param payload Data of the packet * @param payload Data of the packet
* @param payload_size Size of the data payload
* *
* @return 0 for success * @return 0 for success
* @return 1 for failure * @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_arp *arpq, *arpr; /* request and reply */
struct s_ethernet *ethr; struct s_ethernet *ethr;
@ -50,6 +51,12 @@ int arp(struct s_ethernet *ethq, char *payload)
return 1; 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 */ /* test if this packet belongs to us */
if (memcmp(&wrapsix_ipv4_addr, &arpq->ip_dest, 4)) { if (memcmp(&wrapsix_ipv4_addr, &arpq->ip_dest, 4)) {
log_debug("This is unfamiliar ARP packet"); log_debug("This is unfamiliar ARP packet");

View File

@ -1,6 +1,6 @@
/* /*
* WrapSix * WrapSix
* Copyright (C) 2008-2013 xHire <xhire@wrapsix.org> * Copyright (C) 2008-2017 xHire <xhire@wrapsix.org>
* *
* This program is free software: you can redistribute it and/or modify * 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 * 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 */ struct s_ipv4_addr ip_dest; /* 32 b; target protocol addr */
} __attribute__ ((__packed__)); } __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 */ #endif /* ARP_H */

View File

@ -47,14 +47,13 @@ int sub_icmp6_error(struct s_ethernet *eth6,
* @param eth4 Ethernet header * @param eth4 Ethernet header
* @param ip4 IPv4 header * @param ip4 IPv4 header
* @param payload ICMPv4 data * @param payload ICMPv4 data
* @param payload_size Size of payload; needed because IPv4 header has * @param payload_size Size of the data payload
* dynamic length
* *
* @return 0 for success * @return 0 for success
* @return 1 for failure * @return 1 for failure
*/ */
int icmp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, int icmp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, char *payload,
char *payload, unsigned short payload_size) unsigned short payload_size)
{ {
struct s_icmp *icmp; struct s_icmp *icmp;
unsigned int *icmp_extra; unsigned int *icmp_extra;
@ -86,7 +85,7 @@ int icmp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4,
/* ICMP checksum recheck */ /* ICMP checksum recheck */
orig_checksum = icmp->checksum; orig_checksum = icmp->checksum;
icmp->checksum = 0; icmp->checksum = 0x0;
icmp->checksum = checksum((char *) icmp, payload_size); icmp->checksum = checksum((char *) icmp, payload_size);
if (icmp->checksum != orig_checksum) { 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; echo->id = connection->ipv6_port_src;
/* override information in original ICMP header */ /* override information in the original ICMP header */
icmp->type = ICMPV6_ECHO_REPLY; icmp->type = ICMPV6_ECHO_REPLY;
/* copy the payload data */ /* copy the payload data */
@ -164,18 +163,25 @@ int icmp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4,
} else if (icmp->code == 4) { } else if (icmp->code == 4) {
icmp->type = ICMPV6_PKT_TOO_BIG; icmp->type = ICMPV6_PKT_TOO_BIG;
icmp->code = 0; icmp->code = 0;
/* here to write new 4B MTU value */
icmp_extra = (unsigned int *) icmp_extra = (unsigned int *)
((char *) icmp + ((char *) icmp +
sizeof(struct s_icmp)); sizeof(struct s_icmp));
/* from here read original 2B MTU value
* after skipping 2 unused bytes */
icmp_extra_s = (unsigned short *) icmp_extra_s = (unsigned short *)
((char *) icmp + ((char *) icmp +
sizeof(struct s_icmp) + 2); 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 = htonl(
*icmp_extra_s + 20 < ntohs(*icmp_extra_s) +
mtu ? (unsigned int) 20 < mtu ?
*icmp_extra_s + 20 : (unsigned int)
(ntohs(*icmp_extra_s) +
20) :
(unsigned int) mtu); (unsigned int) mtu);
/* does not => leverage packet length */
} else { } else {
/* RFC1191 */ /* RFC1191 */
/* NOTE: >= would cause infinite /* NOTE: >= would cause infinite
@ -186,17 +192,19 @@ int icmp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4,
if (ntohs(eip4->len) > if (ntohs(eip4->len) >
1006) { 1006) {
*icmp_extra = *icmp_extra =
htonl(1006); htonl(1006 +
20);
} else if (ntohs(eip4->len) > } else if (ntohs(eip4->len) >
508) { 508) {
*icmp_extra = *icmp_extra =
htonl(508); htonl(508 + 20);
} else if (ntohs(eip4->len) > } else if (ntohs(eip4->len) >
296) { 296) {
*icmp_extra = *icmp_extra =
htonl(296); htonl(296 + 20);
} else { } else {
*icmp_extra = htonl(68); *icmp_extra =
htonl(68 + 20);
} }
} }
} else if (icmp->code == 3) { } 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 */ /* decide the type of the ICMP packet */
switch (icmp->type) { switch (icmp->type) {
case ICMPV6_ECHO_REQUEST: case ICMPV6_ECHO_REQUEST:
/* this option is already sanitized */
echo = (struct s_icmp_echo *) icmp_data; echo = (struct s_icmp_echo *) icmp_data;
connection = nat_out(nat6_icmp, nat4_icmp, eth6->src, 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 */ /* sanity check */
if (payload_size < sizeof(struct s_icmp) + if (payload_size < sizeof(struct s_icmp) +
sizeof(struct s_icmp_ndp_ns)) { sizeof(struct s_icmp_ndp_ns)) {
log_debug("Too short NDP NS"); log_debug("Too short packet for NDP NS");
return 1; return 1;
} }
@ -561,7 +571,8 @@ int icmp_ipv6(struct s_ethernet *eth6, struct s_ipv6 *ip6, char *payload)
/* sanity check */ /* sanity check */
if (payload_size < sizeof(struct s_icmp) + 4 + if (payload_size < sizeof(struct s_icmp) + 4 +
sizeof(struct s_ipv6)) { sizeof(struct s_ipv6)) {
log_debug("Too short ICMPv6 packet 6"); log_debug("Too short ICMPv6 packet"
"PacketTooBig");
return 1; return 1;
} }
@ -897,13 +908,13 @@ int sub_icmp4_error(char *payload, unsigned short payload_size,
return 1; return 1;
} }
payload_size -= eip4_hlen; payload_size -= eip4_hlen;
payload += eip4_hlen; payload += eip4_hlen;
/* define new inner IPv6 header */ /* define new inner IPv6 header */
/* new_len+4+4+40=102 < 1280 */ /* new_len+4+4+40=102 < 1280 */
eip6 = (struct s_ipv6 *) (packet + *packet_len + sizeof(struct s_icmp) + eip6 = (struct s_ipv6 *) (packet + *packet_len + sizeof(struct s_icmp) +
4); 4);
/* we'll need this right now */ /* we'll need this right now */
ipv4_to_ipv6(&eip4->ip_dest, &eip6->ip_dest); ipv4_to_ipv6(&eip4->ip_dest, &eip6->ip_dest);
@ -955,7 +966,8 @@ int sub_icmp4_error(char *payload, unsigned short payload_size,
/* else: */ /* else: */
/* sanity check */ /* 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"); log_debug("Too short ICMPv4 packet 4");
return 1; return 1;
} }
@ -986,7 +998,7 @@ int sub_icmp4_error(char *payload, unsigned short payload_size,
} }
/* copy ICMP header to new packet */ /* 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); *icmp = (struct s_icmp *) (packet + *packet_len);
/* complete inner IPv6 header */ /* complete inner IPv6 header */
@ -1074,7 +1086,7 @@ int sub_icmp6_error(struct s_ethernet *eth6,
unsigned char skip_l4 = 0; unsigned char skip_l4 = 0;
/* sanity check */ /* sanity check; redundant only for rare 'Packet too big' */
if (payload_size < sizeof(struct s_ipv6)) { if (payload_size < sizeof(struct s_ipv6)) {
log_debug("Too short ICMPv6 packet 2"); log_debug("Too short ICMPv6 packet 2");
return 1; return 1;
@ -1103,6 +1115,7 @@ int sub_icmp6_error(struct s_ethernet *eth6,
eip4->id = htons(ntohl(eip6_frag->id)); eip4->id = htons(ntohl(eip6_frag->id));
if (ntohs(eip6_frag->offset_flag) & 0xfff8) { if (ntohs(eip6_frag->offset_flag) & 0xfff8) {
/* this is not the first fragment */
skip_l4 = 1; skip_l4 = 1;
} }
if (ntohs(eip6_frag->offset_flag) & IPV6_FLAG_MORE_FRAGMENTS) { 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 += sizeof(struct s_ipv6_fragment);
payload_size -= sizeof(struct s_ipv6_fragment); payload_size -= sizeof(struct s_ipv6_fragment);
} else { } else {
eip4->id = 0x0;
eip4->flags_offset = htons(IPV4_FLAG_DONT_FRAGMENT);
}
/* look for the original connection */
if (skip_l4 == 0) {
/* sanity check */ /* sanity check */
/* 4 B -> L4 addrs */ /* 4 B -> L4 addrs */
if (payload_size < 4) { if (payload_size < 4) {
@ -1125,12 +1144,6 @@ int sub_icmp6_error(struct s_ethernet *eth6,
return 1; 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) { switch (eip6->next_header) {
case IPPROTO_TCP: case IPPROTO_TCP:
etcp = (struct s_tcp *) payload; 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); memcpy(packet + *packet_len, *icmp, sizeof(struct s_icmp) + 4);
*icmp = (struct s_icmp *) (packet + *packet_len); *icmp = (struct s_icmp *) (packet + *packet_len);

View File

@ -31,16 +31,16 @@
* *
* @param eth Ethernet header * @param eth Ethernet header
* @param packet Packet data * @param packet Packet data
* @param length Packet data length
* *
* @return 0 for success * @return 0 for success
* @return 1 for failure * @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; struct s_ipv4 *ip;
char *payload; char *payload;
unsigned short header_size; unsigned short header_size;
unsigned short data_size;
/* load IP header */ /* load IP header */
ip = (struct s_ipv4 *) packet; ip = (struct s_ipv4 *) packet;
@ -51,25 +51,34 @@ int ipv4(struct s_ethernet *eth, char *packet)
} }
/* compute sizes and get payload */ /* compute sizes and get payload */
header_size = (ip->ver_hdrlen & 0x0f) * 4; /* # of 4 byte words */ header_size = (ip->ver_hdrlen & 0x0f) * 4; /* # of 4-byte words */
data_size = htons(ip->len) - header_size;
/* 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; payload = packet + header_size;
/* check and decrease TTL */ /* check and decrease TTL */
if (ip->ttl <= 1) { if (ip->ttl <= 1) {
/* deny this error for ICMP (except ping/pong) /* deny this error for ICMP (except ping/pong)
* and for non-first fragments */ * and for non-first fragments */
if ((ip->proto != IPPROTO_ICMP || payload[0] & 0x0 || if ((ip->proto != IPPROTO_ICMP ||
payload[0] & 0x08) && !(ip->flags_offset & htons(0x1fff))) { payload[0] == ICMPV4_ECHO_REPLY ||
/* code 0 = ttl exceeded in transmit */ 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, icmp4_error(ip->ip_src, ICMPV4_TIME_EXCEEDED, 0,
packet, htons(ip->len)); packet, length);
} }
return 1; return 1;
} else { } else {
ip->ttl--; ip->ttl--;
} }
#define data_size length - header_size
switch (ip->proto) { switch (ip->proto) {
case IPPROTO_TCP: case IPPROTO_TCP:
log_debug("IPv4 Protocol: TCP"); log_debug("IPv4 Protocol: TCP");
@ -85,4 +94,5 @@ int ipv4(struct s_ethernet *eth, char *packet)
ip->proto, ip->proto); ip->proto, ip->proto);
return 1; return 1;
} }
#undef data_size
} }

View File

@ -1,6 +1,6 @@
/* /*
* WrapSix * WrapSix
* Copyright (C) 2008-2013 xHire <xhire@wrapsix.org> * Copyright (C) 2008-2017 xHire <xhire@wrapsix.org>
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -64,6 +64,6 @@ struct s_ipv4_pseudo_delta {
address */ address */
} __attribute__ ((__packed__)); } __attribute__ ((__packed__));
int ipv4(struct s_ethernet *eth, char *packet); int ipv4(struct s_ethernet *eth, char *packet, unsigned short length);
#endif /* IPV4_H */ #endif /* IPV4_H */

View File

@ -31,11 +31,12 @@
* *
* @param eth Ethernet header * @param eth Ethernet header
* @param packet Packet data * @param packet Packet data
* @param length Packet data length
* *
* @return 0 for success * @return 0 for success
* @return 1 for failure * @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; struct s_ipv6 *ip;
char *payload; char *payload;
@ -44,6 +45,12 @@ int ipv6(struct s_ethernet *eth, char *packet)
ip = (struct s_ipv6 *) packet; ip = (struct s_ipv6 *) packet;
payload = packet + sizeof(struct s_ipv6); 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 */ /* test if this packet belongs to us */
if (memcmp(&wrapsix_ipv6_prefix, &ip->ip_dest, 12) != 0 && if (memcmp(&wrapsix_ipv6_prefix, &ip->ip_dest, 12) != 0 &&
memcmp(&ndp_multicast_addr, &ip->ip_dest, 13) != 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--; ip->hop_limit--;
} }
#define data_size length - sizeof(struct s_ipv6)
switch (ip->next_header) { switch (ip->next_header) {
case IPPROTO_TCP: case IPPROTO_TCP:
log_debug("IPv6 Protocol: TCP"); log_debug("IPv6 Protocol: TCP");
return tcp_ipv6(eth, ip, payload); return tcp_ipv6(eth, ip, payload, data_size);
case IPPROTO_UDP: case IPPROTO_UDP:
log_debug("IPv6 Protocol: UDP"); log_debug("IPv6 Protocol: UDP");
return udp_ipv6(eth, ip, payload); return udp_ipv6(eth, ip, payload, data_size);
case IPPROTO_ICMPV6: case IPPROTO_ICMPV6:
log_debug("IPv6 Protocol: ICMP"); log_debug("IPv6 Protocol: ICMP");
return icmp_ipv6(eth, ip, payload); return icmp_ipv6(eth, ip, payload, data_size);
default: default:
log_debug("IPv6 Protocol: unknown [%d/0x%x]", log_debug("IPv6 Protocol: unknown [%d/0x%x]",
ip->next_header, ip->next_header); ip->next_header, ip->next_header);
return 1; return 1;
} }
#undef data_size
} }

View File

@ -1,6 +1,6 @@
/* /*
* WrapSix * WrapSix
* Copyright (C) 2008-2013 xHire <xhire@wrapsix.org> * Copyright (C) 2008-2017 xHire <xhire@wrapsix.org>
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -68,6 +68,6 @@ struct s_ipv6_pseudo_delta {
address */ address */
} __attribute__ ((__packed__)); } __attribute__ ((__packed__));
int ipv6(struct s_ethernet *eth, char *packet); int ipv6(struct s_ethernet *eth, char *packet, unsigned short length);
#endif /* IPV6_H */ #endif /* IPV6_H */

View File

@ -36,11 +36,13 @@
* Processing of incoming TCPv4 packets. Directly sends translated TCPv6 * Processing of incoming TCPv4 packets. Directly sends translated TCPv6
* packets. * packets.
* *
* The IPv4 packet, although split into several parts, is expected to be stored
* in a continuous memory.
*
* @param eth4 Ethernet header * @param eth4 Ethernet header
* @param ip4 IPv4 header * @param ip4 IPv4 header
* @param payload TCPv4 data * @param payload TCPv4 data
* @param payload_size Size of payload; needed because IPv4 header has * @param payload_size Size of the data payload
* dynamic length
* *
* @return 0 for success * @return 0 for success
* @return 1 for failure * @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)) == if ((ip4->flags_offset | htons(IPV4_FLAG_DONT_FRAGMENT)) ==
htons(IPV4_FLAG_DONT_FRAGMENT) || htons(IPV4_FLAG_DONT_FRAGMENT) ||
((ip4->flags_offset & htons(IPV4_FLAG_MORE_FRAGMENTS)) && ((ip4->flags_offset & htons(IPV4_FLAG_MORE_FRAGMENTS)) &&
(ip4->flags_offset & htons(0x1fff)) == 0x0000 && (ip4->flags_offset & htons(0x1fff)) == 0x0000)) {
payload_size >= sizeof(struct s_tcp))) { /* sanity check */
if (payload_size < sizeof(struct s_tcp)) {
log_debug("Too short TCPv4 packet");
return 1;
}
/* parse TCP header */ /* parse TCP header */
tcp = (struct s_tcp *) payload; 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) { if (tcp->checksum != tmp_short) {
/* packet is corrupted and shouldn't be /* packet is corrupted and shouldn't be
* processed */ * processed */
log_debug("Wrong checksum"); log_debug("Wrong TCPv4 checksum");
return 1; 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); tcp->port_src, tcp->port_dest);
if (connection == NULL) { if (connection == NULL) {
log_debug("Incoming connection wasn't found in NAT"); log_debug("Incoming TCP connection wasn't found in "
"NAT");
return 1; 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 */ /* check if there are any saved fragments */
if (frag_conn->queue != NULL) { if (frag_conn->queue != NULL) {
log_debug("Processing TCP fragments of %d", log_debug("Processing TCPv4 fragments of %d",
ip4->id); ip4->id);
llnode = frag_conn->queue->first.next; llnode = frag_conn->queue->first.next;
while (llnode->next != NULL) { while (llnode->next != NULL) {
llnode = llnode->next; llnode = llnode->next;
memcpy(&tmp_short, llnode->prev->data, /* first is fragment size */
sizeof(unsigned short));
tcp_ipv4((struct s_ethernet *) ( tcp_ipv4((struct s_ethernet *) (
(char *) llnode->prev->data + (char *) llnode->prev->data +
sizeof(unsigned short)), sizeof(unsigned short)),
@ -188,7 +195,8 @@ int tcp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, char *payload,
sizeof(unsigned short) + sizeof(unsigned short) +
sizeof(struct s_ethernet) + sizeof(struct s_ethernet) +
sizeof(struct s_ipv4), sizeof(struct s_ipv4),
tmp_short); *((unsigned short *)
llnode->prev->data));
free(llnode->prev->data); free(llnode->prev->data);
linkedlist_delete(frag_conn->queue, linkedlist_delete(frag_conn->queue,
llnode->prev); llnode->prev);
@ -289,7 +297,7 @@ int tcp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, char *payload,
} }
if (frag_conn->connection == NULL) { 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"); "fragments table -- saving it");
if ((saved_packet = (char *) malloc( 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, memcpy(saved_packet, &payload_size,
sizeof(unsigned short)); sizeof(unsigned short));
memcpy(saved_packet + sizeof(unsigned short), memcpy(saved_packet + sizeof(unsigned short),
(char *) eth4, sizeof(struct s_ethernet)); (char *) eth4, sizeof(struct s_ethernet) +
memcpy(saved_packet + sizeof(unsigned short) + sizeof(struct s_ipv4));
sizeof(struct s_ethernet), /* just in case the original IPv4 header had options */
(char *) ip4, sizeof(struct s_ipv4));
memcpy(saved_packet + sizeof(unsigned short) + memcpy(saved_packet + sizeof(unsigned short) +
sizeof(struct s_ethernet) + sizeof(struct s_ethernet) +
sizeof(struct s_ipv4), payload, payload_size); 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 */ /* build IPv6 fragment header */
frag->next_header = IPPROTO_TCP; frag->next_header = IPPROTO_TCP;
frag->zeros = 0x0; frag->zeros = 0x0;
frag->id = htonl(htons(ip4->id)); frag->id = htonl(ntohs(ip4->id));
/* fragment the fragment or not? */ /* fragment the fragment or not? */
if (payload_size > mtu - sizeof(struct s_ipv6) - 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)); sizeof(struct s_ipv6_fragment));
/* fill in missing IPv6 fragment header fields */ /* fill in missing IPv6 fragment header fields */
frag->offset_flag = htons((htons(ip4->flags_offset) << frag->offset_flag = htons((ntohs(ip4->flags_offset) <<
3) | 3) |
IPV6_FLAG_MORE_FRAGMENTS); IPV6_FLAG_MORE_FRAGMENTS);
/* copy the payload data */ /* 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 + ip6->len = htons(payload_size +
sizeof(struct s_ipv6_fragment) - sizeof(struct s_ipv6_fragment) -
FRAGMENT_LEN); FRAGMENT_LEN);
frag->offset_flag = htons(((htons(ip4->flags_offset) & frag->offset_flag = htons(((ntohs(ip4->flags_offset) &
0x1fff) + 0x1fff) +
FRAGMENT_LEN / 8) << 3); FRAGMENT_LEN / 8) << 3);
if (ip4->flags_offset & if (ip4->flags_offset &
htons(IPV4_FLAG_MORE_FRAGMENTS)) { htons(IPV4_FLAG_MORE_FRAGMENTS)) {
frag->offset_flag |= 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)); sizeof(struct s_ipv6_fragment));
/* fill in missing IPv6 fragment header fields */ /* fill in missing IPv6 fragment header fields */
frag->offset_flag = htons(htons(ip4->flags_offset) << frag->offset_flag = htons(ntohs(ip4->flags_offset) <<
3); 3);
if (ip4->flags_offset & if (ip4->flags_offset &
htons(IPV4_FLAG_MORE_FRAGMENTS)) { 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 eth6 Ethernet header
* @param ip6 IPv6 header * @param ip6 IPv6 header
* @param payload TCPv6 data * @param payload TCPv6 data
* @param payload_size Size of the data payload (L4+)
* *
* @return 0 for success * @return 0 for success
* @return 1 for failure * @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_tcp *tcp;
struct s_nat *connection; 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; struct s_ipv4 *ip4;
char packet[PACKET_BUFFER]; char packet[PACKET_BUFFER];
/* sanity check */
if (payload_size < sizeof(struct s_tcp)) {
log_debug("Too short TCPv6 packet");
return 1;
}
/* parse TCP header */ /* parse TCP header */
tcp = (struct s_tcp *) payload; 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; orig_checksum = tcp->checksum;
tcp->checksum = 0; tcp->checksum = 0;
tcp->checksum = checksum_ipv6(ip6->ip_src, ip6->ip_dest, tcp->checksum = checksum_ipv6(ip6->ip_src, ip6->ip_dest,
htons(ip6->len), IPPROTO_TCP, payload_size, IPPROTO_TCP,
(char *) payload); (char *) payload);
if (tcp->checksum != orig_checksum) { 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->ver_hdrlen = 0x45; /* ver 4, header length 20 B */
ip4->tos = ((ip6->ver & 0x0f) << 4) | ip4->tos = ((ip6->ver & 0x0f) << 4) |
((ip6->traffic_class & 0xf0) >> 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->id = 0x0;
ip4->flags_offset = htons(IPV4_FLAG_DONT_FRAGMENT); ip4->flags_offset = htons(IPV4_FLAG_DONT_FRAGMENT);
ip4->ttl = ip6->hop_limit; 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); connection->ipv4_port_src);
/* copy the payload data (with new checksum) */ /* 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 */ /* compute IPv4 checksum */
ip4->checksum = 0x0; ip4->checksum = 0x0;
ip4->checksum = checksum(ip4, sizeof(struct s_ipv4)); ip4->checksum = checksum(ip4, sizeof(struct s_ipv4));
/* send translated packet */ /* 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; return 0;
} }

View File

@ -1,6 +1,6 @@
/* /*
* WrapSix * WrapSix
* Copyright (C) 2008-2012 xHire <xhire@wrapsix.org> * Copyright (C) 2008-2017 xHire <xhire@wrapsix.org>
* *
* This program is free software: you can redistribute it and/or modify * 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 * 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, int tcp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, char *payload,
unsigned short payload_size); 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 */ #endif /* TCP_H */

View File

@ -38,8 +38,7 @@
* @param eth4 Ethernet header * @param eth4 Ethernet header
* @param ip4 IPv4 header * @param ip4 IPv4 header
* @param payload UDPv4 data * @param payload UDPv4 data
* @param payload_size Size of payload; needed because IPv4 header has * @param payload_size Size of the data payload
* dynamic length
* *
* @return 0 for success * @return 0 for success
* @return 1 for failure * @return 1 for failure
@ -58,6 +57,13 @@ int udp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, char *payload,
/* parse UDP header */ /* parse UDP header */
udp = (struct s_udp *) payload; 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 */ /* checksum recheck */
if (udp->checksum != 0x0000) { if (udp->checksum != 0x0000) {
orig_checksum = udp->checksum; 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 eth6 Ethernet header
* @param ip6 IPv6 header * @param ip6 IPv6 header
* @param payload UDPv6 data * @param payload UDPv6 data
* @param payload_size Size of the data payload
* *
* @return 0 for success * @return 0 for success
* @return 1 for failure * @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_udp *udp;
struct s_nat *connection; struct s_nat *connection;
@ -154,11 +162,18 @@ int udp_ipv6(struct s_ethernet *eth6, struct s_ipv6 *ip6, char *payload)
/* parse UDP header */ /* parse UDP header */
udp = (struct s_udp *) payload; 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 */ /* checksum recheck */
orig_checksum = udp->checksum; orig_checksum = udp->checksum;
udp->checksum = 0; udp->checksum = 0;
udp->checksum = checksum_ipv6(ip6->ip_src, ip6->ip_dest, udp->checksum = checksum_ipv6(ip6->ip_src, ip6->ip_dest, payload_size,
htons(ip6->len), IPPROTO_UDP, payload); IPPROTO_UDP, payload);
if (udp->checksum != orig_checksum) { if (udp->checksum != orig_checksum) {
/* packet is corrupted and shouldn't be processed */ /* 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 */ /* translated packet */
ip4 = (struct s_ipv4 *) 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 */ /* build IPv4 packet */
ip4->ver_hdrlen = 0x45; /* ver 4, header length 20 B */ 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); connection->ipv4_port_src);
/* copy the payload data (with new checksum) */ /* 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 */ /* compute IPv4 checksum */
ip4->checksum = 0x0; ip4->checksum = 0x0;

View File

@ -1,6 +1,6 @@
/* /*
* WrapSix * WrapSix
* Copyright (C) 2008-2012 xHire <xhire@wrapsix.org> * Copyright (C) 2008-2017 xHire <xhire@wrapsix.org>
* *
* This program is free software: you can redistribute it and/or modify * 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 * 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, int udp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, char *payload,
unsigned short payload_size); 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 */ #endif /* UDP_H */

View File

@ -24,9 +24,11 @@
#include <net/if.h> /* struct ifreq */ #include <net/if.h> /* struct ifreq */
#include <netinet/in.h> /* htons */ #include <netinet/in.h> /* htons */
#include <netpacket/packet.h> /* struct packet_mreq, struct sockaddr_ll */ #include <netpacket/packet.h> /* struct packet_mreq, struct sockaddr_ll */
#include <stdio.h> /* perror */
#include <stdlib.h> /* srand */ #include <stdlib.h> /* srand */
#include <string.h> /* strncpy */ #include <string.h> /* strncpy */
#include <sys/ioctl.h> /* ioctl, SIOCGIFINDEX */ #include <sys/ioctl.h> /* ioctl, SIOCGIFINDEX */
#include <sys/types.h> /* caddr_t */
#include <time.h> /* time, time_t */ #include <time.h> /* time, time_t */
#include <unistd.h> /* close */ #include <unistd.h> /* close */
@ -36,6 +38,7 @@
#endif /* HAVE_CONFIG_H */ #endif /* HAVE_CONFIG_H */
#include "config.h" #include "config.h"
#include "ethernet.h" #include "ethernet.h"
#include "icmp.h"
#include "ipv4.h" #include "ipv4.h"
#include "ipv6.h" #include "ipv6.h"
#include "log.h" #include "log.h"
@ -53,7 +56,7 @@ struct s_ipv4_addr wrapsix_ipv4_addr;
struct s_ipv6_addr host_ipv6_addr; struct s_ipv6_addr host_ipv6_addr;
struct s_ipv4_addr host_ipv4_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) int main(int argc, char **argv)
{ {
@ -177,13 +180,21 @@ int main(int argc, char **argv)
/* sniff! :c) */ /* sniff! :c) */
for (i = 1;; i++) { for (i = 1;; i++) {
if ((length = recv(sniff_sock, buffer, PACKET_BUFFER, 0)) == length = recv(sniff_sock, buffer, PACKET_BUFFER, MSG_TRUNC);
-1) { if (length == -1) {
perror("recv");
log_error("Unable to retrieve data from socket"); log_error("Unable to retrieve data from socket");
return 1; 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) { if (i % 250000) {
curtime = time(NULL); curtime = time(NULL);
@ -217,29 +228,46 @@ int main(int argc, char **argv)
return 0; 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 */ struct s_ethernet *eth;
char *payload; /* the IP header + packet
payload */ /* 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 */ /* parse ethernet header */
eth = (struct s_ethernet *) (packet); eth = (struct s_ethernet *) packet;
payload = packet + sizeof(struct s_ethernet);
#define payload packet + sizeof(struct s_ethernet)
#define payload_length length - sizeof(struct s_ethernet)
switch (htons(eth->type)) { switch (htons(eth->type)) {
case ETHERTYPE_IP: case ETHERTYPE_IP:
return ipv4(eth, payload); return ipv4(eth, payload, payload_length);
case ETHERTYPE_IPV6: case ETHERTYPE_IPV6:
return ipv6(eth, payload); return ipv6(eth, payload, payload_length);
case ETHERTYPE_ARP: case ETHERTYPE_ARP:
log_debug("HW Protocol: ARP"); return arp(eth, payload, payload_length);
return arp(eth, payload);
default: default:
log_debug("HW Protocol: unknown [%d/0x%04x]", log_debug("HW Protocol: unknown [%d/0x%04x]",
htons(eth->type), htons(eth->type)); htons(eth->type), htons(eth->type));
return 1; return 1;
} }
#undef payload_length
#undef payload
} }
/** /**