15d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner/* 25d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * IP checksumming functions. 35d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * (c) 2008 Gerd Hoffmann <kraxel@redhat.com> 45d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * 55d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * This program is free software; you can redistribute it and/or modify 65d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * it under the terms of the GNU General Public License as published by 75d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * the Free Software Foundation; under version 2 of the License. 85d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * 95d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * This program is distributed in the hope that it will be useful, 105d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * but WITHOUT ANY WARRANTY; without even the implied warranty of 115d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 125d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * GNU General Public License for more details. 135d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * 145d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * You should have received a copy of the GNU General Public License 15cb34fa2cb61a4a838b32126fd28eb3450fd9f8ecDavid Turner * along with this program; if not, see <http://www.gnu.org/licenses/>. 165d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner */ 175d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 185d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner#include "hw/hw.h" 19cc330d4169441727fecf1da08aee806fc021c4e2David 'Digit' Turner#include "net/net.h" 205d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 215d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner#define PROTO_TCP 6 225d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner#define PROTO_UDP 17 235d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 245d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turneruint32_t net_checksum_add(int len, uint8_t *buf) 255d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{ 265d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner uint32_t sum = 0; 275d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner int i; 285d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 295d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner for (i = 0; i < len; i++) { 305d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner if (i & 1) 315d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner sum += (uint32_t)buf[i]; 325d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner else 335d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner sum += (uint32_t)buf[i] << 8; 345d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner } 355d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner return sum; 365d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner} 375d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 385d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turneruint16_t net_checksum_finish(uint32_t sum) 395d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{ 405d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner while (sum>>16) 415d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner sum = (sum & 0xFFFF)+(sum >> 16); 425d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner return ~sum; 435d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner} 445d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 455d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turneruint16_t net_checksum_tcpudp(uint16_t length, uint16_t proto, 465d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner uint8_t *addrs, uint8_t *buf) 475d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{ 485d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner uint32_t sum = 0; 495d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 505d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner sum += net_checksum_add(length, buf); // payload 515d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner sum += net_checksum_add(8, addrs); // src + dst address 525d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner sum += proto + length; // protocol & length 535d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner return net_checksum_finish(sum); 545d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner} 555d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 565d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnervoid net_checksum_calculate(uint8_t *data, int length) 575d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{ 585d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner int hlen, plen, proto, csum_offset; 595d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner uint16_t csum; 605d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 615d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner if ((data[14] & 0xf0) != 0x40) 625d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner return; /* not IPv4 */ 635d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner hlen = (data[14] & 0x0f) * 4; 645d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner plen = (data[16] << 8 | data[17]) - hlen; 655d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner proto = data[23]; 665d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 675d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner switch (proto) { 685d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner case PROTO_TCP: 695d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner csum_offset = 16; 705d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner break; 715d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner case PROTO_UDP: 725d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner csum_offset = 6; 735d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner break; 745d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner default: 755d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner return; 765d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner } 775d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 785d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner if (plen < csum_offset+2) 795d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner return; 805d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 815d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner data[14+hlen+csum_offset] = 0; 825d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner data[14+hlen+csum_offset+1] = 0; 835d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner csum = net_checksum_tcpudp(plen, proto, data+14+12, data+14+hlen); 845d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner data[14+hlen+csum_offset] = csum >> 8; 855d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner data[14+hlen+csum_offset+1] = csum & 0xff; 865d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner} 87