/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */

/*
 * Copyright (c) 2001-2007 International Computer Science Institute
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software")
 * to deal in the Software without restriction, subject to the conditions
 * listed in the XORP LICENSE file. These conditions include: you must
 * preserve this copyright notice, and you cannot mention the copyright
 * holders in advertising related to the Software without their permission.
 * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
 * notice is a summary of the XORP LICENSE file; the license in that file is
 * legally binding.
 */

#ident "$XORP: xorp/libproto/checksum.c,v 1.2 2007/02/16 22:46:02 pavlin Exp $"


/*
 * Checksum computations.
 */


#include "libproto_module.h"
#include "libxorp/xorp.h"

#include "checksum.h"


/*
 * inet_checksum extracted from:
 *			P I N G . C
 *
 * Author -
 *	Mike Muuss
 *	U. S. Army Ballistic Research Laboratory
 *	December, 1983
 * Modified at Uc Berkeley
 *
 * (ping.c) Status -
 *	Public Domain.  Distribution Unlimited.
 *
 *			I N _ C K S U M
 *
 * Checksum routine for Internet Protocol family headers (C Version)
 *
 */
uint16_t
inet_checksum(const uint8_t *addr, size_t len)
{
	register size_t nleft = len;
	register const uint8_t *w = addr;
	uint16_t answer = 0;
	register uint32_t sum = 0;

	/*
	 *  Our algorithm is simple, using a 32 bit accumulator (sum),
	 *  we add sequential 16 bit words to it, and at the end, fold
	 *  back all the carry bits from the top 16 bits into the lower
	 *  16 bits.
	 */
	while (nleft > 1)  {
		sum += ((w[0] << 8) | w[1]);
		w += 2;
		nleft -= 2;
	}

	/* mop up an odd byte, if necessary */
	if (nleft == 1) {
		/*
		 * XXX: If the number of bytes is odd, we assume a padding
		 * with a zero byte, hence we just "<< 8" the remaining
		 * odd byte.
		 */
		sum += (w[0] << 8);
	}

	/*
	 * add back carry outs from top 16 bits to low 16 bits
	 */
	sum = (sum >> 16) + (sum & 0xffff);	/* add hi 16 to low 16 */
	sum += (sum >> 16);			/* add carry */
	answer = ~sum;				/* truncate to 16 bits */

	/*
	 * XXX: We need to swap the bytes, because we implicitly performed
	 * network-to-host order swapping when we accessed the data.
	 */
	answer = htons(answer);			/* swap the bytes */
	return (answer);
}

/*
 * inet_checksum_add based on inet_cksum extracted from:
 *			P I N G . C
 *
 * Status -
 *	Public Domain.  Distribution Unlimited.
 *
 *			I N _ C K S U M _ A D D
 *
 * Checksum routine for Internet Protocol family headers (C Version):
 *		adds two previously computed checksums.
 *
 */
uint16_t
inet_checksum_add(uint16_t sum1, uint16_t sum2)
{
	register uint32_t sum = (uint16_t)~sum1 + (uint16_t)~sum2;
	uint16_t answer;
	
	/*
	 * add back carry outs from top 16 bits to low 16 bits
	 */
	sum = (sum >> 16) + (sum & 0xffff);	/* add hi 16 to low 16 */
	sum += (sum >> 16);			/* add carry */
	answer = ~sum;				/* truncate to 16 bits */
	return (answer);
}


syntax highlighted by Code2HTML, v. 0.9.1