【GoCN酷Go推荐】网络流量抓包库 gopacket介绍


当前第2页 返回上一页

在上述代码中,我们调用函数时,传入的LayerType协议层的类型为layers.LayerTypeTCP,函数返回值为interface类型,必须转换成TCP结构体

tcp, _ := tcpLayer.(*layers.TCP)

tcp是layers.TCP这个具体类型的指针,通过tcp则可以获取数据包中tcp协议的相关字段。

3.4 自定义层
自定义层有助于实现当前不包含在gopacket layers包中的协议。


import (
    "fmt"
    "github.com/google/gopacket"
)
// 创建自定义层数据结构,并实现Layer接口中的函数LayerType()、LayerContents()、LayerPayload()
type CustomLayer struct {
    // This layer just has two bytes at the front
    SomeByte    byte
    AnotherByte byte
    restOfData  []byte
}
// 注册自定义层类型,然后我们才可以使用它
// 第一个参数是ID. 自定义层使用大于2000的数字,它必须是唯一的
var CustomLayerType = gopacket.RegisterLayerType(
    2001,
    gopacket.LayerTypeMetadata{
        "CustomLayerType",
        gopacket.DecodeFunc(decodeCustomLayer),
    },
)

//自定义层实现LayerType
func (l CustomLayer) LayerType() gopacket.LayerType {
    return CustomLayerType
}

//自定义层实现LayerContents
func (l CustomLayer) LayerContents() []byte {
    return []byte{l.SomeByte, l.AnotherByte}
}

//自定义层实现LayerPayload
func (l CustomLayer) LayerPayload() []byte {
    return l.restOfData
}

//实现自定义的解码函数
func decodeCustomLayer(data []byte, p gopacket.PacketBuilder) error {
    p.AddLayer(&CustomLayer{data[0], data[1], data[2:]})
    return p.NextDecoder(gopacket.LayerTypePayload)
}
func main() {
    rawBytes := []byte{0xF0, 0x0F, 65, 65, 66, 67, 68}
    packet := gopacket.NewPacket(
        rawBytes,
        CustomLayerType,
        gopacket.Default,
    )
    fmt.Println("Created packet out of raw bytes.")
    fmt.Println(packet)
    // Decode the packet as our custom layer
    customLayer := packet.Layer(CustomLayerType)
    if customLayer != nil {
        fmt.Println("Packet was successfully decoded with custom layer decoder.")
        customLayerContent, _ := customLayer.(*CustomLayer)
        // Now we can access the elements of the custom struct
        fmt.Println("Payload: ", customLayerContent.LayerPayload())
        fmt.Println("SomeByte element:", customLayerContent.SomeByte)
        fmt.Println("AnotherByte element:", customLayerContent.AnotherByte)
    }
}

结合上述代码可知,实现自定义的层需要3步:

1、创建自定义层的结构体,并实现Layer接口中的函数LayerType()、LayerContents()、LayerPayload()

2、按照解码函数签名来实现自定义解码函数,名称可自行命名。

解码函数签名如下:

type DecodeFunc func([]byte, PacketBuilder) error

3、使用gopacket.RegisterLayerType函数来注册自定义层

3.5 TCP流重组
为什么需要tcp流重组?


package main

import (
 "bufio"
 "flag"
 "io"
 "log"
 "net/http"
 "time"

 "github.com/google/gopacket"
 "github.com/google/gopacket/examples/util"
 "github.com/google/gopacket/layers"
 "github.com/google/gopacket/pcap"
 "github.com/google/gopacket/tcpassembly"
 "github.com/google/gopacket/tcpassembly/tcpreader"
)

var iface = flag.String("i", "eth0", "Interface to get packets from")
var snaplen = flag.Int("s", 1600, "SnapLen for pcap packet capture")

// Build a simple HTTP request parser using tcpassembly.StreamFactory and tcpassembly.Stream interfaces

// httpStreamFactory implements tcpassembly.StreamFactory
type httpStreamFactory struct{}

// httpStream will handle the actual decoding of http requests.
type httpStream struct {
 net, transport gopacket.Flow
 r              tcpreader.ReaderStream
}

func (h *httpStreamFactory) New(net, transport gopacket.Flow) tcpassembly.Stream {
 hstream := &httpStream{
  net:       net,
  transport: transport,
  r:         tcpreader.NewReaderStream(),
 }
 go hstream.run() // Important... we must guarantee that data from the reader stream is read.

 // ReaderStream implements tcpassembly.Stream, so we can return a pointer to it.
 return &hstream.r
}

func (h *httpStream) run() {
 buf := bufio.NewReader(&h.r)
 for {
  req, err := http.ReadRequest(buf)
  if err == io.EOF {
   // We must read until we see an EOF... very important!
   return
  } else if err != nil {
   log.Println("Error reading stream", h.net, h.transport, ":", err)
  } else {
   bodyBytes := tcpreader.DiscardBytesToEOF(req.Body)
   req.Body.Close()
   log.Println("Received request from stream", h.net, h.transport, ":", req, "with", bodyBytes, "bytes in request body")
  }
 }
}

func main() {
 defer util.Run()()
 var handle *pcap.Handle
 var err error

 // Set up pcap packet capture
 handle, err = pcap.OpenLive(*iface, int32(*snaplen), true, pcap.BlockForever)
 if err != nil {
  log.Fatal(err)
 }

 // Set up assembly
 streamFactory := &httpStreamFactory{}
 streamPool := tcpassembly.NewStreamPool(streamFactory)
 assembler := tcpassembly.NewAssembler(streamPool)

 // Read in packets, pass to assembler.
 packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
 packets := packetSource.Packets()
 ticker := time.Tick(time.Minute)
 for {
  select {
  case packet := <-packets:
   if packet.NetworkLayer() == nil || packet.TransportLayer() == nil || packet.TransportLayer().LayerType() != layers.LayerTypeTCP {
    log.Println("Unusable packet")
    continue
   }
   tcp := packet.TransportLayer().(*layers.TCP)
   //将数据包进行重组
   assembler.AssembleWithTimestamp(packet.NetworkLayer().NetworkFlow(), tcp, packet.Metadata().Timestamp)

  case <-ticker:
   //每隔一分钟,刷新之前两分钟内不活动的连接
   assembler.FlushOlderThan(time.Now().Add(time.Minute * -2))
  }
 }
}

基本步骤如下:

1、创建httpStreamFactory结构体,实现tcpassembly.StreamFactory接口

2、创建连接池

streamPool := tcpassembly.NewStreamPool(streamFactory)

3、创建重组器

assembler := tcpassembly.NewAssembler(streamPool)

4、将数据包添加到重组器中

assembler.AssembleWithTimestamp(packet.NetworkLayer().NetworkFlow(), tcp, packet.Metadata().Timestamp)

总结

首先,gopacket库是google大厂背书,从使用文档、质量、社区活跃度来说都很不错

其次,使用方式简单,扩展性好。gopacket提供了自定义的接口,可根据自身需要进行定制化开发

最后,gopacket定义的layers齐全,如果是实时捕获数据后进行协议解析,采用其内置的layer即可,无需自己手动去解析繁杂的协议了。


本文来自:51CTO博客

感谢作者:mob604756f0bbf4

查看原文:【GoCN酷Go推荐】网络流量抓包库 gopacket介绍

返回前面的内容

相关阅读 >>

leetcode 1639 -通过给定词典构造目标字符串的方案数

手撸Golang 架构设计原则 接口隔离原则

09 Golang sort排序

Golang依赖注入工具wire指南

Golang怎么解析json格式

Golang基础-高级特性概述

ubuntu下安装Golang

Golang 读写锁 sync.rwmutex

Go实现字符串的逆序

手撸Golang 基本数据结构与算法 网页排名/pagerank,随机游走

更多相关阅读请进入《Go》频道 >>




打赏

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

分享从这里开始,精彩与您同在

评论

管理员已关闭评论功能...