xref: /openbmc/u-boot/net/checksum.c (revision c5f18a0b)
1 /*
2  * This file was originally taken from the FreeBSD project.
3  *
4  * Copyright (c) 2001 Charles Mott <cm@linktel.net>
5  * Copyright (c) 2008 coresystems GmbH
6  * All rights reserved.
7  *
8  * SPDX-License-Identifier:	BSD-2-Clause
9  */
10 
11 #include <common.h>
12 #include <net.h>
13 
14 unsigned compute_ip_checksum(const void *vptr, unsigned nbytes)
15 {
16 	int sum, oddbyte;
17 	const unsigned short *ptr = vptr;
18 
19 	sum = 0;
20 	while (nbytes > 1) {
21 		sum += *ptr++;
22 		nbytes -= 2;
23 	}
24 	if (nbytes == 1) {
25 		oddbyte = 0;
26 		((u8 *)&oddbyte)[0] = *(u8 *)ptr;
27 		((u8 *)&oddbyte)[1] = 0;
28 		sum += oddbyte;
29 	}
30 	sum = (sum >> 16) + (sum & 0xffff);
31 	sum += (sum >> 16);
32 	sum = ~sum & 0xffff;
33 
34 	return sum;
35 }
36 
37 unsigned add_ip_checksums(unsigned offset, unsigned sum, unsigned new)
38 {
39 	unsigned long checksum;
40 
41 	sum = ~sum & 0xffff;
42 	new = ~new & 0xffff;
43 	if (offset & 1) {
44 		/*
45 		 * byte-swap the sum if it came from an odd offset; since the
46 		 * computation is endian independant this works.
47 		 */
48 		new = ((new >> 8) & 0xff) | ((new << 8) & 0xff00);
49 	}
50 	checksum = sum + new;
51 	if (checksum > 0xffff)
52 		checksum -= 0xffff;
53 
54 	return (~checksum) & 0xffff;
55 }
56 
57 int ip_checksum_ok(const void *addr, unsigned nbytes)
58 {
59 	return !(compute_ip_checksum(addr, nbytes) & 0xfffe);
60 }
61