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
net_checksum_add_cont(int len,uint8_t * buf,int seq)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
net_checksum_finish(uint32_t sum)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
net_checksum_tcpudp(uint16_t length,uint16_t proto,uint8_t * addrs,uint8_t * buf)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
net_checksum_calculate(void * data,int length,int csum_flag)60*5930e5ccSAkihiko Odaki void net_checksum_calculate(void *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
770a19d879SMichael Tokarev /* Handle the optional 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
990a19d879SMichael Tokarev /* Now check we have an IP header (with an optional VLAN header) */
100ade6bad1SJean-Christophe Dubois if (length < sizeof(struct ip_header)) {
101ade6bad1SJean-Christophe Dubois return;
102ade6bad1SJean-Christophe Dubois }
103ade6bad1SJean-Christophe Dubois
104*5930e5ccSAkihiko Odaki ip = (struct ip_header *)((uint8_t *)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 */
111f5746335SBin 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);
115f5746335SBin 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 {
133f5746335SBin Meng if (!(csum_flag & CSUM_TCP)) {
134f5746335SBin Meng return;
135f5746335SBin Meng }
136f5746335SBin 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 {
157f5746335SBin Meng if (!(csum_flag & CSUM_UDP)) {
158f5746335SBin Meng return;
159f5746335SBin Meng }
160f5746335SBin 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
net_checksum_add_iov(const struct iovec * iov,const unsigned int iov_cnt,uint32_t iov_off,uint32_t size,uint32_t csum_offset)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 {
189fe4bd917SRichard Henderson size_t iovec_off;
19084026301SDmitry Fleytman unsigned int i;
19184026301SDmitry Fleytman uint32_t res = 0;
19284026301SDmitry Fleytman
19384026301SDmitry Fleytman iovec_off = 0;
19484026301SDmitry Fleytman for (i = 0; i < iov_cnt && size; i++) {
19584026301SDmitry Fleytman if (iov_off < (iovec_off + iov[i].iov_len)) {
19684026301SDmitry Fleytman size_t len = MIN((iovec_off + iov[i].iov_len) - iov_off , size);
19784026301SDmitry Fleytman void *chunk_buf = iov[i].iov_base + (iov_off - iovec_off);
19884026301SDmitry Fleytman
199eb700029SDmitry Fleytman res += net_checksum_add_cont(len, chunk_buf, csum_offset);
200eb700029SDmitry Fleytman csum_offset += len;
20184026301SDmitry Fleytman
20284026301SDmitry Fleytman iov_off += len;
20384026301SDmitry Fleytman size -= len;
20484026301SDmitry Fleytman }
20584026301SDmitry Fleytman iovec_off += iov[i].iov_len;
20684026301SDmitry Fleytman }
20784026301SDmitry Fleytman return res;
20884026301SDmitry Fleytman }
209