1e6eee8abSZhang Chen /* 2e6eee8abSZhang Chen * Copyright (c) 2016 HUAWEI TECHNOLOGIES CO., LTD. 3e6eee8abSZhang Chen * Copyright (c) 2016 FUJITSU LIMITED 4e6eee8abSZhang Chen * Copyright (c) 2016 Intel Corporation 5e6eee8abSZhang Chen * 6e6eee8abSZhang Chen * Author: Zhang Chen <zhangchen.fnst@cn.fujitsu.com> 7e6eee8abSZhang Chen * 8e6eee8abSZhang Chen * This work is licensed under the terms of the GNU GPL, version 2 or 9e6eee8abSZhang Chen * later. See the COPYING file in the top-level directory. 10e6eee8abSZhang Chen */ 11e6eee8abSZhang Chen 12e6eee8abSZhang Chen #include "qemu/osdep.h" 1330656b09SZhang Chen #include "trace.h" 14f27f01dbSMichael S. Tsirkin #include "colo.h" 15e6eee8abSZhang Chen #include "net/filter.h" 16e6eee8abSZhang Chen #include "net/net.h" 174b39bdceSZhang Chen #include "qemu/error-report.h" 18e6eee8abSZhang Chen #include "qom/object.h" 19e6eee8abSZhang Chen #include "qemu/main-loop.h" 20e6eee8abSZhang Chen #include "qemu/iov.h" 21e6eee8abSZhang Chen #include "net/checksum.h" 2224525e93SZhang Chen #include "net/colo.h" 2324525e93SZhang Chen #include "migration/colo.h" 24e05ae1d9SMarc-André Lureau #include "util.h" 25e6eee8abSZhang Chen 26*db1015e9SEduardo Habkost #define TYPE_FILTER_REWRITER "filter-rewriter" 27*db1015e9SEduardo Habkost typedef struct RewriterState RewriterState; 28e6eee8abSZhang Chen #define FILTER_COLO_REWRITER(obj) \ 29e6eee8abSZhang Chen OBJECT_CHECK(RewriterState, (obj), TYPE_FILTER_REWRITER) 30e6eee8abSZhang Chen 3124525e93SZhang Chen #define FAILOVER_MODE_ON true 3224525e93SZhang Chen #define FAILOVER_MODE_OFF false 33e6eee8abSZhang Chen 34*db1015e9SEduardo Habkost struct RewriterState { 35e6eee8abSZhang Chen NetFilterState parent_obj; 36e6eee8abSZhang Chen NetQueue *incoming_queue; 37e6eee8abSZhang Chen /* hashtable to save connection */ 38e6eee8abSZhang Chen GHashTable *connection_track_table; 394b39bdceSZhang Chen bool vnet_hdr; 4024525e93SZhang Chen bool failover_mode; 41*db1015e9SEduardo Habkost }; 42e6eee8abSZhang Chen 4324525e93SZhang Chen static void filter_rewriter_failover_mode(RewriterState *s) 4424525e93SZhang Chen { 4524525e93SZhang Chen s->failover_mode = FAILOVER_MODE_ON; 4624525e93SZhang Chen } 4724525e93SZhang Chen 48e6eee8abSZhang Chen static void filter_rewriter_flush(NetFilterState *nf) 49e6eee8abSZhang Chen { 50e6eee8abSZhang Chen RewriterState *s = FILTER_COLO_REWRITER(nf); 51e6eee8abSZhang Chen 52e6eee8abSZhang Chen if (!qemu_net_queue_flush(s->incoming_queue)) { 53e6eee8abSZhang Chen /* Unable to empty the queue, purge remaining packets */ 54e6eee8abSZhang Chen qemu_net_queue_purge(s->incoming_queue, nf->netdev); 55e6eee8abSZhang Chen } 56e6eee8abSZhang Chen } 57e6eee8abSZhang Chen 58afe46124SZhang Chen /* 59afe46124SZhang Chen * Return 1 on success, if return 0 means the pkt 60afe46124SZhang Chen * is not TCP packet 61afe46124SZhang Chen */ 62afe46124SZhang Chen static int is_tcp_packet(Packet *pkt) 63afe46124SZhang Chen { 64afe46124SZhang Chen if (!parse_packet_early(pkt) && 65afe46124SZhang Chen pkt->ip->ip_p == IPPROTO_TCP) { 66afe46124SZhang Chen return 1; 67afe46124SZhang Chen } else { 68afe46124SZhang Chen return 0; 69afe46124SZhang Chen } 70afe46124SZhang Chen } 71afe46124SZhang Chen 7230656b09SZhang Chen /* handle tcp packet from primary guest */ 736214231aSZhang Chen static int handle_primary_tcp_pkt(RewriterState *rf, 7430656b09SZhang Chen Connection *conn, 756214231aSZhang Chen Packet *pkt, ConnectionKey *key) 7630656b09SZhang Chen { 77e05ae1d9SMarc-André Lureau struct tcp_hdr *tcp_pkt; 7830656b09SZhang Chen 79e05ae1d9SMarc-André Lureau tcp_pkt = (struct tcp_hdr *)pkt->transport_header; 80d87aa138SStefan Hajnoczi if (trace_event_get_state_backends(TRACE_COLO_FILTER_REWRITER_DEBUG)) { 812061c14cSZhang Chen trace_colo_filter_rewriter_pkt_info(__func__, 822061c14cSZhang Chen inet_ntoa(pkt->ip->ip_src), inet_ntoa(pkt->ip->ip_dst), 8330656b09SZhang Chen ntohl(tcp_pkt->th_seq), ntohl(tcp_pkt->th_ack), 8430656b09SZhang Chen tcp_pkt->th_flags); 8530656b09SZhang Chen trace_colo_filter_rewriter_conn_offset(conn->offset); 8630656b09SZhang Chen } 8730656b09SZhang Chen 886214231aSZhang Chen if (((tcp_pkt->th_flags & (TH_ACK | TH_SYN)) == (TH_ACK | TH_SYN)) && 896214231aSZhang Chen conn->tcp_state == TCPS_SYN_SENT) { 906214231aSZhang Chen conn->tcp_state = TCPS_ESTABLISHED; 916214231aSZhang Chen } 926214231aSZhang Chen 9330656b09SZhang Chen if (((tcp_pkt->th_flags & (TH_ACK | TH_SYN)) == TH_SYN)) { 9430656b09SZhang Chen /* 9530656b09SZhang Chen * we use this flag update offset func 9630656b09SZhang Chen * run once in independent tcp connection 9730656b09SZhang Chen */ 986214231aSZhang Chen conn->tcp_state = TCPS_SYN_RECEIVED; 9930656b09SZhang Chen } 10030656b09SZhang Chen 10130656b09SZhang Chen if (((tcp_pkt->th_flags & (TH_ACK | TH_SYN)) == TH_ACK)) { 1026214231aSZhang Chen if (conn->tcp_state == TCPS_SYN_RECEIVED) { 10330656b09SZhang Chen /* 10430656b09SZhang Chen * offset = secondary_seq - primary seq 10530656b09SZhang Chen * ack packet sent by guest from primary node, 10630656b09SZhang Chen * so we use th_ack - 1 get primary_seq 10730656b09SZhang Chen */ 10830656b09SZhang Chen conn->offset -= (ntohl(tcp_pkt->th_ack) - 1); 1096214231aSZhang Chen conn->tcp_state = TCPS_ESTABLISHED; 11030656b09SZhang Chen } 111db0a762eSzhanghailiang if (conn->offset) { 11230656b09SZhang Chen /* handle packets to the secondary from the primary */ 11330656b09SZhang Chen tcp_pkt->th_ack = htonl(ntohl(tcp_pkt->th_ack) + conn->offset); 11430656b09SZhang Chen 1156ce310b5SZhang Chen net_checksum_calculate((uint8_t *)pkt->data + pkt->vnet_hdr_len, 1166ce310b5SZhang Chen pkt->size - pkt->vnet_hdr_len); 11730656b09SZhang Chen } 1186214231aSZhang Chen 1196214231aSZhang Chen /* 1206214231aSZhang Chen * Passive close step 3 1216214231aSZhang Chen */ 1226214231aSZhang Chen if ((conn->tcp_state == TCPS_LAST_ACK) && 1236214231aSZhang Chen (ntohl(tcp_pkt->th_ack) == (conn->fin_ack_seq + 1))) { 1246214231aSZhang Chen conn->tcp_state = TCPS_CLOSED; 1256214231aSZhang Chen g_hash_table_remove(rf->connection_track_table, key); 1266214231aSZhang Chen } 1276214231aSZhang Chen } 1286214231aSZhang Chen 1296214231aSZhang Chen if ((tcp_pkt->th_flags & TH_FIN) == TH_FIN) { 1306214231aSZhang Chen /* 1316214231aSZhang Chen * Passive close. 1326214231aSZhang Chen * Step 1: 1336214231aSZhang Chen * The *server* side of this connect is VM, *client* tries to close 1346214231aSZhang Chen * the connection. We will into CLOSE_WAIT status. 1356214231aSZhang Chen * 1366214231aSZhang Chen * Step 2: 1376214231aSZhang Chen * In this step we will into LAST_ACK status. 1386214231aSZhang Chen * 1396214231aSZhang Chen * We got 'fin=1, ack=1' packet from server side, we need to 1406214231aSZhang Chen * record the seq of 'fin=1, ack=1' packet. 1416214231aSZhang Chen * 1426214231aSZhang Chen * Step 3: 1436214231aSZhang Chen * We got 'ack=1' packets from client side, it acks 'fin=1, ack=1' 1446214231aSZhang Chen * packet from server side. From this point, we can ensure that there 1456214231aSZhang Chen * will be no packets in the connection, except that, some errors 1466214231aSZhang Chen * happen between the path of 'filter object' and vNIC, if this rare 1476214231aSZhang Chen * case really happen, we can still create a new connection, 1486214231aSZhang Chen * So it is safe to remove the connection from connection_track_table. 1496214231aSZhang Chen * 1506214231aSZhang Chen */ 1516214231aSZhang Chen if (conn->tcp_state == TCPS_ESTABLISHED) { 1526214231aSZhang Chen conn->tcp_state = TCPS_CLOSE_WAIT; 1536214231aSZhang Chen } 1546214231aSZhang Chen 1556214231aSZhang Chen /* 1566214231aSZhang Chen * Active close step 2. 1576214231aSZhang Chen */ 1586214231aSZhang Chen if (conn->tcp_state == TCPS_FIN_WAIT_1) { 1596214231aSZhang Chen /* 1606214231aSZhang Chen * For simplify implementation, we needn't wait 2MSL time 1616214231aSZhang Chen * in filter rewriter. Because guest kernel will track the 1626214231aSZhang Chen * TCP status and wait 2MSL time, if client resend the FIN 1636214231aSZhang Chen * packet, guest will apply the last ACK too. 164013a6202SZhang Chen * So, we skip the TCPS_TIME_WAIT state here and go straight 165013a6202SZhang Chen * to TCPS_CLOSED state. 1666214231aSZhang Chen */ 1676214231aSZhang Chen conn->tcp_state = TCPS_CLOSED; 1686214231aSZhang Chen g_hash_table_remove(rf->connection_track_table, key); 1696214231aSZhang Chen } 170db0a762eSzhanghailiang } 17130656b09SZhang Chen 17230656b09SZhang Chen return 0; 17330656b09SZhang Chen } 17430656b09SZhang Chen 17530656b09SZhang Chen /* handle tcp packet from secondary guest */ 1766214231aSZhang Chen static int handle_secondary_tcp_pkt(RewriterState *rf, 17730656b09SZhang Chen Connection *conn, 1786214231aSZhang Chen Packet *pkt, ConnectionKey *key) 17930656b09SZhang Chen { 180e05ae1d9SMarc-André Lureau struct tcp_hdr *tcp_pkt; 18130656b09SZhang Chen 182e05ae1d9SMarc-André Lureau tcp_pkt = (struct tcp_hdr *)pkt->transport_header; 18330656b09SZhang Chen 184d87aa138SStefan Hajnoczi if (trace_event_get_state_backends(TRACE_COLO_FILTER_REWRITER_DEBUG)) { 1852061c14cSZhang Chen trace_colo_filter_rewriter_pkt_info(__func__, 1862061c14cSZhang Chen inet_ntoa(pkt->ip->ip_src), inet_ntoa(pkt->ip->ip_dst), 18730656b09SZhang Chen ntohl(tcp_pkt->th_seq), ntohl(tcp_pkt->th_ack), 18830656b09SZhang Chen tcp_pkt->th_flags); 18930656b09SZhang Chen trace_colo_filter_rewriter_conn_offset(conn->offset); 19030656b09SZhang Chen } 19130656b09SZhang Chen 1926214231aSZhang Chen if (conn->tcp_state == TCPS_SYN_RECEIVED && 1936214231aSZhang Chen ((tcp_pkt->th_flags & (TH_ACK | TH_SYN)) == (TH_ACK | TH_SYN))) { 19430656b09SZhang Chen /* 19530656b09SZhang Chen * save offset = secondary_seq and then 19630656b09SZhang Chen * in handle_primary_tcp_pkt make offset 19730656b09SZhang Chen * = secondary_seq - primary_seq 19830656b09SZhang Chen */ 19930656b09SZhang Chen conn->offset = ntohl(tcp_pkt->th_seq); 20030656b09SZhang Chen } 20130656b09SZhang Chen 2026214231aSZhang Chen /* VM active connect */ 2036214231aSZhang Chen if (conn->tcp_state == TCPS_CLOSED && 2046214231aSZhang Chen ((tcp_pkt->th_flags & (TH_ACK | TH_SYN)) == TH_SYN)) { 2056214231aSZhang Chen conn->tcp_state = TCPS_SYN_SENT; 2066214231aSZhang Chen } 2076214231aSZhang Chen 20830656b09SZhang Chen if ((tcp_pkt->th_flags & (TH_ACK | TH_SYN)) == TH_ACK) { 209db0a762eSzhanghailiang /* Only need to adjust seq while offset is Non-zero */ 210db0a762eSzhanghailiang if (conn->offset) { 21130656b09SZhang Chen /* handle packets to the primary from the secondary*/ 21230656b09SZhang Chen tcp_pkt->th_seq = htonl(ntohl(tcp_pkt->th_seq) - conn->offset); 21330656b09SZhang Chen 2146ce310b5SZhang Chen net_checksum_calculate((uint8_t *)pkt->data + pkt->vnet_hdr_len, 2156ce310b5SZhang Chen pkt->size - pkt->vnet_hdr_len); 21630656b09SZhang Chen } 217db0a762eSzhanghailiang } 21830656b09SZhang Chen 2196214231aSZhang Chen /* 2206214231aSZhang Chen * Passive close step 2: 2216214231aSZhang Chen */ 2226214231aSZhang Chen if (conn->tcp_state == TCPS_CLOSE_WAIT && 2236214231aSZhang Chen (tcp_pkt->th_flags & (TH_ACK | TH_FIN)) == (TH_ACK | TH_FIN)) { 2246214231aSZhang Chen conn->fin_ack_seq = ntohl(tcp_pkt->th_seq); 2256214231aSZhang Chen conn->tcp_state = TCPS_LAST_ACK; 2266214231aSZhang Chen } 2276214231aSZhang Chen 2286214231aSZhang Chen /* 2296214231aSZhang Chen * Active close 2306214231aSZhang Chen * 2316214231aSZhang Chen * Step 1: 2326214231aSZhang Chen * The *server* side of this connect is VM, *server* tries to close 2336214231aSZhang Chen * the connection. 2346214231aSZhang Chen * 2356214231aSZhang Chen * Step 2: 2366214231aSZhang Chen * We will into CLOSE_WAIT status. 2376214231aSZhang Chen * We simplify the TCPS_FIN_WAIT_2, TCPS_TIME_WAIT and 2386214231aSZhang Chen * CLOSING status. 2396214231aSZhang Chen */ 2406214231aSZhang Chen if (conn->tcp_state == TCPS_ESTABLISHED && 2416214231aSZhang Chen (tcp_pkt->th_flags & (TH_ACK | TH_FIN)) == TH_FIN) { 2426214231aSZhang Chen conn->tcp_state = TCPS_FIN_WAIT_1; 2436214231aSZhang Chen } 2446214231aSZhang Chen 24530656b09SZhang Chen return 0; 24630656b09SZhang Chen } 24730656b09SZhang Chen 248e6eee8abSZhang Chen static ssize_t colo_rewriter_receive_iov(NetFilterState *nf, 249e6eee8abSZhang Chen NetClientState *sender, 250e6eee8abSZhang Chen unsigned flags, 251e6eee8abSZhang Chen const struct iovec *iov, 252e6eee8abSZhang Chen int iovcnt, 253e6eee8abSZhang Chen NetPacketSent *sent_cb) 254e6eee8abSZhang Chen { 255afe46124SZhang Chen RewriterState *s = FILTER_COLO_REWRITER(nf); 256afe46124SZhang Chen Connection *conn; 257afe46124SZhang Chen ConnectionKey key; 258afe46124SZhang Chen Packet *pkt; 259afe46124SZhang Chen ssize_t size = iov_size(iov, iovcnt); 2604b39bdceSZhang Chen ssize_t vnet_hdr_len = 0; 261afe46124SZhang Chen char *buf = g_malloc0(size); 262afe46124SZhang Chen 263afe46124SZhang Chen iov_to_buf(iov, iovcnt, 0, buf, size); 2644b39bdceSZhang Chen 2654b39bdceSZhang Chen if (s->vnet_hdr) { 2664b39bdceSZhang Chen vnet_hdr_len = nf->netdev->vnet_hdr_len; 2674b39bdceSZhang Chen } 2684b39bdceSZhang Chen 2694b39bdceSZhang Chen pkt = packet_new(buf, size, vnet_hdr_len); 2702061c14cSZhang Chen g_free(buf); 271afe46124SZhang Chen 272e6eee8abSZhang Chen /* 273e6eee8abSZhang Chen * if we get tcp packet 274e6eee8abSZhang Chen * we will rewrite it to make secondary guest's 275e6eee8abSZhang Chen * connection established successfully 276e6eee8abSZhang Chen */ 277afe46124SZhang Chen if (pkt && is_tcp_packet(pkt)) { 278afe46124SZhang Chen 279afe46124SZhang Chen fill_connection_key(pkt, &key); 280afe46124SZhang Chen 281afe46124SZhang Chen if (sender == nf->netdev) { 282afe46124SZhang Chen /* 283afe46124SZhang Chen * We need make tcp TX and RX packet 284afe46124SZhang Chen * into one connection. 285afe46124SZhang Chen */ 286afe46124SZhang Chen reverse_connection_key(&key); 287afe46124SZhang Chen } 28824525e93SZhang Chen 28924525e93SZhang Chen /* After failover we needn't change new TCP packet */ 29024525e93SZhang Chen if (s->failover_mode && 29124525e93SZhang Chen !connection_has_tracked(s->connection_track_table, &key)) { 29224525e93SZhang Chen goto out; 29324525e93SZhang Chen } 29424525e93SZhang Chen 295afe46124SZhang Chen conn = connection_get(s->connection_track_table, 296afe46124SZhang Chen &key, 297afe46124SZhang Chen NULL); 298afe46124SZhang Chen 299afe46124SZhang Chen if (sender == nf->netdev) { 300afe46124SZhang Chen /* NET_FILTER_DIRECTION_TX */ 3016214231aSZhang Chen if (!handle_primary_tcp_pkt(s, conn, pkt, &key)) { 30230656b09SZhang Chen qemu_net_queue_send(s->incoming_queue, sender, 0, 30330656b09SZhang Chen (const uint8_t *)pkt->data, pkt->size, NULL); 30430656b09SZhang Chen packet_destroy(pkt, NULL); 30530656b09SZhang Chen pkt = NULL; 30630656b09SZhang Chen /* 30730656b09SZhang Chen * We block the packet here,after rewrite pkt 30830656b09SZhang Chen * and will send it 30930656b09SZhang Chen */ 31030656b09SZhang Chen return 1; 31130656b09SZhang Chen } 312afe46124SZhang Chen } else { 313afe46124SZhang Chen /* NET_FILTER_DIRECTION_RX */ 3146214231aSZhang Chen if (!handle_secondary_tcp_pkt(s, conn, pkt, &key)) { 31530656b09SZhang Chen qemu_net_queue_send(s->incoming_queue, sender, 0, 31630656b09SZhang Chen (const uint8_t *)pkt->data, pkt->size, NULL); 31730656b09SZhang Chen packet_destroy(pkt, NULL); 31830656b09SZhang Chen pkt = NULL; 31930656b09SZhang Chen /* 32030656b09SZhang Chen * We block the packet here,after rewrite pkt 32130656b09SZhang Chen * and will send it 32230656b09SZhang Chen */ 32330656b09SZhang Chen return 1; 32430656b09SZhang Chen } 325afe46124SZhang Chen } 326afe46124SZhang Chen } 327afe46124SZhang Chen 32824525e93SZhang Chen out: 329afe46124SZhang Chen packet_destroy(pkt, NULL); 330afe46124SZhang Chen pkt = NULL; 331e6eee8abSZhang Chen return 0; 332e6eee8abSZhang Chen } 333e6eee8abSZhang Chen 33424525e93SZhang Chen static void reset_seq_offset(gpointer key, gpointer value, gpointer user_data) 33524525e93SZhang Chen { 33624525e93SZhang Chen Connection *conn = (Connection *)value; 33724525e93SZhang Chen 33824525e93SZhang Chen conn->offset = 0; 33924525e93SZhang Chen } 34024525e93SZhang Chen 34124525e93SZhang Chen static gboolean offset_is_nonzero(gpointer key, 34224525e93SZhang Chen gpointer value, 34324525e93SZhang Chen gpointer user_data) 34424525e93SZhang Chen { 34524525e93SZhang Chen Connection *conn = (Connection *)value; 34624525e93SZhang Chen 34724525e93SZhang Chen return conn->offset ? true : false; 34824525e93SZhang Chen } 34924525e93SZhang Chen 35024525e93SZhang Chen static void colo_rewriter_handle_event(NetFilterState *nf, int event, 35124525e93SZhang Chen Error **errp) 35224525e93SZhang Chen { 35324525e93SZhang Chen RewriterState *rs = FILTER_COLO_REWRITER(nf); 35424525e93SZhang Chen 35524525e93SZhang Chen switch (event) { 35624525e93SZhang Chen case COLO_EVENT_CHECKPOINT: 35724525e93SZhang Chen g_hash_table_foreach(rs->connection_track_table, 35824525e93SZhang Chen reset_seq_offset, NULL); 35924525e93SZhang Chen break; 36024525e93SZhang Chen case COLO_EVENT_FAILOVER: 36124525e93SZhang Chen if (!g_hash_table_find(rs->connection_track_table, 36224525e93SZhang Chen offset_is_nonzero, NULL)) { 36324525e93SZhang Chen filter_rewriter_failover_mode(rs); 36424525e93SZhang Chen } 36524525e93SZhang Chen break; 36624525e93SZhang Chen default: 36724525e93SZhang Chen break; 36824525e93SZhang Chen } 36924525e93SZhang Chen } 37024525e93SZhang Chen 371e6eee8abSZhang Chen static void colo_rewriter_cleanup(NetFilterState *nf) 372e6eee8abSZhang Chen { 373e6eee8abSZhang Chen RewriterState *s = FILTER_COLO_REWRITER(nf); 374e6eee8abSZhang Chen 375e6eee8abSZhang Chen /* flush packets */ 376e6eee8abSZhang Chen if (s->incoming_queue) { 377e6eee8abSZhang Chen filter_rewriter_flush(nf); 378e6eee8abSZhang Chen g_free(s->incoming_queue); 379e6eee8abSZhang Chen } 380e6eee8abSZhang Chen } 381e6eee8abSZhang Chen 382e6eee8abSZhang Chen static void colo_rewriter_setup(NetFilterState *nf, Error **errp) 383e6eee8abSZhang Chen { 384e6eee8abSZhang Chen RewriterState *s = FILTER_COLO_REWRITER(nf); 385e6eee8abSZhang Chen 386e6eee8abSZhang Chen s->connection_track_table = g_hash_table_new_full(connection_key_hash, 387e6eee8abSZhang Chen connection_key_equal, 388e6eee8abSZhang Chen g_free, 389e6eee8abSZhang Chen connection_destroy); 390e6eee8abSZhang Chen s->incoming_queue = qemu_new_net_queue(qemu_netfilter_pass_to_next, nf); 391e6eee8abSZhang Chen } 392e6eee8abSZhang Chen 3934b39bdceSZhang Chen static bool filter_rewriter_get_vnet_hdr(Object *obj, Error **errp) 3944b39bdceSZhang Chen { 3954b39bdceSZhang Chen RewriterState *s = FILTER_COLO_REWRITER(obj); 3964b39bdceSZhang Chen 3974b39bdceSZhang Chen return s->vnet_hdr; 3984b39bdceSZhang Chen } 3994b39bdceSZhang Chen 4004b39bdceSZhang Chen static void filter_rewriter_set_vnet_hdr(Object *obj, 4014b39bdceSZhang Chen bool value, 4024b39bdceSZhang Chen Error **errp) 4034b39bdceSZhang Chen { 4044b39bdceSZhang Chen RewriterState *s = FILTER_COLO_REWRITER(obj); 4054b39bdceSZhang Chen 4064b39bdceSZhang Chen s->vnet_hdr = value; 4074b39bdceSZhang Chen } 4084b39bdceSZhang Chen 4094b39bdceSZhang Chen static void filter_rewriter_init(Object *obj) 4104b39bdceSZhang Chen { 4114b39bdceSZhang Chen RewriterState *s = FILTER_COLO_REWRITER(obj); 4124b39bdceSZhang Chen 4134b39bdceSZhang Chen s->vnet_hdr = false; 41424525e93SZhang Chen s->failover_mode = FAILOVER_MODE_OFF; 4154b39bdceSZhang Chen object_property_add_bool(obj, "vnet_hdr_support", 4164b39bdceSZhang Chen filter_rewriter_get_vnet_hdr, 417d2623129SMarkus Armbruster filter_rewriter_set_vnet_hdr); 4184b39bdceSZhang Chen } 4194b39bdceSZhang Chen 420e6eee8abSZhang Chen static void colo_rewriter_class_init(ObjectClass *oc, void *data) 421e6eee8abSZhang Chen { 422e6eee8abSZhang Chen NetFilterClass *nfc = NETFILTER_CLASS(oc); 423e6eee8abSZhang Chen 424e6eee8abSZhang Chen nfc->setup = colo_rewriter_setup; 425e6eee8abSZhang Chen nfc->cleanup = colo_rewriter_cleanup; 426e6eee8abSZhang Chen nfc->receive_iov = colo_rewriter_receive_iov; 42724525e93SZhang Chen nfc->handle_event = colo_rewriter_handle_event; 428e6eee8abSZhang Chen } 429e6eee8abSZhang Chen 430e6eee8abSZhang Chen static const TypeInfo colo_rewriter_info = { 431e6eee8abSZhang Chen .name = TYPE_FILTER_REWRITER, 432e6eee8abSZhang Chen .parent = TYPE_NETFILTER, 433e6eee8abSZhang Chen .class_init = colo_rewriter_class_init, 4344b39bdceSZhang Chen .instance_init = filter_rewriter_init, 435e6eee8abSZhang Chen .instance_size = sizeof(RewriterState), 436e6eee8abSZhang Chen }; 437e6eee8abSZhang Chen 438e6eee8abSZhang Chen static void register_types(void) 439e6eee8abSZhang Chen { 440e6eee8abSZhang Chen type_register_static(&colo_rewriter_info); 441e6eee8abSZhang Chen } 442e6eee8abSZhang Chen 443e6eee8abSZhang Chen type_init(register_types); 444