blob: bdba916761baa11a1c35ce3711155c45773ab419 [file] [log] [blame]
/* Copyright (c) 2010 The Regents of the University of California
* Barret Rhoden <brho@cs.berkeley.edu>
* See LICENSE for details.
*
* Arch independent networking infrastructure */
/* Computes an IP checksum over buf. The checksum is the one's-comp of the
* one's complement 16 bit sum of the payload. Anything above 16 bits gets
* added back to the lower 16 bits, which is what the mask and add is doing.
* Check out http://www.netfor2.com/checksum.html for more info.
*
* If you want to start with a sum from something else (like the UDP
* pseudo-header), pass it in as init_sum. */
#include <net.h>
#include <stdio.h>
#include <arch/types.h>
#ifndef FOLD_U32T
#define FOLD_U32T(u) (((u) >> 16) + ((u) & 0x0000ffffUL))
#endif
/* New version of ip_checksum, notice this version does not change it into
* network order. It is useful to keep it in host order for further processing
*/
uint16_t __ip_checksum(void *dataptr, unsigned int len, uint32_t sum)
{
uint8_t *pb = (uint8_t *)dataptr;
uint16_t *ps, t = 0;
int odd = ((uintptr_t)pb & 1);
/* Get aligned to uint16_t */
// this means pb started on some weird address..
// but in our world this should never happen.. since the payload should always be aligned..
// right..
if (odd && len > 0) {
// change the second half of t to what pb is
// and advance pb
((uint8_t *)&t)[1] = *pb++;
len--;
}
/* Add the bulk of the data */
ps = (uint16_t *)(void *)pb;
while (len > 1) {
sum += *ps++;
len -= 2;
}
/* Consume left-over byte, if any */
if (len > 0) {
((uint8_t *)&t)[0] = *(uint8_t *)ps;
}
/* Add end bytes */
sum += t;
/* Fold 32-bit sum to 16 bits
calling this twice is propably faster than if statements... */
sum = FOLD_U32T(sum);
sum = FOLD_U32T(sum);
/* Swap if alignment was odd */
if (odd) {
sum = byte_swap16(sum);
}
return (uint16_t)sum;
}
/* Computes the checksum for the IP header. We could write it in, but for now
* we'll return the checksum (in host-ordering) and have the caller store the
* value. The retval is in host ordering. */
uint16_t ip_checksum(struct ip_hdr *ip_hdr)
{
unsigned int ip_hdr_len = ip_hdr->hdr_len * sizeof(uint32_t);
return ~__ip_checksum(ip_hdr, ip_hdr_len, 0);
}
/* Computes the checksum for the UDP header. We could write it in, but for now
* we'll return the checksum (in host-ordering) and have the caller store the
* value. Note that the UDP header needs info from the IP header (strictly
* speaking, just the src and destination IPs).
* LEGACY: only used for packet that are not in pbuf formats*/
uint16_t udp_checksum(struct ip_hdr *ip_hdr, struct udp_hdr *udp_hdr)
{
/* Add up the info for the UDP pseudo-header */
uint32_t udp_pseudosum = 0;
uint16_t udp_len = ntohs(udp_hdr->length);
udp_pseudosum += (ip_hdr->src_addr & 0xffff);
udp_pseudosum += (ip_hdr->src_addr >> 16);
udp_pseudosum += (ip_hdr->dst_addr & 0xffff);
udp_pseudosum += (ip_hdr->dst_addr >> 16);
udp_pseudosum += (ip_hdr->protocol);
udp_pseudosum += udp_hdr->length;
return ~(__ip_checksum(udp_hdr, udp_len, udp_pseudosum));
}
/* ip addresses need to be network order, protolen and proto are HO */
uint16_t inet_chksum_pseudo(struct pbuf *p, uint32_t src, uint32_t dest, uint8_t proto, uint16_t proto_len) {
uint32_t acc;
uint32_t addr;
struct pbuf *q;
uint8_t swapped;
acc = 0;
swapped = 0;
/* iterate through all pbuf in chain */
for(q = p; q != NULL; q = STAILQ_NEXT(q, next)) {
acc += __ip_checksum(q->payload, q->len, 0);
acc = FOLD_U32T(acc);
if (q->len % 2 != 0) {
swapped = 1 - swapped;
acc = byte_swap16(acc);
}
}
if (swapped) {
acc = byte_swap16(acc);
}
addr = (src);
acc += (addr & 0xffffUL);
acc += ((addr >> 16) & 0xffffUL);
addr = (dest);
acc += (addr & 0xffffUL);
acc += ((addr >> 16) & 0xffffUL);
acc += (uint32_t)htons((uint16_t)proto);
acc += (uint32_t)htons(proto_len);
/* Fold 32-bit sum to 16 bits
calling this twice is propably faster than if statements... */
acc = FOLD_U32T(acc);
acc = FOLD_U32T(acc);
return (uint16_t)~(acc & 0xffffUL);
}
/* Print out a network packet in the same format as tcpdump, making it easier
* to compare */
void dumppacket(unsigned char *buff, size_t len)
{
int i;
for (i=0; i<len; i++) {
if (i%16 == 0)
printk("0x%x\t", i/16);
printk("%02x", buff[i]);
if (i%2 != 0)
printk(" ");
if ((i+1)%16 == 0)
printk("\n");
}
}