diff options
Diffstat (limited to 'xen/net.c')
-rw-r--r-- | xen/net.c | 97 |
1 files changed, 86 insertions, 11 deletions
@@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2009, 2011 Samuel Thibault <samuel.thibault@ens-lyon.org> + * Copyright (C) 2006-2009, 2011 Free Software Foundation * * 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 @@ -18,6 +18,7 @@ #include <sys/types.h> #include <mach/mig_errors.h> +#include <kern/kalloc.h> #include <ipc/ipc_port.h> #include <ipc/ipc_space.h> #include <vm/vm_kern.h> @@ -117,6 +118,61 @@ static void enqueue_rx_buf(struct net_data *nd, int number) { req->gref = nd->rx_buf_gnt[number] = gref; } +static int recompute_checksum(void *data, int len) { + unsigned16_t *header16 = data; + unsigned8_t *header8 = data; + unsigned length, i; + unsigned32_t checksum = 0; + + /* IPv4 header length */ + length = (header8[0] & 0xf) * 4; + if (length < 20) + /* Too small for an IP header16 */ + return -1; + if (length > len) + /* Does not fit in the ethernet frame */ + return -1; + + /* Compute IP header checksum */ + header16[5] = 0; + for (i = 0; i < length/2; i++) + checksum += ntohs(header16[i]); + + while (checksum >> 16) + checksum = (checksum & 0xffff) + (checksum >> 16); + + header16[5] = htons(~checksum); + + if (header8[9] == 6) { + /* Need to fix TCP checksum as well */ + unsigned16_t *tcp_header16 = header16 + length/2; + unsigned8_t *tcp_header8 = header8 + length; + unsigned tcp_length = ntohs(header16[1]) - length; + + /* Pseudo IP header */ + checksum = ntohs(header16[6]) + ntohs(header16[7]) + + ntohs(header16[8]) + ntohs(header16[9]) + + header8[9] + tcp_length; + + tcp_header16[8] = 0; + for (i = 0; i < tcp_length / 2; i++) + checksum += ntohs(tcp_header16[i]); + if (tcp_length & 1) + checksum += tcp_header8[tcp_length-1] << 8; + + while (checksum >> 16) + checksum = (checksum & 0xffff) + (checksum >> 16); + + tcp_header16[8] = htons(~checksum); + } else if (header8[9] == 17) { + /* Drop any bogus checksum */ + unsigned16_t *udp_header16 = header16 + length/2; + udp_header16[3] = 0; + } + + return 0; +} + static void hyp_net_intr(int unit) { ipc_kmsg_t kmsg; struct ether_header *eh; @@ -172,6 +228,25 @@ static void hyp_net_intr(int unit) { data = nd->rx_buf[number] + rx_rsp->offset; len = rx_rsp->status; + if (rx_rsp->flags & NETRXF_csum_blank) { + struct ether_header *ether = data; + + if (!(rx_rsp->flags & NETRXF_data_validated)) { + printf("packet with no checksum and not validated, dropping it\n"); + goto drop_kmsg; + } + + /* TODO: rather tell pfinet to ignore checksums */ + + if (ntohs(ether->ether_type) != 0x0800) { + printf("packet with no checksum and not IPv4, dropping it\n"); + goto drop_kmsg; + } + + if (recompute_checksum(data + sizeof(*ether), len - sizeof(*ether))) + goto drop_kmsg; + } + eh = (void*) (net_kmsg(kmsg)->header); ph = (void*) (net_kmsg(kmsg)->packet); memcpy(eh, data, sizeof (struct ether_header)); @@ -283,12 +358,12 @@ void hyp_net_init(void) { t = hyp_store_transaction_start(); /* Get a page for tx_ring */ - if (kmem_alloc_wired(kernel_map, &addr, PAGE_SIZE) != KERN_SUCCESS) + if ((addr = vm_page_grab_phys_addr()) == -1) panic("eth: couldn't allocate space for store tx_ring"); - tx_ring = (void*) addr; + tx_ring = (void*) phystokv(addr); SHARED_RING_INIT(tx_ring); FRONT_RING_INIT(&nd->tx, tx_ring, PAGE_SIZE); - grant = hyp_grant_give(domid, atop(kvtophys(addr)), 0); + grant = hyp_grant_give(domid, atop(addr), 0); /* and give it to backend. */ i = sprintf(port_name, "%u", grant); @@ -298,12 +373,12 @@ void hyp_net_init(void) { kfree((vm_offset_t) c, strlen(c)+1); /* Get a page for rx_ring */ - if (kmem_alloc_wired(kernel_map, &addr, PAGE_SIZE) != KERN_SUCCESS) + if ((addr = vm_page_grab_phys_addr()) == -1) panic("eth: couldn't allocate space for store tx_ring"); - rx_ring = (void*) addr; + rx_ring = (void*) phystokv(addr); SHARED_RING_INIT(rx_ring); FRONT_RING_INIT(&nd->rx, rx_ring, PAGE_SIZE); - grant = hyp_grant_give(domid, atop(kvtophys(addr)), 0); + grant = hyp_grant_give(domid, atop(addr), 0); /* and give it to backend. */ i = sprintf(port_name, "%u", grant); @@ -396,12 +471,12 @@ void hyp_net_init(void) { /* Get a page for packet reception */ for (i= 0; i<WINDOW; i++) { - if (kmem_alloc_wired(kernel_map, &addr, PAGE_SIZE) != KERN_SUCCESS) + if ((addr = vm_page_grab_phys_addr()) == -1) panic("eth: couldn't allocate space for store tx_ring"); - nd->rx_buf[i] = (void*)phystokv(kvtophys(addr)); - nd->rx_buf_pfn[i] = atop(kvtophys((vm_offset_t)nd->rx_buf[i])); + nd->rx_buf[i] = (void*)phystokv(addr); + nd->rx_buf_pfn[i] = atop(addr); if (!nd->rx_copy) { - if (hyp_do_update_va_mapping(kvtolin(addr), 0, UVMF_INVLPG|UVMF_ALL)) + if (hyp_do_update_va_mapping(kvtolin(nd->rx_buf[i]), 0, UVMF_INVLPG|UVMF_ALL)) panic("eth: couldn't clear rx kv buf %d at %p", i, addr); } /* and enqueue it to backend. */ |