implement VPN backend

This commit is contained in:
Eugene K 2024-04-11 13:01:34 -04:00
parent 67851c64af
commit b7f046abf5
No known key found for this signature in database
GPG Key ID: C8CCB4692865B818
3 changed files with 152 additions and 26 deletions

View File

@ -2,15 +2,19 @@ package vpn
import ( import (
"encoding/json" "encoding/json"
"fmt" "github.com/net-byte/vtun/common/config"
"github.com/net-byte/vtun/tun"
_ "github.com/net-byte/vtun/tun" _ "github.com/net-byte/vtun/tun"
"github.com/net-byte/water"
"github.com/openziti/sdk-golang/ziti" "github.com/openziti/sdk-golang/ziti"
"github.com/openziti/sdk-golang/ziti/edge" "github.com/openziti/sdk-golang/ziti/edge"
"github.com/openziti/zrok/endpoints" "github.com/openziti/zrok/endpoints"
cmap "github.com/orcaman/concurrent-map/v2"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/songgao/water/waterutil" "github.com/songgao/water/waterutil"
"net" "net"
"sync/atomic"
"time" "time"
) )
@ -21,11 +25,20 @@ type BackendConfig struct {
RequestsChan chan *endpoints.Request RequestsChan chan *endpoints.Request
} }
type client struct {
conn net.Conn
}
type Backend struct { type Backend struct {
cfg *BackendConfig cfg *BackendConfig
listener edge.Listener listener edge.Listener
cidr net.IPAddr cidr net.IPAddr
tun *water.Interface
mtu int
counter atomic.Uint32
clients cmap.ConcurrentMap[dest, *client]
} }
func NewBackend(cfg *BackendConfig) (*Backend, error) { func NewBackend(cfg *BackendConfig) (*Backend, error) {
@ -49,14 +62,66 @@ func NewBackend(cfg *BackendConfig) (*Backend, error) {
b := &Backend{ b := &Backend{
cfg: cfg, cfg: cfg,
listener: listener, listener: listener,
mtu: ZROK_VPN_MTU,
clients: cmap.NewWithCustomShardingFunction[dest, *client](func(key dest) uint32 {
return key.toInt32()
}),
} }
b.counter.Store(1)
return b, nil return b, nil
} }
func (b *Backend) readTun() {
buf := make([]byte, ZROK_VPN_MTU)
for {
n, err := b.tun.Read(buf)
if err != nil {
logrus.WithError(err).Error("failed to read tun device")
// handle? error
panic(err)
return
}
pkt := packet(buf[:n])
if !waterutil.IsIPv4(pkt) {
continue
}
logrus.WithField("packet", pkt).Trace("read from tun device")
dest := pkt.destination()
if clt, ok := b.clients.Get(dest); ok {
_, err := clt.conn.Write(pkt)
if err != nil {
logrus.WithError(err).Errorf("failed to write packet to clt[%v]", dest)
_ = clt.conn.Close()
b.clients.Remove(dest)
}
} else {
logrus.Errorf("no client with address[%v]", dest)
}
}
}
func (b *Backend) Run() error { func (b *Backend) Run() error {
logrus.Info("started") logrus.Info("started")
defer logrus.Info("exited") defer logrus.Info("exited")
tunCfg := config.Config{
ServerIP: "192.168.127.1",
ServerIPv6: "fced::ffff:c0a8:7f01",
CIDR: "192.168.127.1/24",
CIDRv6: "fced::ffff:c0a8:7f01/64",
MTU: ZROK_VPN_MTU,
Verbose: true,
}
b.tun = tun.CreateTun(tunCfg)
defer func() {
_ = b.tun.Close()
}()
go b.readTun()
for { for {
if conn, err := b.listener.Accept(); err == nil { if conn, err := b.listener.Accept(); err == nil {
go b.handle(conn) go b.handle(conn)
@ -66,47 +131,54 @@ func (b *Backend) Run() error {
} }
} }
type ipProto waterutil.IPProtocol
func (p ipProto) String() string {
switch p {
case waterutil.TCP:
return "tcp"
case waterutil.UDP:
return "udp"
case waterutil.ICMP:
return "icmp"
default:
return fmt.Sprintf("proto[%d]", p)
}
}
func (b *Backend) handle(conn net.Conn) { func (b *Backend) handle(conn net.Conn) {
defer func(conn net.Conn) { defer func(conn net.Conn) {
_ = conn.Close() _ = conn.Close()
}(conn) }(conn)
num := uint32(0)
for num == 0 || num == 1 {
num = b.counter.Add(1)
num = num % 256
}
ipv4 := net.IPv4(192, 168, 127, byte(num))
ip := ipToDest(ipv4)
cfg := &ClientConfig{ cfg := &ClientConfig{
Greeting: "Welcome to zrok VPN", Greeting: "Welcome to zrok VPN",
IP: ipv4.String(),
ServerIP: "192.168.127.1",
CIDR: ipv4.String() + "/24",
MTU: b.mtu,
} }
j, err := json.Marshal(&cfg) j, err := json.Marshal(&cfg)
if err != nil { if err != nil {
logrus.WithError(err).Error("failed to write client VPN config")
return
}
_, err = conn.Write(j)
if err != nil {
logrus.WithError(err).Error("failed to write client VPN config")
return return
} }
conn.Write(j)
buf := make([]byte, 16*1024) clt := &client{conn: conn}
b.clients.Set(ip, clt)
buf := make([]byte, b.mtu)
for { for {
read, err := conn.Read(buf) read, err := conn.Read(buf)
if err != nil { if err != nil {
logrus.Error("read error", err) logrus.Error("read error", err)
return return
} }
pkt := buf[:read] pkt := packet(buf[:read])
logrus.Infof("read packet %d bytes %v %v:%v -> %v:%v", read, logrus.WithField("packet", pkt).Info("read from ziti")
ipProto(waterutil.IPv4Protocol(pkt)), _, err = b.tun.Write(pkt)
waterutil.IPv4Source(pkt), waterutil.IPv4SourcePort(pkt), if err != nil {
waterutil.IPv4Destination(pkt), waterutil.IPv4DestinationPort(pkt)) logrus.WithError(err).Error("failed to write packet to tun")
}
} }
} }

View File

@ -1,9 +1,63 @@
package vpn package vpn
import (
"fmt"
"github.com/songgao/water/waterutil"
"net"
"strconv"
)
const ZROK_VPN_MTU = 16 * 1024
type ClientConfig struct { type ClientConfig struct {
Greeting string Greeting string
IP string IP string
CIDR string CIDR string
ServerIP string ServerIP string
Routes []string Routes []string
MTU int
}
type dest struct {
addr [4]byte
}
func (d dest) String() string {
return net.IP(d.addr[:]).String()
}
func (d dest) toInt32() uint32 {
return uint32(d.addr[0])<<24 + uint32(d.addr[1])<<16 + uint32(d.addr[2])<<8 + uint32(d.addr[3])
}
func ipToDest(addr net.IP) dest {
return dest{
addr: [4]byte{addr[0], addr[1], addr[2], addr[3]},
}
}
type packet []byte
func (p packet) destination() dest {
return ipToDest(waterutil.IPv4Destination(p))
}
func (p packet) String() string {
return fmt.Sprintf("%s %s:%d -> %s:%d %d bytes", p.proto(),
waterutil.IPv4Source(p), waterutil.IPv4SourcePort(p),
waterutil.IPv4Destination(p), waterutil.IPv4DestinationPort(p), len(waterutil.IPv4Payload(p)))
}
func (p packet) proto() string {
proto := waterutil.IPv4Protocol(p)
switch proto {
case waterutil.TCP:
return "tcp"
case waterutil.UDP:
return "udp"
case waterutil.ICMP:
return "icmp"
default:
return strconv.Itoa(int(proto))
}
} }

4
go.mod
View File

@ -31,6 +31,7 @@ require (
github.com/michaelquigley/pfxlog v0.6.10 github.com/michaelquigley/pfxlog v0.6.10
github.com/muesli/reflow v0.3.0 github.com/muesli/reflow v0.3.0
github.com/net-byte/vtun v1.7.0 github.com/net-byte/vtun v1.7.0
github.com/net-byte/water v0.0.7
github.com/nxadm/tail v1.4.8 github.com/nxadm/tail v1.4.8
github.com/openziti/channel/v2 v2.0.121 github.com/openziti/channel/v2 v2.0.121
github.com/openziti/edge-api v0.26.12 github.com/openziti/edge-api v0.26.12
@ -38,6 +39,7 @@ require (
github.com/openziti/identity v1.0.72 github.com/openziti/identity v1.0.72
github.com/openziti/sdk-golang v0.23.10 github.com/openziti/sdk-golang v0.23.10
github.com/openziti/transport/v2 v2.0.124 github.com/openziti/transport/v2 v2.0.124
github.com/orcaman/concurrent-map/v2 v2.0.1
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/rabbitmq/amqp091-go v1.8.1 github.com/rabbitmq/amqp091-go v1.8.1
github.com/rubenv/sql-migrate v1.6.0 github.com/rubenv/sql-migrate v1.6.0
@ -166,7 +168,6 @@ require (
github.com/muesli/termenv v0.13.0 // indirect github.com/muesli/termenv v0.13.0 // indirect
github.com/muhlemmer/gu v0.3.1 // indirect github.com/muhlemmer/gu v0.3.1 // indirect
github.com/net-byte/go-gateway v0.0.2 // indirect github.com/net-byte/go-gateway v0.0.2 // indirect
github.com/net-byte/water v0.0.7 // indirect
github.com/oklog/ulid v1.3.1 // indirect github.com/oklog/ulid v1.3.1 // indirect
github.com/onsi/ginkgo/v2 v2.9.5 // indirect github.com/onsi/ginkgo/v2 v2.9.5 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect
@ -174,7 +175,6 @@ require (
github.com/openziti/metrics v1.2.47 // indirect github.com/openziti/metrics v1.2.47 // indirect
github.com/openziti/secretstream v0.1.17 // indirect github.com/openziti/secretstream v0.1.17 // indirect
github.com/openziti/storage v0.2.6 // indirect github.com/openziti/storage v0.2.6 // indirect
github.com/orcaman/concurrent-map/v2 v2.0.1 // indirect
github.com/parallaxsecond/parsec-client-go v0.0.0-20221025095442-f0a77d263cf9 // indirect github.com/parallaxsecond/parsec-client-go v0.0.0-20221025095442-f0a77d263cf9 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect