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 182744d920SPeter Maydell #include "qemu/osdep.h" 1984026301SDmitry Fleytman #include "qemu-common.h" 207200ac3cSMark McLoughlin #include "net/checksum.h" 21*50dbce65SJean-Christophe Dubois #include "net/eth.h" 227200ac3cSMark McLoughlin 235acf5ea4SDmitry Fleytman uint32_t net_checksum_add_cont(int len, uint8_t *buf, int seq) 247200ac3cSMark McLoughlin { 257200ac3cSMark McLoughlin uint32_t sum = 0; 267200ac3cSMark McLoughlin int i; 277200ac3cSMark McLoughlin 285acf5ea4SDmitry Fleytman for (i = seq; i < seq + len; i++) { 295acf5ea4SDmitry Fleytman if (i & 1) { 305acf5ea4SDmitry Fleytman sum += (uint32_t)buf[i - seq]; 315acf5ea4SDmitry Fleytman } else { 325acf5ea4SDmitry Fleytman sum += (uint32_t)buf[i - seq] << 8; 335acf5ea4SDmitry Fleytman } 347200ac3cSMark McLoughlin } 357200ac3cSMark McLoughlin return sum; 367200ac3cSMark McLoughlin } 377200ac3cSMark McLoughlin 387200ac3cSMark McLoughlin uint16_t net_checksum_finish(uint32_t sum) 397200ac3cSMark McLoughlin { 407200ac3cSMark McLoughlin while (sum>>16) 417200ac3cSMark McLoughlin sum = (sum & 0xFFFF)+(sum >> 16); 427200ac3cSMark McLoughlin return ~sum; 437200ac3cSMark McLoughlin } 447200ac3cSMark McLoughlin 457200ac3cSMark McLoughlin uint16_t net_checksum_tcpudp(uint16_t length, uint16_t proto, 467200ac3cSMark McLoughlin uint8_t *addrs, uint8_t *buf) 477200ac3cSMark McLoughlin { 487200ac3cSMark McLoughlin uint32_t sum = 0; 497200ac3cSMark McLoughlin 507200ac3cSMark McLoughlin sum += net_checksum_add(length, buf); // payload 517200ac3cSMark McLoughlin sum += net_checksum_add(8, addrs); // src + dst address 527200ac3cSMark McLoughlin sum += proto + length; // protocol & length 537200ac3cSMark McLoughlin return net_checksum_finish(sum); 547200ac3cSMark McLoughlin } 557200ac3cSMark McLoughlin 567200ac3cSMark McLoughlin void net_checksum_calculate(uint8_t *data, int length) 577200ac3cSMark McLoughlin { 58*50dbce65SJean-Christophe Dubois int ip_len; 59*50dbce65SJean-Christophe Dubois struct ip_header *ip; 60*50dbce65SJean-Christophe Dubois 61*50dbce65SJean-Christophe Dubois /* 62*50dbce65SJean-Christophe Dubois * Note: We cannot assume "data" is aligned, so the all code uses 63*50dbce65SJean-Christophe Dubois * some macros that take care of possible unaligned access for 64*50dbce65SJean-Christophe Dubois * struct members (just in case). 65*50dbce65SJean-Christophe Dubois */ 667200ac3cSMark McLoughlin 67362786f1SPrasad J Pandit /* Ensure data has complete L2 & L3 headers. */ 68*50dbce65SJean-Christophe Dubois if (length < (sizeof(struct eth_header) + sizeof(struct ip_header))) { 69362786f1SPrasad J Pandit return; 70362786f1SPrasad J Pandit } 71362786f1SPrasad J Pandit 72*50dbce65SJean-Christophe Dubois ip = (struct ip_header *)(data + sizeof(struct eth_header)); 73*50dbce65SJean-Christophe Dubois 74*50dbce65SJean-Christophe Dubois if (IP_HEADER_VERSION(ip) != IP_HEADER_VERSION_4) { 757200ac3cSMark McLoughlin return; /* not IPv4 */ 76*50dbce65SJean-Christophe Dubois } 777200ac3cSMark McLoughlin 78*50dbce65SJean-Christophe Dubois ip_len = lduw_be_p(&ip->ip_len); 79*50dbce65SJean-Christophe Dubois 80*50dbce65SJean-Christophe Dubois /* Last, check that we have enough data for the all IP frame */ 81*50dbce65SJean-Christophe Dubois if (length < ip_len) { 82*50dbce65SJean-Christophe Dubois return; 83*50dbce65SJean-Christophe Dubois } 84*50dbce65SJean-Christophe Dubois 85*50dbce65SJean-Christophe Dubois ip_len -= IP_HDR_GET_LEN(ip); 86*50dbce65SJean-Christophe Dubois 87*50dbce65SJean-Christophe Dubois switch (ip->ip_p) { 88*50dbce65SJean-Christophe Dubois case IP_PROTO_TCP: 89*50dbce65SJean-Christophe Dubois { 90*50dbce65SJean-Christophe Dubois uint16_t csum; 91*50dbce65SJean-Christophe Dubois tcp_header *tcp = (tcp_header *)(ip + 1); 92*50dbce65SJean-Christophe Dubois 93*50dbce65SJean-Christophe Dubois if (ip_len < sizeof(tcp_header)) { 94*50dbce65SJean-Christophe Dubois return; 95*50dbce65SJean-Christophe Dubois } 96*50dbce65SJean-Christophe Dubois 97*50dbce65SJean-Christophe Dubois /* Set csum to 0 */ 98*50dbce65SJean-Christophe Dubois stw_he_p(&tcp->th_sum, 0); 99*50dbce65SJean-Christophe Dubois 100*50dbce65SJean-Christophe Dubois csum = net_checksum_tcpudp(ip_len, ip->ip_p, 101*50dbce65SJean-Christophe Dubois (uint8_t *)&ip->ip_src, 102*50dbce65SJean-Christophe Dubois (uint8_t *)tcp); 103*50dbce65SJean-Christophe Dubois 104*50dbce65SJean-Christophe Dubois /* Store computed csum */ 105*50dbce65SJean-Christophe Dubois stw_be_p(&tcp->th_sum, csum); 106*50dbce65SJean-Christophe Dubois 1077200ac3cSMark McLoughlin break; 108*50dbce65SJean-Christophe Dubois } 109*50dbce65SJean-Christophe Dubois case IP_PROTO_UDP: 110*50dbce65SJean-Christophe Dubois { 111*50dbce65SJean-Christophe Dubois uint16_t csum; 112*50dbce65SJean-Christophe Dubois udp_header *udp = (udp_header *)(ip + 1); 113*50dbce65SJean-Christophe Dubois 114*50dbce65SJean-Christophe Dubois if (ip_len < sizeof(udp_header)) { 115*50dbce65SJean-Christophe Dubois return; 116*50dbce65SJean-Christophe Dubois } 117*50dbce65SJean-Christophe Dubois 118*50dbce65SJean-Christophe Dubois /* Set csum to 0 */ 119*50dbce65SJean-Christophe Dubois stw_he_p(&udp->uh_sum, 0); 120*50dbce65SJean-Christophe Dubois 121*50dbce65SJean-Christophe Dubois csum = net_checksum_tcpudp(ip_len, ip->ip_p, 122*50dbce65SJean-Christophe Dubois (uint8_t *)&ip->ip_src, 123*50dbce65SJean-Christophe Dubois (uint8_t *)udp); 124*50dbce65SJean-Christophe Dubois 125*50dbce65SJean-Christophe Dubois /* Store computed csum */ 126*50dbce65SJean-Christophe Dubois stw_be_p(&udp->uh_sum, csum); 127*50dbce65SJean-Christophe Dubois 1287200ac3cSMark McLoughlin break; 129*50dbce65SJean-Christophe Dubois } 1307200ac3cSMark McLoughlin default: 131*50dbce65SJean-Christophe Dubois /* Can't handle any other protocol */ 132*50dbce65SJean-Christophe Dubois break; 1337200ac3cSMark McLoughlin } 1347200ac3cSMark McLoughlin } 13584026301SDmitry Fleytman 13684026301SDmitry Fleytman uint32_t 13784026301SDmitry Fleytman net_checksum_add_iov(const struct iovec *iov, const unsigned int iov_cnt, 138eb700029SDmitry Fleytman uint32_t iov_off, uint32_t size, uint32_t csum_offset) 13984026301SDmitry Fleytman { 14084026301SDmitry Fleytman size_t iovec_off, buf_off; 14184026301SDmitry Fleytman unsigned int i; 14284026301SDmitry Fleytman uint32_t res = 0; 14384026301SDmitry Fleytman 14484026301SDmitry Fleytman iovec_off = 0; 14584026301SDmitry Fleytman buf_off = 0; 14684026301SDmitry Fleytman for (i = 0; i < iov_cnt && size; i++) { 14784026301SDmitry Fleytman if (iov_off < (iovec_off + iov[i].iov_len)) { 14884026301SDmitry Fleytman size_t len = MIN((iovec_off + iov[i].iov_len) - iov_off , size); 14984026301SDmitry Fleytman void *chunk_buf = iov[i].iov_base + (iov_off - iovec_off); 15084026301SDmitry Fleytman 151eb700029SDmitry Fleytman res += net_checksum_add_cont(len, chunk_buf, csum_offset); 152eb700029SDmitry Fleytman csum_offset += len; 15384026301SDmitry Fleytman 15484026301SDmitry Fleytman buf_off += len; 15584026301SDmitry Fleytman iov_off += len; 15684026301SDmitry Fleytman size -= len; 15784026301SDmitry Fleytman } 15884026301SDmitry Fleytman iovec_off += iov[i].iov_len; 15984026301SDmitry Fleytman } 16084026301SDmitry Fleytman return res; 16184026301SDmitry Fleytman } 162