如何将修改后的Go Packet数据包序列化为真实IP数据包

ily*_*rov 2 networking go gopacket

为什么

我想编写一个代理服务器,代理服务器更改数据包的IP/端口并发出修改后的数据包。

试图

package main

import (
  "encoding/hex"
  "github.com/google/gopacket"
  "github.com/google/gopacket/layers"
  "fmt"
  "net"
)

func main() {

  packetData := []byte{
    69, 0, 0, 63, 64, 237, 64, 0, 64, 6, 74, 221, 192,
    168, 1, 90, 52, 85, 184, 151, 141, 230, 0, 80,
    174, 147, 86, 192, 18, 107, 243, 149, 128, 24,
    0, 229, 92, 65, 0, 0, 1, 1, 8, 10, 22, 138, 85, 109,
    48, 16, 32, 253, 49, 50, 51, 52, 53, 54, 55, 56,
    57, 48, 10,
  }

  fmt.Println("Hex dump of real IP packet taken as input:\n")
  fmt.Println(hex.Dump(packetData))

  packet := gopacket.NewPacket(packetData, layers.LayerTypeIPv4, gopacket.Default)
  if ipLayer := packet.Layer(layers.LayerTypeIPv4); ipLayer != nil {
    ip := ipLayer.(*layers.IPv4)
    dst := ip.DstIP.String()
    src := ip.SrcIP.String()

    if tcpLayer := packet.Layer(layers.LayerTypeTCP); tcpLayer != nil {
      tcp := tcpLayer.(*layers.TCP)
      dst = fmt.Sprintf("%s:%d", dst, tcp.DstPort)
      src = fmt.Sprintf("%s:%d", src, tcp.SrcPort)
      fmt.Printf("From %s to %s\n\n", src, dst)

      ip.DstIP = net.ParseIP("8.8.8.8")

      options := gopacket.SerializeOptions{
        ComputeChecksums: true,
        FixLengths: true,
      }
      newBuffer := gopacket.NewSerializeBuffer()
      gopacket.SerializeLayers(newBuffer, options,
          ip,
          tcp,
      )
      outgoingPacket := newBuffer.Bytes()

      fmt.Println("Hex dump of go packet serialization output:\n")
      fmt.Println(hex.Dump(outgoingPacket))

    }

  }

}
Run Code Online (Sandbox Code Playgroud)

输出

Hex dump of real IP packet taken as input:

00000000  45 00 00 3f 40 ed 40 00  40 06 4a dd c0 a8 01 5a  |E..?@.@.@.J....Z|
00000010  34 55 b8 97 8d e6 00 50  ae 93 56 c0 12 6b f3 95  |4U.....P..V..k..|
00000020  80 18 00 e5 5c 41 00 00  01 01 08 0a 16 8a 55 6d  |....\A........Um|
00000030  30 10 20 fd 31 32 33 34  35 36 37 38 39 30 0a     |0. .1234567890.|

From 192.168.1.90:36326 to 52.85.184.151:80

Hex dump of go packet serialization output:

00000000  8d e6 00 50 ae 93 56 c0  12 6b f3 95 80 18 00 e5  |...P..V..k......|
00000010  00 00 00 00 01 01 08 0a  16 8a 55 6d 30 10 20 fd  |..........Um0. .|
Run Code Online (Sandbox Code Playgroud)

怎么了

第二个十六进制转储必须以 45 开头(大多数 IPv4 数据包以 45 开头,其中 4 是协议的版本)。第二个十六进制转储在许多细节上必须与第一个相同,除了一个已更改的 IP 地址、TCP 校验和和大小值。第二个十六进制转储必须包含有效负载1234567890\n

类似的问题

  1. 如何使用Golang编写原始TCP数据包(使用gopacket)并通过原始套接字发送
  2. 使用 gopacket 发送 UDP 数据包
  3. 更改 Gopacket 的 IP 地址并使用原始套接字重新传输

ily*_*rov 5

首先,始终检查返回的错误 \xe2\x80\x94 这是您的反馈:

\n\n
err := gopacket.SerializePacket(newBuffer, options, packet)\n// OR\nerr := gopacket.SerializeLayers(newBuffer, options,\n  ip,\n  tcp,\n)\n// THEN\nif err != nil {\n  panic(err)\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

从上面的代码你将得到:TCP/IP layer 4 checksum cannot be computed without network layer... call SetNetworkLayerForChecksum to set which layer to use

\n\n

然后,解决方案是使用tcp.SetNetworkLayerForChecksum(ip)最终的工作代码:

\n\n
package main\n\nimport (\n  "encoding/hex"\n  "github.com/google/gopacket"\n  "github.com/google/gopacket/layers"\n  "fmt"\n  "net"\n)\n\nfunc main() {\n\n  packetData := []byte{\n    69, 0, 0, 63, 64, 237, 64, 0, 64, 6, 74, 221, 192,\n    168, 1, 90, 52, 85, 184, 151, 141, 230, 0, 80,\n    174, 147, 86, 192, 18, 107, 243, 149, 128, 24,\n    0, 229, 92, 65, 0, 0, 1, 1, 8, 10, 22, 138, 85, 109,\n    48, 16, 32, 253, 49, 50, 51, 52, 53, 54, 55, 56,\n    57, 48, 10,\n  }\n\n  fmt.Println("Hex dump of real IP packet taken as input:\\n")\n  fmt.Println(hex.Dump(packetData))\n\n  packet := gopacket.NewPacket(packetData, layers.LayerTypeIPv4, gopacket.Default)\n  if ipLayer := packet.Layer(layers.LayerTypeIPv4); ipLayer != nil {\n    ip := ipLayer.(*layers.IPv4)\n    dst := ip.DstIP.String()\n    src := ip.SrcIP.String()\n\n    if tcpLayer := packet.Layer(layers.LayerTypeTCP); tcpLayer != nil {\n      tcp := tcpLayer.(*layers.TCP)\n      dst = fmt.Sprintf("%s:%d", dst, tcp.DstPort)\n      src = fmt.Sprintf("%s:%d", src, tcp.SrcPort)\n      fmt.Printf("From %s to %s\\n\\n", src, dst)\n\n      ip.DstIP = net.ParseIP("8.8.8.8")\n\n      options := gopacket.SerializeOptions{\n        ComputeChecksums: true,\n        FixLengths: true,\n      }\n\n      tcp.SetNetworkLayerForChecksum(ip)\n\n      newBuffer := gopacket.NewSerializeBuffer()\n      err := gopacket.SerializePacket(newBuffer, options, packet)\n      if err != nil {\n        panic(err)\n      }\n      outgoingPacket := newBuffer.Bytes()\n\n      fmt.Println("Hex dump of go packet serialization output:\\n")\n      fmt.Println(hex.Dump(outgoingPacket))\n\n    }\n\n  }\n\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

输出

\n\n
Hex dump of real IP packet taken as input:\n\n00000000  45 00 00 3f 40 ed 40 00  40 06 4a dd c0 a8 01 5a  |E..?@.@.@.J....Z|\n00000010  34 55 b8 97 8d e6 00 50  ae 93 56 c0 12 6b f3 95  |4U.....P..V..k..|\n00000020  80 18 00 e5 5c 41 00 00  01 01 08 0a 16 8a 55 6d  |....\\A........Um|\n00000030  30 10 20 fd 31 32 33 34  35 36 37 38 39 30 0a     |0. .1234567890.|\n\nFrom 192.168.1.90:36326 to 52.85.184.151:80\n\nHex dump of go packet serialization output:\n\n00000000  45 00 00 3f 40 ed 40 00  40 06 27 ba c0 a8 01 5a  |E..?@.@.@.\'....Z|\n00000010  08 08 08 08 8d e6 00 50  ae 93 56 c0 12 6b f3 95  |.......P..V..k..|\n00000020  80 18 00 e5 39 1e 00 00  01 01 08 0a 16 8a 55 6d  |....9.........Um|\n00000030  30 10 20 fd 31 32 33 34  35 36 37 38 39 30 0a     |0. .1234567890.|\n
Run Code Online (Sandbox Code Playgroud)\n\n

正如您所看到的,08 08 08 08这是一个新的 IP,并且有效负载1234567890\\n也被保留,IP 数据包像往常一样以 45 开头。

\n