commit
227ed206a4
56
README.md
56
README.md
@ -61,10 +61,12 @@ Main configuration example:
|
||||
|
||||
{
|
||||
"ExternalIP": "198.51.100.2",
|
||||
"ExternalIP6": "2001:0db8:85a3:0000:0000:8a2e:0370:7334",
|
||||
"ListenPort": 51820,
|
||||
"Domain": "dsnet",
|
||||
"InterfaceName": "dsnet",
|
||||
"Network": "10.164.236.0/22",
|
||||
"Network6": "fd00:7b31:106a:ae00::/64",
|
||||
"IP": "10.164.236.1",
|
||||
"DNS": "",
|
||||
"Networks": [],
|
||||
@ -76,6 +78,7 @@ Main configuration example:
|
||||
"Owner": "naggie",
|
||||
"Description": "Home server",
|
||||
"IP": "10.164.236.2",
|
||||
"IP6": "fd00:7b31:106a:ae00:44c3:29c3:53b1:a6f9",
|
||||
"Added": "2020-05-07T10:04:46.336286992+01:00",
|
||||
"Networks": [],
|
||||
"PublicKey": "altJeQ/V52JZQrGcA9RiKcpZusYU6zMUJhl7Wbd9rX0=",
|
||||
@ -88,11 +91,11 @@ Explanation of each field:
|
||||
|
||||
{
|
||||
"ExternalIP": "198.51.100.2",
|
||||
"ExternalIP6": "2001:0db8:85a3:0000:0000:8a2e:0370:7334",
|
||||
|
||||
This is the external IP that will be the value of Endpoint for the server peer
|
||||
in client configs. It is automatically detected by opening a socket or using an
|
||||
external IP discovery service -- the first to give a valid public IPv4 will
|
||||
win.
|
||||
external IP discovery service -- the first to give a valid public IP will win.
|
||||
|
||||
|
||||
"ListenPort": 51820,
|
||||
@ -110,6 +113,7 @@ connection by polling the report file.
|
||||
The wireguard interface name.
|
||||
|
||||
"Network": "10.164.236.0/22",
|
||||
"Network6": "fd00:7b31:106a:ae00::/64",
|
||||
|
||||
The CIDR network to use when allocating IPs to peers. This subnet, a `/22` in
|
||||
the `10.0.0.0/16` block is generated randomly to (probably) avoid collisions
|
||||
@ -117,7 +121,10 @@ with other networks. There are 1022 addresses available. Addresses are
|
||||
allocated to peers when peers are added with `dsnet add` using the lowest
|
||||
available address.
|
||||
|
||||
A random ULA network with a subnet of 0 is generated for IPv6.
|
||||
|
||||
"IP": "10.164.236.1",
|
||||
"IP6": "fd00:7b31:106a:ae00:44c3:29c3:53b1:a6f9",
|
||||
|
||||
This is the private VPN IP of the server peer. It is the first address in the
|
||||
above pool.
|
||||
@ -261,11 +268,54 @@ See
|
||||
[etc/README.md](https://github.com/naggie/dsnet/blob/master/contrib/report_rendering/README.md)
|
||||
for hugo and PHP code for rendering a similar table.
|
||||
|
||||
# Generating other config files
|
||||
|
||||
dsnet currently supports the generation of `wg-quick` configuration by default.
|
||||
It can also generate VyOS/Vyatta configuration for EdgeOS/Unifi devices such as
|
||||
the Edgerouter 4 using the
|
||||
[wireguard-vyatta](https://github.com/WireGuard/wireguard-vyatta-ubnt) package.
|
||||
|
||||
To change the config file format, set the following environment variables:
|
||||
|
||||
* `DSNET_OUTPUT=vyatta`
|
||||
* `DSNET_OUTPUT=wg-quick`
|
||||
|
||||
Example vyatta output:
|
||||
|
||||
configure
|
||||
set interfaces wireguard wg0 address 10.165.52.3/22
|
||||
set interfaces wireguard wg0 address fd00:7b31:106a:ae00:f7bb:bf31:201f:60ab/64
|
||||
set interfaces wireguard wg0 route-allowed-ips true
|
||||
set interfaces wireguard wg0 private-key cAtj1tbjGGmVoxdY78q9Sv0EgNlawbzffGWjajQkLFw=
|
||||
set interfaces wireguard wg0 description dsnet
|
||||
|
||||
set interfaces wireguard wg0 peer PjxQM7OwVYvOJfORA1EluLw8CchSu7jLq92YYJi5ohY= endpoint 123.123.123.123:51820
|
||||
set interfaces wireguard wg0 peer PjxQM7OwVYvOJfORA1EluLw8CchSu7jLq92YYJi5ohY= persistent-keepalive 25
|
||||
set interfaces wireguard wg0 peer PjxQM7OwVYvOJfORA1EluLw8CchSu7jLq92YYJi5ohY= preshared-key w1FtOKoMEdnhsjREtSvpg1CHEKFzFzJWaQYZwaUCV38=
|
||||
set interfaces wireguard wg0 peer PjxQM7OwVYvOJfORA1EluLw8CchSu7jLq92YYJi5ohY= allowed-ips 10.165.52.0/22
|
||||
set interfaces wireguard wg0 peer PjxQM7OwVYvOJfORA1EluLw8CchSu7jLq92YYJi5ohY= allowed-ips fd00:7b31:106a:ae00::/64
|
||||
commit; save
|
||||
|
||||
Replace `wg0` with an unused interface name in the range `wg0-wg999`.
|
||||
|
||||
# FAQ
|
||||
|
||||
> Does dsnet support IPv6?
|
||||
|
||||
Not currently but this is a [planned feature](https://github.com/naggie/dsnet/issues/1).
|
||||
Yes! By default since version 0.2, a random ULA subnet is generated with a 0
|
||||
subnet ID. Peers are allocated random addresses when added. Existing IPv4
|
||||
configs will not be updated -- add a `Network6` subnet to the existing config
|
||||
to allocate addresses to new peers.
|
||||
|
||||
Like IPv4, it's up to you if you want to provide NAT IPv6 access to the
|
||||
internet; alternatively (and preferably) you can allocate a a real IPv6 subnet
|
||||
such that all peers have a real globally routeable IPv6 address.
|
||||
|
||||
Upon initialisation, the server IPv4 and IPv6 external IP addresses are
|
||||
discovered on a best-effort basis. Clients will have configuration configured
|
||||
for the server IPv4 preferentially. If not IPv4 is configured, IPv6 is used;
|
||||
this is to give the best chance of the VPN working regardless of the dodgy
|
||||
network you're on.
|
||||
|
||||
> Is dsnet production ready?
|
||||
|
||||
|
76
add.go
76
add.go
@ -3,40 +3,73 @@ package dsnet
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
)
|
||||
|
||||
const wgQuickPeerConf = `[Interface]
|
||||
Address = {{ .Peer.IP }}/22
|
||||
{{ if gt (.DsnetConfig.Network.IPNet.IP | len) 0 -}}
|
||||
Address={{ .Peer.IP }}/{{ .CidrSize }}
|
||||
{{ end -}}
|
||||
{{ if gt (.DsnetConfig.Network6.IPNet.IP | len) 0 -}}
|
||||
Address={{ .Peer.IP6 }}/{{ .CidrSize6 }}
|
||||
{{ end -}}
|
||||
PrivateKey={{ .Peer.PrivateKey.Key }}
|
||||
{{- if .DsnetConfig.DNS }}
|
||||
DNS = {{ .DsnetConfig.DNS }}
|
||||
DNS={{ .DsnetConfig.DNS }}
|
||||
{{ end }}
|
||||
|
||||
[Peer]
|
||||
PublicKey={{ .DsnetConfig.PrivateKey.PublicKey.Key }}
|
||||
PresharedKey={{ .Peer.PresharedKey.Key }}
|
||||
{{ if gt (.DsnetConfig.ExternalIP | len) 0 -}}
|
||||
Endpoint={{ .DsnetConfig.ExternalIP }}:{{ .DsnetConfig.ListenPort }}
|
||||
AllowedIPs={{ .AllowedIPs }}
|
||||
{{ else -}}
|
||||
Endpoint={{ .DsnetConfig.ExternalIP6 }}:{{ .DsnetConfig.ListenPort }}
|
||||
{{ end -}}
|
||||
PersistentKeepalive={{ .Keepalive }}
|
||||
{{ if gt (.DsnetConfig.Network.IPNet.IP | len) 0 -}}
|
||||
AllowedIPs={{ .DsnetConfig.Network }}
|
||||
{{ end -}}
|
||||
{{ if gt (.DsnetConfig.Network6.IPNet.IP | len) 0 -}}
|
||||
AllowedIPs={{ .DsnetConfig.Network6 }}
|
||||
{{ end -}}
|
||||
{{ range .DsnetConfig.Networks -}}
|
||||
AllowedIPs={{ . }}
|
||||
{{ end -}}
|
||||
`
|
||||
|
||||
// TODO use random wg0-wg999 to hopefully avoid conflict by default?
|
||||
const vyattaPeerConf = `configure
|
||||
set interfaces wireguard wg0 address {{ .Peer.IP }}/{{ .Cidrmask }}
|
||||
{{ if gt (.DsnetConfig.Network.IPNet.IP | len) 0 -}}
|
||||
set interfaces wireguard wg0 address {{ .Peer.IP }}/{{ .CidrSize }}
|
||||
{{ end -}}
|
||||
{{ if gt (.DsnetConfig.Network6.IPNet.IP | len) 0 -}}
|
||||
set interfaces wireguard wg0 address {{ .Peer.IP6 }}/{{ .CidrSize6 }}
|
||||
{{ end -}}
|
||||
set interfaces wireguard wg0 route-allowed-ips true
|
||||
set interfaces wireguard wg0 private-key {{ .Peer.PrivateKey.Key }}
|
||||
set interfaces wireguard wg0 description {{ conf.InterfaceName }}
|
||||
set interfaces wireguard wg0 description {{ .DsnetConfig.InterfaceName }}
|
||||
{{- if .DsnetConfig.DNS }}
|
||||
#set service dns forwarding name-server {{ .DsnetConfig.DNS }}
|
||||
{{ end }}
|
||||
|
||||
{{ if gt (.DsnetConfig.ExternalIP | len) 0 -}}
|
||||
set interfaces wireguard wg0 peer {{ .DsnetConfig.PrivateKey.PublicKey.Key }} endpoint {{ .DsnetConfig.ExternalIP }}:{{ .DsnetConfig.ListenPort }}
|
||||
set interfaces wireguard wg0 peer {{ .DsnetConfig.PrivateKey.PublicKey.Key }} allowed-ips {{ .AllowedIPs }}
|
||||
{{ else -}}
|
||||
set interfaces wireguard wg0 peer {{ .DsnetConfig.PrivateKey.PublicKey.Key }} endpoint {{ .DsnetConfig.ExternalIP6 }}:{{ .DsnetConfig.ListenPort }}
|
||||
{{ end -}}
|
||||
set interfaces wireguard wg0 peer {{ .DsnetConfig.PrivateKey.PublicKey.Key }} persistent-keepalive {{ .Keepalive }}
|
||||
set interfaces wireguard wg0 peer {{ .DsnetConfig.PrivateKey.PublicKey.Key }} preshared-key {{ .Peer.PresharedKey.Key }}
|
||||
{{ if gt (.DsnetConfig.Network.IPNet.IP | len) 0 -}}
|
||||
set interfaces wireguard wg0 peer {{ .DsnetConfig.PrivateKey.PublicKey.Key }} allowed-ips {{ .DsnetConfig.Network }}
|
||||
{{ end -}}
|
||||
{{ if gt (.DsnetConfig.Network6.IPNet.IP | len) 0 -}}
|
||||
set interfaces wireguard wg0 peer {{ .DsnetConfig.PrivateKey.PublicKey.Key }} allowed-ips {{ .DsnetConfig.Network6 }}
|
||||
{{ end -}}
|
||||
{{ range .DsnetConfig.Networks -}}
|
||||
set interfaces wireguard wg0 peer {{ .DsnetConfig.PrivateKey.PublicKey.Key }} allowed-ips {{ . }}
|
||||
{{ end -}}
|
||||
commit; save
|
||||
`
|
||||
|
||||
@ -62,8 +95,6 @@ func Add() {
|
||||
privateKey := GenerateJSONPrivateKey()
|
||||
publicKey := privateKey.PublicKey()
|
||||
|
||||
IP := conf.MustAllocateIP()
|
||||
|
||||
peer := PeerConfig{
|
||||
Owner: owner,
|
||||
Hostname: hostname,
|
||||
@ -72,10 +103,21 @@ func Add() {
|
||||
PublicKey: publicKey,
|
||||
PrivateKey: privateKey, // omitted from server config JSON!
|
||||
PresharedKey: GenerateJSONKey(),
|
||||
IP: IP,
|
||||
Networks: []JSONIPNet{},
|
||||
}
|
||||
|
||||
if len(conf.Network.IPNet.Mask) > 0 {
|
||||
peer.IP = conf.MustAllocateIP()
|
||||
}
|
||||
|
||||
if len(conf.Network6.IPNet.Mask) > 0 {
|
||||
peer.IP6 = conf.MustAllocateIP6()
|
||||
}
|
||||
|
||||
if len(conf.IP) == 0 && len(conf.IP6) == 0 {
|
||||
ExitFail("No IPv4 or IPv6 network defined in config")
|
||||
}
|
||||
|
||||
conf.MustAddPeer(peer)
|
||||
PrintPeerCfg(peer, conf)
|
||||
conf.MustSave()
|
||||
@ -83,13 +125,6 @@ func Add() {
|
||||
}
|
||||
|
||||
func PrintPeerCfg(peer PeerConfig, conf *DsnetConfig) {
|
||||
allowedIPsStr := make([]string, len(conf.Networks)+1)
|
||||
allowedIPsStr[0] = conf.Network.String()
|
||||
|
||||
for i, net := range conf.Networks {
|
||||
allowedIPsStr[i+1] = net.String()
|
||||
}
|
||||
|
||||
var peerConf string
|
||||
|
||||
switch os.Getenv("DSNET_OUTPUT") {
|
||||
@ -103,15 +138,16 @@ func PrintPeerCfg(peer PeerConfig, conf *DsnetConfig) {
|
||||
ExitFail("Unrecognised DSNET_OUTPUT type")
|
||||
}
|
||||
|
||||
cidrmask, _ := conf.Network.IPNet.Mask.Size()
|
||||
cidrSize, _ := conf.Network.IPNet.Mask.Size()
|
||||
cidrSize6, _ := conf.Network6.IPNet.Mask.Size()
|
||||
|
||||
t := template.Must(template.New("peerConf").Parse(peerConf))
|
||||
err := t.Execute(os.Stdout, map[string]interface{}{
|
||||
"Peer": peer,
|
||||
"DsnetConfig": conf,
|
||||
"Keepalive": time.Duration(KEEPALIVE).Seconds(),
|
||||
"AllowedIPs": strings.Join(allowedIPsStr, ","),
|
||||
"Cidrmask": cidrmask,
|
||||
"CidrSize": cidrSize,
|
||||
"CidrSize6": cidrSize6,
|
||||
})
|
||||
check(err)
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package dsnet
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
@ -20,7 +21,8 @@ type PeerConfig struct {
|
||||
// Description of what the host is and/or does
|
||||
Description string `validate:"required,gte=1,lte=255"`
|
||||
// Internal VPN IP address. Added to AllowedIPs in server config as a /32
|
||||
IP net.IP `validate:"required`
|
||||
IP net.IP
|
||||
IP6 net.IP
|
||||
Added time.Time `validate:"required"`
|
||||
// TODO ExternalIP support (Endpoint)
|
||||
//ExternalIP net.UDPAddr `validate:"required,udp4_addr"`
|
||||
@ -34,15 +36,18 @@ type PeerConfig struct {
|
||||
type DsnetConfig struct {
|
||||
// domain to append to hostnames. Relies on separate DNS server for
|
||||
// resolution. Informational only.
|
||||
ExternalIP net.IP `validate:"required"`
|
||||
ExternalIP net.IP
|
||||
ExternalIP6 net.IP
|
||||
ListenPort int `validate:"gte=1024,lte=65535"`
|
||||
Domain string `validate:"required,gte=1,lte=255"`
|
||||
InterfaceName string `validate:"required,gte=1,lte=255"`
|
||||
// IP network from which to allocate automatic sequential addresses
|
||||
// Network is chosen randomly when not specified
|
||||
Network JSONIPNet `validate:"required"`
|
||||
IP net.IP `validate:"required"`
|
||||
DNS net.IP
|
||||
Network JSONIPNet `validate:"required"`
|
||||
Network6 JSONIPNet `validate:"required"`
|
||||
IP net.IP
|
||||
IP6 net.IP
|
||||
DNS net.IP
|
||||
// extra networks available, will be added to AllowedIPs
|
||||
Networks []JSONIPNet `validate:"required"`
|
||||
// TODO Default subnets to route via VPN
|
||||
@ -69,6 +74,10 @@ func MustLoadDsnetConfig() *DsnetConfig {
|
||||
err = validator.New().Struct(conf)
|
||||
check(err)
|
||||
|
||||
if len(conf.ExternalIP) == 0 && len(conf.ExternalIP6) == 0 {
|
||||
ExitFail("Config does not contain ExternalIP or ExternalIP6")
|
||||
}
|
||||
|
||||
return &conf
|
||||
}
|
||||
|
||||
@ -127,16 +136,16 @@ func (conf *DsnetConfig) MustRemovePeer(hostname string) {
|
||||
|
||||
// remove peer from slice, retaining order
|
||||
copy(conf.Peers[peerIndex:], conf.Peers[peerIndex+1:]) // shift left
|
||||
conf.Peers = conf.Peers[:len(conf.Peers)-1] // truncate
|
||||
conf.Peers = conf.Peers[:len(conf.Peers)-1] // truncate
|
||||
}
|
||||
|
||||
func (conf DsnetConfig) IPAllocated(IP net.IP) bool {
|
||||
if IP.Equal(conf.IP) {
|
||||
if IP.Equal(conf.IP) || IP.Equal(conf.IP6) {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, peer := range conf.Peers {
|
||||
if IP.Equal(peer.IP) {
|
||||
if IP.Equal(peer.IP) || IP.Equal(peer.IP6) {
|
||||
return true
|
||||
}
|
||||
|
||||
@ -150,16 +159,21 @@ func (conf DsnetConfig) IPAllocated(IP net.IP) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// choose a free IP for a new Peer
|
||||
// choose a free IPv4 for a new Peer (sequential allocation)
|
||||
func (conf DsnetConfig) MustAllocateIP() net.IP {
|
||||
network := conf.Network.IPNet
|
||||
ones, bits := network.Mask.Size()
|
||||
zeros := bits - ones
|
||||
min := 1 // avoids network addr
|
||||
max := (1 << zeros) - 2 // avoids broadcast addr + overflow
|
||||
|
||||
// avoids network addr
|
||||
min := 1
|
||||
// avoids broadcast addr + overflow
|
||||
max := (1 << zeros) - 2
|
||||
|
||||
IP := make(net.IP, len(network.IP))
|
||||
|
||||
for i := min; i <= max; i++ {
|
||||
IP := make(net.IP, len(network.IP))
|
||||
// dst, src!
|
||||
copy(IP, network.IP)
|
||||
|
||||
// OR the host part with the network part
|
||||
@ -178,6 +192,37 @@ func (conf DsnetConfig) MustAllocateIP() net.IP {
|
||||
return net.IP{}
|
||||
}
|
||||
|
||||
// choose a free IPv6 for a new Peer (pseudorandom allocation)
|
||||
func (conf DsnetConfig) MustAllocateIP6() net.IP {
|
||||
network := conf.Network6.IPNet
|
||||
ones, bits := network.Mask.Size()
|
||||
zeros := bits - ones
|
||||
|
||||
rbs := make([]byte, zeros)
|
||||
rand.Seed(time.Now().UTC().UnixNano())
|
||||
|
||||
IP := make(net.IP, len(network.IP))
|
||||
|
||||
for i := 0; i <= 10000; i++ {
|
||||
rand.Read(rbs)
|
||||
// dst, src! Copy prefix of IP
|
||||
copy(IP, network.IP)
|
||||
|
||||
// OR the host part with the network part
|
||||
for j := ones / 8; j < len(IP); j++ {
|
||||
IP[j] = IP[j] | rbs[j]
|
||||
}
|
||||
|
||||
if !conf.IPAllocated(IP) {
|
||||
return IP
|
||||
}
|
||||
}
|
||||
|
||||
ExitFail("Could not allocate random IPv6 after 10000 tries. This was highly unlikely!")
|
||||
|
||||
return net.IP{}
|
||||
}
|
||||
|
||||
func (conf DsnetConfig) GetWgPeerConfigs() []wgtypes.PeerConfig {
|
||||
wgPeers := make([]wgtypes.PeerConfig, 0, len(conf.Peers))
|
||||
|
||||
@ -187,10 +232,26 @@ func (conf DsnetConfig) GetWgPeerConfigs() []wgtypes.PeerConfig {
|
||||
presharedKey := peer.PresharedKey.Key
|
||||
|
||||
// AllowedIPs = private IP + defined networks
|
||||
allowedIPs := make([]net.IPNet, len(peer.Networks)+1)
|
||||
allowedIPs[0] = net.IPNet{
|
||||
IP: peer.IP,
|
||||
Mask: net.IPMask{255, 255, 255, 255},
|
||||
allowedIPs := make([]net.IPNet, 0, len(peer.Networks)+2)
|
||||
|
||||
if len(peer.IP) > 0 {
|
||||
allowedIPs = append(
|
||||
allowedIPs,
|
||||
net.IPNet{
|
||||
IP: peer.IP,
|
||||
Mask: net.IPMask{255, 255, 255, 255},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
if len(peer.IP6) > 0 {
|
||||
allowedIPs = append(
|
||||
allowedIPs,
|
||||
net.IPNet{
|
||||
IP: peer.IP6,
|
||||
Mask: net.IPMask{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
for i, net := range peer.Networks {
|
||||
|
2
const.go
2
const.go
@ -27,7 +27,7 @@ const (
|
||||
|
||||
var (
|
||||
// populated with LDFLAGS, see do-release.sh
|
||||
VERSION = "unknown"
|
||||
VERSION = "unknown"
|
||||
GIT_COMMIT = "unknown"
|
||||
BUILD_DATE = "unknown"
|
||||
)
|
||||
|
22
exttypes.go
22
exttypes.go
@ -12,14 +12,30 @@ type JSONIPNet struct {
|
||||
}
|
||||
|
||||
func (n JSONIPNet) MarshalJSON() ([]byte, error) {
|
||||
return []byte("\"" + n.IPNet.String() + "\""), nil
|
||||
if len(n.IPNet.IP) == 0 {
|
||||
return []byte("\"\""), nil
|
||||
} else {
|
||||
return []byte("\"" + n.IPNet.String() + "\""), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (n *JSONIPNet) UnmarshalJSON(b []byte) error {
|
||||
cidr := strings.Trim(string(b), "\"")
|
||||
|
||||
if cidr == "" {
|
||||
// Leave as empty/uninitialised IPNet. A bit like omitempty behaviour,
|
||||
// but we can leave the field there and blank which is useful if the
|
||||
// user wishes to add the cidr manually.
|
||||
return nil
|
||||
}
|
||||
|
||||
IP, IPNet, err := net.ParseCIDR(cidr)
|
||||
IPNet.IP = IP
|
||||
n.IPNet = *IPNet
|
||||
|
||||
if err == nil {
|
||||
IPNet.IP = IP
|
||||
n.IPNet = *IPNet
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
|
90
init.go
90
init.go
@ -21,27 +21,34 @@ func Init() {
|
||||
conf := DsnetConfig{
|
||||
PrivateKey: GenerateJSONPrivateKey(),
|
||||
ListenPort: DEFAULT_LISTEN_PORT,
|
||||
Network: getRandomNetwork(),
|
||||
Network: getPrivateNet(),
|
||||
Network6: getULANet(),
|
||||
Peers: []PeerConfig{},
|
||||
Domain: "dsnet",
|
||||
ReportFile: DEFAULT_REPORT_FILE,
|
||||
ExternalIP: getExternalIP(),
|
||||
ExternalIP6: getExternalIP6(),
|
||||
InterfaceName: DEFAULT_INTERFACE_NAME,
|
||||
Networks: []JSONIPNet{},
|
||||
}
|
||||
|
||||
IP := conf.MustAllocateIP()
|
||||
conf.IP = IP
|
||||
conf.IP = conf.MustAllocateIP()
|
||||
conf.IP6 = conf.MustAllocateIP6()
|
||||
|
||||
if len(conf.ExternalIP) == 0 && len(conf.ExternalIP6) == 0 {
|
||||
ExitFail("Could not determine any external IP, v4 or v6")
|
||||
}
|
||||
|
||||
// DNS not set by default
|
||||
//conf.DNS = IP
|
||||
|
||||
conf.MustSave()
|
||||
|
||||
fmt.Printf("Config written to %s. Please check/edit.", CONFIG_FILE)
|
||||
fmt.Printf("Config written to %s. Please check/edit.\n", CONFIG_FILE)
|
||||
}
|
||||
|
||||
// get a random /22 subnet on 10.0.0.0 (1023 hosts) (or /24?)
|
||||
func getRandomNetwork() JSONIPNet {
|
||||
// get a random IPv4 /22 subnet on 10.0.0.0 (1023 hosts) (or /24?)
|
||||
func getPrivateNet() JSONIPNet {
|
||||
rbs := make([]byte, 2)
|
||||
rand.Seed(time.Now().UTC().UnixNano())
|
||||
rand.Read(rbs)
|
||||
@ -54,19 +61,37 @@ func getRandomNetwork() JSONIPNet {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO support IPv6
|
||||
func getULANet() JSONIPNet {
|
||||
rbs := make([]byte, 5)
|
||||
rand.Seed(time.Now().UTC().UnixNano())
|
||||
rand.Read(rbs)
|
||||
|
||||
// fd00 prefix with 40 bit global id and zero (16 bit) subnet ID
|
||||
return JSONIPNet{
|
||||
IPNet: net.IPNet{
|
||||
net.IP{0xfd, 0, rbs[0], rbs[1], rbs[2], rbs[3], rbs[4], 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
net.IPMask{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// TODO factor getExternalIP + getExternalIP6
|
||||
func getExternalIP() net.IP {
|
||||
conn, err := net.Dial("udp", "8.8.8.8:80")
|
||||
check(err, "Could not detect internet connection")
|
||||
defer conn.Close()
|
||||
var IP net.IP
|
||||
// arbitrary external IP is used (one that's guaranteed to route outside.
|
||||
// In this case, Google's DNS server. Doesn't actually need to be online.)
|
||||
conn, err := net.Dial("udp", "8.8.8.8:53")
|
||||
if err == nil {
|
||||
defer conn.Close()
|
||||
|
||||
localAddr := conn.LocalAddr().String()
|
||||
IP := net.ParseIP(strings.Split(localAddr, ":")[0])
|
||||
IP = IP.To4()
|
||||
localAddr := conn.LocalAddr().String()
|
||||
IP = net.ParseIP(strings.Split(localAddr, ":")[0])
|
||||
IP = IP.To4()
|
||||
|
||||
if !(IP[0] == 10 || (IP[0] == 172 && IP[1] >= 16 && IP[1] <= 31) || (IP[0] == 192 && IP[1] == 168)) {
|
||||
// not private, so public
|
||||
return IP
|
||||
if !(IP[0] == 10 || (IP[0] == 172 && IP[1] >= 16 && IP[1] <= 31) || (IP[0] == 192 && IP[1] == 168)) {
|
||||
// not private, so public
|
||||
return IP
|
||||
}
|
||||
}
|
||||
|
||||
// detect private IP and use icanhazip.com instead
|
||||
@ -86,3 +111,36 @@ func getExternalIP() net.IP {
|
||||
|
||||
return net.IP{}
|
||||
}
|
||||
|
||||
func getExternalIP6() net.IP {
|
||||
var IP net.IP
|
||||
conn, err := net.Dial("udp", "2001:4860:4860::8888:53")
|
||||
if err == nil {
|
||||
defer conn.Close()
|
||||
|
||||
localAddr := conn.LocalAddr().String()
|
||||
IP = net.ParseIP(strings.Split(localAddr, ":")[0])
|
||||
|
||||
// check is not a ULA
|
||||
if IP[0] != 0xfd && IP[0] != 0xfc {
|
||||
return IP
|
||||
}
|
||||
}
|
||||
|
||||
client := http.Client{
|
||||
Timeout: 5 * time.Second,
|
||||
}
|
||||
resp, err := client.Get("https://ipv6.icanhazip.com/")
|
||||
if err == nil {
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == http.StatusOK {
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
check(err)
|
||||
IP = net.ParseIP(strings.TrimSpace(string(body)))
|
||||
return IP
|
||||
}
|
||||
}
|
||||
|
||||
return net.IP{}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user