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

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

gopacket: DecodeLayers on an IP-in-IP Packet

问题

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

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

  1. type Decoder struct {
  2. eth layers.Ethernet
  3. ip4 []layers.IPv4
  4. ip6 layers.IPv6
  5. icmp4 layers.ICMPv4
  6. icmp6 layers.ICMPv6
  7. tcp layers.TCP
  8. udp layers.UDP
  9. pay gopacket.Payload
  10. parser *gopacket.DecodingLayerParser
  11. types []gopacket.LayerType
  12. unknowns map[string]uint
  13. }
  14. // NewDecoder 分配并初始化一个新的Decoder。
  15. func NewDecoder() *Decoder {
  16. d := new(Decoder)
  17. d.parser = gopacket.NewDecodingLayerParser(layers.LayerTypeEthernet,
  18. &d.eth, &d.ip4, &d.ip6, &d.icmp4, &d.icmp6, &d.tcp, &d.udp, &d.pay)
  19. d.types = make([]gopacket.LayerType, 8, 8)
  20. d.parser.IgnoreUnsupported = true
  21. d.unknowns = make(map[string]uint)
  22. return d
  23. }

在修改后的代码中,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.

  1. type Decoder struct {
  2. eth layers.Ethernet
  3. ip4 layers.IPv4
  4. ipip4 layers.IPv4
  5. ip6 layers.IPv6
  6. icmp4 layers.ICMPv4
  7. icmp6 layers.ICMPv6
  8. tcp layers.TCP
  9. udp layers.UDP
  10. //sip layers.SIP
  11. //dns layers.DNS
  12. //ntp layers.NTP
  13. pay gopacket.Payload
  14. parser *gopacket.DecodingLayerParser
  15. types []gopacket.LayerType
  16. unknowns map[string]uint
  17. }
  18. // NewDecoder allocates and initialises a new Decoder.
  19. func NewDecoder() *Decoder {
  20. d := new(Decoder)
  21. d.parser = gopacket.NewDecodingLayerParser(layers.LayerTypeEthernet,
  22. &d.eth, &d.ip4, &d.ipip4, &d.ip6, &d.icmp4, &d.icmp6, &d.tcp, &d.udp, &d.pay)
  23. //&d.sip, &d.dns, &d.ntp, &d.pay)
  24. d.types = make([]gopacket.LayerType, 10, 10)
  25. d.parser.IgnoreUnsupported = true
  26. d.unknowns = make(map[string]uint)
  27. return d
  28. }

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作为其下一个解码器。以下是示例代码:

  1. package main
  2. import (
  3. "fmt"
  4. "io"
  5. "github.com/google/gopacket"
  6. "github.com/google/gopacket/layers"
  7. "github.com/google/gopacket/pcap"
  8. )
  9. // 0-999 are reserved for the gopacket library. Numbers 1000-1999 should be
  10. // used for common application-specific types.
  11. var LayerTypeIPv4Inner = gopacket.RegisterLayerType(1000, gopacket.LayerTypeMetadata{Name: "IP_in_IP", Decoder: nil})
  12. // IPv4Outer is like layers.IPv4 but it recognizes IP in IP and will choose
  13. // its next decoder accordingly.
  14. type IPv4Outer struct {
  15. layers.IPv4
  16. }
  17. // NextLayerType overrides (*layers.IPv4).NextLayerType to recognize IP in IP.
  18. func (i *IPv4Outer) NextLayerType() gopacket.LayerType {
  19. if i.Flags&layers.IPv4MoreFragments != 0 || i.FragOffset != 0 {
  20. return gopacket.LayerTypeFragment
  21. }
  22. // This is an IP in IP packet.
  23. // See https://datatracker.ietf.org/doc/html/rfc2003#section-3.1
  24. if i.Protocol == 4 {
  25. return LayerTypeIPv4Inner
  26. }
  27. return i.Protocol.LayerType()
  28. }
  29. // IPv4Inner is like layers.IPv4 except that its type is LayerTypeIPv4Inner.
  30. // gopacket.DecodingLayerParser finds next decoder based on this type.
  31. type IPv4Inner struct {
  32. layers.IPv4
  33. }
  34. // CanDecode overrides (*layers.IPv4).CanDecode to choose a type other than
  35. // layers.LayerTypeIPv4.
  36. func (i *IPv4Inner) CanDecode() gopacket.LayerClass {
  37. return LayerTypeIPv4Inner
  38. }
  39. func main() {
  40. handle, err := pcap.OpenOffline("./IP_in_IP.cap")
  41. if err != nil {
  42. panic(err)
  43. }
  44. var (
  45. eth layers.Ethernet
  46. ip4 IPv4Outer
  47. ipip4 IPv4Inner
  48. tcp layers.TCP
  49. icmpv4 layers.ICMPv4
  50. payload gopacket.Payload
  51. )
  52. parser := gopacket.NewDecodingLayerParser(layers.LayerTypeEthernet, &eth, &ip4, &ipip4, &tcp, &icmpv4, &payload)
  53. decodedLayers := make([]gopacket.LayerType, 0, 10)
  54. for {
  55. data, _, err := handle.ZeroCopyReadPacketData()
  56. if err == io.EOF {
  57. fmt.Println("done")
  58. return
  59. }
  60. if err != nil {
  61. panic(err)
  62. }
  63. err = parser.DecodeLayers(data, &decodedLayers)
  64. if err != nil {
  65. panic(err)
  66. }
  67. for _, typ := range decodedLayers {
  68. if typ == layers.LayerTypeIPv4 {
  69. fmt.Printf("Ipv4: %s => %s\n", ip4.SrcIP, ip4.DstIP)
  70. } else if typ == LayerTypeIPv4Inner {
  71. fmt.Printf("IP in IP: %s => %s\n", ipip4.SrcIP, ipip4.DstIP)
  72. }
  73. }
  74. fmt.Printf("%v\n\n", decodedLayers)
  75. }
  76. }

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

  1. Ipv4: 10.0.0.1 => 10.0.0.2
  2. IP in IP: 1.1.1.1 => 2.2.2.2
  3. [Ethernet IPv4 IP_in_IP ICMPv4 Payload]
  4. Ipv4: 10.0.0.2 => 10.0.0.1
  5. IP in IP: 2.2.2.2 => 1.1.1.1
  6. [Ethernet IPv4 IP_in_IP ICMPv4 Payload]
  7. Ipv4: 10.1.1.2 => 10.1.1.1
  8. [Ethernet IPv4 TCP]
  9. Ipv4: 10.1.1.1 => 10.1.1.2
  10. [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:

  1. package main
  2. import (
  3. "fmt"
  4. "io"
  5. "github.com/google/gopacket"
  6. "github.com/google/gopacket/layers"
  7. "github.com/google/gopacket/pcap"
  8. )
  9. // 0-999 are reserved for the gopacket library. Numbers 1000-1999 should be
  10. // used for common application-specific types.
  11. var LayerTypeIPv4Inner = gopacket.RegisterLayerType(1000, gopacket.LayerTypeMetadata{Name: "IP_in_IP", Decoder: nil})
  12. // IPv4Outer is like layers.IPv4 but it recognizes IP in IP and will choose
  13. // its next decoder accordingly.
  14. type IPv4Outer struct {
  15. layers.IPv4
  16. }
  17. // NextLayerType overrides (*layers.IPv4).NextLayerType to recognize IP in IP.
  18. func (i *IPv4Outer) NextLayerType() gopacket.LayerType {
  19. if i.Flags&layers.IPv4MoreFragments != 0 || i.FragOffset != 0 {
  20. return gopacket.LayerTypeFragment
  21. }
  22. // This is an IP in IP packet.
  23. // See https://datatracker.ietf.org/doc/html/rfc2003#section-3.1
  24. if i.Protocol == 4 {
  25. return LayerTypeIPv4Inner
  26. }
  27. return i.Protocol.LayerType()
  28. }
  29. // IPv4Inner is like layers.IPv4 except that its type is LayerTypeIPv4Inner.
  30. // gopacket.DecodingLayerParser finds next decoder based on this type.
  31. type IPv4Inner struct {
  32. layers.IPv4
  33. }
  34. // CanDecode overrides (*layers.IPv4).CanDecode to choose a type other than
  35. // layers.LayerTypeIPv4.
  36. func (i *IPv4Inner) CanDecode() gopacket.LayerClass {
  37. return LayerTypeIPv4Inner
  38. }
  39. func main() {
  40. handle, err := pcap.OpenOffline("./IP_in_IP.cap")
  41. if err != nil {
  42. panic(err)
  43. }
  44. var (
  45. eth layers.Ethernet
  46. ip4 IPv4Outer
  47. ipip4 IPv4Inner
  48. tcp layers.TCP
  49. icmpv4 layers.ICMPv4
  50. payload gopacket.Payload
  51. )
  52. parser := gopacket.NewDecodingLayerParser(layers.LayerTypeEthernet, &eth, &ip4, &ipip4, &tcp, &icmpv4, &payload)
  53. decodedLayers := make([]gopacket.LayerType, 0, 10)
  54. for {
  55. data, _, err := handle.ZeroCopyReadPacketData()
  56. if err == io.EOF {
  57. fmt.Println("done")
  58. return
  59. }
  60. if err != nil {
  61. panic(err)
  62. }
  63. err = parser.DecodeLayers(data, &decodedLayers)
  64. if err != nil {
  65. panic(err)
  66. }
  67. for _, typ := range decodedLayers {
  68. if typ == layers.LayerTypeIPv4 {
  69. fmt.Printf("Ipv4: %s => %s\n", ip4.SrcIP, ip4.DstIP)
  70. } else if typ == LayerTypeIPv4Inner {
  71. fmt.Printf("IP in IP: %s => %s\n", ipip4.SrcIP, ipip4.DstIP)
  72. }
  73. }
  74. fmt.Printf("%v\n\n", decodedLayers)
  75. }
  76. }

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

  1. Ipv4: 10.0.0.1 => 10.0.0.2
  2. IP in IP: 1.1.1.1 => 2.2.2.2
  3. [Ethernet IPv4 IP_in_IP ICMPv4 Payload]
  4. Ipv4: 10.0.0.2 => 10.0.0.1
  5. IP in IP: 2.2.2.2 => 1.1.1.1
  6. [Ethernet IPv4 IP_in_IP ICMPv4 Payload]
  7. Ipv4: 10.1.1.2 => 10.1.1.1
  8. [Ethernet IPv4 TCP]
  9. Ipv4: 10.1.1.1 => 10.1.1.2
  10. [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:

确定