17200ac3cSMark McLoughlin /* 27200ac3cSMark McLoughlin * IP checksumming functions. 37200ac3cSMark McLoughlin * (c) 2008 Gerd Hoffmann <kraxel@redhat.com> 47200ac3cSMark McLoughlin * 57200ac3cSMark McLoughlin * This program is free software; you can redistribute it and/or modify 67200ac3cSMark McLoughlin * it under the terms of the GNU General Public License as published by 74c32fe66SStefan Weil * the Free Software Foundation; under version 2 or later of the License. 87200ac3cSMark McLoughlin * 97200ac3cSMark McLoughlin * This program is distributed in the hope that it will be useful, 107200ac3cSMark McLoughlin * but WITHOUT ANY WARRANTY; without even the implied warranty of 117200ac3cSMark McLoughlin * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 127200ac3cSMark McLoughlin * GNU General Public License for more details. 137200ac3cSMark McLoughlin * 147200ac3cSMark McLoughlin * You should have received a copy of the GNU General Public License 157200ac3cSMark McLoughlin * along with this program; if not, see <http://www.gnu.org/licenses/>. 167200ac3cSMark McLoughlin */ 177200ac3cSMark McLoughlin 18*2744d920SPeter Maydell #include "qemu/osdep.h" 1984026301SDmitry Fleytman #include "qemu-common.h" 207200ac3cSMark McLoughlin #include "net/checksum.h" 217200ac3cSMark McLoughlin 227200ac3cSMark McLoughlin #define PROTO_TCP 6 237200ac3cSMark McLoughlin #define PROTO_UDP 17 247200ac3cSMark McLoughlin 255acf5ea4SDmitry Fleytman uint32_t net_checksum_add_cont(int len, uint8_t *buf, int seq) 267200ac3cSMark McLoughlin { 277200ac3cSMark McLoughlin uint32_t sum = 0; 287200ac3cSMark McLoughlin int i; 297200ac3cSMark McLoughlin 305acf5ea4SDmitry Fleytman for (i = seq; i < seq + len; i++) { 315acf5ea4SDmitry Fleytman if (i & 1) { 325acf5ea4SDmitry Fleytman sum += (uint32_t)buf[i - seq]; 335acf5ea4SDmitry Fleytman } else { 345acf5ea4SDmitry Fleytman sum += (uint32_t)buf[i - seq] << 8; 355acf5ea4SDmitry Fleytman } 367200ac3cSMark McLoughlin } 377200ac3cSMark McLoughlin return sum; 387200ac3cSMark McLoughlin } 397200ac3cSMark McLoughlin 407200ac3cSMark McLoughlin uint16_t net_checksum_finish(uint32_t sum) 417200ac3cSMark McLoughlin { 427200ac3cSMark McLoughlin while (sum>>16) 437200ac3cSMark McLoughlin sum = (sum & 0xFFFF)+(sum >> 16); 447200ac3cSMark McLoughlin return ~sum; 457200ac3cSMark McLoughlin } 467200ac3cSMark McLoughlin 477200ac3cSMark McLoughlin uint16_t net_checksum_tcpudp(uint16_t length, uint16_t proto, 487200ac3cSMark McLoughlin uint8_t *addrs, uint8_t *buf) 497200ac3cSMark McLoughlin { 507200ac3cSMark McLoughlin uint32_t sum = 0; 517200ac3cSMark McLoughlin 527200ac3cSMark McLoughlin sum += net_checksum_add(length, buf); // payload 537200ac3cSMark McLoughlin sum += net_checksum_add(8, addrs); // src + dst address 547200ac3cSMark McLoughlin sum += proto + length; // protocol & length 557200ac3cSMark McLoughlin return net_checksum_finish(sum); 567200ac3cSMark McLoughlin } 577200ac3cSMark McLoughlin 587200ac3cSMark McLoughlin void net_checksum_calculate(uint8_t *data, int length) 597200ac3cSMark McLoughlin { 607200ac3cSMark McLoughlin int hlen, plen, proto, csum_offset; 617200ac3cSMark McLoughlin uint16_t csum; 627200ac3cSMark McLoughlin 637200ac3cSMark McLoughlin if ((data[14] & 0xf0) != 0x40) 647200ac3cSMark McLoughlin return; /* not IPv4 */ 657200ac3cSMark McLoughlin hlen = (data[14] & 0x0f) * 4; 667200ac3cSMark McLoughlin plen = (data[16] << 8 | data[17]) - hlen; 677200ac3cSMark McLoughlin proto = data[23]; 687200ac3cSMark McLoughlin 697200ac3cSMark McLoughlin switch (proto) { 707200ac3cSMark McLoughlin case PROTO_TCP: 717200ac3cSMark McLoughlin csum_offset = 16; 727200ac3cSMark McLoughlin break; 737200ac3cSMark McLoughlin case PROTO_UDP: 747200ac3cSMark McLoughlin csum_offset = 6; 757200ac3cSMark McLoughlin break; 767200ac3cSMark McLoughlin default: 777200ac3cSMark McLoughlin return; 787200ac3cSMark McLoughlin } 797200ac3cSMark McLoughlin 807200ac3cSMark McLoughlin if (plen < csum_offset+2) 817200ac3cSMark McLoughlin return; 827200ac3cSMark McLoughlin 837200ac3cSMark McLoughlin data[14+hlen+csum_offset] = 0; 847200ac3cSMark McLoughlin data[14+hlen+csum_offset+1] = 0; 857200ac3cSMark McLoughlin csum = net_checksum_tcpudp(plen, proto, data+14+12, data+14+hlen); 867200ac3cSMark McLoughlin data[14+hlen+csum_offset] = csum >> 8; 877200ac3cSMark McLoughlin data[14+hlen+csum_offset+1] = csum & 0xff; 887200ac3cSMark McLoughlin } 8984026301SDmitry Fleytman 9084026301SDmitry Fleytman uint32_t 9184026301SDmitry Fleytman net_checksum_add_iov(const struct iovec *iov, const unsigned int iov_cnt, 9284026301SDmitry Fleytman uint32_t iov_off, uint32_t size) 9384026301SDmitry Fleytman { 9484026301SDmitry Fleytman size_t iovec_off, buf_off; 9584026301SDmitry Fleytman unsigned int i; 9684026301SDmitry Fleytman uint32_t res = 0; 9784026301SDmitry Fleytman uint32_t seq = 0; 9884026301SDmitry Fleytman 9984026301SDmitry Fleytman iovec_off = 0; 10084026301SDmitry Fleytman buf_off = 0; 10184026301SDmitry Fleytman for (i = 0; i < iov_cnt && size; i++) { 10284026301SDmitry Fleytman if (iov_off < (iovec_off + iov[i].iov_len)) { 10384026301SDmitry Fleytman size_t len = MIN((iovec_off + iov[i].iov_len) - iov_off , size); 10484026301SDmitry Fleytman void *chunk_buf = iov[i].iov_base + (iov_off - iovec_off); 10584026301SDmitry Fleytman 10684026301SDmitry Fleytman res += net_checksum_add_cont(len, chunk_buf, seq); 10784026301SDmitry Fleytman seq += len; 10884026301SDmitry Fleytman 10984026301SDmitry Fleytman buf_off += len; 11084026301SDmitry Fleytman iov_off += len; 11184026301SDmitry Fleytman size -= len; 11284026301SDmitry Fleytman } 11384026301SDmitry Fleytman iovec_off += iov[i].iov_len; 11484026301SDmitry Fleytman } 11584026301SDmitry Fleytman return res; 11684026301SDmitry Fleytman } 117