Compare commits
49 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
9594e99e67 | ||
![]() |
20c306ddcb | ||
![]() |
6c2a0d15c3 | ||
![]() |
dcbdbe331a | ||
![]() |
275eb18ca8 | ||
![]() |
39b23f4e91 | ||
![]() |
9cc7c03eb3 | ||
![]() |
8da27edd39 | ||
![]() |
c750bc1d92 | ||
![]() |
cc4cd1c010 | ||
![]() |
4b1b189d5b | ||
![]() |
09f1134d1e | ||
![]() |
31cf03c10a | ||
![]() |
1885e741fc | ||
![]() |
03d0cc5453 | ||
![]() |
a27171ea62 | ||
![]() |
c4a57651f2 | ||
![]() |
da9adb6f08 | ||
![]() |
a454f57fb0 | ||
![]() |
9d76c89aca | ||
![]() |
f7664f3263 | ||
![]() |
6418a305aa | ||
![]() |
4e25bc9b36 | ||
![]() |
b1caae2d87 | ||
![]() |
48531ebdc2 | ||
![]() |
663a66b2a2 | ||
![]() |
be276aef0b | ||
![]() |
afc2118b98 | ||
![]() |
92c169dc9c | ||
![]() |
fa103eb1a1 | ||
![]() |
d1baa9fe1a | ||
![]() |
bba99213e0 | ||
![]() |
dd881949f0 | ||
![]() |
c0469dd223 | ||
![]() |
414f80bd6b | ||
![]() |
c81dcf21b5 | ||
![]() |
b543729176 | ||
![]() |
bef656a9e3 | ||
![]() |
dc279408df | ||
![]() |
7ee920fb47 | ||
![]() |
723e4958c8 | ||
![]() |
7a93823466 | ||
![]() |
5ff46dc02e | ||
![]() |
52c78391bb | ||
![]() |
c5ccc0daaa | ||
![]() |
795da55458 | ||
![]() |
5b957d3ed7 | ||
![]() |
f9c74c1cc1 | ||
![]() |
17f09c48dc |
4
.github/FUNDING.yml
vendored
Normal file
4
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: [alecbcs]
|
||||
|
7
.github/workflows/build.sh
vendored
7
.github/workflows/build.sh
vendored
@@ -1,12 +1,13 @@
|
||||
#!/bin/bash
|
||||
|
||||
platforms=("linux/amd64" "linux/arm" "linux/arm64" "darwin/amd64" "darwin/arm64")
|
||||
platforms=("linux/amd64" "linux/arm" "linux/arm64" "darwin/amd64" "darwin/arm64" "windows/amd64" "windows/386")
|
||||
|
||||
for platform in "${platforms[@]}"
|
||||
do
|
||||
platform_split=(${platform//\// })
|
||||
GOOS=${platform_split[0]}
|
||||
GOARCH=${platform_split[1]}
|
||||
env GOOS=$GOOS GOARCH=$GOARCH CGO_ENABLED=0 go build -ldflags "-s -w -X github.com/hyprspace/hyprspace/cli.appVersion=$1" -o hyprspace-$1-${GOOS}-${GOARCH} .
|
||||
[ $GOOS == "windows" ] && EXT=".exe"
|
||||
env GOOS=$GOOS GOARCH=$GOARCH CGO_ENABLED=0 go build -ldflags "-s -w -X github.com/hyprspace/hyprspace/cli.appVersion=$1" -o hyprspace-$1-${GOOS}-${GOARCH}${EXT} .
|
||||
|
||||
done
|
||||
done
|
||||
|
71
.github/workflows/codeql-analysis.yml
vendored
Normal file
71
.github/workflows/codeql-analysis.yml
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ main ]
|
||||
schedule:
|
||||
- cron: '21 19 * * 0'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'go' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
|
||||
# Learn more:
|
||||
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
38
README.md
38
README.md
@@ -1,7 +1,8 @@
|
||||
<img src="hyprspace.png" width="300" height="300">
|
||||
<img src="hyprspace.png" width="250">
|
||||
|
||||
# Hyprspace
|
||||
[](https://goreportcard.com/report/github.com/hyprspace/hyprspace)
|
||||
[](https://matrix.to/#/%23hyprspace:matrix.org)
|
||||
|
||||
A Lightweight VPN Built on top of Libp2p for Truly Distributed Networks.
|
||||
|
||||
@@ -21,12 +22,12 @@ https://user-images.githubusercontent.com/19558067/121469777-f42cdb80-c971-11eb-
|
||||
- [Tutorial](#tutorial)
|
||||
|
||||
## A Bit of Backstory
|
||||
[Libp2p](libp2p.io) is a networking library created by [Protocol Labs](https://protocol.ai) that allows nodes to discover each other using a Distributed Hash Table. Paired with [NAT hole punching](https://en.wikipedia.org/wiki/Hole_punching_(networking)) this allows Hyprspace to create a direct encrypted tunnel between two nodes even if they're both behind firewalls.
|
||||
[Libp2p](https://libp2p.io) is a networking library created by [Protocol Labs](https://protocol.ai) that allows nodes to discover each other using a Distributed Hash Table. Paired with [NAT hole punching](https://en.wikipedia.org/wiki/Hole_punching_(networking)) this allows Hyprspace to create a direct encrypted tunnel between two nodes even if they're both behind firewalls.
|
||||
|
||||
**Moreover! Each node doesn't even need to know the other's ip address prior to starting up the connection.** This makes Hyprspace perfect for devices that frequently migrate between locations but still require a constant virtual ip address.
|
||||
|
||||
### So How Does Hyprspace Compare to Something Like Wireguard?
|
||||
[Wireguard](https://wireguard.com) is an amazing VPN written by Jason A. Donenfeld. If you haven't already, definitely go check it out! Wireguard actually inspired me to write Hyprspace. That said, although Wireguard is in a class of it's own as a great VPN, it requires at least one of your nodes to have a public IP address. In this mode, as long as one of your nodes is publicly accessible, it can be used as a cental relay to reach the other nodes in the network. However, this means that all of the traffic for your entire system is going through that one system which can slow down your network and make it fragile in the case that node goes down and you loose the whole network. So instead say that you want each node to be able to directly connect to each other as they do in Hyprspace. Unfortunately through Wireguard this would require every node to be publicly addressable which means manual port forwarding and no travelling nodes.
|
||||
[Wireguard](https://wireguard.com) is an amazing VPN written by Jason A. Donenfeld. If you haven't already, definitely go check it out! Wireguard actually inspired me to write Hyprspace. That said, although Wireguard is in a class of its own as a great VPN, it requires at least one of your nodes to have a public IP address. In this mode, as long as one of your nodes is publicly accessible, it can be used as a central relay to reach the other nodes in the network. However, this means that all of the traffic for your entire system is going through that one system which can slow down your network and make it fragile in the case that node goes down and you lose the whole network. So instead say that you want each node to be able to directly connect to each other as they do in Hyprspace. Unfortunately through Wireguard this would require every node to be publicly addressable which means manual port forwarding and no travelling nodes.
|
||||
|
||||
By contrast Hyprspace allows all of your nodes to connect directly to each other creating a strong reliable network even if they're all behind their own firewalls. No manual port forwarding required!
|
||||
|
||||
@@ -44,10 +45,7 @@ If anyone else has some use cases please add them! Pull requests welcome!
|
||||
|
||||
## Getting Started
|
||||
### Prerequisites
|
||||
If you're running Hyprspace on a Mac you'll need to install `iproute2mac`. If you're using the `brew` package manager that's as simple as,
|
||||
```bash
|
||||
brew install iproute2mac
|
||||
```
|
||||
If you're running Hyprspace on Windows you'll need to install [tap-windows](http://build.openvpn.net/downloads/releases/).
|
||||
|
||||
### Installation
|
||||
|
||||
@@ -68,7 +66,13 @@ brew install iproute2mac
|
||||
| `init` | `i` | Initialize an interface's configuration. |
|
||||
| `up` | `up` | Create and Bring Up a Hyprspace Interface |
|
||||
| `down ` | `d` | Bring Down and Delete A Hyprspace Interface |
|
||||
| `update` | `upd` | Have Hyprspace update its own binary to the latest release. |
|
||||
| `update` | `upd` | Have Hyprspace update its own binary to the latest release. |
|
||||
|
||||
### Global Flags
|
||||
| Flag | Alias | Description |
|
||||
| ------------------- | ------- | -------------------------------------------------------------------------- |
|
||||
| `--config` | `-c` | Specify the path to a hyprspace config for an interface. |
|
||||
|
||||
|
||||
## Tutorial
|
||||
|
||||
@@ -81,6 +85,8 @@ but yours could be anything you'd like.
|
||||
|
||||
(Note: if you're using a Mac you'll have to use the interface name `utun[0-9]`. Check which interfaces are already in use by running `ip a` once you've got `iproute2mac` installed.)
|
||||
|
||||
(Note: if you're using Windows you'll have to use the interface name as seen in Control Panel. IP address will be set automatically only if you run Hyprspace as Administrator.)
|
||||
|
||||
###### Local Machine
|
||||
```bash
|
||||
sudo hyprspace init hs0
|
||||
@@ -130,20 +136,6 @@ Notice here we'll have to pick one of our machines to be `10.1.1.1`
|
||||
and the other to be `10.1.1.2`. Make sure to update the interface's IP
|
||||
address for the machine who needs to change to be `10.1.1.2`.
|
||||
|
||||
### Update our Discover Key
|
||||
|
||||
Looking in the interface's configuration you'll also notice a field called
|
||||
`discover_key` (right above the interface's private key). It doesn't matter
|
||||
which discovery key you pick but it much be the same for all of the nodes in your little cluster
|
||||
so that they can find each other.
|
||||
|
||||
(*Note you can use different `discover_key`s with different interfaces on the same
|
||||
host to create different isolated networks.)
|
||||
|
||||
```yaml
|
||||
discover_key: fiftieth-dandelion-wronged-craftwork
|
||||
```
|
||||
|
||||
### Starting Up the Interfaces!
|
||||
Now that we've got our configs all sorted we can start up the two interfaces!
|
||||
|
||||
@@ -185,7 +177,7 @@ sudo hyprspace down hs1
|
||||
|
||||
## License
|
||||
|
||||
Copyright 2021 Alec Scott <hi@alecbcs.com>
|
||||
Copyright 2021-2022 Alec Scott <hi@alecbcs.com>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
34
cli/down.go
34
cli/down.go
@@ -2,6 +2,9 @@ package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
|
||||
"github.com/DataDrake/cli-ng/v2/cmd"
|
||||
"github.com/hyprspace/hyprspace/tun"
|
||||
@@ -26,7 +29,34 @@ func DownRun(r *cmd.Root, c *cmd.Sub) {
|
||||
// Parse Command Args
|
||||
args := c.Args.(*DownArgs)
|
||||
|
||||
fmt.Println("[+] ip link delete dev " + args.InterfaceName)
|
||||
err := tun.Delete(args.InterfaceName)
|
||||
// Parse Global Config Flag for Custom Config Path
|
||||
configPath := r.Flags.(*GlobalFlags).Config
|
||||
if configPath == "" {
|
||||
configPath = "/etc/hyprspace/" + args.InterfaceName + ".yaml"
|
||||
}
|
||||
|
||||
// Read lock from file system to stop process.
|
||||
lockPath := filepath.Join(filepath.Dir(configPath), args.InterfaceName+".lock")
|
||||
out, err := os.ReadFile(lockPath)
|
||||
checkErr(err)
|
||||
|
||||
pid, err := strconv.Atoi(string(out))
|
||||
checkErr(err)
|
||||
|
||||
process, err := os.FindProcess(pid)
|
||||
checkErr(err)
|
||||
|
||||
err0 := process.Signal(os.Interrupt)
|
||||
|
||||
err1 := tun.Delete(args.InterfaceName)
|
||||
|
||||
// Different types of systems may need the tun devices destroyed first or
|
||||
// the process to exit first don't worry as long as one of these two has
|
||||
// suceeded.
|
||||
if err0 != nil && err1 != nil {
|
||||
checkErr(err0)
|
||||
checkErr(err1)
|
||||
}
|
||||
|
||||
fmt.Println("[+] deleted hyprspace " + args.InterfaceName + " daemon")
|
||||
}
|
||||
|
20
cli/init.go
20
cli/init.go
@@ -1,16 +1,13 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/DataDrake/cli-ng/v2/cmd"
|
||||
"github.com/hyprspace/hyprspace/config"
|
||||
"github.com/libp2p/go-libp2p"
|
||||
"github.com/libp2p/go-libp2p-core/crypto"
|
||||
"github.com/sethvargo/go-diceware/diceware"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
@@ -40,26 +37,21 @@ func InitRun(r *cmd.Root, c *cmd.Sub) {
|
||||
}
|
||||
|
||||
// Create New Libp2p Node
|
||||
host, err := libp2p.New(context.Background())
|
||||
host, err := libp2p.New()
|
||||
checkErr(err)
|
||||
|
||||
// Get Node's Private Key
|
||||
keyBytes, err := crypto.MarshalPrivateKey(host.Peerstore().PrivKey(host.ID()))
|
||||
checkErr(err)
|
||||
|
||||
// Generate a random diceware discovery key
|
||||
list, err := diceware.Generate(4)
|
||||
checkErr(err)
|
||||
|
||||
// Setup an initial default command.
|
||||
new := config.Config{
|
||||
Interface: config.Interface{
|
||||
Name: args.InterfaceName,
|
||||
ListenPort: 8001,
|
||||
Address: "10.1.1.1/24",
|
||||
ID: host.ID().Pretty(),
|
||||
PrivateKey: string(keyBytes),
|
||||
DiscoverKey: strings.Join(list, "-"),
|
||||
Name: args.InterfaceName,
|
||||
ListenPort: 8001,
|
||||
Address: "10.1.1.1/24",
|
||||
ID: host.ID().Pretty(),
|
||||
PrivateKey: string(keyBytes),
|
||||
},
|
||||
}
|
||||
|
||||
|
224
cli/up.go
224
cli/up.go
@@ -3,13 +3,15 @@ package cli
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
@@ -22,20 +24,17 @@ import (
|
||||
"github.com/libp2p/go-libp2p-core/host"
|
||||
"github.com/libp2p/go-libp2p-core/network"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
"github.com/songgao/water"
|
||||
"golang.org/x/net/ipv4"
|
||||
)
|
||||
|
||||
var (
|
||||
// Global is the global interface configuration for the
|
||||
// application instance.
|
||||
Global config.Config
|
||||
// iface is the tun device used to pass packets between
|
||||
// Hyprspace and the user's machine.
|
||||
iface *water.Interface
|
||||
tunDev *tun.TUN
|
||||
// RevLookup allow quick lookups of an incoming stream
|
||||
// for security before accepting or responding to any data.
|
||||
RevLookup map[string]bool
|
||||
RevLookup map[string]string
|
||||
// activeStreams is a map of active streams to a peer
|
||||
activeStreams map[string]network.Stream
|
||||
)
|
||||
|
||||
// Up creates and brings up a Hyprspace Interface.
|
||||
@@ -73,13 +72,13 @@ func UpRun(r *cmd.Root, c *cmd.Sub) {
|
||||
}
|
||||
|
||||
// Read in configuration from file.
|
||||
Global, err := config.Read(configPath)
|
||||
cfg, err := config.Read(configPath)
|
||||
checkErr(err)
|
||||
|
||||
if !flags.Foreground {
|
||||
// Make results chan
|
||||
out := make(chan error)
|
||||
go createDaemon(out)
|
||||
go createDaemon(cfg, out)
|
||||
|
||||
select {
|
||||
case err = <-out:
|
||||
@@ -94,21 +93,42 @@ func UpRun(r *cmd.Root, c *cmd.Sub) {
|
||||
}
|
||||
|
||||
// Setup reverse lookup hash map for authentication.
|
||||
RevLookup = make(map[string]bool, len(Global.Peers))
|
||||
for _, id := range Global.Peers {
|
||||
RevLookup[id.ID] = true
|
||||
RevLookup = make(map[string]string, len(cfg.Peers))
|
||||
for ip, id := range cfg.Peers {
|
||||
RevLookup[id.ID] = ip
|
||||
}
|
||||
|
||||
fmt.Println("[+] Creating TUN Device")
|
||||
// Create new TUN device
|
||||
iface, err = tun.New(Global.Interface.Name)
|
||||
if err != nil {
|
||||
checkErr(errors.New("interface already in use"))
|
||||
|
||||
if runtime.GOOS == "darwin" {
|
||||
if len(cfg.Peers) > 1 {
|
||||
checkErr(errors.New("cannot create interface macos does not support more than one peer"))
|
||||
}
|
||||
|
||||
// Grab ip address of only peer in config
|
||||
var destPeer string
|
||||
for ip := range cfg.Peers {
|
||||
destPeer = ip
|
||||
}
|
||||
|
||||
// Create new TUN device
|
||||
tunDev, err = tun.New(
|
||||
cfg.Interface.Name,
|
||||
tun.Address(cfg.Interface.Address),
|
||||
tun.DestAddress(destPeer),
|
||||
tun.MTU(1420),
|
||||
)
|
||||
} else {
|
||||
// Create new TUN device
|
||||
tunDev, err = tun.New(
|
||||
cfg.Interface.Name,
|
||||
tun.Address(cfg.Interface.Address),
|
||||
tun.MTU(1420),
|
||||
)
|
||||
}
|
||||
if err != nil {
|
||||
checkErr(err)
|
||||
}
|
||||
// Set TUN MTU
|
||||
tun.SetMTU(Global.Interface.Name, 1420)
|
||||
// Add Address to Interface
|
||||
tun.SetAddress(Global.Interface.Name, Global.Interface.Address)
|
||||
|
||||
// Setup System Context
|
||||
ctx := context.Background()
|
||||
@@ -116,47 +136,33 @@ func UpRun(r *cmd.Root, c *cmd.Sub) {
|
||||
fmt.Println("[+] Creating LibP2P Node")
|
||||
|
||||
// Check that the listener port is available.
|
||||
var ln net.Listener
|
||||
port := Global.Interface.ListenPort
|
||||
if port != 8001 {
|
||||
ln, err = net.Listen("tcp", ":"+strconv.Itoa(port))
|
||||
if err != nil {
|
||||
checkErr(errors.New("could not create node, listen port already in use by something else"))
|
||||
}
|
||||
} else {
|
||||
for {
|
||||
ln, err = net.Listen("tcp", ":"+strconv.Itoa(port))
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
if port >= 65535 {
|
||||
checkErr(errors.New("failed to find open port"))
|
||||
}
|
||||
port++
|
||||
}
|
||||
}
|
||||
if ln != nil {
|
||||
ln.Close()
|
||||
}
|
||||
port, err := verifyPort(cfg.Interface.ListenPort)
|
||||
checkErr(err)
|
||||
|
||||
// Create P2P Node
|
||||
host, dht, err := p2p.CreateNode(ctx,
|
||||
Global.Interface.PrivateKey,
|
||||
host, dht, err := p2p.CreateNode(
|
||||
ctx,
|
||||
cfg.Interface.PrivateKey,
|
||||
port,
|
||||
streamHandler)
|
||||
streamHandler,
|
||||
)
|
||||
checkErr(err)
|
||||
|
||||
// Setup Peer Table for Quick Packet --> Dest ID lookup
|
||||
peerTable := make(map[string]peer.ID)
|
||||
for ip, id := range Global.Peers {
|
||||
for ip, id := range cfg.Peers {
|
||||
peerTable[ip], err = peer.Decode(id.ID)
|
||||
checkErr(err)
|
||||
}
|
||||
|
||||
fmt.Println("[+] Setting Up Node Discovery via DHT")
|
||||
// Setup P2P Discovery
|
||||
go p2p.Discover(ctx, host, dht, Global.Interface.DiscoverKey, peerTable)
|
||||
go p2p.Discover(ctx, host, dht, peerTable)
|
||||
go prettyDiscovery(ctx, host, peerTable)
|
||||
|
||||
// Configure path for lock
|
||||
lockPath := filepath.Join(filepath.Dir(cfg.Path), cfg.Interface.Name+".lock")
|
||||
|
||||
go func() {
|
||||
// Wait for a SIGINT or SIGTERM signal
|
||||
ch := make(chan os.Signal, 1)
|
||||
@@ -168,36 +174,70 @@ func UpRun(r *cmd.Root, c *cmd.Sub) {
|
||||
if err := host.Close(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Remove daemon lock from file system.
|
||||
err = os.Remove(lockPath)
|
||||
checkErr(err)
|
||||
|
||||
// Exit the application.
|
||||
os.Exit(0)
|
||||
}()
|
||||
|
||||
// Write lock to filesystem to indicate an existing running daemon.
|
||||
err = os.WriteFile(lockPath, []byte(fmt.Sprint(os.Getpid())), os.ModePerm)
|
||||
checkErr(err)
|
||||
|
||||
// Bring Up TUN Device
|
||||
tun.Up(Global.Interface.Name)
|
||||
err = tunDev.Up()
|
||||
if err != nil {
|
||||
checkErr(errors.New("unable to bring up tun device"))
|
||||
}
|
||||
|
||||
fmt.Println("[+] Network Setup Complete...Waiting on Node Discovery")
|
||||
// Listen For New Packets on TUN Interface
|
||||
packet := make([]byte, 1420)
|
||||
var stream network.Stream
|
||||
var header *ipv4.Header
|
||||
var plen int
|
||||
activeStreams = make(map[string]network.Stream)
|
||||
var packet = make([]byte, 1420)
|
||||
for {
|
||||
plen, err = iface.Read(packet)
|
||||
checkErr(err)
|
||||
header, _ = ipv4.ParseHeader(packet)
|
||||
_, ok := Global.Peers[header.Dst.String()]
|
||||
plen, err := tunDev.Iface.Read(packet)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
dst := net.IPv4(packet[16], packet[17], packet[18], packet[19]).String()
|
||||
stream, ok := activeStreams[dst]
|
||||
if ok {
|
||||
stream, err = host.NewStream(ctx, peerTable[header.Dst.String()], p2p.Protocol)
|
||||
err = binary.Write(stream, binary.LittleEndian, uint16(plen))
|
||||
if err == nil {
|
||||
_, err = stream.Write(packet[:plen])
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
}
|
||||
stream.Close()
|
||||
delete(activeStreams, dst)
|
||||
ok = false
|
||||
}
|
||||
if peer, ok := peerTable[dst]; ok {
|
||||
stream, err = host.NewStream(ctx, peer, p2p.Protocol)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
stream.Write(packet[:plen])
|
||||
stream.Close()
|
||||
err = binary.Write(stream, binary.LittleEndian, uint16(plen))
|
||||
if err != nil {
|
||||
stream.Close()
|
||||
continue
|
||||
}
|
||||
_, err = stream.Write(packet[:plen])
|
||||
if err != nil {
|
||||
stream.Close()
|
||||
continue
|
||||
}
|
||||
activeStreams[dst] = stream
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func createDaemon(out chan<- error) {
|
||||
func createDaemon(cfg *config.Config, out chan<- error) {
|
||||
path, err := os.Executable()
|
||||
checkErr(err)
|
||||
// Create Pipe to monitor for daemon output.
|
||||
@@ -214,14 +254,17 @@ func createDaemon(out chan<- error) {
|
||||
checkErr(err)
|
||||
scanner := bufio.NewScanner(r)
|
||||
count := 0
|
||||
for scanner.Scan() && count < (4+len(Global.Peers)) {
|
||||
for count < len(cfg.Peers) && scanner.Scan() {
|
||||
fmt.Println(scanner.Text())
|
||||
count++
|
||||
if strings.HasPrefix(scanner.Text(), "[+] Connection to") {
|
||||
count++
|
||||
}
|
||||
}
|
||||
fmt.Println(scanner.Text())
|
||||
|
||||
// Release the created daemon
|
||||
err = process.Release()
|
||||
checkErr(err)
|
||||
if count < 4 {
|
||||
if count < len(cfg.Peers) {
|
||||
out <- errors.New("failed to create daemon")
|
||||
}
|
||||
out <- nil
|
||||
@@ -231,9 +274,28 @@ func streamHandler(stream network.Stream) {
|
||||
// If the remote node ID isn't in the list of known nodes don't respond.
|
||||
if _, ok := RevLookup[stream.Conn().RemotePeer().Pretty()]; !ok {
|
||||
stream.Reset()
|
||||
return
|
||||
}
|
||||
var packet = make([]byte, 1420)
|
||||
var packetSize = make([]byte, 2)
|
||||
for {
|
||||
_, err := stream.Read(packetSize)
|
||||
if err != nil {
|
||||
stream.Close()
|
||||
return
|
||||
}
|
||||
size := binary.LittleEndian.Uint16(packetSize)
|
||||
var plen uint16 = 0
|
||||
for plen < size {
|
||||
tmp, err := stream.Read(packet[plen:size])
|
||||
plen += uint16(tmp)
|
||||
if err != nil {
|
||||
stream.Close()
|
||||
return
|
||||
}
|
||||
}
|
||||
tunDev.Iface.Write(packet[:size])
|
||||
}
|
||||
io.Copy(iface.ReadWriteCloser, stream)
|
||||
stream.Close()
|
||||
}
|
||||
|
||||
func prettyDiscovery(ctx context.Context, node host.Host, peerTable map[string]peer.ID) {
|
||||
@@ -257,3 +319,29 @@ func prettyDiscovery(ctx context.Context, node host.Host, peerTable map[string]p
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func verifyPort(port int) (int, error) {
|
||||
var ln net.Listener
|
||||
var err error
|
||||
if port != 8001 {
|
||||
ln, err = net.Listen("tcp", ":"+strconv.Itoa(port))
|
||||
if err != nil {
|
||||
return port, errors.New("could not create node, listen port already in use by something else")
|
||||
}
|
||||
} else {
|
||||
for {
|
||||
ln, err = net.Listen("tcp", ":"+strconv.Itoa(port))
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
if port >= 65535 {
|
||||
return port, errors.New("failed to find open port")
|
||||
}
|
||||
port++
|
||||
}
|
||||
}
|
||||
if ln != nil {
|
||||
ln.Close()
|
||||
}
|
||||
return port, nil
|
||||
}
|
||||
|
@@ -8,18 +8,18 @@ import (
|
||||
|
||||
// Config is the main Configuration Struct for Hyprspace.
|
||||
type Config struct {
|
||||
Path string `yaml:"path,omitempty"`
|
||||
Interface Interface `yaml:"interface"`
|
||||
Peers map[string]Peer `yaml:"peers"`
|
||||
}
|
||||
|
||||
// Interface defines all of the fields that a local node needs to know about itself!
|
||||
type Interface struct {
|
||||
Name string `yaml:"name"`
|
||||
ID string `yaml:"id"`
|
||||
ListenPort int `yaml:"listen_port"`
|
||||
Address string `yaml:"address"`
|
||||
DiscoverKey string `yaml:"discover_key"`
|
||||
PrivateKey string `yaml:"private_key"`
|
||||
Name string `yaml:"name"`
|
||||
ID string `yaml:"id"`
|
||||
ListenPort int `yaml:"listen_port"`
|
||||
Address string `yaml:"address"`
|
||||
PrivateKey string `yaml:"private_key"`
|
||||
}
|
||||
|
||||
// Peer defines a peer in the configuration. We might add more to this later.
|
||||
@@ -28,21 +28,28 @@ type Peer struct {
|
||||
}
|
||||
|
||||
// Read initializes a config from a file.
|
||||
func Read(path string) (result Config, err error) {
|
||||
func Read(path string) (*Config, error) {
|
||||
in, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return
|
||||
return nil, err
|
||||
}
|
||||
result = Config{
|
||||
result := Config{
|
||||
Interface: Interface{
|
||||
Name: "hs0",
|
||||
ListenPort: 8001,
|
||||
Address: "10.1.1.1",
|
||||
ID: "",
|
||||
DiscoverKey: "",
|
||||
PrivateKey: "",
|
||||
Name: "hs0",
|
||||
ListenPort: 8001,
|
||||
Address: "10.1.1.1",
|
||||
ID: "",
|
||||
PrivateKey: "",
|
||||
},
|
||||
}
|
||||
|
||||
// Read in config settings from file.
|
||||
err = yaml.Unmarshal(in, &result)
|
||||
return
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Overwrite path of config to input.
|
||||
result.Path = path
|
||||
return &result, nil
|
||||
}
|
||||
|
@@ -2,8 +2,10 @@
|
||||
Description=hyprspace daemon for %i
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
Requires=network-online.target
|
||||
|
||||
[Service]
|
||||
ExecStartPre=/usr/bin/sleep 10
|
||||
ExecStart=/usr/local/bin/hyprspace up %i --foreground
|
||||
ExecStop=/usr/local/bin/hyprspace down %i
|
||||
Restart=on-failure
|
||||
|
20
go.mod
20
go.mod
@@ -4,20 +4,18 @@ go 1.16
|
||||
|
||||
require (
|
||||
github.com/DataDrake/cli-ng/v2 v2.0.2
|
||||
github.com/hashicorp/go-version v1.2.1 // indirect
|
||||
github.com/hashicorp/go-version v1.4.0 // indirect
|
||||
github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf
|
||||
github.com/ipfs/go-datastore v0.4.5
|
||||
github.com/ipfs/go-datastore v0.5.1
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/libp2p/go-libp2p v0.14.1
|
||||
github.com/libp2p/go-libp2p-core v0.8.5
|
||||
github.com/libp2p/go-libp2p-discovery v0.5.0
|
||||
github.com/libp2p/go-libp2p-kad-dht v0.12.1
|
||||
github.com/multiformats/go-multiaddr v0.3.1
|
||||
github.com/sethvargo/go-diceware v0.2.1
|
||||
github.com/libp2p/go-libp2p v0.17.0
|
||||
github.com/libp2p/go-libp2p-core v0.13.0
|
||||
github.com/libp2p/go-libp2p-kad-dht v0.15.0
|
||||
github.com/libp2p/go-libp2p-quic-transport v0.15.2
|
||||
github.com/libp2p/go-tcp-transport v0.4.0
|
||||
github.com/multiformats/go-multiaddr v0.4.1
|
||||
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8
|
||||
github.com/tcnksm/go-latest v0.0.0-20170313132115-e3007ae9052e
|
||||
golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6
|
||||
golang.org/x/sys v0.0.0-20210603125802-9665404d3644 // indirect
|
||||
github.com/vishvananda/netlink v1.1.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
honnef.co/go/tools v0.0.1-2020.1.4 // indirect
|
||||
)
|
||||
|
BIN
hyprspace.png
BIN
hyprspace.png
Binary file not shown.
Before Width: | Height: | Size: 145 KiB After Width: | Height: | Size: 604 KiB |
@@ -2,22 +2,16 @@ package p2p
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/libp2p/go-libp2p-core/host"
|
||||
"github.com/libp2p/go-libp2p-core/network"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
discovery "github.com/libp2p/go-libp2p-discovery"
|
||||
dht "github.com/libp2p/go-libp2p-kad-dht"
|
||||
)
|
||||
|
||||
// Discover starts up a DHT based discovery system finding and adding nodes with the same rendezvous string.
|
||||
func Discover(ctx context.Context, h host.Host, dht *dht.IpfsDHT, rendezvous string, peerTable map[string]peer.ID) {
|
||||
var routingDiscovery = discovery.NewRoutingDiscovery(dht)
|
||||
discovery.Advertise(ctx, routingDiscovery, rendezvous)
|
||||
|
||||
func Discover(ctx context.Context, h host.Host, dht *dht.IpfsDHT, peerTable map[string]peer.ID) {
|
||||
ticker := time.NewTicker(time.Second * 5)
|
||||
defer ticker.Stop()
|
||||
|
||||
@@ -26,23 +20,14 @@ func Discover(ctx context.Context, h host.Host, dht *dht.IpfsDHT, rendezvous str
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-ticker.C:
|
||||
|
||||
peers, err := discovery.FindPeers(ctx, routingDiscovery, rendezvous)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
for _, p := range peers {
|
||||
if p.ID == h.ID() {
|
||||
continue
|
||||
}
|
||||
if h.Network().Connectedness(p.ID) != network.Connected {
|
||||
_, err = h.Network().DialPeer(ctx, p.ID)
|
||||
for _, id := range peerTable {
|
||||
if h.Network().Connectedness(id) != network.Connected {
|
||||
addrs, err := dht.FindPeer(ctx, id)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
_, err = h.Network().DialPeer(ctx, addrs.ID)
|
||||
if err != nil {
|
||||
fmt.Printf("Error Connecting To: %s\n", p.ID)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
30
p2p/node.go
30
p2p/node.go
@@ -2,6 +2,7 @@ package p2p
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
@@ -12,6 +13,8 @@ import (
|
||||
"github.com/libp2p/go-libp2p-core/network"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
dht "github.com/libp2p/go-libp2p-kad-dht"
|
||||
libp2pquic "github.com/libp2p/go-libp2p-quic-transport"
|
||||
"github.com/libp2p/go-tcp-transport"
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
)
|
||||
|
||||
@@ -26,10 +29,22 @@ func CreateNode(ctx context.Context, inputKey string, port int, handler network.
|
||||
return
|
||||
}
|
||||
|
||||
ip6quic := fmt.Sprintf("/ip6/::/udp/%d/quic", port)
|
||||
ip4quic := fmt.Sprintf("/ip4/0.0.0.0/udp/%d/quic", port)
|
||||
|
||||
ip6tcp := fmt.Sprintf("/ip6/::/tcp/%d", port)
|
||||
ip4tcp := fmt.Sprintf("/ip4/0.0.0.0/tcp/%d", port)
|
||||
|
||||
// Create libp2p node
|
||||
node, err = libp2p.New(ctx,
|
||||
libp2p.ListenAddrStrings(fmt.Sprintf("/ip4/0.0.0.0/tcp/%d", port)),
|
||||
node, err = libp2p.New(
|
||||
libp2p.ListenAddrStrings(ip6quic, ip4quic, ip6tcp, ip4tcp),
|
||||
libp2p.Identity(privateKey),
|
||||
libp2p.DefaultSecurity,
|
||||
libp2p.NATPortMap(),
|
||||
libp2p.DefaultMuxers,
|
||||
libp2p.Transport(libp2pquic.NewTransport),
|
||||
libp2p.Transport(tcp.NewTCPTransport),
|
||||
libp2p.FallbackDefaults,
|
||||
)
|
||||
if err != nil {
|
||||
return
|
||||
@@ -73,6 +88,7 @@ func CreateNode(ctx context.Context, inputKey string, port int, handler network.
|
||||
// Let's connect to the bootstrap nodes first. They will tell us about the
|
||||
// other nodes in the network.
|
||||
var wg sync.WaitGroup
|
||||
lock := sync.Mutex{}
|
||||
count := 0
|
||||
wg.Add(len(BootstrapPeers))
|
||||
for _, peerInfo := range BootstrapPeers {
|
||||
@@ -80,12 +96,18 @@ func CreateNode(ctx context.Context, inputKey string, port int, handler network.
|
||||
defer wg.Done()
|
||||
err := node.Connect(ctx, *peerInfo)
|
||||
if err == nil {
|
||||
lock.Lock()
|
||||
count++
|
||||
lock.Unlock()
|
||||
|
||||
}
|
||||
}(peerInfo)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
err = dhtOut.Bootstrap(ctx)
|
||||
return
|
||||
if count < 1 {
|
||||
return node, dhtOut, errors.New("unable to bootstrap libp2p node")
|
||||
}
|
||||
|
||||
return node, dhtOut, nil
|
||||
}
|
||||
|
21
tun/options.go
Normal file
21
tun/options.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package tun
|
||||
|
||||
type Option func(tun *TUN) error
|
||||
|
||||
func Address(address string) Option {
|
||||
return func(tun *TUN) error {
|
||||
return tun.setAddress(address)
|
||||
}
|
||||
}
|
||||
|
||||
func MTU(mtu int) Option {
|
||||
return func(tun *TUN) error {
|
||||
return tun.setMTU(mtu)
|
||||
}
|
||||
}
|
||||
|
||||
func DestAddress(address string) Option {
|
||||
return func(tun *TUN) error {
|
||||
return tun.setDestAddress(address)
|
||||
}
|
||||
}
|
62
tun/tun.go
62
tun/tun.go
@@ -1,52 +1,22 @@
|
||||
package tun
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
import "github.com/songgao/water"
|
||||
|
||||
"github.com/songgao/water"
|
||||
)
|
||||
type TUN struct {
|
||||
Iface *water.Interface
|
||||
MTU int
|
||||
Src string
|
||||
Dst string
|
||||
}
|
||||
|
||||
// New creates and returns a new TUN interface for the application.
|
||||
func New(name string) (result *water.Interface, err error) {
|
||||
// Setup TUN Config
|
||||
cfg := water.Config{
|
||||
DeviceType: water.TUN,
|
||||
func (t *TUN) Apply(opts ...Option) error {
|
||||
for _, opt := range opts {
|
||||
if opt == nil {
|
||||
continue
|
||||
}
|
||||
if err := opt(t); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
cfg.Name = name
|
||||
// Create TUN Interface
|
||||
result, err = water.New(cfg)
|
||||
return
|
||||
}
|
||||
|
||||
// SetMTU sets the Maximum Tansmission Unit Size for a
|
||||
// Packet on the interface.
|
||||
func SetMTU(name string, mtu int) (err error) {
|
||||
return ip("link", "set", "dev", name, "mtu", fmt.Sprintf("%d", mtu))
|
||||
}
|
||||
|
||||
// SetAddress sets the interface's known address and subnet.
|
||||
func SetAddress(name string, address string) (err error) {
|
||||
return ip("addr", "add", address, "dev", name)
|
||||
}
|
||||
|
||||
// Up brings up an interface to allow it to start accepting connections.
|
||||
func Up(name string) (err error) {
|
||||
return ip("link", "set", "dev", name, "up")
|
||||
}
|
||||
|
||||
// Down brings down an interface stopping active connections.
|
||||
func Down(name string) (err error) {
|
||||
return ip("link", "set", "dev", name, "down")
|
||||
}
|
||||
|
||||
// Delete removes a TUN device from the host.
|
||||
func Delete(name string) (err error) {
|
||||
return ip("link", "delete", "dev", name)
|
||||
}
|
||||
|
||||
func ip(args ...string) (err error) {
|
||||
cmd := exec.Command("/sbin/ip", args...)
|
||||
err = cmd.Run()
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
72
tun/tun_darwin.go
Normal file
72
tun/tun_darwin.go
Normal file
@@ -0,0 +1,72 @@
|
||||
//go:build darwin
|
||||
// +build darwin
|
||||
|
||||
package tun
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
|
||||
"github.com/songgao/water"
|
||||
)
|
||||
|
||||
// New creates and returns a new TUN interface for the application.
|
||||
func New(name string, opts ...Option) (*TUN, error) {
|
||||
// Setup TUN Config
|
||||
cfg := water.Config{
|
||||
DeviceType: water.TUN,
|
||||
}
|
||||
|
||||
// Create Water Interface
|
||||
iface, err := water.New(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create TUN result struct
|
||||
result := TUN{
|
||||
Iface: iface,
|
||||
}
|
||||
|
||||
// Apply options to set TUN config values
|
||||
err = result.Apply(opts...)
|
||||
return &result, err
|
||||
}
|
||||
|
||||
// SetMTU sets the Maximum Tansmission Unit Size for a
|
||||
// Packet on the interface.
|
||||
func (t *TUN) setMTU(mtu int) error {
|
||||
return ifconfig(t.Iface.Name(), "mtu", fmt.Sprintf("%d", mtu))
|
||||
}
|
||||
|
||||
// SetDestAddress sets the interface's address.
|
||||
func (t *TUN) setAddress(address string) error {
|
||||
t.Src = address
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetDestAddress sets the interface's address.
|
||||
func (t *TUN) setDestAddress(address string) error {
|
||||
t.Dst = address
|
||||
return nil
|
||||
}
|
||||
|
||||
// Up brings up an interface to allow it to start accepting connections.
|
||||
func (t *TUN) Up() error {
|
||||
return ifconfig(t.Iface.Name(), "inet", t.Src, t.Dst, "up")
|
||||
}
|
||||
|
||||
// Down brings down an interface stopping active connections.
|
||||
func (t *TUN) Down() error {
|
||||
return ifconfig(t.Iface.Name(), "down")
|
||||
}
|
||||
|
||||
// Delete removes a TUN device from the host.
|
||||
func Delete(name string) error {
|
||||
return fmt.Errorf("removing an interface is unsupported under mac")
|
||||
}
|
||||
|
||||
func ifconfig(args ...string) error {
|
||||
cmd := exec.Command("ifconfig", args...)
|
||||
return cmd.Run()
|
||||
}
|
92
tun/tun_linux.go
Normal file
92
tun/tun_linux.go
Normal file
@@ -0,0 +1,92 @@
|
||||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package tun
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/songgao/water"
|
||||
"github.com/vishvananda/netlink"
|
||||
)
|
||||
|
||||
// New creates and returns a new TUN interface for the application.
|
||||
func New(name string, opts ...Option) (*TUN, error) {
|
||||
// Setup TUN Config
|
||||
cfg := water.Config{
|
||||
DeviceType: water.TUN,
|
||||
}
|
||||
cfg.Name = name
|
||||
|
||||
// Create Water Interface
|
||||
iface, err := water.New(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create TUN result struct
|
||||
result := TUN{
|
||||
Iface: iface,
|
||||
}
|
||||
|
||||
// Apply options to set TUN config values
|
||||
err = result.Apply(opts...)
|
||||
return &result, err
|
||||
}
|
||||
|
||||
// setMTU sets the Maximum Tansmission Unit Size for a
|
||||
// Packet on the interface.
|
||||
func (t *TUN) setMTU(mtu int) error {
|
||||
link, err := netlink.LinkByName(t.Iface.Name())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return netlink.LinkSetMTU(link, mtu)
|
||||
}
|
||||
|
||||
// setDestAddress sets the interface's destination address and subnet.
|
||||
func (t *TUN) setAddress(address string) error {
|
||||
addr, err := netlink.ParseAddr(address)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
link, err := netlink.LinkByName(t.Iface.Name())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return netlink.AddrAdd(link, addr)
|
||||
}
|
||||
|
||||
// SetDestAddress isn't supported under Linux.
|
||||
// You should instead use set address to set the interface to handle
|
||||
// all addresses within a subnet.
|
||||
func (t *TUN) setDestAddress(address string) error {
|
||||
return errors.New("destination addresses are not supported under linux")
|
||||
}
|
||||
|
||||
// Up brings up an interface to allow it to start accepting connections.
|
||||
func (t *TUN) Up() error {
|
||||
link, err := netlink.LinkByName(t.Iface.Name())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return netlink.LinkSetUp(link)
|
||||
}
|
||||
|
||||
// Down brings down an interface stopping active connections.
|
||||
func (t *TUN) Down() error {
|
||||
link, err := netlink.LinkByName(t.Iface.Name())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return netlink.LinkSetDown(link)
|
||||
}
|
||||
|
||||
// Delete removes a TUN device from the host.
|
||||
func Delete(name string) error {
|
||||
link, err := netlink.LinkByName(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return netlink.LinkDel(link)
|
||||
}
|
127
tun/tun_windows.go
Normal file
127
tun/tun_windows.go
Normal file
@@ -0,0 +1,127 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package tun
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"os/exec"
|
||||
|
||||
"github.com/songgao/water"
|
||||
)
|
||||
|
||||
// New creates and returns a new TUN interface for the application.
|
||||
func New(name string, opts ...Option) (*TUN, error) {
|
||||
result := TUN{}
|
||||
|
||||
// Apply options early to set struct values for interface creation.
|
||||
err := result.Apply(opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TUN on Windows requires address and network to be set on device creation stage
|
||||
// We also set network to 0.0.0.0/0 so we able to reach networks behind the node
|
||||
// https://github.com/songgao/water/blob/master/params_windows.go
|
||||
// https://gitlab.com/openconnect/openconnect/-/blob/master/tun-win32.c
|
||||
ip, _, err := net.ParseCIDR(result.Src)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
network := net.IPNet{
|
||||
IP: ip,
|
||||
Mask: net.IPv4Mask(0, 0, 0, 0),
|
||||
}
|
||||
|
||||
// Setup TUN Config
|
||||
cfg := water.Config{
|
||||
DeviceType: water.TUN,
|
||||
PlatformSpecificParams: water.PlatformSpecificParams{
|
||||
ComponentID: "tap0901",
|
||||
InterfaceName: name,
|
||||
Network: network.String(),
|
||||
},
|
||||
}
|
||||
|
||||
// Interface should be enabled before creation of water interface
|
||||
// Otherwise there will be an error "The system cannot find the file specified."
|
||||
netsh("interface", "set", "interface", "name=", name, "enable")
|
||||
|
||||
// Create Water Interface
|
||||
iface, err := water.New(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Set TUN interface to newly created interface
|
||||
result.Iface = iface
|
||||
|
||||
// Apply options to setup TUN interface configuration
|
||||
// Setup interface address
|
||||
err = result.setupAddress(result.Src)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Setup interface mtu size
|
||||
err = result.setupMTU(result.MTU)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &result, err
|
||||
}
|
||||
|
||||
// setMTU configures the interface's MTU.
|
||||
func (t *TUN) setMTU(mtu int) error {
|
||||
t.MTU = mtu
|
||||
return nil
|
||||
}
|
||||
|
||||
// setAddress configures the interface's address.
|
||||
func (t *TUN) setAddress(address string) error {
|
||||
t.Src = address
|
||||
return nil
|
||||
}
|
||||
|
||||
// setupMTU sets the Maximum Tansmission Unit Size for a
|
||||
// Packet on the interface.
|
||||
func (t *TUN) setupMTU(mtu int) error {
|
||||
return netsh("interface", "ipv4", "set", "subinterface", t.Iface.Name(), "mtu=", fmt.Sprintf("%d", mtu))
|
||||
}
|
||||
|
||||
// setupAddress sets the interface's destination address and subnet.
|
||||
func (t *TUN) setupAddress(address string) error {
|
||||
return netsh("interface", "ip", "set", "address", "name=", t.Iface.Name(), "static", address)
|
||||
}
|
||||
|
||||
// SetDestAddress isn't supported under Windows.
|
||||
// You should instead use set address to set the interface to handle
|
||||
// all addresses within a subnet.
|
||||
func (t *TUN) setDestAddress(address string) error {
|
||||
return errors.New("destination addresses are not supported under windows")
|
||||
}
|
||||
|
||||
// Up brings up an interface to allow it to start accepting connections.
|
||||
func (t *TUN) Up() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Down brings down an interface stopping active connections.
|
||||
func (t *TUN) Down() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete removes a TUN device from the host.
|
||||
func Delete(name string) error {
|
||||
return netsh("interface", "set", "interface", "name=", name, "disable")
|
||||
}
|
||||
|
||||
func netsh(args ...string) (err error) {
|
||||
cmd := exec.Command("netsh", args...)
|
||||
err = cmd.Run()
|
||||
return
|
||||
}
|
Reference in New Issue
Block a user