tailscale/ebpf/main.go
2021-11-10 12:15:03 -08:00

161 lines
3.5 KiB
Go

package main
import (
"fmt"
"log"
"net"
"os"
"os/exec"
"github.com/florianl/go-tc"
"github.com/florianl/go-tc/core"
"golang.zx2c4.com/wireguard/tun"
)
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cflags "-O2 -Iinclude" bpf bpf.c
func main() {
if err := run(); err != nil {
log.Fatal(err)
os.Exit(1)
}
}
const (
divertInterface = "hack0"
tailscaleInterface = "tailscale0"
)
func run() error {
dev, err := tun.CreateTUN(divertInterface, 1280)
if err != nil {
return fmt.Errorf("creating tun: %v", err)
}
defer dev.Close()
divertIdx, err := ifIndex(divertInterface)
if err != nil {
return fmt.Errorf("getting index of divert interface: %v", err)
}
tailscaleIdx, err := ifIndex(tailscaleInterface)
if err != nil {
return fmt.Errorf("getting index of tailscale interface: %v", err)
}
// Prevent IPv6 link-local spam
if err := exec.Command("ip", "link", "set", divertInterface, "addrgenmode", "none").Run(); err != nil {
return fmt.Errorf("bringing up iface: %v", err)
}
if err := exec.Command("ip", "link", "set", divertInterface, "up").Run(); err != nil {
return fmt.Errorf("bringing up iface: %v", err)
}
spec, err := loadBpf()
if err != nil {
return fmt.Errorf("loading tailscale egress program: %v", err)
}
err = spec.RewriteConstants(map[string]interface{}{
"ifidx_divert": divertIdx,
"ifidx_tailscale": tailscaleIdx,
})
if err != nil {
return fmt.Errorf("setting interface ID: %v", err)
}
var objs bpfObjects
if err := spec.LoadAndAssign(&objs, nil); err != nil {
return fmt.Errorf("loading tailscale egress program: %v", err)
}
defer objs.Close()
tcnl, err := tc.Open(&tc.Config{})
if err != nil {
return fmt.Errorf("opening rtnetlink socket: %v", err)
}
defer tcnl.Close()
qdisc := tc.Object{
tc.Msg{
Ifindex: tailscaleIdx,
Handle: core.BuildHandle(tc.HandleIngress, 0),
Parent: tc.HandleIngress,
},
tc.Attribute{
Kind: "clsact",
},
}
if err := tcnl.Qdisc().Replace(&qdisc); err != nil {
return fmt.Errorf("adding/replacing clsact qdisc on tailscale interface: %v", err)
}
qdisc.Msg.Ifindex = divertIdx
if err := tcnl.Qdisc().Replace(&qdisc); err != nil {
return fmt.Errorf("adding/replacing clsact qdisc on divert interface: %v", err)
}
fd := uint32(objs.EgressTailscale.FD())
flags := uint32(1) // TCA_BPF_FLAG_ACT_DIRECT
handleEgress := uint32(0xFFFFFFF3)
name := "fucksake"
filter := &tc.Object{
tc.Msg{
Ifindex: tailscaleIdx,
Parent: handleEgress,
Handle: 1,
Info: 0x0300, // ETH_P_ALL, byteswapped into the lower 16 bits of info. Yeah, really.
},
tc.Attribute{
Kind: "bpf",
BPF: &tc.Bpf{
FD: &fd,
Name: &name,
Flags: &flags,
},
},
}
fmt.Println(fd)
if err := tcnl.Filter().Add(filter); err != nil {
return fmt.Errorf("adding/replacing tailscale egress filter: %v", err)
}
if err := dumpPackets(dev); err != nil {
return fmt.Errorf("reading packets: %v", err)
}
return nil
}
func ifIndex(name string) (uint32, error) {
intf, err := net.InterfaceByName(name)
if err != nil {
return 0, err
}
return uint32(intf.Index), nil
}
func dumpPackets(dev tun.Device) error {
for {
var b [1284]byte
n, err := dev.Read(b[:], 4)
if err != nil {
return fmt.Errorf("reading from tun: %v", err)
}
bs := b[4 : 4+n]
fmt.Printf("received packet of %d bytes\n", len(bs))
hexdump(bs)
}
}
func hexdump(bs []byte) {
for i := range bs {
switch {
case i == 0:
case i%16 == 0:
fmt.Println("")
case i%8 == 0:
fmt.Printf(" ")
}
fmt.Printf("%02x ", bs[i])
}
fmt.Printf("\n\n")
}