summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.target2
-rw-r--r--net-checksum.c87
-rw-r--r--net.h7
3 files changed, 95 insertions, 1 deletions
diff --git a/Makefile.target b/Makefile.target
index ff105c1fa..42162c393 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -472,7 +472,7 @@ endif #CONFIG_DARWIN_USER
# System emulator target
ifndef CONFIG_USER_ONLY
-OBJS=vl.o osdep.o monitor.o pci.o loader.o isa_mmio.o machine.o
+OBJS=vl.o osdep.o monitor.o pci.o loader.o isa_mmio.o machine.o net-checksum.o
ifdef CONFIG_WIN32
OBJS+=block-raw-win32.o
else
diff --git a/net-checksum.c b/net-checksum.c
new file mode 100644
index 000000000..a4806558e
--- /dev/null
+++ b/net-checksum.c
@@ -0,0 +1,87 @@
+/*
+ * IP checksumming functions.
+ * (c) 2008 Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * 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
+ * the Free Software Foundation; under version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "hw/hw.h"
+#include "net.h"
+
+#define PROTO_TCP 6
+#define PROTO_UDP 17
+
+uint32_t net_checksum_add(int len, uint8_t *buf)
+{
+ uint32_t sum = 0;
+ int i;
+
+ for (i = 0; i < len; i++) {
+ if (i & 1)
+ sum += (uint32_t)buf[i];
+ else
+ sum += (uint32_t)buf[i] << 8;
+ }
+ return sum;
+}
+
+uint16_t net_checksum_finish(uint32_t sum)
+{
+ while (sum>>16)
+ sum = (sum & 0xFFFF)+(sum >> 16);
+ return ~sum;
+}
+
+uint16_t net_checksum_tcpudp(uint16_t length, uint16_t proto,
+ uint8_t *addrs, uint8_t *buf)
+{
+ uint32_t sum = 0;
+
+ sum += net_checksum_add(length, buf); // payload
+ sum += net_checksum_add(8, addrs); // src + dst address
+ sum += proto + length; // protocol & length
+ return net_checksum_finish(sum);
+}
+
+void net_checksum_calculate(uint8_t *data, int length)
+{
+ int hlen, plen, proto, csum_offset;
+ uint16_t csum;
+
+ if ((data[14] & 0xf0) != 0x40)
+ return; /* not IPv4 */
+ hlen = (data[14] & 0x0f) * 4;
+ plen = (data[16] << 8 | data[17]) - hlen;
+ proto = data[23];
+
+ switch (proto) {
+ case PROTO_TCP:
+ csum_offset = 16;
+ break;
+ case PROTO_UDP:
+ csum_offset = 6;
+ break;
+ default:
+ return;
+ }
+
+ if (plen < csum_offset+2)
+ 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;
+}
diff --git a/net.h b/net.h
index d00910f28..5212b4832 100644
--- a/net.h
+++ b/net.h
@@ -48,4 +48,11 @@ struct NICInfo {
extern int nb_nics;
extern NICInfo nd_table[MAX_NICS];
+/* checksumming functions (net-checksum.c) */
+uint32_t net_checksum_add(int len, uint8_t *buf);
+uint16_t net_checksum_finish(uint32_t sum);
+uint16_t net_checksum_tcpudp(uint16_t length, uint16_t proto,
+ uint8_t *addrs, uint8_t *buf);
+void net_checksum_calculate(uint8_t *data, int length);
+
#endif