summaryrefslogtreecommitdiffhomepage
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-21 20:01:54 +0200
commitb2718e250d7ef39048f03b9cc5eeeccf23d5ca8e (patch)
treef8b6b9593447ca83f46aab2ed2f08c0981d11b00
parente819029f3ad059bfc1635b7f2a196c332fa7532f (diff)
WIP: GRE
-rw-r--r--pkg/tcpip/header/BUILD1
-rw-r--r--pkg/tcpip/header/arp.go1
-rw-r--r--pkg/tcpip/header/gre.go45
-rw-r--r--pkg/tcpip/header/parse/parse.go10
-rw-r--r--pkg/tcpip/link/channel/channel.go5
-rw-r--r--pkg/tcpip/link/tunnel/BUILD21
-rw-r--r--pkg/tcpip/link/tunnel/gre.go250
-rw-r--r--pkg/tcpip/transport/gre/BUILD18
-rw-r--r--pkg/tcpip/transport/gre/forwarder.go67
-rw-r--r--pkg/tcpip/transport/gre/protocol.go115
10 files changed, 531 insertions, 2 deletions
diff --git a/pkg/tcpip/header/BUILD b/pkg/tcpip/header/BUILD
index 01240f5d0..20144ee85 100644
--- a/pkg/tcpip/header/BUILD
+++ b/pkg/tcpip/header/BUILD
@@ -8,6 +8,7 @@ go_library(
"arp.go",
"checksum.go",
"eth.go",
+ "gre.go",
"gue.go",
"icmpv4.go",
"icmpv6.go",
diff --git a/pkg/tcpip/header/arp.go b/pkg/tcpip/header/arp.go
index 83189676e..7cc0e2db9 100644
--- a/pkg/tcpip/header/arp.go
+++ b/pkg/tcpip/header/arp.go
@@ -41,6 +41,7 @@ const (
// https://www.iana.org/assignments/arp-parameters/arp-parameters.xhtml#arp-parameters-2
ARPHardwareEther ARPHardwareType = 1
ARPHardwareLoopback ARPHardwareType = 2
+ ARPHardwareIPGRE ARPHardwareType = 3
)
// ARPOp is an ARP opcode.
diff --git a/pkg/tcpip/header/gre.go b/pkg/tcpip/header/gre.go
new file mode 100644
index 000000000..d4b1f200b
--- /dev/null
+++ b/pkg/tcpip/header/gre.go
@@ -0,0 +1,45 @@
+// 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 header
+
+import (
+ "encoding/binary"
+
+ "gvisor.dev/gvisor/pkg/tcpip"
+)
+
+const (
+ greProtocolType = 2
+)
+
+// GRE represents a GRE header stored in a byte array.
+type GRE []byte
+
+const (
+ GREHeaderSize = 4
+
+ // GREProtocolNumber is GRE's transport protocol number.
+ GREProtocolNumber tcpip.TransportProtocolNumber = 47
+)
+
+func (b GRE) ProtocolType() tcpip.NetworkProtocolNumber {
+ proto := binary.BigEndian.Uint16(b[greProtocolType:])
+ return tcpip.NetworkProtocolNumber(proto)
+}
+
+// SetLength sets the "length" field of the udp header.
+func (b GRE) SetProtocolType(protocol tcpip.NetworkProtocolNumber) {
+ binary.BigEndian.PutUint16(b[greProtocolType:], uint16(protocol))
+}
diff --git a/pkg/tcpip/header/parse/parse.go b/pkg/tcpip/header/parse/parse.go
index 1c913b5e1..107665d9d 100644
--- a/pkg/tcpip/header/parse/parse.go
+++ b/pkg/tcpip/header/parse/parse.go
@@ -175,3 +175,13 @@ func TCP(pkt *stack.PacketBuffer) bool {
pkt.TransportProtocolNumber = header.TCPProtocolNumber
return ok
}
+
+// GRE parses a GRE packet found in pkt.Data and populates pkt's transport
+// header with the GRE header.
+//
+// Returns true if the header was successfully parsed.
+func GRE(pkt *stack.PacketBuffer) bool {
+ _, ok := pkt.TransportHeader().Consume(header.GREHeaderSize)
+ pkt.TransportProtocolNumber = header.GREProtocolNumber
+ return ok
+}
diff --git a/pkg/tcpip/link/channel/channel.go b/pkg/tcpip/link/channel/channel.go
index 658557d62..fc6993722 100644
--- a/pkg/tcpip/link/channel/channel.go
+++ b/pkg/tcpip/link/channel/channel.go
@@ -136,6 +136,7 @@ type Endpoint struct {
linkAddr tcpip.LinkAddress
LinkEPCapabilities stack.LinkEndpointCapabilities
SupportedGSOKind stack.SupportedGSO
+ NMaxHeaderLength uint16
// Outbound packet queue.
q *queue
@@ -229,8 +230,8 @@ func (e *Endpoint) SupportedGSO() stack.SupportedGSO {
// MaxHeaderLength returns the maximum size of the link layer header. Given it
// doesn't have a header, it just returns 0.
-func (*Endpoint) MaxHeaderLength() uint16 {
- return 0
+func (e *Endpoint) MaxHeaderLength() uint16 {
+ return e.NMaxHeaderLength
}
// LinkAddress returns the link address of this endpoint.
diff --git a/pkg/tcpip/link/tunnel/BUILD b/pkg/tcpip/link/tunnel/BUILD
new file mode 100644
index 000000000..3b75b8ee3
--- /dev/null
+++ b/pkg/tcpip/link/tunnel/BUILD
@@ -0,0 +1,21 @@
+load("//tools:defs.bzl", "go_library")
+
+package(licenses = ["notice"])
+
+go_library(
+ name = "tunnel",
+ srcs = ["gre.go"],
+ visibility = ["//visibility:public"],
+ deps = [
+ "//pkg/tcpip",
+ "//pkg/tcpip/buffer",
+ "//pkg/tcpip/header",
+ "//pkg/tcpip/link/channel",
+ "//pkg/tcpip/network/ipv4",
+ "//pkg/tcpip/network/ipv6",
+ "//pkg/tcpip/stack",
+ "//pkg/tcpip/transport/gre",
+ "//pkg/tcpip/transport/raw",
+ "//pkg/waiter",
+ ],
+)
diff --git a/pkg/tcpip/link/tunnel/gre.go b/pkg/tcpip/link/tunnel/gre.go
new file mode 100644
index 000000000..ef2f9c4de
--- /dev/null
+++ b/pkg/tcpip/link/tunnel/gre.go
@@ -0,0 +1,250 @@
+// 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 tunnel
+
+import (
+ "context"
+ "errors"
+ "log"
+
+ "gvisor.dev/gvisor/pkg/tcpip"
+ "gvisor.dev/gvisor/pkg/tcpip/buffer"
+ "gvisor.dev/gvisor/pkg/tcpip/header"
+ "gvisor.dev/gvisor/pkg/tcpip/link/channel"
+ "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/raw"
+ "gvisor.dev/gvisor/pkg/waiter"
+)
+
+var SIZE = 16
+
+type Endpoint struct {
+ channel.Endpoint
+ LocalAddr tcpip.Address
+ RemoteAddr tcpip.Address
+}
+
+type writer Endpoint
+
+func New(mtu uint32) *Endpoint {
+ var linkAddress tcpip.LinkAddress
+
+ ch := channel.New(SIZE, mtu, linkAddress)
+ return &Endpoint{
+ Endpoint: *ch,
+ }
+}
+
+func (e *Endpoint) GetChannel() *channel.Endpoint {
+ return &e.Endpoint
+}
+
+func (e *Endpoint) GetLocalAddress() tcpip.Address {
+ return e.LocalAddr
+}
+
+func (e *Endpoint) GetRemoteAddress() tcpip.Address {
+ return e.RemoteAddr
+}
+
+// Attach saves the stack network-layer dispatcher for use later when packets
+// are injected.
+func (e *Endpoint) Attach(dispatcher stack.NetworkDispatcher) {
+ log.Println("GRE: Attach")
+
+ e.Endpoint.Attach(dispatcher)
+}
+
+type GrePacketInfo struct {
+ pi *channel.PacketInfo
+}
+
+func (g GrePacketInfo) Len() int {
+ return g.pi.Pkt.Size()
+}
+
+func (g GrePacketInfo) Read(p []byte) (n int, err error){
+ pkt := g.pi.Pkt
+ size := pkt.Size()
+
+ if size > len(p) {
+ return 0, errors.New("Buffer too small")
+ }
+ vv := buffer.NewVectorisedView(size, pkt.Views())
+ pos := 0
+ for {
+ copied, err := vv.Read(p[pos:])
+ log.Printf("VectorisedView Read: %d %d %d %v", size, pos, copied, err)
+ if err != nil {
+ return 0, errors.New("&tcpip.ErrBadBuffer{}")
+ }
+ pos = pos + copied
+ if pos == size {
+ break
+ }
+ }
+
+ return pos, nil
+}
+
+func (g GrePacketInfo) Payload(size int) ([]byte, tcpip.Error){
+ log.Println("Payload")
+ return nil, &tcpip.ErrNotSupported{}
+}
+
+type GreHandlerInfo struct {
+ ChEp *channel.Endpoint
+ Raw tcpip.Endpoint
+ Raddr tcpip.FullAddress
+ Stack *stack.Stack
+ NIC tcpip.NICID
+}
+
+func (info *GreHandlerInfo) greHandler(req *gre.ForwarderRequest) {
+ pkt := req.Pkt
+ log.Println("greHandler: ", req.Pkt.Size(), req.Pkt.Views())
+ greHdr := header.GRE(pkt.TransportHeader().View())
+ proto := greHdr.ProtocolType()
+ pktData := pkt.Data()
+ views := pktData.Views()
+ size := pktData.Size()
+ data := buffer.NewVectorisedView(size, views)
+ newPkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
+ Data: data,
+ })
+ log.Printf("greHandler proto: %d cloned: %v", proto, newPkt.Views())
+
+ info.ChEp.InjectInbound(proto, newPkt)
+}
+
+func ipAny(protocol tcpip.NetworkProtocolNumber) (tcpip.Address, tcpip.Error) {
+ switch protocol {
+ case header.IPv4ProtocolNumber:
+ return header.IPv4Any, nil
+ case header.IPv6ProtocolNumber:
+ return header.IPv6Any, nil
+ default:
+ log.Printf("Unsupported protocol %v", protocol)
+ return tcpip.Address([]byte{}), &tcpip.ErrNotSupported{}
+ }
+}
+
+func (info *GreHandlerInfo) greRead(ep *channel.Endpoint) {
+ for {
+ pi, err := ep.ReadContext(context.Background());
+ linkHdr := pi.Pkt.LinkHeader()
+ greHdr := header.GRE(linkHdr.Push(header.GREHeaderSize))
+ greHdr.SetProtocolType(pi.Proto)
+ log.Printf("greRead %d %v %v %v", pi.Proto, pi, err, greHdr)
+ opts := tcpip.WriteOptions{
+ //To: &info.Raddr
+ }
+ if info.Raddr.Addr == header.IPv4Any {
+ protocol := pi.Pkt.NetworkProtocolNumber
+ nw := pi.Pkt.Network()
+ addr := nw.DestinationAddress()
+ laddr, err := ipAny(protocol); if err != nil {
+ continue
+ }
+
+ err = info.Stack.GetLinkAddress(info.NIC, addr, laddr, protocol, func (res stack.LinkResolutionResult) {
+
+ if res.Err != nil {
+ return
+ }
+
+ opts.To = &tcpip.FullAddress{
+ NIC: 0,
+ Addr: tcpip.Address(res.LinkAddress),
+ }
+ })
+ if err != nil {
+ continue
+ }
+ }
+ info.Raw.Write(GrePacketInfo{&pi}, opts)
+ }
+}
+
+func networkProtocolNumber(addr *tcpip.Address) tcpip.NetworkProtocolNumber {
+ if addr.To4() != "" {
+ return ipv4.ProtocolNumber
+ } else {
+ return ipv6.ProtocolNumber
+ }
+}
+
+// ARPHardwareType implements stack.LinkEndpoint.ARPHardwareType.
+func (*Endpoint) ARPHardwareType() header.ARPHardwareType {
+ return header.ARPHardwareIPGRE
+}
+
+func (e *Endpoint) Start(s *stack.Stack, nic tcpip.NICID, laddr, raddr *tcpip.Address) tcpip.Error {
+ proto := networkProtocolNumber(laddr)
+ if proto != networkProtocolNumber(raddr) {
+ return &tcpip.ErrBadAddress{}
+ }
+
+ // Create TCP endpoint.
+ var rawWq waiter.Queue
+ rawEp, tcperr := raw.NewEndpoint(s, proto, header.GREProtocolNumber, &rawWq)
+ if tcperr != nil {
+ return tcperr
+ }
+ log.Println("EP: %s", rawEp)
+
+ e.LocalAddr = *laddr
+ e.RemoteAddr = *raddr
+
+ fraddr := tcpip.FullAddress{NIC: 0, Addr: *raddr}
+ flAddr := tcpip.FullAddress{NIC: 0, Addr: *laddr}
+ tcperr = rawEp.Bind(flAddr)
+ if tcperr != nil {
+ return tcperr
+ }
+ log.Printf("Remote: %v %v", raddr, fraddr)
+
+ // Create GRE
+ // greEP := grelink.New(mtu - 24)
+ chEP := e.GetChannel()
+ // TODO detect IPv4/IPv6
+ e.NMaxHeaderLength = header.IPv6FixedHeaderSize + header.GREHeaderSize
+
+ greInfo := GreHandlerInfo{
+// Ep: loEP,
+ ChEp: chEP,
+ Raw: rawEp,
+ Raddr: fraddr,
+ Stack: s,
+ NIC: nic,
+ }
+ greFwd := gre.NewForwarder(s, greInfo.greHandler)
+ s.SetTransportProtocolHandler(header.GREProtocolNumber, greFwd.HandlePacket)
+
+ go greInfo.greRead(chEP)
+
+ any, _ := ipAny(proto)
+ if fraddr.Addr != any {
+ tcperr = rawEp.Connect(fraddr)
+ if tcperr != nil {
+ return tcperr
+ }
+ }
+
+ return nil
+}
diff --git a/pkg/tcpip/transport/gre/BUILD b/pkg/tcpip/transport/gre/BUILD
new file mode 100644
index 000000000..a812c1000
--- /dev/null
+++ b/pkg/tcpip/transport/gre/BUILD
@@ -0,0 +1,18 @@
+load("//tools:defs.bzl", "go_library")
+
+package(licenses = ["notice"])
+
+go_library(
+ name = "gre",
+ srcs = ["forwarder.go", "protocol.go"],
+ visibility = ["//visibility:public"],
+ deps = [
+ "//pkg/tcpip",
+ "//pkg/tcpip/buffer",
+ "//pkg/tcpip/header",
+ "//pkg/tcpip/header/parse",
+ "//pkg/tcpip/stack",
+ "//pkg/tcpip/transport/raw",
+ "//pkg/waiter",
+ ],
+)
diff --git a/pkg/tcpip/transport/gre/forwarder.go b/pkg/tcpip/transport/gre/forwarder.go
new file mode 100644
index 000000000..83f63c844
--- /dev/null
+++ b/pkg/tcpip/transport/gre/forwarder.go
@@ -0,0 +1,67 @@
+// 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 gre
+
+import (
+ "gvisor.dev/gvisor/pkg/tcpip/stack"
+)
+
+// Forwarder is a session request forwarder, which allows clients to decide
+// what to do with a session request, for example: ignore it, or process it.
+//
+// The canonical way of using it is to pass the Forwarder.HandlePacket function
+// to stack.SetTransportProtocolHandler.
+type Forwarder struct {
+ handler func(*ForwarderRequest)
+
+ stack *stack.Stack
+}
+
+// NewForwarder allocates and initializes a new forwarder.
+func NewForwarder(s *stack.Stack, handler func(*ForwarderRequest)) *Forwarder {
+ return &Forwarder{
+ stack: s,
+ handler: handler,
+ }
+}
+
+// HandlePacket handles all packets.
+//
+// This function is expected to be passed as an argument to the
+// stack.SetTransportProtocolHandler function.
+func (f *Forwarder) HandlePacket(id stack.TransportEndpointID, pkt *stack.PacketBuffer) bool {
+ f.handler(&ForwarderRequest{
+ Stack: f.stack,
+ Id: id,
+ Pkt: pkt,
+ })
+
+ return true
+}
+
+// ForwarderRequest represents a session request received by the forwarder and
+// passed to the client. Clients may optionally create an endpoint to represent
+// it via CreateEndpoint.
+type ForwarderRequest struct {
+ Stack *stack.Stack
+ Id stack.TransportEndpointID
+ Pkt *stack.PacketBuffer
+}
+
+// ID returns the 4-tuple (src address, src port, dst address, dst port) that
+// represents the session request.
+func (r *ForwarderRequest) ID() stack.TransportEndpointID {
+ return r.Id
+}
diff --git a/pkg/tcpip/transport/gre/protocol.go b/pkg/tcpip/transport/gre/protocol.go
new file mode 100644
index 000000000..0a58cb73a
--- /dev/null
+++ b/pkg/tcpip/transport/gre/protocol.go
@@ -0,0 +1,115 @@
+// 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 gre contains the implementation of the GRE transport protocol.
+package gre
+
+import (
+ "log"
+
+ "gvisor.dev/gvisor/pkg/tcpip"
+ "gvisor.dev/gvisor/pkg/tcpip/buffer"
+ "gvisor.dev/gvisor/pkg/tcpip/header"
+ "gvisor.dev/gvisor/pkg/tcpip/header/parse"
+ "gvisor.dev/gvisor/pkg/tcpip/stack"
+ "gvisor.dev/gvisor/pkg/tcpip/transport/raw"
+ "gvisor.dev/gvisor/pkg/waiter"
+)
+
+const (
+ // ProtocolNumber is the gre protocol number.
+ ProtocolNumber = header.GREProtocolNumber
+
+ // MinBufferSize is the smallest size of a receive or send buffer.
+ MinBufferSize = 4 << 10 // 4KiB bytes.
+
+ // DefaultSendBufferSize is the default size of the send buffer for
+ // an endpoint.
+ DefaultSendBufferSize = 32 << 10 // 32KiB
+
+ // DefaultReceiveBufferSize is the default size of the receive buffer
+ // for an endpoint.
+ DefaultReceiveBufferSize = 32 << 10 // 32KiB
+
+ // MaxBufferSize is the largest size a receive/send buffer can grow to.
+ MaxBufferSize = 4 << 20 // 4MiB
+)
+
+type protocol struct {
+ stack *stack.Stack
+}
+
+// Number returns the gre protocol number.
+func (*protocol) Number() tcpip.TransportProtocolNumber {
+ return ProtocolNumber
+}
+
+// NewEndpoint creates a new gre endpoint.
+func (p *protocol) NewEndpoint(netProto tcpip.NetworkProtocolNumber, waiterQueue *waiter.Queue) (tcpip.Endpoint, tcpip.Error) {
+ // return newEndpoint(p.stack, netProto, waiterQueue), nil
+ return nil, &tcpip.ErrUnknownProtocolOption{}
+}
+
+// NewRawEndpoint creates a new raw GRE endpoint. It implements
+// stack.TransportProtocol.NewRawEndpoint.
+func (p *protocol) NewRawEndpoint(netProto tcpip.NetworkProtocolNumber, waiterQueue *waiter.Queue) (tcpip.Endpoint, tcpip.Error) {
+ return raw.NewEndpoint(p.stack, netProto, ProtocolNumber, waiterQueue)
+}
+
+// MinimumPacketSize returns the minimum valid gre packet size.
+func (*protocol) MinimumPacketSize() int {
+ // TODO ?
+ return header.GREHeaderSize
+}
+
+// ParsePorts returns the source and destination ports stored in the given gre
+// packet.
+func (*protocol) ParsePorts(v buffer.View) (src, dst uint16, err tcpip.Error) {
+ // h := header.GRE(v)
+ // return h.SourcePort(), h.DestinationPort(), nil
+ return 0, 0, nil
+}
+
+// HandleUnknownDestinationPacket handles packets that are targeted at this
+// protocol but don't match any existing endpoint.
+func (p *protocol) HandleUnknownDestinationPacket(id stack.TransportEndpointID, pkt *stack.PacketBuffer) stack.UnknownDestinationPacketDisposition {
+ log.Println("HandleUnknownDestinationPacket")
+ return stack.UnknownDestinationPacketHandled
+}
+
+// SetOption implements stack.TransportProtocol.SetOption.
+func (*protocol) SetOption(tcpip.SettableTransportProtocolOption) tcpip.Error {
+ return &tcpip.ErrUnknownProtocolOption{}
+}
+
+// Option implements stack.TransportProtocol.Option.
+func (*protocol) Option(tcpip.GettableTransportProtocolOption) tcpip.Error {
+ return &tcpip.ErrUnknownProtocolOption{}
+}
+
+// Close implements stack.TransportProtocol.Close.
+func (*protocol) Close() {}
+
+// Wait implements stack.TransportProtocol.Wait.
+func (*protocol) Wait() {}
+
+// Parse implements stack.TransportProtocol.Parse.
+func (*protocol) Parse(pkt *stack.PacketBuffer) bool {
+ return parse.GRE(pkt)
+}
+
+// NewProtocol returns a GRE transport protocol.
+func NewProtocol(s *stack.Stack) stack.TransportProtocol {
+ return &protocol{stack: s}
+}