xref: /openbmc/qemu/net/checksum.c (revision 7200ac3c7c8eefe574193b49eeff09f120e11ec7)
1*7200ac3cSMark McLoughlin /*
2*7200ac3cSMark McLoughlin  *  IP checksumming functions.
3*7200ac3cSMark McLoughlin  *  (c) 2008 Gerd Hoffmann <kraxel@redhat.com>
4*7200ac3cSMark McLoughlin  *
5*7200ac3cSMark McLoughlin  *  This program is free software; you can redistribute it and/or modify
6*7200ac3cSMark McLoughlin  *  it under the terms of the GNU General Public License as published by
7*7200ac3cSMark McLoughlin  *  the Free Software Foundation; under version 2 of the License.
8*7200ac3cSMark McLoughlin  *
9*7200ac3cSMark McLoughlin  *  This program is distributed in the hope that it will be useful,
10*7200ac3cSMark McLoughlin  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11*7200ac3cSMark McLoughlin  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12*7200ac3cSMark McLoughlin  *  GNU General Public License for more details.
13*7200ac3cSMark McLoughlin  *
14*7200ac3cSMark McLoughlin  *  You should have received a copy of the GNU General Public License
15*7200ac3cSMark McLoughlin  *  along with this program; if not, see <http://www.gnu.org/licenses/>.
16*7200ac3cSMark McLoughlin  */
17*7200ac3cSMark McLoughlin 
18*7200ac3cSMark McLoughlin #include "net/checksum.h"
19*7200ac3cSMark McLoughlin 
20*7200ac3cSMark McLoughlin #define PROTO_TCP  6
21*7200ac3cSMark McLoughlin #define PROTO_UDP 17
22*7200ac3cSMark McLoughlin 
23*7200ac3cSMark McLoughlin uint32_t net_checksum_add(int len, uint8_t *buf)
24*7200ac3cSMark McLoughlin {
25*7200ac3cSMark McLoughlin     uint32_t sum = 0;
26*7200ac3cSMark McLoughlin     int i;
27*7200ac3cSMark McLoughlin 
28*7200ac3cSMark McLoughlin     for (i = 0; i < len; i++) {
29*7200ac3cSMark McLoughlin 	if (i & 1)
30*7200ac3cSMark McLoughlin 	    sum += (uint32_t)buf[i];
31*7200ac3cSMark McLoughlin 	else
32*7200ac3cSMark McLoughlin 	    sum += (uint32_t)buf[i] << 8;
33*7200ac3cSMark McLoughlin     }
34*7200ac3cSMark McLoughlin     return sum;
35*7200ac3cSMark McLoughlin }
36*7200ac3cSMark McLoughlin 
37*7200ac3cSMark McLoughlin uint16_t net_checksum_finish(uint32_t sum)
38*7200ac3cSMark McLoughlin {
39*7200ac3cSMark McLoughlin     while (sum>>16)
40*7200ac3cSMark McLoughlin 	sum = (sum & 0xFFFF)+(sum >> 16);
41*7200ac3cSMark McLoughlin     return ~sum;
42*7200ac3cSMark McLoughlin }
43*7200ac3cSMark McLoughlin 
44*7200ac3cSMark McLoughlin uint16_t net_checksum_tcpudp(uint16_t length, uint16_t proto,
45*7200ac3cSMark McLoughlin                              uint8_t *addrs, uint8_t *buf)
46*7200ac3cSMark McLoughlin {
47*7200ac3cSMark McLoughlin     uint32_t sum = 0;
48*7200ac3cSMark McLoughlin 
49*7200ac3cSMark McLoughlin     sum += net_checksum_add(length, buf);         // payload
50*7200ac3cSMark McLoughlin     sum += net_checksum_add(8, addrs);            // src + dst address
51*7200ac3cSMark McLoughlin     sum += proto + length;                        // protocol & length
52*7200ac3cSMark McLoughlin     return net_checksum_finish(sum);
53*7200ac3cSMark McLoughlin }
54*7200ac3cSMark McLoughlin 
55*7200ac3cSMark McLoughlin void net_checksum_calculate(uint8_t *data, int length)
56*7200ac3cSMark McLoughlin {
57*7200ac3cSMark McLoughlin     int hlen, plen, proto, csum_offset;
58*7200ac3cSMark McLoughlin     uint16_t csum;
59*7200ac3cSMark McLoughlin 
60*7200ac3cSMark McLoughlin     if ((data[14] & 0xf0) != 0x40)
61*7200ac3cSMark McLoughlin 	return; /* not IPv4 */
62*7200ac3cSMark McLoughlin     hlen  = (data[14] & 0x0f) * 4;
63*7200ac3cSMark McLoughlin     plen  = (data[16] << 8 | data[17]) - hlen;
64*7200ac3cSMark McLoughlin     proto = data[23];
65*7200ac3cSMark McLoughlin 
66*7200ac3cSMark McLoughlin     switch (proto) {
67*7200ac3cSMark McLoughlin     case PROTO_TCP:
68*7200ac3cSMark McLoughlin 	csum_offset = 16;
69*7200ac3cSMark McLoughlin 	break;
70*7200ac3cSMark McLoughlin     case PROTO_UDP:
71*7200ac3cSMark McLoughlin 	csum_offset = 6;
72*7200ac3cSMark McLoughlin 	break;
73*7200ac3cSMark McLoughlin     default:
74*7200ac3cSMark McLoughlin 	return;
75*7200ac3cSMark McLoughlin     }
76*7200ac3cSMark McLoughlin 
77*7200ac3cSMark McLoughlin     if (plen < csum_offset+2)
78*7200ac3cSMark McLoughlin 	return;
79*7200ac3cSMark McLoughlin 
80*7200ac3cSMark McLoughlin     data[14+hlen+csum_offset]   = 0;
81*7200ac3cSMark McLoughlin     data[14+hlen+csum_offset+1] = 0;
82*7200ac3cSMark McLoughlin     csum = net_checksum_tcpudp(plen, proto, data+14+12, data+14+hlen);
83*7200ac3cSMark McLoughlin     data[14+hlen+csum_offset]   = csum >> 8;
84*7200ac3cSMark McLoughlin     data[14+hlen+csum_offset+1] = csum & 0xff;
85*7200ac3cSMark McLoughlin }
86