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
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ require (
github.com/getsavvyinc/upgrade-cli v0.3.0
github.com/getsentry/sentry-go v0.26.0
github.com/go-logr/logr v1.4.3
github.com/go-logr/stdr v1.2.2
github.com/goccy/go-json v0.9.11
github.com/golang-jwt/jwt v3.2.2+incompatible
github.com/golang-jwt/jwt/v5 v5.3.0
Expand Down Expand Up @@ -60,6 +61,7 @@ require (
github.com/quic-go/quic-go v0.50.1
github.com/shirou/gopsutil v3.21.11+incompatible
github.com/sirupsen/logrus v1.9.3
github.com/slavc/xdp v0.3.4
github.com/spf13/cobra v1.9.1
github.com/stretchr/testify v1.10.0
github.com/telepresenceio/watchable v0.0.0-20220726211108-9bb86f92afa7
Expand Down Expand Up @@ -180,7 +182,6 @@ require (
github.com/ghodss/yaml v1.0.0 // indirect
github.com/go-faster/city v1.0.1 // indirect
github.com/go-faster/errors v0.7.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-logr/zapr v1.3.0 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-openapi/jsonpointer v0.21.1 // indirect
Expand Down Expand Up @@ -305,6 +306,7 @@ require (
github.com/robfig/cron v1.2.0 // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/safchain/ethtool v0.6.1 // indirect
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect
github.com/seccomp/libseccomp-golang v0.10.0 // indirect
github.com/segmentio/asm v1.2.0 // indirect
Expand Down
14 changes: 12 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,6 @@ 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.1 h1:1uEvkyc2+IYHTvn8FMv/sbCOr8poJiQNmWLudGBuguY=
github.com/apoxy-dev/icx v0.7.1/go.mod h1:Muuk3bRXTp3YB5Xj+xHOGQ/T1xVxIKJuvmMfLBXhIN4=
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/quic-go v0.0.0-20250530165952-53cca597715e h1:10GIpiVyKoRgCyr0J2TvJtdn17bsFHN+ROWkeVJpcOU=
Expand Down Expand Up @@ -200,6 +198,7 @@ github.com/checkpoint-restore/go-criu/v6 v6.3.0/go.mod h1:rrRTN/uSwY2X+BPRl/gkul
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
github.com/cilium/ebpf v0.18.0 h1:OsSwqS4y+gQHxaKgg2U/+Fev834kdnsQbtzRnbVC6Gs=
github.com/cilium/ebpf v0.18.0/go.mod h1:vmsAT73y4lW2b4peE+qcOqw6MxvWQdC+LiU5gd/xyo4=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
Expand Down Expand Up @@ -368,6 +367,7 @@ github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk=
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
Expand Down Expand Up @@ -732,6 +732,7 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
Expand Down Expand Up @@ -800,6 +801,7 @@ github.com/metal-stack/go-ipam v1.14.12/go.mod h1:B6R3ADxm1r5C1DJafhI90oB3+DRRby
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/miekg/dns v1.1.45/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY=
github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs=
Expand Down Expand Up @@ -1004,6 +1006,8 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/safchain/ethtool v0.6.1 h1:mhRnXE1H8fV8TTXh/HdqE4tXtb57r//BQh5pPYMuM5k=
github.com/safchain/ethtool v0.6.1/go.mod h1:JzoNbG8xeg/BeVeVoMCtCb3UPWoppZZbFpA+1WFh+M0=
github.com/samuel/go-thrift v0.0.0-20190219015601-e8b6b52668fe/go.mod h1:Vrkh1pnjV9Bl8c3P9zH0/D4NlOHWP5d4/hF4YTULaec=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
Expand Down Expand Up @@ -1050,6 +1054,8 @@ github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/slavc/xdp v0.3.4 h1:UFvt36LBz0pIqeRairAo4IP/sDWQ7mgT8LJRuF3MS8M=
github.com/slavc/xdp v0.3.4/go.mod h1:+pr19iCFDgI8wBXh5MUQftx5UuLlKaF7UvDAFWZq0zw=
github.com/softlayer/softlayer-go v0.0.0-20180806151055-260589d94c7d h1:bVQRCxQvfjNUeRqaY/uT0tFuvuFY0ulgnczuR684Xic=
github.com/softlayer/softlayer-go v0.0.0-20180806151055-260589d94c7d/go.mod h1:Cw4GTlQccdRGSEf6KiMju767x0NEHE0YIVPJSaXjlsw=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
Expand Down Expand Up @@ -1168,8 +1174,10 @@ github.com/vbatts/tar-split v0.11.3/go.mod h1:9QlHN18E+fEH7RdG+QAJJcuya3rqT7eXST
github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
github.com/vishvananda/netlink v1.3.1 h1:3AEMt62VKqz90r0tmNhog0r/PpWKmrEShJU0wJW6bV0=
github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4=
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY=
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI=
Expand Down Expand Up @@ -1439,6 +1447,7 @@ golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
Expand Down Expand Up @@ -1541,6 +1550,7 @@ golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
Expand Down
1 change: 1 addition & 0 deletions pkg/cmd/alpha/alpha.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ func Cmd() *cobra.Command {

func init() {
alphaCmd.AddCommand(rateLimitCmd)
alphaCmd.AddCommand(tunnelCmd)
}
215 changes: 215 additions & 0 deletions pkg/cmd/alpha/tunnel.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
package alpha

import (
"context"
"crypto/tls"
"fmt"
"log/slog"
"net"
"net/netip"
"net/url"
"time"

"github.com/dpeckett/network"
"github.com/spf13/cobra"
"golang.org/x/sync/errgroup"

"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/router"
)

var (
agentName string
tunnelName string
relayAddr string
token string
insecureSkipVerify bool
socksListenAddr string
pcapPath string
)

var tunnelCmd = &cobra.Command{
Use: "tunnel",
Short: "Manage tunnels",
Long: "Manage icx tunnels and connect to the remote Apoxy Edge fabric.",
}

var tunnelRunCmd = &cobra.Command{
Use: "run",
Short: "Run a tunnel",
Long: "Create a secure tunnel to the remote Apoxy Edge fabric.",
RunE: func(cmd *cobra.Command, args []string) error {
pc, err := net.ListenPacket("udp", ":0")
if err != nil {
return fmt.Errorf("failed to create UDP socket: %w", err)
}
defer pc.Close()

pcGeneve, pcQuic := bifurcate.Bifurcate(pc)
defer pcGeneve.Close()
defer pcQuic.Close()

apiURL := url.URL{
Scheme: "https",
Host: relayAddr,
}
if insecureSkipVerify {
apiURL.Scheme = "http"
}

tlsConf := &tls.Config{
InsecureSkipVerify: insecureSkipVerify,
}

client, err := api.NewClient(api.ClientOptions{
BaseURL: apiURL.String(),
Agent: agentName,
TunnelName: tunnelName,
Token: token,
TLSConfig: tlsConf,
})
if err != nil {
return fmt.Errorf("failed to create tunnel API client: %w", err)
}
defer client.Close()

connectResp, err := client.Connect(cmd.Context())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where do we handle redundant connections? Inside client?

Copy link
Collaborator Author

@dpeckett dpeckett Sep 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You know I'd totally overlooked this, I had this broken mental model that one process -> one connection. But looking at the QUIC code I realize this was incorrect.

Thankfully icx internally has support for cryptokey routing (ala allowedips) so supporting multiple redundant connections for a single handler is really straight forward (just register a new virtual network for each with the assigned ip ranges).

Thankfully no need for seperate muxers.

if err != nil {
return fmt.Errorf("failed to connect to tunnel relay: %w", err)
}

// Ensure we disconnect when the command exits
defer func() {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)

err := client.Disconnect(ctx, connectResp.ID)
cancel()
if err != nil {
slog.Error("Failed to disconnect from tunnel", slog.Any("error", err))
}
}()

slog.Info("Connected to tunnel relay", slog.String("id", connectResp.ID),
slog.Int("vni", int(connectResp.VNI)), slog.Int("mtu", connectResp.MTU))

var routerOpts []router.Option

if connectResp.DNS != nil {
resolveConf := &network.ResolveConfig{
Nameservers: connectResp.DNS.Servers,
SearchDomains: connectResp.DNS.SearchDomains,
NDots: connectResp.DNS.NDots,
}
routerOpts = append(routerOpts, router.WithResolveConfig(resolveConf))
}

if socksListenAddr != "" {
routerOpts = append(routerOpts, router.WithSocksListenAddr(socksListenAddr))
}

if pcapPath != "" {
routerOpts = append(routerOpts, router.WithPcapPath(pcapPath))
}

r, err := router.NewICXNetstackRouter(pcGeneve, connectResp.MTU, routerOpts...)
if err != nil {
return fmt.Errorf("failed to create ICX netstack router: %w", err)
}
defer r.Close()

remoteAddr, err := netip.ParseAddrPort(relayAddr)
if err != nil {
return fmt.Errorf("failed to parse relay address: %w", err)
}

overlayAddrs, err := stringsToPrefixes(connectResp.Addresses)
if err != nil {
return fmt.Errorf("failed to parse assigned addresses: %w", err)
}

if err := r.Handler.AddVirtualNetwork(connectResp.VNI, netstack.ToFullAddress(remoteAddr), overlayAddrs); err != nil {
return fmt.Errorf("failed to add virtual network to ICX handler: %w", err)
}

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

g.Go(func() error {
// Rotate keys at half-life; retry with a short backoff on failure.
apply := func(k api.Keys) time.Duration {
// Apply new keys to the ICX handler.
if err := r.Handler.UpdateVirtualNetworkKeys(connectResp.VNI, k.Epoch, k.Recv, k.Send, k.ExpiresAt); err != nil {
slog.Error("Failed to apply new keys to router", slog.Any("error", err))
}

// Compute next refresh: half of remaining lifetime.
remaining := time.Until(k.ExpiresAt)
next := remaining / 2
// Clamp to a sensible minimum to avoid tight loops.
if next < 10*time.Second {
next = 10 * time.Second
}
return next
}

// Seed initial schedule from the keys we got on Connect.
next := apply(connectResp.Keys)

timer := time.NewTimer(next)
defer timer.Stop()

for {
select {
case <-ctx.Done():
return ctx.Err()
case <-timer.C:
// Try to rotate keys.
upd, err := client.UpdateKeys(ctx, connectResp.ID)
if err != nil {
slog.Warn("Key update failed; retrying soon", slog.Any("error", err))
timer.Reset(5 * time.Second)
continue
}

slog.Info("Rotated tunnel keys", slog.Uint64("epoch", uint64(upd.Keys.Epoch)))
timer.Reset(apply(upd.Keys))
}
}
})

g.Go(func() error {
return r.Start(ctx)
})

return g.Wait()
},
}

func init() {
tunnelRunCmd.Flags().StringVarP(&agentName, "agent", "a", "", "The name of this agent.")
tunnelRunCmd.Flags().StringVarP(&tunnelName, "name", "n", "", "The name of the tunnel to connect to.")
tunnelRunCmd.Flags().StringVarP(&relayAddr, "relay-addr", "r", "", "The address of the tunnel relay to connect to.")
tunnelRunCmd.Flags().StringVarP(&token, "token", "k", "", "The token to use for authenticating with the tunnel relay.")
tunnelRunCmd.Flags().BoolVar(&insecureSkipVerify, "insecure-skip-verify", false, "Skip TLS certificate verification.")
tunnelRunCmd.Flags().StringVarP(&pcapPath, "pcap", "p", "", "Path to an optional packet capture file to write.")
tunnelRunCmd.Flags().StringVar(&socksListenAddr, "socks-addr", "localhost:1080", "Listen address for SOCKS proxy.")
cobra.CheckErr(tunnelRunCmd.MarkFlagRequired("agent"))
cobra.CheckErr(tunnelRunCmd.MarkFlagRequired("name"))
cobra.CheckErr(tunnelRunCmd.MarkFlagRequired("relay-addr"))
cobra.CheckErr(tunnelRunCmd.MarkFlagRequired("token"))

tunnelCmd.AddCommand(tunnelRunCmd)
}

func stringsToPrefixes(addrs []string) ([]netip.Prefix, error) {
prefixes := make([]netip.Prefix, 0, len(addrs))
for _, addr := range addrs {
p, err := netip.ParsePrefix(addr)
if err != nil {
return nil, fmt.Errorf("failed to parse address %q: %w", addr, err)
}
prefixes = append(prefixes, p)
}
return prefixes, nil
}
25 changes: 22 additions & 3 deletions pkg/netstack/icx_network.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ type ICXNetwork struct {
// NewICXNetwork creates a new ICXNetwork instance with the given handler, physical connection, MTU, and resolve configuration.
// If pcapPath is provided, it will create a packet sniffer that writes to the specified file.
// The handler must be configured in layer3 mode.
func NewICXNetwork(handler *icx.Handler, phy *l2pc.L2PacketConn, pathMTU int, resolveConf *network.ResolveConfig, pcapPath string) (*ICXNetwork, error) {
func NewICXNetwork(handler *icx.Handler, phy *l2pc.L2PacketConn, mtu int, resolveConf *network.ResolveConfig, pcapPath string) (*ICXNetwork, error) {
ipt := newIPTables()
opts := stack.Options{
NetworkProtocols: []stack.NetworkProtocolFactory{
Expand Down Expand Up @@ -79,7 +79,7 @@ func NewICXNetwork(handler *icx.Handler, phy *l2pc.L2PacketConn, pathMTU int, re
}

nicID := ipstack.NextNICID()
linkEP := channel.New(4096, uint32(icx.MTU(pathMTU)), "")
linkEP := channel.New(4096, uint32(mtu), "")
var nicEP stack.LinkEndpoint = linkEP

var pcapFile *os.File
Expand Down Expand Up @@ -116,7 +116,7 @@ func NewICXNetwork(handler *icx.Handler, phy *l2pc.L2PacketConn, pathMTU int, re
incomingPacket: make(chan *buffer.View),
pktPool: sync.Pool{
New: func() any {
b := make([]byte, 0, pathMTU)
b := make([]byte, 0, 65535)
return &b
},
},
Expand Down Expand Up @@ -291,6 +291,21 @@ func (net *ICXNetwork) DelAddr(addr netip.Prefix) error {
return nil
}

// LocalAddresses returns the list of local addresses assigned to the network.
func (net *ICXNetwork) LocalAddresses() ([]netip.Prefix, error) {
nic := net.stack.NICInfo()[net.nicID]

var addrs []netip.Prefix
for _, assignedAddr := range nic.ProtocolAddresses {
addrs = append(addrs, netip.PrefixFrom(
addrFromNetstackIP(assignedAddr.AddressWithPrefix.Address),
assignedAddr.AddressWithPrefix.PrefixLen,
))
}

return addrs, nil
}

// ForwardTo forwards all inbound TCP traffic to the upstream network.
func (net *ICXNetwork) ForwardTo(ctx context.Context, upstream network.Network) error {
// Allow outgoing packets to have a source address different from the NIC.
Expand All @@ -305,5 +320,9 @@ func (net *ICXNetwork) ForwardTo(ctx context.Context, upstream network.Network)

tcpForwarder := TCPForwarder(ctx, net.stack, upstream)
net.stack.SetTransportProtocolHandler(tcp.ProtocolNumber, tcpForwarder)

udpForwarder := UDPForwarder(ctx, net.stack, upstream)
net.stack.SetTransportProtocolHandler(udp.ProtocolNumber, udpForwarder)

return nil
}
Loading