summaryrefslogtreecommitdiffhomepage
path: root/pkg/tcpip/link
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/tcpip/link')
-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
3 files changed, 274 insertions, 2 deletions
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
+}