• 开源网络抓包与分析框架学习-Packetbeat篇


    开源简介
    packbeat是一个开源的实时网络抓包与分析框架,内置了很多常见的协议捕获及解析,如HTTP、MySQL、Redis等。在实际使用中,通常和Elasticsearch以及kibana联合使用,用于数据搜索和分析以及数据展示。
    开发环境:Go语言
    Git:源码管理
    IDE:推荐sublime或者liteide
    开发之前
    1.packbeat已经被elastic整合在beats项目中,使用前登录github,并打开
    https://github.com/elasticsearch/beats.fork到自己的仓库。

    如:https://github.com/lindsay-show/packbeat

    2.创建相应目录

    [html] view plain copy
     
    1. mkdir -p $GOPATH/src/github.com/elastic  
    2. cd $GOPATH/src/github.com/elastic  

    3.git clone

    [html] view plain copy
     
    1. git clone https://github.com/elasitc/beats.git  
    2. cd beats  

    4.修改官方库为upstream源,设置自己的仓库为orgin源

    [html] view plain copy
     
    1. git remote rename origin upstream  
    2. git remote add origin git@github.com:lindsay-show/packbeat.git  

    5.获取最新代码(刚fork,可忽略),并创建分支用于自定义功能开发

    [html] view plain copy
     
    1. git pull upstream master  
    2. git checkout -b mypackbeat  

    6.切换到packbeat,并获取依赖信息

    [html] view plain copy
     
    1. cd packbeat  
    2. mkdir -p $GOPATH/src/golang.org/x/  
    3. cd $GOPATH/src/golang.org/x   
    4. git clone https://github.com/golang/tools.git  
    5. go get github.com/tools/godep  

    7.使用make编译packbeat源码,得到packbeat可执行文件
    注:
    [1] git的相关介绍和命令可参考 Git教程(http://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000)
    [2] go安装及环境变量配置可参考 Golang官网(https://golang.org/doc/install)

    源码框架
    packbeat项目源码结构如下:

    packetbeat整合在beats项目中,其中还包括topbeat以及filebeat,现简要介绍beats源码框架内容如下:

    • /libbeat:公共依赖库
    • /filebeat:logstash升级版,处理日志类型数据
    • /packbeat:网络抓包
    • /topbeat:监控系统性能;
    • /vendor:依赖的第三方库(如dns开源库或者其他协议栈)
    • /tests:用于测试的pcamp抓包文件
    • /scripts:测试脚本

    关于topbeat及filebeat的更多介绍参考elastic官网(https://www.elastic.co/)。

    packebeat源码框架介绍如下:

    • /packetbeat/main.go:项目启动入口;
    • /packetbeat/config/:config.go,定义了所有配置相关的struct结构体
    • /packetbeat/debian/:打包相关
    • /packetbeat/docs/:文档
    • /packetbeat/etc/:配置文件示例
    • /packetbeat/procs/:获取系统内核运作状态与进程信息的工具类
    • /packetbeat/protos/:自定义协议类,每个子目录对应一个应用协议,包含配置相关的结构体及具体实现
    • /packetbeat/sniffer/:三种不同抓包方式的实现,如pcap、af_packet及pf_ring
    • /packetbeat/tests/:测试相关的文件,包含协议pcap文件及python测试脚本

    注:以上介绍针对packetbeat-1.2.1,区别官网的开发帮助文档(官网未更新)。

    工作原理

    介绍了beats及packetbeat源码结构,简要说明一下packetbeat的工作原理:

    每一个协议都有一个或者多个固定的端口用于通信,开发者要做的事情就是定义协议端口,然后按照TCP以及UDP实现对应的接口,Packetbeat会捕获到指定端口的数据包,然后交给开发者定义的方法来解析,如TCP对应的是Parse,UDP是ParseUdp.解析出来的结构化数据封装成Json,插入到Elasticsearch中,后续便可使用Elasticsearch的搜索和数据统计能力进行应用层数据分析。

    使用方法

    了解Packetbeat的工作原理后,接下来介绍如何使用packetbeat进行网络数据包捕获及分析。

    在上述介绍中,我们知道packetbeat/protos目录下支持自定义协议,目前Packebeat支持的协议如下:

    • ICMP (v4 and v6)
    • DNS
    • HTTP
    • Mysql
    • PostgreSQL
    • Redis
    • Thrift-RPC
    • MongoDB
    • Memcache

    以HTTP为例,安装packetbeat源码后,配置文件packetbeat.yml中默认已经配置了上述支持的协议类型。使用步骤简述如下:

    • 安装Packetbeat源码
    • 配置packetbeat.yml文件,默认不用更改(默认配置输出到elasticsearch)
    • 加载packetbeat索引至elasticsearch中(使用第三方脚本)
    • 启动elasticsearch及kibana,查看http数据包捕获及分析

    如:启动packetbeat,打开几个网页,在终端极即可看到packetbeat已注册的协议类型以及http请求数据和应答数据包

    [html] view plain copy
     
    1. cd packetbeat-1.2.1  
    2. ./packetbeat -N -e  


    注:
    [1] Packetbeat详细使用说明,请参考Packetbeat官方帮助文档(https://www.elastic.co/guide/en/beats/packetbeat/current/_step_4_starting_packetbeat.html),非常详细。
    [2] Elasticsearch及kibana的安装和使用,请参考Elastic官方帮助文档(https://www.elastic.co/guide/index.html)。

    扩展协议开发
    在前面介绍到,目前packetbeat支持的协议类型主要是HTTP等常见协议类型,即时通信协议,如sip、msrp以及xmpp等暂不支持。如何对packetbeat进行协议扩展是我们研究该源码的主要目的。
    网络传输两大协议TCP和UDP,应用层协议都离不开这两种协议,如源码中的HTTP、MySQL走的是TCP传输协议,DNS走的是UDP协议,在Packetbeat里面,要实现自定义协议,只需实现这两者对应的接口。扩展协议的框架代码可分别参考基于TCP的http以及基于udp的dns协议实现代码。


    在进行扩展协议开发之前,需要了解protos/register.Go中tcp、udp以及基础协议的接口定义:

    • TcpPlugin:TCP协议插件的接口定义。其中Pares()用于解析Packet,ReceivedFin()用于处理TCP断开连接,GapInStream()处理空包丢包,ConnectionTimeout()处理超时时间
    • UdpPlugin:UDP协议插件的接口定义。其中ParseUdp()用于解析Packet
    • ProtocolPlugin:TCP和UDP以及其他扩展协议均需要实现ProtocolPlugin的基础接口,主要是提供获取端口方法

    上述对应的接口定义如下所示:

    [cpp] view plain copy
     
    1. type Plugin interface {  
    2. // Called to return the configured ports  
    3. GetPorts() []int  
    4. }  
    5. type TcpPlugin interface {  
    6. Plugin  
    7. // Called when TCP payload data is available for parsing.  
    8. Parse(pkt *Packet, tcptuple *common.TcpTuple,  
    9.     dir uint8, private ProtocolData) ProtocolData  
    10. // Called when the FIN flag is seen in the TCP stream.  
    11. ReceivedFin(tcptuple *common.TcpTuple, dir uint8,    private ProtocolData) ProtocolData  
    12.    // Called when a packets are missing from the tcp    // stream.  
    13. GapInStream(tcptuple *common.TcpTuple, dir uint8, nbytes int,  
    14.     private ProtocolData) (priv ProtocolData, drop bool)  
    15. // ConnectionTimeout returns the per stream connection timeout.  
    16. // Return <=0 to set default tcp module transaction timeout.  
    17. ConnectionTimeout() time.Duration  
    18. }  
    19. type UdpPlugin interface {  
    20. Plugin  
    21.   
    22. // ParseUdp is invoked when UDP payload data is available for parsing.  
    23.   
    24. ParseUdp(pkt *Packet)  
    25. }  


    接下来,需要了解config.go中ProtocolCommon的结构体,扩展协议需要继承该基本结构.

    协议的基本配置结构体定义如下所示(该结构体对应packetbeat.yml的配置结构,参考默认的packetbeat.yml文件):

    [html] view plain copy
     
    1. type ProtocolCommon struct {  
    2. Ports              []int         `config:"ports"`  
    3. SendRequest        bool          `config:"send_request"`  
    4. SendResponse       bool          `config:"send_response"`  
    5. TransactionTimeout time.Duration `config:"transaction_timeout"`  
    6. }  


    最后了解一下packetbeat中的关于packet结构定义:

    [cpp] view plain copy
     
    1. type Packet struct {  
    2. Ts      time.Time  
    3. Tuple   common.IpPortTuple  
    4. Payload []byte  
    5. }  
    • Ts:收到数据包的时间戳
    • Tuple:来源ip+来源端口+目的ip+目的端口的元组
    • Payload:应用层字节数,不包括tcp及udp头部信息,这部分正是七层协议需要解析的部分

    以上,扩展协议的基本思路介绍完毕。现以sip协议扩展开发为例:(扩展开发之前,参考前文开发之前所述)

    [html] view plain copy
     
    1. cd $GOPATH/src/github.com/elastic/beats/packetbeat/protos  
    2. mkdir sip&&cd sip  
    3. touch sip.go config.go sip_parse.go  


    其中,sip.go用于sip协议的具体实现,包括实现基于TCP及UDP对应的解析方法,config.go用于sip协议的配置结构定义,sip_parse.go用于sip消息解析结构的定义。
    config.go中定义如下:

    [cpp] view plain copy
     
    1. package sip  
    2. import (  
    3.   
    4. "github.com/elastic/beats/packetbeat/config"  
    5.   
    6. "github.com/elastic/beats/packetbeat/protos"  
    7. )  
    8. //ProtocolCommon struct  
    9. type sipConfig struct {  
    10. config.ProtocolCommon ``config:",inline"  
    11. }  
    12. var (    defaultConfig = sipConfig{  
    13.     ProtocolCommon: config.ProtocolCommon{  
    14.   
    15.         TransactionTimeout: protos.DefaultTransactionExpiration,  
    16.     },  
    17. }  
    18. )  


    sip下的config.go定义完毕后,在packetbeat.yml中增加sip对应的配置,如下所示:

    [html] view plain copy
     
    1. protocols:  sip:    ports: [5060,5260]  
    2.   
    3. # send_request and send_response control whether or not the stringified SIP  
    4. # request and response message are added to the result.  
    5. # Nearly all data about the request/response is available in the sip.*  
    6. # fields, but this can be useful if you need visibility specifically  
    7. # into the request or the response.  
    8. # Default: false  
    9. # send_request:  true  
    10. # send_response: true  


    在sip.go中实现udp协议插件接口方法Parseudp,并注册协议,使用registor.go中的register方法,如下:

    [cpp] view plain copy
     
    1. func init() {  
    2.   
    3. protos.Register("sip", New)  
    4. }  
    5. func New(    testMode bool,  
    6. results publish.Transactions,  
    7.   
    8. cfg *common.Config,  
    9. ) (protos.Plugin, error) {  
    10. p := &Sip{}  
    11. config := defaultConfig  
    12. if !testMode {  
    13.     if err := cfg.Unpack(&config); err != nil {  
    14.         return nil, err  
    15.     }  
    16. }  
    17. if err := p.init(results, &config); err != nil {  
    18.     return nil, err  
    19. }  
    20. return p, nil  
    21. }   


    最后一步,在packetbeat的main.go主程序中加载sip协议,如下所示:

    [cpp] view plain copy
     
    1. package main  
    2. import (  
    3.   
    4. "os"  
    5. "github.com/elastic/beats/libbeat/beat"  
    6. "github.com/elastic/beats/packetbeat/beater"  
    7. // import support protocol modules  
    8. "github.com/elastic/beats/packetbeat/protos/amqp"  
    9. "github.com/elastic/beats/packetbeat/protos/dns"  
    10. "github.com/elastic/beats/packetbeat/protos/http"  
    11. "github.com/elastic/beats/packetbeat/protos/memcache"  
    12. "github.com/elastic/beats/packetbeat/protos/mongodb"  
    13. "github.com/elastic/beats/packetbeat/protos/mysql"  
    14. "github.com/elastic/beats/packetbeat/protos/nfs"  
    15. "github.com/elastic/beats/packetbeat/protos/pgsql"  
    16. "github.com/elastic/beats/packetbeat/protos/redis"  
    17. "github.com/elastic/beats/packetbeat/protos/sip"  
    18. "github.com/elastic/beats/packetbeat/protos/thrift"  
    19. )  


    使用makefile,编译packeteat,执行./packetbeat -N -e后,在终端上会显示sip协议已注册成功。


    至此,packetbeat的协议扩展介绍完毕了。

  • 相关阅读:
    对象进行比较
    java中length,length(),size()区别
    引用数据类型、自定义类
    java方法
    学员信息管理系统案例
    商品库存管理查看增减
    引用数据类型Scanner,Random
    Cantor表巧妙的做法
    UVA 11292 The dragon of Loowater勇士斗恶龙 11729 突击战 Commando War
    期末,祝不挂
  • 原文地址:https://www.cnblogs.com/beautiful-code/p/6625507.html
Copyright © 2020-2023  润新知