Files
visor/pkg/sentry/socket/netstack/stack.go
T
Shailend ChandandgVisor bot b30c659b42 Add RTNLGRP_LINK netlink multicast support for recv
Only netstack supports sending link events, requests to join multicast
groups for netlink sockets continue to be denied when hostinet is in use.

connect() and sendmsg() to RTMGRP_LINK still fails.

PiperOrigin-RevId: 831688843
2025-11-12 22:04:10 -08:00

1067 lines
31 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)"`
eventSubscriber inet.InterfaceEventSubscriber
// linkMu serializes link creation, modification and deletion.
// It is a rough parallel to the per-netns rtnl_mutex in Linux.
linkMu netstackLinkMutex `state:"nosave"`
}
// AddInterfaceEventSubscriber implements inet.InterfaceEventPublisher.AddInterfaceEventSubscriber.
func (s *Stack) AddInterfaceEventSubscriber(sub inet.InterfaceEventSubscriber) {
if s.eventSubscriber != nil {
panic("AddInterfaceEventSubscriber called twice: multiple subscribers yet to be supported")
}
s.eventSubscriber = sub
}
func makeInterfaceInfo(ni *stack.NICInfo) inet.Interface {
return inet.Interface{
Name: ni.Name,
Addr: []byte(ni.LinkAddress),
Flags: uint32(nicStateFlagsToLinux(ni.Flags)),
DeviceType: toLinuxARPHardwareType(ni.ARPHardwareType),
MTU: ni.MTU,
}
}
func (s *Stack) sendChangeEvent(ctx context.Context, id tcpip.NICID) {
if s.eventSubscriber == nil {
return
}
if nicInfo, ok := s.Stack.SingleNICInfo(id); ok {
s.eventSubscriber.OnInterfaceChangeEvent(ctx, int32(id), makeInterfaceInfo(nicInfo))
}
}
func (s *Stack) sendDeleteEvent(ctx context.Context, id tcpip.NICID, nicInfo *stack.NICInfo) {
if s.eventSubscriber == nil {
return
}
s.eventSubscriber.OnInterfaceDeleteEvent(ctx, int32(id), makeInterfaceInfo(nicInfo))
}
// 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)] = makeInterfaceInfo(&ni)
}
return is
}
// RemoveInterface implements inet.Stack.RemoveInterface.
func (s *Stack) RemoveInterface(ctx context.Context, idx int32) error {
s.linkMu.Lock()
defer s.linkMu.Unlock()
nic := tcpip.NICID(idx)
nicInfo, ok := s.Stack.SingleNICInfo(nic)
if !ok {
return syserr.ErrUnknownNICID.ToError()
}
// Don't allow removing the loopback interface.
if nicInfo.Flags.Loopback {
return syserr.ErrNotSupported.ToError()
}
if err := syserr.TranslateNetstackError(s.Stack.RemoveNIC(nic)); err != nil {
return err.ToError()
}
s.sendDeleteEvent(ctx, nic, nicInfo)
return nil
}
// 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.
}
s.linkMu.Lock()
defer s.linkMu.Unlock()
return s.setLinkLocked(ctx, tcpip.NICID(ifinfomsg.Index), attrs)
}
// precondition: s.linkLock is held.
func (s *Stack) setLinkLocked(ctx context.Context, id tcpip.NICID, linkAttrs map[uint16]nlmsg.BytesView) *syserr.Error {
oldNicInfo, ok := s.Stack.SingleNICInfo(id)
if !ok {
return syserr.ErrUnknownNICID
}
// 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
oldID := id
id, err = s.Stack.SetNICStack(id, peer.Stack)
if err != nil {
return syserr.TranslateNetstackError(err)
}
s.sendDeleteEvent(ctx, oldID, oldNicInfo) // inform about exit from old ns
peer.sendChangeEvent(ctx, id) // inform about entry into new ns
// TODO: Once we support IFLA_LINK_NETNSID, we need to call sendChangeEvent on
// the peer interface if this interface is part of a veth pair.
}
}
changed := false
for t, v := range linkAttrs {
switch t {
case linux.IFLA_MASTER:
master, ok := v.Uint32()
if !ok {
return syserr.ErrInvalidArgument
}
if mid, ok := s.Stack.GetNICCoordinatorID(id); ok && mid == tcpip.NICID(master) {
continue
}
if master != 0 {
if err := s.Stack.SetNICCoordinator(id, tcpip.NICID(master)); err != nil {
return syserr.TranslateNetstackError(err)
}
changed = true
}
case linux.IFLA_ADDRESS:
if len(v) != tcpip.LinkAddressSize {
return syserr.ErrInvalidArgument
}
addr := tcpip.LinkAddress(v)
if oldNicInfo.LinkAddress == addr {
continue
}
if err := s.Stack.SetNICAddress(id, addr); err != nil {
return syserr.TranslateNetstackError(err)
}
changed = true
case linux.IFLA_IFNAME:
if oldNicInfo.Name == v.String() {
continue
}
if err := s.Stack.SetNICName(id, v.String()); err != nil {
return syserr.TranslateNetstackError(err)
}
changed = true
case linux.IFLA_MTU:
mtu, ok := v.Uint32()
if !ok {
return syserr.ErrInvalidArgument
}
if oldNicInfo.MTU == mtu {
continue
}
if err := s.Stack.SetNICMTU(id, mtu); err != nil {
return syserr.TranslateNetstackError(err)
}
changed = true
case linux.IFLA_TXQLEN:
// TODO(b/340388892): support IFLA_TXQLEN.
}
}
if changed {
s.sendChangeEvent(ctx, id)
}
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)
}
}
}
s.linkMu.Lock()
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 {
s.linkMu.Unlock()
return syserr.TranslateNetstackError(err)
}
if err := s.setLinkLocked(ctx, id, linkAttrs); err != nil {
s.linkMu.Unlock()
peerEP.Close()
return err
}
s.linkMu.Unlock()
if peerName == "" {
peerName = fmt.Sprintf("veth%d", peerID)
}
peerStack.linkMu.Lock()
defer peerStack.linkMu.Unlock()
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.setLinkLocked(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 {
s.linkMu.Lock()
defer s.linkMu.Unlock()
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.setLinkLocked(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()
}
// SetRemoveNICs implements inet.Stack.SetRemoveNICs.
func (s *Stack) SetRemoveNICs() {
s.Stack.SetRemoveNICs()
}