2022-01-30 19:48:32 +00:00
|
|
|
package mobile
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/hex"
|
|
|
|
"encoding/json"
|
|
|
|
"net"
|
2022-09-03 10:42:05 +00:00
|
|
|
"regexp"
|
2022-01-30 19:48:32 +00:00
|
|
|
|
|
|
|
"github.com/gologme/log"
|
|
|
|
|
|
|
|
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
|
|
|
"github.com/yggdrasil-network/yggdrasil-go/src/config"
|
|
|
|
"github.com/yggdrasil-network/yggdrasil-go/src/core"
|
|
|
|
"github.com/yggdrasil-network/yggdrasil-go/src/ipv6rwc"
|
|
|
|
"github.com/yggdrasil-network/yggdrasil-go/src/multicast"
|
2023-06-06 21:11:49 +00:00
|
|
|
"github.com/yggdrasil-network/yggdrasil-go/src/tun"
|
2022-01-30 19:48:32 +00:00
|
|
|
"github.com/yggdrasil-network/yggdrasil-go/src/version"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Yggdrasil mobile package is meant to "plug the gap" for mobile support, as
|
|
|
|
// Gomobile will not create headers for Swift/Obj-C etc if they have complex
|
|
|
|
// (non-native) types. Therefore for iOS we will expose some nice simple
|
|
|
|
// functions. Note that in the case of iOS we handle reading/writing to/from TUN
|
|
|
|
// in Swift therefore we use the "dummy" TUN interface instead.
|
|
|
|
type Yggdrasil struct {
|
2022-08-06 14:05:12 +00:00
|
|
|
core *core.Core
|
2022-01-30 19:48:32 +00:00
|
|
|
iprwc *ipv6rwc.ReadWriteCloser
|
|
|
|
config *config.NodeConfig
|
2022-09-03 10:42:05 +00:00
|
|
|
multicast *multicast.Multicast
|
2023-06-06 21:11:49 +00:00
|
|
|
tun *tun.TunAdapter // optional
|
2022-01-30 19:48:32 +00:00
|
|
|
log MobileLogger
|
2023-06-06 21:11:49 +00:00
|
|
|
logger *log.Logger
|
2022-01-30 19:48:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// StartAutoconfigure starts a node with a randomly generated config
|
|
|
|
func (m *Yggdrasil) StartAutoconfigure() error {
|
|
|
|
return m.StartJSON([]byte("{}"))
|
|
|
|
}
|
|
|
|
|
|
|
|
// StartJSON starts a node with the given JSON config. You can get JSON config
|
|
|
|
// (rather than HJSON) by using the GenerateConfigJSON() function
|
|
|
|
func (m *Yggdrasil) StartJSON(configjson []byte) error {
|
2023-10-11 17:25:35 +00:00
|
|
|
setMemLimitIfPossible()
|
|
|
|
|
2022-01-30 19:48:32 +00:00
|
|
|
logger := log.New(m.log, "", 0)
|
|
|
|
logger.EnableLevel("error")
|
|
|
|
logger.EnableLevel("warn")
|
|
|
|
logger.EnableLevel("info")
|
2023-10-25 18:59:19 +00:00
|
|
|
m.logger = logger
|
2023-04-06 20:45:49 +00:00
|
|
|
m.config = config.GenerateConfig()
|
|
|
|
if err := m.config.UnmarshalHJSON(configjson); err != nil {
|
2022-01-30 19:48:32 +00:00
|
|
|
return err
|
|
|
|
}
|
2024-05-27 20:57:28 +00:00
|
|
|
// Set up the Yggdrasil node itself.
|
2022-09-03 10:42:05 +00:00
|
|
|
{
|
2024-11-16 22:59:03 +00:00
|
|
|
iprange := net.IPNet{
|
|
|
|
IP: net.ParseIP("200::"),
|
|
|
|
Mask: net.CIDRMask(7, 128),
|
|
|
|
}
|
|
|
|
options := []core.SetupOption{
|
|
|
|
core.PeerFilter(func(ip net.IP) bool {
|
|
|
|
return !iprange.Contains(ip)
|
|
|
|
}),
|
|
|
|
}
|
2022-09-03 10:42:05 +00:00
|
|
|
for _, peer := range m.config.Peers {
|
|
|
|
options = append(options, core.Peer{URI: peer})
|
|
|
|
}
|
|
|
|
for intf, peers := range m.config.InterfacePeers {
|
|
|
|
for _, peer := range peers {
|
|
|
|
options = append(options, core.Peer{URI: peer, SourceInterface: intf})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for _, allowed := range m.config.AllowedPublicKeys {
|
|
|
|
k, err := hex.DecodeString(allowed)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
options = append(options, core.AllowedPublicKey(k[:]))
|
|
|
|
}
|
2023-10-21 17:33:17 +00:00
|
|
|
for _, lAddr := range m.config.Listen {
|
|
|
|
options = append(options, core.ListenAddress(lAddr))
|
|
|
|
}
|
2023-04-06 20:45:49 +00:00
|
|
|
var err error
|
|
|
|
m.core, err = core.New(m.config.Certificate, logger, options...)
|
2022-08-06 14:05:12 +00:00
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2023-10-22 14:51:30 +00:00
|
|
|
address, subnet := m.core.Address(), m.core.Subnet()
|
|
|
|
logger.Infof("Your public key is %s", hex.EncodeToString(m.core.PublicKey()))
|
|
|
|
logger.Infof("Your IPv6 address is %s", address.String())
|
|
|
|
logger.Infof("Your IPv6 subnet is %s", subnet.String())
|
2022-08-06 14:05:12 +00:00
|
|
|
}
|
2022-09-03 10:42:05 +00:00
|
|
|
|
2024-05-27 20:57:28 +00:00
|
|
|
// Set up the multicast module.
|
2022-09-03 10:42:05 +00:00
|
|
|
if len(m.config.MulticastInterfaces) > 0 {
|
|
|
|
var err error
|
2023-10-25 18:59:19 +00:00
|
|
|
logger.Infof("Initializing multicast %s", "")
|
2022-09-03 10:42:05 +00:00
|
|
|
options := []multicast.SetupOption{}
|
|
|
|
for _, intf := range m.config.MulticastInterfaces {
|
|
|
|
options = append(options, multicast.MulticastInterface{
|
2022-10-26 08:24:24 +00:00
|
|
|
Regex: regexp.MustCompile(intf.Regex),
|
|
|
|
Beacon: intf.Beacon,
|
|
|
|
Listen: intf.Listen,
|
|
|
|
Port: intf.Port,
|
2022-11-01 17:42:52 +00:00
|
|
|
Priority: uint8(intf.Priority),
|
2023-10-11 18:28:28 +00:00
|
|
|
Password: intf.Password,
|
2022-09-03 10:42:05 +00:00
|
|
|
})
|
|
|
|
}
|
2023-10-25 18:59:19 +00:00
|
|
|
logger.Infof("Starting multicast %s", "")
|
2023-06-06 21:11:49 +00:00
|
|
|
m.multicast, err = multicast.New(m.core, m.logger, options...)
|
2022-09-03 10:42:05 +00:00
|
|
|
if err != nil {
|
2023-10-25 18:59:19 +00:00
|
|
|
logger.Errorln("An error occurred starting multicast:", err)
|
2022-09-03 10:42:05 +00:00
|
|
|
}
|
2022-01-30 19:48:32 +00:00
|
|
|
}
|
2022-09-03 10:42:05 +00:00
|
|
|
|
2022-01-30 19:48:32 +00:00
|
|
|
mtu := m.config.IfMTU
|
2022-08-06 14:05:12 +00:00
|
|
|
m.iprwc = ipv6rwc.NewReadWriteCloser(m.core)
|
2022-01-30 19:48:32 +00:00
|
|
|
if m.iprwc.MaxMTU() < mtu {
|
|
|
|
mtu = m.iprwc.MaxMTU()
|
|
|
|
}
|
|
|
|
m.iprwc.SetMTU(mtu)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Send sends a packet to Yggdrasil. It should be a fully formed
|
|
|
|
// IPv6 packet
|
|
|
|
func (m *Yggdrasil) Send(p []byte) error {
|
|
|
|
if m.iprwc == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
_, _ = m.iprwc.Write(p)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-11-01 12:10:50 +00:00
|
|
|
// Send sends a packet from given buffer to Yggdrasil. From first byte up to length.
|
|
|
|
func (m *Yggdrasil) SendBuffer(p []byte, length int) error {
|
|
|
|
if m.iprwc == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if len(p) < length {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
_, _ = m.iprwc.Write(p[:length])
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-01-30 19:48:32 +00:00
|
|
|
// Recv waits for and reads a packet coming from Yggdrasil. It
|
|
|
|
// will be a fully formed IPv6 packet
|
|
|
|
func (m *Yggdrasil) Recv() ([]byte, error) {
|
|
|
|
if m.iprwc == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
var buf [65535]byte
|
|
|
|
n, _ := m.iprwc.Read(buf[:])
|
|
|
|
return buf[:n], nil
|
|
|
|
}
|
|
|
|
|
2022-11-01 17:42:52 +00:00
|
|
|
// Recv waits for and reads a packet coming from Yggdrasil to given buffer, returning size of packet
|
2022-11-01 12:10:50 +00:00
|
|
|
func (m *Yggdrasil) RecvBuffer(buf []byte) (int, error) {
|
|
|
|
if m.iprwc == nil {
|
|
|
|
return 0, nil
|
|
|
|
}
|
|
|
|
n, _ := m.iprwc.Read(buf)
|
|
|
|
return n, nil
|
|
|
|
}
|
|
|
|
|
2022-01-30 19:48:32 +00:00
|
|
|
// Stop the mobile Yggdrasil instance
|
|
|
|
func (m *Yggdrasil) Stop() error {
|
|
|
|
logger := log.New(m.log, "", 0)
|
|
|
|
logger.EnableLevel("info")
|
2023-10-25 18:59:19 +00:00
|
|
|
logger.Infof("Stopping the mobile Yggdrasil instance %s", "")
|
|
|
|
if m.multicast != nil {
|
2023-10-28 10:26:43 +00:00
|
|
|
logger.Infof("Stopping multicast %s", "")
|
|
|
|
if err := m.multicast.Stop(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2022-01-30 19:48:32 +00:00
|
|
|
}
|
2023-10-25 18:59:19 +00:00
|
|
|
logger.Infof("Stopping TUN device %s", "")
|
2023-06-06 21:11:49 +00:00
|
|
|
if m.tun != nil {
|
|
|
|
if err := m.tun.Stop(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2023-10-25 18:59:19 +00:00
|
|
|
logger.Infof("Stopping Yggdrasil core %s", "")
|
2022-01-30 19:48:32 +00:00
|
|
|
m.core.Stop()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-11-12 11:30:03 +00:00
|
|
|
// Retry resets the peer connection timer and tries to dial them immediately.
|
|
|
|
func (m *Yggdrasil) RetryPeersNow() {
|
|
|
|
m.core.RetryPeersNow()
|
|
|
|
}
|
|
|
|
|
2022-01-30 19:48:32 +00:00
|
|
|
// GenerateConfigJSON generates mobile-friendly configuration in JSON format
|
|
|
|
func GenerateConfigJSON() []byte {
|
2023-04-06 20:45:49 +00:00
|
|
|
nc := config.GenerateConfig()
|
2022-01-30 19:48:32 +00:00
|
|
|
nc.IfName = "none"
|
|
|
|
if json, err := json.Marshal(nc); err == nil {
|
|
|
|
return json
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetAddressString gets the node's IPv6 address
|
|
|
|
func (m *Yggdrasil) GetAddressString() string {
|
|
|
|
ip := m.core.Address()
|
|
|
|
return ip.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetSubnetString gets the node's IPv6 subnet in CIDR notation
|
|
|
|
func (m *Yggdrasil) GetSubnetString() string {
|
|
|
|
subnet := m.core.Subnet()
|
|
|
|
return subnet.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetPublicKeyString gets the node's public key in hex form
|
|
|
|
func (m *Yggdrasil) GetPublicKeyString() string {
|
|
|
|
return hex.EncodeToString(m.core.GetSelf().Key)
|
|
|
|
}
|
|
|
|
|
2023-05-14 09:16:33 +00:00
|
|
|
// GetRoutingEntries gets the number of entries in the routing table
|
|
|
|
func (m *Yggdrasil) GetRoutingEntries() int {
|
|
|
|
return int(m.core.GetSelf().RoutingEntries)
|
2022-01-30 19:48:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Yggdrasil) GetPeersJSON() (result string) {
|
|
|
|
peers := []struct {
|
2022-08-06 14:05:12 +00:00
|
|
|
core.PeerInfo
|
2022-01-30 19:48:32 +00:00
|
|
|
IP string
|
|
|
|
}{}
|
|
|
|
for _, v := range m.core.GetPeers() {
|
2023-10-18 21:38:10 +00:00
|
|
|
var ip string
|
|
|
|
if v.Key != nil {
|
|
|
|
a := address.AddrForKey(v.Key)
|
|
|
|
ip = net.IP(a[:]).String()
|
|
|
|
}
|
2022-01-30 19:48:32 +00:00
|
|
|
peers = append(peers, struct {
|
2022-08-06 14:05:12 +00:00
|
|
|
core.PeerInfo
|
2022-01-30 19:48:32 +00:00
|
|
|
IP string
|
|
|
|
}{
|
2022-08-06 14:05:12 +00:00
|
|
|
PeerInfo: v,
|
|
|
|
IP: ip,
|
2022-01-30 19:48:32 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
if res, err := json.Marshal(peers); err == nil {
|
|
|
|
return string(res)
|
|
|
|
} else {
|
|
|
|
return "{}"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-14 09:16:33 +00:00
|
|
|
func (m *Yggdrasil) GetPathsJSON() (result string) {
|
|
|
|
if res, err := json.Marshal(m.core.GetPaths()); err == nil {
|
|
|
|
return string(res)
|
|
|
|
} else {
|
|
|
|
return "{}"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Yggdrasil) GetTreeJSON() (result string) {
|
|
|
|
if res, err := json.Marshal(m.core.GetTree()); err == nil {
|
2022-01-30 19:48:32 +00:00
|
|
|
return string(res)
|
|
|
|
} else {
|
|
|
|
return "{}"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetMTU returns the configured node MTU. This must be called AFTER Start.
|
|
|
|
func (m *Yggdrasil) GetMTU() int {
|
|
|
|
return int(m.core.MTU())
|
|
|
|
}
|
|
|
|
|
|
|
|
func GetVersion() string {
|
|
|
|
return version.BuildVersion()
|
|
|
|
}
|