diff options
author | Mikael Magnusson <mikma@users.sourceforge.net> | 2021-02-05 02:04:06 +0100 |
---|---|---|
committer | Mikael Magnusson <mikma@users.sourceforge.net> | 2021-09-21 20:01:54 +0200 |
commit | b2718e250d7ef39048f03b9cc5eeeccf23d5ca8e (patch) | |
tree | f8b6b9593447ca83f46aab2ed2f08c0981d11b00 | |
parent | e819029f3ad059bfc1635b7f2a196c332fa7532f (diff) |
WIP: GRE
-rw-r--r-- | pkg/tcpip/header/BUILD | 1 | ||||
-rw-r--r-- | pkg/tcpip/header/arp.go | 1 | ||||
-rw-r--r-- | pkg/tcpip/header/gre.go | 45 | ||||
-rw-r--r-- | pkg/tcpip/header/parse/parse.go | 10 | ||||
-rw-r--r-- | pkg/tcpip/link/channel/channel.go | 5 | ||||
-rw-r--r-- | pkg/tcpip/link/tunnel/BUILD | 21 | ||||
-rw-r--r-- | pkg/tcpip/link/tunnel/gre.go | 250 | ||||
-rw-r--r-- | pkg/tcpip/transport/gre/BUILD | 18 | ||||
-rw-r--r-- | pkg/tcpip/transport/gre/forwarder.go | 67 | ||||
-rw-r--r-- | pkg/tcpip/transport/gre/protocol.go | 115 |
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} +} |