-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathvni.go
211 lines (192 loc) · 6.13 KB
/
vni.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
package vni
import (
"errors"
"fmt"
"github.com/clarkmcc/remotenetstack/netstack"
"github.com/clarkmcc/remotenetstack/utils"
"go.uber.org/zap"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/link/channel"
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
"gvisor.dev/gvisor/pkg/tcpip/stack"
"gvisor.dev/gvisor/pkg/tcpip/transport/icmp"
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
"gvisor.dev/gvisor/pkg/tcpip/transport/udp"
"io"
"net/netip"
)
// defaultNicAddress is the address of the NIC in the virtual network interface. It's assigned arbitrarily
// because it doesn't actually matter (right?) since we're not interfacing with any other networking systems
var defaultNicAddress = tcpip.Address(netip.MustParseAddr("100.127.255.255").AsSlice())
var defaultGatewayAddress = tcpip.Address(netip.MustParseAddr("100.127.255.255").AsSlice())
// Mode determines how the Interface operates. In Entrance
// mode, the routes determine whether packets are forwarded to the Exit interface. In Exit mode,
// the routes determine what packets are allowed to be forwarded. We always route from
// entrance -> exit, not the other way around.
type Mode uint
const (
Entrance Mode = iota
Exit
)
func (m Mode) String() string {
switch m {
case Entrance:
return "entrance"
case Exit:
return "exit"
default:
return "unknown"
}
}
// Interface acts as a network interface that can be accessed remotely
type Interface struct {
logger *zap.Logger
Stack *stack.Stack // Userspace networking Stack
ep *channel.Endpoint // The internal netstack endpoint
epw *netstack.Endpoint // The wrapper around the netstack endpoint that allows us to read/write packets over arbitrary transports
routes []tcpip.Route // Routes that are exposed via this network interface
mode Mode // Determines how this interface operates
nicId tcpip.NICID // The ID of the network interface in the netstack
linkLayer io.ReadWriter
stopChan chan struct{}
}
type Config struct {
Logger *zap.Logger
Mode Mode // The mode that this network interface should operate under
LinkLayer io.ReadWriter // The linkLayer where packets are read/written
MTU uint32 // Maximum transmission unit
}
func New(config Config) (*Interface, error) {
if config.LinkLayer == nil {
return nil, errors.New("linkLayer cannot be nil")
}
if config.MTU == 0 {
config.MTU = 1500
}
if config.Logger == nil {
config.Logger = zap.NewNop()
}
logger := config.Logger.Named("vni").With(zap.String("mode", config.Mode.String()))
nicId := tcpip.NICID(1)
ep := channel.New(128, config.MTU, "")
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocolFactory{
ipv4.NewProtocol,
ipv6.NewProtocol,
},
TransportProtocols: []stack.TransportProtocolFactory{
tcp.NewProtocol,
udp.NewProtocol,
icmp.NewProtocol4,
icmp.NewProtocol6,
},
})
// Create a network interface in the netstack
tcpErr := s.CreateNIC(nicId, ep)
if tcpErr != nil {
return nil, errors.New(tcpErr.String())
}
s.AddProtocolAddress(nicId, tcpip.ProtocolAddress{
Protocol: ipv4.ProtocolNumber,
AddressWithPrefix: tcpip.AddressWithPrefix{
Address: defaultNicAddress,
PrefixLen: 32,
},
}, stack.AddressProperties{})
epw := netstack.WrapChannel(ep)
epw.Logger = logger.Named(config.Mode.String())
iface := &Interface{
Stack: s,
nicId: nicId,
epw: epw,
ep: ep,
mode: config.Mode,
logger: logger,
linkLayer: config.LinkLayer,
stopChan: make(chan struct{}),
}
switch config.Mode {
case Entrance:
// For entrance interfaces, we want to accept packets for all routes
// and route them through this network interface.
iface.addRoute(tcpip.Route{
Destination: tcpip.AddressWithPrefix{
Address: tcpip.Address(netip.MustParseAddr("0.0.0.0").AsSlice()),
PrefixLen: 0,
}.Subnet(),
Gateway: defaultGatewayAddress,
NIC: nicId,
})
case Exit:
// Setup protocol forwarders on the exit interface
s.SetTransportProtocolHandler(tcp.ProtocolNumber, tcp.NewForwarder(s, 0, 5, (&netstack.TCPForwarder{
Logger: config.Logger.Named("tcp-forwarder"),
}).Handle).HandlePacket)
s.SetTransportProtocolHandler(udp.ProtocolNumber, udp.NewForwarder(s, (&netstack.UDPForwarder{
Logger: config.Logger.Named("udp-forwarder"),
}).Handle).HandlePacket)
// Add the route back to the gateway
iface.addRoute(tcpip.Route{
Destination: tcpip.AddressWithPrefix{
Address: defaultNicAddress,
PrefixLen: 32,
}.Subnet(),
Gateway: defaultGatewayAddress,
NIC: nicId,
})
s.SetPromiscuousMode(nicId, true)
s.SetSpoofing(nicId, true)
}
go iface.linkLayerWorker()
return iface, nil
}
// Stop stops the Interface and prevents it from forwarding any more packets to/from the linkLayer
func (v *Interface) Stop() {
close(v.stopChan)
}
// linkLayerWorker reads/writes packets to/from the linkLayer and reads/writes them to the netstack.
func (v *Interface) linkLayerWorker() {
for {
select {
case _, ok := <-v.stopChan:
if !ok {
return
}
default:
utils.Join(v.epw, v.linkLayer)
}
}
}
// addRoute adds a new route to the network interface and updates the netstack's routing table
func (v *Interface) addRoute(route tcpip.Route) {
v.routes = append(v.routes, route)
v.logger.Debug("adding route", zap.String("route", route.String()))
v.Stack.SetRouteTable(v.routes)
}
// ExposeRoutes allows the caller to expose routes to the remote network interface. This should
// only be called on Exit interfaces, entrance interfaces will automatically expose all routes.
func (v *Interface) ExposeRoutes(routes []string) error {
if v.mode == Entrance {
return fmt.Errorf("cannot expose routes on entrance interface")
}
var rp []netip.Prefix
for _, r := range routes {
p, err := netip.ParsePrefix(r)
if err != nil {
return err
}
rp = append(rp, p)
}
for _, r := range rp {
v.addRoute(tcpip.Route{
Destination: tcpip.AddressWithPrefix{
Address: tcpip.Address(r.Masked().Addr().AsSlice()),
PrefixLen: r.Bits(),
}.Subnet(),
Gateway: defaultGatewayAddress,
NIC: v.nicId,
})
}
return nil
}