qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

checksum.c (5545B)


      1 /*
      2  *  IP checksumming functions.
      3  *  (c) 2008 Gerd Hoffmann <kraxel@redhat.com>
      4  *
      5  *  This program is free software; you can redistribute it and/or modify
      6  *  it under the terms of the GNU General Public License as published by
      7  *  the Free Software Foundation; under version 2 or later of the License.
      8  *
      9  *  This program is distributed in the hope that it will be useful,
     10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     12  *  GNU General Public License for more details.
     13  *
     14  *  You should have received a copy of the GNU General Public License
     15  *  along with this program; if not, see <http://www.gnu.org/licenses/>.
     16  */
     17 
     18 #include "qemu/osdep.h"
     19 #include "net/checksum.h"
     20 #include "net/eth.h"
     21 
     22 uint32_t net_checksum_add_cont(int len, uint8_t *buf, int seq)
     23 {
     24     uint32_t sum1 = 0, sum2 = 0;
     25     int i;
     26 
     27     for (i = 0; i < len - 1; i += 2) {
     28         sum1 += (uint32_t)buf[i];
     29         sum2 += (uint32_t)buf[i + 1];
     30     }
     31     if (i < len) {
     32         sum1 += (uint32_t)buf[i];
     33     }
     34 
     35     if (seq & 1) {
     36         return sum1 + (sum2 << 8);
     37     } else {
     38         return sum2 + (sum1 << 8);
     39     }
     40 }
     41 
     42 uint16_t net_checksum_finish(uint32_t sum)
     43 {
     44     while (sum>>16)
     45         sum = (sum & 0xFFFF)+(sum >> 16);
     46     return ~sum;
     47 }
     48 
     49 uint16_t net_checksum_tcpudp(uint16_t length, uint16_t proto,
     50                              uint8_t *addrs, uint8_t *buf)
     51 {
     52     uint32_t sum = 0;
     53 
     54     sum += net_checksum_add(length, buf);         // payload
     55     sum += net_checksum_add(8, addrs);            // src + dst address
     56     sum += proto + length;                        // protocol & length
     57     return net_checksum_finish(sum);
     58 }
     59 
     60 void net_checksum_calculate(uint8_t *data, int length, int csum_flag)
     61 {
     62     int mac_hdr_len, ip_len;
     63     struct ip_header *ip;
     64     uint16_t csum;
     65 
     66     /*
     67      * Note: We cannot assume "data" is aligned, so the all code uses
     68      * some macros that take care of possible unaligned access for
     69      * struct members (just in case).
     70      */
     71 
     72     /* Ensure we have at least an Eth header */
     73     if (length < sizeof(struct eth_header)) {
     74         return;
     75     }
     76 
     77     /* Handle the optionnal VLAN headers */
     78     switch (lduw_be_p(&PKT_GET_ETH_HDR(data)->h_proto)) {
     79     case ETH_P_VLAN:
     80         mac_hdr_len = sizeof(struct eth_header) +
     81                      sizeof(struct vlan_header);
     82         break;
     83     case ETH_P_DVLAN:
     84         if (lduw_be_p(&PKT_GET_VLAN_HDR(data)->h_proto) == ETH_P_VLAN) {
     85             mac_hdr_len = sizeof(struct eth_header) +
     86                          2 * sizeof(struct vlan_header);
     87         } else {
     88             mac_hdr_len = sizeof(struct eth_header) +
     89                          sizeof(struct vlan_header);
     90         }
     91         break;
     92     default:
     93         mac_hdr_len = sizeof(struct eth_header);
     94         break;
     95     }
     96 
     97     length -= mac_hdr_len;
     98 
     99     /* Now check we have an IP header (with an optionnal VLAN header) */
    100     if (length < sizeof(struct ip_header)) {
    101         return;
    102     }
    103 
    104     ip = (struct ip_header *)(data + mac_hdr_len);
    105 
    106     if (IP_HEADER_VERSION(ip) != IP_HEADER_VERSION_4) {
    107         return; /* not IPv4 */
    108     }
    109 
    110     /* Calculate IP checksum */
    111     if (csum_flag & CSUM_IP) {
    112         stw_he_p(&ip->ip_sum, 0);
    113         csum = net_raw_checksum((uint8_t *)ip, IP_HDR_GET_LEN(ip));
    114         stw_be_p(&ip->ip_sum, csum);
    115     }
    116 
    117     if (IP4_IS_FRAGMENT(ip)) {
    118         return; /* a fragmented IP packet */
    119     }
    120 
    121     ip_len = lduw_be_p(&ip->ip_len);
    122 
    123     /* Last, check that we have enough data for the all IP frame */
    124     if (length < ip_len) {
    125         return;
    126     }
    127 
    128     ip_len -= IP_HDR_GET_LEN(ip);
    129 
    130     switch (ip->ip_p) {
    131     case IP_PROTO_TCP:
    132     {
    133         if (!(csum_flag & CSUM_TCP)) {
    134             return;
    135         }
    136 
    137         tcp_header *tcp = (tcp_header *)(ip + 1);
    138 
    139         if (ip_len < sizeof(tcp_header)) {
    140             return;
    141         }
    142 
    143         /* Set csum to 0 */
    144         stw_he_p(&tcp->th_sum, 0);
    145 
    146         csum = net_checksum_tcpudp(ip_len, ip->ip_p,
    147                                    (uint8_t *)&ip->ip_src,
    148                                    (uint8_t *)tcp);
    149 
    150         /* Store computed csum */
    151         stw_be_p(&tcp->th_sum, csum);
    152 
    153         break;
    154     }
    155     case IP_PROTO_UDP:
    156     {
    157         if (!(csum_flag & CSUM_UDP)) {
    158             return;
    159         }
    160 
    161         udp_header *udp = (udp_header *)(ip + 1);
    162 
    163         if (ip_len < sizeof(udp_header)) {
    164             return;
    165         }
    166 
    167         /* Set csum to 0 */
    168         stw_he_p(&udp->uh_sum, 0);
    169 
    170         csum = net_checksum_tcpudp(ip_len, ip->ip_p,
    171                                    (uint8_t *)&ip->ip_src,
    172                                    (uint8_t *)udp);
    173 
    174         /* Store computed csum */
    175         stw_be_p(&udp->uh_sum, csum);
    176 
    177         break;
    178     }
    179     default:
    180         /* Can't handle any other protocol */
    181         break;
    182     }
    183 }
    184 
    185 uint32_t
    186 net_checksum_add_iov(const struct iovec *iov, const unsigned int iov_cnt,
    187                      uint32_t iov_off, uint32_t size, uint32_t csum_offset)
    188 {
    189     size_t iovec_off;
    190     unsigned int i;
    191     uint32_t res = 0;
    192 
    193     iovec_off = 0;
    194     for (i = 0; i < iov_cnt && size; i++) {
    195         if (iov_off < (iovec_off + iov[i].iov_len)) {
    196             size_t len = MIN((iovec_off + iov[i].iov_len) - iov_off , size);
    197             void *chunk_buf = iov[i].iov_base + (iov_off - iovec_off);
    198 
    199             res += net_checksum_add_cont(len, chunk_buf, csum_offset);
    200             csum_offset += len;
    201 
    202             iov_off += len;
    203             size -= len;
    204         }
    205         iovec_off += iov[i].iov_len;
    206     }
    207     return res;
    208 }