/* packet-drbd.c * Routines for DRBD dissection * By Joel Colledge * Copyright 2019, LINBIT Information Technologies GmbH * * Wireshark - Network traffic analyzer * By Gerald Combs * Copyright 1998 Gerald Combs * * SPDX-License-Identifier: GPL-2.0-or-later */ /* * Wireshark dissector for DRBD - Distributed Replicated Block Device. * The DRBD Linux kernel module sources can be found at https://github.com/LINBIT/drbd * More information about Linbit and DRBD can be found at https://www.linbit.com/ */ #include #include #include #include #include "packet-tcp.h" #include #include /* Known as SHARED_SECRET_MAX in the DRBD sources */ #define DRBD_STRING_MAX 64 enum drbd_packet { P_DATA = 0x00, P_DATA_REPLY = 0x01, P_RS_DATA_REPLY = 0x02, P_BARRIER = 0x03, P_BITMAP = 0x04, P_BECOME_SYNC_TARGET = 0x05, P_BECOME_SYNC_SOURCE = 0x06, P_UNPLUG_REMOTE = 0x07, P_DATA_REQUEST = 0x08, P_RS_DATA_REQUEST = 0x09, P_SYNC_PARAM = 0x0a, P_PROTOCOL = 0x0b, P_UUIDS = 0x0c, P_SIZES = 0x0d, P_STATE = 0x0e, P_SYNC_UUID = 0x0f, P_AUTH_CHALLENGE = 0x10, P_AUTH_RESPONSE = 0x11, P_STATE_CHG_REQ = 0x12, P_PING = 0x13, P_PING_ACK = 0x14, P_RECV_ACK = 0x15, P_WRITE_ACK = 0x16, P_RS_WRITE_ACK = 0x17, P_SUPERSEDED = 0x18, P_NEG_ACK = 0x19, P_NEG_DREPLY = 0x1a, P_NEG_RS_DREPLY = 0x1b, P_BARRIER_ACK = 0x1c, P_STATE_CHG_REPLY = 0x1d, P_OV_REQUEST = 0x1e, P_OV_REPLY = 0x1f, P_OV_RESULT = 0x20, P_CSUM_RS_REQUEST = 0x21, P_RS_IS_IN_SYNC = 0x22, P_SYNC_PARAM89 = 0x23, P_COMPRESSED_BITMAP = 0x24, P_DELAY_PROBE = 0x27, P_OUT_OF_SYNC = 0x28, P_RS_CANCEL = 0x29, P_CONN_ST_CHG_REQ = 0x2a, P_CONN_ST_CHG_REPLY = 0x2b, P_RETRY_WRITE = 0x2c, P_PROTOCOL_UPDATE = 0x2d, P_TWOPC_PREPARE = 0x2e, P_TWOPC_ABORT = 0x2f, P_DAGTAG = 0x30, P_TRIM = 0x31, P_RS_THIN_REQ = 0x32, P_RS_DEALLOCATED = 0x33, P_WSAME = 0x34, P_TWOPC_PREP_RSZ = 0x35, P_ZEROES = 0x36, P_PEER_ACK = 0x40, P_PEERS_IN_SYNC = 0x41, P_UUIDS110 = 0x42, P_PEER_DAGTAG = 0x43, P_CURRENT_UUID = 0x44, P_TWOPC_YES = 0x45, P_TWOPC_NO = 0x46, P_TWOPC_COMMIT = 0x47, P_TWOPC_RETRY = 0x48, P_CONFIRM_STABLE = 0x49, P_RS_CANCEL_AHEAD = 0x4a, P_DISCONNECT = 0x4b, P_RS_DAGTAG_REQ = 0x4c, P_RS_CSUM_DAGTAG_REQ = 0x4d, P_RS_THIN_DAGTAG_REQ = 0x4e, P_OV_DAGTAG_REQ = 0x4f, P_OV_DAGTAG_REPLY = 0x50, P_WRITE_ACK_IN_SYNC = 0x51, P_RS_NEG_ACK = 0x52, P_OV_RESULT_ID = 0x53, P_RS_DEALLOCATED_ID = 0x54, P_INITIAL_META = 0xfff1, P_INITIAL_DATA = 0xfff2, P_CONNECTION_FEATURES = 0xfffe }; typedef struct { uint32_t tid; int32_t initiator_node_id; } drbd_twopc_key; typedef struct { uint32_t prepare_frame; enum drbd_packet command; } drbd_twopc_val; static unsigned drbd_twopc_key_hash(const void *k) { const drbd_twopc_key *key = (const drbd_twopc_key *) k; return key->tid; } static int drbd_twopc_key_equal(const void *k1, const void *k2) { const drbd_twopc_key *key1 = (const drbd_twopc_key*) k1; const drbd_twopc_key *key2 = (const drbd_twopc_key*) k2; return key1->tid == key2->tid && key1->initiator_node_id == key2->initiator_node_id; } typedef struct { wmem_map_t *twopc; } drbd_conv; typedef struct value_payload_decoder { int value; void (*state_reader_fn)(tvbuff_t *, packet_info *, drbd_conv *); void (*tree_fn)(tvbuff_t *, proto_tree *, drbd_conv *); } value_payload_decoder; static const value_string packet_names[] = { { P_DATA, "P_DATA" }, { P_DATA_REPLY, "P_DATA_REPLY" }, { P_RS_DATA_REPLY, "P_RS_DATA_REPLY" }, { P_BARRIER, "P_BARRIER" }, { P_BITMAP, "P_BITMAP" }, { P_BECOME_SYNC_TARGET, "P_BECOME_SYNC_TARGET" }, { P_BECOME_SYNC_SOURCE, "P_BECOME_SYNC_SOURCE" }, { P_UNPLUG_REMOTE, "P_UNPLUG_REMOTE" }, { P_DATA_REQUEST, "P_DATA_REQUEST" }, { P_RS_DATA_REQUEST, "P_RS_DATA_REQUEST" }, { P_SYNC_PARAM, "P_SYNC_PARAM" }, { P_PROTOCOL, "P_PROTOCOL" }, { P_UUIDS, "P_UUIDS" }, { P_SIZES, "P_SIZES" }, { P_STATE, "P_STATE" }, { P_SYNC_UUID, "P_SYNC_UUID" }, { P_AUTH_CHALLENGE, "P_AUTH_CHALLENGE" }, { P_AUTH_RESPONSE, "P_AUTH_RESPONSE" }, { P_STATE_CHG_REQ, "P_STATE_CHG_REQ" }, { P_PING, "P_PING" }, { P_PING_ACK, "P_PING_ACK" }, { P_RECV_ACK, "P_RECV_ACK" }, { P_WRITE_ACK, "P_WRITE_ACK" }, { P_RS_WRITE_ACK, "P_RS_WRITE_ACK" }, { P_SUPERSEDED, "P_SUPERSEDED" }, { P_NEG_ACK, "P_NEG_ACK" }, { P_NEG_DREPLY, "P_NEG_DREPLY" }, { P_NEG_RS_DREPLY, "P_NEG_RS_DREPLY" }, { P_BARRIER_ACK, "P_BARRIER_ACK" }, { P_STATE_CHG_REPLY, "P_STATE_CHG_REPLY" }, { P_OV_REQUEST, "P_OV_REQUEST" }, { P_OV_REPLY, "P_OV_REPLY" }, { P_OV_RESULT, "P_OV_RESULT" }, { P_CSUM_RS_REQUEST, "P_CSUM_RS_REQUEST" }, { P_RS_IS_IN_SYNC, "P_RS_IS_IN_SYNC" }, { P_SYNC_PARAM89, "P_SYNC_PARAM89" }, { P_COMPRESSED_BITMAP, "P_COMPRESSED_BITMAP" }, { P_DELAY_PROBE, "P_DELAY_PROBE" }, { P_OUT_OF_SYNC, "P_OUT_OF_SYNC" }, { P_RS_CANCEL, "P_RS_CANCEL" }, { P_CONN_ST_CHG_REQ, "P_CONN_ST_CHG_REQ" }, { P_CONN_ST_CHG_REPLY, "P_CONN_ST_CHG_REPLY" }, { P_RETRY_WRITE, "P_RETRY_WRITE" }, { P_PROTOCOL_UPDATE, "P_PROTOCOL_UPDATE" }, { P_TWOPC_PREPARE, "P_TWOPC_PREPARE" }, { P_TWOPC_ABORT, "P_TWOPC_ABORT" }, { P_DAGTAG, "P_DAGTAG" }, { P_TRIM, "P_TRIM" }, { P_RS_THIN_REQ, "P_RS_THIN_REQ" }, { P_RS_DEALLOCATED, "P_RS_DEALLOCATED" }, { P_WSAME, "P_WSAME" }, { P_TWOPC_PREP_RSZ, "P_TWOPC_PREP_RSZ" }, { P_ZEROES, "P_ZEROES" }, { P_PEER_ACK, "P_PEER_ACK" }, { P_PEERS_IN_SYNC, "P_PEERS_IN_SYNC" }, { P_UUIDS110, "P_UUIDS110" }, { P_PEER_DAGTAG, "P_PEER_DAGTAG" }, { P_CURRENT_UUID, "P_CURRENT_UUID" }, { P_TWOPC_YES, "P_TWOPC_YES" }, { P_TWOPC_NO, "P_TWOPC_NO" }, { P_TWOPC_COMMIT, "P_TWOPC_COMMIT" }, { P_TWOPC_RETRY, "P_TWOPC_RETRY" }, { P_CONFIRM_STABLE, "P_CONFIRM_STABLE" }, { P_RS_CANCEL_AHEAD, "P_RS_CANCEL_AHEAD" }, { P_DISCONNECT, "P_DISCONNECT" }, { P_RS_DAGTAG_REQ, "P_RS_DAGTAG_REQ" }, { P_RS_CSUM_DAGTAG_REQ, "P_RS_CSUM_DAGTAG_REQ" }, { P_RS_THIN_DAGTAG_REQ, "P_RS_THIN_DAGTAG_REQ" }, { P_OV_DAGTAG_REQ, "P_OV_DAGTAG_REQ" }, { P_OV_DAGTAG_REPLY, "P_OV_DAGTAG_REPLY" }, { P_WRITE_ACK_IN_SYNC, "P_WRITE_ACK_IN_SYNC" }, { P_RS_NEG_ACK, "P_RS_NEG_ACK" }, { P_OV_RESULT_ID, "P_OV_RESULT_ID" }, { P_RS_DEALLOCATED_ID, "P_RS_DEALLOCATED_ID" }, { P_INITIAL_META, "P_INITIAL_META" }, { P_INITIAL_DATA, "P_INITIAL_DATA" }, { P_CONNECTION_FEATURES, "P_CONNECTION_FEATURES" }, { 0, NULL } }; #define DRBD_PROT_A 1 #define DRBD_PROT_B 2 #define DRBD_PROT_C 3 static const value_string protocol_names[] = { { DRBD_PROT_A, "A" }, { DRBD_PROT_B, "B" }, { DRBD_PROT_C, "C" }, { 0, NULL } }; #define DRBD_ROLE_UNKNOWN 0 #define DRBD_ROLE_PRIMARY 1 #define DRBD_ROLE_SECONDARY 2 static const value_string role_names[] = { { DRBD_ROLE_UNKNOWN, "UNKNOWN" }, { DRBD_ROLE_PRIMARY, "PRIMARY" }, { DRBD_ROLE_SECONDARY, "SECONDARY" }, { 0, NULL } }; #define DRBD_CONNECTION_STATE_C_STANDALONE 0 #define DRBD_CONNECTION_STATE_C_DISCONNECTING 1 #define DRBD_CONNECTION_STATE_C_UNCONNECTED 2 #define DRBD_CONNECTION_STATE_C_TIMEOUT 3 #define DRBD_CONNECTION_STATE_C_BROKEN_PIPE 4 #define DRBD_CONNECTION_STATE_C_NETWORK_FAILURE 5 #define DRBD_CONNECTION_STATE_C_PROTOCOL_ERROR 6 #define DRBD_CONNECTION_STATE_C_TEAR_DOWN 7 #define DRBD_CONNECTION_STATE_C_CONNECTING 8 #define DRBD_CONNECTION_STATE_C_CONNECTED 9 #define DRBD_CONNECTION_STATE_L_ESTABLISHED 10 #define DRBD_CONNECTION_STATE_L_STARTING_SYNC_S 11 #define DRBD_CONNECTION_STATE_L_STARTING_SYNC_T 12 #define DRBD_CONNECTION_STATE_L_WF_BITMAP_S 13 #define DRBD_CONNECTION_STATE_L_WF_BITMAP_T 14 #define DRBD_CONNECTION_STATE_L_WF_SYNC_UUID 15 #define DRBD_CONNECTION_STATE_L_SYNC_SOURCE 16 #define DRBD_CONNECTION_STATE_L_SYNC_TARGET 17 #define DRBD_CONNECTION_STATE_L_VERIFY_S 18 #define DRBD_CONNECTION_STATE_L_VERIFY_T 19 #define DRBD_CONNECTION_STATE_L_PAUSED_SYNC_S 20 #define DRBD_CONNECTION_STATE_L_PAUSED_SYNC_T 21 #define DRBD_CONNECTION_STATE_L_AHEAD 22 #define DRBD_CONNECTION_STATE_L_BEHIND 23 static const value_string connection_state_names[] = { { DRBD_CONNECTION_STATE_C_STANDALONE, "C_STANDALONE" }, { DRBD_CONNECTION_STATE_C_DISCONNECTING, "C_DISCONNECTING" }, { DRBD_CONNECTION_STATE_C_UNCONNECTED, "C_UNCONNECTED" }, { DRBD_CONNECTION_STATE_C_TIMEOUT, "C_TIMEOUT" }, { DRBD_CONNECTION_STATE_C_BROKEN_PIPE, "C_BROKEN_PIPE" }, { DRBD_CONNECTION_STATE_C_NETWORK_FAILURE, "C_NETWORK_FAILURE" }, { DRBD_CONNECTION_STATE_C_PROTOCOL_ERROR, "C_PROTOCOL_ERROR" }, { DRBD_CONNECTION_STATE_C_TEAR_DOWN, "C_TEAR_DOWN" }, { DRBD_CONNECTION_STATE_C_CONNECTING, "C_CONNECTING" }, { DRBD_CONNECTION_STATE_C_CONNECTED, "C_CONNECTED" }, { DRBD_CONNECTION_STATE_L_ESTABLISHED, "L_ESTABLISHED" }, { DRBD_CONNECTION_STATE_L_STARTING_SYNC_S, "L_STARTING_SYNC_S" }, { DRBD_CONNECTION_STATE_L_STARTING_SYNC_T, "L_STARTING_SYNC_T" }, { DRBD_CONNECTION_STATE_L_WF_BITMAP_S, "L_WF_BITMAP_S" }, { DRBD_CONNECTION_STATE_L_WF_BITMAP_T, "L_WF_BITMAP_T" }, { DRBD_CONNECTION_STATE_L_WF_SYNC_UUID, "L_WF_SYNC_UUID" }, { DRBD_CONNECTION_STATE_L_SYNC_SOURCE, "L_SYNC_SOURCE" }, { DRBD_CONNECTION_STATE_L_SYNC_TARGET, "L_SYNC_TARGET" }, { DRBD_CONNECTION_STATE_L_VERIFY_S, "L_VERIFY_S" }, { DRBD_CONNECTION_STATE_L_VERIFY_T, "L_VERIFY_T" }, { DRBD_CONNECTION_STATE_L_PAUSED_SYNC_S, "L_PAUSED_SYNC_S" }, { DRBD_CONNECTION_STATE_L_PAUSED_SYNC_T, "L_PAUSED_SYNC_T" }, { DRBD_CONNECTION_STATE_L_AHEAD, "L_AHEAD" }, { DRBD_CONNECTION_STATE_L_BEHIND, "L_BEHIND" }, { 0, NULL } }; #define DRBD_DISK_STATE_DISKLESS 0 #define DRBD_DISK_STATE_ATTACHING 1 #define DRBD_DISK_STATE_DETACHING 2 #define DRBD_DISK_STATE_FAILED 3 #define DRBD_DISK_STATE_NEGOTIATING 4 #define DRBD_DISK_STATE_INCONSISTENT 5 #define DRBD_DISK_STATE_OUTDATED 6 #define DRBD_DISK_STATE_UNKNOWN 7 #define DRBD_DISK_STATE_CONSISTENT 8 #define DRBD_DISK_STATE_UP_TO_DATE 9 static const value_string disk_state_names[] = { { DRBD_DISK_STATE_DISKLESS, "D_DISKLESS" }, { DRBD_DISK_STATE_ATTACHING, "D_ATTACHING" }, { DRBD_DISK_STATE_DETACHING, "D_DETACHING" }, { DRBD_DISK_STATE_FAILED, "D_FAILED" }, { DRBD_DISK_STATE_NEGOTIATING, "D_NEGOTIATING" }, { DRBD_DISK_STATE_INCONSISTENT, "D_INCONSISTENT" }, { DRBD_DISK_STATE_OUTDATED, "D_OUTDATED" }, { DRBD_DISK_STATE_UNKNOWN, "D_UNKNOWN" }, { DRBD_DISK_STATE_CONSISTENT, "D_CONSISTENT" }, { DRBD_DISK_STATE_UP_TO_DATE, "D_UP_TO_DATE" }, { 0, NULL } }; #define STATE_ROLE (0x3 << 0) /* 3/4 primary/secondary/unknown */ #define STATE_PEER (0x3 << 2) /* 3/4 primary/secondary/unknown */ #define STATE_CONN (0x1f << 4) /* 17/32 cstates */ #define STATE_DISK (0xf << 9) /* 8/16 from D_DISKLESS to D_UP_TO_DATE */ #define STATE_PDSK (0xf << 13) /* 8/16 from D_DISKLESS to D_UP_TO_DATE */ #define STATE_SUSP (0x1 << 17) /* 2/2 IO suspended no/yes (by user) */ #define STATE_AFTR_ISP (0x1 << 18) /* isp .. imposed sync pause */ #define STATE_PEER_ISP (0x1 << 19) #define STATE_USER_ISP (0x1 << 20) #define STATE_SUSP_NOD (0x1 << 21) /* IO suspended because no data */ #define STATE_SUSP_FEN (0x1 << 22) /* IO suspended because fence peer handler runs*/ #define STATE_QUORUM (0x1 << 23) #define TWOPC_HAS_FLAGS 0x80000000 /* For packet dissectors */ #define TWOPC_HAS_REACHABLE 0x40000000 /* The reachable_nodes field is valid */ #define UUID_FLAG_DISCARD_MY_DATA 1 #define UUID_FLAG_CRASHED_PRIMARY 2 #define UUID_FLAG_INCONSISTENT 4 #define UUID_FLAG_SKIP_INITIAL_SYNC 8 #define UUID_FLAG_NEW_DATAGEN 16 #define UUID_FLAG_STABLE 32 #define UUID_FLAG_GOT_STABLE 64 #define UUID_FLAG_RESYNC 128 #define UUID_FLAG_RECONNECT 256 #define UUID_FLAG_DISKLESS_PRIMARY 512 #define UUID_FLAG_PRIMARY_LOST_QUORUM 1024 #define DP_HARDBARRIER 1 #define DP_RW_SYNC 2 #define DP_MAY_SET_IN_SYNC 4 #define DP_UNPLUG 8 #define DP_FUA 16 #define DP_FLUSH 32 #define DP_DISCARD 64 #define DP_SEND_RECEIVE_ACK 128 #define DP_SEND_WRITE_ACK 256 #define DP_WSAME 512 #define DP_ZEROES 1024 #define OV_RESULT_SKIP 4710 #define OV_RESULT_IN_SYNC 4711 #define OV_RESULT_OUT_OF_SYNC 4712 static const val64_string ov_result_codes[] = { { OV_RESULT_SKIP, "SKIP" }, { OV_RESULT_IN_SYNC, "IN_SYNC" }, { OV_RESULT_OUT_OF_SYNC, "OUT_OF_SYNC" }, { 0, NULL } }; #define DRBD_STREAM_DATA 0 #define DRBD_STREAM_CONTROL 1 static const value_string stream_names[] = { { DRBD_STREAM_DATA, "Data" }, { DRBD_STREAM_CONTROL, "Control" }, { 0, NULL } }; static void dissect_drbd_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree); static void read_state_twopc_prepare(tvbuff_t *tvb, packet_info *pinfo, drbd_conv *conv_data); static void read_state_twopc_prep_rsz(tvbuff_t *tvb, packet_info *pinfo, drbd_conv *conv_data); static void decode_payload_connection_features(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data); static void decode_payload_auth_challenge(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data); static void decode_payload_auth_response(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data); static void decode_payload_data(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data); static void decode_payload_barrier(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data); static void decode_payload_dagtag_data_request(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data); static void decode_payload_data_request(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data); static void decode_payload_sync_param(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data); static void decode_payload_protocol(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data); static void decode_payload_uuids(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data); static void decode_payload_sizes(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data); static void decode_payload_state(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data); static void decode_payload_req_state(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data); static void decode_payload_sync_uuid(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data); static void decode_payload_skip(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data); static void decode_payload_out_of_sync(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data); static void decode_payload_twopc_prepare(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data); static void decode_payload_twopc_prep_rsz(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data); static void decode_payload_twopc_commit(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data); static void decode_payload_dagtag(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data); static void decode_payload_uuids110(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data); static void decode_payload_peer_dagtag(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data); static void decode_payload_current_uuid(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data); static void decode_payload_data_size(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data); static void decode_payload_data_wsame(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data); static void decode_payload_rs_deallocated(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data); static void decode_payload_block_ack(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data); static void decode_payload_ov_result(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data); static void decode_payload_barrier_ack(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data); static void decode_payload_confirm_stable(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data); static void decode_payload_rq_s_reply(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data); static void decode_payload_peer_ack(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data); static void decode_payload_peers_in_sync(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data); static void decode_payload_twopc_reply(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data); static const value_payload_decoder payload_decoders[] = { { P_CONNECTION_FEATURES, NULL, decode_payload_connection_features }, { P_AUTH_CHALLENGE, NULL, decode_payload_auth_challenge }, { P_AUTH_RESPONSE, NULL, decode_payload_auth_response }, { P_DATA, NULL, decode_payload_data }, { P_DATA_REPLY, NULL, decode_payload_data }, { P_RS_DATA_REPLY, NULL, decode_payload_data }, { P_BARRIER, NULL, decode_payload_barrier }, { P_BITMAP, NULL, NULL }, /* TODO: decode additional data */ { P_COMPRESSED_BITMAP, NULL, NULL }, /* TODO: decode additional data */ { P_UNPLUG_REMOTE, NULL, NULL }, { P_DATA_REQUEST, NULL, decode_payload_data_request }, { P_RS_DATA_REQUEST, NULL, decode_payload_data_request }, { P_SYNC_PARAM, NULL, decode_payload_sync_param }, { P_SYNC_PARAM89, NULL, decode_payload_sync_param }, { P_PROTOCOL, NULL, decode_payload_protocol }, { P_UUIDS, NULL, decode_payload_uuids }, { P_SIZES, NULL, decode_payload_sizes }, { P_STATE, NULL, decode_payload_state }, { P_STATE_CHG_REQ, NULL, decode_payload_req_state }, { P_SYNC_UUID, NULL, decode_payload_sync_uuid }, { P_OV_REQUEST, NULL, decode_payload_data_request }, { P_OV_REPLY, NULL, decode_payload_data_request }, /* TODO: decode additional data */ { P_CSUM_RS_REQUEST, NULL, decode_payload_data_request }, /* TODO: decode additional data */ { P_RS_THIN_REQ, NULL, decode_payload_data_request }, { P_DELAY_PROBE, NULL, decode_payload_skip }, { P_OUT_OF_SYNC, NULL, decode_payload_out_of_sync }, { P_CONN_ST_CHG_REQ, NULL, decode_payload_req_state }, { P_PROTOCOL_UPDATE, NULL, decode_payload_protocol }, /* TODO: decode additional data */ { P_TWOPC_PREPARE, read_state_twopc_prepare, decode_payload_twopc_prepare }, { P_TWOPC_PREP_RSZ, read_state_twopc_prep_rsz, decode_payload_twopc_prep_rsz }, { P_TWOPC_ABORT, NULL, decode_payload_twopc_commit }, { P_DAGTAG, NULL, decode_payload_dagtag }, { P_UUIDS110, NULL, decode_payload_uuids110 }, { P_PEER_DAGTAG, NULL, decode_payload_peer_dagtag }, { P_CURRENT_UUID, NULL, decode_payload_current_uuid }, { P_TWOPC_COMMIT, NULL, decode_payload_twopc_commit }, { P_TRIM, NULL, decode_payload_data_size }, { P_ZEROES, NULL, decode_payload_data_size }, { P_RS_DEALLOCATED, NULL, decode_payload_rs_deallocated }, { P_RS_DEALLOCATED_ID, NULL, decode_payload_block_ack }, { P_WSAME, NULL, decode_payload_data_wsame }, { P_DISCONNECT, NULL, NULL }, { P_RS_DAGTAG_REQ, NULL, decode_payload_dagtag_data_request }, { P_RS_CSUM_DAGTAG_REQ, NULL, decode_payload_dagtag_data_request }, { P_RS_THIN_DAGTAG_REQ, NULL, decode_payload_dagtag_data_request }, { P_OV_DAGTAG_REQ, NULL, decode_payload_dagtag_data_request }, { P_OV_DAGTAG_REPLY, NULL, decode_payload_dagtag_data_request }, { P_PING, NULL, NULL }, { P_PING_ACK, NULL, NULL }, { P_RECV_ACK, NULL, decode_payload_block_ack }, { P_WRITE_ACK, NULL, decode_payload_block_ack }, { P_WRITE_ACK_IN_SYNC, NULL, decode_payload_block_ack }, { P_SUPERSEDED, NULL, decode_payload_block_ack }, { P_NEG_ACK, NULL, decode_payload_block_ack }, { P_NEG_DREPLY, NULL, decode_payload_block_ack }, { P_NEG_RS_DREPLY, NULL, decode_payload_block_ack }, { P_RS_WRITE_ACK, NULL, decode_payload_block_ack }, { P_RS_NEG_ACK, NULL, decode_payload_block_ack }, { P_OV_RESULT, NULL, decode_payload_block_ack }, { P_OV_RESULT_ID, NULL, decode_payload_ov_result }, { P_BARRIER_ACK, NULL, decode_payload_barrier_ack }, { P_CONFIRM_STABLE, NULL, decode_payload_confirm_stable }, { P_STATE_CHG_REPLY, NULL, decode_payload_rq_s_reply }, { P_RS_IS_IN_SYNC, NULL, decode_payload_block_ack }, { P_DELAY_PROBE, NULL, decode_payload_skip }, { P_RS_CANCEL, NULL, decode_payload_block_ack }, { P_RS_CANCEL_AHEAD, NULL, decode_payload_block_ack }, { P_CONN_ST_CHG_REPLY, NULL, decode_payload_rq_s_reply }, { P_RETRY_WRITE, NULL, decode_payload_block_ack }, { P_PEER_ACK, NULL, decode_payload_peer_ack }, { P_PEERS_IN_SYNC, NULL, decode_payload_peers_in_sync }, { P_TWOPC_YES, NULL, decode_payload_twopc_reply }, { P_TWOPC_NO, NULL, decode_payload_twopc_reply }, { P_TWOPC_RETRY, NULL, decode_payload_twopc_reply }, }; void proto_register_drbd(void); void proto_reg_handoff_drbd(void); static dissector_handle_t drbd_handle; static dissector_handle_t drbd_lb_tcp_handle; static int proto_drbd; static int proto_drbd_lb_tcp; static int hf_drbd_command; static int hf_drbd_length; static int hf_drbd_volume; static int hf_drbd_auth_challenge_nonce; static int hf_drbd_auth_response_hash; static int hf_drbd_sector; static int hf_drbd_block_id; static int hf_drbd_seq_num; static int hf_drbd_ov_result; static int hf_drbd_dp_flags; static int hf_drbd_data; static int hf_drbd_size; static int hf_drbd_protocol_min; static int hf_drbd_feature_flags; static int hf_drbd_protocol_max; static int hf_drbd_sender_node_id; static int hf_drbd_receiver_node_id; static int hf_drbd_barrier; static int hf_drbd_set_size; static int hf_drbd_oldest_block_id; static int hf_drbd_youngest_block_id; static int hf_drbd_resync_rate; static int hf_drbd_verify_alg; static int hf_drbd_csums_alg; static int hf_drbd_c_plan_ahead; static int hf_drbd_c_delay_target; static int hf_drbd_c_fill_target; static int hf_drbd_c_max_rate; static int hf_drbd_protocol; static int hf_drbd_after_sb_0p; static int hf_drbd_after_sb_1p; static int hf_drbd_after_sb_2p; static int hf_drbd_conn_flags; static int hf_drbd_two_primaries; static int hf_drbd_integrity_alg; static int hf_drbd_current_uuid; static int hf_drbd_bitmap_uuid; static int hf_drbd_history_uuid_list; static int hf_drbd_history_uuid; static int hf_drbd_dirty_bits; static int hf_drbd_uuid_flags; static int hf_drbd_node_mask; static int hf_drbd_bitmap_uuids_mask; static int hf_drbd_uuid; static int hf_drbd_weak_nodes; static int hf_drbd_physical_block_size; static int hf_drbd_logical_block_size; static int hf_drbd_alignment_offset; static int hf_drbd_io_min; static int hf_drbd_io_opt; static int hf_drbd_discard_enabled; static int hf_drbd_discard_zeroes_data; static int hf_drbd_write_same_capable; static int hf_drbd_d_size; static int hf_drbd_u_size; static int hf_drbd_c_size; static int hf_drbd_max_bio_size; static int hf_drbd_queue_order_type; static int hf_drbd_dds_flags; static int hf_drbd_state; static int hf_drbd_retcode; static int hf_drbd_twopc_prepare_in; static int hf_drbd_tid; static int hf_drbd_twopc_flags; static int hf_drbd_initiator_node_id; static int hf_drbd_target_node_id; static int hf_drbd_nodes_to_reach; static int hf_drbd_primary_nodes; static int hf_drbd_user_size; static int hf_drbd_diskful_primary_nodes; static int hf_drbd_exposed_size; static int hf_drbd_reachable_nodes; static int hf_drbd_max_possible_size; static int hf_drbd_offset; static int hf_drbd_dagtag; static int hf_drbd_dagtag_node_id; static int hf_drbd_new_rx_descs_data; static int hf_drbd_new_rx_descs_control; static int hf_drbd_rx_desc_stolen_from; static int hf_drbd_state_role; static int hf_drbd_state_peer; static int hf_drbd_state_conn; static int hf_drbd_state_disk; static int hf_drbd_state_pdsk; static int hf_drbd_state_susp; static int hf_drbd_state_aftr_isp; static int hf_drbd_state_peer_isp; static int hf_drbd_state_user_isp; static int hf_drbd_state_susp_nod; static int hf_drbd_state_susp_fen; static int hf_drbd_state_quorum; static int hf_drbd_twopc_flag_has_reachable; static int hf_drbd_uuid_flag_discard_my_data; static int hf_drbd_uuid_flag_crashed_primary; static int hf_drbd_uuid_flag_inconsistent; static int hf_drbd_uuid_flag_skip_initial_sync; static int hf_drbd_uuid_flag_new_datagen; static int hf_drbd_uuid_flag_stable; static int hf_drbd_uuid_flag_got_stable; static int hf_drbd_uuid_flag_resync; static int hf_drbd_uuid_flag_reconnect; static int hf_drbd_uuid_flag_diskless_primary; static int hf_drbd_uuid_flag_primary_lost_quorum; static int hf_drbd_dp_hardbarrier; static int hf_drbd_dp_rw_sync; static int hf_drbd_dp_may_set_in_sync; static int hf_drbd_dp_unplug; static int hf_drbd_dp_fua; static int hf_drbd_dp_flush; static int hf_drbd_dp_discard; static int hf_drbd_dp_send_receive_ack; static int hf_drbd_dp_send_write_ack; static int hf_drbd_dp_wsame; static int hf_drbd_dp_zeroes; static int hf_drbd_lb_tcp_seq; static int hf_drbd_lb_tcp_length; static int ett_drbd; static int ett_drbd_lb_tcp; static int ett_drbd_state; static int ett_drbd_twopc_flags; static int ett_drbd_uuid_flags; static int ett_drbd_history_uuids; static int ett_drbd_data_flags; static int * const state_fields[] = { &hf_drbd_state_role, &hf_drbd_state_peer, &hf_drbd_state_conn, &hf_drbd_state_disk, &hf_drbd_state_pdsk, &hf_drbd_state_susp, &hf_drbd_state_aftr_isp, &hf_drbd_state_peer_isp, &hf_drbd_state_user_isp, &hf_drbd_state_susp_nod, &hf_drbd_state_susp_fen, &hf_drbd_state_quorum, NULL }; static int * const twopc_flag_fields[] = { &hf_drbd_twopc_flag_has_reachable, NULL }; static int * const uuid_flag_fields[] = { &hf_drbd_uuid_flag_discard_my_data, &hf_drbd_uuid_flag_crashed_primary, &hf_drbd_uuid_flag_inconsistent, &hf_drbd_uuid_flag_skip_initial_sync, &hf_drbd_uuid_flag_new_datagen, &hf_drbd_uuid_flag_stable, &hf_drbd_uuid_flag_got_stable, &hf_drbd_uuid_flag_resync, &hf_drbd_uuid_flag_reconnect, &hf_drbd_uuid_flag_diskless_primary, &hf_drbd_uuid_flag_primary_lost_quorum, NULL }; static int * const data_flag_fields[] = { &hf_drbd_dp_hardbarrier, &hf_drbd_dp_rw_sync, &hf_drbd_dp_may_set_in_sync, &hf_drbd_dp_unplug, &hf_drbd_dp_fua, &hf_drbd_dp_flush, &hf_drbd_dp_discard, &hf_drbd_dp_send_receive_ack, &hf_drbd_dp_send_write_ack, &hf_drbd_dp_wsame, &hf_drbd_dp_zeroes, NULL }; #define CHALLENGE_LEN 64 static bool is_bit_set_64(uint64_t value, int bit) { return !!(value & (UINT64_C(1) << bit)); } /* * Length of the frame header. */ #define DRBD_FRAME_HEADER_80_LEN 8 #define DRBD_FRAME_HEADER_95_LEN 8 #define DRBD_FRAME_HEADER_100_LEN 16 #define DRBD_TRANSPORT_RDMA_PACKET_LEN 16 #define DRBD_MAGIC 0x83740267 #define DRBD_MAGIC_BIG 0x835a #define DRBD_MAGIC_100 0x8620ec20 #define DRBD_TRANSPORT_RDMA_MAGIC 0x5257494E static unsigned read_drbd_packet_len(tvbuff_t *tvb, int offset) { uint32_t magic32; uint16_t magic16; magic32 = tvb_get_ntohl(tvb, offset); if (magic32 == DRBD_MAGIC) return DRBD_FRAME_HEADER_80_LEN + tvb_get_ntohs(tvb, offset + 6); if (tvb_reported_length_remaining(tvb, offset) >= DRBD_FRAME_HEADER_100_LEN && magic32 == DRBD_MAGIC_100) return DRBD_FRAME_HEADER_100_LEN + tvb_get_ntohl(tvb, offset + 8); magic16 = tvb_get_ntohs(tvb, offset); if (magic16 == DRBD_MAGIC_BIG) return DRBD_FRAME_HEADER_95_LEN + tvb_get_ntohl(tvb, offset + 4); return 0; } static unsigned get_drbd_pdu_len(packet_info *pinfo, tvbuff_t *tvb, int offset, void *data _U_) { unsigned drbd_len = read_drbd_packet_len(tvb, offset); if (tvb_reported_length_remaining(tvb, offset) >= DRBD_FRAME_HEADER_100_LEN && !drbd_len) { /* We have enough data to recognize any header, but none matched. * Either there is data corruption or this is actually an lb-tcp * stream. It is possible that the capture is missing the first lb-tcp * header. In that case, the stream will be misidentified as normal * DRBD on TCP. * * Reset the dissector and throw some exception so that a new dissector * is chosen by the heuristic. */ conversation_t *conversation = find_or_create_conversation(pinfo); conversation_set_dissector(conversation, NULL); THROW(ReportedBoundsError); } return drbd_len; } static int dissect_drbd_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) { dissect_drbd_message(tvb, pinfo, tree); return tvb_reported_length(tvb); } static int dissect_drbd(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data) { col_set_str(pinfo->cinfo, COL_PROTOCOL, "DRBD"); tcp_dissect_pdus(tvb, pinfo, tree, true, DRBD_FRAME_HEADER_80_LEN, get_drbd_pdu_len, dissect_drbd_pdu, data); return tvb_reported_length(tvb); } static unsigned get_drbd_lb_tcp_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_) { return 8 + tvb_get_ntohl(tvb, offset + 4); } static int dissect_drbd_lb_tcp_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) { proto_tree *lb_tcp_tree; proto_item *lb_tcp_ti; lb_tcp_ti = proto_tree_add_item(tree, proto_drbd_lb_tcp, tvb, 0, -1, ENC_NA); proto_item_set_text(lb_tcp_ti, "DRBD [lb-tcp]"); lb_tcp_tree = proto_item_add_subtree(lb_tcp_ti, ett_drbd_lb_tcp); proto_tree_add_item(lb_tcp_tree, hf_drbd_lb_tcp_seq, tvb, 0, 4, ENC_BIG_ENDIAN); proto_tree_add_item(lb_tcp_tree, hf_drbd_lb_tcp_length, tvb, 4, 4, ENC_BIG_ENDIAN); unsigned offset = 8; while (tvb_captured_length(tvb) >= offset + DRBD_FRAME_HEADER_80_LEN) { unsigned length = read_drbd_packet_len(tvb, offset); /* Was a header recognized? */ if (length == 0) { const char *info_text = col_get_text(pinfo->cinfo, COL_INFO); col_clear(pinfo->cinfo, COL_INFO); if (!info_text || !info_text[0]) col_append_ports(pinfo->cinfo, COL_INFO, PT_TCP, pinfo->srcport, pinfo->destport); col_append_str(pinfo->cinfo, COL_INFO, " [lb-tcp Payload]"); col_set_fence(pinfo->cinfo, COL_INFO); break; } dissect_drbd_message(tvb_new_subset_length(tvb, offset, length), pinfo, tree); offset += length; } return tvb_reported_length(tvb); } static int dissect_drbd_lb_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data) { col_set_str(pinfo->cinfo, COL_PROTOCOL, "DRBD lb-tcp"); tcp_dissect_pdus(tvb, pinfo, tree, true, 8, get_drbd_lb_tcp_pdu_len, dissect_drbd_lb_tcp_pdu, data); return tvb_reported_length(tvb); } static bool test_drbd_header(tvbuff_t *tvb, int offset) { int reported_length = tvb_reported_length(tvb); int captured_length = tvb_captured_length(tvb); if (reported_length < offset + DRBD_FRAME_HEADER_80_LEN || captured_length < offset + 4) return false; uint32_t magic32 = tvb_get_ntohl(tvb, offset); if (magic32 == DRBD_MAGIC) return true; else if (reported_length >= offset + DRBD_FRAME_HEADER_100_LEN && magic32 == DRBD_MAGIC_100) return true; else { uint16_t magic16 = tvb_get_ntohs(tvb, offset); if (magic16 == DRBD_MAGIC_BIG) return true; } return false; } static bool test_drbd_rdma_control_header(tvbuff_t *tvb) { unsigned reported_length = tvb_reported_length(tvb); if (reported_length < DRBD_TRANSPORT_RDMA_PACKET_LEN || tvb_captured_length(tvb) < 4) { return false; } uint32_t magic32 = tvb_get_ntohl(tvb, 0); return magic32 == DRBD_TRANSPORT_RDMA_MAGIC; } static bool test_drbd_protocol(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) { if (!test_drbd_header(tvb, 0)) return false; conversation_t *conversation = find_or_create_conversation(pinfo); conversation_set_dissector(conversation, drbd_handle); dissect_drbd(tvb, pinfo, tree, data); return true; } static bool test_drbd_lb_tcp_protocol(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) { /* DRBD packets may be split between lb-tcp wrapper packets. As a result, * there may be lb-tcp packets that do not contain any DRBD header. * However, we have no other way to identify lb-tcp packets, so look for a * DRBD header anyway. This is a best-effort solution. */ if (!test_drbd_header(tvb, 8)) return false; conversation_t *conversation = find_or_create_conversation(pinfo); conversation_set_dissector(conversation, drbd_lb_tcp_handle); dissect_drbd_lb_tcp(tvb, pinfo, tree, data); return true; } /* * A DRBD connection consists of 2 TCP connections. We need information from * one to correctly interpret the other. However, it is impossible to determine * definitely just from a packet trace which TCP connections belong together. * Fortunately, there is an essentially universal convention that the * connections have a statically allocated port number in common. One * connection uses it on one node, the other connection uses the same port * number but on the other node. The other port numbers are dynamically * allocated and thus greater. * * For example, the connections use: * 1. Port 7000 on node A, port 44444 on node B * 2. Port 55555 on node A, port 7000 on node B * * Hence we can associate one conversation_t to the DRBD connection by keying * it on the lower port number and the two addresses in a consistent order. */ static conversation_t *find_drbd_conversation(packet_info *pinfo) { address* addr_a; address* addr_b; uint32_t port_a = MIN(pinfo->srcport, pinfo->destport); if (cmp_address(&pinfo->src, &pinfo->dst) < 0) { addr_a = &pinfo->src; addr_b = &pinfo->dst; } else { addr_a = &pinfo->dst; addr_b = &pinfo->src; } conversation_t *conv = find_conversation(pinfo->num, addr_a, addr_b, CONVERSATION_TCP, port_a, 0, NO_PORT_B); if (!conv) { /* CONVERSATION_TEMPLATE prevents the port information being added once * a wildcard search matches. */ conv = conversation_new(pinfo->num, addr_a, addr_b, CONVERSATION_TCP, port_a, 0, NO_PORT2|CONVERSATION_TEMPLATE); } return conv; } /** * Returns buffer containing the payload. */ static tvbuff_t *decode_header(tvbuff_t *tvb, proto_tree *pt, uint16_t *command) { uint32_t magic32; uint16_t magic16; magic32 = tvb_get_ntohl(tvb, 0); if (magic32 == DRBD_MAGIC) { *command = tvb_get_ntohs(tvb, 4); proto_tree_add_item(pt, hf_drbd_command, tvb, 4, 2, ENC_BIG_ENDIAN); proto_tree_add_item(pt, hf_drbd_length, tvb, 6, 2, ENC_BIG_ENDIAN); return tvb_new_subset_remaining(tvb, DRBD_FRAME_HEADER_80_LEN); } if (tvb_reported_length(tvb) >= DRBD_FRAME_HEADER_100_LEN && magic32 == DRBD_MAGIC_100) { *command = tvb_get_ntohs(tvb, 6); proto_tree_add_item(pt, hf_drbd_volume, tvb, 4, 2, ENC_BIG_ENDIAN); proto_tree_add_item(pt, hf_drbd_command, tvb, 6, 2, ENC_BIG_ENDIAN); proto_tree_add_item(pt, hf_drbd_length, tvb, 8, 4, ENC_BIG_ENDIAN); return tvb_new_subset_remaining(tvb, DRBD_FRAME_HEADER_100_LEN); } magic16 = tvb_get_ntohs(tvb, 0); if (magic16 == DRBD_MAGIC_BIG) { *command = tvb_get_ntohs(tvb, 2); proto_tree_add_item(pt, hf_drbd_command, tvb, 2, 2, ENC_BIG_ENDIAN); proto_tree_add_item(pt, hf_drbd_length, tvb, 4, 4, ENC_BIG_ENDIAN); return tvb_new_subset_remaining(tvb, DRBD_FRAME_HEADER_95_LEN); } return NULL; } static const value_payload_decoder *find_payload_decoder(uint16_t command) { for (unsigned int i = 0; i < array_length(payload_decoders); i++) { if (payload_decoders[i].value == command) { return &payload_decoders[i]; } } return NULL; } static void dissect_drbd_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { proto_tree *drbd_tree; proto_item *ti; uint16_t command = -1; col_clear(pinfo->cinfo, COL_INFO); ti = proto_tree_add_item(tree, proto_drbd, tvb, 0, -1, ENC_NA); drbd_tree = proto_item_add_subtree(ti, ett_drbd); tvbuff_t *payload_tvb = decode_header(tvb, drbd_tree, &command); if (!payload_tvb) return; /* Indicate what kind of message this is. */ const char *packet_name = val_to_str(command, packet_names, "Unknown (0x%02x)"); const char *info_text = col_get_text(pinfo->cinfo, COL_INFO); if (!info_text || !info_text[0]) col_append_ports(pinfo->cinfo, COL_INFO, PT_TCP, pinfo->srcport, pinfo->destport); col_append_fstr(pinfo->cinfo, COL_INFO, " [%s]", packet_name); col_set_fence(pinfo->cinfo, COL_INFO); conversation_t *conv = find_drbd_conversation(pinfo); drbd_conv *conv_data = (drbd_conv *)conversation_get_proto_data(conv, proto_drbd); if (!conv_data) { conv_data = wmem_new0(wmem_file_scope(), drbd_conv); conv_data->twopc = wmem_map_new(wmem_file_scope(), drbd_twopc_key_hash, drbd_twopc_key_equal); conversation_add_proto_data(conv, proto_drbd, conv_data); } const value_payload_decoder *payload_decoder = find_payload_decoder(command); if (!PINFO_FD_VISITED(pinfo) && payload_decoder && payload_decoder->state_reader_fn) (*payload_decoder->state_reader_fn) (payload_tvb, pinfo, conv_data); if (tree == NULL) return; proto_item_set_text(ti, "DRBD [%s]", packet_name); if (payload_decoder && payload_decoder->tree_fn) (*payload_decoder->tree_fn) (payload_tvb, drbd_tree, conv_data); } static void drbd_ib_append_col_info(packet_info *pinfo, const char *packet_name) { const char *info_text; col_clear(pinfo->cinfo, COL_INFO); info_text = col_get_text(pinfo->cinfo, COL_INFO); if (!info_text || !info_text[0]) col_append_fstr(pinfo->cinfo, COL_INFO, "QP=0x%06x [%s]", pinfo->destport, packet_name); else col_append_fstr(pinfo->cinfo, COL_INFO, " [%s]", packet_name); col_set_fence(pinfo->cinfo, COL_INFO); } static void dissect_drbd_ib_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { proto_tree *drbd_tree; proto_item *ti; uint16_t command = -1; ti = proto_tree_add_item(tree, proto_drbd, tvb, 0, -1, ENC_NA); drbd_tree = proto_item_add_subtree(ti, ett_drbd); tvbuff_t *payload_tvb = decode_header(tvb, drbd_tree, &command); if (!payload_tvb) return; /* Indicate what kind of message this is. */ const char *packet_name = val_to_str(command, packet_names, "Unknown (0x%02x)"); drbd_ib_append_col_info(pinfo, packet_name); if (tree == NULL) return; proto_item_set_text(ti, "DRBD [%s]", packet_name); const value_payload_decoder *payload_decoder = find_payload_decoder(command); if (payload_decoder && payload_decoder->tree_fn) (*payload_decoder->tree_fn) (payload_tvb, drbd_tree, NULL); } static void dissect_drbd_ib_control_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { proto_tree *drbd_tree; proto_item *ti; drbd_ib_append_col_info(pinfo, "RDMA Flow Control"); if (tree == NULL) return; ti = proto_tree_add_item(tree, proto_drbd, tvb, 0, -1, ENC_NA); proto_item_set_text(ti, "DRBD [RDMA Flow Control]"); drbd_tree = proto_item_add_subtree(ti, ett_drbd); proto_tree_add_item(drbd_tree, hf_drbd_new_rx_descs_data, tvb, 4, 4, ENC_BIG_ENDIAN); proto_tree_add_item(drbd_tree, hf_drbd_new_rx_descs_control, tvb, 8, 4, ENC_BIG_ENDIAN); proto_tree_add_item(drbd_tree, hf_drbd_rx_desc_stolen_from, tvb, 12, 4, ENC_BIG_ENDIAN); } static bool dissect_drbd_ib(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) { if (!test_drbd_header(tvb, 0) && !test_drbd_rdma_control_header(tvb)) return false; col_set_str(pinfo->cinfo, COL_PROTOCOL, "DRBD RDMA"); while (1) { unsigned length; bool is_control_packet = test_drbd_rdma_control_header(tvb); if (is_control_packet) length = DRBD_TRANSPORT_RDMA_PACKET_LEN; else length = read_drbd_packet_len(tvb, 0); /* Was a header recognized? */ if (length == 0) break; tvbuff_t *packet_tvb = tvb_new_subset_length(tvb, 0, length); if (is_control_packet) dissect_drbd_ib_control_message(packet_tvb, pinfo, tree); else dissect_drbd_ib_message(packet_tvb, pinfo, tree); /* Is there enough data for another DRBD packet? */ if (tvb_reported_length(tvb) < length + DRBD_FRAME_HEADER_80_LEN) break; /* Move to the next DRBD packet. */ tvb = tvb_new_subset_remaining(tvb, length); } return true; } static void insert_twopc(tvbuff_t *tvb, packet_info *pinfo, drbd_conv *conv_data, enum drbd_packet command) { uint32_t flags = tvb_get_ntohl(tvb, 4); drbd_twopc_key *key = wmem_new0(wmem_file_scope(), drbd_twopc_key); key->tid = tvb_get_ntohl(tvb, 0); if (flags & TWOPC_HAS_FLAGS) key->initiator_node_id = tvb_get_int8(tvb, 10); else key->initiator_node_id = tvb_get_ntohil(tvb, 4); drbd_twopc_val *val = wmem_new0(wmem_file_scope(), drbd_twopc_val); val->prepare_frame = pinfo->num; val->command = command; wmem_map_insert(conv_data->twopc, key, val); } static void read_state_twopc_prepare(tvbuff_t *tvb, packet_info *pinfo, drbd_conv *conv_data) { insert_twopc(tvb, pinfo, conv_data, P_TWOPC_PREPARE); } static void read_state_twopc_prep_rsz(tvbuff_t *tvb, packet_info *pinfo, drbd_conv *conv_data) { insert_twopc(tvb, pinfo, conv_data, P_TWOPC_PREP_RSZ); } static void decode_payload_connection_features(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data _U_) { proto_tree_add_item(tree, hf_drbd_protocol_min, tvb, 0, 4, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_feature_flags, tvb, 4, 4, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_protocol_max, tvb, 8, 4, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_sender_node_id, tvb, 12, 4, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_receiver_node_id, tvb, 16, 4, ENC_BIG_ENDIAN); } static void decode_payload_auth_challenge(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data _U_) { proto_tree_add_bytes_format(tree, hf_drbd_auth_challenge_nonce, tvb, 0, CHALLENGE_LEN, NULL, "Nonce"); } static void decode_payload_auth_response(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data _U_) { proto_tree_add_bytes_format(tree, hf_drbd_auth_response_hash, tvb, 0, -1, NULL, "Hash"); } static void decode_data_common(tvbuff_t *tvb, proto_tree *tree) { proto_tree_add_item(tree, hf_drbd_sector, tvb, 0, 8, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_block_id, tvb, 8, 8, ENC_LITTLE_ENDIAN); proto_tree_add_item(tree, hf_drbd_seq_num, tvb, 16, 4, ENC_BIG_ENDIAN); proto_tree_add_bitmask(tree, tvb, 20, hf_drbd_dp_flags, ett_drbd_data_flags, data_flag_fields, ENC_BIG_ENDIAN); } static void decode_payload_data(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data _U_) { decode_data_common(tvb, tree); unsigned nbytes = tvb_reported_length_remaining(tvb, 24); proto_tree_add_uint(tree, hf_drbd_size, tvb, 0, 0, nbytes); /* For infiniband the data is not in this tvb, so we do not show the data field. */ if (tvb_captured_length(tvb) >= 24 + nbytes) { proto_tree_add_bytes_format(tree, hf_drbd_data, tvb, 24, nbytes, NULL, "Data (%u byte%s)", nbytes, plurality(nbytes, "", "s")); } } static void decode_payload_barrier(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data _U_) { proto_tree_add_item(tree, hf_drbd_barrier, tvb, 0, 4, ENC_LITTLE_ENDIAN); } static void decode_payload_data_request(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data _U_) { proto_tree_add_item(tree, hf_drbd_sector, tvb, 0, 8, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_block_id, tvb, 8, 8, ENC_LITTLE_ENDIAN); proto_tree_add_item(tree, hf_drbd_size, tvb, 16, 4, ENC_BIG_ENDIAN); } static void decode_payload_dagtag_data_request(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data _U_) { proto_tree_add_item(tree, hf_drbd_sector, tvb, 0, 8, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_block_id, tvb, 8, 8, ENC_LITTLE_ENDIAN); proto_tree_add_item(tree, hf_drbd_size, tvb, 16, 4, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_dagtag_node_id, tvb, 20, 4, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_dagtag, tvb, 24, 8, ENC_BIG_ENDIAN); } static void decode_payload_sync_param(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data _U_) { unsigned length = tvb_reported_length(tvb); unsigned offset = 0; proto_tree_add_item(tree, hf_drbd_resync_rate, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; proto_tree_add_item(tree, hf_drbd_verify_alg, tvb, offset, DRBD_STRING_MAX, ENC_ASCII | ENC_NA); offset += DRBD_STRING_MAX; if (length >= offset + DRBD_STRING_MAX) { proto_tree_add_item(tree, hf_drbd_csums_alg, tvb, offset, DRBD_STRING_MAX, ENC_ASCII | ENC_NA); offset += DRBD_STRING_MAX; } if (length >= offset + 16) { proto_tree_add_item(tree, hf_drbd_c_plan_ahead, tvb, offset, 4, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_c_delay_target, tvb, offset + 4, 4, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_c_fill_target, tvb, offset + 8, 4, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_c_max_rate, tvb, offset + 12, 4, ENC_BIG_ENDIAN); } } static void decode_payload_protocol(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data _U_) { proto_tree_add_item(tree, hf_drbd_protocol, tvb, 0, 4, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_after_sb_0p, tvb, 4, 4, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_after_sb_1p, tvb, 8, 4, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_after_sb_2p, tvb, 12, 4, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_conn_flags, tvb, 16, 4, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_two_primaries, tvb, 20, 4, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_integrity_alg, tvb, 24, -1, ENC_ASCII | ENC_NA); } static void decode_payload_uuids(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data _U_) { proto_tree_add_item(tree, hf_drbd_current_uuid, tvb, 0, 8, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_bitmap_uuid, tvb, 8, 8, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_history_uuid, tvb, 16, 8, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_history_uuid, tvb, 24, 8, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_dirty_bits, tvb, 32, 8, ENC_BIG_ENDIAN); proto_tree_add_bitmask(tree, tvb, 40, hf_drbd_uuid_flags, ett_drbd_uuid_flags, uuid_flag_fields, ENC_BIG_ENDIAN); } static void decode_payload_sizes(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data _U_) { proto_tree_add_item(tree, hf_drbd_d_size, tvb, 0, 8, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_u_size, tvb, 8, 8, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_c_size, tvb, 16, 8, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_max_bio_size, tvb, 24, 4, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_queue_order_type, tvb, 28, 2, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_dds_flags, tvb, 30, 2, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_physical_block_size, tvb, 32, 4, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_logical_block_size, tvb, 36, 4, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_alignment_offset, tvb, 40, 4, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_io_min, tvb, 44, 4, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_io_opt, tvb, 48, 4, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_discard_enabled, tvb, 52, 1, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_discard_zeroes_data, tvb, 53, 1, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_write_same_capable, tvb, 54, 1, ENC_BIG_ENDIAN); } static void decode_payload_state(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data _U_) { proto_tree_add_bitmask(tree, tvb, 0, hf_drbd_state, ett_drbd_state, state_fields, ENC_BIG_ENDIAN); } /* Filter fields leaving only those with bitmask overlapping with the given mask. */ static void mask_fields(uint32_t mask, int * const fields[], int * masked_fields[]) { int masked_i = 0; for (int fields_i = 0; fields[fields_i]; fields_i++) { header_field_info *hf = proto_registrar_get_nth(*fields[fields_i]); if (hf && mask & hf->bitmask) { masked_fields[masked_i] = fields[fields_i]; masked_i++; } } masked_fields[masked_i] = NULL; } static void decode_state_change(tvbuff_t *tvb, proto_tree *tree, int offset) { uint32_t state_mask = tvb_get_ntohl(tvb, offset); int * masked_state_fields[array_length(state_fields)]; mask_fields(state_mask, state_fields, masked_state_fields); if (masked_state_fields[0]) { proto_tree_add_bitmask(tree, tvb, offset + 4, hf_drbd_state, ett_drbd_state, masked_state_fields, ENC_BIG_ENDIAN); } else { proto_tree_add_item(tree, hf_drbd_state, tvb, offset + 4, 4, ENC_BIG_ENDIAN); } } static void decode_payload_req_state(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data _U_) { decode_state_change(tvb, tree, 0); } static void decode_payload_sync_uuid(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data _U_) { proto_tree_add_item(tree, hf_drbd_uuid, tvb, 0, 8, ENC_BIG_ENDIAN); } static void decode_payload_skip(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data _U_) { proto_tree_add_item(tree, hf_drbd_seq_num, tvb, 0, 4, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_offset, tvb, 4, 4, ENC_BIG_ENDIAN); } static void decode_payload_out_of_sync(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data _U_) { proto_tree_add_item(tree, hf_drbd_sector, tvb, 0, 8, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_size, tvb, 8, 4, ENC_BIG_ENDIAN); } /* Return the twopc flags, if present. */ static uint32_t decode_twopc_request_common(tvbuff_t *tvb, proto_tree *tree, drbd_twopc_key *key) { proto_tree_add_item_ret_uint(tree, hf_drbd_tid, tvb, 0, 4, ENC_BIG_ENDIAN, key ? &key->tid : NULL); uint32_t flags = tvb_get_ntohl(tvb, 4); if (flags & TWOPC_HAS_FLAGS) { proto_tree_add_bitmask(tree, tvb, 4, hf_drbd_twopc_flags, ett_drbd_twopc_flags, twopc_flag_fields, ENC_BIG_ENDIAN); proto_tree_add_item_ret_int(tree, hf_drbd_initiator_node_id, tvb, 10, 1, ENC_BIG_ENDIAN, key ? &key->initiator_node_id : NULL); proto_tree_add_item(tree, hf_drbd_target_node_id, tvb, 11, 1, ENC_BIG_ENDIAN); } else { flags = 0; proto_tree_add_item_ret_int(tree, hf_drbd_initiator_node_id, tvb, 4, 4, ENC_BIG_ENDIAN, key ? &key->initiator_node_id : NULL); proto_tree_add_item(tree, hf_drbd_target_node_id, tvb, 8, 4, ENC_BIG_ENDIAN); } proto_tree_add_item(tree, hf_drbd_nodes_to_reach, tvb, 12, 8, ENC_BIG_ENDIAN); return flags; } static void decode_payload_twopc_prepare(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data _U_) { uint32_t flags = decode_twopc_request_common(tvb, tree, NULL); if (!(flags & TWOPC_HAS_FLAGS)) proto_tree_add_item(tree, hf_drbd_primary_nodes, tvb, 20, 8, ENC_BIG_ENDIAN); decode_state_change(tvb, tree, 28); } static void decode_payload_twopc_prep_rsz(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data _U_) { decode_twopc_request_common(tvb, tree, NULL); proto_tree_add_item(tree, hf_drbd_user_size, tvb, 20, 8, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_dds_flags, tvb, 28, 2, ENC_BIG_ENDIAN); } static void decode_payload_twopc_commit(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data) { drbd_twopc_key key; uint32_t flags = decode_twopc_request_common(tvb, tree, &key); if (!conv_data) return; drbd_twopc_val *val = wmem_map_lookup(conv_data->twopc, &key); if (!val) return; proto_item *it = proto_tree_add_uint(tree, hf_drbd_twopc_prepare_in, tvb, 0, 0, val->prepare_frame); proto_item_set_generated(it); if (val->command == P_TWOPC_PREPARE) { proto_tree_add_item(tree, hf_drbd_primary_nodes, tvb, 20, 8, ENC_BIG_ENDIAN); if (!(flags & TWOPC_HAS_FLAGS)) decode_state_change(tvb, tree, 28); else if (flags & TWOPC_HAS_REACHABLE) proto_tree_add_item(tree, hf_drbd_reachable_nodes, tvb, 28, 8, ENC_BIG_ENDIAN); } else if (val->command == P_TWOPC_PREP_RSZ) { proto_tree_add_item(tree, hf_drbd_diskful_primary_nodes, tvb, 20, 8, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_exposed_size, tvb, 28, 8, ENC_BIG_ENDIAN); } } static void decode_payload_dagtag(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data _U_) { proto_tree_add_item(tree, hf_drbd_dagtag, tvb, 0, 8, ENC_BIG_ENDIAN); } static void decode_payload_uuids110(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data _U_) { proto_tree_add_item(tree, hf_drbd_current_uuid, tvb, 0, 8, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_dirty_bits, tvb, 8, 8, ENC_BIG_ENDIAN); proto_tree_add_bitmask(tree, tvb, 16, hf_drbd_uuid_flags, ett_drbd_uuid_flags, uuid_flag_fields, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_node_mask, tvb, 24, 8, ENC_BIG_ENDIAN); uint64_t bitmap_uuids_mask; proto_tree_add_item_ret_uint64(tree, hf_drbd_bitmap_uuids_mask, tvb, 32, 8, ENC_BIG_ENDIAN, &bitmap_uuids_mask); unsigned offset = 40; for (int i = 0; i < 64; i++) { if (is_bit_set_64(bitmap_uuids_mask, i)) { uint64_t bitmap_uuid = tvb_get_ntoh64(tvb, offset); proto_tree_add_uint64_format(tree, hf_drbd_bitmap_uuid, tvb, offset, 8, bitmap_uuid, "Bitmap UUID for node %d: 0x%016" PRIx64, i, bitmap_uuid); offset += 8; } } proto_item *history_uuids = proto_tree_add_item(tree, hf_drbd_history_uuid_list, tvb, offset, -1, ENC_NA); proto_tree *history_tree = proto_item_add_subtree(history_uuids, ett_drbd_history_uuids); unsigned total_length = tvb_reported_length(tvb); while (offset < total_length) { proto_tree_add_item(history_tree, hf_drbd_history_uuid, tvb, offset, 8, ENC_BIG_ENDIAN); offset += 8; } } static void decode_payload_peer_dagtag(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data _U_) { proto_tree_add_item(tree, hf_drbd_dagtag, tvb, 0, 8, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_dagtag_node_id, tvb, 8, 4, ENC_BIG_ENDIAN); } static void decode_payload_current_uuid(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data _U_) { proto_tree_add_item(tree, hf_drbd_uuid, tvb, 0, 8, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_weak_nodes, tvb, 8, 8, ENC_BIG_ENDIAN); } static void decode_payload_data_size(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data _U_) { decode_data_common(tvb, tree); proto_tree_add_item(tree, hf_drbd_size, tvb, 24, 4, ENC_BIG_ENDIAN); } static void decode_payload_data_wsame(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data _U_) { decode_data_common(tvb, tree); proto_tree_add_item(tree, hf_drbd_size, tvb, 24, 4, ENC_BIG_ENDIAN); unsigned nbytes = tvb_reported_length_remaining(tvb, 28); /* For infiniband the data is not in this tvb, so we do not show the data field. */ if (tvb_captured_length(tvb) >= 28 + nbytes) { proto_tree_add_bytes_format(tree, hf_drbd_data, tvb, 28, nbytes, NULL, "Data (%u byte%s)", nbytes, plurality(nbytes, "", "s")); } } static void decode_payload_rs_deallocated(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data _U_) { proto_tree_add_item(tree, hf_drbd_sector, tvb, 0, 8, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_size, tvb, 8, 4, ENC_BIG_ENDIAN); } static void decode_payload_block_ack(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data _U_) { proto_tree_add_item(tree, hf_drbd_sector, tvb, 0, 8, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_block_id, tvb, 8, 8, ENC_LITTLE_ENDIAN); proto_tree_add_item(tree, hf_drbd_size, tvb, 16, 4, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_seq_num, tvb, 20, 4, ENC_BIG_ENDIAN); } static void decode_payload_ov_result(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data _U_) { proto_tree_add_item(tree, hf_drbd_sector, tvb, 0, 8, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_block_id, tvb, 8, 8, ENC_LITTLE_ENDIAN); proto_tree_add_item(tree, hf_drbd_size, tvb, 16, 4, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_seq_num, tvb, 20, 4, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_ov_result, tvb, 24, 8, ENC_BIG_ENDIAN); } static void decode_payload_barrier_ack(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data _U_) { proto_tree_add_item(tree, hf_drbd_barrier, tvb, 0, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(tree, hf_drbd_set_size, tvb, 4, 4, ENC_BIG_ENDIAN); } static void decode_payload_confirm_stable(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data _U_) { proto_tree_add_item(tree, hf_drbd_oldest_block_id, tvb, 0, 8, ENC_LITTLE_ENDIAN); proto_tree_add_item(tree, hf_drbd_youngest_block_id, tvb, 8, 8, ENC_LITTLE_ENDIAN); proto_tree_add_item(tree, hf_drbd_set_size, tvb, 16, 4, ENC_BIG_ENDIAN); } static void decode_payload_rq_s_reply(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data _U_) { proto_tree_add_item(tree, hf_drbd_retcode, tvb, 0, 4, ENC_BIG_ENDIAN); } static void decode_payload_peer_ack(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data _U_) { proto_tree_add_item(tree, hf_drbd_node_mask, tvb, 0, 8, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_dagtag, tvb, 8, 8, ENC_BIG_ENDIAN); } static void decode_payload_peers_in_sync(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data _U_) { proto_tree_add_item(tree, hf_drbd_sector, tvb, 0, 8, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_node_mask, tvb, 8, 8, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_size, tvb, 16, 4, ENC_BIG_ENDIAN); } static void decode_payload_twopc_reply(tvbuff_t *tvb, proto_tree *tree, drbd_conv *conv_data) { drbd_twopc_key key; proto_tree_add_item_ret_uint(tree, hf_drbd_tid, tvb, 0, 4, ENC_BIG_ENDIAN, &key.tid); proto_tree_add_item_ret_int(tree, hf_drbd_initiator_node_id, tvb, 4, 4, ENC_BIG_ENDIAN, &key.initiator_node_id); proto_tree_add_item(tree, hf_drbd_reachable_nodes, tvb, 8, 8, ENC_BIG_ENDIAN); if (!conv_data) return; drbd_twopc_val *val = wmem_map_lookup(conv_data->twopc, &key); if (!val) return; proto_item *it = proto_tree_add_uint(tree, hf_drbd_twopc_prepare_in, tvb, 0, 0, val->prepare_frame); proto_item_set_generated(it); if (val->command == P_TWOPC_PREPARE) { proto_tree_add_item(tree, hf_drbd_primary_nodes, tvb, 16, 8, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_weak_nodes, tvb, 24, 8, ENC_BIG_ENDIAN); } else if (val->command == P_TWOPC_PREP_RSZ) { proto_tree_add_item(tree, hf_drbd_diskful_primary_nodes, tvb, 16, 8, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_drbd_max_possible_size, tvb, 24, 8, ENC_BIG_ENDIAN); } } static void format_node_mask(char *s, uint64_t value) { if (!value) { (void) g_strlcpy(s, "", ITEM_LABEL_LENGTH); return; } int written = 0; int run_start = -1; for (int i = 0; i < 64 && written < ITEM_LABEL_LENGTH; i++) { bool is_set = is_bit_set_64(value, i); int run_end; if (!is_set) { run_end = i; } else if (i == 63) { if (run_start == -1) run_start = i; run_end = 64; } else { run_end = -1; } if (run_start != -1 && run_end != -1) { int run_length = run_end - run_start; const char *sep = written ? ", " : ""; if (run_length == 1) written += snprintf(s + written, ITEM_LABEL_LENGTH - written, "%s%d", sep, run_start); else if (run_length == 2) written += snprintf(s + written, ITEM_LABEL_LENGTH - written, "%s%d, %d", sep, run_start, run_start + 1); else written += snprintf(s + written, ITEM_LABEL_LENGTH - written, "%s%d - %d", sep, run_start, run_end - 1); } if (!is_set) run_start = -1; else if (run_start == -1) run_start = i; } } void proto_register_drbd(void) { static hf_register_info hf[] = { { &hf_drbd_command, { "Command", "drbd.command", FT_UINT16, BASE_HEX, VALS(packet_names), 0x0, NULL, HFILL }}, { &hf_drbd_length, { "Payload length", "drbd.length", FT_UINT32, BASE_HEX_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_volume, { "Volume", "drbd.volume", FT_INT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_auth_challenge_nonce, { "Nonce", "drbd.auth_nonce", FT_BYTES, BASE_NO_DISPLAY_VALUE, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_auth_response_hash, { "Hash", "drbd.auth_hash", FT_BYTES, BASE_NO_DISPLAY_VALUE, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_sector, { "Sector", "drbd.sector", FT_UINT64, BASE_DEC_HEX, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_block_id, { "Block ID", "drbd.block_id", FT_UINT64, BASE_HEX, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_seq_num, { "Sequence number", "drbd.seq_num", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_ov_result, { "Online verify result", "drbd.ov_result", FT_UINT64, BASE_DEC|BASE_VAL64_STRING, VALS64(ov_result_codes), 0x0, NULL, HFILL }}, { &hf_drbd_dp_flags, { "Data flags", "drbd.dp_flags", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_data, { "Data", "drbd.data", FT_BYTES, BASE_NO_DISPLAY_VALUE, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_size, { "Size", "drbd.size", FT_UINT32, BASE_DEC_HEX, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_protocol_min, { "protocol_min", "drbd.protocol_min", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_feature_flags, { "feature_flags", "drbd.feature_flags", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_protocol_max, { "protocol_max", "drbd.protocol_max", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_sender_node_id, { "sender_node_id", "drbd.sender_node_id", FT_INT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_receiver_node_id, { "receiver_node_id", "drbd.receiver_node_id", FT_INT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_barrier, { "barrier", "drbd.barrier", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_set_size, { "set_size", "drbd.set_size", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_oldest_block_id, { "oldest_block_id", "drbd.oldest_block_id", FT_UINT64, BASE_HEX, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_youngest_block_id, { "youngest_block_id", "drbd.youngest_block_id", FT_UINT64, BASE_HEX, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_resync_rate, { "resync_rate", "drbd.resync_rate", FT_UINT32, BASE_DEC|BASE_UNIT_STRING, UNS(&units_kibps), 0x0, NULL, HFILL }}, { &hf_drbd_verify_alg, { "verify_alg", "drbd.verify_alg", FT_STRINGZ, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_csums_alg, { "csums_alg", "drbd.csums_alg", FT_STRINGZ, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_c_plan_ahead, { "c_plan_ahead", "drbd.c_plan_ahead", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_c_delay_target, { "c_delay_target", "drbd.c_delay_target", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_c_fill_target, { "c_fill_target", "drbd.c_fill_target", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_c_max_rate, { "c_max_rate", "drbd.c_max_rate", FT_UINT32, BASE_DEC|BASE_UNIT_STRING, UNS(&units_kibps), 0x0, NULL, HFILL }}, { &hf_drbd_protocol, { "protocol", "drbd.protocol", FT_UINT32, BASE_HEX, VALS(protocol_names), 0x0, NULL, HFILL }}, { &hf_drbd_after_sb_0p, { "after_sb_0p", "drbd.after_sb_0p", FT_UINT32, BASE_HEX_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_after_sb_1p, { "after_sb_1p", "drbd.after_sb_1p", FT_UINT32, BASE_HEX_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_after_sb_2p, { "after_sb_2p", "drbd.after_sb_2p", FT_UINT32, BASE_HEX_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_conn_flags, { "conn_flags", "drbd.conn_flags", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_two_primaries, { "two_primaries", "drbd.two_primaries", FT_BOOLEAN, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_integrity_alg, { "integrity_alg", "drbd.integrity_alg", FT_STRINGZ, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_current_uuid, { "Current UUID", "drbd.current_uuid", FT_UINT64, BASE_HEX, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_bitmap_uuid, { "Bitmap UUID", "drbd.bitmap_uuid", FT_UINT64, BASE_HEX, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_history_uuid_list, { "History UUIDs", "drbd.history_uuids", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_history_uuid, { "History UUID", "drbd.history_uuid", FT_UINT64, BASE_HEX, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_dirty_bits, { "Dirty bits", "drbd.dirty_bits", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_uuid_flags, { "UUID flags", "drbd.uuid_flags", FT_UINT64, BASE_HEX, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_node_mask, { "Nodes", "drbd.node_mask", FT_UINT64, BASE_CUSTOM, CF_FUNC(format_node_mask), 0x0, NULL, HFILL }}, { &hf_drbd_bitmap_uuids_mask, { "Bitmap UUID nodes", "drbd.bitmap_uuids_mask", FT_UINT64, BASE_CUSTOM, CF_FUNC(format_node_mask), 0x0, NULL, HFILL }}, { &hf_drbd_uuid, { "uuid", "drbd.uuid", FT_UINT64, BASE_HEX, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_weak_nodes, { "weak_nodes", "drbd.weak_nodes", FT_UINT64, BASE_CUSTOM, CF_FUNC(format_node_mask), 0x0, NULL, HFILL }}, { &hf_drbd_physical_block_size, { "physical_block_size", "drbd.physical_block_size", FT_UINT32, BASE_DEC_HEX, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_logical_block_size, { "logical_block_size", "drbd.logical_block_size", FT_UINT32, BASE_DEC_HEX, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_alignment_offset, { "alignment_offset", "drbd.alignment_offset", FT_UINT32, BASE_DEC_HEX, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_io_min, { "io_min", "drbd.io_min", FT_UINT32, BASE_DEC_HEX, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_io_opt, { "io_opt", "drbd.io_opt", FT_UINT32, BASE_DEC_HEX, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_discard_enabled, { "discard_enabled", "drbd.discard_enabled", FT_BOOLEAN, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_discard_zeroes_data, { "discard_zeroes_data", "drbd.discard_zeroes_data", FT_BOOLEAN, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_write_same_capable, { "write_same_capable", "drbd.write_same_capable", FT_BOOLEAN, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_d_size, { "d_size", "drbd.d_size", FT_UINT64, BASE_DEC_HEX, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_u_size, { "u_size", "drbd.u_size", FT_UINT64, BASE_DEC_HEX, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_c_size, { "c_size", "drbd.c_size", FT_UINT64, BASE_DEC_HEX, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_max_bio_size, { "max_bio_size", "drbd.max_bio_size", FT_UINT32, BASE_DEC_HEX, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_queue_order_type, { "queue_order_type", "drbd.queue_order_type", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_dds_flags, { "dds_flags", "drbd.dds_flags", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_state, { "state", "drbd.state", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_retcode, { "retcode", "drbd.retcode", FT_UINT32, BASE_HEX_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_twopc_prepare_in, { "Two-phase commit prepare in", "drbd.twopc_prepare_in", FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_RESPONSE), 0x0, NULL, HFILL }}, { &hf_drbd_tid, { "tid", "drbd.tid", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_twopc_flags, { "twopc_flags", "drbd.twopc_flags", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_initiator_node_id, { "initiator_node_id", "drbd.initiator_node_id", FT_INT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_target_node_id, { "target_node_id", "drbd.target_node_id", FT_INT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_nodes_to_reach, { "nodes_to_reach", "drbd.nodes_to_reach", FT_UINT64, BASE_CUSTOM, CF_FUNC(format_node_mask), 0x0, NULL, HFILL }}, { &hf_drbd_primary_nodes, { "primary_nodes", "drbd.primary_nodes", FT_UINT64, BASE_CUSTOM, CF_FUNC(format_node_mask), 0x0, NULL, HFILL }}, { &hf_drbd_user_size, { "user_size", "drbd.user_size", FT_UINT64, BASE_DEC_HEX, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_diskful_primary_nodes, { "diskful_primary_nodes", "drbd.diskful_primary_nodes", FT_UINT64, BASE_CUSTOM, CF_FUNC(format_node_mask), 0x0, NULL, HFILL }}, { &hf_drbd_exposed_size, { "exposed_size", "drbd.exposed_size", FT_UINT64, BASE_DEC_HEX, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_reachable_nodes, { "reachable_nodes", "drbd.reachable_nodes", FT_UINT64, BASE_CUSTOM, CF_FUNC(format_node_mask), 0x0, NULL, HFILL }}, { &hf_drbd_max_possible_size, { "max_possible_size", "drbd.max_possible_size", FT_UINT64, BASE_DEC_HEX, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_offset, { "offset", "drbd.offset", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_dagtag, { "dagtag", "drbd.dagtag", FT_UINT64, BASE_DEC_HEX, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_dagtag_node_id, { "dagtag_node_id", "drbd.dagtag_node_id", FT_INT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_new_rx_descs_data, { "New descriptors received (data)", "drbd.new_rx_descs_data", FT_INT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_new_rx_descs_control, { "New descriptors received (control)", "drbd.new_rx_descs_control", FT_INT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_rx_desc_stolen_from, { "Descriptor stolen from", "drbd.rx_desc_stolen_from", FT_INT32, BASE_DEC, VALS(stream_names), 0x0, NULL, HFILL }}, { &hf_drbd_state_role, { "role", "drbd.state.role", FT_UINT32, BASE_DEC, VALS(role_names), STATE_ROLE, NULL, HFILL }}, { &hf_drbd_state_peer, { "peer", "drbd.state.peer", FT_UINT32, BASE_DEC, VALS(role_names), STATE_PEER, NULL, HFILL }}, { &hf_drbd_state_conn, { "conn", "drbd.state.conn", FT_UINT32, BASE_DEC, VALS(connection_state_names), STATE_CONN, NULL, HFILL }}, { &hf_drbd_state_disk, { "disk", "drbd.state.disk", FT_UINT32, BASE_DEC, VALS(disk_state_names), STATE_DISK, NULL, HFILL }}, { &hf_drbd_state_pdsk, { "pdsk", "drbd.state.pdsk", FT_UINT32, BASE_DEC, VALS(disk_state_names), STATE_PDSK, NULL, HFILL }}, { &hf_drbd_state_susp, { "susp", "drbd.state.susp", FT_BOOLEAN, 32, NULL, STATE_SUSP, NULL, HFILL }}, { &hf_drbd_state_aftr_isp, { "aftr_isp", "drbd.state.aftr_isp", FT_BOOLEAN, 32, NULL, STATE_AFTR_ISP, NULL, HFILL }}, { &hf_drbd_state_peer_isp, { "peer_isp", "drbd.state.peer_isp", FT_BOOLEAN, 32, NULL, STATE_PEER_ISP, NULL, HFILL }}, { &hf_drbd_state_user_isp, { "user_isp", "drbd.state.user_isp", FT_BOOLEAN, 32, NULL, STATE_USER_ISP, NULL, HFILL }}, { &hf_drbd_state_susp_nod, { "susp_nod", "drbd.state.susp_nod", FT_BOOLEAN, 32, NULL, STATE_SUSP_NOD, NULL, HFILL }}, { &hf_drbd_state_susp_fen, { "susp_fen", "drbd.state.susp_fen", FT_BOOLEAN, 32, NULL, STATE_SUSP_FEN, NULL, HFILL }}, { &hf_drbd_state_quorum, { "quorum", "drbd.state.quorum", FT_BOOLEAN, 32, NULL, STATE_QUORUM, NULL, HFILL }}, { &hf_drbd_twopc_flag_has_reachable, { "has_reachable", "drbd.twopc_flags.has_reachable", FT_BOOLEAN, 32, NULL, TWOPC_HAS_REACHABLE, NULL, HFILL }}, { &hf_drbd_uuid_flag_discard_my_data, { "discard_my_data", "drbd.uuid_flag.discard_my_data", FT_BOOLEAN, 64, NULL, UUID_FLAG_DISCARD_MY_DATA, NULL, HFILL }}, { &hf_drbd_uuid_flag_crashed_primary, { "crashed_primary", "drbd.uuid_flag.crashed_primary", FT_BOOLEAN, 64, NULL, UUID_FLAG_CRASHED_PRIMARY, NULL, HFILL }}, { &hf_drbd_uuid_flag_inconsistent, { "inconsistent", "drbd.uuid_flag.inconsistent", FT_BOOLEAN, 64, NULL, UUID_FLAG_INCONSISTENT, NULL, HFILL }}, { &hf_drbd_uuid_flag_skip_initial_sync, { "skip_initial_sync", "drbd.uuid_flag.skip_initial_sync", FT_BOOLEAN, 64, NULL, UUID_FLAG_SKIP_INITIAL_SYNC, NULL, HFILL }}, { &hf_drbd_uuid_flag_new_datagen, { "new_datagen", "drbd.uuid_flag.new_datagen", FT_BOOLEAN, 64, NULL, UUID_FLAG_NEW_DATAGEN, NULL, HFILL }}, { &hf_drbd_uuid_flag_stable, { "stable", "drbd.uuid_flag.stable", FT_BOOLEAN, 64, NULL, UUID_FLAG_STABLE, NULL, HFILL }}, { &hf_drbd_uuid_flag_got_stable, { "got_stable", "drbd.uuid_flag.got_stable", FT_BOOLEAN, 64, NULL, UUID_FLAG_GOT_STABLE, NULL, HFILL }}, { &hf_drbd_uuid_flag_resync, { "resync", "drbd.uuid_flag.resync", FT_BOOLEAN, 64, NULL, UUID_FLAG_RESYNC, NULL, HFILL }}, { &hf_drbd_uuid_flag_reconnect, { "reconnect", "drbd.uuid_flag.reconnect", FT_BOOLEAN, 64, NULL, UUID_FLAG_RECONNECT, NULL, HFILL }}, { &hf_drbd_uuid_flag_diskless_primary, { "diskless_primary", "drbd.uuid_flag.diskless_primary", FT_BOOLEAN, 64, NULL, UUID_FLAG_DISKLESS_PRIMARY, NULL, HFILL }}, { &hf_drbd_uuid_flag_primary_lost_quorum, { "primary_lost_quorum", "drbd.uuid_flag.primary_lost_quorum", FT_BOOLEAN, 64, NULL, UUID_FLAG_PRIMARY_LOST_QUORUM, NULL, HFILL }}, { &hf_drbd_dp_hardbarrier, { "hardbarrier", "drbd.dp_flag.hardbarrier", FT_BOOLEAN, 32, NULL, DP_HARDBARRIER, NULL, HFILL }}, { &hf_drbd_dp_rw_sync, { "rw_sync", "drbd.dp_flag.rw_sync", FT_BOOLEAN, 32, NULL, DP_RW_SYNC, NULL, HFILL }}, { &hf_drbd_dp_may_set_in_sync, { "may_set_in_sync", "drbd.dp_flag.may_set_in_sync", FT_BOOLEAN, 32, NULL, DP_MAY_SET_IN_SYNC, NULL, HFILL }}, { &hf_drbd_dp_unplug, { "unplug", "drbd.dp_flag.unplug", FT_BOOLEAN, 32, NULL, DP_UNPLUG, NULL, HFILL }}, { &hf_drbd_dp_fua, { "fua", "drbd.dp_flag.fua", FT_BOOLEAN, 32, NULL, DP_FUA, NULL, HFILL }}, { &hf_drbd_dp_flush, { "flush", "drbd.dp_flag.flush", FT_BOOLEAN, 32, NULL, DP_FLUSH, NULL, HFILL }}, { &hf_drbd_dp_discard, { "discard", "drbd.dp_flag.discard", FT_BOOLEAN, 32, NULL, DP_DISCARD, NULL, HFILL }}, { &hf_drbd_dp_send_receive_ack, { "send_receive_ack", "drbd.dp_flag.send_receive_ack", FT_BOOLEAN, 32, NULL, DP_SEND_RECEIVE_ACK, NULL, HFILL }}, { &hf_drbd_dp_send_write_ack, { "send_write_ack", "drbd.dp_flag.send_write_ack", FT_BOOLEAN, 32, NULL, DP_SEND_WRITE_ACK, NULL, HFILL }}, { &hf_drbd_dp_wsame, { "wsame", "drbd.dp_flag.wsame", FT_BOOLEAN, 32, NULL, DP_WSAME, NULL, HFILL }}, { &hf_drbd_dp_zeroes, { "zeroes", "drbd.dp_flag.zeroes", FT_BOOLEAN, 32, NULL, DP_ZEROES, NULL, HFILL }}, }; static hf_register_info hf_lb_tcp[] = { { &hf_drbd_lb_tcp_seq, { "lb-tcp sequence number", "drbd_lb_tcp.seq", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_drbd_lb_tcp_length, { "lb-tcp length", "drbd_lb_tcp.length", FT_UINT32, BASE_HEX_DEC, NULL, 0x0, NULL, HFILL }}, }; static int *ett[] = { &ett_drbd, &ett_drbd_lb_tcp, &ett_drbd_state, &ett_drbd_twopc_flags, &ett_drbd_uuid_flags, &ett_drbd_history_uuids, &ett_drbd_data_flags, }; proto_drbd = proto_register_protocol("DRBD Protocol", "DRBD", "drbd"); proto_register_field_array(proto_drbd, hf, array_length(hf)); proto_drbd_lb_tcp = proto_register_protocol("DRBD Load-Balanced Protocol", "DRBD lb-tcp", "drbd_lb_tcp"); proto_register_field_array(proto_drbd_lb_tcp, hf_lb_tcp, array_length(hf_lb_tcp)); proto_register_subtree_array(ett, array_length(ett)); drbd_handle = register_dissector("drbd", dissect_drbd, proto_drbd); drbd_lb_tcp_handle = register_dissector("drbd_lb_tcp", dissect_drbd_lb_tcp, proto_drbd_lb_tcp); } void proto_reg_handoff_drbd(void) { heur_dissector_add("tcp", test_drbd_protocol, "DRBD over TCP", "drbd_tcp", proto_drbd, HEURISTIC_DISABLE); heur_dissector_add("infiniband.payload", dissect_drbd_ib, "DRBD over RDMA", "drbd_rdma", proto_drbd, HEURISTIC_DISABLE); heur_dissector_add("tcp", test_drbd_lb_tcp_protocol, "DRBD Load-Balanced over TCP", "drbd_lb_tcp", proto_drbd_lb_tcp, HEURISTIC_DISABLE); } /* * Editor modelines - https://www.wireshark.org/tools/modelines.html * * Local variables: * c-basic-offset: 4 * tab-width: 8 * indent-tabs-mode: nil * End: * * vi: set shiftwidth=4 tabstop=8 expandtab: * :indentSize=4:tabSize=8:noTabs=true: */