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 26e6eee8abSZhang Chen #define TYPE_FILTER_REWRITER "filter-rewriter" 278063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(RewriterState, FILTER_REWRITER) 28e6eee8abSZhang Chen 2924525e93SZhang Chen #define FAILOVER_MODE_ON true 3024525e93SZhang Chen #define FAILOVER_MODE_OFF false 31e6eee8abSZhang Chen 32db1015e9SEduardo Habkost struct RewriterState { 33e6eee8abSZhang Chen NetFilterState parent_obj; 34e6eee8abSZhang Chen NetQueue *incoming_queue; 35e6eee8abSZhang Chen /* hashtable to save connection */ 36e6eee8abSZhang Chen GHashTable *connection_track_table; 374b39bdceSZhang Chen bool vnet_hdr; 3824525e93SZhang Chen bool failover_mode; 39db1015e9SEduardo Habkost }; 40e6eee8abSZhang Chen 4124525e93SZhang Chen static void filter_rewriter_failover_mode(RewriterState *s) 4224525e93SZhang Chen { 4324525e93SZhang Chen s->failover_mode = FAILOVER_MODE_ON; 4424525e93SZhang Chen } 4524525e93SZhang Chen 46e6eee8abSZhang Chen static void filter_rewriter_flush(NetFilterState *nf) 47e6eee8abSZhang Chen { 4850cd7d54SEduardo Habkost RewriterState *s = FILTER_REWRITER(nf); 49e6eee8abSZhang Chen 50e6eee8abSZhang Chen if (!qemu_net_queue_flush(s->incoming_queue)) { 51e6eee8abSZhang Chen /* Unable to empty the queue, purge remaining packets */ 52e6eee8abSZhang Chen qemu_net_queue_purge(s->incoming_queue, nf->netdev); 53e6eee8abSZhang Chen } 54e6eee8abSZhang Chen } 55e6eee8abSZhang Chen 56afe46124SZhang Chen /* 57afe46124SZhang Chen * Return 1 on success, if return 0 means the pkt 58afe46124SZhang Chen * is not TCP packet 59afe46124SZhang Chen */ 60afe46124SZhang Chen static int is_tcp_packet(Packet *pkt) 61afe46124SZhang Chen { 62afe46124SZhang Chen if (!parse_packet_early(pkt) && 63afe46124SZhang Chen pkt->ip->ip_p == IPPROTO_TCP) { 64afe46124SZhang Chen return 1; 65afe46124SZhang Chen } else { 66afe46124SZhang Chen return 0; 67afe46124SZhang Chen } 68afe46124SZhang Chen } 69afe46124SZhang Chen 7030656b09SZhang Chen /* handle tcp packet from primary guest */ 716214231aSZhang Chen static int handle_primary_tcp_pkt(RewriterState *rf, 7230656b09SZhang Chen Connection *conn, 736214231aSZhang Chen Packet *pkt, ConnectionKey *key) 7430656b09SZhang Chen { 75e05ae1d9SMarc-André Lureau struct tcp_hdr *tcp_pkt; 7630656b09SZhang Chen 77e05ae1d9SMarc-André Lureau tcp_pkt = (struct tcp_hdr *)pkt->transport_header; 788c8ed038SRoman Bolshakov if (trace_event_get_state_backends(TRACE_COLO_FILTER_REWRITER_PKT_INFO)) { 792061c14cSZhang Chen trace_colo_filter_rewriter_pkt_info(__func__, 802061c14cSZhang Chen inet_ntoa(pkt->ip->ip_src), inet_ntoa(pkt->ip->ip_dst), 8130656b09SZhang Chen ntohl(tcp_pkt->th_seq), ntohl(tcp_pkt->th_ack), 8230656b09SZhang Chen tcp_pkt->th_flags); 838c8ed038SRoman Bolshakov } 848c8ed038SRoman Bolshakov if (trace_event_get_state_backends( 858c8ed038SRoman Bolshakov TRACE_COLO_FILTER_REWRITER_CONN_OFFSET)) { 8630656b09SZhang Chen trace_colo_filter_rewriter_conn_offset(conn->offset); 8730656b09SZhang Chen } 8830656b09SZhang Chen 896214231aSZhang Chen if (((tcp_pkt->th_flags & (TH_ACK | TH_SYN)) == (TH_ACK | TH_SYN)) && 906214231aSZhang Chen conn->tcp_state == TCPS_SYN_SENT) { 916214231aSZhang Chen conn->tcp_state = TCPS_ESTABLISHED; 926214231aSZhang Chen } 936214231aSZhang Chen 9430656b09SZhang Chen if (((tcp_pkt->th_flags & (TH_ACK | TH_SYN)) == TH_SYN)) { 9530656b09SZhang Chen /* 9630656b09SZhang Chen * we use this flag update offset func 9730656b09SZhang Chen * run once in independent tcp connection 9830656b09SZhang Chen */ 996214231aSZhang Chen conn->tcp_state = TCPS_SYN_RECEIVED; 10030656b09SZhang Chen } 10130656b09SZhang Chen 10230656b09SZhang Chen if (((tcp_pkt->th_flags & (TH_ACK | TH_SYN)) == TH_ACK)) { 1036214231aSZhang Chen if (conn->tcp_state == TCPS_SYN_RECEIVED) { 10430656b09SZhang Chen /* 10530656b09SZhang Chen * offset = secondary_seq - primary seq 10630656b09SZhang Chen * ack packet sent by guest from primary node, 10730656b09SZhang Chen * so we use th_ack - 1 get primary_seq 10830656b09SZhang Chen */ 10930656b09SZhang Chen conn->offset -= (ntohl(tcp_pkt->th_ack) - 1); 1106214231aSZhang Chen conn->tcp_state = TCPS_ESTABLISHED; 11130656b09SZhang Chen } 112db0a762eSzhanghailiang if (conn->offset) { 11330656b09SZhang Chen /* handle packets to the secondary from the primary */ 11430656b09SZhang Chen tcp_pkt->th_ack = htonl(ntohl(tcp_pkt->th_ack) + conn->offset); 11530656b09SZhang Chen 1166ce310b5SZhang Chen net_checksum_calculate((uint8_t *)pkt->data + pkt->vnet_hdr_len, 117*f5746335SBin Meng pkt->size - pkt->vnet_hdr_len, CSUM_TCP); 11830656b09SZhang Chen } 1196214231aSZhang Chen 1206214231aSZhang Chen /* 1216214231aSZhang Chen * Passive close step 3 1226214231aSZhang Chen */ 1236214231aSZhang Chen if ((conn->tcp_state == TCPS_LAST_ACK) && 1246214231aSZhang Chen (ntohl(tcp_pkt->th_ack) == (conn->fin_ack_seq + 1))) { 1256214231aSZhang Chen conn->tcp_state = TCPS_CLOSED; 1266214231aSZhang Chen g_hash_table_remove(rf->connection_track_table, key); 1276214231aSZhang Chen } 1286214231aSZhang Chen } 1296214231aSZhang Chen 1306214231aSZhang Chen if ((tcp_pkt->th_flags & TH_FIN) == TH_FIN) { 1316214231aSZhang Chen /* 1326214231aSZhang Chen * Passive close. 1336214231aSZhang Chen * Step 1: 1346214231aSZhang Chen * The *server* side of this connect is VM, *client* tries to close 1356214231aSZhang Chen * the connection. We will into CLOSE_WAIT status. 1366214231aSZhang Chen * 1376214231aSZhang Chen * Step 2: 1386214231aSZhang Chen * In this step we will into LAST_ACK status. 1396214231aSZhang Chen * 1406214231aSZhang Chen * We got 'fin=1, ack=1' packet from server side, we need to 1416214231aSZhang Chen * record the seq of 'fin=1, ack=1' packet. 1426214231aSZhang Chen * 1436214231aSZhang Chen * Step 3: 1446214231aSZhang Chen * We got 'ack=1' packets from client side, it acks 'fin=1, ack=1' 1456214231aSZhang Chen * packet from server side. From this point, we can ensure that there 1466214231aSZhang Chen * will be no packets in the connection, except that, some errors 1476214231aSZhang Chen * happen between the path of 'filter object' and vNIC, if this rare 1486214231aSZhang Chen * case really happen, we can still create a new connection, 1496214231aSZhang Chen * So it is safe to remove the connection from connection_track_table. 1506214231aSZhang Chen * 1516214231aSZhang Chen */ 1526214231aSZhang Chen if (conn->tcp_state == TCPS_ESTABLISHED) { 1536214231aSZhang Chen conn->tcp_state = TCPS_CLOSE_WAIT; 1546214231aSZhang Chen } 1556214231aSZhang Chen 1566214231aSZhang Chen /* 1576214231aSZhang Chen * Active close step 2. 1586214231aSZhang Chen */ 1596214231aSZhang Chen if (conn->tcp_state == TCPS_FIN_WAIT_1) { 1606214231aSZhang Chen /* 1616214231aSZhang Chen * For simplify implementation, we needn't wait 2MSL time 1626214231aSZhang Chen * in filter rewriter. Because guest kernel will track the 1636214231aSZhang Chen * TCP status and wait 2MSL time, if client resend the FIN 1646214231aSZhang Chen * packet, guest will apply the last ACK too. 165013a6202SZhang Chen * So, we skip the TCPS_TIME_WAIT state here and go straight 166013a6202SZhang Chen * to TCPS_CLOSED state. 1676214231aSZhang Chen */ 1686214231aSZhang Chen conn->tcp_state = TCPS_CLOSED; 1696214231aSZhang Chen g_hash_table_remove(rf->connection_track_table, key); 1706214231aSZhang Chen } 171db0a762eSzhanghailiang } 17230656b09SZhang Chen 17330656b09SZhang Chen return 0; 17430656b09SZhang Chen } 17530656b09SZhang Chen 17630656b09SZhang Chen /* handle tcp packet from secondary guest */ 1776214231aSZhang Chen static int handle_secondary_tcp_pkt(RewriterState *rf, 17830656b09SZhang Chen Connection *conn, 1796214231aSZhang Chen Packet *pkt, ConnectionKey *key) 18030656b09SZhang Chen { 181e05ae1d9SMarc-André Lureau struct tcp_hdr *tcp_pkt; 18230656b09SZhang Chen 183e05ae1d9SMarc-André Lureau tcp_pkt = (struct tcp_hdr *)pkt->transport_header; 18430656b09SZhang Chen 1858c8ed038SRoman Bolshakov if (trace_event_get_state_backends(TRACE_COLO_FILTER_REWRITER_PKT_INFO)) { 1862061c14cSZhang Chen trace_colo_filter_rewriter_pkt_info(__func__, 1872061c14cSZhang Chen inet_ntoa(pkt->ip->ip_src), inet_ntoa(pkt->ip->ip_dst), 18830656b09SZhang Chen ntohl(tcp_pkt->th_seq), ntohl(tcp_pkt->th_ack), 18930656b09SZhang Chen tcp_pkt->th_flags); 1908c8ed038SRoman Bolshakov } 1918c8ed038SRoman Bolshakov if (trace_event_get_state_backends( 1928c8ed038SRoman Bolshakov TRACE_COLO_FILTER_REWRITER_CONN_OFFSET)) { 19330656b09SZhang Chen trace_colo_filter_rewriter_conn_offset(conn->offset); 19430656b09SZhang Chen } 19530656b09SZhang Chen 1966214231aSZhang Chen if (conn->tcp_state == TCPS_SYN_RECEIVED && 1976214231aSZhang Chen ((tcp_pkt->th_flags & (TH_ACK | TH_SYN)) == (TH_ACK | TH_SYN))) { 19830656b09SZhang Chen /* 19930656b09SZhang Chen * save offset = secondary_seq and then 20030656b09SZhang Chen * in handle_primary_tcp_pkt make offset 20130656b09SZhang Chen * = secondary_seq - primary_seq 20230656b09SZhang Chen */ 20330656b09SZhang Chen conn->offset = ntohl(tcp_pkt->th_seq); 20430656b09SZhang Chen } 20530656b09SZhang Chen 2066214231aSZhang Chen /* VM active connect */ 2076214231aSZhang Chen if (conn->tcp_state == TCPS_CLOSED && 2086214231aSZhang Chen ((tcp_pkt->th_flags & (TH_ACK | TH_SYN)) == TH_SYN)) { 2096214231aSZhang Chen conn->tcp_state = TCPS_SYN_SENT; 2106214231aSZhang Chen } 2116214231aSZhang Chen 21230656b09SZhang Chen if ((tcp_pkt->th_flags & (TH_ACK | TH_SYN)) == TH_ACK) { 213db0a762eSzhanghailiang /* Only need to adjust seq while offset is Non-zero */ 214db0a762eSzhanghailiang if (conn->offset) { 21530656b09SZhang Chen /* handle packets to the primary from the secondary*/ 21630656b09SZhang Chen tcp_pkt->th_seq = htonl(ntohl(tcp_pkt->th_seq) - conn->offset); 21730656b09SZhang Chen 2186ce310b5SZhang Chen net_checksum_calculate((uint8_t *)pkt->data + pkt->vnet_hdr_len, 219*f5746335SBin Meng pkt->size - pkt->vnet_hdr_len, CSUM_TCP); 22030656b09SZhang Chen } 221db0a762eSzhanghailiang } 22230656b09SZhang Chen 2236214231aSZhang Chen /* 2246214231aSZhang Chen * Passive close step 2: 2256214231aSZhang Chen */ 2266214231aSZhang Chen if (conn->tcp_state == TCPS_CLOSE_WAIT && 2276214231aSZhang Chen (tcp_pkt->th_flags & (TH_ACK | TH_FIN)) == (TH_ACK | TH_FIN)) { 2286214231aSZhang Chen conn->fin_ack_seq = ntohl(tcp_pkt->th_seq); 2296214231aSZhang Chen conn->tcp_state = TCPS_LAST_ACK; 2306214231aSZhang Chen } 2316214231aSZhang Chen 2326214231aSZhang Chen /* 2336214231aSZhang Chen * Active close 2346214231aSZhang Chen * 2356214231aSZhang Chen * Step 1: 2366214231aSZhang Chen * The *server* side of this connect is VM, *server* tries to close 2376214231aSZhang Chen * the connection. 2386214231aSZhang Chen * 2396214231aSZhang Chen * Step 2: 2406214231aSZhang Chen * We will into CLOSE_WAIT status. 2416214231aSZhang Chen * We simplify the TCPS_FIN_WAIT_2, TCPS_TIME_WAIT and 2426214231aSZhang Chen * CLOSING status. 2436214231aSZhang Chen */ 2446214231aSZhang Chen if (conn->tcp_state == TCPS_ESTABLISHED && 2456214231aSZhang Chen (tcp_pkt->th_flags & (TH_ACK | TH_FIN)) == TH_FIN) { 2466214231aSZhang Chen conn->tcp_state = TCPS_FIN_WAIT_1; 2476214231aSZhang Chen } 2486214231aSZhang Chen 24930656b09SZhang Chen return 0; 25030656b09SZhang Chen } 25130656b09SZhang Chen 252e6eee8abSZhang Chen static ssize_t colo_rewriter_receive_iov(NetFilterState *nf, 253e6eee8abSZhang Chen NetClientState *sender, 254e6eee8abSZhang Chen unsigned flags, 255e6eee8abSZhang Chen const struct iovec *iov, 256e6eee8abSZhang Chen int iovcnt, 257e6eee8abSZhang Chen NetPacketSent *sent_cb) 258e6eee8abSZhang Chen { 25950cd7d54SEduardo Habkost RewriterState *s = FILTER_REWRITER(nf); 260afe46124SZhang Chen Connection *conn; 261afe46124SZhang Chen ConnectionKey key; 262afe46124SZhang Chen Packet *pkt; 263afe46124SZhang Chen ssize_t size = iov_size(iov, iovcnt); 2644b39bdceSZhang Chen ssize_t vnet_hdr_len = 0; 265afe46124SZhang Chen char *buf = g_malloc0(size); 266afe46124SZhang Chen 267afe46124SZhang Chen iov_to_buf(iov, iovcnt, 0, buf, size); 2684b39bdceSZhang Chen 2694b39bdceSZhang Chen if (s->vnet_hdr) { 2704b39bdceSZhang Chen vnet_hdr_len = nf->netdev->vnet_hdr_len; 2714b39bdceSZhang Chen } 2724b39bdceSZhang Chen 2734b39bdceSZhang Chen pkt = packet_new(buf, size, vnet_hdr_len); 2742061c14cSZhang Chen g_free(buf); 275afe46124SZhang Chen 276e6eee8abSZhang Chen /* 277e6eee8abSZhang Chen * if we get tcp packet 278e6eee8abSZhang Chen * we will rewrite it to make secondary guest's 279e6eee8abSZhang Chen * connection established successfully 280e6eee8abSZhang Chen */ 281afe46124SZhang Chen if (pkt && is_tcp_packet(pkt)) { 282afe46124SZhang Chen 283afe46124SZhang Chen fill_connection_key(pkt, &key); 284afe46124SZhang Chen 285afe46124SZhang Chen if (sender == nf->netdev) { 286afe46124SZhang Chen /* 287afe46124SZhang Chen * We need make tcp TX and RX packet 288afe46124SZhang Chen * into one connection. 289afe46124SZhang Chen */ 290afe46124SZhang Chen reverse_connection_key(&key); 291afe46124SZhang Chen } 29224525e93SZhang Chen 29324525e93SZhang Chen /* After failover we needn't change new TCP packet */ 29424525e93SZhang Chen if (s->failover_mode && 29524525e93SZhang Chen !connection_has_tracked(s->connection_track_table, &key)) { 29624525e93SZhang Chen goto out; 29724525e93SZhang Chen } 29824525e93SZhang Chen 299afe46124SZhang Chen conn = connection_get(s->connection_track_table, 300afe46124SZhang Chen &key, 301afe46124SZhang Chen NULL); 302afe46124SZhang Chen 303afe46124SZhang Chen if (sender == nf->netdev) { 304afe46124SZhang Chen /* NET_FILTER_DIRECTION_TX */ 3056214231aSZhang Chen if (!handle_primary_tcp_pkt(s, conn, pkt, &key)) { 30630656b09SZhang Chen qemu_net_queue_send(s->incoming_queue, sender, 0, 30730656b09SZhang Chen (const uint8_t *)pkt->data, pkt->size, NULL); 30830656b09SZhang Chen packet_destroy(pkt, NULL); 30930656b09SZhang Chen pkt = NULL; 31030656b09SZhang Chen /* 31130656b09SZhang Chen * We block the packet here,after rewrite pkt 31230656b09SZhang Chen * and will send it 31330656b09SZhang Chen */ 31430656b09SZhang Chen return 1; 31530656b09SZhang Chen } 316afe46124SZhang Chen } else { 317afe46124SZhang Chen /* NET_FILTER_DIRECTION_RX */ 3186214231aSZhang Chen if (!handle_secondary_tcp_pkt(s, conn, pkt, &key)) { 31930656b09SZhang Chen qemu_net_queue_send(s->incoming_queue, sender, 0, 32030656b09SZhang Chen (const uint8_t *)pkt->data, pkt->size, NULL); 32130656b09SZhang Chen packet_destroy(pkt, NULL); 32230656b09SZhang Chen pkt = NULL; 32330656b09SZhang Chen /* 32430656b09SZhang Chen * We block the packet here,after rewrite pkt 32530656b09SZhang Chen * and will send it 32630656b09SZhang Chen */ 32730656b09SZhang Chen return 1; 32830656b09SZhang Chen } 329afe46124SZhang Chen } 330afe46124SZhang Chen } 331afe46124SZhang Chen 33224525e93SZhang Chen out: 333afe46124SZhang Chen packet_destroy(pkt, NULL); 334afe46124SZhang Chen pkt = NULL; 335e6eee8abSZhang Chen return 0; 336e6eee8abSZhang Chen } 337e6eee8abSZhang Chen 33824525e93SZhang Chen static void reset_seq_offset(gpointer key, gpointer value, gpointer user_data) 33924525e93SZhang Chen { 34024525e93SZhang Chen Connection *conn = (Connection *)value; 34124525e93SZhang Chen 34224525e93SZhang Chen conn->offset = 0; 34324525e93SZhang Chen } 34424525e93SZhang Chen 34524525e93SZhang Chen static gboolean offset_is_nonzero(gpointer key, 34624525e93SZhang Chen gpointer value, 34724525e93SZhang Chen gpointer user_data) 34824525e93SZhang Chen { 34924525e93SZhang Chen Connection *conn = (Connection *)value; 35024525e93SZhang Chen 35124525e93SZhang Chen return conn->offset ? true : false; 35224525e93SZhang Chen } 35324525e93SZhang Chen 35424525e93SZhang Chen static void colo_rewriter_handle_event(NetFilterState *nf, int event, 35524525e93SZhang Chen Error **errp) 35624525e93SZhang Chen { 35750cd7d54SEduardo Habkost RewriterState *rs = FILTER_REWRITER(nf); 35824525e93SZhang Chen 35924525e93SZhang Chen switch (event) { 36024525e93SZhang Chen case COLO_EVENT_CHECKPOINT: 36124525e93SZhang Chen g_hash_table_foreach(rs->connection_track_table, 36224525e93SZhang Chen reset_seq_offset, NULL); 36324525e93SZhang Chen break; 36424525e93SZhang Chen case COLO_EVENT_FAILOVER: 36524525e93SZhang Chen if (!g_hash_table_find(rs->connection_track_table, 36624525e93SZhang Chen offset_is_nonzero, NULL)) { 36724525e93SZhang Chen filter_rewriter_failover_mode(rs); 36824525e93SZhang Chen } 36924525e93SZhang Chen break; 37024525e93SZhang Chen default: 37124525e93SZhang Chen break; 37224525e93SZhang Chen } 37324525e93SZhang Chen } 37424525e93SZhang Chen 375e6eee8abSZhang Chen static void colo_rewriter_cleanup(NetFilterState *nf) 376e6eee8abSZhang Chen { 37750cd7d54SEduardo Habkost RewriterState *s = FILTER_REWRITER(nf); 378e6eee8abSZhang Chen 379e6eee8abSZhang Chen /* flush packets */ 380e6eee8abSZhang Chen if (s->incoming_queue) { 381e6eee8abSZhang Chen filter_rewriter_flush(nf); 382e6eee8abSZhang Chen g_free(s->incoming_queue); 383e6eee8abSZhang Chen } 384b492a4b8SPan Nengyuan 385b492a4b8SPan Nengyuan g_hash_table_destroy(s->connection_track_table); 386e6eee8abSZhang Chen } 387e6eee8abSZhang Chen 388e6eee8abSZhang Chen static void colo_rewriter_setup(NetFilterState *nf, Error **errp) 389e6eee8abSZhang Chen { 39050cd7d54SEduardo Habkost RewriterState *s = FILTER_REWRITER(nf); 391e6eee8abSZhang Chen 392e6eee8abSZhang Chen s->connection_track_table = g_hash_table_new_full(connection_key_hash, 393e6eee8abSZhang Chen connection_key_equal, 394e6eee8abSZhang Chen g_free, 395e6eee8abSZhang Chen connection_destroy); 396e6eee8abSZhang Chen s->incoming_queue = qemu_new_net_queue(qemu_netfilter_pass_to_next, nf); 397e6eee8abSZhang Chen } 398e6eee8abSZhang Chen 3994b39bdceSZhang Chen static bool filter_rewriter_get_vnet_hdr(Object *obj, Error **errp) 4004b39bdceSZhang Chen { 40150cd7d54SEduardo Habkost RewriterState *s = FILTER_REWRITER(obj); 4024b39bdceSZhang Chen 4034b39bdceSZhang Chen return s->vnet_hdr; 4044b39bdceSZhang Chen } 4054b39bdceSZhang Chen 4064b39bdceSZhang Chen static void filter_rewriter_set_vnet_hdr(Object *obj, 4074b39bdceSZhang Chen bool value, 4084b39bdceSZhang Chen Error **errp) 4094b39bdceSZhang Chen { 41050cd7d54SEduardo Habkost RewriterState *s = FILTER_REWRITER(obj); 4114b39bdceSZhang Chen 4124b39bdceSZhang Chen s->vnet_hdr = value; 4134b39bdceSZhang Chen } 4144b39bdceSZhang Chen 4154b39bdceSZhang Chen static void filter_rewriter_init(Object *obj) 4164b39bdceSZhang Chen { 41750cd7d54SEduardo Habkost RewriterState *s = FILTER_REWRITER(obj); 4184b39bdceSZhang Chen 4194b39bdceSZhang Chen s->vnet_hdr = false; 42024525e93SZhang Chen s->failover_mode = FAILOVER_MODE_OFF; 4214b39bdceSZhang Chen } 4224b39bdceSZhang Chen 423e6eee8abSZhang Chen static void colo_rewriter_class_init(ObjectClass *oc, void *data) 424e6eee8abSZhang Chen { 425e6eee8abSZhang Chen NetFilterClass *nfc = NETFILTER_CLASS(oc); 426e6eee8abSZhang Chen 427f0e34a06SEduardo Habkost object_class_property_add_bool(oc, "vnet_hdr_support", 428f0e34a06SEduardo Habkost filter_rewriter_get_vnet_hdr, 429f0e34a06SEduardo Habkost filter_rewriter_set_vnet_hdr); 430f0e34a06SEduardo Habkost 431e6eee8abSZhang Chen nfc->setup = colo_rewriter_setup; 432e6eee8abSZhang Chen nfc->cleanup = colo_rewriter_cleanup; 433e6eee8abSZhang Chen nfc->receive_iov = colo_rewriter_receive_iov; 43424525e93SZhang Chen nfc->handle_event = colo_rewriter_handle_event; 435e6eee8abSZhang Chen } 436e6eee8abSZhang Chen 437e6eee8abSZhang Chen static const TypeInfo colo_rewriter_info = { 438e6eee8abSZhang Chen .name = TYPE_FILTER_REWRITER, 439e6eee8abSZhang Chen .parent = TYPE_NETFILTER, 440e6eee8abSZhang Chen .class_init = colo_rewriter_class_init, 4414b39bdceSZhang Chen .instance_init = filter_rewriter_init, 442e6eee8abSZhang Chen .instance_size = sizeof(RewriterState), 443e6eee8abSZhang Chen }; 444e6eee8abSZhang Chen 445e6eee8abSZhang Chen static void register_types(void) 446e6eee8abSZhang Chen { 447e6eee8abSZhang Chen type_register_static(&colo_rewriter_info); 448e6eee8abSZhang Chen } 449e6eee8abSZhang Chen 450e6eee8abSZhang Chen type_init(register_types); 451