gopacket:对IP-in-IP数据包进行解码层处理。

huangapple go评论76阅读模式
英文:

gopacket: DecodeLayers on an IP-in-IP Packet

问题

如果我有一些数据通过DecodingLayerParser进行处理,并且其中一些数据可能包含IP协议4(IP-in-IP)数据包,我该如何捕获两个IPv4头部的信息?我似乎只能捕获其中一个。

你可以通过修改Decoder结构体中的代码来实现。具体来说,你可以将ipip4字段更改为一个切片类型,以便存储多个IPv4头部的信息。以下是修改后的代码示例:

type Decoder struct {
	eth    layers.Ethernet
	ip4    []layers.IPv4
	ip6    layers.IPv6
	icmp4  layers.ICMPv4
	icmp6  layers.ICMPv6
	tcp    layers.TCP
	udp    layers.UDP
	pay    gopacket.Payload
	parser *gopacket.DecodingLayerParser
	types  []gopacket.LayerType
	unknowns map[string]uint
}

// NewDecoder 分配并初始化一个新的Decoder。
func NewDecoder() *Decoder {
	d := new(Decoder)
	d.parser = gopacket.NewDecodingLayerParser(layers.LayerTypeEthernet,
		&d.eth, &d.ip4, &d.ip6, &d.icmp4, &d.icmp6, &d.tcp, &d.udp, &d.pay)
	d.types = make([]gopacket.LayerType, 8, 8)
	d.parser.IgnoreUnsupported = true
	d.unknowns = make(map[string]uint)
	return d
}

在修改后的代码中,ip4字段被更改为一个[]layers.IPv4切片类型,以便存储多个IPv4头部的信息。当调用DecodeLayers方法时,它将会将所有的IPv4头部信息存储在ip4切片中,而不仅仅是第二个IPv4头部的信息。

英文:

If I have data I'm putting through a DecodingLayerParser and some of that data could have IP Protocol 4 (IP-in-IP) packets included, how would I get it to capture BOTH IPv4 headers in the packet? I can only seem to get it to capture one of them.

type Decoder struct {
	eth   layers.Ethernet
	ip4   layers.IPv4
	ipip4 layers.IPv4
	ip6   layers.IPv6
	icmp4 layers.ICMPv4
	icmp6 layers.ICMPv6
	tcp   layers.TCP
	udp   layers.UDP
	//sip      layers.SIP
	//dns      layers.DNS
	//ntp      layers.NTP
	pay      gopacket.Payload
	parser   *gopacket.DecodingLayerParser
	types    []gopacket.LayerType
	unknowns map[string]uint
}

// NewDecoder allocates and initialises a new Decoder.
func NewDecoder() *Decoder {
	d := new(Decoder)
	d.parser = gopacket.NewDecodingLayerParser(layers.LayerTypeEthernet,
		&d.eth, &d.ip4, &d.ipip4, &d.ip6, &d.icmp4, &d.icmp6, &d.tcp, &d.udp, &d.pay)
	//&d.sip, &d.dns, &d.ntp, &d.pay)
	d.types = make([]gopacket.LayerType, 10, 10)
	d.parser.IgnoreUnsupported = true
	d.unknowns = make(map[string]uint)
	return d
}

How may I modify this in order to do this when DecodeLayers is called from the parser? It only seems to store the second IPv4 header's information in ipip4.

答案1

得分: 0

为什么它不起作用

接口DecodingLayerContainer的设计是通过其LayerType索引DecodingLayer(参见Decoder(LayerType) (DecodingLayer, bool))。由于ip4ipip4具有相同的LayerType(layers.LayerTypeIPv4),后者将覆盖前者在容器中的位置。每次DecodingLayerParser从容器中获取layers.LayerTypeIPv4的解码器时,它都会获取ipip4。因此,ipip4的状态会一次又一次地被改变。

解决方法

一个解决方法是给ip4ipip4分配不同的LayerType。并且当数据包是IP in IP数据包时,让ip4选择ipip4作为其下一个解码器。以下是示例代码:

package main

import (
	"fmt"
	"io"

	"github.com/google/gopacket"
	"github.com/google/gopacket/layers"
	"github.com/google/gopacket/pcap"
)

// 0-999 are reserved for the gopacket library. Numbers 1000-1999 should be
// used for common application-specific types.
var LayerTypeIPv4Inner = gopacket.RegisterLayerType(1000, gopacket.LayerTypeMetadata{Name: "IP_in_IP", Decoder: nil})

// IPv4Outer is like layers.IPv4 but it recognizes IP in IP and will choose
// its next decoder accordingly.
type IPv4Outer struct {
	layers.IPv4
}

// NextLayerType overrides (*layers.IPv4).NextLayerType to recognize IP in IP.
func (i *IPv4Outer) NextLayerType() gopacket.LayerType {
	if i.Flags&layers.IPv4MoreFragments != 0 || i.FragOffset != 0 {
		return gopacket.LayerTypeFragment
	}
	// This is an IP in IP packet.
	// See https://datatracker.ietf.org/doc/html/rfc2003#section-3.1
	if i.Protocol == 4 {
		return LayerTypeIPv4Inner
	}
	return i.Protocol.LayerType()
}

// IPv4Inner is like layers.IPv4 except that its type is LayerTypeIPv4Inner.
// gopacket.DecodingLayerParser finds next decoder based on this type.
type IPv4Inner struct {
	layers.IPv4
}

// CanDecode overrides (*layers.IPv4).CanDecode to choose a type other than
// layers.LayerTypeIPv4.
func (i *IPv4Inner) CanDecode() gopacket.LayerClass {
	return LayerTypeIPv4Inner
}

func main() {
	handle, err := pcap.OpenOffline("./IP_in_IP.cap")
	if err != nil {
		panic(err)
	}

	var (
		eth     layers.Ethernet
		ip4     IPv4Outer
		ipip4   IPv4Inner
		tcp     layers.TCP
		icmpv4  layers.ICMPv4
		payload gopacket.Payload
	)
	parser := gopacket.NewDecodingLayerParser(layers.LayerTypeEthernet, &eth, &ip4, &ipip4, &tcp, &icmpv4, &payload)
	decodedLayers := make([]gopacket.LayerType, 0, 10)
	for {
		data, _, err := handle.ZeroCopyReadPacketData()
		if err == io.EOF {
			fmt.Println("done")
			return
		}

		if err != nil {
			panic(err)
		}

		err = parser.DecodeLayers(data, &decodedLayers)
		if err != nil {
			panic(err)
		}

		for _, typ := range decodedLayers {
			if typ == layers.LayerTypeIPv4 {
				fmt.Printf("Ipv4: %s => %s\n", ip4.SrcIP, ip4.DstIP)
			} else if typ == LayerTypeIPv4Inner {
				fmt.Printf("IP in IP: %s => %s\n", ipip4.SrcIP, ipip4.DstIP)
			}
		}

		fmt.Printf("%v\n\n", decodedLayers)
	}
}

我已经使用https://packetlife.net/media/captures/IP_in_IP.cap和gopacket中的test_ethernet.pcap进行了测试。输出如下:

Ipv4: 10.0.0.1 => 10.0.0.2
IP in IP: 1.1.1.1 => 2.2.2.2
[Ethernet IPv4 IP_in_IP ICMPv4 Payload]
Ipv4: 10.0.0.2 => 10.0.0.1
IP in IP: 2.2.2.2 => 1.1.1.1
[Ethernet IPv4 IP_in_IP ICMPv4 Payload]
Ipv4: 10.1.1.2 => 10.1.1.1
[Ethernet IPv4 TCP]
Ipv4: 10.1.1.1 => 10.1.1.2
[Ethernet IPv4 TCP]

注意事项

  1. DecodingLayerParser速度更快,但也更加严格。而PacketSource速度较慢,但可以安全且轻松地处理任何已知类型的数据包(我相信您已经知道这一点)。
  2. 这只是一个解决方法,我不确定这是否是最佳实践。
  3. 这个示例的目的是说明一种解决此类问题的方法。它无法正确解析具有多个IPv4头的其他情况。例如,它无法正确解析此文件:https://packetlife.net/media/captures/GRE.cap。我们知道DecodingLayerParser是严格的,我们必须自己处理其他边缘情况。
英文:

Why it does not work

The interface DecodingLayerContainer is designed to index DecodingLayer by its LayerType (See Decoder(LayerType) (DecodingLayer, bool)). Since ip4 and ipip4 have the same LayerType (layers.LayerTypeIPv4), the latter will overwrite the former in the container. And every time DecodingLayerParser gets a decoder from the container for layers.LayerTypeIPv4, it gets ipip4. So the state of ipip4 will be changed again and again.

A workaround

A workaround is to give ip4 and ipip4 different LayerType. And make ip4 choose ipip4 as its next decoder when the packet is an IP in IP packet. Here comes the demo:

package main

import (
	"fmt"
	"io"

	"github.com/google/gopacket"
	"github.com/google/gopacket/layers"
	"github.com/google/gopacket/pcap"
)

// 0-999 are reserved for the gopacket library. Numbers 1000-1999 should be
// used for common application-specific types.
var LayerTypeIPv4Inner = gopacket.RegisterLayerType(1000, gopacket.LayerTypeMetadata{Name: "IP_in_IP", Decoder: nil})

// IPv4Outer is like layers.IPv4 but it recognizes IP in IP and will choose
// its next decoder accordingly.
type IPv4Outer struct {
	layers.IPv4
}

// NextLayerType overrides (*layers.IPv4).NextLayerType to recognize IP in IP.
func (i *IPv4Outer) NextLayerType() gopacket.LayerType {
	if i.Flags&layers.IPv4MoreFragments != 0 || i.FragOffset != 0 {
		return gopacket.LayerTypeFragment
	}
	// This is an IP in IP packet.
	// See https://datatracker.ietf.org/doc/html/rfc2003#section-3.1
	if i.Protocol == 4 {
		return LayerTypeIPv4Inner
	}
	return i.Protocol.LayerType()
}

// IPv4Inner is like layers.IPv4 except that its type is LayerTypeIPv4Inner.
// gopacket.DecodingLayerParser finds next decoder based on this type.
type IPv4Inner struct {
	layers.IPv4
}

// CanDecode overrides (*layers.IPv4).CanDecode to choose a type other than
// layers.LayerTypeIPv4.
func (i *IPv4Inner) CanDecode() gopacket.LayerClass {
	return LayerTypeIPv4Inner
}

func main() {
	handle, err := pcap.OpenOffline("./IP_in_IP.cap")
	if err != nil {
		panic(err)
	}

	var (
		eth     layers.Ethernet
		ip4     IPv4Outer
		ipip4   IPv4Inner
		tcp     layers.TCP
		icmpv4  layers.ICMPv4
		payload gopacket.Payload
	)
	parser := gopacket.NewDecodingLayerParser(layers.LayerTypeEthernet, &eth, &ip4, &ipip4, &tcp, &icmpv4, &payload)
	decodedLayers := make([]gopacket.LayerType, 0, 10)
	for {
		data, _, err := handle.ZeroCopyReadPacketData()
		if err == io.EOF {
			fmt.Println("done")
			return
		}

		if err != nil {
			panic(err)
		}

		err = parser.DecodeLayers(data, &decodedLayers)
		if err != nil {
			panic(err)
		}

		for _, typ := range decodedLayers {
			if typ == layers.LayerTypeIPv4 {
				fmt.Printf("Ipv4: %s => %s\n", ip4.SrcIP, ip4.DstIP)
			} else if typ == LayerTypeIPv4Inner {
				fmt.Printf("IP in IP: %s => %s\n", ipip4.SrcIP, ipip4.DstIP)
			}
		}

		fmt.Printf("%v\n\n", decodedLayers)
	}
}

I have tested with https://packetlife.net/media/captures/IP_in_IP.cap and test_ethernet.pcap in gopacket. The output looks like this:

Ipv4: 10.0.0.1 => 10.0.0.2
IP in IP: 1.1.1.1 => 2.2.2.2
[Ethernet IPv4 IP_in_IP ICMPv4 Payload]
Ipv4: 10.0.0.2 => 10.0.0.1
IP in IP: 2.2.2.2 => 1.1.1.1
[Ethernet IPv4 IP_in_IP ICMPv4 Payload]
Ipv4: 10.1.1.2 => 10.1.1.1
[Ethernet IPv4 TCP]
Ipv4: 10.1.1.1 => 10.1.1.2
[Ethernet IPv4 TCP]

Notes

  1. DecodingLayerParser is faster, but it is also more rigid. while PacketSource is slower, it will handle any known type of packet safely and easily. (I'm sure you already know this).
  2. This is only a workaround. I'm not sure if this is a best practice.
  3. The purpose of the demo is to illustrate an idea to workaround such kind of issue. It does not work for other cases that a packet has multiple IPv4 headers. For example, it can not parse this file correctly: https://packetlife.net/media/captures/GRE.cap. We know that DecodingLayerParser is rigid and we have to handle other corner cases ourself.

huangapple
  • 本文由 发表于 2023年4月8日 02:24:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/75961066.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定