cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

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
     22uint32_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
     42uint16_t net_checksum_finish(uint32_t sum)
     43{
     44    while (sum>>16)
     45        sum = (sum & 0xFFFF)+(sum >> 16);
     46    return ~sum;
     47}
     48
     49uint16_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
     60void 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
    185uint32_t
    186net_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}