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" 2150dbce65SJean-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 { 25*d5aa3e6eSLadi Prosek uint32_t sum1 = 0, sum2 = 0; 267200ac3cSMark McLoughlin int i; 277200ac3cSMark McLoughlin 28*d5aa3e6eSLadi Prosek for (i = 0; i < len - 1; i += 2) { 29*d5aa3e6eSLadi Prosek sum1 += (uint32_t)buf[i]; 30*d5aa3e6eSLadi Prosek sum2 += (uint32_t)buf[i + 1]; 31*d5aa3e6eSLadi Prosek } 32*d5aa3e6eSLadi Prosek if (i < len) { 33*d5aa3e6eSLadi Prosek sum1 += (uint32_t)buf[i]; 34*d5aa3e6eSLadi Prosek } 35*d5aa3e6eSLadi Prosek 36*d5aa3e6eSLadi Prosek if (seq & 1) { 37*d5aa3e6eSLadi Prosek return sum1 + (sum2 << 8); 385acf5ea4SDmitry Fleytman } else { 39*d5aa3e6eSLadi Prosek return sum2 + (sum1 << 8); 405acf5ea4SDmitry Fleytman } 417200ac3cSMark McLoughlin } 427200ac3cSMark McLoughlin 437200ac3cSMark McLoughlin uint16_t net_checksum_finish(uint32_t sum) 447200ac3cSMark McLoughlin { 457200ac3cSMark McLoughlin while (sum>>16) 467200ac3cSMark McLoughlin sum = (sum & 0xFFFF)+(sum >> 16); 477200ac3cSMark McLoughlin return ~sum; 487200ac3cSMark McLoughlin } 497200ac3cSMark McLoughlin 507200ac3cSMark McLoughlin uint16_t net_checksum_tcpudp(uint16_t length, uint16_t proto, 517200ac3cSMark McLoughlin uint8_t *addrs, uint8_t *buf) 527200ac3cSMark McLoughlin { 537200ac3cSMark McLoughlin uint32_t sum = 0; 547200ac3cSMark McLoughlin 557200ac3cSMark McLoughlin sum += net_checksum_add(length, buf); // payload 567200ac3cSMark McLoughlin sum += net_checksum_add(8, addrs); // src + dst address 577200ac3cSMark McLoughlin sum += proto + length; // protocol & length 587200ac3cSMark McLoughlin return net_checksum_finish(sum); 597200ac3cSMark McLoughlin } 607200ac3cSMark McLoughlin 617200ac3cSMark McLoughlin void net_checksum_calculate(uint8_t *data, int length) 627200ac3cSMark McLoughlin { 63ade6bad1SJean-Christophe Dubois int mac_hdr_len, ip_len; 6450dbce65SJean-Christophe Dubois struct ip_header *ip; 6550dbce65SJean-Christophe Dubois 6650dbce65SJean-Christophe Dubois /* 6750dbce65SJean-Christophe Dubois * Note: We cannot assume "data" is aligned, so the all code uses 6850dbce65SJean-Christophe Dubois * some macros that take care of possible unaligned access for 6950dbce65SJean-Christophe Dubois * struct members (just in case). 7050dbce65SJean-Christophe Dubois */ 717200ac3cSMark McLoughlin 72ade6bad1SJean-Christophe Dubois /* Ensure we have at least an Eth header */ 73ade6bad1SJean-Christophe Dubois if (length < sizeof(struct eth_header)) { 74362786f1SPrasad J Pandit return; 75362786f1SPrasad J Pandit } 76362786f1SPrasad J Pandit 77ade6bad1SJean-Christophe Dubois /* Handle the optionnal VLAN headers */ 78ade6bad1SJean-Christophe Dubois switch (lduw_be_p(&PKT_GET_ETH_HDR(data)->h_proto)) { 79ade6bad1SJean-Christophe Dubois case ETH_P_VLAN: 80ade6bad1SJean-Christophe Dubois mac_hdr_len = sizeof(struct eth_header) + 81ade6bad1SJean-Christophe Dubois sizeof(struct vlan_header); 82ade6bad1SJean-Christophe Dubois break; 83ade6bad1SJean-Christophe Dubois case ETH_P_DVLAN: 84ade6bad1SJean-Christophe Dubois if (lduw_be_p(&PKT_GET_VLAN_HDR(data)->h_proto) == ETH_P_VLAN) { 85ade6bad1SJean-Christophe Dubois mac_hdr_len = sizeof(struct eth_header) + 86ade6bad1SJean-Christophe Dubois 2 * sizeof(struct vlan_header); 87ade6bad1SJean-Christophe Dubois } else { 88ade6bad1SJean-Christophe Dubois mac_hdr_len = sizeof(struct eth_header) + 89ade6bad1SJean-Christophe Dubois sizeof(struct vlan_header); 90ade6bad1SJean-Christophe Dubois } 91ade6bad1SJean-Christophe Dubois break; 92ade6bad1SJean-Christophe Dubois default: 93ade6bad1SJean-Christophe Dubois mac_hdr_len = sizeof(struct eth_header); 94ade6bad1SJean-Christophe Dubois break; 95ade6bad1SJean-Christophe Dubois } 96ade6bad1SJean-Christophe Dubois 97ade6bad1SJean-Christophe Dubois length -= mac_hdr_len; 98ade6bad1SJean-Christophe Dubois 99ade6bad1SJean-Christophe Dubois /* Now check we have an IP header (with an optionnal VLAN header) */ 100ade6bad1SJean-Christophe Dubois if (length < sizeof(struct ip_header)) { 101ade6bad1SJean-Christophe Dubois return; 102ade6bad1SJean-Christophe Dubois } 103ade6bad1SJean-Christophe Dubois 104ade6bad1SJean-Christophe Dubois ip = (struct ip_header *)(data + mac_hdr_len); 10550dbce65SJean-Christophe Dubois 10650dbce65SJean-Christophe Dubois if (IP_HEADER_VERSION(ip) != IP_HEADER_VERSION_4) { 1077200ac3cSMark McLoughlin return; /* not IPv4 */ 10850dbce65SJean-Christophe Dubois } 1097200ac3cSMark McLoughlin 11050dbce65SJean-Christophe Dubois ip_len = lduw_be_p(&ip->ip_len); 11150dbce65SJean-Christophe Dubois 11250dbce65SJean-Christophe Dubois /* Last, check that we have enough data for the all IP frame */ 11350dbce65SJean-Christophe Dubois if (length < ip_len) { 11450dbce65SJean-Christophe Dubois return; 11550dbce65SJean-Christophe Dubois } 11650dbce65SJean-Christophe Dubois 11750dbce65SJean-Christophe Dubois ip_len -= IP_HDR_GET_LEN(ip); 11850dbce65SJean-Christophe Dubois 11950dbce65SJean-Christophe Dubois switch (ip->ip_p) { 12050dbce65SJean-Christophe Dubois case IP_PROTO_TCP: 12150dbce65SJean-Christophe Dubois { 12250dbce65SJean-Christophe Dubois uint16_t csum; 12350dbce65SJean-Christophe Dubois tcp_header *tcp = (tcp_header *)(ip + 1); 12450dbce65SJean-Christophe Dubois 12550dbce65SJean-Christophe Dubois if (ip_len < sizeof(tcp_header)) { 12650dbce65SJean-Christophe Dubois return; 12750dbce65SJean-Christophe Dubois } 12850dbce65SJean-Christophe Dubois 12950dbce65SJean-Christophe Dubois /* Set csum to 0 */ 13050dbce65SJean-Christophe Dubois stw_he_p(&tcp->th_sum, 0); 13150dbce65SJean-Christophe Dubois 13250dbce65SJean-Christophe Dubois csum = net_checksum_tcpudp(ip_len, ip->ip_p, 13350dbce65SJean-Christophe Dubois (uint8_t *)&ip->ip_src, 13450dbce65SJean-Christophe Dubois (uint8_t *)tcp); 13550dbce65SJean-Christophe Dubois 13650dbce65SJean-Christophe Dubois /* Store computed csum */ 13750dbce65SJean-Christophe Dubois stw_be_p(&tcp->th_sum, csum); 13850dbce65SJean-Christophe Dubois 1397200ac3cSMark McLoughlin break; 14050dbce65SJean-Christophe Dubois } 14150dbce65SJean-Christophe Dubois case IP_PROTO_UDP: 14250dbce65SJean-Christophe Dubois { 14350dbce65SJean-Christophe Dubois uint16_t csum; 14450dbce65SJean-Christophe Dubois udp_header *udp = (udp_header *)(ip + 1); 14550dbce65SJean-Christophe Dubois 14650dbce65SJean-Christophe Dubois if (ip_len < sizeof(udp_header)) { 14750dbce65SJean-Christophe Dubois return; 14850dbce65SJean-Christophe Dubois } 14950dbce65SJean-Christophe Dubois 15050dbce65SJean-Christophe Dubois /* Set csum to 0 */ 15150dbce65SJean-Christophe Dubois stw_he_p(&udp->uh_sum, 0); 15250dbce65SJean-Christophe Dubois 15350dbce65SJean-Christophe Dubois csum = net_checksum_tcpudp(ip_len, ip->ip_p, 15450dbce65SJean-Christophe Dubois (uint8_t *)&ip->ip_src, 15550dbce65SJean-Christophe Dubois (uint8_t *)udp); 15650dbce65SJean-Christophe Dubois 15750dbce65SJean-Christophe Dubois /* Store computed csum */ 15850dbce65SJean-Christophe Dubois stw_be_p(&udp->uh_sum, csum); 15950dbce65SJean-Christophe Dubois 1607200ac3cSMark McLoughlin break; 16150dbce65SJean-Christophe Dubois } 1627200ac3cSMark McLoughlin default: 16350dbce65SJean-Christophe Dubois /* Can't handle any other protocol */ 16450dbce65SJean-Christophe Dubois break; 1657200ac3cSMark McLoughlin } 1667200ac3cSMark McLoughlin } 16784026301SDmitry Fleytman 16884026301SDmitry Fleytman uint32_t 16984026301SDmitry Fleytman net_checksum_add_iov(const struct iovec *iov, const unsigned int iov_cnt, 170eb700029SDmitry Fleytman uint32_t iov_off, uint32_t size, uint32_t csum_offset) 17184026301SDmitry Fleytman { 17284026301SDmitry Fleytman size_t iovec_off, buf_off; 17384026301SDmitry Fleytman unsigned int i; 17484026301SDmitry Fleytman uint32_t res = 0; 17584026301SDmitry Fleytman 17684026301SDmitry Fleytman iovec_off = 0; 17784026301SDmitry Fleytman buf_off = 0; 17884026301SDmitry Fleytman for (i = 0; i < iov_cnt && size; i++) { 17984026301SDmitry Fleytman if (iov_off < (iovec_off + iov[i].iov_len)) { 18084026301SDmitry Fleytman size_t len = MIN((iovec_off + iov[i].iov_len) - iov_off , size); 18184026301SDmitry Fleytman void *chunk_buf = iov[i].iov_base + (iov_off - iovec_off); 18284026301SDmitry Fleytman 183eb700029SDmitry Fleytman res += net_checksum_add_cont(len, chunk_buf, csum_offset); 184eb700029SDmitry Fleytman csum_offset += len; 18584026301SDmitry Fleytman 18684026301SDmitry Fleytman buf_off += len; 18784026301SDmitry Fleytman iov_off += len; 18884026301SDmitry Fleytman size -= len; 18984026301SDmitry Fleytman } 19084026301SDmitry Fleytman iovec_off += iov[i].iov_len; 19184026301SDmitry Fleytman } 19284026301SDmitry Fleytman return res; 19384026301SDmitry Fleytman } 194