packages/hyprspace: support manually configuring relays

This commit is contained in:
Max Headroom 2023-01-23 19:27:20 +01:00
parent 02bee68d13
commit 8f3105cdd3
7 changed files with 229 additions and 9 deletions

View file

@ -33,6 +33,7 @@ func init() {
cmd.Register(&Down) cmd.Register(&Down)
cmd.Register(&Status) cmd.Register(&Status)
cmd.Register(&Peers) cmd.Register(&Peers)
cmd.Register(&Route)
cmd.Register(&cmd.Version) cmd.Register(&cmd.Version)
} }

View file

@ -0,0 +1,47 @@
package cli
import (
"fmt"
"github.com/DataDrake/cli-ng/v2/cmd"
"github.com/hyprspace/hyprspace/rpc"
)
var Route = cmd.Sub{
Name: "route",
Alias: "r",
Short: "Control routing",
Args: &RouteArgs{},
Run: RouteRun,
}
type RouteArgs struct {
Action string
InterfaceName string
Args []string `zero:"true"`
}
func RouteRun(r *cmd.Root, c *cmd.Sub) {
// Parse Command Args
args := c.Args.(*RouteArgs)
action := rpc.RouteAction(args.Action)
rArgs := rpc.RouteArgs{
Action: action,
Args: args.Args,
}
reply := rpc.Route(args.InterfaceName, rArgs)
for _, r := range reply.Routes {
var target string
connectStatus := ""
if r.IsRelay {
target = fmt.Sprintf("%s relay target %s", r.RelayAddr, r.TargetAddr)
} else {
target = fmt.Sprintf("%s direct", r.TargetAddr)
}
if r.IsConnected {
connectStatus = " connected"
}
fmt.Printf("%s via %s%s\n", &r.Network, target, connectStatus)
}
}

View file

@ -178,12 +178,17 @@ func UpRun(r *cmd.Root, c *cmd.Sub) {
if ip.Equal(dstIP) { if ip.Equal(dstIP) {
continue continue
} }
var dst *config.Peer var dst *peer.ID
// Check route table for destination address. // Check route table for destination address.
for _, route := range cfg.Routes { for _, route := range cfg.Routes {
if route.Network.Contains(dstIP) { if route.Network.Contains(dstIP) {
dst = &route.Target reroute, found := p2p.FindReroute(route.Network, false)
if found {
dst = &reroute.To
} else {
dst = &route.Target.ID
}
break break
} }
} }
@ -195,9 +200,9 @@ func UpRun(r *cmd.Root, c *cmd.Sub) {
} }
} }
func sendPacket(dst config.Peer, packet []byte, plen int) { func sendPacket(dst peer.ID, packet []byte, plen int) {
// Check if we already have an open connection to the destination peer. // Check if we already have an open connection to the destination peer.
stream, ok := activeStreams[dst.ID] stream, ok := activeStreams[dst]
if ok { if ok {
// Write out the packet's length to the libp2p stream to ensure // Write out the packet's length to the libp2p stream to ensure
// we know the full size of the packet at the other end. // we know the full size of the packet at the other end.
@ -214,12 +219,12 @@ func sendPacket(dst config.Peer, packet []byte, plen int) {
// If we encounter an error when writing to a stream we should // If we encounter an error when writing to a stream we should
// close that stream and delete it from the active stream map. // close that stream and delete it from the active stream map.
stream.Close() stream.Close()
delete(activeStreams, dst.ID) delete(activeStreams, dst)
} }
stream, err := node.NewStream(ctx, dst.ID, p2p.Protocol) stream, err := node.NewStream(ctx, dst, p2p.Protocol)
if err != nil { if err != nil {
fmt.Println("[!] Failed to open stream to " + dst.ID.String()) fmt.Println("[!] Failed to open stream to " + dst.String())
go p2p.Rediscover() go p2p.Rediscover()
return return
} }
@ -239,7 +244,7 @@ func sendPacket(dst config.Peer, packet []byte, plen int) {
// If all succeeds when writing the packet to the stream // If all succeeds when writing the packet to the stream
// we should reuse this stream by adding it active streams map. // we should reuse this stream by adding it active streams map.
activeStreams[dst.ID] = stream activeStreams[dst] = stream
} }
func signalHandler(ctx context.Context, host host.Host, lockPath string, dht *dht.IpfsDHT) { func signalHandler(ctx context.Context, host host.Host, lockPath string, dht *dht.IpfsDHT) {
@ -337,7 +342,7 @@ func streamHandler(stream network.Stream) {
tunDev.Iface.Write(packet[:size]) tunDev.Iface.Write(packet[:size])
} else { } else {
// FIXME: should decrease the TTL here // FIXME: should decrease the TTL here
sendPacket(route.Target, packet, int(plen)) sendPacket(route.Target.ID, packet, int(plen))
} }
} }
} }

View file

@ -0,0 +1,52 @@
package p2p
import (
"net"
"sync"
"github.com/libp2p/go-libp2p/core/peer"
)
type Reroute struct {
Network net.IPNet
To peer.ID
}
var (
reroutes []Reroute
mut sync.Mutex
)
func findReroute(network net.IPNet, doDelete bool) (int, *Reroute, bool) {
for i, r := range reroutes {
bits1, _ := r.Network.Mask.Size()
bits2, _ := network.Mask.Size()
if r.Network.IP.Equal(network.IP) && bits1 == bits2 {
if doDelete {
reroutes = append(reroutes[:i], reroutes[i+1:]...)
}
return i, &r, true
}
}
return 0, nil, false
}
func FindReroute(network net.IPNet, doDelete bool) (*Reroute, bool) {
mut.Lock()
defer mut.Unlock()
_, i, r := findReroute(network, doDelete)
return i, r
}
func AddReroute(network net.IPNet, peerID peer.ID) {
mut.Lock()
defer mut.Unlock()
if i, _, found := findReroute(network, false); found {
reroutes[i].To = peerID
} else {
reroutes = append(reroutes, Reroute{
Network: network,
To: peerID,
})
}
}

View file

@ -31,3 +31,12 @@ func Peers(ifname string) PeersReply {
} }
return reply return reply
} }
func Route(ifname string, args RouteArgs) RouteReply {
client := connect(ifname)
var reply RouteReply
if err := client.Call("HyprspaceRPC.Route", args, &reply); err != nil {
log.Fatal("[!] RPC call failed: ", err)
}
return reply
}

View file

@ -2,6 +2,7 @@ package rpc
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"log" "log"
"net" "net"
@ -9,8 +10,10 @@ import (
"os" "os"
"github.com/hyprspace/hyprspace/config" "github.com/hyprspace/hyprspace/config"
"github.com/hyprspace/hyprspace/p2p"
"github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/host"
"github.com/libp2p/go-libp2p/core/network" "github.com/libp2p/go-libp2p/core/network"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/multiformats/go-multiaddr" "github.com/multiformats/go-multiaddr"
) )
@ -49,6 +52,76 @@ func (hsr *HyprspaceRPC) Status(args *Args, reply *StatusReply) error {
return nil return nil
} }
func (hsr *HyprspaceRPC) Route(args *RouteArgs, reply *RouteReply) error {
switch args.Action {
case Show:
var routes []RouteInfo
for _, r := range hsr.config.Routes {
connected := hsr.host.Network().Connectedness(r.Target.ID) == network.Connected
reroute, found := p2p.FindReroute(r.Network, false)
relayAddr := r.Target.ID
if found {
relayAddr = reroute.To
}
routes = append(routes, RouteInfo{
Network: r.Network,
TargetAddr: r.Target.ID,
RelayAddr: relayAddr,
IsRelay: found,
IsConnected: connected,
})
}
*reply = RouteReply{
Routes: routes,
}
case Relay:
var networks []net.IPNet
if args.Args[0] == "all" {
for _, r := range hsr.config.Routes {
networks = append(networks, r.Network)
}
} else {
_, network, err := net.ParseCIDR(args.Args[0])
if err != nil {
return err
} else if _, found := config.FindRoute(hsr.config.Routes, *network); !found {
return errors.New("no such network")
}
networks = []net.IPNet{*network}
}
p, err := peer.Decode(args.Args[1])
if err != nil {
return err
} else if _, found := config.FindPeer(hsr.config.Peers, p); !found {
return errors.New("no such peer")
}
for _, n := range networks {
p2p.AddReroute(n, p)
}
case Reset:
var networks []net.IPNet
if args.Args[0] == "all" {
for _, r := range hsr.config.Routes {
networks = append(networks, r.Network)
}
} else {
_, network, err := net.ParseCIDR(args.Args[0])
if err != nil {
return err
} else if _, found := config.FindRoute(hsr.config.Routes, *network); !found {
return errors.New("no such network")
}
networks = []net.IPNet{*network}
}
for _, n := range networks {
p2p.FindReroute(n, true)
}
default:
return errors.New("no such action")
}
return nil
}
func (hsr *HyprspaceRPC) Peers(args *Args, reply *PeersReply) error { func (hsr *HyprspaceRPC) Peers(args *Args, reply *PeersReply) error {
var peerAddrs []string var peerAddrs []string
for _, c := range hsr.host.Network().Conns() { for _, c := range hsr.host.Network().Conns() {

View file

@ -1,5 +1,11 @@
package rpc package rpc
import (
"net"
"github.com/libp2p/go-libp2p/core/peer"
)
type Args struct { type Args struct {
} }
@ -15,3 +21,30 @@ type StatusReply struct {
type PeersReply struct { type PeersReply struct {
PeerAddrs []string PeerAddrs []string
} }
type RouteAction string
const (
Show RouteAction = "show"
Relay = "relay"
Reset = "reset"
)
type RouteInfo struct {
Network net.IPNet
TargetAddr peer.ID
RelayAddr peer.ID
IsRelay bool
IsConnected bool
}
type RouteArgs struct {
Action RouteAction
Args []string
}
type RouteReply struct {
Out string
Routes []RouteInfo
Err error
}