工欲善其事,必先利其器-redis 6.0 RCMB集群协议wireshark解析插件

先看效果

代码实现

需要修改适配相关的端口,或者wireshark设置强制解析

do
    -- 简单的认为单个tcp报文就是单个rcmb消息,假设不会拆包或者合包
    local p_RCMB = Proto("RCMB","Redis Cluster message bus")

    --RCmb协议的基础字段
    local base_sig = ProtoField.string("RCMB.sig","Sig")
    local base_totlen = ProtoField.uint32("RCMB.totlen","TotLen",base.DEC)
    local base_ver = ProtoField.uint16("RCMB.ver","Ver",base.DEC)
    local base_port = ProtoField.uint16("RCMB.port","Port",base.DEC)
    local base_type = ProtoField.uint16("RCMB.type","Type",base.DEC,
        { [0] = "PING", [1] = "PONG", [2] = "MEET", [3] = "FAIL", [4] = "PUBLISH",
         [5] = "FAILOVER_AUTH_REQUEST", [6] = "FAILOVER_AUTH_ACK", 
         [7] = "UPDATE", [8] = "MFSTART", [9] = "MODULE"})

    local base_count = ProtoField.uint16("RCMB.count","Count",base.DEC)
    local base_currentEpoch = ProtoField.uint64("RCMB.currentEpoch","CurrentEpoch",base.DEC)
    local base_configEpoch = ProtoField.uint64("RCMB.configEpoch","ConfigEpoch",base.DEC)
    local base_offset = ProtoField.uint64("RCMB.offset","Offset",base.DEC)

    local base_sender = ProtoField.string("RCMB.sender","Sender")
    local base_myslots = ProtoField.bytes("RCMB.myslots","Myslots")
    local base_slaveof = ProtoField.string("RCMB.slaveof","Slaveof")
    local base_myip = ProtoField.string("RCMB.myip","Myip")
    local base_notused1 = ProtoField.bytes("RCMB.notused1","Notused1")
    local base_cport = ProtoField.uint16("RCMB.cport","Cport",base.DEC)
    local base_flags = ProtoField.uint16("RCMB.flags","Flags",base.DEC)
    local base_state = ProtoField.uint8("RCMB.state","State",base.DEC,
        { [0] = "CLUSTER_OK", [1] = "CLUSTER_FAIL"})
    local base_mflags = ProtoField.bytes("RCMB.mflags","Mflags")

    -- ping消息字段
    local gossip_nodename = ProtoField.string("RCMB.gossip_nodename","Gossip_nodename")
    local gossip_ping_sent = ProtoField.uint32("RCMB.gossip_ping_sent","Gossip_ping_sent",base.DEC)
    local gossip_pong_received= ProtoField.uint32("RCMB.gossip_pong_received","Gossip_pong_received",base.DEC)
    local gossip_ip = ProtoField.string("RCMB.gossip_ip","Gossip_ip")
    local gossip_port = ProtoField.uint16("RCMB.gossip_port","Gossip_port",base.DEC)
    local gossip_cport = ProtoField.uint16("RCMB.gossip_cport","Gossip_cport",base.DEC)
    local gossip_flags = ProtoField.uint16("RCMB.gossip_flags","Gossip_flags",base.DEC)
    local gossip_notused1= ProtoField.uint32("RCMB.gossip_notused1","Gossip_notused1",base.DEC)

    -- fail消息字段
    local fail_nodename = ProtoField.string("RCMB.fail_nodename","Fail_nodename")

    -- publish消息字段
    local publish_channel_len= ProtoField.uint32("RCMB.publish_channel_len","Publish_channel_len",base.DEC)
    local publish_message_len= ProtoField.uint32("RCMB.publish_message_len","Publish_message_len",base.DEC)
    local publish_bulk_data = ProtoField.bytes("RCMB.publish_bulk_data","Publish_bulk_data")

    -- update消息字段
    local update_configEpoch= ProtoField.uint64("RCMB.update_configEpoch","Update_configEpoch",base.DEC)
    local update_nodename = ProtoField.string("RCMB.update_nodename","Update_nodename")
    local update_slots = ProtoField.bytes("RCMB.update_slots","Update_slots")

    -- module消息字段
    local module_id = ProtoField.uint64("RCMB.module_id","Module_id",base.DEC)
    local module_len = ProtoField.uint32("RCMB.module_len","Module_len",base.DEC)
    local module_type = ProtoField.uint8("RCMB.module_type","Module_type",base.DEC)
    local module_bulk_data = ProtoField.bytes("RCMB.module_bulk_data","Module_bulk_data")


    p_RCMB.fields = { base_sig, base_totlen, base_ver, base_port, base_type, base_count, base_currentEpoch, base_configEpoch, base_offset,
        base_sender, base_myslots, base_slaveof, base_myip, base_notused1, base_cport, base_flags, base_state, base_mflags, 
        gossip_nodename, gossip_ping_sent, gossip_pong_received, gossip_ip, gossip_port, gossip_cport, gossip_flags, gossip_notused1,
        fail_nodename,
        publish_channel_len, publish_message_len, publish_bulk_data,
        update_configEpoch, update_nodename, update_slots,
        module_id,module_len,module_type,module_bulk_data}

    local data_dis = Dissector.get("data")

    local function RCMB_dissector(buf,pkt,root)
        local buf_len = buf:len();
        -- 先确保至少有4个byte存放"RCmb"
        if buf_len < 4 then return false end

        if (buf(0,4):uint()~=1380150626)
            -- 1380150626为RCmc的uint32表示
            then return false end


        local t = root:add(p_RCMB,buf)
        pkt.cols.protocol = "RCMB"

        t:add(base_sig,buf(0,4))
        t:add(base_totlen,buf(4,4))
        t:add(base_ver,buf(8,2))
        t:add(base_port,buf(10,2))
        -- 记录type信息,用于后续根据type解析不同字段
        local i_type = buf(12,2):uint()
        t:add(base_type,buf(12,2))
        t:add(base_count,buf(14,2))

        t:add(base_currentEpoch,buf(16,8))
        t:add(base_configEpoch,buf(24,8))
        t:add(base_offset,buf(32,8))

        t:add(base_sender,buf(40,40))
        t:add(base_myslots,buf(80,2048))
        t:add(base_slaveof,buf(2128,40))

        t:add(base_myip,buf(2168,46))

        t:add(base_notused1,buf(2214,34))
        t:add(base_cport,buf(2248,2))
        t:add(base_flags,buf(2250,2))
        t:add(base_state,buf(2252,1))
        t:add(base_mflags,buf(2253,3))


        -- 按照消息类型分类型解析
        if (i_type == 0) then
            --把存在的字段逐个添加进去
            t:add(gossip_nodename,buf(2256,40))
            t:add(gossip_ping_sent,buf(2296,4))
            t:add(gossip_pong_received,buf(2300,4))
            t:add(gossip_ip,buf(2304,46))

            t:add(gossip_port,buf(2350,2))
            t:add(gossip_cport,buf(2352,2))
            t:add(gossip_flags,buf(2354,2))
            t:add(gossip_notused1,buf(2256,4))

        elseif (i_type == 3) then
            t:add(fail_nodename,buf(2256,40))

        elseif (i_type == 4) then
            t:add(publish_channel_len, buf(2256,4))
            t:add(publish_message_len, buf(2260,4))
            t:add(publish_bulk_data, buf(2264,8))

        elseif (i_type == 7) then
            t:add(update_configEpoch,buf(2256,8))
            t:add(update_nodename,buf(2264,40))
            t:add(update_slots,buf(2304,2048))

        elseif (i_type == 9) then
            t:add(module_id,buf(2256,8))
            t:add(module_len,buf(2264,4))
            t:add(module_type,buf(2268,2))
            t:add(module_bulk_data,buf(2270,3))
        end


        return true
    end

    function p_RCMB.dissector(buf,pkt,root) 
        if RCMB_dissector(buf,pkt,root) then
            --valid RCMB diagram
        else
            --data这个dissector几乎是必不可少的;当发现不是我的协议时,就应该调用data
            -- return 0;同样效果?
            data_dis:call(buf,pkt,root)
        end
    end

    local tcp_port_table = DissectorTable.get("tcp.port")
    --只需要处理UDP1127端口就可以了
    tcp_port_table:add(40001,p_RCMB)
    for i,port in ipairs{40002,40003, 40004, 40005, 40006} do
        tcp_port_table:add(port,p_RCMB)
    end
end

补充:

  1. 实现参考: https://www.jianshu.com/p/1e2f63a484d6
  2. TCP解析复杂处理https://wiki.wireshark.org/Lua/Examples?action=AttachFile&do=get&target=fpm.lua

附件列表

     

    posted @ 2020-10-05 12:24  lshs  阅读(514)  评论(0编辑  收藏  举报