diff --git a/packages/networking/hyprspace/cli/root.go b/packages/networking/hyprspace/cli/root.go index 9268061..560d6b6 100644 --- a/packages/networking/hyprspace/cli/root.go +++ b/packages/networking/hyprspace/cli/root.go @@ -33,6 +33,7 @@ func init() { cmd.Register(&Down) cmd.Register(&Status) cmd.Register(&Peers) + cmd.Register(&Route) cmd.Register(&cmd.Version) } diff --git a/packages/networking/hyprspace/cli/route.go b/packages/networking/hyprspace/cli/route.go new file mode 100644 index 0000000..45b47b1 --- /dev/null +++ b/packages/networking/hyprspace/cli/route.go @@ -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) + } +} diff --git a/packages/networking/hyprspace/cli/up.go b/packages/networking/hyprspace/cli/up.go index ea39779..b7b6812 100644 --- a/packages/networking/hyprspace/cli/up.go +++ b/packages/networking/hyprspace/cli/up.go @@ -178,12 +178,17 @@ func UpRun(r *cmd.Root, c *cmd.Sub) { if ip.Equal(dstIP) { continue } - var dst *config.Peer + var dst *peer.ID // Check route table for destination address. for _, route := range cfg.Routes { 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 } } @@ -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. - stream, ok := activeStreams[dst.ID] + stream, ok := activeStreams[dst] if ok { // Write out the packet's length to the libp2p stream to ensure // 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 // close that stream and delete it from the active stream map. 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 { - fmt.Println("[!] Failed to open stream to " + dst.ID.String()) + fmt.Println("[!] Failed to open stream to " + dst.String()) go p2p.Rediscover() return } @@ -239,7 +244,7 @@ func sendPacket(dst config.Peer, packet []byte, plen int) { // If all succeeds when writing the packet to the stream // 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) { @@ -337,7 +342,7 @@ func streamHandler(stream network.Stream) { tunDev.Iface.Write(packet[:size]) } else { // FIXME: should decrease the TTL here - sendPacket(route.Target, packet, int(plen)) + sendPacket(route.Target.ID, packet, int(plen)) } } } diff --git a/packages/networking/hyprspace/p2p/routing.go b/packages/networking/hyprspace/p2p/routing.go new file mode 100644 index 0000000..bbd75ec --- /dev/null +++ b/packages/networking/hyprspace/p2p/routing.go @@ -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, + }) + } +} diff --git a/packages/networking/hyprspace/rpc/client.go b/packages/networking/hyprspace/rpc/client.go index 2413f01..29acab0 100644 --- a/packages/networking/hyprspace/rpc/client.go +++ b/packages/networking/hyprspace/rpc/client.go @@ -31,3 +31,12 @@ func Peers(ifname string) PeersReply { } 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 +} diff --git a/packages/networking/hyprspace/rpc/server.go b/packages/networking/hyprspace/rpc/server.go index 1bdd413..edb3129 100644 --- a/packages/networking/hyprspace/rpc/server.go +++ b/packages/networking/hyprspace/rpc/server.go @@ -2,6 +2,7 @@ package rpc import ( "context" + "errors" "fmt" "log" "net" @@ -9,8 +10,10 @@ import ( "os" "github.com/hyprspace/hyprspace/config" + "github.com/hyprspace/hyprspace/p2p" "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/network" + "github.com/libp2p/go-libp2p/core/peer" "github.com/multiformats/go-multiaddr" ) @@ -49,6 +52,76 @@ func (hsr *HyprspaceRPC) Status(args *Args, reply *StatusReply) error { 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 { var peerAddrs []string for _, c := range hsr.host.Network().Conns() { diff --git a/packages/networking/hyprspace/rpc/types.go b/packages/networking/hyprspace/rpc/types.go index ea2f308..d21b3e8 100644 --- a/packages/networking/hyprspace/rpc/types.go +++ b/packages/networking/hyprspace/rpc/types.go @@ -1,5 +1,11 @@ package rpc +import ( + "net" + + "github.com/libp2p/go-libp2p/core/peer" +) + type Args struct { } @@ -15,3 +21,30 @@ type StatusReply struct { type PeersReply struct { 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 +}