Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ require (
github.com/adrg/xdg v0.5.3
github.com/alphadose/haxmap v1.4.1
github.com/anatol/vmtest v0.0.0-20250318022921-2f32244e2f0f
github.com/apoxy-dev/icx v0.7.2
github.com/apoxy-dev/icx v0.8.0
github.com/avast/retry-go/v4 v4.6.1
github.com/bramvdbogaerde/go-scp v1.5.0
github.com/buraksezer/olric v0.5.6
Expand Down Expand Up @@ -45,6 +45,7 @@ require (
github.com/google/gopacket v1.1.19
github.com/google/uuid v1.6.0
github.com/hashicorp/go-discover v0.0.0-20240726212017-342faf50e5d4
github.com/hashicorp/golang-lru/v2 v2.0.7
github.com/jedib0t/go-pretty/v6 v6.4.9
github.com/julienschmidt/httprouter v1.3.0
github.com/k3s-io/kine v0.13.2
Expand Down
6 changes: 4 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,8 @@ github.com/apoxy-dev/apiserver-runtime v0.0.0-20250420214109-979c605051d1 h1:sAS
github.com/apoxy-dev/apiserver-runtime v0.0.0-20250420214109-979c605051d1/go.mod h1:zOVeivsnCWenmbgr6kiefIExoqlbuv2xyg9SXXfbs5U=
github.com/apoxy-dev/connect-ip-go v0.0.0-20250530062404-603929a73f45 h1:SwPk1n/oSVX7YwlNpC9KNH9YaYkcL/k6OfqSGVnxyyI=
github.com/apoxy-dev/connect-ip-go v0.0.0-20250530062404-603929a73f45/go.mod h1:z5rtgIizc+/K27UtB0occwZgqg/mz3IqgyUJW8aubbI=
github.com/apoxy-dev/icx v0.7.2 h1:6GqlqxkjwyEwaQBAJJ40+iM6D6w46IKmKWtE/43bCUk=
github.com/apoxy-dev/icx v0.7.2/go.mod h1:Muuk3bRXTp3YB5Xj+xHOGQ/T1xVxIKJuvmMfLBXhIN4=
github.com/apoxy-dev/icx v0.8.0 h1:Aj/LWtFokyBYYFuISFqgbiWBQJpMdIN6vCMa21MIROc=
github.com/apoxy-dev/icx v0.8.0/go.mod h1:Muuk3bRXTp3YB5Xj+xHOGQ/T1xVxIKJuvmMfLBXhIN4=
github.com/apoxy-dev/quic-go v0.0.0-20250530165952-53cca597715e h1:10GIpiVyKoRgCyr0J2TvJtdn17bsFHN+ROWkeVJpcOU=
github.com/apoxy-dev/quic-go v0.0.0-20250530165952-53cca597715e/go.mod h1:MFlGGpcpJqRAfmYi6NC2cptDPSxRWTOGNuP4wqrWmzQ=
github.com/apoxy-dev/upgrade-cli v0.0.0-20240213232412-a56c3a52fa0e h1:FBNxMQD93z2ththupB/BYKLEaMWaEr+G+sJWJqU2wC4=
Expand Down Expand Up @@ -643,6 +643,8 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
Expand Down
72 changes: 66 additions & 6 deletions pkg/cmd/alpha/tunnel.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/apoxy-dev/apoxy/pkg/netstack"
"github.com/apoxy-dev/apoxy/pkg/tunnel/api"
"github.com/apoxy-dev/apoxy/pkg/tunnel/bifurcate"
"github.com/apoxy-dev/apoxy/pkg/tunnel/conntrackpc"
"github.com/apoxy-dev/apoxy/pkg/tunnel/router"
)

Expand Down Expand Up @@ -60,6 +61,9 @@ var tunnelRunCmd = &cobra.Command{
defer pcGeneve.Close()
defer pcQuic.Close()

pcQuicMultiplexed := conntrackpc.New(pcQuic, conntrackpc.Options{})
defer pcQuicMultiplexed.Close()

// Context and goroutines.
g, ctx := errgroup.WithContext(cmd.Context())

Expand Down Expand Up @@ -119,9 +123,20 @@ var tunnelRunCmd = &cobra.Command{
}
relay := addr
g.Go(func() error {
// TODO (dpeckett): we will need to create a kind of multiplexed packetconn
// so that each QUIC client gets its own virtual private connection from pcQuic.
// This will be based on the remote ip presumably.
relayAddr, err := resolveAddrPort(ctx, relay)
if err != nil {
return fmt.Errorf("failed to resolve relay addr %q: %w", relay, err)
}

pcQuic, err := pcQuicMultiplexed.Open(&net.UDPAddr{
IP: relayAddr.Addr().AsSlice(),
Port: int(relayAddr.Port()),
})
if err != nil {
return fmt.Errorf("failed to create multiplexed packet conn for relay %q: %w", relay, err)
}
defer pcQuic.Close()

return manageRelayConnection(ctx, pcQuic, getHandler, relay, tlsConf)
})
}
Expand Down Expand Up @@ -237,10 +252,10 @@ func manageRelayConnection(
return cleanupOnErr(fmt.Errorf("init router: %w", err))
}

// Parse relay addr
remoteAddr, err := netip.ParseAddrPort(relayAddr)
// Resolve relay addr (supports hostname:port and ip:port)
remoteAddr, err := resolveAddrPort(ctx, relayAddr)
if err != nil {
return cleanupOnErr(fmt.Errorf("parse relay addr %q: %w", relayAddr, err))
return cleanupOnErr(fmt.Errorf("resolve relay addr %q: %w", relayAddr, err))
}

overlayAddrs, err := stringsToPrefixes(connectResp.Addresses)
Expand Down Expand Up @@ -390,6 +405,51 @@ func manageKeyRotation(
}
}

// resolveAddrPort accepts "host:port" where host may be a hostname or IP
// (IPv4/IPv6, with or without brackets) and returns a concrete netip.AddrPort.
func resolveAddrPort(ctx context.Context, hostport string) (netip.AddrPort, error) {
host, portStr, err := net.SplitHostPort(hostport)
if err != nil {
return netip.AddrPort{}, fmt.Errorf("split host/port: %w", err)
}
pn, err := net.LookupPort("udp", portStr)
if err != nil {
return netip.AddrPort{}, fmt.Errorf("lookup port %q: %w", portStr, err)
}
port := uint16(pn)

// If host is already an IP, use it.
if ip, err := netip.ParseAddr(host); err == nil {
return netip.AddrPortFrom(ip, port), nil
}

// Resolve hostname. Prefer IPv4, then IPv6.
addrs, err := net.DefaultResolver.LookupIPAddr(ctx, host)
if err != nil {
return netip.AddrPort{}, fmt.Errorf("lookup %q: %w", host, err)
}
var v4, v6 *netip.Addr
for _, a := range addrs {
if ip, ok := netip.AddrFromSlice(a.IP); ok {
if ip.Is4() && v4 == nil {
ipCopy := ip
v4 = &ipCopy
} else if ip.Is6() && v6 == nil {
ipCopy := ip
v6 = &ipCopy
}
}
}
switch {
case v4 != nil:
return netip.AddrPortFrom(*v4, port), nil
case v6 != nil:
return netip.AddrPortFrom(*v6, port), nil
default:
return netip.AddrPort{}, fmt.Errorf("no usable A/AAAA records for %q", host)
}
}

func stringsToPrefixes(addrs []string) ([]netip.Prefix, error) {
prefixes := make([]netip.Prefix, 0, len(addrs))
for _, addr := range addrs {
Expand Down
32 changes: 4 additions & 28 deletions pkg/cmd/alpha/tunnel_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import (
"github.com/apoxy-dev/apoxy/pkg/tunnel/connection"
"github.com/apoxy-dev/apoxy/pkg/tunnel/controllers"
"github.com/apoxy-dev/apoxy/pkg/tunnel/hasher"
"github.com/apoxy-dev/apoxy/pkg/tunnel/router"
)

func TestTunnelRun(t *testing.T) {
Expand Down Expand Up @@ -100,6 +99,10 @@ func startRelay(t *testing.T, token string, onConnect func(context.Context, stri

rtr.On("Start", mock.Anything).Return(nil)
rtr.On("Close").Return(nil)
rtr.On("AddAddr", mock.Anything, mock.Anything).Return(nil)
rtr.On("DelAddr", mock.Anything).Return(nil)
rtr.On("AddRoute", mock.Anything).Return(nil)
rtr.On("DelRoute", mock.Anything).Return(nil)

r := tunnel.NewRelay("relay-it", pc, serverCert, h, idHasher, rtr)
r.SetCredentials("test-tunnel", token)
Expand Down Expand Up @@ -146,15 +149,6 @@ func (m *mockRouter) AddAddr(addr netip.Prefix, tun connection.Connection) error
return args.Error(0)
}

func (m *mockRouter) ListAddrs() ([]netip.Prefix, error) {
args := m.Called()
var addrs []netip.Prefix
if v := args.Get(0); v != nil {
addrs = v.([]netip.Prefix)
}
return addrs, args.Error(1)
}

func (m *mockRouter) DelAddr(addr netip.Prefix) error {
args := m.Called(addr)
return args.Error(0)
Expand All @@ -170,24 +164,6 @@ func (m *mockRouter) DelRoute(dst netip.Prefix) error {
return args.Error(0)
}

func (m *mockRouter) ListRoutes() ([]router.TunnelRoute, error) {
args := m.Called()
var routes []router.TunnelRoute
if v := args.Get(0); v != nil {
routes = v.([]router.TunnelRoute)
}
return routes, args.Error(1)
}

func (m *mockRouter) LocalAddresses() ([]netip.Prefix, error) {
args := m.Called()
var addrs []netip.Prefix
if v := args.Get(0); v != nil {
addrs = v.([]netip.Prefix)
}
return addrs, args.Error(1)
}

func (m *mockRouter) Close() error {
args := m.Called()
return args.Error(0)
Expand Down
132 changes: 0 additions & 132 deletions pkg/tunnel/adapter/connection.go

This file was deleted.

Loading