summaryrefslogtreecommitdiffhomepage
path: root/pkg
diff options
context:
space:
mode:
authorMikael Magnusson <mikma@users.sourceforge.net>2021-02-05 02:04:06 +0100
committerMikael Magnusson <mikma@users.sourceforge.net>2021-09-26 23:34:44 +0200
commite30c52d292d5f03883a83d0a6bec71335ab99342 (patch)
tree60d49c63deb8fa6ed4677428e49e7eacf9dc3225 /pkg
parentea43d9ffc0fd7bc1f42bada76006dbcf26f752af (diff)
Implement dummy network interfacesfeature/dummy-iface
Diffstat (limited to 'pkg')
-rw-r--r--pkg/abi/linux/netlink_route.go10
-rw-r--r--pkg/sentry/inet/inet.go3
-rw-r--r--pkg/sentry/socket/hostinet/stack.go5
-rw-r--r--pkg/sentry/socket/netlink/route/protocol.go73
-rw-r--r--pkg/sentry/socket/netstack/BUILD1
-rw-r--r--pkg/sentry/socket/netstack/stack.go24
-rw-r--r--pkg/tcpip/link/dummy/BUILD14
-rw-r--r--pkg/tcpip/link/dummy/dummy.go100
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
+}