• gopacket源码分析


    gopacket源码分析

    package main
    
    import (
    	"fmt"
    	"log"
    	"time"
    
    	"github.com/google/gopacket"
    	"github.com/google/gopacket/pcap"
    )
    
    var (
    	device       string = " \\Device\\NPF_{C410B1B0-56DE-4CD5-BC7A-5A5ACAB7619F}\n"
    	snapshot_len int32  = 65536
    	promiscuous  bool   = true
    	err          error
    	timeout      time.Duration = -1 * time.Second
    	handle       *pcap.Handle
    )
    
    func main() {
    	// 打开设备进行实时捕获
    	handle, err = pcap.OpenLive(device, snapshot_len, promiscuous, timeout)
    	if err != nil {
    		log.Fatal(err)
    	}
    	defer handle.Close()
    	// 构造一个数据包源
    	packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
    	// 读取包
    	for packet := range packetSource.Packets() {
    		// 打印包
    		fmt.Println(packet)
    	}
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    概览

    NewPacketSource

    NewPacketSource 其参数需要两个接口 PacketDataSource 和 Decoder,PacketDataSource 为数据包,一般我们使用 handle,Decoder 为解码器,一般我们都使用 hanler.LinkType 来传递

    func NewPacketSource(source PacketDataSource, decoder Decoder) *PacketSource {
    	return &PacketSource{
    		source:  source,
    		decoder: decoder,
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    handle.LinkType

    handle.LinkType()是源的链路层类型,一般是 LinkType 对应枚举 LinkTypeEthernet

    func (p *Handle) LinkType() layers.LinkType {
    	return p.pcapDatalink()
    }
    func (p *Handle) pcapDatalink() layers.LinkType {
    	ret, _, _ := syscall.Syscall(pcapDatalinkPtr, 1, uintptr(p.cptr), 0, 0)
    	return layers.LinkType(ret)
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    LinkType

    type LinkType uint8
    func (a LinkType) Decode(data []byte, p gopacket.PacketBuilder) error {
    	return LinkTypeMetadata[a].DecodeWith.Decode(data, p)
    }
    
    • 1
    • 2
    • 3
    • 4

    那么 LinkTypeMetadata 数据是从那里呢?我们可以借助 IDE 的全局查找功能进行查找

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WESNx1yE-1659280486701)(assets/image-20220729111733-0mflbru.png)]

    查找后我们可以在 layers.enums.go 大约 368 行看到 LinkTypeEthernet 的注册信息

    LinkTypeMetadata[LinkTypeEthernet] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEthernet), Name: "Ethernet"}
    
    
    • 1
    • 2

    然后反推其调用链为 init->initActualTypeData

    func init() {
    	initUnknownTypesForLinkType()
    	initUnknownTypesForEthernetType()
    	initUnknownTypesForPPPType()
    	initUnknownTypesForIPProtocol()
    	initUnknownTypesForSCTPChunkType()
    	initUnknownTypesForPPPoECode()
    	initUnknownTypesForFDDIFrameControl()
    	initUnknownTypesForEAPOLType()
    	initUnknownTypesForProtocolFamily()
    	initUnknownTypesForDot11Type()
    	initUnknownTypesForUSBTransportType()
    	initActualTypeData()
    }
    
    func initActualTypeData() {
    	...
    	LinkTypeMetadata[LinkTypeEthernet] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEthernet), Name: "Ethernet"}
    	...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    decodeEthernet

    func decodeEthernet(data []byte, p gopacket.PacketBuilder) error {
    	eth := &Ethernet{}
    	err := eth.DecodeFromBytes(data, p)
    	if err != nil {
    		return err
    	}
    	p.AddLayer(eth)
    	p.SetLinkLayer(eth)
    	return p.NextDecoder(eth.EthernetType)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    然后我们可以看到 ether 的解析过程

    func (eth *Ethernet) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
    	if len(data) < 14 {
    		return errors.New("Ethernet packet too small")
    	}
    	eth.DstMAC = net.HardwareAddr(data[0:6])
    	eth.SrcMAC = net.HardwareAddr(data[6:12])
    	eth.EthernetType = EthernetType(binary.BigEndian.Uint16(data[12:14]))
    	eth.BaseLayer = BaseLayer{data[:14], data[14:]}
    	eth.Length = 0
    	if eth.EthernetType < 0x0600 {
    		eth.Length = uint16(eth.EthernetType)
    		eth.EthernetType = EthernetTypeLLC
    		if cmp := len(eth.Payload) - int(eth.Length); cmp < 0 {
    			df.SetTruncated()
    		} else if cmp > 0 {
    			// Strip off bytes at the end, since we have too many bytes
    			eth.Payload = eth.Payload[:len(eth.Payload)-cmp]
    		}
    		//	fmt.Println(eth)
    	}
    	return nil
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    NextPacket

    func (p *PacketSource) NextPacket() (Packet, error) {
    	data, ci, err := p.source.ReadPacketData()
    	if err != nil {
    		return nil, err
    	}
    	packet := NewPacket(data, p.decoder, p.DecodeOptions)
    	m := packet.Metadata()
    	m.CaptureInfo = ci
    	m.Truncated = m.Truncated || ci.CaptureLength < ci.Length
    	return packet, nil
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    NewPacket

    func NewPacket(data []byte, firstLayerDecoder Decoder, options DecodeOptions) Packet {
    	if !options.NoCopy {
    		dataCopy := make([]byte, len(data))
    		copy(dataCopy, data)
    		data = dataCopy
    	}
    	if options.Lazy {
    		p := &lazyPacket{
    			packet: packet{data: data, decodeOptions: options},
    			next:   firstLayerDecoder,
    		}
    		p.layers = p.initialLayers[:0]
    		// Crazy craziness:
    		// If the following return statemet is REMOVED, and Lazy is FALSE, then
    		// eager packet processing becomes 17% FASTER.  No, there is no logical
    		// explanation for this.  However, it's such a hacky micro-optimization that
    		// we really can't rely on it.  It appears to have to do with the size the
    		// compiler guesses for this function's stack space, since one symptom is
    		// that with the return statement in place, we more than double calls to
    		// runtime.morestack/runtime.lessstack.  We'll hope the compiler gets better
    		// over time and we get this optimization for free.  Until then, we'll have
    		// to live with slower packet processing.
    		return p
    	}
    	p := &eagerPacket{
    		packet: packet{data: data, decodeOptions: options},
    	}
    	p.layers = p.initialLayers[:0]
    	p.initialDecode(firstLayerDecoder)
    	return p
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    initialDecode

    func (p *eagerPacket) initialDecode(dec Decoder) {
    	defer p.recoverDecodeError()
    	err := dec.Decode(p.data, p)
    	if err != nil {
    		p.addFinalDecodeError(err, nil)
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    NextDecoder

    func (p *eagerPacket) NextDecoder(next Decoder) error {
    	if next == nil {
    		return errNilDecoder
    	}
    	if p.last == nil {
    		return errors.New("NextDecoder called, but no layers added yet")
    	}
    	d := p.last.LayerPayload()
    	if len(d) == 0 {
    		return nil
    	}
    	// Since we're eager, immediately call the next decoder.
    	return next.Decode(d, p)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    推荐一个零声学院免费公开课程,个人觉得老师讲得不错,分享给大家:Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK等技术内容,点击立即学习

  • 相关阅读:
    Django——模板应用
    springboot和vue:十一、Axios网络请求的安装引入与使用、跨域问题解决(CORS)
    部署LVS-DR+Keepalived高可用群集构建
    史上无敌的超级详细的Node Js 环境搭建步骤
    网络编程。。
    JAVA算法练习(13):最小路径和
    Re14:读论文 ILLSI Interpretable Low-Resource Legal Decision Making
    【Java中23种面试常考的设计模式之建造者模式(Builder)---创建型模式】
    qt 6知识集
    PY32F003F18点灯
  • 原文地址:https://blog.csdn.net/hzb869168467/article/details/126092288