diff options
author | Mikael Magnusson <mikma@users.sourceforge.net> | 2021-02-05 02:04:06 +0100 |
---|---|---|
committer | Mikael Magnusson <mikma@users.sourceforge.net> | 2021-09-26 23:34:44 +0200 |
commit | e30c52d292d5f03883a83d0a6bec71335ab99342 (patch) | |
tree | 60d49c63deb8fa6ed4677428e49e7eacf9dc3225 | |
parent | ea43d9ffc0fd7bc1f42bada76006dbcf26f752af (diff) |
Implement dummy network interfacesfeature/dummy-iface
-rw-r--r-- | pkg/abi/linux/netlink_route.go | 10 | ||||
-rw-r--r-- | pkg/sentry/inet/inet.go | 3 | ||||
-rw-r--r-- | pkg/sentry/socket/hostinet/stack.go | 5 | ||||
-rw-r--r-- | pkg/sentry/socket/netlink/route/protocol.go | 73 | ||||
-rw-r--r-- | pkg/sentry/socket/netstack/BUILD | 1 | ||||
-rw-r--r-- | pkg/sentry/socket/netstack/stack.go | 24 | ||||
-rw-r--r-- | pkg/tcpip/link/dummy/BUILD | 14 | ||||
-rw-r--r-- | pkg/tcpip/link/dummy/dummy.go | 100 |
8 files changed, 230 insertions, 0 deletions
diff --git a/pkg/abi/linux/netlink_route.go b/pkg/abi/linux/netlink_route.go index 932a06bac..71e45cbfc 100644 --- a/pkg/abi/linux/netlink_route.go +++ b/pkg/abi/linux/netlink_route.go @@ -165,6 +165,16 @@ const ( IFLA_GSO_MAX_SIZE = 41 ) +// Interface link info attributes, from uapi/linux/if_link.h. +const ( + IFLA_INFO_UNSPEC = 0 + IFLA_INFO_KIND = 1 + IFLA_INFO_DATA = 2 + IFLA_INFO_XSTATS = 3 + IFLA_INFO_SLAVE_KIND = 4 + IFLA_INFO_SLAVE_DATA = 5 +) + // InterfaceAddrMessage is struct ifaddrmsg, from uapi/linux/if_addr.h. // // +marshal diff --git a/pkg/sentry/inet/inet.go b/pkg/sentry/inet/inet.go index f92706ab7..2e128f7a5 100644 --- a/pkg/sentry/inet/inet.go +++ b/pkg/sentry/inet/inet.go @@ -27,6 +27,9 @@ type Stack interface { // integers. Interfaces() map[int32]Interface + // AddDummyInterface adds a new dummy network interface + AddDummyInterface(name string) (int32, error) + // RemoveInterface removes the specified network interface. RemoveInterface(idx int32) error diff --git a/pkg/sentry/socket/hostinet/stack.go b/pkg/sentry/socket/hostinet/stack.go index b80d64c9a..85cc1b586 100644 --- a/pkg/sentry/socket/hostinet/stack.go +++ b/pkg/sentry/socket/hostinet/stack.go @@ -309,6 +309,11 @@ func (s *Stack) Interfaces() map[int32]inet.Interface { return interfaces } +// AddDummyInterface implements inet.Stack.AddDummyInterface. +func (s *Stack) AddDummyInterface(name string) (int32, error) { + return -1, linuxerr.EACCES +} + // RemoveInterface implements inet.Stack.RemoveInterface. func (*Stack) RemoveInterface(int32) error { return linuxerr.EACCES diff --git a/pkg/sentry/socket/netlink/route/protocol.go b/pkg/sentry/socket/netlink/route/protocol.go index 38f714c48..359db6e99 100644 --- a/pkg/sentry/socket/netlink/route/protocol.go +++ b/pkg/sentry/socket/netlink/route/protocol.go @@ -161,6 +161,77 @@ func (p *Protocol) getLink(ctx context.Context, msg *netlink.Message, ms *netlin return nil } +// newLink handles RTM_NEWLINK requests. +func (p *Protocol) newLink(ctx context.Context, msg *netlink.Message, ms *netlink.MessageSet) *syserr.Error { + stack := inet.StackFromContext(ctx) + if stack == nil { + // No network devices. + return nil + } + + // Parse message. + var ifi linux.InterfaceInfoMessage + attrs, ok := msg.GetData(&ifi) + if !ok { + return syserr.ErrInvalidArgument + } + + if attrs.Empty() { + return nil + } + + // Parse attributes. + var name []byte + var kind string + + for !attrs.Empty() { + ahdr, value, rest, ok := attrs.ParseFirst() + if !ok { + return syserr.ErrInvalidArgument + } + attrs = rest + + switch ahdr.Type { + case linux.IFLA_IFNAME: + if len(value) < 1 { + return syserr.ErrInvalidArgument + } + name = value[:len(value)-1] + case linux.IFLA_LINKINFO: + attrs := netlink.AttrsView(value) + for !attrs.Empty() { + ahdr, value, rest, ok := attrs.ParseFirst() + if !ok { + return syserr.ErrInvalidArgument + } + attrs = rest + + switch ahdr.Type { + case linux.IFLA_INFO_KIND: + if len(value) < 1 { + return syserr.ErrInvalidArgument + } + kind = string(value) + } + } + } + } + + var ret *syserr.Error = nil + + switch kind { + case "dummy": + _, err := stack.AddDummyInterface(string(name)) + if err != nil { + ret = syserr.ErrInvalidArgument + } + default: + ret = syserr.ErrInvalidArgument + } + + return ret +} + // delLink handles RTM_DELLINK requests. func (p *Protocol) delLink(ctx context.Context, msg *netlink.Message, ms *netlink.MessageSet) *syserr.Error { stack := inet.StackFromContext(ctx) @@ -704,6 +775,8 @@ func (p *Protocol) ProcessMessage(ctx context.Context, msg *netlink.Message, ms switch hdr.Type { case linux.RTM_GETLINK: return p.getLink(ctx, msg, ms) + case linux.RTM_NEWLINK: + return p.newLink(ctx, msg, ms) case linux.RTM_DELLINK: return p.delLink(ctx, msg, ms) case linux.RTM_GETROUTE: diff --git a/pkg/sentry/socket/netstack/BUILD b/pkg/sentry/socket/netstack/BUILD index bf5ec4558..b6da24820 100644 --- a/pkg/sentry/socket/netstack/BUILD +++ b/pkg/sentry/socket/netstack/BUILD @@ -44,6 +44,7 @@ go_library( "//pkg/syserr", "//pkg/tcpip", "//pkg/tcpip/header", + "//pkg/tcpip/link/dummy", "//pkg/tcpip/link/tun", "//pkg/tcpip/network/ipv4", "//pkg/tcpip/network/ipv6", diff --git a/pkg/sentry/socket/netstack/stack.go b/pkg/sentry/socket/netstack/stack.go index 3fceb4cd5..360b270c1 100644 --- a/pkg/sentry/socket/netstack/stack.go +++ b/pkg/sentry/socket/netstack/stack.go @@ -24,6 +24,7 @@ import ( "gvisor.dev/gvisor/pkg/syserr" "gvisor.dev/gvisor/pkg/tcpip" "gvisor.dev/gvisor/pkg/tcpip/header" + "gvisor.dev/gvisor/pkg/tcpip/link/dummy" "gvisor.dev/gvisor/pkg/tcpip/network/ipv4" "gvisor.dev/gvisor/pkg/tcpip/network/ipv6" "gvisor.dev/gvisor/pkg/tcpip/stack" @@ -71,6 +72,17 @@ func (s *Stack) Interfaces() map[int32]inet.Interface { return is } +func (s *Stack) AddDummyInterface(name string) (int32, error) { + var idx tcpip.NICID = s.nextInterfaceIndex() + + dummyEP := dummy.New() + if err := s.Stack.CreateNICWithOptions(idx, dummyEP, stack.NICOptions{Name: name}); err != nil { + return -1, syserr.TranslateNetstackError(err).ToError() + } + + return int32(idx), nil +} + // RemoveInterface implements inet.Stack.RemoveInterface. func (s *Stack) RemoveInterface(idx int32) error { nic := tcpip.NICID(idx) @@ -146,6 +158,18 @@ func convertAddr(addr inet.InterfaceAddr) (tcpip.ProtocolAddress, error) { return protocolAddress, nil } +func (s *Stack) nextInterfaceIndex() tcpip.NICID { + var maxIdx tcpip.NICID = 0 + + for id, _ := range s.Stack.NICInfo() { + if id > maxIdx { + maxIdx = id + } + } + + return maxIdx + 1 +} + // AddInterfaceAddr implements inet.Stack.AddInterfaceAddr. func (s *Stack) AddInterfaceAddr(idx int32, addr inet.InterfaceAddr) error { protocolAddress, err := convertAddr(addr) diff --git a/pkg/tcpip/link/dummy/BUILD b/pkg/tcpip/link/dummy/BUILD new file mode 100644 index 000000000..050eb9397 --- /dev/null +++ b/pkg/tcpip/link/dummy/BUILD @@ -0,0 +1,14 @@ +load("//tools:defs.bzl", "go_library") + +package(licenses = ["notice"]) + +go_library( + name = "dummy", + srcs = ["dummy.go"], + visibility = ["//visibility:public"], + deps = [ + "//pkg/tcpip", + "//pkg/tcpip/header", + "//pkg/tcpip/stack", + ], +) diff --git a/pkg/tcpip/link/dummy/dummy.go b/pkg/tcpip/link/dummy/dummy.go new file mode 100644 index 000000000..7e8c39ef1 --- /dev/null +++ b/pkg/tcpip/link/dummy/dummy.go @@ -0,0 +1,100 @@ +// Copyright 2021 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 dummy provides the implemention of dummy data-link layer +// endpoints. Such endpoints just discards outgoing packets. +// +// Dummy endpoints can be used in the networking stack by calling New() to +// create a new endpoint, and then passing it as an argument to +// Stack.CreateNIC(). +package dummy + +import ( + "gvisor.dev/gvisor/pkg/tcpip" + "gvisor.dev/gvisor/pkg/tcpip/header" + "gvisor.dev/gvisor/pkg/tcpip/stack" +) + +type endpoint struct { + dispatcher stack.NetworkDispatcher +} + +// New creates a new dummy endpoint. This link-layer endpoint just +// discards outbound packets. +func New() stack.LinkEndpoint { + return &endpoint{} +} + +// Attach implements stack.LinkEndpoint.Attach. It just saves the stack network- +// layer dispatcher for later use when packets need to be dispatched. +func (e *endpoint) Attach(dispatcher stack.NetworkDispatcher) { + e.dispatcher = dispatcher +} + +// IsAttached implements stack.LinkEndpoint.IsAttached. +func (e *endpoint) IsAttached() bool { + return e.dispatcher != nil +} + +// MTU implements stack.LinkEndpoint.MTU. It returns a constant that matches the +// default value for the linux dummy interface. +func (*endpoint) MTU() uint32 { + return 65536 +} + +// Capabilities implements stack.LinkEndpoint.Capabilities. Dummy advertises +// itself as supporting checksum offload. +func (*endpoint) Capabilities() stack.LinkEndpointCapabilities { + return stack.CapabilityRXChecksumOffload | stack.CapabilityTXChecksumOffload | stack.CapabilitySaveRestore +} + +// MaxHeaderLength implements stack.LinkEndpoint.MaxHeaderLength. Given that the +// dummy interface doesn't have a header, it just returns 0. +func (*endpoint) MaxHeaderLength() uint16 { + return 0 +} + +// LinkAddress returns the link address of this endpoint. +func (*endpoint) LinkAddress() tcpip.LinkAddress { + // TODO add link address + return "" +} + +// Wait implements stack.LinkEndpoint.Wait. +func (*endpoint) Wait() {} + +// WritePacket implements stack.LinkEndpoint.WritePacket. It delivers outbound +// packets to the network-layer dispatcher. +func (e *endpoint) WritePacket(_ stack.RouteInfo, _ tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) tcpip.Error { + return e.WriteRawPacket(pkt) +} + +// WritePackets implements stack.LinkEndpoint.WritePackets. +func (e *endpoint) WritePackets(stack.RouteInfo, stack.PacketBufferList, tcpip.NetworkProtocolNumber) (int, tcpip.Error) { + panic("not implemented") +} + +// ARPHardwareType implements stack.LinkEndpoint.ARPHardwareType. +func (*endpoint) ARPHardwareType() header.ARPHardwareType { + return header.ARPHardwareEther +} + +func (e *endpoint) AddHeader(local, remote tcpip.LinkAddress, protocol tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) { +} + +// WriteRawPacket implements stack.LinkEndpoint. +func (e *endpoint) WriteRawPacket(pkt *stack.PacketBuffer) tcpip.Error { + // Discard + return nil +} |