Skip to content

Commit aaef320

Browse files
committed
udp: endpoint-independent mapping mk1
1 parent 205c9ba commit aaef320

File tree

5 files changed

+275
-204
lines changed

5 files changed

+275
-204
lines changed

intra/netstack/hdl.go

+20
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@ import (
1313
"strings"
1414
"sync"
1515

16+
"github.com/celzero/firestack/intra/settings"
1617
"gvisor.dev/gvisor/pkg/tcpip"
18+
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
19+
"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
1720
"gvisor.dev/gvisor/pkg/tcpip/stack"
1821
)
1922

@@ -134,3 +137,20 @@ func nsaddr2ip(addr tcpip.Address) net.IP {
134137
b := addr.AsSlice()
135138
return net.IP(b)
136139
}
140+
141+
func addrport2nsaddr(ipp netip.AddrPort) (tcpip.FullAddress, tcpip.NetworkProtocolNumber) {
142+
var proto tcpip.NetworkProtocolNumber
143+
var addr tcpip.Address
144+
if ipp.Addr().Is4() {
145+
proto = ipv4.ProtocolNumber
146+
addr = tcpip.AddrFrom4(ipp.Addr().As4())
147+
} else {
148+
proto = ipv6.ProtocolNumber
149+
addr = tcpip.AddrFrom16(ipp.Addr().As16())
150+
}
151+
return tcpip.FullAddress{
152+
NIC: settings.NICID,
153+
Addr: addr,
154+
Port: ipp.Port(),
155+
}, proto
156+
}

intra/netstack/udp.go

+43-32
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import (
1515
"github.com/celzero/firestack/intra/core"
1616
"github.com/celzero/firestack/intra/log"
1717
"github.com/celzero/firestack/intra/settings"
18-
"gvisor.dev/gvisor/pkg/tcpip"
1918

2019
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
2120

@@ -30,7 +29,7 @@ type GUDPConnHandler interface {
3029
// Proxy proxies data between conn (src) and dst.
3130
Proxy(conn *GUDPConn, src, dst netip.AddrPort) bool
3231
// ProxyMux proxies data between conn and multiple destinations.
33-
ProxyMux(conn *GUDPConn, src netip.AddrPort) bool
32+
ProxyMux(conn *GUDPConn, src, dst netip.AddrPort) bool
3433
// Error notes the error in connecting src to dst.
3534
Error(conn *GUDPConn, src, dst netip.AddrPort, err error)
3635
// CloseConns closes conns by ids, or all if ids is empty.
@@ -42,21 +41,26 @@ type GUDPConnHandler interface {
4241
var _ core.UDPConn = (*GUDPConn)(nil)
4342

4443
type GUDPConn struct {
45-
c *core.Volatile[*gonet.UDPConn] // conn exposes UDP semantics atop endpoint
46-
ep *core.Volatile[tcpip.Endpoint] // ep is the endpoint for netstack io
47-
src netip.AddrPort // local addr (remote addr in netstack)
48-
dst netip.AddrPort // remote addr (local addr in netstack)
49-
req *udp.ForwarderRequest // egress request as UDP
44+
stack *stack.Stack
45+
c *core.Volatile[*gonet.UDPConn] // conn exposes UDP semantics atop endpoint
46+
src netip.AddrPort // local addr (remote addr in netstack)
47+
dst netip.AddrPort // remote addr (local addr in netstack)
48+
req *udp.ForwarderRequest // egress request as UDP
49+
50+
eim bool // endpoint is muxed
51+
eif bool // endpoint is transparent
5052
}
5153

5254
// ref: github.com/google/gvisor/blob/e89e736f1/pkg/tcpip/adapters/gonet/gonet_test.go#L373
53-
func makeGUDPConn(r *udp.ForwarderRequest, src, dst netip.AddrPort) *GUDPConn {
55+
func makeGUDPConn(s *stack.Stack, r *udp.ForwarderRequest, src, dst netip.AddrPort) *GUDPConn {
5456
return &GUDPConn{
55-
c: core.NewZeroVolatile[*gonet.UDPConn](),
56-
ep: core.NewZeroVolatile[tcpip.Endpoint](),
57-
src: src,
58-
dst: dst,
59-
req: r,
57+
stack: s,
58+
c: core.NewZeroVolatile[*gonet.UDPConn](),
59+
src: src,
60+
dst: dst,
61+
req: r,
62+
eim: settings.EndpointIndependentMapping.Load(),
63+
eif: settings.EndpointIndependentFiltering.Load(),
6064
}
6165
}
6266

@@ -90,11 +94,11 @@ func udpForwarder(s *stack.Stack, h GUDPConnHandler) *udp.Forwarder {
9094
// multiple dst in the unconnected udp case.
9195
dst := localAddrPort(id)
9296

93-
gc := makeGUDPConn(req, src, dst)
97+
gc := makeGUDPConn(s, req, src, dst)
9498
// setup to recv right away, so that netstack's internal state is consistent
9599
// in case there are multiple forwarders dispatching from the TUN device.
96100
if !settings.SingleThreaded.Load() {
97-
if err := gc.tryConnect(); err != nil {
101+
if err := gc.Establish(); err != nil {
98102
log.E("ns: udp: forwarder: connect: %v; src(%v) dst(%v)", err, src, dst)
99103
go h.Error(gc, src, dst, err)
100104
return
@@ -103,10 +107,10 @@ func udpForwarder(s *stack.Stack, h GUDPConnHandler) *udp.Forwarder {
103107

104108
// proxy in a separate gorountine; return immediately
105109
// why? netstack/dispatcher.go:newReadvDispatcher
106-
if gc.connected() { // gc is connected udp; proxy it like a stream
107-
go h.Proxy(gc, src, dst)
110+
if gc.eim {
111+
go h.ProxyMux(gc, src, dst)
108112
} else {
109-
go h.ProxyMux(gc, src)
113+
go h.Proxy(gc, src, dst)
110114
}
111115
})
112116
}
@@ -119,17 +123,16 @@ func (g *GUDPConn) conn() *gonet.UDPConn {
119123
return g.c.Load()
120124
}
121125

122-
func (g *GUDPConn) endpoint() tcpip.Endpoint {
123-
return g.ep.Load()
124-
}
125-
126126
func (g *GUDPConn) StatefulTeardown() (fin bool) {
127127
_ = g.tryConnect() // establish circuit then teardown
128128
_ = g.Close() // then shutdown
129129
return true // always fin
130130
}
131131

132-
func (g *GUDPConn) Connect() error {
132+
func (g *GUDPConn) Establish() error {
133+
if g.eif {
134+
return g.tryBind()
135+
}
133136
return g.tryConnect()
134137
}
135138

@@ -139,18 +142,33 @@ func (g *GUDPConn) tryConnect() error {
139142
}
140143

141144
wq := new(waiter.Queue)
142-
// use gonet.DialUDP instead?
143145
if endpoint, err := g.req.CreateEndpoint(wq); err != nil {
144146
// ex: CONNECT endpoint for [fd66:f83a:c650::1]:15753 => [fd66:f83a:c650::3]:53; err(no route to host)
145147
log.E("ns: udp: connect: endpoint for %v => %v; err(%v)", g.src, g.dst, err)
146148
return e(err)
147149
} else {
148-
g.ep.Store(endpoint)
149150
g.c.Store(gonet.NewUDPConn(wq, endpoint))
150151
}
151152
return nil
152153
}
153154

155+
func (g *GUDPConn) tryBind() error {
156+
if g.ok() { // already setup
157+
return nil
158+
}
159+
160+
src, proto := addrport2nsaddr(g.src)
161+
// unconnected socket w/ gonet.DialUDP
162+
if conn, err := gonet.DialUDP(g.stack, &src, nil, proto); err != nil {
163+
log.E("ns: udp: bind: endpoint for %v [=> %v]; err(%v)", g.src, g.dst, err)
164+
return err
165+
} else {
166+
// todo: handle the first pkt like in g.req.CreateEndpoint
167+
g.c.Store(conn)
168+
}
169+
return nil
170+
}
171+
154172
func (g *GUDPConn) LocalAddr() (addr net.Addr) {
155173
if c := g.conn(); c != nil {
156174
addr = c.RemoteAddr()
@@ -227,15 +245,8 @@ func (g *GUDPConn) SetWriteDeadline(t time.Time) error {
227245

228246
// Close closes the connection.
229247
func (g *GUDPConn) Close() error {
230-
if ep := g.endpoint(); ep != nil {
231-
ep.Abort()
232-
}
233248
if c := g.conn(); c != nil {
234249
_ = c.Close()
235250
}
236251
return nil
237252
}
238-
239-
func (g *GUDPConn) connected() bool {
240-
return !g.dst.Addr().IsUnspecified()
241-
}

intra/settings/config.go

+8
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,14 @@ var Loopingback = atomic.Bool{}
8383
// in a single-threaded mode.
8484
var SingleThreaded = atomic.Bool{}
8585

86+
// EndpointIndependentMapping is a global flag to enable endpoint-independent
87+
// mapping for UDP as per RFC 4787.
88+
var EndpointIndependentMapping = atomic.Bool{}
89+
90+
// EndpointIndependentFiltering is a global flag to enable endpoint-independent
91+
// filtering for UDP as per RFC 4787.
92+
var EndpointIndependentFiltering = atomic.Bool{}
93+
8694
// L3 returns the string'd repr of engine.
8795
func L3(engine int) string {
8896
switch engine {

0 commit comments

Comments
 (0)