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>
This commit is contained in:
		
							parent
							
								
									7c375e2294
								
							
						
					
					
						commit
						50dbce6538
					
				@ -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)
 | 
			
		||||
    ip = (struct ip_header *)(data + sizeof(struct eth_header));
 | 
			
		||||
 | 
			
		||||
    if (IP_HEADER_VERSION(ip) != IP_HEADER_VERSION_4) {
 | 
			
		||||
        return; /* not IPv4 */
 | 
			
		||||
    hlen  = (data[14] & 0x0f) * 4;
 | 
			
		||||
    plen  = (data[16] << 8 | data[17]) - hlen;
 | 
			
		||||
    proto = data[23];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
    switch (proto) {
 | 
			
		||||
    case PROTO_TCP:
 | 
			
		||||
	csum_offset = 16;
 | 
			
		||||
        break;
 | 
			
		||||
    case PROTO_UDP:
 | 
			
		||||
	csum_offset = 6;
 | 
			
		||||
    }
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user