Skip to content

Commit 4e975cc

Browse files
committed
intra/tunnel: hide actual tunnel impl from client
With the Restart() impl, the underlying tunnel can be swapped out for a new one in which case the client would have to access it in a thread-safe way over a mutex, say, but it can't since the underlying tunnel's exposed as a top-level construct by intra/tunnel. Also, move to core.Volatile over rw mutexes for the underlying tun and remove a bunch of obsolete funcs that are not longer in use.
1 parent ae9dbd8 commit 4e975cc

2 files changed

Lines changed: 88 additions & 59 deletions

File tree

intra/tunnel.go

Lines changed: 73 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -88,24 +88,26 @@ type Tunnel interface {
8888
// Get local services.
8989
GetServices() (x.Services, error)
9090

91-
// Sets new default routes for the given engine, where engine is
92-
// one of the constants (Ns4, Ns6, Ns46) defined in package settings.
93-
SetRoute(engine int) error
9491
// SetLinkAndRoutes sets the tun fd as link with mtu & engine as routes for the tunnel.
92+
// where engine is one of the constants (Ns4, Ns6, Ns46) defined in package settings.
9593
SetLinkAndRoutes(fd, mtu, engine int) error
9694
// Restart restarts the tunnel with the given fd, mtu, and engine.
9795
Restart(fd, mtu, engine int) error
9896

97+
// Close connections by pid, cid, uid.
98+
CloseConns(activecsv string) (closedcsv string)
99+
99100
// Sets pcap output to fpcap which is the absolute filepath
100101
// to which a PCAP file will be written to.
101102
// If len(fpcap) is 0, no PCAP file will be written.
102103
// If len(fpcap) is 1, PCAP be written to stdout.
103104
SetPcap(fpcap string) error
105+
// NIC, IP, TCP, UDP, and ICMP stats.
106+
Stat() (*x.NetStat, error)
104107
}
105108

106109
type rtunnel struct {
107-
tunnel.Tunnel
108-
tunmu sync.Mutex // serializes access to tunnel.Tunnel
110+
t *core.Volatile[tunnel.Tunnel]
109111
ctx context.Context
110112
done context.CancelFunc
111113
handlers netstack.GConnHandler
@@ -116,6 +118,20 @@ type rtunnel struct {
116118
once sync.Once
117119
}
118120

121+
var _ Tunnel = (*rtunnel)(nil)
122+
123+
type clogAdapter struct {
124+
b Bridge
125+
}
126+
127+
var _ log.Console = (*clogAdapter)(nil)
128+
129+
func (l *clogAdapter) Log(lvl log.LogLevel, msg log.Logmsg) {
130+
if bdg := l.b; bdg != nil {
131+
bdg.Log(int32(lvl), x.StrOf(msg)) // adopt the log message
132+
}
133+
}
134+
119135
func NewTunnel(fd, mtu int, fakedns string, dtr DefaultDNS, bdg Bridge) (t Tunnel, err error) {
120136
defer core.Recover(core.Exit11, "i.newTunnel")
121137

@@ -198,7 +214,7 @@ func NewTunnel(fd, mtu int, fakedns string, dtr DefaultDNS, bdg Bridge) (t Tunne
198214
rerr := proxies.Reverser(revhdl)
199215

200216
t = &rtunnel{
201-
Tunnel: gt,
217+
t: core.NewVolatile[tunnel.Tunnel](gt),
202218
ctx: ctx,
203219
done: cancel,
204220
handlers: hdl,
@@ -231,15 +247,15 @@ func (t *rtunnel) SetLinkAndRoutes(fd, mtu, engine int) error {
231247
return errClosed
232248
}
233249

234-
t.tunmu.Lock()
235-
defer t.tunmu.Unlock()
250+
tunnel := t.t.Load()
236251

237-
mtudiff := t.Tunnel.Mtu() != int32(mtu)
252+
mtudiff := tunnel.Mtu() != int32(mtu)
238253
l3 := settings.L3(engine)
239254
l3diff := dialers.IPProtos(l3)
240255

241-
err := t.Tunnel.SetLink(fd, mtu) // route is always dual-stack
256+
err := tunnel.SetLinkAndRoutes(fd, mtu, engine) // route is always dual-stack
242257

258+
// TODO: skip refresh on err?
243259
core.Gx("i.setLinkAndRoutesRefresh", func() {
244260
if l3diff || mtudiff {
245261
// dialers.IPProtos must always preced calls to other refreshes
@@ -275,16 +291,16 @@ func (t *rtunnel) Restart(fd, mtu, engine int) error {
275291

276292
gt, revhdl, err := tunnel.NewGTunnel(t.ctx, fd, mtu, l3, t.handlers)
277293

278-
if gt == nil || err != nil {
279-
log.W("tun: <<< restart >>>; err(%v)", err)
280-
t.Tunnel.Disconnect()
294+
old := t.t.Load()
295+
old.Disconnect() // may hve been disconnected already
296+
297+
if err != nil || gt == nil || core.IsNil(gt) {
298+
log.W("tun: <<< restart >>>; new? %t; err(%v)", gt != nil, err)
281299
return core.OneErr(err, errMakeTunnel)
282300
}
283301

284-
t.tunmu.Lock()
285-
t.Tunnel.Disconnect()
286-
t.Tunnel = gt
287-
t.tunmu.Unlock()
302+
// TODO: CompareAndSwap
303+
t.t.Store(gt) // gt never nil
288304

289305
// TODO: err on reverser errors too?
290306
rerr := t.proxies.Reverser(revhdl)
@@ -365,9 +381,9 @@ func (t *rtunnel) Stat() (*x.NetStat, error) {
365381
}
366382

367383
func (t *rtunnel) stat() (*x.NetStat, error) {
368-
t.tunmu.Lock()
369-
out, err := t.Tunnel.Stat()
370-
t.tunmu.Unlock()
384+
tunnel := t.t.Load()
385+
386+
out, err := tunnel.Stat()
371387

372388
if err != nil {
373389
return nil, err
@@ -455,6 +471,43 @@ func (t *rtunnel) stat() (*x.NetStat, error) {
455471
return out, nil
456472
}
457473

474+
// CloseConns implements Tunnel.
475+
func (t *rtunnel) CloseConns(activecsv string) (closedcsv string) {
476+
defer core.Recover(core.Exit11, "i.CloseConns")
477+
478+
return t.handlers.CloseConns(activecsv)
479+
}
480+
481+
// Enabled implements Tunnel.
482+
func (t *rtunnel) Enabled() bool {
483+
tunnel := t.t.Load()
484+
return tunnel.Enabled()
485+
}
486+
487+
// IsConnected implements Tunnel.
488+
func (t *rtunnel) IsConnected() bool {
489+
tunnel := t.t.Load()
490+
return tunnel.IsConnected()
491+
}
492+
493+
// Mtu implements Tunnel.
494+
func (t *rtunnel) Mtu() int32 {
495+
tunnel := t.t.Load()
496+
return tunnel.Mtu()
497+
}
498+
499+
// SetPcap implements Tunnel.
500+
func (t *rtunnel) SetPcap(fpcap string) error {
501+
tunnel := t.t.Load()
502+
return tunnel.SetPcap(fpcap)
503+
}
504+
505+
// Unlink implements Tunnel.
506+
func (t *rtunnel) Unlink() error {
507+
tunnel := t.t.Load()
508+
return tunnel.Unlink()
509+
}
510+
458511
func csv2ssv(csv string) string {
459512
return strings.ReplaceAll(csv, ",", ";")
460513
}
@@ -481,15 +534,3 @@ func fetchDNSInfo(r dnsx.Resolver, id string) string {
481534
return rerr.Error()
482535
}
483536
}
484-
485-
type clogAdapter struct {
486-
b Bridge
487-
}
488-
489-
var _ log.Console = (*clogAdapter)(nil)
490-
491-
func (l *clogAdapter) Log(lvl log.LogLevel, msg log.Logmsg) {
492-
if bdg := l.b; bdg != nil {
493-
bdg.Log(int32(lvl), x.StrOf(msg)) // adopt the log message
494-
}
495-
}

tunnel/tunnel.go

Lines changed: 15 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -54,14 +54,8 @@ type Tunnel interface {
5454
Disconnect()
5555
// Enabled checks if the tunnel is up and running.
5656
Enabled() bool
57-
// Write writes input data to the TUN interface.
58-
Write(data []byte) (int, error)
59-
// Close connections by pid, cid, uid.
60-
CloseConns(activecsv string) (closedcsv string)
6157
// Creates a new link using fd (tun device).
62-
SetLink(fd, mtu int) error
63-
// New route
64-
SetRoute(engine int) error
58+
SetLinkAndRoutes(fd, mtu, engine int) error
6559
// Unsets existing link and closes the fd (tun device).
6660
Unlink() error
6761
// Set or unset the pcap sink
@@ -72,6 +66,7 @@ type Tunnel interface {
7266

7367
type gtunnel struct {
7468
ctx context.Context
69+
done context.CancelFunc
7570
stack *stack.Stack // a tcpip stack
7671
ep netstack.SeamlessEndpoint // endpoint for the stack
7772
sid *core.Volatile[int] // session id (almost always tunnel fd)
@@ -116,6 +111,7 @@ func (t *gtunnel) waitForEndpoint(ctx context.Context) {
116111

117112
select {
118113
case <-ctx.Done():
114+
t.Disconnect() // may already be disconnected
119115
log.D("tun: waiter: ctx done; #%d", i)
120116
return
121117
case <-core.SigFin(t.ep.Wait): // wait until endpoint closes
@@ -169,11 +165,6 @@ func (t *gtunnel) IsConnected() bool {
169165
return !t.closed.Load()
170166
}
171167

172-
func (t *gtunnel) Write([]byte) (int, error) {
173-
// May be: t.endpoint.WritePackets()
174-
return 0, errNoWriter
175-
}
176-
177168
// fd must be non-blocking.
178169
func NewGTunnel(pctx context.Context, fd, mtu int, l3 string, hdl netstack.GConnHandler) (t *gtunnel, rev netstack.GConnHandler, err error) {
179170
dupfd, err := dup(fd) // tunnel will own dupfd
@@ -212,6 +203,7 @@ func NewGTunnel(pctx context.Context, fd, mtu int, l3 string, hdl netstack.GConn
212203

213204
t = &gtunnel{
214205
ctx: ctx,
206+
done: done,
215207
stack: stack,
216208
ep: ep,
217209
sid: core.NewVolatile(fd), // fd is the og tun device
@@ -221,20 +213,11 @@ func NewGTunnel(pctx context.Context, fd, mtu int, l3 string, hdl netstack.GConn
221213
once: sync.Once{},
222214
}
223215

224-
go func() {
225-
defer done()
226-
t.waitForEndpoint(ctx)
227-
}()
216+
go t.waitForEndpoint(ctx)
228217

229218
return
230219
}
231220

232-
func (t *gtunnel) CloseConns(activecsv string) (closedcsv string) {
233-
defer core.Recover(core.Exit11, "g.CloseConns")
234-
235-
return t.hdl.CloseConns(activecsv)
236-
}
237-
238221
func (t *gtunnel) SetPcap(fp string) error {
239222
defer core.Recover(core.Exit11, "g.SetPcap")
240223

@@ -265,9 +248,16 @@ func (t *gtunnel) Unlink() error {
265248
return t.ep.Dispose()
266249
}
267250

268-
func (t *gtunnel) SetLink(fd, mtu int) (err error) {
269-
defer core.Recover(core.Exit11, "g.SetLink")
251+
func (t *gtunnel) SetLinkAndRoutes(fd, mtu, engine int) (err error) {
252+
defer core.Recover(core.Exit11, "g.SetLinkAndRoutes")
270253

254+
if err := t.setLink(fd, mtu); err != nil {
255+
return err
256+
}
257+
return t.setRoute(engine)
258+
}
259+
260+
func (t *gtunnel) setLink(fd, mtu int) (err error) {
271261
defer func() {
272262
if err != nil {
273263
t.sid.Store(-1) // reset sid
@@ -288,9 +278,7 @@ func (t *gtunnel) SetLink(fd, mtu int) (err error) {
288278
return err
289279
}
290280

291-
func (t *gtunnel) SetRoute(engine int) error {
292-
defer core.Recover(core.Exit11, "g.SetRoute")
293-
281+
func (t *gtunnel) setRoute(engine int) error {
294282
// netstack route is never changed; always dual-stack
295283
netstack.Route(t.stack, settings.IP46)
296284
log.I("tun: new route; (no-op) got %s but set %s", settings.L3(engine), settings.IP46)

0 commit comments

Comments
 (0)