summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorMikael Magnusson <mikma@users.sourceforge.net>2021-01-23 01:14:51 +0100
committerMikael Magnusson <mikma@users.sourceforge.net>2021-01-23 21:43:01 +0100
commit2ce080f581556f9512a481e7fcddecaed0ae7be7 (patch)
tree13e32a386e0fbddd21aa22f07fd1b841a51e2099
parent0627e26831c8c58a5fab619d127587dc1ad8a19b (diff)
WIP GRE tunnel
-rw-r--r--pkg/tcpip/header/gre.go19
-rw-r--r--pkg/tcpip/header/parse/parse.go10
-rw-r--r--pkg/tcpip/link/channel/channel.go5
-rw-r--r--pkg/tcpip/link/gre/gre.go92
-rw-r--r--pkg/tcpip/sample/tun_tcp_connect/main.go145
-rw-r--r--pkg/tcpip/transport/gre/forwarder.go67
-rw-r--r--pkg/tcpip/transport/gre/protocol.go114
7 files changed, 445 insertions, 7 deletions
diff --git a/pkg/tcpip/header/gre.go b/pkg/tcpip/header/gre.go
new file mode 100644
index 000000000..3d1cc97d9
--- /dev/null
+++ b/pkg/tcpip/header/gre.go
@@ -0,0 +1,19 @@
+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
+
+// 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 2042f214a..3e5336c04 100644
--- a/pkg/tcpip/header/parse/parse.go
+++ b/pkg/tcpip/header/parse/parse.go
@@ -169,3 +169,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(4)
+ pkt.TransportProtocolNumber = 47
+ return ok
+}
diff --git a/pkg/tcpip/link/channel/channel.go b/pkg/tcpip/link/channel/channel.go
index d9f8e3b35..caf20e718 100644
--- a/pkg/tcpip/link/channel/channel.go
+++ b/pkg/tcpip/link/channel/channel.go
@@ -131,6 +131,7 @@ type Endpoint struct {
mtu uint32
linkAddr tcpip.LinkAddress
LinkEPCapabilities stack.LinkEndpointCapabilities
+ NMaxHeaderLength uint16
// Outbound packet queue.
q *queue
@@ -219,8 +220,8 @@ func (*Endpoint) GSOMaxSize() uint32 {
// 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/gre/gre.go b/pkg/tcpip/link/gre/gre.go
new file mode 100644
index 000000000..cd05f39d7
--- /dev/null
+++ b/pkg/tcpip/link/gre/gre.go
@@ -0,0 +1,92 @@
+package gre
+
+import (
+ "math"
+ "net"
+ "log"
+ "os"
+
+ "gvisor.dev/gvisor/pkg/tcpip"
+ "gvisor.dev/gvisor/pkg/tcpip/link/channel"
+ "gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
+ "gvisor.dev/gvisor/pkg/tcpip/transport/raw"
+ "gvisor.dev/gvisor/pkg/tcpip/stack"
+ "gvisor.dev/gvisor/pkg/waiter"
+)
+
+var SIZE = 16
+var ProtocolNumber tcpip.TransportProtocolNumber = 47
+
+type Endpoint struct {
+ channel.Endpoint
+}
+
+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) Bind(s *stack.Stack, laddr tcpip.FullAddress) {
+ log.Println("Bind begin")
+
+ // Create TCP endpoint.
+ var wq waiter.Queue
+ ep, err := raw.NewEndpoint(s, ipv4.ProtocolNumber, ProtocolNumber, &wq)
+ if err != nil {
+ log.Fatal(err)
+ }
+ log.Println("EP: %s", ep)
+
+ raddr := tcpip.FullAddress{NIC: 1, Addr: tcpip.Address(net.ParseIP("10.0.0.1"))}
+ log.Println("Remote:", raddr.Addr, raddr)
+
+ if err := ep.Bind(tcpip.FullAddress{1, "", 0}); err != nil {
+ log.Fatal("Bind failed: ", err)
+ }
+
+ // if err := ep.Connect(raddr); err != nil {
+ // log.Fatal("Connect failed: ", err)
+ // }
+
+ // Issue connect request and wait for it to complete.
+ waitEntry, notifyCh := waiter.NewChannelEntry(nil)
+
+ // Read data and write to standard output until the peer closes the
+ // connection from its side.
+ wq.EventRegister(&waitEntry, waiter.EventIn)
+ for {
+ _, err := ep.Read(os.Stdout, math.MaxUint16, tcpip.ReadOptions{})
+ if err != nil {
+ if err == tcpip.ErrClosedForReceive {
+ break
+ }
+
+ if err == tcpip.ErrWouldBlock {
+ <-notifyCh
+ continue
+ }
+
+ log.Fatal("Read() failed:", err)
+ }
+ }
+ wq.EventUnregister(&waitEntry)
+ log.Println("Bind end")
+}
+
+// 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)
+}
diff --git a/pkg/tcpip/sample/tun_tcp_connect/main.go b/pkg/tcpip/sample/tun_tcp_connect/main.go
index a7da9dcd9..f44237295 100644
--- a/pkg/tcpip/sample/tun_tcp_connect/main.go
+++ b/pkg/tcpip/sample/tun_tcp_connect/main.go
@@ -41,6 +41,7 @@
package main
import (
+ "context"
"bufio"
"fmt"
"log"
@@ -54,12 +55,16 @@ import (
"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/link/fdbased"
+ grelink "gvisor.dev/gvisor/pkg/tcpip/link/gre"
"gvisor.dev/gvisor/pkg/tcpip/link/rawfile"
"gvisor.dev/gvisor/pkg/tcpip/link/sniffer"
"gvisor.dev/gvisor/pkg/tcpip/link/tun"
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
"gvisor.dev/gvisor/pkg/tcpip/stack"
+ "gvisor.dev/gvisor/pkg/tcpip/transport/gre"
+ "gvisor.dev/gvisor/pkg/tcpip/transport/raw"
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
"gvisor.dev/gvisor/pkg/waiter"
)
@@ -93,9 +98,75 @@ func writer(ch chan struct{}, ep tcpip.Endpoint) {
}
}
+type GrePacketInfo struct {
+ pi *channel.PacketInfo
+}
+
+func (g GrePacketInfo) FullPayload() ([]byte, *tcpip.Error){
+ log.Println("FullPayload")
+ pkt := g.pi.Pkt
+ size := pkt.Size()
+ vv := buffer.NewVectorisedView(size, pkt.Views())
+ var buf = make([]byte, size, size)
+ pos := 0
+ for {
+ copied, err := vv.Read(buf[pos:])
+ log.Printf("VectorisedView Read: %d %d %d %v", size, pos, copied, err)
+ if err != nil {
+ return nil, tcpip.ErrBadBuffer
+ }
+ pos = pos + copied
+ if pos == size {
+ break
+ }
+ }
+
+ log.Printf("FullPayload return: %d %v", len(buf), buf)
+ return buf, 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
+}
+
+func (info *GreHandlerInfo) greHandler(req *gre.ForwarderRequest) {
+ pkt := req.Pkt
+ log.Println("greHandler: ", req.Pkt.Size(), req.Pkt.Views())
+ views := pkt.Data.Views()
+ size := pkt.Data.Size()
+ data := buffer.NewVectorisedView(size, views)
+ newPkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
+ Data: data,
+ })
+ log.Println("greHandler cloned: ", newPkt.Views())
+
+ info.ChEp.InjectInbound(header.IPv4ProtocolNumber, newPkt)
+}
+
+func (info *GreHandlerInfo) greRead(ep *channel.Endpoint) {
+ for {
+ pi, err := ep.ReadContext(context.Background());
+ linkHdr := pi.Pkt.LinkHeader()
+ greHdr := header.GRE(linkHdr.Push(4)) //header.IPv4MinimumSize + 4)
+ greHdr.SetProtocolType(header.IPv4ProtocolNumber)
+ log.Printf("greRead %d %v %v %v", pi.Proto, pi, err, greHdr)
+ opts := tcpip.WriteOptions{
+ //To: &info.Raddr
+ }
+ info.Raw.Write(GrePacketInfo{&pi}, opts)
+ }
+}
+
func main() {
- if len(os.Args) != 6 {
- log.Fatal("Usage: ", os.Args[0], " <tun-device> <local-ipv4-address> <local-port> <remote-ipv4-address> <remote-port>")
+ if len(os.Args) != 8 {
+ log.Fatal("Usage: ", os.Args[0], " <tun-device> <local-ipv4-address> <local-port> <remote-ipv4-address> <remote-port> <local-gre-address> <remote-gre-address>")
}
tunName := os.Args[1]
@@ -103,15 +174,21 @@ func main() {
portName := os.Args[3]
remoteAddrName := os.Args[4]
remotePortName := os.Args[5]
+ greAddrName := os.Args[6]
+ greRemoteAddrName := os.Args[7]
rand.Seed(time.Now().UnixNano())
addr := tcpip.Address(net.ParseIP(addrName).To4())
+ greAddr := tcpip.Address(net.ParseIP(greAddrName).To4())
+ greRemoteAddr := tcpip.Address(net.ParseIP(greRemoteAddrName).To4())
remote := tcpip.FullAddress{
- NIC: 1,
+ NIC: 0,
Addr: tcpip.Address(net.ParseIP(remoteAddrName).To4()),
}
+ log.Printf("local:%v remote:%v", addr, remote)
+
var localPort uint16
if v, err := strconv.Atoi(portName); err != nil {
log.Fatalf("Unable to convert port %v: %v", portName, err)
@@ -129,7 +206,7 @@ func main() {
// NIC and ipv4 address.
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol},
- TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol},
+ TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol, gre.NewProtocol},
})
mtu, err := rawfile.GetMTU(tunName)
@@ -150,17 +227,73 @@ func main() {
log.Fatal(err)
}
- if err := s.AddAddress(1, ipv4.ProtocolNumber, addr); err != nil {
+ if err := s.AddAddress(1, ipv4.ProtocolNumber, greAddr); err != nil {
+ log.Fatal(err)
+ }
+
+ // Create TCP endpoint.
+ var rawWq waiter.Queue
+ rawEp, tcperr := raw.NewEndpoint(s, ipv4.ProtocolNumber, 47, &rawWq)
+ if tcperr != nil {
+ log.Fatal(tcperr)
+ }
+ log.Println("EP: %s", rawEp)
+
+ raddr := greRemoteAddr
+ fraddr := tcpip.FullAddress{NIC: 1, Addr: raddr}
+ fgreAddr := tcpip.FullAddress{NIC: 1, Addr: greAddr}
+ tcperr = rawEp.Bind(fgreAddr)
+ if tcperr != nil {
+ log.Fatal(tcperr)
+ }
+ log.Printf("Remote: %v %v", raddr, fraddr)
+
+ // Create GRE
+ greEP := grelink.New(mtu - 24)
+ chEP := greEP.GetChannel()
+ greEP.NMaxHeaderLength = 4 + 20
+
+ greInfo := GreHandlerInfo{
+// Ep: loEP,
+ ChEp: chEP,
+ Raw: rawEp,
+ Raddr: fraddr,
+ }
+ greFwd := gre.NewForwarder(s, greInfo.greHandler)
+ s.SetTransportProtocolHandler(47, greFwd.HandlePacket)
+
+ go greInfo.greRead(chEP)
+ //greEP.Bind(s, greIP)
+ if err := s.CreateNIC(2, sniffer.New(greEP)); err != nil {
+ log.Fatal(err)
+ }
+ if err := s.AddAddress(2, ipv4.ProtocolNumber, addr); err != nil {
log.Fatal(err)
}
// Add default route.
+ subnet, err := tcpip.NewSubnet(
+ tcpip.Address(net.ParseIP("10.0.1.0").To4()),
+ tcpip.AddressMask(net.ParseIP("255.255.255.0").To4()))
+ if err != nil {
+ panic(err)
+ }
s.SetRouteTable([]tcpip.Route{
{
+ Destination: subnet,
+ NIC: 2,
+ },
+ {
Destination: header.IPv4EmptySubnet,
NIC: 1,
},
})
+ log.Printf("Nics enabled 1:%v 2:%v 3:%v", s.CheckNIC(1), s.CheckNIC(2), s.CheckNIC(3))
+
+ tcperr = rawEp.Connect(fraddr)
+ if tcperr != nil {
+ log.Fatal(tcperr)
+ }
// Create TCP endpoint.
var wq waiter.Queue
@@ -179,7 +312,9 @@ func main() {
// Issue connect request and wait for it to complete.
waitEntry, notifyCh := waiter.NewChannelEntry(nil)
wq.EventRegister(&waitEntry, waiter.EventOut)
+ fmt.Println("Connect")
terr := ep.Connect(remote)
+ fmt.Println("Connect called")
if terr == tcpip.ErrConnectStarted {
fmt.Println("Connect is pending...")
<-notifyCh
diff --git a/pkg/tcpip/transport/gre/forwarder.go b/pkg/tcpip/transport/gre/forwarder.go
new file mode 100644
index 000000000..807c4a785
--- /dev/null
+++ b/pkg/tcpip/transport/gre/forwarder.go
@@ -0,0 +1,67 @@
+// Copyright 2019 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..c1a49d042
--- /dev/null
+++ b/pkg/tcpip/transport/gre/protocol.go
@@ -0,0 +1,114 @@
+// 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 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 = 47 //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, /*header.GREProtocolNumber*/ 47, waiterQueue)
+}
+
+// MinimumPacketSize returns the minimum valid gre packet size.
+func (*protocol) MinimumPacketSize() int {
+ return 4 //header.GREMinimumSize
+}
+
+// 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}
+}