1
0
mirror of https://code.semirocket.science/wrapsix synced 2024-09-19 15:01:06 +03:00

Partial checksum updates for TCP and UDP

This commit is contained in:
Michal Zima 2012-04-13 20:04:01 +02:00
parent 88882ea5ca
commit e35adde820
6 changed files with 178 additions and 14 deletions

View File

@ -64,6 +64,49 @@ unsigned short checksum(const void *data, int length)
return ~sum;
}
/**
* General checksum update computation function. Inspired by algorithm
* in RFC3022.
*
* @param old_sum Old checksum
* @param old_data Pointer to old part of data. Must be of even
* number of octets
* @param old_len Length of old data
* @param new_data Pointer to new part of data. Must be of even
* number of octets
* @param new_len Length of new data
*
* @return Updated checksum
*/
unsigned short checksum_update(unsigned short old_sum,
unsigned short *old_data, short old_len,
unsigned short *new_data, short new_len)
{
unsigned int sum;
sum = ~old_sum & 0xffff;
while (old_len) {
sum -= *old_data++;
if (sum & 0x80000000) {
sum--;
sum &= 0xffff;
}
old_len -= 2;
}
while (new_len) {
sum += *new_data++;
if (sum & 0x00010000) {
sum++;
sum &= 0xffff;
}
new_len -= 2;
}
return ~sum & 0xffff;
}
/**
* IPv4 checksum computation function
*
@ -147,3 +190,81 @@ unsigned short checksum_ipv6(struct s_ipv6_addr ip_src,
return sum;
}
/**
* IPv4 checksum update computation function
*
* @param old_sum Old checksum
* @param ip6_src Original source IPv6 address
* @param ip6_dest Original destination IPv6 address
* @param old_port Original transport layer address (port)
* @param ip4_src New source IPv4 address
* @param ip4_dest New destination IPv4 address
* @param new_port New transport layer address (port)
*
* @return Checksum
*/
unsigned short checksum_ipv4_update(unsigned short old_sum,
struct s_ipv6_addr ip6_src,
struct s_ipv6_addr ip6_dest,
unsigned short old_port,
struct s_ipv4_addr ip4_src,
struct s_ipv4_addr ip4_dest,
unsigned short new_port)
{
struct s_ipv4_pseudo_delta delta4;
struct s_ipv6_pseudo_delta delta6;
delta4.ip_src = ip4_src;
delta4.ip_dest = ip4_dest;
delta4.port = new_port;
delta6.ip_src = ip6_src;
delta6.ip_dest = ip6_dest;
delta6.port = old_port;
return checksum_update(old_sum,
(unsigned short *) &delta6,
sizeof(struct s_ipv6_pseudo_delta),
(unsigned short *) &delta4,
sizeof(struct s_ipv4_pseudo_delta));
}
/**
* IPv6 checksum update computation function
*
* @param old_sum Old checksum
* @param ip4_src Original source IPv4 address
* @param ip4_dest Original destination IPv4 address
* @param old_port Original transport layer address (port)
* @param ip6_src New source IPv6 address
* @param ip6_dest New destination IPv6 address
* @param new_port New transport layer address (port)
*
* @return Checksum
*/
unsigned short checksum_ipv6_update(unsigned short old_sum,
struct s_ipv4_addr ip4_src,
struct s_ipv4_addr ip4_dest,
unsigned short old_port,
struct s_ipv6_addr ip6_src,
struct s_ipv6_addr ip6_dest,
unsigned short new_port)
{
struct s_ipv4_pseudo_delta delta4;
struct s_ipv6_pseudo_delta delta6;
delta4.ip_src = ip4_src;
delta4.ip_dest = ip4_dest;
delta4.port = old_port;
delta6.ip_src = ip6_src;
delta6.ip_dest = ip6_dest;
delta6.port = new_port;
return checksum_update(old_sum,
(unsigned short *) &delta4,
sizeof(struct s_ipv4_pseudo_delta),
(unsigned short *) &delta6,
sizeof(struct s_ipv6_pseudo_delta));
}

View File

@ -23,11 +23,28 @@
#include "ipv6.h"
unsigned short checksum(const void *data, int length);
unsigned short checksum_update(unsigned short old_sum,
unsigned short *old_data, short old_len,
unsigned short *new_data, short new_len);
unsigned short checksum_ipv4(struct s_ipv4_addr ip_src,
struct s_ipv4_addr ip_dest, unsigned short length,
unsigned char proto, unsigned char *payload);
unsigned short checksum_ipv6(struct s_ipv6_addr ip_src,
struct s_ipv6_addr ip_dest, unsigned short length,
unsigned char proto, unsigned char *payload);
unsigned short checksum_ipv4_update(unsigned short old_sum,
struct s_ipv6_addr ip6_src,
struct s_ipv6_addr ip6_dest,
unsigned short old_port,
struct s_ipv4_addr ip4_src,
struct s_ipv4_addr ip4_dest,
unsigned short new_port);
unsigned short checksum_ipv6_update(unsigned short old_sum,
struct s_ipv4_addr ip4_src,
struct s_ipv4_addr ip4_dest,
unsigned short old_port,
struct s_ipv6_addr ip6_src,
struct s_ipv6_addr ip6_dest,
unsigned short new_port);
#endif /* CHECKSUM_H */

View File

@ -56,6 +56,13 @@ struct s_ipv4_pseudo {
unsigned short len; /* 16 b; payload length */
} __attribute__ ((__packed__));
/* IPv4 pseudoheader structure for checksum update */
struct s_ipv4_pseudo_delta {
struct s_ipv4_addr ip_src; /* 32 b; source address */
struct s_ipv4_addr ip_dest; /* 32 b; destination address */
unsigned short port; /* 16 b; transport layer address */
} __attribute__ ((__packed__));
int ipv4(struct s_ethernet *eth, char *packet);
#endif /* IPV4_H */

View File

@ -47,6 +47,13 @@ struct s_ipv6_pseudo {
unsigned char next_header; /* 8 b; next header */
} __attribute__ ((__packed__));
/* IPv6 pseudoheader structure for checksum update */
struct s_ipv6_pseudo_delta {
struct s_ipv6_addr ip_src; /* 128 b; source address */
struct s_ipv6_addr ip_dest; /* 128 b; destination address */
unsigned short port; /* 16 b; transport layer address */
} __attribute__ ((__packed__));
int ipv6(struct s_ethernet *eth, char *packet);
#endif /* IPV6_H */

View File

@ -110,9 +110,11 @@ int tcp_ipv4(struct s_ethernet *eth, struct s_ipv4 *ip4, char *payload,
tcp->port_dest = connection->ipv6_port_src;
/* compute TCP checksum */
tcp->checksum = 0x0;
tcp->checksum = checksum_ipv6(ip6->ip_src, ip6->ip_dest, payload_size,
IPPROTO_TCP, (unsigned char *) tcp);
tcp->checksum = checksum_ipv6_update(tcp->checksum,
ip4->ip_src, ip4->ip_dest,
connection->ipv4_port_src,
ip6->ip_src, ip6->ip_dest,
connection->ipv6_port_src);
/* copy the payload data (with new checksum) */
memcpy(packet + sizeof(struct s_ethernet) + sizeof(struct s_ipv6),
@ -198,10 +200,11 @@ int tcp_ipv6(struct s_ethernet *eth, struct s_ipv6 *ip6, char *payload)
tcp->port_src = connection->ipv4_port_src;
/* compute TCP checksum */
tcp->checksum = 0;
tcp->checksum = checksum_ipv4(ip4->ip_src, ip4->ip_dest,
htons(ip6->len), IPPROTO_TCP,
(unsigned char *) tcp);
tcp->checksum = checksum_ipv4_update(tcp->checksum,
ip6->ip_src, ip6->ip_dest,
connection->ipv6_port_src,
ip4->ip_src, ip4->ip_dest,
connection->ipv4_port_src);
/* copy the payload data (with new checksum) */
memcpy(packet + sizeof(struct s_ipv4), payload, htons(ip6->len));

View File

@ -110,9 +110,17 @@ int udp_ipv4(struct s_ethernet *eth, struct s_ipv4 *ip4, char *payload,
udp->port_dest = connection->ipv6_port_src;
/* compute UDP checksum */
udp->checksum = 0x0;
udp->checksum = checksum_ipv6(ip6->ip_src, ip6->ip_dest, payload_size,
IPPROTO_UDP, (unsigned char *) udp);
if (udp->checksum) {
udp->checksum = checksum_ipv6_update(udp->checksum,
ip4->ip_src, ip4->ip_dest,
connection->ipv4_port_src,
ip6->ip_src, ip6->ip_dest,
connection->ipv6_port_src);
} else {
/* if original checksum was 0x0000, we need to compute it */
udp->checksum = checksum_ipv6(ip6->ip_src, ip6->ip_dest, payload_size,
IPPROTO_UDP, (unsigned char *) udp);
}
/* copy the payload data (with new checksum) */
memcpy(packet + sizeof(struct s_ethernet) + sizeof(struct s_ipv6),
@ -198,10 +206,11 @@ int udp_ipv6(struct s_ethernet *eth, struct s_ipv6 *ip6, char *payload)
udp->port_src = connection->ipv4_port_src;
/* compute UDP checksum */
udp->checksum = 0x0;
udp->checksum = checksum_ipv4(ip4->ip_src, ip4->ip_dest,
htons(ip6->len), IPPROTO_UDP,
(unsigned char *) payload);
udp->checksum = checksum_ipv4_update(udp->checksum,
ip6->ip_src, ip6->ip_dest,
connection->ipv6_port_src,
ip4->ip_src, ip4->ip_dest,
connection->ipv4_port_src);
/* copy the payload data (with new checksum) */
memcpy(packet + sizeof(struct s_ipv4), payload, htons(ip6->len));