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" 197200ac3cSMark McLoughlin #include "net/checksum.h" 2050dbce65SJean-Christophe Dubois #include "net/eth.h" 217200ac3cSMark McLoughlin 225acf5ea4SDmitry Fleytman uint32_t net_checksum_add_cont(int len, uint8_t *buf, int seq) 237200ac3cSMark McLoughlin { 24d5aa3e6eSLadi Prosek uint32_t sum1 = 0, sum2 = 0; 257200ac3cSMark McLoughlin int i; 267200ac3cSMark McLoughlin 27d5aa3e6eSLadi Prosek for (i = 0; i < len - 1; i += 2) { 28d5aa3e6eSLadi Prosek sum1 += (uint32_t)buf[i]; 29d5aa3e6eSLadi Prosek sum2 += (uint32_t)buf[i + 1]; 30d5aa3e6eSLadi Prosek } 31d5aa3e6eSLadi Prosek if (i < len) { 32d5aa3e6eSLadi Prosek sum1 += (uint32_t)buf[i]; 33d5aa3e6eSLadi Prosek } 34d5aa3e6eSLadi Prosek 35d5aa3e6eSLadi Prosek if (seq & 1) { 36d5aa3e6eSLadi Prosek return sum1 + (sum2 << 8); 375acf5ea4SDmitry Fleytman } else { 38d5aa3e6eSLadi Prosek return sum2 + (sum1 << 8); 395acf5ea4SDmitry Fleytman } 407200ac3cSMark McLoughlin } 417200ac3cSMark McLoughlin 427200ac3cSMark McLoughlin uint16_t net_checksum_finish(uint32_t sum) 437200ac3cSMark McLoughlin { 447200ac3cSMark McLoughlin while (sum>>16) 457200ac3cSMark McLoughlin sum = (sum & 0xFFFF)+(sum >> 16); 467200ac3cSMark McLoughlin return ~sum; 477200ac3cSMark McLoughlin } 487200ac3cSMark McLoughlin 497200ac3cSMark McLoughlin uint16_t net_checksum_tcpudp(uint16_t length, uint16_t proto, 507200ac3cSMark McLoughlin uint8_t *addrs, uint8_t *buf) 517200ac3cSMark McLoughlin { 527200ac3cSMark McLoughlin uint32_t sum = 0; 537200ac3cSMark McLoughlin 547200ac3cSMark McLoughlin sum += net_checksum_add(length, buf); // payload 557200ac3cSMark McLoughlin sum += net_checksum_add(8, addrs); // src + dst address 567200ac3cSMark McLoughlin sum += proto + length; // protocol & length 577200ac3cSMark McLoughlin return net_checksum_finish(sum); 587200ac3cSMark McLoughlin } 597200ac3cSMark McLoughlin 607200ac3cSMark McLoughlin void net_checksum_calculate(uint8_t *data, int length) 617200ac3cSMark McLoughlin { 62ade6bad1SJean-Christophe Dubois int mac_hdr_len, ip_len; 6350dbce65SJean-Christophe Dubois struct ip_header *ip; 6450dbce65SJean-Christophe Dubois 6550dbce65SJean-Christophe Dubois /* 6650dbce65SJean-Christophe Dubois * Note: We cannot assume "data" is aligned, so the all code uses 6750dbce65SJean-Christophe Dubois * some macros that take care of possible unaligned access for 6850dbce65SJean-Christophe Dubois * struct members (just in case). 6950dbce65SJean-Christophe Dubois */ 707200ac3cSMark McLoughlin 71ade6bad1SJean-Christophe Dubois /* Ensure we have at least an Eth header */ 72ade6bad1SJean-Christophe Dubois if (length < sizeof(struct eth_header)) { 73362786f1SPrasad J Pandit return; 74362786f1SPrasad J Pandit } 75362786f1SPrasad J Pandit 76ade6bad1SJean-Christophe Dubois /* Handle the optionnal VLAN headers */ 77ade6bad1SJean-Christophe Dubois switch (lduw_be_p(&PKT_GET_ETH_HDR(data)->h_proto)) { 78ade6bad1SJean-Christophe Dubois case ETH_P_VLAN: 79ade6bad1SJean-Christophe Dubois mac_hdr_len = sizeof(struct eth_header) + 80ade6bad1SJean-Christophe Dubois sizeof(struct vlan_header); 81ade6bad1SJean-Christophe Dubois break; 82ade6bad1SJean-Christophe Dubois case ETH_P_DVLAN: 83ade6bad1SJean-Christophe Dubois if (lduw_be_p(&PKT_GET_VLAN_HDR(data)->h_proto) == ETH_P_VLAN) { 84ade6bad1SJean-Christophe Dubois mac_hdr_len = sizeof(struct eth_header) + 85ade6bad1SJean-Christophe Dubois 2 * sizeof(struct vlan_header); 86ade6bad1SJean-Christophe Dubois } else { 87ade6bad1SJean-Christophe Dubois mac_hdr_len = sizeof(struct eth_header) + 88ade6bad1SJean-Christophe Dubois sizeof(struct vlan_header); 89ade6bad1SJean-Christophe Dubois } 90ade6bad1SJean-Christophe Dubois break; 91ade6bad1SJean-Christophe Dubois default: 92ade6bad1SJean-Christophe Dubois mac_hdr_len = sizeof(struct eth_header); 93ade6bad1SJean-Christophe Dubois break; 94ade6bad1SJean-Christophe Dubois } 95ade6bad1SJean-Christophe Dubois 96ade6bad1SJean-Christophe Dubois length -= mac_hdr_len; 97ade6bad1SJean-Christophe Dubois 98ade6bad1SJean-Christophe Dubois /* Now check we have an IP header (with an optionnal VLAN header) */ 99ade6bad1SJean-Christophe Dubois if (length < sizeof(struct ip_header)) { 100ade6bad1SJean-Christophe Dubois return; 101ade6bad1SJean-Christophe Dubois } 102ade6bad1SJean-Christophe Dubois 103ade6bad1SJean-Christophe Dubois ip = (struct ip_header *)(data + mac_hdr_len); 10450dbce65SJean-Christophe Dubois 10550dbce65SJean-Christophe Dubois if (IP_HEADER_VERSION(ip) != IP_HEADER_VERSION_4) { 1067200ac3cSMark McLoughlin return; /* not IPv4 */ 10750dbce65SJean-Christophe Dubois } 1087200ac3cSMark McLoughlin 109*0dcf0c0aSMarkus Carlstedt if (IP4_IS_FRAGMENT(ip)) { 110*0dcf0c0aSMarkus Carlstedt return; /* a fragmented IP packet */ 111*0dcf0c0aSMarkus Carlstedt } 112*0dcf0c0aSMarkus Carlstedt 11350dbce65SJean-Christophe Dubois ip_len = lduw_be_p(&ip->ip_len); 11450dbce65SJean-Christophe Dubois 11550dbce65SJean-Christophe Dubois /* Last, check that we have enough data for the all IP frame */ 11650dbce65SJean-Christophe Dubois if (length < ip_len) { 11750dbce65SJean-Christophe Dubois return; 11850dbce65SJean-Christophe Dubois } 11950dbce65SJean-Christophe Dubois 12050dbce65SJean-Christophe Dubois ip_len -= IP_HDR_GET_LEN(ip); 12150dbce65SJean-Christophe Dubois 12250dbce65SJean-Christophe Dubois switch (ip->ip_p) { 12350dbce65SJean-Christophe Dubois case IP_PROTO_TCP: 12450dbce65SJean-Christophe Dubois { 12550dbce65SJean-Christophe Dubois uint16_t csum; 12650dbce65SJean-Christophe Dubois tcp_header *tcp = (tcp_header *)(ip + 1); 12750dbce65SJean-Christophe Dubois 12850dbce65SJean-Christophe Dubois if (ip_len < sizeof(tcp_header)) { 12950dbce65SJean-Christophe Dubois return; 13050dbce65SJean-Christophe Dubois } 13150dbce65SJean-Christophe Dubois 13250dbce65SJean-Christophe Dubois /* Set csum to 0 */ 13350dbce65SJean-Christophe Dubois stw_he_p(&tcp->th_sum, 0); 13450dbce65SJean-Christophe Dubois 13550dbce65SJean-Christophe Dubois csum = net_checksum_tcpudp(ip_len, ip->ip_p, 13650dbce65SJean-Christophe Dubois (uint8_t *)&ip->ip_src, 13750dbce65SJean-Christophe Dubois (uint8_t *)tcp); 13850dbce65SJean-Christophe Dubois 13950dbce65SJean-Christophe Dubois /* Store computed csum */ 14050dbce65SJean-Christophe Dubois stw_be_p(&tcp->th_sum, csum); 14150dbce65SJean-Christophe Dubois 1427200ac3cSMark McLoughlin break; 14350dbce65SJean-Christophe Dubois } 14450dbce65SJean-Christophe Dubois case IP_PROTO_UDP: 14550dbce65SJean-Christophe Dubois { 14650dbce65SJean-Christophe Dubois uint16_t csum; 14750dbce65SJean-Christophe Dubois udp_header *udp = (udp_header *)(ip + 1); 14850dbce65SJean-Christophe Dubois 14950dbce65SJean-Christophe Dubois if (ip_len < sizeof(udp_header)) { 15050dbce65SJean-Christophe Dubois return; 15150dbce65SJean-Christophe Dubois } 15250dbce65SJean-Christophe Dubois 15350dbce65SJean-Christophe Dubois /* Set csum to 0 */ 15450dbce65SJean-Christophe Dubois stw_he_p(&udp->uh_sum, 0); 15550dbce65SJean-Christophe Dubois 15650dbce65SJean-Christophe Dubois csum = net_checksum_tcpudp(ip_len, ip->ip_p, 15750dbce65SJean-Christophe Dubois (uint8_t *)&ip->ip_src, 15850dbce65SJean-Christophe Dubois (uint8_t *)udp); 15950dbce65SJean-Christophe Dubois 16050dbce65SJean-Christophe Dubois /* Store computed csum */ 16150dbce65SJean-Christophe Dubois stw_be_p(&udp->uh_sum, csum); 16250dbce65SJean-Christophe Dubois 1637200ac3cSMark McLoughlin break; 16450dbce65SJean-Christophe Dubois } 1657200ac3cSMark McLoughlin default: 16650dbce65SJean-Christophe Dubois /* Can't handle any other protocol */ 16750dbce65SJean-Christophe Dubois break; 1687200ac3cSMark McLoughlin } 1697200ac3cSMark McLoughlin } 17084026301SDmitry Fleytman 17184026301SDmitry Fleytman uint32_t 17284026301SDmitry Fleytman net_checksum_add_iov(const struct iovec *iov, const unsigned int iov_cnt, 173eb700029SDmitry Fleytman uint32_t iov_off, uint32_t size, uint32_t csum_offset) 17484026301SDmitry Fleytman { 17584026301SDmitry Fleytman size_t iovec_off, buf_off; 17684026301SDmitry Fleytman unsigned int i; 17784026301SDmitry Fleytman uint32_t res = 0; 17884026301SDmitry Fleytman 17984026301SDmitry Fleytman iovec_off = 0; 18084026301SDmitry Fleytman buf_off = 0; 18184026301SDmitry Fleytman for (i = 0; i < iov_cnt && size; i++) { 18284026301SDmitry Fleytman if (iov_off < (iovec_off + iov[i].iov_len)) { 18384026301SDmitry Fleytman size_t len = MIN((iovec_off + iov[i].iov_len) - iov_off , size); 18484026301SDmitry Fleytman void *chunk_buf = iov[i].iov_base + (iov_off - iovec_off); 18584026301SDmitry Fleytman 186eb700029SDmitry Fleytman res += net_checksum_add_cont(len, chunk_buf, csum_offset); 187eb700029SDmitry Fleytman csum_offset += len; 18884026301SDmitry Fleytman 18984026301SDmitry Fleytman buf_off += len; 19084026301SDmitry Fleytman iov_off += len; 19184026301SDmitry Fleytman size -= len; 19284026301SDmitry Fleytman } 19384026301SDmitry Fleytman iovec_off += iov[i].iov_len; 19484026301SDmitry Fleytman } 19584026301SDmitry Fleytman return res; 19684026301SDmitry Fleytman } 197