From 2ce080f581556f9512a481e7fcddecaed0ae7be7 Mon Sep 17 00:00:00 2001 From: Mikael Magnusson Date: Sat, 23 Jan 2021 01:14:51 +0100 Subject: WIP GRE tunnel --- pkg/tcpip/header/gre.go | 19 ++++ pkg/tcpip/header/parse/parse.go | 10 +++ pkg/tcpip/link/channel/channel.go | 5 +- pkg/tcpip/link/gre/gre.go | 92 ++++++++++++++++++++ pkg/tcpip/sample/tun_tcp_connect/main.go | 145 +++++++++++++++++++++++++++++-- pkg/tcpip/transport/gre/forwarder.go | 67 ++++++++++++++ pkg/tcpip/transport/gre/protocol.go | 114 ++++++++++++++++++++++++ 7 files changed, 445 insertions(+), 7 deletions(-) create mode 100644 pkg/tcpip/header/gre.go create mode 100644 pkg/tcpip/link/gre/gre.go create mode 100644 pkg/tcpip/transport/gre/forwarder.go create mode 100644 pkg/tcpip/transport/gre/protocol.go (limited to 'pkg/tcpip') 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], " ") + if len(os.Args) != 8 { + log.Fatal("Usage: ", os.Args[0], " ") } 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} +} -- cgit v1.2.3