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 60*f5746335SBin Meng void net_checksum_calculate(uint8_t *data, int length, int csum_flag) 617200ac3cSMark McLoughlin { 62ade6bad1SJean-Christophe Dubois int mac_hdr_len, ip_len; 6350dbce65SJean-Christophe Dubois struct ip_header *ip; 64d97f1159SGuishan Qin uint16_t csum; 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 110d97f1159SGuishan Qin /* Calculate IP checksum */ 111*f5746335SBin Meng if (csum_flag & CSUM_IP) { 112d97f1159SGuishan Qin stw_he_p(&ip->ip_sum, 0); 113d97f1159SGuishan Qin csum = net_raw_checksum((uint8_t *)ip, IP_HDR_GET_LEN(ip)); 114d97f1159SGuishan Qin stw_be_p(&ip->ip_sum, csum); 115*f5746335SBin Meng } 116d97f1159SGuishan Qin 1170dcf0c0aSMarkus Carlstedt if (IP4_IS_FRAGMENT(ip)) { 1180dcf0c0aSMarkus Carlstedt return; /* a fragmented IP packet */ 1190dcf0c0aSMarkus Carlstedt } 1200dcf0c0aSMarkus Carlstedt 12150dbce65SJean-Christophe Dubois ip_len = lduw_be_p(&ip->ip_len); 12250dbce65SJean-Christophe Dubois 12350dbce65SJean-Christophe Dubois /* Last, check that we have enough data for the all IP frame */ 12450dbce65SJean-Christophe Dubois if (length < ip_len) { 12550dbce65SJean-Christophe Dubois return; 12650dbce65SJean-Christophe Dubois } 12750dbce65SJean-Christophe Dubois 12850dbce65SJean-Christophe Dubois ip_len -= IP_HDR_GET_LEN(ip); 12950dbce65SJean-Christophe Dubois 13050dbce65SJean-Christophe Dubois switch (ip->ip_p) { 13150dbce65SJean-Christophe Dubois case IP_PROTO_TCP: 13250dbce65SJean-Christophe Dubois { 133*f5746335SBin Meng if (!(csum_flag & CSUM_TCP)) { 134*f5746335SBin Meng return; 135*f5746335SBin Meng } 136*f5746335SBin Meng 13750dbce65SJean-Christophe Dubois tcp_header *tcp = (tcp_header *)(ip + 1); 13850dbce65SJean-Christophe Dubois 13950dbce65SJean-Christophe Dubois if (ip_len < sizeof(tcp_header)) { 14050dbce65SJean-Christophe Dubois return; 14150dbce65SJean-Christophe Dubois } 14250dbce65SJean-Christophe Dubois 14350dbce65SJean-Christophe Dubois /* Set csum to 0 */ 14450dbce65SJean-Christophe Dubois stw_he_p(&tcp->th_sum, 0); 14550dbce65SJean-Christophe Dubois 14650dbce65SJean-Christophe Dubois csum = net_checksum_tcpudp(ip_len, ip->ip_p, 14750dbce65SJean-Christophe Dubois (uint8_t *)&ip->ip_src, 14850dbce65SJean-Christophe Dubois (uint8_t *)tcp); 14950dbce65SJean-Christophe Dubois 15050dbce65SJean-Christophe Dubois /* Store computed csum */ 15150dbce65SJean-Christophe Dubois stw_be_p(&tcp->th_sum, csum); 15250dbce65SJean-Christophe Dubois 1537200ac3cSMark McLoughlin break; 15450dbce65SJean-Christophe Dubois } 15550dbce65SJean-Christophe Dubois case IP_PROTO_UDP: 15650dbce65SJean-Christophe Dubois { 157*f5746335SBin Meng if (!(csum_flag & CSUM_UDP)) { 158*f5746335SBin Meng return; 159*f5746335SBin Meng } 160*f5746335SBin Meng 16150dbce65SJean-Christophe Dubois udp_header *udp = (udp_header *)(ip + 1); 16250dbce65SJean-Christophe Dubois 16350dbce65SJean-Christophe Dubois if (ip_len < sizeof(udp_header)) { 16450dbce65SJean-Christophe Dubois return; 16550dbce65SJean-Christophe Dubois } 16650dbce65SJean-Christophe Dubois 16750dbce65SJean-Christophe Dubois /* Set csum to 0 */ 16850dbce65SJean-Christophe Dubois stw_he_p(&udp->uh_sum, 0); 16950dbce65SJean-Christophe Dubois 17050dbce65SJean-Christophe Dubois csum = net_checksum_tcpudp(ip_len, ip->ip_p, 17150dbce65SJean-Christophe Dubois (uint8_t *)&ip->ip_src, 17250dbce65SJean-Christophe Dubois (uint8_t *)udp); 17350dbce65SJean-Christophe Dubois 17450dbce65SJean-Christophe Dubois /* Store computed csum */ 17550dbce65SJean-Christophe Dubois stw_be_p(&udp->uh_sum, csum); 17650dbce65SJean-Christophe Dubois 1777200ac3cSMark McLoughlin break; 17850dbce65SJean-Christophe Dubois } 1797200ac3cSMark McLoughlin default: 18050dbce65SJean-Christophe Dubois /* Can't handle any other protocol */ 18150dbce65SJean-Christophe Dubois break; 1827200ac3cSMark McLoughlin } 1837200ac3cSMark McLoughlin } 18484026301SDmitry Fleytman 18584026301SDmitry Fleytman uint32_t 18684026301SDmitry Fleytman net_checksum_add_iov(const struct iovec *iov, const unsigned int iov_cnt, 187eb700029SDmitry Fleytman uint32_t iov_off, uint32_t size, uint32_t csum_offset) 18884026301SDmitry Fleytman { 18984026301SDmitry Fleytman size_t iovec_off, buf_off; 19084026301SDmitry Fleytman unsigned int i; 19184026301SDmitry Fleytman uint32_t res = 0; 19284026301SDmitry Fleytman 19384026301SDmitry Fleytman iovec_off = 0; 19484026301SDmitry Fleytman buf_off = 0; 19584026301SDmitry Fleytman for (i = 0; i < iov_cnt && size; i++) { 19684026301SDmitry Fleytman if (iov_off < (iovec_off + iov[i].iov_len)) { 19784026301SDmitry Fleytman size_t len = MIN((iovec_off + iov[i].iov_len) - iov_off , size); 19884026301SDmitry Fleytman void *chunk_buf = iov[i].iov_base + (iov_off - iovec_off); 19984026301SDmitry Fleytman 200eb700029SDmitry Fleytman res += net_checksum_add_cont(len, chunk_buf, csum_offset); 201eb700029SDmitry Fleytman csum_offset += len; 20284026301SDmitry Fleytman 20384026301SDmitry Fleytman buf_off += len; 20484026301SDmitry Fleytman iov_off += len; 20584026301SDmitry Fleytman size -= len; 20684026301SDmitry Fleytman } 20784026301SDmitry Fleytman iovec_off += iov[i].iov_len; 20884026301SDmitry Fleytman } 20984026301SDmitry Fleytman return res; 21084026301SDmitry Fleytman } 211