Files
gvisor/pkg/sentry/socket/netstack/stack.go
T
2025-01-29 21:16:51 -08:00

973 lines
29 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 netstack
import (
"fmt"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/refs"
"gvisor.dev/gvisor/pkg/sentry/inet"
"gvisor.dev/gvisor/pkg/sentry/socket/netlink/nlmsg"
"gvisor.dev/gvisor/pkg/syserr"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/link/ethernet"
"gvisor.dev/gvisor/pkg/tcpip/link/packetsocket"
"gvisor.dev/gvisor/pkg/tcpip/link/veth"
"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/tcp"
)
// Stack implements inet.Stack for netstack/tcpip/stack.Stack.
//
// +stateify savable
type Stack struct {
Stack *stack.Stack `state:".(*stack.Stack)"`
}
// EnableSaveRestore enables netstack s/r.
func (s *Stack) EnableSaveRestore() error {
s.Stack.EnableSaveRestore()
return nil
}
// IsSaveRestoreEnabled implements inet.Stack.IsSaveRestoreEnabled.
func (s *Stack) IsSaveRestoreEnabled() bool {
if s.Stack == nil {
return false
}
return s.Stack.IsSaveRestoreEnabled()
}
// Destroy implements inet.Stack.Destroy.
func (s *Stack) Destroy() {
s.Stack.Close()
refs.CleanupSync.Add(1)
go func() {
s.Stack.Wait()
refs.CleanupSync.Done()
}()
}
// SupportsIPv6 implements Stack.SupportsIPv6.
func (s *Stack) SupportsIPv6() bool {
return s.Stack.CheckNetworkProtocol(ipv6.ProtocolNumber)
}
// Converts Netstack's ARPHardwareType to equivalent linux constants.
func toLinuxARPHardwareType(t header.ARPHardwareType) uint16 {
switch t {
case header.ARPHardwareNone:
return linux.ARPHRD_NONE
case header.ARPHardwareLoopback:
return linux.ARPHRD_LOOPBACK
case header.ARPHardwareEther:
return linux.ARPHRD_ETHER
default:
panic(fmt.Sprintf("unknown ARPHRD type: %d", t))
}
}
// Interfaces implements inet.Stack.Interfaces.
func (s *Stack) Interfaces() map[int32]inet.Interface {
is := make(map[int32]inet.Interface)
for id, ni := range s.Stack.NICInfo() {
is[int32(id)] = inet.Interface{
Name: ni.Name,
Addr: []byte(ni.LinkAddress),
Flags: uint32(nicStateFlagsToLinux(ni.Flags)),
DeviceType: toLinuxARPHardwareType(ni.ARPHardwareType),
MTU: ni.MTU,
}
}
return is
}
// RemoveInterface implements inet.Stack.RemoveInterface.
func (s *Stack) RemoveInterface(idx int32) error {
nic := tcpip.NICID(idx)
nicInfo, ok := s.Stack.NICInfo()[nic]
if !ok {
return syserr.ErrUnknownNICID.ToError()
}
// Don't allow removing the loopback interface.
if nicInfo.Flags.Loopback {
return syserr.ErrNotSupported.ToError()
}
return syserr.TranslateNetstackError(s.Stack.RemoveNIC(nic)).ToError()
}
// SetInterface implements inet.Stack.SetInterface.
func (s *Stack) SetInterface(ctx context.Context, msg *nlmsg.Message) *syserr.Error {
var ifinfomsg linux.InterfaceInfoMessage
attrsView, ok := msg.GetData(&ifinfomsg)
if !ok {
return syserr.ErrInvalidArgument
}
attrs, ok := attrsView.Parse()
if !ok {
return syserr.ErrInvalidArgument
}
ifname := ""
for attr := range attrs {
value := attrs[attr]
switch attr {
case linux.IFLA_IFNAME:
if len(value) < 1 {
return syserr.ErrInvalidArgument
}
if ifinfomsg.Index == 0 {
ifname = value.String()
for idx, ifa := range s.Interfaces() {
if ifname == ifa.Name {
ifinfomsg.Index = idx
break
}
}
}
case linux.IFLA_MASTER:
case linux.IFLA_LINKINFO:
case linux.IFLA_ADDRESS:
case linux.IFLA_MTU:
case linux.IFLA_NET_NS_FD:
case linux.IFLA_TXQLEN:
default:
ctx.Warningf("unexpected attribute: %x", attr)
return syserr.ErrNotSupported
}
}
flags := msg.Header().Flags
if ifinfomsg.Index == 0 {
if flags&linux.NLM_F_CREATE != 0 {
return s.newInterface(ctx, msg, attrs)
}
return syserr.ErrNoDevice
}
if flags&(linux.NLM_F_EXCL|linux.NLM_F_REPLACE) != 0 {
return syserr.ErrExists
}
if ifinfomsg.Flags != 0 || ifinfomsg.Change != 0 {
if ifinfomsg.Change & ^uint32(linux.IFF_UP) != 0 {
ctx.Warningf("Unsupported ifi_change flags: %x", ifinfomsg.Change)
return syserr.ErrInvalidArgument
}
if ifinfomsg.Flags & ^uint32(linux.IFF_UP) != 0 {
ctx.Warningf("Unsupported ifi_flags: %x", ifinfomsg.Change)
return syserr.ErrInvalidArgument
}
// Netstack interfaces are always up.
}
return s.setLink(ctx, tcpip.NICID(ifinfomsg.Index), attrs)
}
func (s *Stack) setLink(ctx context.Context, id tcpip.NICID, linkAttrs map[uint16]nlmsg.BytesView) *syserr.Error {
// IFLA_NET_NS_FD has to be handled first, because other parameters may be reset.
if v, ok := linkAttrs[linux.IFLA_NET_NS_FD]; ok {
fd, ok := v.Uint32()
if !ok {
return syserr.ErrInvalidArgument
}
f := inet.NamespaceByFDFromContext(ctx)
if f == nil {
return syserr.ErrInvalidArgument
}
ns, err := f(int32(fd))
if err != nil {
return syserr.FromError(err)
}
defer ns.DecRef(ctx)
peer := ns.Stack().(*Stack)
if peer.Stack != s.Stack {
var err tcpip.Error
id, err = s.Stack.SetNICStack(id, peer.Stack)
if err != nil {
return syserr.TranslateNetstackError(err)
}
}
}
for t, v := range linkAttrs {
switch t {
case linux.IFLA_MASTER:
master, ok := v.Uint32()
if !ok {
return syserr.ErrInvalidArgument
}
if master != 0 {
if err := s.Stack.SetNICCoordinator(id, tcpip.NICID(master)); err != nil {
return syserr.TranslateNetstackError(err)
}
}
case linux.IFLA_ADDRESS:
if len(v) != tcpip.LinkAddressSize {
return syserr.ErrInvalidArgument
}
addr := tcpip.LinkAddress(v)
if err := s.Stack.SetNICAddress(id, addr); err != nil {
return syserr.TranslateNetstackError(err)
}
case linux.IFLA_IFNAME:
if err := s.Stack.SetNICName(id, v.String()); err != nil {
return syserr.TranslateNetstackError(err)
}
case linux.IFLA_MTU:
mtu, ok := v.Uint32()
if !ok {
return syserr.ErrInvalidArgument
}
if err := s.Stack.SetNICMTU(id, mtu); err != nil {
return syserr.TranslateNetstackError(err)
}
case linux.IFLA_TXQLEN:
// TODO(b/340388892): support IFLA_TXQLEN.
}
}
return nil
}
const defaultMTU = 1500
func (s *Stack) newVeth(ctx context.Context, linkAttrs map[uint16]nlmsg.BytesView, linkInfoAttrs map[uint16]nlmsg.BytesView) *syserr.Error {
var (
linkInfoData map[uint16]nlmsg.BytesView
ifinfomsg linux.InterfaceInfoMessage
peerLinkAttrs map[uint16]nlmsg.BytesView
)
peerStack := s
peerName := ""
ifname := ""
if v, ok := linkAttrs[linux.IFLA_IFNAME]; ok {
ifname = v.String()
}
if value, ok := linkInfoAttrs[linux.IFLA_INFO_DATA]; ok {
linkInfoData, ok = nlmsg.AttrsView(value).Parse()
if !ok {
return syserr.ErrInvalidArgument
}
if v, ok := linkInfoData[linux.VETH_INFO_PEER]; ok {
attrsView := nlmsg.AttrsView(v[ifinfomsg.SizeBytes():])
if !ok {
return syserr.ErrInvalidArgument
}
peerLinkAttrs, ok = attrsView.Parse()
if !ok {
return syserr.ErrInvalidArgument
}
if v, ok = peerLinkAttrs[linux.IFLA_IFNAME]; ok {
peerName = v.String()
}
if v, ok = peerLinkAttrs[linux.IFLA_NET_NS_FD]; ok {
fd, ok := v.Uint32()
if !ok {
return syserr.ErrInvalidArgument
}
f := inet.NamespaceByFDFromContext(ctx)
if f == nil {
return syserr.ErrInvalidArgument
}
ns, err := f(int32(fd))
if err != nil {
return syserr.FromError(err)
}
defer ns.DecRef(ctx)
peerStack = ns.Stack().(*Stack)
}
}
}
ep, peerEP := veth.NewPair(defaultMTU, veth.DefaultBacklogSize)
id := s.Stack.NextNICID()
peerID := peerStack.Stack.NextNICID()
if ifname == "" {
ifname = fmt.Sprintf("veth%d", id)
}
err := s.Stack.CreateNICWithOptions(id, packetsocket.New(ethernet.New(ep)), stack.NICOptions{
Name: ifname,
})
if err != nil {
return syserr.TranslateNetstackError(err)
}
if err := s.setLink(ctx, id, linkAttrs); err != nil {
peerEP.Close()
return err
}
if peerName == "" {
peerName = fmt.Sprintf("veth%d", peerID)
}
err = peerStack.Stack.CreateNICWithOptions(peerID, packetsocket.New(ethernet.New(peerEP)), stack.NICOptions{
Name: peerName,
})
if err != nil {
peerEP.Close()
return syserr.TranslateNetstackError(err)
}
if peerLinkAttrs != nil {
if err := peerStack.setLink(ctx, peerID, peerLinkAttrs); err != nil {
peerStack.Stack.RemoveNIC(peerID)
peerEP.Close()
return err
}
}
return nil
}
func (s *Stack) newBridge(ctx context.Context, linkAttrs map[uint16]nlmsg.BytesView, linkInfoAttrs map[uint16]nlmsg.BytesView) *syserr.Error {
ifname := ""
if v, ok := linkAttrs[linux.IFLA_IFNAME]; ok {
ifname = v.String()
}
ep := stack.NewBridgeEndpoint(defaultMTU)
id := s.Stack.NextNICID()
err := s.Stack.CreateNICWithOptions(id, ep, stack.NICOptions{
Name: ifname,
})
if err != nil {
return syserr.TranslateNetstackError(err)
}
if err := s.setLink(ctx, id, linkAttrs); err != nil {
return err
}
return nil
}
func (s *Stack) newInterface(ctx context.Context, msg *nlmsg.Message, linkAttrs map[uint16]nlmsg.BytesView) *syserr.Error {
var (
linkInfoAttrs map[uint16]nlmsg.BytesView
kind string
)
if v, ok := linkAttrs[linux.IFLA_LINKINFO]; ok {
linkInfoAttrs, ok = nlmsg.AttrsView(v).Parse()
if !ok {
return syserr.ErrInvalidArgument
}
for attr := range linkInfoAttrs {
value := linkInfoAttrs[attr]
switch attr {
case linux.IFLA_INFO_KIND:
kind = value.String()
case linux.IFLA_INFO_DATA:
default:
ctx.Warningf("unexpected link info attribute: %x", attr)
return syserr.ErrNotSupported
}
}
}
switch kind {
case "":
return syserr.ErrInvalidArgument
case "bridge":
return s.newBridge(ctx, linkAttrs, linkInfoAttrs)
case "veth":
return s.newVeth(ctx, linkAttrs, linkInfoAttrs)
}
return syserr.ErrNotSupported
}
// InterfaceAddrs implements inet.Stack.InterfaceAddrs.
func (s *Stack) InterfaceAddrs() map[int32][]inet.InterfaceAddr {
nicAddrs := make(map[int32][]inet.InterfaceAddr)
for id, ni := range s.Stack.NICInfo() {
var addrs []inet.InterfaceAddr
for _, a := range ni.ProtocolAddresses {
var family uint8
switch a.Protocol {
case ipv4.ProtocolNumber:
family = linux.AF_INET
case ipv6.ProtocolNumber:
family = linux.AF_INET6
default:
log.Warningf("Unknown network protocol in %+v", a)
continue
}
addrCopy := a.AddressWithPrefix.Address
addrs = append(addrs, inet.InterfaceAddr{
Family: family,
PrefixLen: uint8(a.AddressWithPrefix.PrefixLen),
Addr: addrCopy.AsSlice(),
// TODO(b/68878065): Other fields.
})
}
nicAddrs[int32(id)] = addrs
}
return nicAddrs
}
// convertAddr converts an InterfaceAddr to a ProtocolAddress.
func convertAddr(addr inet.InterfaceAddr) (tcpip.ProtocolAddress, error) {
var (
protocol tcpip.NetworkProtocolNumber
address tcpip.Address
protocolAddress tcpip.ProtocolAddress
)
switch addr.Family {
case linux.AF_INET:
if len(addr.Addr) != header.IPv4AddressSize {
return protocolAddress, linuxerr.EINVAL
}
if addr.PrefixLen > header.IPv4AddressSize*8 {
return protocolAddress, linuxerr.EINVAL
}
protocol = ipv4.ProtocolNumber
address = tcpip.AddrFrom4Slice(addr.Addr)
case linux.AF_INET6:
if len(addr.Addr) != header.IPv6AddressSize {
return protocolAddress, linuxerr.EINVAL
}
if addr.PrefixLen > header.IPv6AddressSize*8 {
return protocolAddress, linuxerr.EINVAL
}
protocol = ipv6.ProtocolNumber
address = tcpip.AddrFrom16Slice(addr.Addr)
default:
return protocolAddress, linuxerr.ENOTSUP
}
protocolAddress = tcpip.ProtocolAddress{
Protocol: protocol,
AddressWithPrefix: tcpip.AddressWithPrefix{
Address: address,
PrefixLen: int(addr.PrefixLen),
},
}
return protocolAddress, nil
}
// AddInterfaceAddr implements inet.Stack.AddInterfaceAddr.
func (s *Stack) AddInterfaceAddr(idx int32, addr inet.InterfaceAddr) error {
protocolAddress, err := convertAddr(addr)
if err != nil {
return err
}
// Attach address to interface.
nicID := tcpip.NICID(idx)
if err := s.Stack.AddProtocolAddress(nicID, protocolAddress, stack.AddressProperties{}); err != nil {
return syserr.TranslateNetstackError(err).ToError()
}
// Add route for local network if it doesn't exist already.
localRoute := tcpip.Route{
Destination: protocolAddress.AddressWithPrefix.Subnet(),
Gateway: tcpip.Address{}, // No gateway for local network.
NIC: nicID,
}
for _, rt := range s.Stack.GetRouteTable() {
if rt.Equal(localRoute) {
return nil
}
}
// Local route does not exist yet. Add it.
s.Stack.AddRoute(localRoute)
return nil
}
// RemoveInterfaceAddr implements inet.Stack.RemoveInterfaceAddr.
func (s *Stack) RemoveInterfaceAddr(idx int32, addr inet.InterfaceAddr) error {
protocolAddress, err := convertAddr(addr)
if err != nil {
return err
}
// Remove addresses matching the address and prefix.
nicID := tcpip.NICID(idx)
if err := s.Stack.RemoveAddress(nicID, protocolAddress.AddressWithPrefix.Address); err != nil {
return syserr.TranslateNetstackError(err).ToError()
}
// Remove the corresponding local network route if it exists.
localRoute := tcpip.Route{
Destination: protocolAddress.AddressWithPrefix.Subnet(),
Gateway: tcpip.Address{}, // No gateway for local network.
NIC: nicID,
}
s.Stack.RemoveRoutes(func(rt tcpip.Route) bool {
return rt.Equal(localRoute)
})
return nil
}
// TCPReceiveBufferSize implements inet.Stack.TCPReceiveBufferSize.
func (s *Stack) TCPReceiveBufferSize() (inet.TCPBufferSize, error) {
var rs tcpip.TCPReceiveBufferSizeRangeOption
err := s.Stack.TransportProtocolOption(tcp.ProtocolNumber, &rs)
return inet.TCPBufferSize{
Min: rs.Min,
Default: rs.Default,
Max: rs.Max,
}, syserr.TranslateNetstackError(err).ToError()
}
// SetTCPReceiveBufferSize implements inet.Stack.SetTCPReceiveBufferSize.
func (s *Stack) SetTCPReceiveBufferSize(size inet.TCPBufferSize) error {
rs := tcpip.TCPReceiveBufferSizeRangeOption{
Min: size.Min,
Default: size.Default,
Max: size.Max,
}
return syserr.TranslateNetstackError(s.Stack.SetTransportProtocolOption(tcp.ProtocolNumber, &rs)).ToError()
}
// TCPSendBufferSize implements inet.Stack.TCPSendBufferSize.
func (s *Stack) TCPSendBufferSize() (inet.TCPBufferSize, error) {
var ss tcpip.TCPSendBufferSizeRangeOption
err := s.Stack.TransportProtocolOption(tcp.ProtocolNumber, &ss)
return inet.TCPBufferSize{
Min: ss.Min,
Default: ss.Default,
Max: ss.Max,
}, syserr.TranslateNetstackError(err).ToError()
}
// SetTCPSendBufferSize implements inet.Stack.SetTCPSendBufferSize.
func (s *Stack) SetTCPSendBufferSize(size inet.TCPBufferSize) error {
ss := tcpip.TCPSendBufferSizeRangeOption{
Min: size.Min,
Default: size.Default,
Max: size.Max,
}
return syserr.TranslateNetstackError(s.Stack.SetTransportProtocolOption(tcp.ProtocolNumber, &ss)).ToError()
}
// TCPSACKEnabled implements inet.Stack.TCPSACKEnabled.
func (s *Stack) TCPSACKEnabled() (bool, error) {
var sack tcpip.TCPSACKEnabled
err := s.Stack.TransportProtocolOption(tcp.ProtocolNumber, &sack)
return bool(sack), syserr.TranslateNetstackError(err).ToError()
}
// SetTCPSACKEnabled implements inet.Stack.SetTCPSACKEnabled.
func (s *Stack) SetTCPSACKEnabled(enabled bool) error {
opt := tcpip.TCPSACKEnabled(enabled)
return syserr.TranslateNetstackError(s.Stack.SetTransportProtocolOption(tcp.ProtocolNumber, &opt)).ToError()
}
// TCPRecovery implements inet.Stack.TCPRecovery.
func (s *Stack) TCPRecovery() (inet.TCPLossRecovery, error) {
var recovery tcpip.TCPRecovery
if err := s.Stack.TransportProtocolOption(tcp.ProtocolNumber, &recovery); err != nil {
return 0, syserr.TranslateNetstackError(err).ToError()
}
return inet.TCPLossRecovery(recovery), nil
}
// SetTCPRecovery implements inet.Stack.SetTCPRecovery.
func (s *Stack) SetTCPRecovery(recovery inet.TCPLossRecovery) error {
opt := tcpip.TCPRecovery(recovery)
return syserr.TranslateNetstackError(s.Stack.SetTransportProtocolOption(tcp.ProtocolNumber, &opt)).ToError()
}
// Statistics implements inet.Stack.Statistics.
func (s *Stack) Statistics(stat any, arg string) error {
netStats := s.Stats()
switch stats := stat.(type) {
case *inet.StatDev:
for _, ni := range s.Stack.NICInfo() {
if ni.Name != arg {
continue
}
// TODO(gvisor.dev/issue/2103) Support stubbed stats.
*stats = inet.StatDev{
// Receive section.
ni.Stats.Rx.Bytes.Value(), // bytes.
ni.Stats.Rx.Packets.Value(), // packets.
0, // errs.
0, // drop.
0, // fifo.
0, // frame.
0, // compressed.
0, // multicast.
// Transmit section.
ni.Stats.Tx.Bytes.Value(), // bytes.
ni.Stats.Tx.Packets.Value(), // packets.
0, // errs.
0, // drop.
0, // fifo.
0, // colls.
0, // carrier.
0, // compressed.
}
break
}
case *inet.StatSNMPIP:
ip := netStats.IP
// TODO(gvisor.dev/issue/969) Support stubbed stats.
*stats = inet.StatSNMPIP{
0, // Ip/Forwarding.
0, // Ip/DefaultTTL.
ip.PacketsReceived.Value(), // InReceives.
0, // Ip/InHdrErrors.
ip.InvalidDestinationAddressesReceived.Value(), // InAddrErrors.
0, // Ip/ForwDatagrams.
0, // Ip/InUnknownProtos.
0, // Ip/InDiscards.
ip.PacketsDelivered.Value(), // InDelivers.
ip.PacketsSent.Value(), // OutRequests.
ip.OutgoingPacketErrors.Value(), // OutDiscards.
0, // Ip/OutNoRoutes.
0, // Support Ip/ReasmTimeout.
0, // Support Ip/ReasmReqds.
0, // Support Ip/ReasmOKs.
0, // Support Ip/ReasmFails.
0, // Support Ip/FragOKs.
0, // Support Ip/FragFails.
0, // Support Ip/FragCreates.
}
case *inet.StatSNMPICMP:
in := netStats.ICMP.V4.PacketsReceived.ICMPv4PacketStats
out := netStats.ICMP.V4.PacketsSent.ICMPv4PacketStats
// TODO(gvisor.dev/issue/969) Support stubbed stats.
*stats = inet.StatSNMPICMP{
0, // Icmp/InMsgs.
Metrics.ICMP.V4.PacketsSent.Dropped.Value(), // InErrors.
0, // Icmp/InCsumErrors.
in.DstUnreachable.Value(), // InDestUnreachs.
in.TimeExceeded.Value(), // InTimeExcds.
in.ParamProblem.Value(), // InParmProbs.
in.SrcQuench.Value(), // InSrcQuenchs.
in.Redirect.Value(), // InRedirects.
in.EchoRequest.Value(), // InEchos.
in.EchoReply.Value(), // InEchoReps.
in.Timestamp.Value(), // InTimestamps.
in.TimestampReply.Value(), // InTimestampReps.
in.InfoRequest.Value(), // InAddrMasks.
in.InfoReply.Value(), // InAddrMaskReps.
0, // Icmp/OutMsgs.
Metrics.ICMP.V4.PacketsReceived.Invalid.Value(), // OutErrors.
out.DstUnreachable.Value(), // OutDestUnreachs.
out.TimeExceeded.Value(), // OutTimeExcds.
out.ParamProblem.Value(), // OutParmProbs.
out.SrcQuench.Value(), // OutSrcQuenchs.
out.Redirect.Value(), // OutRedirects.
out.EchoRequest.Value(), // OutEchos.
out.EchoReply.Value(), // OutEchoReps.
out.Timestamp.Value(), // OutTimestamps.
out.TimestampReply.Value(), // OutTimestampReps.
out.InfoRequest.Value(), // OutAddrMasks.
out.InfoReply.Value(), // OutAddrMaskReps.
}
case *inet.StatSNMPTCP:
tcp := netStats.TCP
// RFC 2012 (updates 1213): SNMPv2-MIB-TCP.
*stats = inet.StatSNMPTCP{
1, // RtoAlgorithm.
200, // RtoMin.
120000, // RtoMax.
(1<<64 - 1), // MaxConn.
tcp.ActiveConnectionOpenings.Value(), // ActiveOpens.
tcp.PassiveConnectionOpenings.Value(), // PassiveOpens.
tcp.FailedConnectionAttempts.Value(), // AttemptFails.
tcp.EstablishedResets.Value(), // EstabResets.
tcp.CurrentEstablished.Value(), // CurrEstab.
tcp.ValidSegmentsReceived.Value(), // InSegs.
tcp.SegmentsSent.Value(), // OutSegs.
tcp.Retransmits.Value(), // RetransSegs.
tcp.InvalidSegmentsReceived.Value(), // InErrs.
tcp.ResetsSent.Value(), // OutRsts.
tcp.ChecksumErrors.Value(), // InCsumErrors.
}
case *inet.StatSNMPUDP:
udp := netStats.UDP
// TODO(gvisor.dev/issue/969) Support stubbed stats.
*stats = inet.StatSNMPUDP{
udp.PacketsReceived.Value(), // InDatagrams.
udp.UnknownPortErrors.Value(), // NoPorts.
0, // Udp/InErrors.
udp.PacketsSent.Value(), // OutDatagrams.
udp.ReceiveBufferErrors.Value(), // RcvbufErrors.
0, // Udp/SndbufErrors.
udp.ChecksumErrors.Value(), // Udp/InCsumErrors.
0, // Udp/IgnoredMulti.
}
default:
return syserr.ErrEndpointOperation.ToError()
}
return nil
}
// Stats implements inet.Stack.Stats.
func (s *Stack) Stats() tcpip.Stats {
return s.Stack.Stats()
}
// RouteTable implements inet.Stack.RouteTable.
func (s *Stack) RouteTable() []inet.Route {
var routeTable []inet.Route
for _, rt := range s.Stack.GetRouteTable() {
var family uint8
switch rt.Destination.ID().BitLen() {
case header.IPv4AddressSizeBits:
family = linux.AF_INET
case header.IPv6AddressSizeBits:
family = linux.AF_INET6
default:
log.Warningf("Unknown network protocol in route %+v", rt)
continue
}
dstAddr := rt.Destination.ID()
routeTable = append(routeTable, inet.Route{
Family: family,
DstLen: uint8(rt.Destination.Prefix()), // The CIDR prefix for the destination.
// Always return unspecified protocol since we have no notion of
// protocol for routes.
Protocol: linux.RTPROT_UNSPEC,
// Set statically to LINK scope for now.
//
// TODO(gvisor.dev/issue/595): Set scope for routes.
Scope: linux.RT_SCOPE_LINK,
Type: linux.RTN_UNICAST,
DstAddr: dstAddr.AsSlice(),
OutputInterface: int32(rt.NIC),
GatewayAddr: rt.Gateway.AsSlice(),
})
}
return routeTable
}
// localRoute constructs a local route from the netlink message.
func (s *Stack) localRoute(msg *nlmsg.Message) (tcpip.Route, *syserr.Error) {
var rtMsg linux.RouteMessage
attrs, ok := msg.GetData(&rtMsg)
if !ok {
return tcpip.Route{}, syserr.ErrInvalidArgument
}
route := inet.Route{
Family: rtMsg.Family,
DstLen: rtMsg.DstLen,
SrcLen: rtMsg.SrcLen,
TOS: rtMsg.TOS,
Table: rtMsg.Table,
Protocol: rtMsg.Protocol,
Scope: rtMsg.Scope,
Type: rtMsg.Type,
Flags: rtMsg.Flags,
}
for !attrs.Empty() {
ahdr, value, rest, ok := attrs.ParseFirst()
if !ok {
return tcpip.Route{}, syserr.ErrInvalidArgument
}
attrs = rest
switch ahdr.Type {
case linux.RTA_DST:
if len(value) < 1 {
return tcpip.Route{}, syserr.ErrInvalidArgument
}
route.DstAddr = value
case linux.RTA_SRC:
if len(value) < 1 {
return tcpip.Route{}, syserr.ErrInvalidArgument
}
route.SrcAddr = value
case linux.RTA_OIF:
oif := nlmsg.BytesView(value)
outputInterface, ok := oif.Int32()
if !ok {
return tcpip.Route{}, syserr.ErrInvalidArgument
}
if _, exist := s.Interfaces()[outputInterface]; !exist {
return tcpip.Route{}, syserr.ErrNoDevice
}
route.OutputInterface = outputInterface
case linux.RTA_GATEWAY:
if len(value) < 1 {
return tcpip.Route{}, syserr.ErrInvalidArgument
}
route.GatewayAddr = value
case linux.RTA_PRIORITY:
default:
log.Warningf("Unknown attribute: %v", ahdr.Type)
return tcpip.Route{}, syserr.ErrNotSupported
}
}
var dest tcpip.Subnet
// When no destination address is provided, the new route might be the default route.
if route.DstAddr == nil {
if route.GatewayAddr == nil {
return tcpip.Route{}, syserr.ErrInvalidArgument
}
switch len(route.GatewayAddr) {
case header.IPv4AddressSize:
subnet, err := tcpip.NewSubnet(tcpip.AddrFromSlice(tcpip.IPv4Zero), tcpip.MaskFromBytes(tcpip.IPv4Zero))
if err != nil {
return tcpip.Route{}, syserr.ErrInvalidArgument
}
dest = subnet
case header.IPv6AddressSize:
subnet, err := tcpip.NewSubnet(tcpip.AddrFromSlice(tcpip.IPv6Zero), tcpip.MaskFromBytes(tcpip.IPv6Zero))
if err != nil {
return tcpip.Route{}, syserr.ErrInvalidArgument
}
dest = subnet
default:
return tcpip.Route{}, syserr.ErrInvalidArgument
}
} else {
dest = tcpip.AddressWithPrefix{
Address: tcpip.AddrFromSlice(route.DstAddr),
PrefixLen: int(route.DstLen)}.Subnet()
}
localRoute := tcpip.Route{
Destination: dest,
Gateway: tcpip.AddrFromSlice(route.GatewayAddr),
NIC: tcpip.NICID(route.OutputInterface),
}
if len(route.SrcAddr) != 0 {
localRoute.SourceHint = tcpip.AddrFromSlice(route.SrcAddr)
}
return localRoute, nil
}
// RemoveRoute implements inte.Stack.RemoveRoute.
func (s *Stack) RemoveRoute(ctx context.Context, msg *nlmsg.Message) *syserr.Error {
localRoute, err := s.localRoute(msg)
if err != nil {
return err
}
if removed := s.Stack.RemoveRoutes(func(rt tcpip.Route) bool {
// Both gateway and NIC are compared with existing routes
// only when they are present in the netlink message.
if localRoute.Gateway.Len() > 0 && !localRoute.Gateway.Equal(rt.Gateway) {
return false
}
if localRoute.NIC > 0 && localRoute.NIC != rt.NIC {
return false
}
return rt.Destination.Equal(localRoute.Destination)
}); removed == 0 {
return syserr.ErrNoProcess
}
return nil
}
// NewRoute implements inet.Stack.NewRoute.
func (s *Stack) NewRoute(ctx context.Context, msg *nlmsg.Message) *syserr.Error {
localRoute, err := s.localRoute(msg)
if err != nil {
return err
}
found := false
for _, rt := range s.Stack.GetRouteTable() {
if localRoute.Equal(rt) {
found = true
break
}
}
flags := msg.Header().Flags
switch {
case !found && flags&linux.NLM_F_CREATE == linux.NLM_F_CREATE:
s.Stack.AddRoute(localRoute)
case found && flags&linux.NLM_F_REPLACE != linux.NLM_F_REPLACE:
return syserr.ErrExists
}
if flags&linux.NLM_F_REPLACE == linux.NLM_F_REPLACE {
s.Stack.ReplaceRoute(localRoute)
}
return nil
}
// IPTables returns the stack's iptables.
func (s *Stack) IPTables() (*stack.IPTables, error) {
return s.Stack.IPTables(), nil
}
// Pause implements inet.Stack.Pause.
func (s *Stack) Pause() {
s.Stack.Pause()
}
// Restore implements inet.Stack.Restore.
func (s *Stack) Restore() {
s.Stack.Restore()
}
// ReplaceConfig implements inet.Stack.ReplaceConfig.
func (s *Stack) ReplaceConfig(st inet.Stack) {
if _, ok := st.(*Stack); !ok {
panic("netstack.Stack cannot be nil when netstack s/r is enabled")
}
s.Stack.ReplaceConfig(st.(*Stack).Stack)
}
// Resume implements inet.Stack.Resume.
func (s *Stack) Resume() {
s.Stack.Resume()
}
// RegisteredEndpoints implements inet.Stack.RegisteredEndpoints.
func (s *Stack) RegisteredEndpoints() []stack.TransportEndpoint {
return s.Stack.RegisteredEndpoints()
}
// CleanupEndpoints implements inet.Stack.CleanupEndpoints.
func (s *Stack) CleanupEndpoints() []stack.TransportEndpoint {
return s.Stack.CleanupEndpoints()
}
// RestoreCleanupEndpoints implements inet.Stack.RestoreCleanupEndpoints.
func (s *Stack) RestoreCleanupEndpoints(es []stack.TransportEndpoint) {
s.Stack.RestoreCleanupEndpoints(es)
}
// SetForwarding implements inet.Stack.SetForwarding.
func (s *Stack) SetForwarding(protocol tcpip.NetworkProtocolNumber, enable bool) error {
if err := s.Stack.SetForwardingDefaultAndAllNICs(protocol, enable); err != nil {
return fmt.Errorf("SetForwardingDefaultAndAllNICs(%d, %t): %s", protocol, enable, err)
}
return nil
}
// PortRange implements inet.Stack.PortRange.
func (s *Stack) PortRange() (uint16, uint16) {
return s.Stack.PortRange()
}
// SetPortRange implements inet.Stack.SetPortRange.
func (s *Stack) SetPortRange(start uint16, end uint16) error {
return syserr.TranslateNetstackError(s.Stack.SetPortRange(start, end)).ToError()
}