qemu

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

colo.c (6720B)


      1 /*
      2  * COarse-grain LOck-stepping Virtual Machines for Non-stop Service (COLO)
      3  * (a.k.a. Fault Tolerance or Continuous Replication)
      4  *
      5  * Copyright (c) 2016 HUAWEI TECHNOLOGIES CO., LTD.
      6  * Copyright (c) 2016 FUJITSU LIMITED
      7  * Copyright (c) 2016 Intel Corporation
      8  *
      9  * Author: Zhang Chen <zhangchen.fnst@cn.fujitsu.com>
     10  *
     11  * This work is licensed under the terms of the GNU GPL, version 2 or
     12  * later.  See the COPYING file in the top-level directory.
     13  */
     14 
     15 #include "qemu/osdep.h"
     16 #include "trace.h"
     17 #include "colo.h"
     18 #include "util.h"
     19 
     20 uint32_t connection_key_hash(const void *opaque)
     21 {
     22     const ConnectionKey *key = opaque;
     23     uint32_t a, b, c;
     24 
     25     /* Jenkins hash */
     26     a = b = c = JHASH_INITVAL + sizeof(*key);
     27     a += key->src.s_addr;
     28     b += key->dst.s_addr;
     29     c += (key->src_port | key->dst_port << 16);
     30     __jhash_mix(a, b, c);
     31 
     32     a += key->ip_proto;
     33     __jhash_final(a, b, c);
     34 
     35     return c;
     36 }
     37 
     38 int connection_key_equal(const void *key1, const void *key2)
     39 {
     40     return memcmp(key1, key2, sizeof(ConnectionKey)) == 0;
     41 }
     42 
     43 int parse_packet_early(Packet *pkt)
     44 {
     45     int network_length;
     46     static const uint8_t vlan[] = {0x81, 0x00};
     47     uint8_t *data = pkt->data;
     48     uint16_t l3_proto;
     49     ssize_t l2hdr_len;
     50 
     51     assert(data);
     52 
     53     /* Check the received vnet_hdr_len then add the offset */
     54     if ((pkt->vnet_hdr_len > sizeof(struct virtio_net_hdr_v1_hash)) ||
     55         (pkt->size < sizeof(struct eth_header) + sizeof(struct vlan_header) +
     56         pkt->vnet_hdr_len)) {
     57         /*
     58          * The received remote packet maybe misconfiguration here,
     59          * Please enable/disable filter module's the vnet_hdr flag at
     60          * the same time.
     61          */
     62         trace_colo_proxy_main_vnet_info("This received packet load wrong ",
     63                                         pkt->vnet_hdr_len, pkt->size);
     64         return 1;
     65     }
     66     data += pkt->vnet_hdr_len;
     67 
     68     l2hdr_len = eth_get_l2_hdr_length(data);
     69 
     70     /*
     71      * TODO: support vlan.
     72      */
     73     if (!memcmp(&data[12], vlan, sizeof(vlan))) {
     74         trace_colo_proxy_main("COLO-proxy don't support vlan");
     75         return 1;
     76     }
     77 
     78     pkt->network_header = data + l2hdr_len;
     79 
     80     const struct iovec l2vec = {
     81         .iov_base = (void *) data,
     82         .iov_len = l2hdr_len
     83     };
     84     l3_proto = eth_get_l3_proto(&l2vec, 1, l2hdr_len);
     85 
     86     if (l3_proto != ETH_P_IP) {
     87         return 1;
     88     }
     89 
     90     network_length = pkt->ip->ip_hl * 4;
     91     if (pkt->size < l2hdr_len + network_length + pkt->vnet_hdr_len) {
     92         trace_colo_proxy_main("pkt->size < network_header + network_length");
     93         return 1;
     94     }
     95     pkt->transport_header = pkt->network_header + network_length;
     96 
     97     return 0;
     98 }
     99 
    100 void extract_ip_and_port(uint32_t tmp_ports, ConnectionKey *key,
    101                          Packet *pkt, bool reverse)
    102 {
    103     if (reverse) {
    104         key->src = pkt->ip->ip_dst;
    105         key->dst = pkt->ip->ip_src;
    106         key->src_port = ntohs(tmp_ports & 0xffff);
    107         key->dst_port = ntohs(tmp_ports >> 16);
    108     } else {
    109         key->src = pkt->ip->ip_src;
    110         key->dst = pkt->ip->ip_dst;
    111         key->src_port = ntohs(tmp_ports >> 16);
    112         key->dst_port = ntohs(tmp_ports & 0xffff);
    113     }
    114 }
    115 
    116 void fill_connection_key(Packet *pkt, ConnectionKey *key, bool reverse)
    117 {
    118     uint32_t tmp_ports = 0;
    119 
    120     key->ip_proto = pkt->ip->ip_p;
    121 
    122     switch (key->ip_proto) {
    123     case IPPROTO_TCP:
    124     case IPPROTO_UDP:
    125     case IPPROTO_DCCP:
    126     case IPPROTO_ESP:
    127     case IPPROTO_SCTP:
    128     case IPPROTO_UDPLITE:
    129         tmp_ports = *(uint32_t *)(pkt->transport_header);
    130         break;
    131     case IPPROTO_AH:
    132         tmp_ports = *(uint32_t *)(pkt->transport_header + 4);
    133         break;
    134     default:
    135         break;
    136     }
    137 
    138     extract_ip_and_port(tmp_ports, key, pkt, reverse);
    139 }
    140 
    141 Connection *connection_new(ConnectionKey *key)
    142 {
    143     Connection *conn = g_slice_new0(Connection);
    144 
    145     conn->ip_proto = key->ip_proto;
    146     conn->processing = false;
    147     conn->tcp_state = TCPS_CLOSED;
    148     g_queue_init(&conn->primary_list);
    149     g_queue_init(&conn->secondary_list);
    150 
    151     return conn;
    152 }
    153 
    154 void connection_destroy(void *opaque)
    155 {
    156     Connection *conn = opaque;
    157 
    158     g_queue_foreach(&conn->primary_list, packet_destroy, NULL);
    159     g_queue_clear(&conn->primary_list);
    160     g_queue_foreach(&conn->secondary_list, packet_destroy, NULL);
    161     g_queue_clear(&conn->secondary_list);
    162     g_slice_free(Connection, conn);
    163 }
    164 
    165 Packet *packet_new(const void *data, int size, int vnet_hdr_len)
    166 {
    167     Packet *pkt = g_slice_new0(Packet);
    168 
    169     pkt->data = g_memdup(data, size);
    170     pkt->size = size;
    171     pkt->creation_ms = qemu_clock_get_ms(QEMU_CLOCK_HOST);
    172     pkt->vnet_hdr_len = vnet_hdr_len;
    173 
    174     return pkt;
    175 }
    176 
    177 /*
    178  * packet_new_nocopy will not copy data, so the caller can't release
    179  * the data. And it will be released in packet_destroy.
    180  */
    181 Packet *packet_new_nocopy(void *data, int size, int vnet_hdr_len)
    182 {
    183     Packet *pkt = g_slice_new0(Packet);
    184 
    185     pkt->data = data;
    186     pkt->size = size;
    187     pkt->creation_ms = qemu_clock_get_ms(QEMU_CLOCK_HOST);
    188     pkt->vnet_hdr_len = vnet_hdr_len;
    189 
    190     return pkt;
    191 }
    192 
    193 void packet_destroy(void *opaque, void *user_data)
    194 {
    195     Packet *pkt = opaque;
    196 
    197     g_free(pkt->data);
    198     g_slice_free(Packet, pkt);
    199 }
    200 
    201 void packet_destroy_partial(void *opaque, void *user_data)
    202 {
    203     Packet *pkt = opaque;
    204 
    205     g_slice_free(Packet, pkt);
    206 }
    207 
    208 /*
    209  * Clear hashtable, stop this hash growing really huge
    210  */
    211 void connection_hashtable_reset(GHashTable *connection_track_table)
    212 {
    213     g_hash_table_remove_all(connection_track_table);
    214 }
    215 
    216 /* if not found, create a new connection and add to hash table */
    217 Connection *connection_get(GHashTable *connection_track_table,
    218                            ConnectionKey *key,
    219                            GQueue *conn_list)
    220 {
    221     Connection *conn = g_hash_table_lookup(connection_track_table, key);
    222 
    223     if (conn == NULL) {
    224         ConnectionKey *new_key = g_memdup(key, sizeof(*key));
    225 
    226         conn = connection_new(key);
    227 
    228         if (g_hash_table_size(connection_track_table) > HASHTABLE_MAX_SIZE) {
    229             trace_colo_proxy_main("colo proxy connection hashtable full,"
    230                                   " clear it");
    231             connection_hashtable_reset(connection_track_table);
    232             /*
    233              * clear the conn_list
    234              */
    235             while (conn_list && !g_queue_is_empty(conn_list)) {
    236                 connection_destroy(g_queue_pop_head(conn_list));
    237             }
    238         }
    239 
    240         g_hash_table_insert(connection_track_table, new_key, conn);
    241     }
    242 
    243     return conn;
    244 }
    245 
    246 bool connection_has_tracked(GHashTable *connection_track_table,
    247                             ConnectionKey *key)
    248 {
    249     Connection *conn = g_hash_table_lookup(connection_track_table, key);
    250 
    251     return conn ? true : false;
    252 }