I’m intercepting packets on a toy packet sniffer I have that just pulls all traffic off a TUN
device. I’ve setup a default route to the TUN on my mac via:
sudo route -n add default 10.1.0.10
where 10.1.0.10
is the IP of the utun5
device. While I can see the packets coming through to my program, I want to just print them then forward them off to the router. For example, if I were to curl google.com
or ping yahoo.com
I would see the packet, but the client would still get the expected response as normal.
I feel like on Linux, this isn’t too hard with IP forwarding, NAT and of course, iptables
via iproute2. On Mac, I just can’t crack how this is supposed to work. Any help or reading is appreciated.
From the coding side, I’m using Go and the water
library for simplifying the creation of the tun device.
Here is a basic implementation:
package main
import (
"log"
"net"
"github.com/songgao/water"
"golang.org/x/net/ipv4"
)
func main() {
ifce, err := water.New(water.Config{
DeviceType: water.TUN,
})
if err != nil {
log.Fatal(err)
}
log.Printf("Interface Name: %sn", ifce.Name())
packet := make([]byte, 2000)
// Replace "en0" with your internet-facing network interface name
internetInterface, err := net.InterfaceByName("en0")
if err != nil {
log.Fatal(err)
}
conn, err := net.ListenPacket("ip4:4", "0.0.0.0") // Listen for IPv4 packets
if err != nil {
log.Fatal(err)
}
defer conn.Close()
rawConn, err := ipv4.NewRawConn(conn)
if err != nil {
log.Fatal(err)
}
for {
n, err := ifce.Read(packet)
if err != nil {
log.Fatal(err)
}
header, err := ipv4.ParseHeader(packet[:n])
if err != nil {
log.Printf("Error parsing IPv4 header: %v", err)
continue
}
cm := &ipv4.ControlMessage{
IfIndex: internetInterface.Index,
}
err = rawConn.WriteTo(header, packet[header.Len:n], cm)
if err != nil {
log.Printf("Error writing packet to raw socket: %v", err)
}
}
}