Browse Source

:wrench: Add virtualIP for services

Ettore Di Giacinto 3 years ago
parent
commit
6ee37c38d1
6 changed files with 98 additions and 9 deletions
  1. 2 0
      api/public/services.html
  2. 5 1
      cmd/service.go
  3. 1 0
      go.mod
  4. 62 0
      pkg/edgevpn/edgevpn.go
  5. 25 6
      pkg/edgevpn/services.go
  6. 3 2
      pkg/edgevpn/types/service.go

+ 2 - 0
api/public/services.html

@@ -120,6 +120,7 @@
           <tr>
               <th ><abbr title="ip">Name</abbr></th>
               <th ><abbr title="peer">PeerID</abbr></th>
+              <th ><abbr title="virtualIP">VirtualIP</abbr></th>
 
             </tr>
           </thead>
@@ -144,6 +145,7 @@
                     "columns": [
                         { "data": "Name" },
                         { "data": "PeerID" },
+                        { "data": "VirtualIP" },
                     ],
                 } );
 

+ 5 - 1
cmd/service.go

@@ -36,6 +36,10 @@ func ServiceAdd() cli.Command {
 		The host will act as a proxy between the service and the connection`,
 		UsageText: "edgevpn service-add unique-id ip:port",
 		Flags: append(CommonFlags,
+			cli.StringFlag{
+				Name:  "virtual-ip",
+				Usage: `VirtualIP for the service inside the VPN network. For example 10.1.0.10:90`,
+			},
 			cli.StringFlag{
 				Name:  "name",
 				Usage: `Unique name of the service to be server over the network.`,
@@ -61,7 +65,7 @@ For example, '192.168.1.1:80', or '127.0.0.1:22'.`,
 			}
 
 			// Join the node to the network, using our ledger
-			e.ExposeService(ledger, name, address)
+			e.ExposeService(ledger, name, c.String("virtual-ip"), address)
 			// Join the node to the network, using our ledger
 			if err := e.Join(); err != nil {
 				return err

+ 1 - 0
go.mod

@@ -7,6 +7,7 @@ require (
 	github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
 	github.com/fsnotify/fsnotify v1.5.1 // indirect
 	github.com/google/btree v1.0.1 // indirect
+	github.com/google/gopacket v1.1.19 // indirect
 	github.com/gookit/color v1.5.0 // indirect
 	github.com/hashicorp/errwrap v1.1.0 // indirect
 	github.com/ipfs/go-cid v0.1.0 // indirect

+ 62 - 0
pkg/edgevpn/edgevpn.go

@@ -2,12 +2,15 @@ package edgevpn
 
 import (
 	"context"
+	"fmt"
 	"io"
 	"net"
 	"os"
 	"runtime"
 	"time"
 
+	"github.com/google/gopacket"
+	"github.com/google/gopacket/layers"
 	"github.com/ipfs/go-log"
 	"github.com/libp2p/go-libp2p"
 	"github.com/libp2p/go-libp2p-core/host"
@@ -225,6 +228,9 @@ func (e *EdgeVPN) readPackets(ledger *blockchain.Ledger, ifce *water.Interface)
 		}
 		frame = frame[:n]
 
+		framecopy := make([]byte, n)
+		copy(framecopy, frame)
+
 		header, err := ipv4.ParseHeader(frame)
 		if err != nil {
 			e.config.Logger.Debugf("could not parase ipv4 header from frame")
@@ -236,7 +242,63 @@ func (e *EdgeVPN) readPackets(ledger *blockchain.Ledger, ifce *water.Interface)
 		// Query the routing table
 		value, found := ledger.GetKey(MachinesLedgerKey, dst)
 		if !found {
+			// No machines found. Try by Services VirtualIPs
+			// TODO: Unsticky this in its own controller
 			e.config.Logger.Debugf("'%s' not found in the routing table", dst)
+
+			e.config.Logger.Debug("Searching into services")
+
+			var (
+				eth     layers.Ethernet
+				ip4     layers.IPv4
+				ip6     layers.IPv6
+				tcp     layers.TCP
+				udp     layers.UDP
+				dns     layers.DNS
+				payload gopacket.Payload
+			)
+			parser := gopacket.NewDecodingLayerParser(layers.LayerTypeIPv4, &eth, &ip4, &ip6, &tcp, &udp, &dns, &payload)
+			var decoded []gopacket.LayerType
+			if err := parser.DecodeLayers(framecopy, &decoded); err != nil {
+
+				e.config.Logger.Debug("failed decoding layer with gopacket")
+
+			}
+			var dstPort uint16
+			for _, layerType := range decoded {
+				switch layerType {
+				case layers.LayerTypeTCP:
+					dstPort = uint16(tcp.DstPort)
+				case layers.LayerTypeUDP:
+					dstPort = uint16(udp.DstPort)
+				}
+			}
+			e.config.Logger.Debugf("Destination %s:%d", dst, dstPort)
+
+			services := ledger.LastBlock().Storage[ServicesLedgerKey]
+			for id, s := range services {
+				service := &types.Service{}
+				s.Unmarshal(service)
+				if service.VirtualIP == fmt.Sprintf("%s:%d", dst, dstPort) {
+					e.config.Logger.Debugf("'%s' matches %s:%d", id, dst, dstPort)
+					// Decode the Peer
+					d, err := peer.Decode(service.PeerID)
+					if err != nil {
+						e.config.Logger.Debugf("could not decode peer '%s'", value)
+						continue
+					}
+
+					// Open a stream
+					stream, err := e.host.NewStream(ctx, d, ServiceProtocol)
+					if err != nil {
+						e.config.Logger.Debugf("could not open stream '%s'", err.Error())
+						continue
+					}
+					stream.Write(frame)
+					stream.Close()
+					break
+				}
+			}
 			continue
 		}
 		machine := &types.Machine{}

+ 25 - 6
pkg/edgevpn/services.go

@@ -18,7 +18,7 @@ const (
 	UsersLedgerKey    = "users"
 )
 
-func (e *EdgeVPN) ExposeService(ledger *blockchain.Ledger, serviceID, dstaddress string) {
+func (e *EdgeVPN) ExposeService(ledger *blockchain.Ledger, serviceID, virtualIP, dstaddress string) {
 
 	e.Logger().Infof("Exposing service '%s' (%s)", serviceID, dstaddress)
 
@@ -35,7 +35,11 @@ func (e *EdgeVPN) ExposeService(ledger *blockchain.Ledger, serviceID, dstaddress
 			// If mismatch, update the blockchain
 			if !found || service.PeerID != e.host.ID().String() {
 				updatedMap := map[string]interface{}{}
-				updatedMap[serviceID] = types.Service{PeerID: e.host.ID().String(), Name: serviceID}
+				updatedMap[serviceID] = types.Service{
+					PeerID:    e.host.ID().String(),
+					Name:      serviceID,
+					VirtualIP: virtualIP,
+				}
 				ledger.Add(ServicesLedgerKey, updatedMap)
 			}
 		},
@@ -49,11 +53,26 @@ func (e *EdgeVPN) ExposeService(ledger *blockchain.Ledger, serviceID, dstaddress
 
 			// Retrieve current ID for ip in the blockchain
 			_, found := ledger.GetKey(UsersLedgerKey, stream.Conn().RemotePeer().String())
-			// If mismatch, update the blockchain
+
+			// TODO: Unsticky this.
+			// Maybe Services and files should have their own controllers
 			if !found {
-				e.config.Logger.Debugf("Reset '%s': not found in the ledger", stream.Conn().RemotePeer().String())
-				stream.Reset()
-				return
+				// No user found, so we check if the connection was originated from a VPN node
+				data := ledger.LastBlock().Storage[MachinesLedgerKey]
+				for _, m := range data {
+					machine := &types.Machine{}
+					m.Unmarshal(machine)
+					if machine.PeerID == stream.Conn().RemotePeer().String() {
+						found = true
+					}
+				}
+
+				if !found {
+					// We didn't find again any match, so we close the connection
+					e.config.Logger.Debugf("Reset '%s': not found in the ledger", stream.Conn().RemotePeer().String())
+					stream.Reset()
+					return
+				}
 			}
 
 			e.config.Logger.Infof("Connecting to '%s'", dstaddress)

+ 3 - 2
pkg/edgevpn/types/service.go

@@ -1,6 +1,7 @@
 package types
 
 type Service struct {
-	PeerID string
-	Name   string
+	PeerID    string
+	Name      string
+	VirtualIP string
 }