vx46/main.go

314 lines
7.5 KiB
Go
Raw Normal View History

2023-08-11 13:35:34 +02:00
package main
import (
"encoding/binary"
2023-08-23 19:23:22 +02:00
"errors"
2023-08-11 13:35:34 +02:00
"flag"
"fmt"
"log"
"math"
"net"
"net/netip"
2023-08-23 19:23:22 +02:00
"sync"
2023-08-11 13:35:34 +02:00
2023-08-23 19:23:22 +02:00
"golang.org/x/net/ipv4"
2023-08-11 13:35:34 +02:00
"golang.org/x/net/ipv6"
2023-08-23 19:23:22 +02:00
"golang.org/x/sys/unix"
2023-08-11 13:35:34 +02:00
)
2023-08-23 19:23:22 +02:00
const (
NUM_BUFFERS = 1
)
var (
OOB_SIZE = unix.CmsgSpace(16 + 2) // ipv6 address + port
)
func (vx *vx46) transform46(msgs4 []ipv4.Message, msgs6 []ipv6.Message) error {
// embed the "client" ipv4 into the src address for the packet to the upstream vxlan ipv6 endpoint
// the destination is the upstream vxlan endpoint
for i := range msgs4 {
cm4 := ipv4.ControlMessage{}
if err := cm4.Parse(msgs4[i].OOB[:msgs4[i].NN]); err != nil {
return err
}
inUDPAddr4 := msgs4[i].Addr.(*net.UDPAddr)
egressSrcAddr := vx.natprefix.As16()
copy(egressSrcAddr[10:14], inUDPAddr4.IP.To4()[:4]) // panics if To4 returns nil
binary.BigEndian.PutUint16(egressSrcAddr[14:16], uint16(inUDPAddr4.Port))
if vx.oobkey != egressSrcAddr {
2023-08-23 22:46:24 +02:00
vx.oobkey = egressSrcAddr
2023-08-23 19:23:22 +02:00
cm6 := ipv6.ControlMessage{Src: net.IP(egressSrcAddr[:])}
vx.oobcache = cm6.Marshal()
}
msgs6[i].Buffers[0] = msgs4[i].Buffers[0][:msgs4[i].N]
msgs6[i].OOB = vx.oobcache
msgs6[i].Addr = vx.upstream
msgs6[i].N = msgs4[i].N
msgs6[i].NN = len(msgs6[i].OOB)
}
return nil
}
func (vx *vx46) forward46() error {
2023-08-23 21:24:47 +02:00
msgs4 := make([]ipv4.Message, vx.buffers)
msgs6 := make([]ipv6.Message, vx.buffers)
2023-08-23 19:23:22 +02:00
for i := range msgs6 {
msgs4[i].Buffers = [][]byte{make([]byte, vx.mtu)}
msgs6[i].Buffers = [][]byte{nil}
}
for {
n, err := vx.pc4.ReadBatch(msgs4[:], 0)
2023-08-23 21:24:57 +02:00
if n > vx.buffers*3/4 {
//log.Printf("forward46: %d in", n)
2023-08-23 19:23:22 +02:00
}
if err != nil {
return err
}
err = vx.transform46(msgs4[:n], msgs6[:n])
if err != nil {
return err
}
queue := msgs6[:n]
for len(queue) > 0 {
outn, err := vx.pc6.WriteBatch(queue, 0)
if err != nil {
return err
}
queue = queue[outn:]
2023-08-23 19:23:22 +02:00
}
}
}
func (vx *vx46) transform64(msgs6 []ipv6.Message, msgs4 []ipv4.Message) error {
// embed the "client" ipv4 into the src address for the packet to the upstream vxlan ipv6 endpoint
// the destination is the upstream vxlan endpoint
for i := range msgs6 {
cm6 := ipv6.ControlMessage{}
if err := cm6.Parse(msgs6[i].OOB[:msgs6[i].NN]); err != nil {
return err
}
if cm6.Dst.To16() == nil {
return fmt.Errorf("Destination information not available")
}
msgs4[i].Buffers[0] = msgs6[i].Buffers[0][:msgs6[i].N]
if msgs4[i].Addr == nil {
msgs4[i].Addr = &net.UDPAddr{}
}
addr := msgs4[i].Addr.(*net.UDPAddr)
addr.IP = cm6.Dst[10:14]
addr.Port = int(binary.BigEndian.Uint16(cm6.Dst[14:16]))
msgs4[i].N = msgs6[i].N
msgs4[i].OOB = nil
msgs4[i].NN = 0
}
return nil
}
func (vx *vx46) forward64() error {
2023-08-23 21:24:47 +02:00
msgs4 := make([]ipv4.Message, vx.buffers)
msgs6 := make([]ipv6.Message, vx.buffers)
2023-08-23 19:23:22 +02:00
for i := range msgs6 {
msgs6[i].Buffers = [][]byte{make([]byte, vx.mtu)}
msgs6[i].OOB = make([]byte, OOB_SIZE)
msgs4[i].Buffers = [][]byte{nil}
}
for {
n, err := vx.pc6.ReadBatch(msgs6[:], 0)
2023-08-23 21:24:57 +02:00
if n > vx.buffers*3/4 {
//log.Printf("forward64: %d in", n)
2023-08-23 19:23:22 +02:00
}
if err != nil {
return err
}
err = vx.transform64(msgs6[:n], msgs4[:n])
if err != nil {
return err
}
queue := msgs4[:n]
for len(queue) > 0 {
outn, err := vx.pc4.WriteBatch(queue, 0)
if err != nil {
return err
}
queue = queue[outn:]
2023-08-23 19:23:22 +02:00
}
}
}
func (vx *vx46) forward() error {
l4, err := net.ListenUDP("udp4", &net.UDPAddr{Port: int(vx.port)})
if err != nil {
return err
}
l6, err := net.ListenUDP("udp6", &net.UDPAddr{Port: int(vx.port)})
if err != nil {
return err
}
vx.pc4 = ipv4.NewPacketConn(l4)
vx.pc6 = ipv6.NewPacketConn(l6)
vx.pc6.SetControlMessage(ipv6.FlagDst, true)
var wg sync.WaitGroup
var err4 error
var err6 error
wg.Add(1)
go func() {
err4 = vx.forward46()
l6.Close()
wg.Done()
}()
wg.Add(1)
go func() {
err6 = vx.forward64()
l4.Close()
wg.Done()
}()
wg.Wait()
return errors.Join(err4, err6)
}
type vx46 struct {
pc4 *ipv4.PacketConn
pc6 *ipv6.PacketConn
natprefix netip.Addr
upstreamAddr netip.Addr
upstream *net.UDPAddr
port uint16
mtu uint16
buffers int
oobkey [16]byte
oobcache []byte
}
func vx46forward(natprefix netip.Addr, upstreamAddr netip.Addr, port uint16, mtu uint16) error {
2023-08-11 13:35:34 +02:00
upstream := netip.AddrPortFrom(upstreamAddr, port)
p, err := net.ListenUDP("udp", &net.UDPAddr{Port: int(port)})
if err != nil {
return err
}
// we need to know to which address incoming ipv6 packets were sent to
// extract the client ipv4 and port
ipv6.NewPacketConn(p).SetControlMessage(ipv6.FlagDst, true)
defer p.Close()
2023-08-23 19:23:22 +02:00
b := make([]byte, mtu) // inner ethernet header + vxlan header
oob := make([]byte, OOB_SIZE)
2023-08-11 13:35:34 +02:00
for {
n, oobn, _, ingressSrcAddrPort, err := p.ReadMsgUDPAddrPort(b[:], oob[:])
cm := ipv6.ControlMessage{}
if err := cm.Parse(oob[:oobn]); err != nil {
return err
}
ingressDstAddr, ok := netip.AddrFromSlice(cm.Dst)
if !ok {
continue
}
var ingressDstAddrPort netip.AddrPort
ingressDstAddrPort = netip.AddrPortFrom(ingressDstAddr.Unmap(), port)
ingressSrcAddrPort = netip.AddrPortFrom(ingressSrcAddrPort.Addr().Unmap(), ingressSrcAddrPort.Port())
var egressDstAddrPort netip.AddrPort
var egressOOB []byte
if ingressSrcAddrPort.Addr().Is4() {
// embed the "client" ipv4 into the src address for the packet to the upstream vxlan ipv6 endpoint
// the destination is the upstream vxlan endpoint
inaddr4 := ingressSrcAddrPort.Addr().As4()
egressSrcAddr := natprefix.As16()
copy(egressSrcAddr[10:14], inaddr4[:])
binary.BigEndian.PutUint16(egressSrcAddr[14:16], ingressSrcAddrPort.Port())
cm := ipv6.ControlMessage{Src: net.IP(egressSrcAddr[:])}
egressOOB = cm.Marshal()
egressDstAddrPort = upstream
} else {
inaddr6 := ingressDstAddrPort.Addr().As16()
egressDstAddrPort = netip.AddrPortFrom(
netip.AddrFrom4([4]byte(inaddr6[10:14])), // extract client ipv4
binary.BigEndian.Uint16(inaddr6[14:16]), // extract client port
)
}
outn, outoobn, err := p.WriteMsgUDPAddrPort(b[:n], egressOOB, egressDstAddrPort)
if err != nil {
return err
}
if outn != n {
return fmt.Errorf("dropped bytes: %d sent of %d", outn, n)
}
if outoobn != len(egressOOB) {
return fmt.Errorf("dropped oob bytes: %d sent of %d", outoobn, len(egressOOB))
}
}
}
func main() {
natprefixStr := flag.String("prefix", "", "local IPv6 base address for a /80 to use for communication with upstream")
upstreamStr := flag.String("upstream", "", "IPv6 address of the upstream VXLAN endpoint")
2023-08-23 19:23:22 +02:00
portInt := flag.Int("port", 8472, "port for vxlan communication")
2023-08-11 13:35:34 +02:00
mtuInt := flag.Uint("mtu", 1422, "buffer size")
2023-08-23 19:23:22 +02:00
buffersInt := flag.Int("buffers", 64, "number of buffers for I/O batching")
2023-08-11 13:35:34 +02:00
flag.Parse()
natprefix, err := netip.ParseAddr(*natprefixStr)
if err != nil {
log.Fatalf("Invalid prefix: %s", err)
}
2023-08-23 19:23:22 +02:00
upstreamAddr, err := netip.ParseAddr(*upstreamStr)
2023-08-11 13:35:34 +02:00
if err != nil {
log.Fatalf("Invalid upstream: %s", err)
}
if *portInt > math.MaxUint16 {
log.Fatalf("port out of range: %d", *portInt)
}
port := uint16(*portInt)
if *mtuInt > math.MaxUint16 {
2023-08-23 19:23:22 +02:00
log.Fatalf("mtu out of range: %d", *mtuInt)
2023-08-11 13:35:34 +02:00
}
2023-08-23 19:23:22 +02:00
mtu := uint16(*mtuInt + 14 + 8) // inner ethernet header + vxlan header
2023-08-11 13:35:34 +02:00
2023-08-23 19:23:22 +02:00
if *buffersInt < 1 {
log.Fatalf("buffers < 1: %d", *mtuInt)
}
if *buffersInt == 1 {
log.Println(vx46forward(natprefix, upstreamAddr, port, mtu))
} else {
vx := vx46{
natprefix: natprefix,
upstream: &net.UDPAddr{
IP: net.IP(upstreamAddr.AsSlice()),
Port: *portInt,
},
port: port,
mtu: mtu,
buffers: *buffersInt,
}
log.Println(vx.forward())
2023-08-11 13:35:34 +02:00
}
}