I want to write a proxy server, proxy server changes IP/port of a packet and emits modified.
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))
}
}
}
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. .|
Second hex dump must start with 45 (most IPv4 packets start with 45, where 4 is a version of the protocol). Second hexdump must be identical to the first in many details except one IP address that was changed, TCP checksum and size values. Second hex dump must contain payload 1234567890\n
.
First, always check returned errors — it's your feedback:
err := gopacket.SerializePacket(newBuffer, options, packet)
// OR
err := gopacket.SerializeLayers(newBuffer, options,
ip,
tcp,
)
// THEN
if err != nil {
panic(err)
}
From code above you will get: TCP/IP layer 4 checksum cannot be computed without network layer... call SetNetworkLayerForChecksum to set which layer to use
Then, the solution is to use tcp.SetNetworkLayerForChecksum(ip)
and the final working code:
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,
}
tcp.SetNetworkLayerForChecksum(ip)
newBuffer := gopacket.NewSerializeBuffer()
err := gopacket.SerializePacket(newBuffer, options, packet)
if err != nil {
panic(err)
}
outgoingPacket := newBuffer.Bytes()
fmt.Println("Hex dump of go packet serialization output:\n")
fmt.Println(hex.Dump(outgoingPacket))
}
}
}
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 45 00 00 3f 40 ed 40 00 40 06 27 ba c0 a8 01 5a |E..?@.@.@.'....Z|
00000010 08 08 08 08 8d e6 00 50 ae 93 56 c0 12 6b f3 95 |.......P..V..k..|
00000020 80 18 00 e5 39 1e 00 00 01 01 08 0a 16 8a 55 6d |....9.........Um|
00000030 30 10 20 fd 31 32 33 34 35 36 37 38 39 30 0a |0. .1234567890.|
As you see 08 08 08 08
is a new IP and payload 1234567890\n
is also preserved, IP packet starts with 45 as usual.