From 9ed2c54502009fb4b1c7179214a031c12518dfed Mon Sep 17 00:00:00 2001 From: Mikael Magnusson Date: Sat, 6 Feb 2021 01:18:47 +0100 Subject: WIP: GRE runsc --- pkg/abi/linux/netlink_route.go | 40 ++++++++++ pkg/sentry/inet/inet.go | 3 + pkg/sentry/socket/hostinet/stack.go | 5 ++ pkg/sentry/socket/netlink/route/BUILD | 2 + pkg/sentry/socket/netlink/route/protocol.go | 115 ++++++++++++++++++++++++++++ pkg/sentry/socket/netstack/BUILD | 1 + pkg/sentry/socket/netstack/stack.go | 45 +++++++++++ runsc/boot/BUILD | 1 + runsc/boot/loader.go | 2 + 9 files changed, 214 insertions(+) diff --git a/pkg/abi/linux/netlink_route.go b/pkg/abi/linux/netlink_route.go index 581a11b24..514d8cb21 100644 --- a/pkg/abi/linux/netlink_route.go +++ b/pkg/abi/linux/netlink_route.go @@ -165,6 +165,45 @@ 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 +) + +// Interface link GRE attributes, from uapi/linux/if_link.h. +const ( + IFLA_GRE_UNSPEC = 0 + IFLA_GRE_LINK = 1 + IFLA_GRE_IFLAGS = 2 + IFLA_GRE_OFLAGS = 3 + IFLA_GRE_IKEY = 4 + IFLA_GRE_OKEY = 5 + IFLA_GRE_LOCAL = 6 + IFLA_GRE_REMOTE = 7 + IFLA_GRE_TTL = 8 + IFLA_GRE_TOS = 9 + IFLA_GRE_PMTUDISC = 10 + IFLA_GRE_ENCAP_LIMIT = 11 + IFLA_GRE_FLOWINFO = 12 + IFLA_GRE_FLAGS = 13 + IFLA_GRE_ENCAP_TYPE = 14 + IFLA_GRE_ENCAP_FLAGS = 15 + IFLA_GRE_ENCAP_SPORT = 16 + IFLA_GRE_ENCAP_DPORT = 17 + IFLA_GRE_COLLECT_METADATA = 18 + IFLA_GRE_IGNORE_DF = 19 + IFLA_GRE_FWMARK = 20 + IFLA_GRE_ERSPAN_INDEX = 21 + IFLA_GRE_ERSPAN_VER = 22 + IFLA_GRE_ERSPAN_DIR = 23 + IFLA_GRE_ERSPAN_HWID = 24 +) + // InterfaceAddrMessage is struct ifaddrmsg, from uapi/linux/if_addr.h. // // +marshal @@ -194,6 +233,7 @@ const ( ARPHRD_NONE = 65534 ARPHRD_ETHER = 1 ARPHRD_LOOPBACK = 772 + ARPHRD_IPGRE = 778 ) // RouteMessage is struct rtmsg, from uapi/linux/rtnetlink.h. diff --git a/pkg/sentry/inet/inet.go b/pkg/sentry/inet/inet.go index b121fc1b4..81462571c 100644 --- a/pkg/sentry/inet/inet.go +++ b/pkg/sentry/inet/inet.go @@ -34,6 +34,9 @@ type Stack interface { // interface indexes to a slice of associated interface address properties. InterfaceAddrs() map[int32][]InterfaceAddr + // AddGREInterface adds a GRE tunnel interface + AddGREInterface(name string, local, remote InterfaceAddr) (int32, error) + // AddInterfaceAddr adds an address to the network interface identified by // idx. AddInterfaceAddr(idx int32, addr InterfaceAddr) error diff --git a/pkg/sentry/socket/hostinet/stack.go b/pkg/sentry/socket/hostinet/stack.go index 61111ac6c..31e4ab445 100644 --- a/pkg/sentry/socket/hostinet/stack.go +++ b/pkg/sentry/socket/hostinet/stack.go @@ -323,6 +323,11 @@ func (s *Stack) InterfaceAddrs() map[int32][]inet.InterfaceAddr { return addrs } +// AddGREInterface implements inet.Stack.AddGREInterface. +func (s *Stack) AddGREInterface(name string, local, remote inet.InterfaceAddr) (int32, error) { + return -1, linuxerr.EACCES +} + // AddInterfaceAddr implements inet.Stack.AddInterfaceAddr. func (*Stack) AddInterfaceAddr(int32, inet.InterfaceAddr) error { return linuxerr.EACCES diff --git a/pkg/sentry/socket/netlink/route/BUILD b/pkg/sentry/socket/netlink/route/BUILD index c6c04b4e3..77eb76a5e 100644 --- a/pkg/sentry/socket/netlink/route/BUILD +++ b/pkg/sentry/socket/netlink/route/BUILD @@ -10,7 +10,9 @@ go_library( visibility = ["//pkg/sentry:internal"], deps = [ "//pkg/abi/linux", + "//pkg/binary", "//pkg/context", + "//pkg/hostarch", "//pkg/marshal/primitive", "//pkg/sentry/inet", "//pkg/sentry/kernel", diff --git a/pkg/sentry/socket/netlink/route/protocol.go b/pkg/sentry/socket/netlink/route/protocol.go index d526acb73..5851b8826 100644 --- a/pkg/sentry/socket/netlink/route/protocol.go +++ b/pkg/sentry/socket/netlink/route/protocol.go @@ -20,7 +20,9 @@ import ( "golang.org/x/sys/unix" "gvisor.dev/gvisor/pkg/abi/linux" + "gvisor.dev/gvisor/pkg/binary" "gvisor.dev/gvisor/pkg/context" + "gvisor.dev/gvisor/pkg/hostarch" "gvisor.dev/gvisor/pkg/marshal/primitive" "gvisor.dev/gvisor/pkg/sentry/inet" "gvisor.dev/gvisor/pkg/sentry/kernel" @@ -161,6 +163,117 @@ 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 byName []byte + var kind string + var local []byte + var remote []byte + + 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 + } + byName = value[:len(value)-1] + case linux.IFLA_LINKINFO: + var data []byte + + 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) + case linux.IFLA_INFO_DATA: + data = value + } + } + + if data == nil { + return syserr.ErrInvalidArgument + } + + switch kind { + case "gre": + local = make([]byte,4,4) + remote = make([]byte,4,4) + case "ip6gre": + local = make([]byte,16,16) + remote = make([]byte,16,16) + default: + return syserr.ErrInvalidArgument + } + + attrs = netlink.AttrsView(data) + for !attrs.Empty() { + ahdr, value, rest, ok := attrs.ParseFirst() + if !ok { + return syserr.ErrInvalidArgument + } + attrs = rest + + switch ahdr.Type { + case linux.IFLA_GRE_LOCAL: + binary.Unmarshal(value, hostarch.ByteOrder, &local) + case linux.IFLA_GRE_REMOTE: + binary.Unmarshal(value, hostarch.ByteOrder, &remote) + } + } + } + } + + if kind == "" { + return syserr.ErrInvalidArgument + } + + localAddr := inet.InterfaceAddr{ + Addr: local, + } + remoteAddr := inet.InterfaceAddr{ + Addr: remote, + } + _, err := stack.AddGREInterface(string(byName), localAddr, remoteAddr) + if err != nil { + return syserr.ErrInvalidArgument + } + + return nil +} + // delLink handles RTM_DELLINK requests. func (p *Protocol) delLink(ctx context.Context, msg *netlink.Message, ms *netlink.MessageSet) *syserr.Error { stack := inet.StackFromContext(ctx) @@ -578,6 +691,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..94be6344a 100644 --- a/pkg/sentry/socket/netstack/BUILD +++ b/pkg/sentry/socket/netstack/BUILD @@ -51,6 +51,7 @@ go_library( "//pkg/tcpip/transport", "//pkg/tcpip/transport/tcp", "//pkg/tcpip/transport/udp", + "//pkg/tcpip/link/tunnel", "//pkg/usermem", "//pkg/waiter", "@org_golang_x_sys//unix:go_default_library", diff --git a/pkg/sentry/socket/netstack/stack.go b/pkg/sentry/socket/netstack/stack.go index ea199f223..751ba74ac 100644 --- a/pkg/sentry/socket/netstack/stack.go +++ b/pkg/sentry/socket/netstack/stack.go @@ -28,6 +28,7 @@ import ( "gvisor.dev/gvisor/pkg/tcpip/network/ipv6" "gvisor.dev/gvisor/pkg/tcpip/stack" "gvisor.dev/gvisor/pkg/tcpip/transport/tcp" + "gvisor.dev/gvisor/pkg/tcpip/link/tunnel" ) // Stack implements inet.Stack for netstack/tcpip/stack.Stack. @@ -51,6 +52,8 @@ func toLinuxARPHardwareType(t header.ARPHardwareType) uint16 { return linux.ARPHRD_LOOPBACK case header.ARPHardwareEther: return linux.ARPHRD_ETHER + case header.ARPHardwareIPGRE: + return linux.ARPHRD_IPGRE default: panic(fmt.Sprintf("unknown ARPHRD type: %d", t)) } @@ -74,6 +77,12 @@ func (s *Stack) Interfaces() map[int32]inet.Interface { // RemoveInterface implements inet.Stack.RemoveInterface. func (s *Stack) RemoveInterface(idx int32) error { nic := tcpip.NICID(idx) + name := s.Stack.FindNICNameFromID(nic) + + if name == "lo" || name == "eth0" { + return syserr.ErrNotPermitted.ToError() + } + return syserr.TranslateNetstackError(s.Stack.RemoveNIC(nic)).ToError() } @@ -146,6 +155,42 @@ 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 +} + +func (s *Stack) AddGREInterface(name string, local, remote inet.InterfaceAddr) (int32, error) { + var mtu uint32 = 1280 + var idx tcpip.NICID = s.nextInterfaceIndex() + + localAddr := tcpip.Address(local.Addr) + remoteAddr := tcpip.Address(remote.Addr) + + greEP := tunnel.New(mtu) + if remoteAddr == header.IPv4Any { + greEP.Endpoint.LinkEPCapabilities = stack.CapabilityResolutionRequired + } + + if err := s.Stack.CreateNICWithOptions(idx, greEP, stack.NICOptions{Name: name}); err != nil { + return -1, syserr.TranslateNetstackError(err).ToError() + } + + err := greEP.Start(s.Stack, idx, &localAddr, &remoteAddr) + if err != nil { + return -1, syserr.TranslateNetstackError(err).ToError() + } + + return int32(idx), nil +} + // AddInterfaceAddr implements inet.Stack.AddInterfaceAddr. func (s *Stack) AddInterfaceAddr(idx int32, addr inet.InterfaceAddr) error { protocolAddress, err := convertAddr(addr) diff --git a/runsc/boot/BUILD b/runsc/boot/BUILD index ff7a5a44b..a34884a00 100644 --- a/runsc/boot/BUILD +++ b/runsc/boot/BUILD @@ -107,6 +107,7 @@ go_library( "//pkg/tcpip/network/ipv4", "//pkg/tcpip/network/ipv6", "//pkg/tcpip/stack", + "//pkg/tcpip/transport/gre", "//pkg/tcpip/transport/icmp", "//pkg/tcpip/transport/raw", "//pkg/tcpip/transport/tcp", diff --git a/runsc/boot/loader.go b/runsc/boot/loader.go index b46d84e5a..e4e313ada 100644 --- a/runsc/boot/loader.go +++ b/runsc/boot/loader.go @@ -65,6 +65,7 @@ import ( "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/gre" "gvisor.dev/gvisor/pkg/tcpip/transport/icmp" "gvisor.dev/gvisor/pkg/tcpip/transport/raw" "gvisor.dev/gvisor/pkg/tcpip/transport/tcp" @@ -1137,6 +1138,7 @@ func newEmptySandboxNetworkStack(clock tcpip.Clock, uniqueID stack.UniqueID, all udp.NewProtocol, icmp.NewProtocol4, icmp.NewProtocol6, + gre.NewProtocol, } s := netstack.Stack{Stack: stack.New(stack.Options{ NetworkProtocols: netProtos, -- cgit v1.2.3