英文:
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))。由于ip4
和ipip4
具有相同的LayerType(layers.LayerTypeIPv4
),后者将覆盖前者在容器中的位置。每次DecodingLayerParser
从容器中获取layers.LayerTypeIPv4
的解码器时,它都会获取ipip4
。因此,ipip4
的状态会一次又一次地被改变。
解决方法
一个解决方法是给ip4
和ipip4
分配不同的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, ð, &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]
注意事项
DecodingLayerParser
速度更快,但也更加严格。而PacketSource
速度较慢,但可以安全且轻松地处理任何已知类型的数据包(我相信您已经知道这一点)。- 这只是一个解决方法,我不确定这是否是最佳实践。
- 这个示例的目的是说明一种解决此类问题的方法。它无法正确解析具有多个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
DecodingLayerParser
is faster, but it is also more rigid. whilePacketSource
is slower, it will handle any known type of packet safely and easily. (I'm sure you already know this).- This is only a workaround. I'm not sure if this is a best practice.
- 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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论