mirror of
https://github.com/hanzoai/visor.git
synced 2026-09-24 17:04:14 +00:00
urpc encodes with encoding/json, which walks the payload on every call: a realistic control.ExecArgs costs 7084ns and 10 allocations to cross. ZAP reads a field at a constant offset out of the buffer that arrived, so the same payload is 740ns and one allocation -- but only if the offsets are stated, and a stated offset that is wrong is silent corruption rather than a compile error. So state them from the types themselves. //tools/zap reflects over the payloads at build time and writes the wire as Go source with the offsets as constants. It lives inside Bazel because pkg/sentry/control cannot be imported from outside it: the *_go_proto packages it depends on have no source in the tree. Six payloads had no layout to state. A map, an interface and a type from another module have no offsets, so each is answered in the type: SaveOpts.Metadata and StartContainerArgs.InitialCgroups/Limits cross as lists of pairs; boot.Route.Destination restates net.IPNet as Subnet; OpenMountArgs restates the OCI mount fields the call actually uses; StartArgs.Spec and SinkConfig.Config carry their documents as the bytes they arrived as; MetricsRegistrationResponse carries the protocol buffer the same way; and seccheck's session options become a type instead of a map of any. ExecArgs and MetricsExportData still have none: ExecArgs holds live kernel objects behind an interface, and MetricsExportData reaches a prometheus snapshot whose labels are a map. Co-authored-by: Hanzo Dev <[email protected]>
643 lines
18 KiB
Go
643 lines
18 KiB
Go
// Copyright 2018 The gVisor Authors.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package boot
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"net"
|
|
"os"
|
|
"runtime"
|
|
"strings"
|
|
"syscall"
|
|
|
|
"golang.org/x/sys/unix"
|
|
"gvisor.dev/gvisor/pkg/log"
|
|
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
|
"gvisor.dev/gvisor/pkg/sentry/socket/netfilter"
|
|
"gvisor.dev/gvisor/pkg/sentry/socket/plugin"
|
|
"gvisor.dev/gvisor/pkg/tcpip"
|
|
"gvisor.dev/gvisor/pkg/tcpip/link/ethernet"
|
|
"gvisor.dev/gvisor/pkg/tcpip/link/fdbased"
|
|
"gvisor.dev/gvisor/pkg/tcpip/link/loopback"
|
|
"gvisor.dev/gvisor/pkg/tcpip/link/qdisc/fifo"
|
|
"gvisor.dev/gvisor/pkg/tcpip/link/qdisc/tbf"
|
|
"gvisor.dev/gvisor/pkg/tcpip/link/sniffer"
|
|
"gvisor.dev/gvisor/pkg/tcpip/link/xdp"
|
|
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
|
|
"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
|
|
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
|
"gvisor.dev/gvisor/pkg/urpc"
|
|
"gvisor.dev/gvisor/runsc/config"
|
|
)
|
|
|
|
// DefaultLoopbackLink contains IP addresses and routes of "127.0.0.1/8" and
|
|
// "::1/8" on "lo" interface.
|
|
var DefaultLoopbackLink = LoopbackLink{
|
|
Name: "lo",
|
|
Addresses: []IPWithPrefix{
|
|
{Address: net.IP("\x7f\x00\x00\x01"), PrefixLen: 8},
|
|
{Address: net.IPv6loopback, PrefixLen: 128},
|
|
},
|
|
Routes: []Route{
|
|
{
|
|
Destination: Subnet{
|
|
IP: net.IPv4(0x7f, 0, 0, 0),
|
|
Mask: net.IPv4Mask(0xff, 0, 0, 0),
|
|
},
|
|
},
|
|
{
|
|
Destination: Subnet{
|
|
IP: net.IPv6loopback,
|
|
Mask: net.IPMask(strings.Repeat("\xff", net.IPv6len)),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
// Network exposes methods that can be used to configure a network stack.
|
|
type Network struct {
|
|
Stack *stack.Stack
|
|
Kernel *kernel.Kernel
|
|
|
|
// PluginStack is a third-party network stack to use in place of
|
|
// netstack when non-nil.
|
|
PluginStack plugin.PluginStack
|
|
}
|
|
|
|
// Subnet is an address and the mask that selects its network. It restates
|
|
// net.IPNet, which the RPC cannot carry: a stdlib type cannot state its own
|
|
// wire, and a conversion between the two is legal in either direction.
|
|
type Subnet struct {
|
|
IP net.IP
|
|
Mask net.IPMask
|
|
}
|
|
|
|
// Route represents a route in the network stack.
|
|
type Route struct {
|
|
Destination Subnet
|
|
Gateway net.IP
|
|
MTU uint32
|
|
}
|
|
|
|
// DefaultRoute represents a catch all route to the default gateway.
|
|
type DefaultRoute struct {
|
|
Route Route
|
|
Name string
|
|
}
|
|
|
|
type Neighbor struct {
|
|
IP net.IP
|
|
HardwareAddr net.HardwareAddr
|
|
}
|
|
|
|
// FDBasedLink configures an fd-based link.
|
|
type FDBasedLink struct {
|
|
Name string
|
|
InterfaceIndex int
|
|
MTU int
|
|
Addresses []IPWithPrefix
|
|
Routes []Route
|
|
GSOMaxSize uint32
|
|
GVisorGSOEnabled bool
|
|
GVisorGRO bool
|
|
TXChecksumOffload bool
|
|
RXChecksumOffload bool
|
|
LinkAddress net.HardwareAddr
|
|
QDisc config.QueueingDiscipline
|
|
TBFRate uint64
|
|
TBFBurst uint32
|
|
Neighbors []Neighbor
|
|
|
|
// NumChannels controls how many underlying FDs are to be used to
|
|
// create this endpoint.
|
|
NumChannels int
|
|
|
|
// ProcessorsPerChannel controls how many goroutines are used to handle
|
|
// packets on each channel.
|
|
ProcessorsPerChannel int
|
|
|
|
// IsPacket indicates whether each FD in this link is a packet socket.
|
|
IsPacket []bool
|
|
|
|
// PreConfigured indicates that getsockname and setsockopt(PACKET_FANOUT)
|
|
// have already been performed on the host FDs.
|
|
PreConfigured bool
|
|
}
|
|
|
|
// BindOpt indicates whether the sentry or runsc process is responsible for
|
|
// binding the AF_XDP socket.
|
|
type BindOpt int
|
|
|
|
const (
|
|
// BindSentry indicates the sentry process must call bind.
|
|
BindSentry BindOpt = iota
|
|
|
|
// BindRunsc indicates the runsc process must call bind.
|
|
BindRunsc
|
|
)
|
|
|
|
// XDPLink configures an XDP link.
|
|
type XDPLink struct {
|
|
Name string
|
|
InterfaceIndex int
|
|
MTU int
|
|
Addresses []IPWithPrefix
|
|
Routes []Route
|
|
TXChecksumOffload bool
|
|
RXChecksumOffload bool
|
|
LinkAddress net.HardwareAddr
|
|
QDisc config.QueueingDiscipline
|
|
TBFRate uint64
|
|
TBFBurst uint32
|
|
Neighbors []Neighbor
|
|
GVisorGRO bool
|
|
Bind BindOpt
|
|
|
|
// NumChannels controls how many underlying FDs are to be used to
|
|
// create this endpoint.
|
|
NumChannels int
|
|
}
|
|
|
|
// LoopbackLink configures a loopback link.
|
|
type LoopbackLink struct {
|
|
Name string
|
|
Addresses []IPWithPrefix
|
|
Routes []Route
|
|
GVisorGRO bool
|
|
}
|
|
|
|
// CreateLinksAndRoutesArgs are arguments to CreateLinkAndRoutes.
|
|
type CreateLinksAndRoutesArgs struct {
|
|
// FilePayload contains the fds associated with the FDBasedLinks. The
|
|
// number of fd's should match the sum of the NumChannels field of the
|
|
// FDBasedLink entries below.
|
|
urpc.FilePayload
|
|
|
|
LoopbackLinks []LoopbackLink
|
|
FDBasedLinks []FDBasedLink
|
|
XDPLinks []XDPLink
|
|
|
|
Defaultv4Gateway DefaultRoute
|
|
Defaultv6Gateway DefaultRoute
|
|
|
|
// PCAP indicates that FilePayload also contains a PCAP log file.
|
|
PCAP bool
|
|
|
|
// LogPackets indicates that packets should be logged.
|
|
LogPackets bool
|
|
|
|
// NATBlob indicates whether FilePayload also contains an iptables NAT
|
|
// ruleset.
|
|
NATBlob bool
|
|
|
|
// PauseExternalNetworking indicates whether external networking should be
|
|
// disabled initially.
|
|
PauseExternalNetworking bool
|
|
|
|
// AllowConnectedOnSave indicates whether connections should be allowed to
|
|
// remain connected during save.
|
|
AllowConnectedOnSave bool
|
|
|
|
// IsRestore indicates whether this is part of a restore flow.
|
|
IsRestore bool
|
|
}
|
|
|
|
// InitPluginStackArgs are arguments to InitPluginStack.
|
|
type InitPluginStackArgs struct {
|
|
urpc.FilePayload
|
|
|
|
InitStr string
|
|
}
|
|
|
|
// IPWithPrefix is an address with its subnet prefix length.
|
|
type IPWithPrefix struct {
|
|
// Address is a network address.
|
|
Address net.IP
|
|
|
|
// PrefixLen is the subnet prefix length.
|
|
PrefixLen int
|
|
}
|
|
|
|
func (ip IPWithPrefix) String() string {
|
|
return fmt.Sprintf("%s/%d", ip.Address, ip.PrefixLen)
|
|
}
|
|
|
|
// Empty returns true if route hasn't been set.
|
|
func (r *Route) Empty() bool {
|
|
return r.Destination.IP == nil && r.Destination.Mask == nil && r.Gateway == nil
|
|
}
|
|
|
|
func (r *Route) toTcpipRoute(id tcpip.NICID) (tcpip.Route, error) {
|
|
subnet, err := tcpip.NewSubnet(ipToAddress(r.Destination.IP), ipMaskToAddressMask(r.Destination.Mask))
|
|
if err != nil {
|
|
return tcpip.Route{}, err
|
|
}
|
|
return tcpip.Route{
|
|
Destination: subnet,
|
|
Gateway: ipToAddress(r.Gateway),
|
|
NIC: id,
|
|
MTU: r.MTU,
|
|
}, nil
|
|
}
|
|
|
|
// InitPluginStack initializes plugin network stack.
|
|
// It will invoke Init() that is registered by current plugin stack.
|
|
func (n *Network) InitPluginStack(args *InitPluginStackArgs, _ *struct{}) error {
|
|
pluginStack := n.PluginStack
|
|
if pluginStack == nil {
|
|
return fmt.Errorf("plugin stack is not registered")
|
|
}
|
|
|
|
fdNum := len(args.FilePayload.Files)
|
|
fds := make([]int, fdNum)
|
|
for i := 0; i < fdNum; i++ {
|
|
oldFD := args.FilePayload.Files[i].Fd()
|
|
if newFD, err := syscall.Dup(int(oldFD)); err != nil {
|
|
return fmt.Errorf("failed to dup FD")
|
|
} else {
|
|
fds[i] = newFD
|
|
}
|
|
}
|
|
|
|
return pluginStack.Init(&plugin.InitStackArgs{
|
|
InitStr: args.InitStr,
|
|
FDs: fds,
|
|
})
|
|
}
|
|
|
|
// CreateLinksAndRoutes creates links and routes in a network stack. It should
|
|
// only be called once.
|
|
func (n *Network) CreateLinksAndRoutes(args *CreateLinksAndRoutesArgs, _ *struct{}) error {
|
|
if len(args.FDBasedLinks) > 0 && len(args.XDPLinks) > 0 {
|
|
return fmt.Errorf("received both fdbased and XDP links, but only one can be used at a time")
|
|
}
|
|
wantFDs := 0
|
|
for _, l := range args.FDBasedLinks {
|
|
wantFDs += l.NumChannels
|
|
}
|
|
for _, link := range args.XDPLinks {
|
|
// We have to keep several FDs alive when the sentry is
|
|
// responsible for binding, but when runsc binds we only expect
|
|
// the AF_XDP socket itself.
|
|
switch v := link.Bind; v {
|
|
case BindSentry:
|
|
wantFDs += 4
|
|
case BindRunsc:
|
|
wantFDs++
|
|
default:
|
|
return fmt.Errorf("unknown bind value: %d", v)
|
|
}
|
|
}
|
|
if args.PCAP {
|
|
wantFDs++
|
|
}
|
|
if args.NATBlob {
|
|
wantFDs++
|
|
}
|
|
if got := len(args.FilePayload.Files); got != wantFDs {
|
|
return fmt.Errorf("args.FilePayload.Files has %d FDs but we need %d entries based on FDBasedLinks, XDPLinks, and PCAP", got, wantFDs)
|
|
}
|
|
|
|
nicids := make(map[string]tcpip.NICID)
|
|
|
|
// Collect routes from all links.
|
|
var routes []tcpip.Route
|
|
|
|
// Loopback normally appear before other interfaces.
|
|
for _, link := range args.LoopbackLinks {
|
|
nicID := n.Stack.NextNICID()
|
|
nicids[link.Name] = nicID
|
|
|
|
var linkEP stack.LinkEndpoint
|
|
linkEP = ethernet.New(loopback.New())
|
|
if args.LogPackets {
|
|
linkEP = sniffer.New(linkEP)
|
|
}
|
|
|
|
log.Infof("Enabling loopback interface %q with id %d on addresses %+v", link.Name, nicID, link.Addresses)
|
|
opts := stack.NICOptions{
|
|
Name: link.Name,
|
|
DeliverLinkPackets: true,
|
|
}
|
|
if err := n.createNICWithAddrs(nicID, linkEP, opts, link.Addresses); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Collect the routes from this link.
|
|
for _, r := range link.Routes {
|
|
route, err := r.toTcpipRoute(nicID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
routes = append(routes, route)
|
|
}
|
|
}
|
|
|
|
// Setup fdbased or XDP links.
|
|
fdOffset := 0
|
|
if len(args.FDBasedLinks) > 0 {
|
|
// Choose a dispatch mode.
|
|
// Testing has shown that RecvMMsg is the fastest. Attempts to use
|
|
// the PacketMMap dispatcher have failed to result in higher throughput.
|
|
dispatchMode := fdbased.RecvMMsg
|
|
|
|
for _, link := range args.FDBasedLinks {
|
|
nicID := n.Stack.NextNICID()
|
|
nicids[link.Name] = nicID
|
|
|
|
FDs := make([]int, 0, link.NumChannels)
|
|
for j := 0; j < link.NumChannels; j++ {
|
|
// Copy the underlying FD.
|
|
oldFD := args.FilePayload.Files[fdOffset].Fd()
|
|
newFD, err := unix.Dup(int(oldFD))
|
|
if err != nil {
|
|
return fmt.Errorf("failed to dup FD %v: %v", oldFD, err)
|
|
}
|
|
FDs = append(FDs, newFD)
|
|
fdOffset++
|
|
}
|
|
|
|
mac := tcpip.LinkAddress(link.LinkAddress)
|
|
|
|
linkEP, err := fdbased.New(&fdbased.Options{
|
|
FDs: FDs,
|
|
MTU: uint32(link.MTU),
|
|
EthernetHeader: mac != "",
|
|
Address: mac,
|
|
PacketDispatchMode: dispatchMode,
|
|
GSOMaxSize: link.GSOMaxSize,
|
|
GVisorGSOEnabled: link.GVisorGSOEnabled,
|
|
TXChecksumOffload: link.TXChecksumOffload,
|
|
RXChecksumOffload: link.RXChecksumOffload,
|
|
GRO: link.GVisorGRO,
|
|
ProcessorsPerChannel: link.ProcessorsPerChannel,
|
|
IsPacketSocket: link.IsPacket,
|
|
PreConfigured: link.PreConfigured,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Setup packet logging if requested.
|
|
if args.PCAP {
|
|
newFD, err := unix.Dup(int(args.FilePayload.Files[fdOffset].Fd()))
|
|
if err != nil {
|
|
return fmt.Errorf("failed to dup pcap FD: %v", err)
|
|
}
|
|
const packetTruncateSize = 4096
|
|
linkEP, err = sniffer.NewWithWriter(linkEP, os.NewFile(uintptr(newFD), "pcap-file"), packetTruncateSize)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create PCAP logger: %v", err)
|
|
}
|
|
fdOffset++
|
|
} else if args.LogPackets {
|
|
linkEP = sniffer.New(linkEP)
|
|
}
|
|
|
|
var qDisc stack.QueueingDiscipline
|
|
switch link.QDisc {
|
|
case config.QDiscNone:
|
|
case config.QDiscFIFO:
|
|
log.Infof("Enabling FIFO QDisc on %q", link.Name)
|
|
qDisc = fifo.New(linkEP, runtime.GOMAXPROCS(0), 1000)
|
|
case config.QDiscTBF:
|
|
log.Infof("Enabling TBF QDisc on %q rate=%d burst=%d", link.Name, link.TBFRate, link.TBFBurst)
|
|
var err error
|
|
qDisc, err = tbf.New(linkEP, n.Stack.Clock(), link.TBFRate, link.TBFBurst, 1000)
|
|
if err != nil {
|
|
return fmt.Errorf("creating TBF qdisc for %q: %w", link.Name, err)
|
|
}
|
|
}
|
|
|
|
log.Infof("Enabling interface %q with id %d on addresses %+v (%v) w/ %d channels", link.Name, nicID, link.Addresses, mac, link.NumChannels)
|
|
opts := stack.NICOptions{
|
|
Name: link.Name,
|
|
QDisc: qDisc,
|
|
DeliverLinkPackets: true,
|
|
}
|
|
if err := n.createNICWithAddrs(nicID, linkEP, opts, link.Addresses); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Collect the routes from this link.
|
|
for _, r := range link.Routes {
|
|
route, err := r.toTcpipRoute(nicID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
routes = append(routes, route)
|
|
}
|
|
|
|
for _, neigh := range link.Neighbors {
|
|
proto, tcpipAddr := ipToAddressAndProto(neigh.IP)
|
|
n.Stack.AddStaticNeighbor(nicID, proto, tcpipAddr, tcpip.LinkAddress(neigh.HardwareAddr))
|
|
}
|
|
}
|
|
} else if len(args.XDPLinks) > 0 {
|
|
if nlinks := len(args.XDPLinks); nlinks > 1 {
|
|
return fmt.Errorf("XDP only supports one link device, but got %d", nlinks)
|
|
}
|
|
link := args.XDPLinks[0]
|
|
nicID := n.Stack.NextNICID()
|
|
nicids[link.Name] = nicID
|
|
|
|
// Get the AF_XDP socket.
|
|
oldFD := args.FilePayload.Files[fdOffset].Fd()
|
|
fd, err := unix.Dup(int(oldFD))
|
|
if err != nil {
|
|
return fmt.Errorf("failed to dup AF_XDP fd %v: %v", oldFD, err)
|
|
}
|
|
fdOffset++
|
|
|
|
// When the sentry is responsible for binding, the runsc
|
|
// process sends several other FDs in order to keep them open
|
|
// and alive. These are for BPF programs and maps that, if
|
|
// closed, will break the dispatcher.
|
|
if link.Bind == BindSentry {
|
|
for _, fdName := range []string{"program-fd", "sockmap-fd", "link-fd"} {
|
|
oldFD := args.FilePayload.Files[fdOffset].Fd()
|
|
if _, err := unix.Dup(int(oldFD)); err != nil {
|
|
return fmt.Errorf("failed to dup %s with FD %d: %v", fdName, oldFD, err)
|
|
}
|
|
fdOffset++
|
|
}
|
|
}
|
|
|
|
// Setup packet logging if requested.
|
|
mac := tcpip.LinkAddress(link.LinkAddress)
|
|
linkEP, err := xdp.New(&xdp.Options{
|
|
FD: fd,
|
|
Address: mac,
|
|
TXChecksumOffload: link.TXChecksumOffload,
|
|
RXChecksumOffload: link.RXChecksumOffload,
|
|
InterfaceIndex: link.InterfaceIndex,
|
|
Bind: link.Bind == BindSentry,
|
|
GRO: link.GVisorGRO,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if args.PCAP {
|
|
newFD, err := unix.Dup(int(args.FilePayload.Files[fdOffset].Fd()))
|
|
if err != nil {
|
|
return fmt.Errorf("failed to dup pcap FD: %v", err)
|
|
}
|
|
const packetTruncateSize = 4096
|
|
linkEP, err = sniffer.NewWithWriter(linkEP, os.NewFile(uintptr(newFD), "pcap-file"), packetTruncateSize)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create PCAP logger: %v", err)
|
|
}
|
|
fdOffset++
|
|
} else if args.LogPackets {
|
|
linkEP = sniffer.New(linkEP)
|
|
}
|
|
|
|
var qDisc stack.QueueingDiscipline
|
|
switch link.QDisc {
|
|
case config.QDiscNone:
|
|
case config.QDiscFIFO:
|
|
log.Infof("Enabling FIFO QDisc on %q", link.Name)
|
|
qDisc = fifo.New(linkEP, runtime.GOMAXPROCS(0), 1000)
|
|
case config.QDiscTBF:
|
|
log.Infof("Enabling TBF QDisc on %q rate=%d burst=%d", link.Name, link.TBFRate, link.TBFBurst)
|
|
var err error
|
|
qDisc, err = tbf.New(linkEP, n.Stack.Clock(), link.TBFRate, link.TBFBurst, 1000)
|
|
if err != nil {
|
|
return fmt.Errorf("creating TBF qdisc for %q: %w", link.Name, err)
|
|
}
|
|
}
|
|
|
|
log.Infof("Enabling interface %q with id %d on addresses %+v (%v) w/ %d channels", link.Name, nicID, link.Addresses, mac, link.NumChannels)
|
|
opts := stack.NICOptions{
|
|
Name: link.Name,
|
|
QDisc: qDisc,
|
|
DeliverLinkPackets: true,
|
|
}
|
|
if err := n.createNICWithAddrs(nicID, linkEP, opts, link.Addresses); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Collect the routes from this link.
|
|
for _, r := range link.Routes {
|
|
route, err := r.toTcpipRoute(nicID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
routes = append(routes, route)
|
|
}
|
|
|
|
for _, neigh := range link.Neighbors {
|
|
proto, tcpipAddr := ipToAddressAndProto(neigh.IP)
|
|
n.Stack.AddStaticNeighbor(nicID, proto, tcpipAddr, tcpip.LinkAddress(neigh.HardwareAddr))
|
|
}
|
|
}
|
|
|
|
if !args.Defaultv4Gateway.Route.Empty() {
|
|
nicID, ok := nicids[args.Defaultv4Gateway.Name]
|
|
if !ok {
|
|
return fmt.Errorf("invalid interface name %q for default route", args.Defaultv4Gateway.Name)
|
|
}
|
|
route, err := args.Defaultv4Gateway.Route.toTcpipRoute(nicID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
routes = append(routes, route)
|
|
}
|
|
|
|
if !args.Defaultv6Gateway.Route.Empty() {
|
|
nicID, ok := nicids[args.Defaultv6Gateway.Name]
|
|
if !ok {
|
|
return fmt.Errorf("invalid interface name %q for default route", args.Defaultv6Gateway.Name)
|
|
}
|
|
route, err := args.Defaultv6Gateway.Route.toTcpipRoute(nicID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
routes = append(routes, route)
|
|
}
|
|
|
|
log.Infof("Setting routes %+v", routes)
|
|
n.Stack.SetRouteTable(routes)
|
|
|
|
// Set NAT table rules if necessary.
|
|
if args.NATBlob {
|
|
log.Infof("Replacing NAT table")
|
|
iptReplaceBlob, err := io.ReadAll(args.FilePayload.Files[fdOffset])
|
|
if err != nil {
|
|
return fmt.Errorf("failed to read iptables blob: %v", err)
|
|
}
|
|
fdOffset++
|
|
if err := netfilter.SetEntries(n.Kernel.RootUserNamespace(), n.Stack, iptReplaceBlob, false); err != nil {
|
|
return fmt.Errorf("failed to SetEntries: %v", err)
|
|
}
|
|
}
|
|
|
|
if args.PauseExternalNetworking {
|
|
n.Stack.DisableAllNonLoopbackNICs()
|
|
}
|
|
|
|
n.Stack.SetAllowConnectedOnSave(args.AllowConnectedOnSave)
|
|
|
|
return nil
|
|
}
|
|
|
|
// createNICWithAddrs creates a NIC in the network stack and adds the given
|
|
// addresses.
|
|
func (n *Network) createNICWithAddrs(id tcpip.NICID, ep stack.LinkEndpoint, opts stack.NICOptions, addrs []IPWithPrefix) error {
|
|
if err := n.Stack.CreateNICWithOptions(id, ep, opts); err != nil {
|
|
return fmt.Errorf("CreateNICWithOptions(%d, _, %+v) failed: %v", id, opts, err)
|
|
}
|
|
|
|
for _, addr := range addrs {
|
|
proto, tcpipAddr := ipToAddressAndProto(addr.Address)
|
|
protocolAddr := tcpip.ProtocolAddress{
|
|
Protocol: proto,
|
|
AddressWithPrefix: tcpip.AddressWithPrefix{
|
|
Address: tcpipAddr,
|
|
PrefixLen: addr.PrefixLen,
|
|
},
|
|
}
|
|
if err := n.Stack.AddProtocolAddress(id, protocolAddr, stack.AddressProperties{}); err != nil {
|
|
return fmt.Errorf("AddProtocolAddress(%d, %+v, {}) failed: %s", id, protocolAddr, err)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ipToAddressAndProto converts IP to tcpip.Address and a protocol number.
|
|
//
|
|
// Note: don't use 'len(ip)' to determine IP version because length is always 16.
|
|
func ipToAddressAndProto(ip net.IP) (tcpip.NetworkProtocolNumber, tcpip.Address) {
|
|
if i4 := ip.To4(); i4 != nil {
|
|
return ipv4.ProtocolNumber, tcpip.AddrFromSlice(i4)
|
|
}
|
|
return ipv6.ProtocolNumber, tcpip.AddrFromSlice(ip)
|
|
}
|
|
|
|
// ipToAddress converts IP to tcpip.Address, ignoring the protocol.
|
|
func ipToAddress(ip net.IP) tcpip.Address {
|
|
_, addr := ipToAddressAndProto(ip)
|
|
return addr
|
|
}
|
|
|
|
// ipMaskToAddressMask converts IPMask to tcpip.AddressMask, ignoring the
|
|
// protocol.
|
|
func ipMaskToAddressMask(ipMask net.IPMask) tcpip.AddressMask {
|
|
addr := ipToAddress(net.IP(ipMask))
|
|
return tcpip.MaskFromBytes(addr.AsSlice())
|
|
}
|