net: improve UDP/TCP checksum computation.

* based on Eth, UDP, TCP struct present in eth.h instead of hardcoded
   indexes and sizes.
 * based on various macros present in eth.h.

Signed-off-by: Jean-Christophe Dubois <jcd@tribudubois.net>
Signed-off-by: Jason Wang <jasowang@redhat.com>
master
Jean-Christophe Dubois 2016-05-30 19:25:46 +02:00 committed by Jason Wang
parent 7c375e2294
commit 50dbce6538
1 changed files with 69 additions and 29 deletions

View File

@ -18,9 +18,7 @@
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "net/checksum.h"
#define PROTO_TCP 6
#define PROTO_UDP 17
#include "net/eth.h"
uint32_t net_checksum_add_cont(int len, uint8_t *buf, int seq)
{
@ -57,40 +55,82 @@ uint16_t net_checksum_tcpudp(uint16_t length, uint16_t proto,
void net_checksum_calculate(uint8_t *data, int length)
{
int hlen, plen, proto, csum_offset;
uint16_t csum;
int ip_len;
struct ip_header *ip;
/*
* Note: We cannot assume "data" is aligned, so the all code uses
* some macros that take care of possible unaligned access for
* struct members (just in case).
*/
/* Ensure data has complete L2 & L3 headers. */
if (length < 14 + 20) {
if (length < (sizeof(struct eth_header) + sizeof(struct ip_header))) {
return;
}
if ((data[14] & 0xf0) != 0x40)
return; /* not IPv4 */
hlen = (data[14] & 0x0f) * 4;
plen = (data[16] << 8 | data[17]) - hlen;
proto = data[23];
ip = (struct ip_header *)(data + sizeof(struct eth_header));
switch (proto) {
case PROTO_TCP:
csum_offset = 16;
break;
case PROTO_UDP:
csum_offset = 6;
break;
if (IP_HEADER_VERSION(ip) != IP_HEADER_VERSION_4) {
return; /* not IPv4 */
}
ip_len = lduw_be_p(&ip->ip_len);
/* Last, check that we have enough data for the all IP frame */
if (length < ip_len) {
return;
}
ip_len -= IP_HDR_GET_LEN(ip);
switch (ip->ip_p) {
case IP_PROTO_TCP:
{
uint16_t csum;
tcp_header *tcp = (tcp_header *)(ip + 1);
if (ip_len < sizeof(tcp_header)) {
return;
}
/* Set csum to 0 */
stw_he_p(&tcp->th_sum, 0);
csum = net_checksum_tcpudp(ip_len, ip->ip_p,
(uint8_t *)&ip->ip_src,
(uint8_t *)tcp);
/* Store computed csum */
stw_be_p(&tcp->th_sum, csum);
break;
}
case IP_PROTO_UDP:
{
uint16_t csum;
udp_header *udp = (udp_header *)(ip + 1);
if (ip_len < sizeof(udp_header)) {
return;
}
/* Set csum to 0 */
stw_he_p(&udp->uh_sum, 0);
csum = net_checksum_tcpudp(ip_len, ip->ip_p,
(uint8_t *)&ip->ip_src,
(uint8_t *)udp);
/* Store computed csum */
stw_be_p(&udp->uh_sum, csum);
break;
}
default:
return;
/* Can't handle any other protocol */
break;
}
if (plen < csum_offset + 2 || 14 + hlen + plen > length) {
return;
}
data[14+hlen+csum_offset] = 0;
data[14+hlen+csum_offset+1] = 0;
csum = net_checksum_tcpudp(plen, proto, data+14+12, data+14+hlen);
data[14+hlen+csum_offset] = csum >> 8;
data[14+hlen+csum_offset+1] = csum & 0xff;
}
uint32_t