summaryrefslogtreecommitdiff
path: root/xen/net.c
diff options
context:
space:
mode:
Diffstat (limited to 'xen/net.c')
-rw-r--r--xen/net.c97
1 files changed, 86 insertions, 11 deletions
diff --git a/xen/net.c b/xen/net.c
index dd903eda..10e4bbef 100644
--- a/xen/net.c
+++ b/xen/net.c
@@ -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. */