Skip to content

Commit fcb8daa

Browse files
committed
netstack: make seamless endpoint swappable
1 parent 900829a commit fcb8daa

3 files changed

Lines changed: 218 additions & 117 deletions

File tree

intra/netstack/fdbased.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ import (
4848

4949
var _ stack.InjectableLinkEndpoint = (*endpoint)(nil)
5050
var _ stack.LinkEndpoint = (*endpoint)(nil)
51-
var _ stack.LinkEndpoint = (*linkFdSwap)(nil)
52-
var _ FdSwapper = (*linkFdSwap)(nil)
51+
var _ stack.LinkEndpoint = (*linkSwap)(nil)
52+
var _ FdSwapper = (*linkSwap)(nil)
5353

5454
// placeholder FD for whenever existing FD wrapped in struct fds is closed.
5555
const invalidfd int = -1

intra/netstack/netstack.go

Lines changed: 0 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,7 @@ package netstack
99
import (
1010
"errors"
1111
"fmt"
12-
"io"
1312
"net/netip"
14-
"strconv"
15-
"strings"
16-
"sync"
17-
"syscall"
1813

1914
"github.com/celzero/firestack/intra/core"
2015
"github.com/celzero/firestack/intra/log"
@@ -32,116 +27,6 @@ import (
3227
// enable forwarding of packets on the interface
3328
const nicfwd = false
3429

35-
// SnapLen is the maximum bytes of a packet to be saved. Packets with a length
36-
// less than or equal to snapLen will be saved in their entirety. Longer
37-
// packets will be truncated to snapLen.
38-
const SnapLen uint32 = 2048 // in bytes; some sufficient value
39-
40-
var (
41-
errNoFdSwapper = errors.New("linkFdSwap: no FdSwapper")
42-
)
43-
44-
type linkFdSwap struct {
45-
sync.Mutex
46-
stack.LinkEndpoint
47-
FdSwapper
48-
}
49-
50-
// Swap implements FdSwapper.
51-
func (l *linkFdSwap) Swap(fd int) error {
52-
l.Lock()
53-
defer l.Unlock()
54-
55-
if l.FdSwapper == nil {
56-
return errNoFdSwapper
57-
}
58-
59-
err := l.FdSwapper.Swap(fd)
60-
if errors.Is(err, errNeedsNewEndpoint) {
61-
umtu := uint32(l.MTU())
62-
opt := Options{
63-
FDs: []int{fd},
64-
MTU: umtu,
65-
}
66-
core.Go("linkFdSwap."+strconv.Itoa(fd), l.LinkEndpoint.Close)
67-
l.LinkEndpoint, err = newFdbasedInjectableEndpoint(&opt)
68-
}
69-
70-
return err
71-
}
72-
73-
// ref: github.com/google/gvisor/blob/91f58d2cc/pkg/tcpip/sample/tun_tcp_echo/main.go#L102
74-
func NewEndpoint(dev, mtu int, sink io.WriteCloser) (ep SeamlessEndpoint, err error) {
75-
defer func() {
76-
if err != nil {
77-
_ = syscall.Close(dev)
78-
}
79-
log.I("netstack: new endpoint(fd:%d / mtu:%d); err? %v", dev, mtu, err)
80-
}()
81-
82-
umtu := uint32(mtu)
83-
opt := Options{
84-
FDs: []int{dev},
85-
MTU: umtu,
86-
}
87-
88-
if ep, err = newFdbasedInjectableEndpoint(&opt); err != nil {
89-
return nil, err
90-
}
91-
// ref: github.com/google/gvisor/blob/aeabb785278/pkg/tcpip/link/sniffer/sniffer.go#L111-L131
92-
return snoop(ep, sink)
93-
}
94-
95-
func snoop(ep SeamlessEndpoint, sink io.WriteCloser) (SeamlessEndpoint, error) {
96-
if sink == nil {
97-
return ep, nil
98-
}
99-
// TODO: MTU instead of SnapLen? Must match pcapsink.begin()
100-
if link, err := NewSnoopyEndpoint(ep, sink); err != nil {
101-
return nil, err
102-
} else {
103-
return &linkFdSwap{sync.Mutex{}, link, ep}, nil
104-
}
105-
}
106-
107-
func Pcap2Stdout(y bool) (ok bool) {
108-
if y {
109-
ok = LogPackets.CompareAndSwap(0, 1)
110-
} else {
111-
ok = LogPackets.CompareAndSwap(1, 0)
112-
}
113-
log.I("netstack: pcap stdout(%t): done?(%t)", y, ok)
114-
return
115-
}
116-
117-
func Pcap2File(y bool) (ok bool) {
118-
if y {
119-
ok = WritePCAP.CompareAndSwap(0, 1)
120-
} else {
121-
ok = WritePCAP.CompareAndSwap(1, 0)
122-
}
123-
log.I("netstack: pcap file(%t): done?(%t)", y, ok)
124-
return
125-
}
126-
127-
// PCAP logging modes:
128-
// - stdout: packets are logged to stdout
129-
// - file: packets are logged to a file
130-
// - none: no packets are logged
131-
func PcapModes() string {
132-
var modes []string
133-
if LogPackets.Load() == 1 {
134-
modes = append(modes, "stdout")
135-
}
136-
if WritePCAP.Load() == 1 {
137-
modes = append(modes, "file")
138-
}
139-
if len(modes) == 0 {
140-
return "none"
141-
}
142-
return strings.Join(modes, ",")
143-
}
144-
14530
// ref: github.com/brewlin/net-protocol/blob/ec64e5f899/internal/endpoint/endpoint.go#L20
14631
func Up(who string, s *stack.Stack, ep stack.LinkEndpoint, h GConnHandler) (tcpip.NICID, error) {
14732
nic := tcpip.NICID(settings.NICID)

intra/netstack/seamless.go

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
package netstack
2+
3+
import (
4+
"errors"
5+
"io"
6+
"strconv"
7+
"strings"
8+
"sync"
9+
"syscall"
10+
11+
"github.com/celzero/firestack/intra/core"
12+
"github.com/celzero/firestack/intra/log"
13+
"gvisor.dev/gvisor/pkg/tcpip"
14+
"gvisor.dev/gvisor/pkg/tcpip/header"
15+
"gvisor.dev/gvisor/pkg/tcpip/stack"
16+
)
17+
18+
// SnapLen is the maximum bytes of a packet to be saved. Packets with a length
19+
// less than or equal to snapLen will be saved in their entirety. Longer
20+
// packets will be truncated to snapLen.
21+
const SnapLen uint32 = 2048 // in bytes; some sufficient value
22+
23+
var (
24+
errNoFdSwapper = errors.New("linkFdSwap: no FdSwapper")
25+
)
26+
27+
type linkSwap struct {
28+
sync.Mutex
29+
e stack.LinkEndpoint
30+
FdSwapper
31+
}
32+
33+
// ref: github.com/google/gvisor/blob/91f58d2cc/pkg/tcpip/sample/tun_tcp_echo/main.go#L102
34+
func NewEndpoint(dev, mtu int, sink io.WriteCloser) (ep SeamlessEndpoint, err error) {
35+
defer func() {
36+
if err != nil {
37+
_ = syscall.Close(dev)
38+
}
39+
log.I("netstack: new endpoint(fd:%d / mtu:%d); err? %v", dev, mtu, err)
40+
}()
41+
42+
umtu := uint32(mtu)
43+
opt := Options{
44+
FDs: []int{dev},
45+
MTU: umtu,
46+
}
47+
48+
if ep, err = newFdbasedInjectableEndpoint(&opt); err != nil {
49+
return nil, err
50+
}
51+
// ref: github.com/google/gvisor/blob/aeabb785278/pkg/tcpip/link/sniffer/sniffer.go#L111-L131
52+
return snoop(ep, sink)
53+
}
54+
55+
func snoop(ep SeamlessEndpoint, sink io.WriteCloser) (SeamlessEndpoint, error) {
56+
if sink == nil {
57+
return ep, nil
58+
}
59+
// TODO: MTU instead of SnapLen? Must match pcapsink.begin()
60+
if link, err := NewSnoopyEndpoint(ep, sink); err != nil {
61+
return nil, err
62+
} else {
63+
return &linkSwap{sync.Mutex{}, link, ep}, nil
64+
}
65+
}
66+
67+
func Pcap2Stdout(y bool) (ok bool) {
68+
if y {
69+
ok = LogPackets.CompareAndSwap(0, 1)
70+
} else {
71+
ok = LogPackets.CompareAndSwap(1, 0)
72+
}
73+
log.I("netstack: pcap stdout(%t): done?(%t)", y, ok)
74+
return
75+
}
76+
77+
func Pcap2File(y bool) (ok bool) {
78+
if y {
79+
ok = WritePCAP.CompareAndSwap(0, 1)
80+
} else {
81+
ok = WritePCAP.CompareAndSwap(1, 0)
82+
}
83+
log.I("netstack: pcap file(%t): done?(%t)", y, ok)
84+
return
85+
}
86+
87+
// PCAP logging modes:
88+
// - stdout: packets are logged to stdout
89+
// - file: packets are logged to a file
90+
// - none: no packets are logged
91+
func PcapModes() string {
92+
var modes []string
93+
if LogPackets.Load() == 1 {
94+
modes = append(modes, "stdout")
95+
}
96+
if WritePCAP.Load() == 1 {
97+
modes = append(modes, "file")
98+
}
99+
if len(modes) == 0 {
100+
return "none"
101+
}
102+
return strings.Join(modes, ",")
103+
}
104+
105+
// Swap implements FdSwapper.
106+
func (l *linkSwap) Swap(fd int) error {
107+
l.Lock()
108+
defer l.Unlock()
109+
110+
if l.FdSwapper == nil {
111+
return errNoFdSwapper
112+
}
113+
114+
err := l.FdSwapper.Swap(fd)
115+
if errors.Is(err, errNeedsNewEndpoint) {
116+
umtu := uint32(l.e.MTU())
117+
opt := Options{
118+
FDs: []int{fd},
119+
MTU: umtu,
120+
}
121+
core.Go("linkFdSwap."+strconv.Itoa(fd), l.e.Close)
122+
l.e, err = newFdbasedInjectableEndpoint(&opt)
123+
}
124+
125+
return err
126+
}
127+
128+
func (l *linkSwap) MTU() uint32 {
129+
l.Lock()
130+
defer l.Unlock()
131+
return l.e.MTU()
132+
}
133+
134+
func (l *linkSwap) SetMTU(mtu uint32) {
135+
l.Lock()
136+
defer l.Unlock()
137+
l.e.SetMTU(mtu)
138+
}
139+
140+
func (l *linkSwap) MaxHeaderLength() uint16 {
141+
l.Lock()
142+
defer l.Unlock()
143+
return l.e.MaxHeaderLength()
144+
}
145+
146+
func (l *linkSwap) LinkAddress() tcpip.LinkAddress {
147+
l.Lock()
148+
defer l.Unlock()
149+
return l.e.LinkAddress()
150+
}
151+
152+
func (l *linkSwap) SetLinkAddress(addr tcpip.LinkAddress) {
153+
l.Lock()
154+
defer l.Unlock()
155+
l.e.SetLinkAddress(addr)
156+
}
157+
158+
func (l *linkSwap) Capabilities() stack.LinkEndpointCapabilities {
159+
l.Lock()
160+
defer l.Unlock()
161+
return l.e.Capabilities()
162+
}
163+
164+
func (l *linkSwap) Attach(dispatcher stack.NetworkDispatcher) {
165+
l.Lock()
166+
defer l.Unlock()
167+
l.e.Attach(dispatcher)
168+
}
169+
170+
func (l *linkSwap) IsAttached() bool {
171+
l.Lock()
172+
defer l.Unlock()
173+
return l.e.IsAttached()
174+
}
175+
176+
func (l *linkSwap) WritePackets(pkts stack.PacketBufferList) (int, tcpip.Error) {
177+
l.Lock()
178+
defer l.Unlock()
179+
return l.e.WritePackets(pkts)
180+
}
181+
182+
func (l *linkSwap) Wait() {
183+
l.Lock()
184+
defer l.Unlock()
185+
l.e.Wait()
186+
}
187+
188+
func (l *linkSwap) ARPHardwareType() header.ARPHardwareType {
189+
l.Lock()
190+
defer l.Unlock()
191+
return l.e.ARPHardwareType()
192+
}
193+
194+
func (l *linkSwap) AddHeader(pkt *stack.PacketBuffer) {
195+
l.Lock()
196+
defer l.Unlock()
197+
l.e.AddHeader(pkt)
198+
}
199+
200+
func (l *linkSwap) ParseHeader(pkt *stack.PacketBuffer) bool {
201+
l.Lock()
202+
defer l.Unlock()
203+
return l.e.ParseHeader(pkt)
204+
}
205+
206+
func (l *linkSwap) Close() {
207+
l.Lock()
208+
defer l.Unlock()
209+
l.e.Close()
210+
}
211+
212+
func (l *linkSwap) SetOnCloseAction(f func()) {
213+
l.Lock()
214+
defer l.Unlock()
215+
l.e.SetOnCloseAction(f)
216+
}

0 commit comments

Comments
 (0)