mirror of
https://github.com/tobychui/zoraxy.git
synced 2025-06-03 06:07:20 +02:00
Removed GAN
- Removed UI and module of GAN - this feature is moved to plugin
This commit is contained in:
parent
549e492ffd
commit
75c351e7e2
19
src/api.go
19
src/api.go
@ -144,24 +144,6 @@ func RegisterStatisticalAPIs(authRouter *auth.RouterDef) {
|
|||||||
authRouter.HandleFunc("/api/utm/list", HandleUptimeMonitorListing)
|
authRouter.HandleFunc("/api/utm/list", HandleUptimeMonitorListing)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register the APIs for Global Area Network management functions, Will be moving to plugin soon
|
|
||||||
func RegisterGANAPIs(authRouter *auth.RouterDef) {
|
|
||||||
authRouter.HandleFunc("/api/gan/network/info", ganManager.HandleGetNodeID)
|
|
||||||
authRouter.HandleFunc("/api/gan/network/add", ganManager.HandleAddNetwork)
|
|
||||||
authRouter.HandleFunc("/api/gan/network/remove", ganManager.HandleRemoveNetwork)
|
|
||||||
authRouter.HandleFunc("/api/gan/network/list", ganManager.HandleListNetwork)
|
|
||||||
authRouter.HandleFunc("/api/gan/network/name", ganManager.HandleNetworkNaming)
|
|
||||||
//authRouter.HandleFunc("/api/gan/network/detail", ganManager.HandleNetworkDetails)
|
|
||||||
authRouter.HandleFunc("/api/gan/network/setRange", ganManager.HandleSetRanges)
|
|
||||||
authRouter.HandleFunc("/api/gan/network/join", ganManager.HandleServerJoinNetwork)
|
|
||||||
authRouter.HandleFunc("/api/gan/network/leave", ganManager.HandleServerLeaveNetwork)
|
|
||||||
authRouter.HandleFunc("/api/gan/members/list", ganManager.HandleMemberList)
|
|
||||||
authRouter.HandleFunc("/api/gan/members/ip", ganManager.HandleMemberIP)
|
|
||||||
authRouter.HandleFunc("/api/gan/members/name", ganManager.HandleMemberNaming)
|
|
||||||
authRouter.HandleFunc("/api/gan/members/authorize", ganManager.HandleMemberAuthorization)
|
|
||||||
authRouter.HandleFunc("/api/gan/members/delete", ganManager.HandleMemberDelete)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register the APIs for Stream (TCP / UDP) Proxy management functions
|
// Register the APIs for Stream (TCP / UDP) Proxy management functions
|
||||||
func RegisterStreamProxyAPIs(authRouter *auth.RouterDef) {
|
func RegisterStreamProxyAPIs(authRouter *auth.RouterDef) {
|
||||||
authRouter.HandleFunc("/api/streamprox/config/add", streamProxyManager.HandleAddProxyConfig)
|
authRouter.HandleFunc("/api/streamprox/config/add", streamProxyManager.HandleAddProxyConfig)
|
||||||
@ -344,7 +326,6 @@ func initAPIs(targetMux *http.ServeMux) {
|
|||||||
RegisterAccessRuleAPIs(authRouter)
|
RegisterAccessRuleAPIs(authRouter)
|
||||||
RegisterPathRuleAPIs(authRouter)
|
RegisterPathRuleAPIs(authRouter)
|
||||||
RegisterStatisticalAPIs(authRouter)
|
RegisterStatisticalAPIs(authRouter)
|
||||||
RegisterGANAPIs(authRouter)
|
|
||||||
RegisterStreamProxyAPIs(authRouter)
|
RegisterStreamProxyAPIs(authRouter)
|
||||||
RegisterMDNSAPIs(authRouter)
|
RegisterMDNSAPIs(authRouter)
|
||||||
RegisterNetworkUtilsAPIs(authRouter)
|
RegisterNetworkUtilsAPIs(authRouter)
|
||||||
|
@ -1,80 +0,0 @@
|
|||||||
package ganserv
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TryLoadorAskUserForAuthkey() (string, error) {
|
|
||||||
//Check for zt auth token
|
|
||||||
value, exists := os.LookupEnv("ZT_AUTH")
|
|
||||||
if !exists {
|
|
||||||
log.Println("Environment variable ZT_AUTH not defined. Trying to load authtoken from file.")
|
|
||||||
} else {
|
|
||||||
return value, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
authKey := ""
|
|
||||||
if runtime.GOOS == "windows" {
|
|
||||||
if isAdmin() {
|
|
||||||
//Read the secret file directly
|
|
||||||
b, err := os.ReadFile("C:\\ProgramData\\ZeroTier\\One\\authtoken.secret")
|
|
||||||
if err == nil {
|
|
||||||
log.Println("Zerotier authkey loaded")
|
|
||||||
authKey = string(b)
|
|
||||||
} else {
|
|
||||||
log.Println("Unable to read authkey at C:\\ProgramData\\ZeroTier\\One\\authtoken.secret: ", err.Error())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
//Elavate the permission to admin
|
|
||||||
ak, err := readAuthTokenAsAdmin()
|
|
||||||
if err == nil {
|
|
||||||
log.Println("Zerotier authkey loaded")
|
|
||||||
authKey = ak
|
|
||||||
} else {
|
|
||||||
log.Println("Unable to read authkey at C:\\ProgramData\\ZeroTier\\One\\authtoken.secret: ", err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if runtime.GOOS == "linux" {
|
|
||||||
if isAdmin() {
|
|
||||||
//Try to read from source using sudo
|
|
||||||
ak, err := readAuthTokenAsAdmin()
|
|
||||||
if err == nil {
|
|
||||||
log.Println("Zerotier authkey loaded")
|
|
||||||
authKey = strings.TrimSpace(ak)
|
|
||||||
} else {
|
|
||||||
log.Println("Unable to read authkey at /var/lib/zerotier-one/authtoken.secret: ", err.Error())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
//Try read from source
|
|
||||||
b, err := os.ReadFile("/var/lib/zerotier-one/authtoken.secret")
|
|
||||||
if err == nil {
|
|
||||||
log.Println("Zerotier authkey loaded")
|
|
||||||
authKey = string(b)
|
|
||||||
} else {
|
|
||||||
log.Println("Unable to read authkey at /var/lib/zerotier-one/authtoken.secret: ", err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if runtime.GOOS == "darwin" {
|
|
||||||
b, err := os.ReadFile("/Library/Application Support/ZeroTier/One/authtoken.secret")
|
|
||||||
if err == nil {
|
|
||||||
log.Println("Zerotier authkey loaded")
|
|
||||||
authKey = string(b)
|
|
||||||
} else {
|
|
||||||
log.Println("Unable to read authkey at /Library/Application Support/ZeroTier/One/authtoken.secret ", err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
authKey = strings.TrimSpace(authKey)
|
|
||||||
|
|
||||||
if authKey == "" {
|
|
||||||
return "", errors.New("Unable to load authkey from file")
|
|
||||||
}
|
|
||||||
|
|
||||||
return authKey, nil
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
//go:build linux
|
|
||||||
// +build linux
|
|
||||||
|
|
||||||
package ganserv
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"os/user"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"imuslab.com/zoraxy/mod/utils"
|
|
||||||
)
|
|
||||||
|
|
||||||
func readAuthTokenAsAdmin() (string, error) {
|
|
||||||
if utils.FileExists("./conf/authtoken.secret") {
|
|
||||||
authKey, err := os.ReadFile("./conf/authtoken.secret")
|
|
||||||
if err == nil {
|
|
||||||
return strings.TrimSpace(string(authKey)), nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd := exec.Command("sudo", "cat", "/var/lib/zerotier-one/authtoken.secret")
|
|
||||||
output, err := cmd.Output()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return string(output), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func isAdmin() bool {
|
|
||||||
currentUser, err := user.Current()
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return currentUser.Username == "root"
|
|
||||||
}
|
|
@ -1,73 +0,0 @@
|
|||||||
//go:build windows
|
|
||||||
// +build windows
|
|
||||||
|
|
||||||
package ganserv
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"syscall"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"golang.org/x/sys/windows"
|
|
||||||
"imuslab.com/zoraxy/mod/utils"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Use admin permission to read auth token on Windows
|
|
||||||
func readAuthTokenAsAdmin() (string, error) {
|
|
||||||
//Check if the previous startup already extracted the authkey
|
|
||||||
if utils.FileExists("./conf/authtoken.secret") {
|
|
||||||
authKey, err := os.ReadFile("./conf/authtoken.secret")
|
|
||||||
if err == nil {
|
|
||||||
return strings.TrimSpace(string(authKey)), nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
verb := "runas"
|
|
||||||
exe := "cmd.exe"
|
|
||||||
cwd, _ := os.Getwd()
|
|
||||||
|
|
||||||
output, _ := filepath.Abs(filepath.Join("./conf/", "authtoken.secret"))
|
|
||||||
os.WriteFile(output, []byte(""), 0775)
|
|
||||||
args := fmt.Sprintf("/C type \"C:\\ProgramData\\ZeroTier\\One\\authtoken.secret\" > \"" + output + "\"")
|
|
||||||
|
|
||||||
verbPtr, _ := syscall.UTF16PtrFromString(verb)
|
|
||||||
exePtr, _ := syscall.UTF16PtrFromString(exe)
|
|
||||||
cwdPtr, _ := syscall.UTF16PtrFromString(cwd)
|
|
||||||
argPtr, _ := syscall.UTF16PtrFromString(args)
|
|
||||||
|
|
||||||
var showCmd int32 = 1 //SW_NORMAL
|
|
||||||
|
|
||||||
err := windows.ShellExecute(0, verbPtr, exePtr, argPtr, cwdPtr, showCmd)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println("Please click agree to allow access to ZeroTier authtoken from ProgramData")
|
|
||||||
retry := 0
|
|
||||||
time.Sleep(3 * time.Second)
|
|
||||||
for !utils.FileExists("./conf/authtoken.secret") && retry < 10 {
|
|
||||||
time.Sleep(3 * time.Second)
|
|
||||||
log.Println("Waiting for ZeroTier authtoken extraction...")
|
|
||||||
retry++
|
|
||||||
}
|
|
||||||
|
|
||||||
authKey, err := os.ReadFile("./conf/authtoken.secret")
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings.TrimSpace(string(authKey)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if admin on Windows
|
|
||||||
func isAdmin() bool {
|
|
||||||
_, err := os.Open("\\\\.\\PHYSICALDRIVE0")
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
@ -1,130 +0,0 @@
|
|||||||
package ganserv
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"imuslab.com/zoraxy/mod/database"
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
Global Area Network
|
|
||||||
Server side implementation
|
|
||||||
|
|
||||||
This module do a few things to help manage
|
|
||||||
the system GANs
|
|
||||||
|
|
||||||
- Provide DHCP assign to client
|
|
||||||
- Provide a list of connected nodes in the same VLAN
|
|
||||||
- Provide proxy of packet if the target VLAN is online but not reachable
|
|
||||||
|
|
||||||
Also provide HTTP Handler functions for management
|
|
||||||
- Create Network
|
|
||||||
- Update Network Properties (Name / Desc)
|
|
||||||
- Delete Network
|
|
||||||
|
|
||||||
- Authorize Node
|
|
||||||
- Deauthorize Node
|
|
||||||
- Set / Get Network Prefered Subnet Mask
|
|
||||||
- Handle Node ping
|
|
||||||
*/
|
|
||||||
|
|
||||||
type Node struct {
|
|
||||||
Auth bool //If the node is authorized in this network
|
|
||||||
ClientID string //The client ID
|
|
||||||
MAC string //The tap MAC this client is using
|
|
||||||
Name string //Name of the client in this network
|
|
||||||
Description string //Description text
|
|
||||||
ManagedIP net.IP //The IP address assigned by this network
|
|
||||||
LastSeen int64 //Last time it is seen from this host
|
|
||||||
ClientVersion string //Client application version
|
|
||||||
PublicIP net.IP //Public IP address as seen from this host
|
|
||||||
}
|
|
||||||
|
|
||||||
type Network struct {
|
|
||||||
UID string //UUID of the network, must be a 16 char random ASCII string
|
|
||||||
Name string //Name of the network, ASCII only
|
|
||||||
Description string //Description of the network
|
|
||||||
CIDR string //The subnet masked use by this network
|
|
||||||
Nodes []*Node //The nodes currently attached in this network
|
|
||||||
}
|
|
||||||
|
|
||||||
type NetworkManagerOptions struct {
|
|
||||||
Database *database.Database
|
|
||||||
AuthToken string
|
|
||||||
ApiPort int
|
|
||||||
}
|
|
||||||
|
|
||||||
type NetworkMetaData struct {
|
|
||||||
Desc string
|
|
||||||
}
|
|
||||||
|
|
||||||
type MemberMetaData struct {
|
|
||||||
Name string
|
|
||||||
}
|
|
||||||
|
|
||||||
type NetworkManager struct {
|
|
||||||
authToken string
|
|
||||||
apiPort int
|
|
||||||
ControllerID string
|
|
||||||
option *NetworkManagerOptions
|
|
||||||
networksMetadata map[string]NetworkMetaData
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a new GAN manager
|
|
||||||
func NewNetworkManager(option *NetworkManagerOptions) *NetworkManager {
|
|
||||||
option.Database.NewTable("ganserv")
|
|
||||||
|
|
||||||
//Load network metadata
|
|
||||||
networkMeta := map[string]NetworkMetaData{}
|
|
||||||
if option.Database.KeyExists("ganserv", "networkmeta") {
|
|
||||||
option.Database.Read("ganserv", "networkmeta", &networkMeta)
|
|
||||||
}
|
|
||||||
|
|
||||||
//Start the zerotier instance if not exists
|
|
||||||
|
|
||||||
//Get controller info
|
|
||||||
instanceInfo, err := getControllerInfo(option.AuthToken, option.ApiPort)
|
|
||||||
if err != nil {
|
|
||||||
log.Println("ZeroTier connection failed: ", err.Error())
|
|
||||||
return &NetworkManager{
|
|
||||||
authToken: option.AuthToken,
|
|
||||||
apiPort: option.ApiPort,
|
|
||||||
ControllerID: "",
|
|
||||||
option: option,
|
|
||||||
networksMetadata: networkMeta,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &NetworkManager{
|
|
||||||
authToken: option.AuthToken,
|
|
||||||
apiPort: option.ApiPort,
|
|
||||||
ControllerID: instanceInfo.Address,
|
|
||||||
option: option,
|
|
||||||
networksMetadata: networkMeta,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *NetworkManager) GetNetworkMetaData(netid string) *NetworkMetaData {
|
|
||||||
md, ok := m.networksMetadata[netid]
|
|
||||||
if !ok {
|
|
||||||
return &NetworkMetaData{}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &md
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *NetworkManager) WriteNetworkMetaData(netid string, meta *NetworkMetaData) {
|
|
||||||
m.networksMetadata[netid] = *meta
|
|
||||||
m.option.Database.Write("ganserv", "networkmeta", m.networksMetadata)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *NetworkManager) GetMemberMetaData(netid string, memid string) *MemberMetaData {
|
|
||||||
thisMemberData := MemberMetaData{}
|
|
||||||
m.option.Database.Read("ganserv", "memberdata_"+netid+"_"+memid, &thisMemberData)
|
|
||||||
return &thisMemberData
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *NetworkManager) WriteMemeberMetaData(netid string, memid string, meta *MemberMetaData) {
|
|
||||||
m.option.Database.Write("ganserv", "memberdata_"+netid+"_"+memid, meta)
|
|
||||||
}
|
|
@ -1,504 +0,0 @@
|
|||||||
package ganserv
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"imuslab.com/zoraxy/mod/utils"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (m *NetworkManager) HandleGetNodeID(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if m.ControllerID == "" {
|
|
||||||
//Node id not exists. Check again
|
|
||||||
instanceInfo, err := getControllerInfo(m.option.AuthToken, m.option.ApiPort)
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, "unable to access node id information")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
m.ControllerID = instanceInfo.Address
|
|
||||||
}
|
|
||||||
|
|
||||||
js, _ := json.Marshal(m.ControllerID)
|
|
||||||
utils.SendJSONResponse(w, string(js))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *NetworkManager) HandleAddNetwork(w http.ResponseWriter, r *http.Request) {
|
|
||||||
networkInfo, err := m.createNetwork()
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
//Network created. Assign it the standard network settings
|
|
||||||
err = m.configureNetwork(networkInfo.Nwid, "192.168.192.1", "192.168.192.254", "192.168.192.0/24")
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the new network ID
|
|
||||||
js, _ := json.Marshal(networkInfo.Nwid)
|
|
||||||
utils.SendJSONResponse(w, string(js))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *NetworkManager) HandleRemoveNetwork(w http.ResponseWriter, r *http.Request) {
|
|
||||||
networkID, err := utils.PostPara(r, "id")
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, "invalid or empty network id given")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if !m.networkExists(networkID) {
|
|
||||||
utils.SendErrorResponse(w, "network id not exists")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = m.deleteNetwork(networkID)
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
utils.SendOK(w)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *NetworkManager) HandleListNetwork(w http.ResponseWriter, r *http.Request) {
|
|
||||||
netid, _ := utils.GetPara(r, "netid")
|
|
||||||
if netid != "" {
|
|
||||||
targetNetInfo, err := m.getNetworkInfoById(netid)
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
js, _ := json.Marshal(targetNetInfo)
|
|
||||||
utils.SendJSONResponse(w, string(js))
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// Return the list of networks as JSON
|
|
||||||
networkIds, err := m.listNetworkIds()
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
networkInfos := []*NetworkInfo{}
|
|
||||||
for _, id := range networkIds {
|
|
||||||
thisNetInfo, err := m.getNetworkInfoById(id)
|
|
||||||
if err == nil {
|
|
||||||
networkInfos = append(networkInfos, thisNetInfo)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
js, _ := json.Marshal(networkInfos)
|
|
||||||
utils.SendJSONResponse(w, string(js))
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *NetworkManager) HandleNetworkNaming(w http.ResponseWriter, r *http.Request) {
|
|
||||||
netid, err := utils.PostPara(r, "netid")
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, "network id not given")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if !m.networkExists(netid) {
|
|
||||||
utils.SendErrorResponse(w, "network not eixsts")
|
|
||||||
}
|
|
||||||
|
|
||||||
newName, _ := utils.PostPara(r, "name")
|
|
||||||
newDesc, _ := utils.PostPara(r, "desc")
|
|
||||||
if newName != "" && newDesc != "" {
|
|
||||||
//Strip away html from name and desc
|
|
||||||
re := regexp.MustCompile("<[^>]*>")
|
|
||||||
newName := re.ReplaceAllString(newName, "")
|
|
||||||
newDesc := re.ReplaceAllString(newDesc, "")
|
|
||||||
|
|
||||||
//Set the new network name and desc
|
|
||||||
err = m.setNetworkNameAndDescription(netid, newName, newDesc)
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
utils.SendOK(w)
|
|
||||||
} else {
|
|
||||||
//Get current name and description
|
|
||||||
name, desc, err := m.getNetworkNameAndDescription(netid)
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
js, _ := json.Marshal([]string{name, desc})
|
|
||||||
utils.SendJSONResponse(w, string(js))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *NetworkManager) HandleNetworkDetails(w http.ResponseWriter, r *http.Request) {
|
|
||||||
netid, err := utils.PostPara(r, "netid")
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, "netid not given")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
targetNetwork, err := m.getNetworkInfoById(netid)
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
js, _ := json.Marshal(targetNetwork)
|
|
||||||
utils.SendJSONResponse(w, string(js))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *NetworkManager) HandleSetRanges(w http.ResponseWriter, r *http.Request) {
|
|
||||||
netid, err := utils.PostPara(r, "netid")
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, "netid not given")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
cidr, err := utils.PostPara(r, "cidr")
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, "cidr not given")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ipstart, err := utils.PostPara(r, "ipstart")
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, "ipstart not given")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ipend, err := utils.PostPara(r, "ipend")
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, "ipend not given")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
//Validate the CIDR is real, the ip range is within the CIDR range
|
|
||||||
_, ipnet, err := net.ParseCIDR(cidr)
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, "invalid cidr string given")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
startIP := net.ParseIP(ipstart)
|
|
||||||
endIP := net.ParseIP(ipend)
|
|
||||||
if startIP == nil || endIP == nil {
|
|
||||||
utils.SendErrorResponse(w, "invalid start or end ip given")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
withinRange := ipnet.Contains(startIP) && ipnet.Contains(endIP)
|
|
||||||
if !withinRange {
|
|
||||||
utils.SendErrorResponse(w, "given CIDR did not cover all of the start to end ip range")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = m.configureNetwork(netid, startIP.String(), endIP.String(), strings.TrimSpace(cidr))
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
utils.SendOK(w)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle listing of network members. Set details=true for listing all details
|
|
||||||
func (m *NetworkManager) HandleMemberList(w http.ResponseWriter, r *http.Request) {
|
|
||||||
netid, err := utils.GetPara(r, "netid")
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, "netid is empty")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
details, _ := utils.GetPara(r, "detail")
|
|
||||||
|
|
||||||
memberIds, err := m.getNetworkMembers(netid)
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if details == "" {
|
|
||||||
//Only show client ids
|
|
||||||
js, _ := json.Marshal(memberIds)
|
|
||||||
utils.SendJSONResponse(w, string(js))
|
|
||||||
} else {
|
|
||||||
//Show detail members info
|
|
||||||
detailMemberInfo := []*MemberInfo{}
|
|
||||||
for _, thisMemberId := range memberIds {
|
|
||||||
memInfo, err := m.getNetworkMemberInfo(netid, thisMemberId)
|
|
||||||
if err == nil {
|
|
||||||
detailMemberInfo = append(detailMemberInfo, memInfo)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
js, _ := json.Marshal(detailMemberInfo)
|
|
||||||
utils.SendJSONResponse(w, string(js))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle Authorization of members
|
|
||||||
func (m *NetworkManager) HandleMemberAuthorization(w http.ResponseWriter, r *http.Request) {
|
|
||||||
netid, err := utils.PostPara(r, "netid")
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, "net id not set")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
memberid, err := utils.PostPara(r, "memid")
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, "memid not set")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
//Check if the target memeber exists
|
|
||||||
if !m.memberExistsInNetwork(netid, memberid) {
|
|
||||||
utils.SendErrorResponse(w, "member not exists in given network")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
setAuthorized, err := utils.PostPara(r, "auth")
|
|
||||||
if err != nil || setAuthorized == "" {
|
|
||||||
//Get the member authorization state
|
|
||||||
memberInfo, err := m.getNetworkMemberInfo(netid, memberid)
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
js, _ := json.Marshal(memberInfo.Authorized)
|
|
||||||
utils.SendJSONResponse(w, string(js))
|
|
||||||
} else if setAuthorized == "true" {
|
|
||||||
m.AuthorizeMember(netid, memberid, true)
|
|
||||||
} else if setAuthorized == "false" {
|
|
||||||
m.AuthorizeMember(netid, memberid, false)
|
|
||||||
} else {
|
|
||||||
utils.SendErrorResponse(w, "unknown operation state: "+setAuthorized)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle Delete or Add IP for a member in a network
|
|
||||||
func (m *NetworkManager) HandleMemberIP(w http.ResponseWriter, r *http.Request) {
|
|
||||||
netid, err := utils.PostPara(r, "netid")
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, "net id not set")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
memberid, err := utils.PostPara(r, "memid")
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, "memid not set")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
opr, err := utils.PostPara(r, "opr")
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, "opr not defined")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
targetip, _ := utils.PostPara(r, "ip")
|
|
||||||
|
|
||||||
memberInfo, err := m.getNetworkMemberInfo(netid, memberid)
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if opr == "add" {
|
|
||||||
if targetip == "" {
|
|
||||||
utils.SendErrorResponse(w, "ip not set")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if !isValidIPAddr(targetip) {
|
|
||||||
utils.SendErrorResponse(w, "ip address not valid")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
newIpList := append(memberInfo.IPAssignments, targetip)
|
|
||||||
err = m.setAssignedIps(netid, memberid, newIpList)
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
utils.SendOK(w)
|
|
||||||
|
|
||||||
} else if opr == "del" {
|
|
||||||
if targetip == "" {
|
|
||||||
utils.SendErrorResponse(w, "ip not set")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
//Delete user ip from the list
|
|
||||||
newIpList := []string{}
|
|
||||||
for _, thisIp := range memberInfo.IPAssignments {
|
|
||||||
if thisIp != targetip {
|
|
||||||
newIpList = append(newIpList, thisIp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err = m.setAssignedIps(netid, memberid, newIpList)
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
utils.SendOK(w)
|
|
||||||
} else if opr == "get" {
|
|
||||||
js, _ := json.Marshal(memberInfo.IPAssignments)
|
|
||||||
utils.SendJSONResponse(w, string(js))
|
|
||||||
} else {
|
|
||||||
utils.SendErrorResponse(w, "unsupported opr type: "+opr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle naming for members
|
|
||||||
func (m *NetworkManager) HandleMemberNaming(w http.ResponseWriter, r *http.Request) {
|
|
||||||
netid, err := utils.PostPara(r, "netid")
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, "net id not set")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
memberid, err := utils.PostPara(r, "memid")
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, "memid not set")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if !m.memberExistsInNetwork(netid, memberid) {
|
|
||||||
utils.SendErrorResponse(w, "target member not exists in given network")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
//Read memeber data
|
|
||||||
targetMemberData := m.GetMemberMetaData(netid, memberid)
|
|
||||||
|
|
||||||
newname, err := utils.PostPara(r, "name")
|
|
||||||
if err != nil {
|
|
||||||
//Send over the member data
|
|
||||||
js, _ := json.Marshal(targetMemberData)
|
|
||||||
utils.SendJSONResponse(w, string(js))
|
|
||||||
} else {
|
|
||||||
//Write member data
|
|
||||||
targetMemberData.Name = newname
|
|
||||||
m.WriteMemeberMetaData(netid, memberid, targetMemberData)
|
|
||||||
utils.SendOK(w)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle delete of a given memver
|
|
||||||
func (m *NetworkManager) HandleMemberDelete(w http.ResponseWriter, r *http.Request) {
|
|
||||||
netid, err := utils.PostPara(r, "netid")
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, "net id not set")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
memberid, err := utils.PostPara(r, "memid")
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, "memid not set")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
//Check if that member is authorized.
|
|
||||||
memberInfo, err := m.getNetworkMemberInfo(netid, memberid)
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, "member not exists in given GANet")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if memberInfo.Authorized {
|
|
||||||
//Deauthorized this member before deleting
|
|
||||||
m.AuthorizeMember(netid, memberid, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
//Remove the memeber
|
|
||||||
err = m.deleteMember(netid, memberid)
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
utils.SendOK(w)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if a given network id is a network hosted on this zoraxy node
|
|
||||||
func (m *NetworkManager) IsLocalGAN(networkId string) bool {
|
|
||||||
networks, err := m.listNetworkIds()
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, network := range networks {
|
|
||||||
if network == networkId {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle server instant joining a given network
|
|
||||||
func (m *NetworkManager) HandleServerJoinNetwork(w http.ResponseWriter, r *http.Request) {
|
|
||||||
netid, err := utils.PostPara(r, "netid")
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, "net id not set")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
//Check if the target network is a network hosted on this server
|
|
||||||
if !m.IsLocalGAN(netid) {
|
|
||||||
utils.SendErrorResponse(w, "given network is not a GAN hosted on this node")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if m.memberExistsInNetwork(netid, m.ControllerID) {
|
|
||||||
utils.SendErrorResponse(w, "controller already inside network")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
//Join the network
|
|
||||||
err = m.joinNetwork(netid)
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
utils.SendOK(w)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle server instant leaving a given network
|
|
||||||
func (m *NetworkManager) HandleServerLeaveNetwork(w http.ResponseWriter, r *http.Request) {
|
|
||||||
netid, err := utils.PostPara(r, "netid")
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, "net id not set")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
//Check if the target network is a network hosted on this server
|
|
||||||
if !m.IsLocalGAN(netid) {
|
|
||||||
utils.SendErrorResponse(w, "given network is not a GAN hosted on this node")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
//Leave the network
|
|
||||||
err = m.leaveNetwork(netid)
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
//Remove it from target network if it is authorized
|
|
||||||
err = m.deleteMember(netid, m.ControllerID)
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
utils.SendOK(w)
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
package ganserv
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"math/rand"
|
|
||||||
"net"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
//Get a random free IP from the pool
|
|
||||||
func (n *Network) GetRandomFreeIP() (net.IP, error) {
|
|
||||||
// Get all IP addresses in the subnet
|
|
||||||
ips, err := GetAllAddressFromCIDR(n.CIDR)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter out used IPs
|
|
||||||
usedIPs := make(map[string]bool)
|
|
||||||
for _, node := range n.Nodes {
|
|
||||||
usedIPs[node.ManagedIP.String()] = true
|
|
||||||
}
|
|
||||||
availableIPs := []string{}
|
|
||||||
for _, ip := range ips {
|
|
||||||
if !usedIPs[ip] {
|
|
||||||
availableIPs = append(availableIPs, ip)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Randomly choose an available IP
|
|
||||||
if len(availableIPs) == 0 {
|
|
||||||
return nil, fmt.Errorf("no available IP")
|
|
||||||
}
|
|
||||||
rand.Seed(time.Now().UnixNano())
|
|
||||||
randIndex := rand.Intn(len(availableIPs))
|
|
||||||
pickedFreeIP := availableIPs[randIndex]
|
|
||||||
|
|
||||||
return net.ParseIP(pickedFreeIP), nil
|
|
||||||
}
|
|
@ -1,55 +0,0 @@
|
|||||||
package ganserv_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"strconv"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"imuslab.com/zoraxy/mod/ganserv"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestGetRandomFreeIP(t *testing.T) {
|
|
||||||
n := ganserv.Network{
|
|
||||||
CIDR: "172.16.0.0/12",
|
|
||||||
Nodes: []*ganserv.Node{
|
|
||||||
{
|
|
||||||
Name: "nodeC1",
|
|
||||||
ManagedIP: net.ParseIP("172.16.1.142"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "nodeC2",
|
|
||||||
ManagedIP: net.ParseIP("172.16.5.174"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call the function for 10 times
|
|
||||||
for i := 0; i < 10; i++ {
|
|
||||||
freeIP, err := n.GetRandomFreeIP()
|
|
||||||
fmt.Println("["+strconv.Itoa(i)+"] Free IP address assigned: ", freeIP)
|
|
||||||
|
|
||||||
// Assert that no error occurred
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Unexpected error: %s", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assert that the returned IP is a valid IPv4 address
|
|
||||||
if freeIP.To4() == nil {
|
|
||||||
t.Errorf("Invalid IP address format: %s", freeIP.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assert that the returned IP is not already used by a node
|
|
||||||
for _, node := range n.Nodes {
|
|
||||||
if freeIP.Equal(node.ManagedIP) {
|
|
||||||
t.Errorf("Returned IP is already in use: %s", freeIP.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
n.Nodes = append(n.Nodes, &ganserv.Node{
|
|
||||||
Name: "NodeT" + strconv.Itoa(i),
|
|
||||||
ManagedIP: freeIP,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,55 +0,0 @@
|
|||||||
package ganserv
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
)
|
|
||||||
|
|
||||||
//Generate all ip address from a CIDR
|
|
||||||
func GetAllAddressFromCIDR(cidr string) ([]string, error) {
|
|
||||||
ip, ipnet, err := net.ParseCIDR(cidr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var ips []string
|
|
||||||
for ip := ip.Mask(ipnet.Mask); ipnet.Contains(ip); inc(ip) {
|
|
||||||
ips = append(ips, ip.String())
|
|
||||||
}
|
|
||||||
// remove network address and broadcast address
|
|
||||||
return ips[1 : len(ips)-1], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func inc(ip net.IP) {
|
|
||||||
for j := len(ip) - 1; j >= 0; j-- {
|
|
||||||
ip[j]++
|
|
||||||
if ip[j] > 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func isValidIPAddr(ipAddr string) bool {
|
|
||||||
ip := net.ParseIP(ipAddr)
|
|
||||||
if ip == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func ipWithinCIDR(ipAddr string, cidr string) bool {
|
|
||||||
// Parse the CIDR string
|
|
||||||
_, ipNet, err := net.ParseCIDR(cidr)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse the IP address
|
|
||||||
ip := net.ParseIP(ipAddr)
|
|
||||||
if ip == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the IP address is in the CIDR range
|
|
||||||
return ipNet.Contains(ip)
|
|
||||||
}
|
|
@ -1,669 +0,0 @@
|
|||||||
package ganserv
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
zerotier.go
|
|
||||||
|
|
||||||
This hold the functions that required to communicate with
|
|
||||||
a zerotier instance
|
|
||||||
|
|
||||||
See more on
|
|
||||||
https://docs.zerotier.com/self-hosting/network-controllers/
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
type NodeInfo struct {
|
|
||||||
Address string `json:"address"`
|
|
||||||
Clock int64 `json:"clock"`
|
|
||||||
Config struct {
|
|
||||||
Settings struct {
|
|
||||||
AllowTCPFallbackRelay bool `json:"allowTcpFallbackRelay,omitempty"`
|
|
||||||
ForceTCPRelay bool `json:"forceTcpRelay,omitempty"`
|
|
||||||
HomeDir string `json:"homeDir,omitempty"`
|
|
||||||
ListeningOn []string `json:"listeningOn,omitempty"`
|
|
||||||
PortMappingEnabled bool `json:"portMappingEnabled,omitempty"`
|
|
||||||
PrimaryPort int `json:"primaryPort,omitempty"`
|
|
||||||
SecondaryPort int `json:"secondaryPort,omitempty"`
|
|
||||||
SoftwareUpdate string `json:"softwareUpdate,omitempty"`
|
|
||||||
SoftwareUpdateChannel string `json:"softwareUpdateChannel,omitempty"`
|
|
||||||
SurfaceAddresses []string `json:"surfaceAddresses,omitempty"`
|
|
||||||
TertiaryPort int `json:"tertiaryPort,omitempty"`
|
|
||||||
} `json:"settings"`
|
|
||||||
} `json:"config"`
|
|
||||||
Online bool `json:"online"`
|
|
||||||
PlanetWorldID int `json:"planetWorldId"`
|
|
||||||
PlanetWorldTimestamp int64 `json:"planetWorldTimestamp"`
|
|
||||||
PublicIdentity string `json:"publicIdentity"`
|
|
||||||
TCPFallbackActive bool `json:"tcpFallbackActive"`
|
|
||||||
Version string `json:"version"`
|
|
||||||
VersionBuild int `json:"versionBuild"`
|
|
||||||
VersionMajor int `json:"versionMajor"`
|
|
||||||
VersionMinor int `json:"versionMinor"`
|
|
||||||
VersionRev int `json:"versionRev"`
|
|
||||||
}
|
|
||||||
type ErrResp struct {
|
|
||||||
Message string `json:"message"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type NetworkInfo struct {
|
|
||||||
AuthTokens []interface{} `json:"authTokens"`
|
|
||||||
AuthorizationEndpoint string `json:"authorizationEndpoint"`
|
|
||||||
Capabilities []interface{} `json:"capabilities"`
|
|
||||||
ClientID string `json:"clientId"`
|
|
||||||
CreationTime int64 `json:"creationTime"`
|
|
||||||
DNS []interface{} `json:"dns"`
|
|
||||||
EnableBroadcast bool `json:"enableBroadcast"`
|
|
||||||
ID string `json:"id"`
|
|
||||||
IPAssignmentPools []interface{} `json:"ipAssignmentPools"`
|
|
||||||
Mtu int `json:"mtu"`
|
|
||||||
MulticastLimit int `json:"multicastLimit"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Nwid string `json:"nwid"`
|
|
||||||
Objtype string `json:"objtype"`
|
|
||||||
Private bool `json:"private"`
|
|
||||||
RemoteTraceLevel int `json:"remoteTraceLevel"`
|
|
||||||
RemoteTraceTarget interface{} `json:"remoteTraceTarget"`
|
|
||||||
Revision int `json:"revision"`
|
|
||||||
Routes []interface{} `json:"routes"`
|
|
||||||
Rules []struct {
|
|
||||||
Not bool `json:"not"`
|
|
||||||
Or bool `json:"or"`
|
|
||||||
Type string `json:"type"`
|
|
||||||
} `json:"rules"`
|
|
||||||
RulesSource string `json:"rulesSource"`
|
|
||||||
SsoEnabled bool `json:"ssoEnabled"`
|
|
||||||
Tags []interface{} `json:"tags"`
|
|
||||||
V4AssignMode struct {
|
|
||||||
Zt bool `json:"zt"`
|
|
||||||
} `json:"v4AssignMode"`
|
|
||||||
V6AssignMode struct {
|
|
||||||
SixPlane bool `json:"6plane"`
|
|
||||||
Rfc4193 bool `json:"rfc4193"`
|
|
||||||
Zt bool `json:"zt"`
|
|
||||||
} `json:"v6AssignMode"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type MemberInfo struct {
|
|
||||||
ActiveBridge bool `json:"activeBridge"`
|
|
||||||
Address string `json:"address"`
|
|
||||||
AuthenticationExpiryTime int `json:"authenticationExpiryTime"`
|
|
||||||
Authorized bool `json:"authorized"`
|
|
||||||
Capabilities []interface{} `json:"capabilities"`
|
|
||||||
CreationTime int64 `json:"creationTime"`
|
|
||||||
ID string `json:"id"`
|
|
||||||
Identity string `json:"identity"`
|
|
||||||
IPAssignments []string `json:"ipAssignments"`
|
|
||||||
LastAuthorizedCredential interface{} `json:"lastAuthorizedCredential"`
|
|
||||||
LastAuthorizedCredentialType string `json:"lastAuthorizedCredentialType"`
|
|
||||||
LastAuthorizedTime int `json:"lastAuthorizedTime"`
|
|
||||||
LastDeauthorizedTime int `json:"lastDeauthorizedTime"`
|
|
||||||
NoAutoAssignIps bool `json:"noAutoAssignIps"`
|
|
||||||
Nwid string `json:"nwid"`
|
|
||||||
Objtype string `json:"objtype"`
|
|
||||||
RemoteTraceLevel int `json:"remoteTraceLevel"`
|
|
||||||
RemoteTraceTarget interface{} `json:"remoteTraceTarget"`
|
|
||||||
Revision int `json:"revision"`
|
|
||||||
SsoExempt bool `json:"ssoExempt"`
|
|
||||||
Tags []interface{} `json:"tags"`
|
|
||||||
VMajor int `json:"vMajor"`
|
|
||||||
VMinor int `json:"vMinor"`
|
|
||||||
VProto int `json:"vProto"`
|
|
||||||
VRev int `json:"vRev"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the zerotier node info from local service
|
|
||||||
func getControllerInfo(token string, apiPort int) (*NodeInfo, error) {
|
|
||||||
url := "http://localhost:" + strconv.Itoa(apiPort) + "/status"
|
|
||||||
|
|
||||||
req, err := http.NewRequest("GET", url, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
req.Header.Set("X-ZT1-AUTH", token)
|
|
||||||
client := &http.Client{}
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
//Read from zerotier service instance
|
|
||||||
|
|
||||||
defer resp.Body.Close()
|
|
||||||
payload, err := io.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
//Parse the payload into struct
|
|
||||||
thisInstanceInfo := NodeInfo{}
|
|
||||||
err = json.Unmarshal(payload, &thisInstanceInfo)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &thisInstanceInfo, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Network Functions
|
|
||||||
*/
|
|
||||||
//Create a zerotier network
|
|
||||||
func (m *NetworkManager) createNetwork() (*NetworkInfo, error) {
|
|
||||||
url := fmt.Sprintf("http://localhost:"+strconv.Itoa(m.apiPort)+"/controller/network/%s______", m.ControllerID)
|
|
||||||
|
|
||||||
data := []byte(`{}`)
|
|
||||||
req, err := http.NewRequest("POST", url, bytes.NewBuffer(data))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
req.Header.Set("X-ZT1-AUTH", m.authToken)
|
|
||||||
client := &http.Client{}
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
payload, err := io.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
networkInfo := NetworkInfo{}
|
|
||||||
err = json.Unmarshal(payload, &networkInfo)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &networkInfo, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// List network details
|
|
||||||
func (m *NetworkManager) getNetworkInfoById(networkId string) (*NetworkInfo, error) {
|
|
||||||
req, err := http.NewRequest("GET", os.ExpandEnv("http://localhost:"+strconv.Itoa(m.apiPort)+"/controller/network/"+networkId+"/"), nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
req.Header.Set("X-Zt1-Auth", m.authToken)
|
|
||||||
|
|
||||||
resp, err := http.DefaultClient.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
if resp.StatusCode != 200 {
|
|
||||||
return nil, errors.New("network error. Status code: " + strconv.Itoa(resp.StatusCode))
|
|
||||||
}
|
|
||||||
|
|
||||||
thisNetworkInfo := NetworkInfo{}
|
|
||||||
payload, err := io.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = json.Unmarshal(payload, &thisNetworkInfo)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &thisNetworkInfo, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *NetworkManager) setNetworkInfoByID(networkId string, newNetworkInfo *NetworkInfo) error {
|
|
||||||
payloadBytes, err := json.Marshal(newNetworkInfo)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
payloadBuffer := bytes.NewBuffer(payloadBytes)
|
|
||||||
|
|
||||||
// Create the HTTP request
|
|
||||||
url := "http://localhost:" + strconv.Itoa(m.apiPort) + "/controller/network/" + networkId + "/"
|
|
||||||
req, err := http.NewRequest("POST", url, payloadBuffer)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
req.Header.Set("X-Zt1-Auth", m.authToken)
|
|
||||||
req.Header.Set("Content-Type", "application/json")
|
|
||||||
|
|
||||||
// Send the HTTP request
|
|
||||||
resp, err := http.DefaultClient.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
// Print the response status code
|
|
||||||
if resp.StatusCode != 200 {
|
|
||||||
return errors.New("network error. status code: " + strconv.Itoa(resp.StatusCode))
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// List network IDs
|
|
||||||
func (m *NetworkManager) listNetworkIds() ([]string, error) {
|
|
||||||
req, err := http.NewRequest("GET", "http://localhost:"+strconv.Itoa(m.apiPort)+"/controller/network/", nil)
|
|
||||||
if err != nil {
|
|
||||||
return []string{}, err
|
|
||||||
}
|
|
||||||
req.Header.Set("X-Zt1-Auth", m.authToken)
|
|
||||||
|
|
||||||
resp, err := http.DefaultClient.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return []string{}, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
if resp.StatusCode != 200 {
|
|
||||||
return []string{}, errors.New("network error")
|
|
||||||
}
|
|
||||||
|
|
||||||
networkIds := []string{}
|
|
||||||
payload, err := io.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return []string{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = json.Unmarshal(payload, &networkIds)
|
|
||||||
if err != nil {
|
|
||||||
return []string{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return networkIds, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// wrapper for checking if a network id exists
|
|
||||||
func (m *NetworkManager) networkExists(networkId string) bool {
|
|
||||||
networkIds, err := m.listNetworkIds()
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, thisid := range networkIds {
|
|
||||||
if thisid == networkId {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// delete a network
|
|
||||||
func (m *NetworkManager) deleteNetwork(networkID string) error {
|
|
||||||
url := "http://localhost:" + strconv.Itoa(m.apiPort) + "/controller/network/" + networkID + "/"
|
|
||||||
client := &http.Client{}
|
|
||||||
|
|
||||||
// Create a new DELETE request
|
|
||||||
req, err := http.NewRequest("DELETE", url, nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the required authorization header
|
|
||||||
req.Header.Set("X-Zt1-Auth", m.authToken)
|
|
||||||
|
|
||||||
// Send the request and get the response
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close the response body when we're done
|
|
||||||
defer resp.Body.Close()
|
|
||||||
s, err := io.ReadAll(resp.Body)
|
|
||||||
fmt.Println(string(s), err, resp.StatusCode)
|
|
||||||
|
|
||||||
// Print the response status code
|
|
||||||
if resp.StatusCode != 200 {
|
|
||||||
return errors.New("network error. status code: " + strconv.Itoa(resp.StatusCode))
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure network
|
|
||||||
// Example: configureNetwork(netid, "192.168.192.1", "192.168.192.254", "192.168.192.0/24")
|
|
||||||
func (m *NetworkManager) configureNetwork(networkID string, ipRangeStart string, ipRangeEnd string, routeTarget string) error {
|
|
||||||
url := "http://localhost:" + strconv.Itoa(m.apiPort) + "/controller/network/" + networkID + "/"
|
|
||||||
data := map[string]interface{}{
|
|
||||||
"ipAssignmentPools": []map[string]string{
|
|
||||||
{
|
|
||||||
"ipRangeStart": ipRangeStart,
|
|
||||||
"ipRangeEnd": ipRangeEnd,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"routes": []map[string]interface{}{
|
|
||||||
{
|
|
||||||
"target": routeTarget,
|
|
||||||
"via": nil,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"v4AssignMode": "zt",
|
|
||||||
"private": true,
|
|
||||||
}
|
|
||||||
|
|
||||||
payload, err := json.Marshal(data)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := http.NewRequest("POST", url, bytes.NewBuffer(payload))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
req.Header.Set("Content-Type", "application/json")
|
|
||||||
req.Header.Set("X-ZT1-AUTH", m.authToken)
|
|
||||||
|
|
||||||
client := &http.Client{}
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
defer resp.Body.Close()
|
|
||||||
// Print the response status code
|
|
||||||
if resp.StatusCode != 200 {
|
|
||||||
return errors.New("network error. status code: " + strconv.Itoa(resp.StatusCode))
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *NetworkManager) setAssignedIps(networkID string, memid string, newIps []string) error {
|
|
||||||
url := "http://localhost:" + strconv.Itoa(m.apiPort) + "/controller/network/" + networkID + "/member/" + memid
|
|
||||||
data := map[string]interface{}{
|
|
||||||
"ipAssignments": newIps,
|
|
||||||
}
|
|
||||||
|
|
||||||
payload, err := json.Marshal(data)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := http.NewRequest("POST", url, bytes.NewBuffer(payload))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
req.Header.Set("Content-Type", "application/json")
|
|
||||||
req.Header.Set("X-ZT1-AUTH", m.authToken)
|
|
||||||
|
|
||||||
client := &http.Client{}
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
defer resp.Body.Close()
|
|
||||||
// Print the response status code
|
|
||||||
if resp.StatusCode != 200 {
|
|
||||||
return errors.New("network error. status code: " + strconv.Itoa(resp.StatusCode))
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *NetworkManager) setNetworkNameAndDescription(netid string, name string, desc string) error {
|
|
||||||
// Convert string to rune slice
|
|
||||||
r := []rune(name)
|
|
||||||
|
|
||||||
// Loop over runes and remove non-ASCII characters
|
|
||||||
for i, v := range r {
|
|
||||||
if v > 127 {
|
|
||||||
r[i] = ' '
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert back to string and trim whitespace
|
|
||||||
name = strings.TrimSpace(string(r))
|
|
||||||
|
|
||||||
url := "http://localhost:" + strconv.Itoa(m.apiPort) + "/controller/network/" + netid + "/"
|
|
||||||
data := map[string]interface{}{
|
|
||||||
"name": name,
|
|
||||||
}
|
|
||||||
|
|
||||||
payload, err := json.Marshal(data)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := http.NewRequest("POST", url, bytes.NewBuffer(payload))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
req.Header.Set("Content-Type", "application/json")
|
|
||||||
req.Header.Set("X-ZT1-AUTH", m.authToken)
|
|
||||||
|
|
||||||
client := &http.Client{}
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
defer resp.Body.Close()
|
|
||||||
// Print the response status code
|
|
||||||
if resp.StatusCode != 200 {
|
|
||||||
return errors.New("network error. status code: " + strconv.Itoa(resp.StatusCode))
|
|
||||||
}
|
|
||||||
|
|
||||||
meta := m.GetNetworkMetaData(netid)
|
|
||||||
if meta != nil {
|
|
||||||
meta.Desc = desc
|
|
||||||
m.WriteNetworkMetaData(netid, meta)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *NetworkManager) getNetworkNameAndDescription(netid string) (string, string, error) {
|
|
||||||
//Get name from network info
|
|
||||||
netinfo, err := m.getNetworkInfoById(netid)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
name := netinfo.Name
|
|
||||||
|
|
||||||
//Get description from meta
|
|
||||||
desc := ""
|
|
||||||
networkMeta := m.GetNetworkMetaData(netid)
|
|
||||||
if networkMeta != nil {
|
|
||||||
desc = networkMeta.Desc
|
|
||||||
}
|
|
||||||
|
|
||||||
return name, desc, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Member functions
|
|
||||||
*/
|
|
||||||
|
|
||||||
func (m *NetworkManager) getNetworkMembers(networkId string) ([]string, error) {
|
|
||||||
url := "http://localhost:" + strconv.Itoa(m.apiPort) + "/controller/network/" + networkId + "/member"
|
|
||||||
reqBody := bytes.NewBuffer([]byte{})
|
|
||||||
req, err := http.NewRequest("GET", url, reqBody)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
req.Header.Set("X-ZT1-AUTH", m.authToken)
|
|
||||||
|
|
||||||
client := &http.Client{}
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
return nil, errors.New("failed to get network members")
|
|
||||||
}
|
|
||||||
|
|
||||||
memberList := map[string]int{}
|
|
||||||
payload, err := io.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = json.Unmarshal(payload, &memberList)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
members := make([]string, 0, len(memberList))
|
|
||||||
for k := range memberList {
|
|
||||||
members = append(members, k)
|
|
||||||
}
|
|
||||||
|
|
||||||
return members, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *NetworkManager) memberExistsInNetwork(netid string, memid string) bool {
|
|
||||||
//Get a list of member
|
|
||||||
memberids, err := m.getNetworkMembers(netid)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, thisMemberId := range memberids {
|
|
||||||
if thisMemberId == memid {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get a network memeber info by netid and memberid
|
|
||||||
func (m *NetworkManager) getNetworkMemberInfo(netid string, memberid string) (*MemberInfo, error) {
|
|
||||||
req, err := http.NewRequest("GET", "http://localhost:"+strconv.Itoa(m.apiPort)+"/controller/network/"+netid+"/member/"+memberid, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
req.Header.Set("X-Zt1-Auth", m.authToken)
|
|
||||||
|
|
||||||
resp, err := http.DefaultClient.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
thisMemeberInfo := &MemberInfo{}
|
|
||||||
payload, err := io.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = json.Unmarshal(payload, &thisMemeberInfo)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return thisMemeberInfo, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the authorization state of a member
|
|
||||||
func (m *NetworkManager) AuthorizeMember(netid string, memberid string, setAuthorized bool) error {
|
|
||||||
url := "http://localhost:" + strconv.Itoa(m.apiPort) + "/controller/network/" + netid + "/member/" + memberid
|
|
||||||
payload := []byte(`{"authorized": true}`)
|
|
||||||
if !setAuthorized {
|
|
||||||
payload = []byte(`{"authorized": false}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := http.NewRequest("POST", url, bytes.NewBuffer(payload))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
req.Header.Set("X-ZT1-AUTH", m.authToken)
|
|
||||||
client := &http.Client{}
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
if resp.StatusCode != 200 {
|
|
||||||
return errors.New("network error. Status code: " + strconv.Itoa(resp.StatusCode))
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete a member from the network
|
|
||||||
func (m *NetworkManager) deleteMember(netid string, memid string) error {
|
|
||||||
req, err := http.NewRequest("DELETE", "http://localhost:"+strconv.Itoa(m.apiPort)+"/controller/network/"+netid+"/member/"+memid, nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
req.Header.Set("X-Zt1-Auth", os.ExpandEnv(m.authToken))
|
|
||||||
|
|
||||||
resp, err := http.DefaultClient.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
if resp.StatusCode != 200 {
|
|
||||||
return errors.New("network error. Status code: " + strconv.Itoa(resp.StatusCode))
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make the host to join a given network
|
|
||||||
func (m *NetworkManager) joinNetwork(netid string) error {
|
|
||||||
req, err := http.NewRequest("POST", "http://localhost:"+strconv.Itoa(m.apiPort)+"/network/"+netid, nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
req.Header.Set("X-Zt1-Auth", os.ExpandEnv(m.authToken))
|
|
||||||
|
|
||||||
resp, err := http.DefaultClient.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
if resp.StatusCode != 200 {
|
|
||||||
return errors.New("network error. Status code: " + strconv.Itoa(resp.StatusCode))
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make the host to leave a given network
|
|
||||||
func (m *NetworkManager) leaveNetwork(netid string) error {
|
|
||||||
req, err := http.NewRequest("DELETE", "http://localhost:"+strconv.Itoa(m.apiPort)+"/network/"+netid, nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
req.Header.Set("X-Zt1-Auth", os.ExpandEnv(m.authToken))
|
|
||||||
|
|
||||||
resp, err := http.DefaultClient.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
if resp.StatusCode != 200 {
|
|
||||||
return errors.New("network error. Status code: " + strconv.Itoa(resp.StatusCode))
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
26
src/start.go
26
src/start.go
@ -20,7 +20,6 @@ import (
|
|||||||
"imuslab.com/zoraxy/mod/dynamicproxy/loadbalance"
|
"imuslab.com/zoraxy/mod/dynamicproxy/loadbalance"
|
||||||
"imuslab.com/zoraxy/mod/dynamicproxy/redirection"
|
"imuslab.com/zoraxy/mod/dynamicproxy/redirection"
|
||||||
"imuslab.com/zoraxy/mod/forwardproxy"
|
"imuslab.com/zoraxy/mod/forwardproxy"
|
||||||
"imuslab.com/zoraxy/mod/ganserv"
|
|
||||||
"imuslab.com/zoraxy/mod/geodb"
|
"imuslab.com/zoraxy/mod/geodb"
|
||||||
"imuslab.com/zoraxy/mod/info/logger"
|
"imuslab.com/zoraxy/mod/info/logger"
|
||||||
"imuslab.com/zoraxy/mod/info/logviewer"
|
"imuslab.com/zoraxy/mod/info/logviewer"
|
||||||
@ -156,6 +155,7 @@ func startupSequence() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
statisticCollector.SetAutoSave(STATISTIC_AUTO_SAVE_INTERVAL)
|
||||||
|
|
||||||
//Start the static web server
|
//Start the static web server
|
||||||
staticWebServer = webserv.NewWebServer(&webserv.WebServerOptions{
|
staticWebServer = webserv.NewWebServer(&webserv.WebServerOptions{
|
||||||
@ -247,24 +247,6 @@ func startupSequence() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
Global Area Network
|
|
||||||
|
|
||||||
Require zerotier token to work
|
|
||||||
*/
|
|
||||||
usingZtAuthToken := *ztAuthToken
|
|
||||||
if usingZtAuthToken == "" {
|
|
||||||
usingZtAuthToken, err = ganserv.TryLoadorAskUserForAuthkey()
|
|
||||||
if err != nil {
|
|
||||||
SystemWideLogger.Println("Failed to load ZeroTier controller API authtoken")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ganManager = ganserv.NewNetworkManager(&ganserv.NetworkManagerOptions{
|
|
||||||
AuthToken: usingZtAuthToken,
|
|
||||||
ApiPort: *ztAPIPort,
|
|
||||||
Database: sysdb,
|
|
||||||
})
|
|
||||||
|
|
||||||
//Create WebSSH Manager
|
//Create WebSSH Manager
|
||||||
webSshManager = sshprox.NewSSHProxyManager()
|
webSshManager = sshprox.NewSSHProxyManager()
|
||||||
|
|
||||||
@ -332,6 +314,12 @@ func startupSequence() {
|
|||||||
},
|
},
|
||||||
Database: sysdb,
|
Database: sysdb,
|
||||||
Logger: SystemWideLogger,
|
Logger: SystemWideLogger,
|
||||||
|
//TODO: REMOVE AFTER DEBUG
|
||||||
|
PluginGroups: map[string][]string{
|
||||||
|
"debug": {
|
||||||
|
"org.aroz.zoraxy.debugger",
|
||||||
|
},
|
||||||
|
},
|
||||||
CSRFTokenGen: func(r *http.Request) string {
|
CSRFTokenGen: func(r *http.Request) string {
|
||||||
return csrf.Token(r)
|
return csrf.Token(r)
|
||||||
},
|
},
|
||||||
|
@ -1,231 +0,0 @@
|
|||||||
<div id="ganetWindow" class="standardContainer">
|
|
||||||
<div class="ui basic segment">
|
|
||||||
<h2>Global Area Network</h2>
|
|
||||||
<p>Virtual Network Hub that allows all networked devices to communicate as if they all reside in the same physical data center or cloud region</p>
|
|
||||||
</div>
|
|
||||||
<div class="ui yellow message">
|
|
||||||
<b>Deprecation Notice</b>
|
|
||||||
<p>Global Area Network will be deprecating in v3.2.x and moved to Plugin</p>
|
|
||||||
</div>
|
|
||||||
<div class="gansnetworks">
|
|
||||||
<div class="ganstats ui basic segment">
|
|
||||||
<div style="float: right; max-width: 300px; margin-top: 0.4em;">
|
|
||||||
<h1 class="ui header" style="text-align: right;">
|
|
||||||
<span class="ganControllerID"></span>
|
|
||||||
<div class="sub header">Network Controller ID</div>
|
|
||||||
</h1>
|
|
||||||
</div>
|
|
||||||
<div class="ui list">
|
|
||||||
<div class="item">
|
|
||||||
<i class="exchange icon"></i>
|
|
||||||
<div class="content">
|
|
||||||
<div class="header" style="font-size: 1.2em;" id="ganetCount">0</div>
|
|
||||||
<div class="description">Networks</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="item">
|
|
||||||
<i class="desktop icon"></i>
|
|
||||||
<div class="content">
|
|
||||||
<div class="header" style="font-size: 1.2em;" id="ganodeCount">0</div>
|
|
||||||
<div class="description" id="connectedNodes" count="0">Connected Nodes</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="ganlist">
|
|
||||||
<button class="ui basic orange button" onclick="addGANet();">Create New Network</button>
|
|
||||||
<div class="ui divider"></div>
|
|
||||||
<!--
|
|
||||||
<div class="ui icon input" style="margin-bottom: 1em;">
|
|
||||||
<input type="text" placeholder="Search a Network">
|
|
||||||
<i class="circular search link icon"></i>
|
|
||||||
</div>-->
|
|
||||||
<div style="width: 100%; overflow-x: auto;">
|
|
||||||
<table class="ui celled basic unstackable striped table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Network ID</th>
|
|
||||||
<th>Name</th>
|
|
||||||
<th>Description</th>
|
|
||||||
<th>Subnet (Assign Range)</th>
|
|
||||||
<th>Nodes</th>
|
|
||||||
<th>Actions</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody id="GANetList">
|
|
||||||
<tr>
|
|
||||||
<td colspan="6"><i class="ui green circle check icon"></i> No Global Area Network Found on this host</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<script>
|
|
||||||
/*
|
|
||||||
Network Management Functions
|
|
||||||
*/
|
|
||||||
function handleAddNetwork(){
|
|
||||||
let networkName = $("#networkName").val().trim();
|
|
||||||
if (networkName == ""){
|
|
||||||
msgbox("Network name cannot be empty", false, 5000);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Add network with default settings
|
|
||||||
addGANet(networkName, "192.168.196.0/24");
|
|
||||||
$("#networkName").val("");
|
|
||||||
}
|
|
||||||
|
|
||||||
function initGANetID(){
|
|
||||||
$.get("/api/gan/network/info", function(data){
|
|
||||||
if (data.error !== undefined){
|
|
||||||
msgbox(data.error, false, 5000)
|
|
||||||
}else{
|
|
||||||
if (data != ""){
|
|
||||||
$(".ganControllerID").text(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function addGANet() {
|
|
||||||
$.cjax({
|
|
||||||
url: "/api/gan/network/add",
|
|
||||||
type: "POST",
|
|
||||||
dataType: "json",
|
|
||||||
data: {},
|
|
||||||
success: function(response) {
|
|
||||||
if (response.error != undefined){
|
|
||||||
msgbox(response.error, false, 5000);
|
|
||||||
}else{
|
|
||||||
msgbox("Network added successfully");
|
|
||||||
}
|
|
||||||
console.log("Network added successfully:", response);
|
|
||||||
listGANet();
|
|
||||||
},
|
|
||||||
error: function(xhr, status, error) {
|
|
||||||
console.log("Error adding network:", error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function listGANet(){
|
|
||||||
$("#connectedNodes").attr("count", "0");
|
|
||||||
|
|
||||||
$.get("/api/gan/network/list", function(data){
|
|
||||||
$("#GANetList").empty();
|
|
||||||
if (data.error != undefined){
|
|
||||||
console.log(data.error);
|
|
||||||
msgbox("Unable to load auth token for GANet", false, 5000);
|
|
||||||
//token error or no zerotier found
|
|
||||||
$(".gansnetworks").addClass("disabled");
|
|
||||||
$("#GANetList").append(`<tr>
|
|
||||||
<td colspan="6"><i class="red times circle icon"></i> Auth token access error or not found</td>
|
|
||||||
</tr>`);
|
|
||||||
$(".ganControllerID").text('Access Denied');
|
|
||||||
}else{
|
|
||||||
var nodeCount = 0;
|
|
||||||
data.forEach(function(gan){
|
|
||||||
$("#GANetList").append(`<tr class="ganetEntry" addr="${gan.nwid}">
|
|
||||||
<td><a href="#" onclick="event.preventDefault(); openGANetDetails('${gan.nwid}');">${gan.nwid}</a></td>
|
|
||||||
<td>${gan.name}</td>
|
|
||||||
<td class="gandesc" addr="${gan.nwid}"></td>
|
|
||||||
<td class="ganetSubnet"></td>
|
|
||||||
<td class="ganetNodes"></td>
|
|
||||||
<td>
|
|
||||||
<button onclick="openGANetDetails('${gan.nwid}');" class="ui tiny basic icon button" title="Edit Network"><i class="edit icon"></i></button>
|
|
||||||
<button onclick="removeGANet('${gan.nwid}');" class="ui tiny basic icon button" title="Remove Network"><i class="red remove icon"></i></button>
|
|
||||||
</td>
|
|
||||||
</tr>`);
|
|
||||||
|
|
||||||
nodeCount += 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (data.length == 0){
|
|
||||||
$("#GANetList").append(`<tr>
|
|
||||||
<td colspan="6"><i class="ui green circle check icon"></i> No Global Area Network Found on this host</td>
|
|
||||||
</tr>`);
|
|
||||||
}
|
|
||||||
|
|
||||||
$("#ganodeCount").text(nodeCount);
|
|
||||||
$("#ganetCount").text(data.length);
|
|
||||||
|
|
||||||
//Load description
|
|
||||||
$(".gandesc").each(function(){
|
|
||||||
let addr = $(this).attr("addr");
|
|
||||||
let domEle = $(this);
|
|
||||||
$.get("/api/gan/network/name?netid=" + addr, function(data){
|
|
||||||
$(domEle).text(data[1]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
$(".ganetEntry").each(function(){
|
|
||||||
let addr = $(this).attr("addr");
|
|
||||||
let subnetEle = $(this).find(".ganetSubnet");
|
|
||||||
let nodeEle = $(this).find(".ganetNodes");
|
|
||||||
|
|
||||||
$.get("/api/gan/network/list?netid=" + addr, function(data){
|
|
||||||
if (data.routes != undefined && data.routes.length > 0){
|
|
||||||
|
|
||||||
if (data.ipAssignmentPools != undefined && data.ipAssignmentPools.length > 0){
|
|
||||||
$(subnetEle).html(`${data.routes[0].target} <br> (${data.ipAssignmentPools[0].ipRangeStart} - ${data.ipAssignmentPools[0].ipRangeEnd})`);
|
|
||||||
}else{
|
|
||||||
$(subnetEle).html(`${data.routes[0].target}<br>(Unassigned Range)`);
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
$(subnetEle).text("Unassigned");
|
|
||||||
}
|
|
||||||
//console.log(data);
|
|
||||||
});
|
|
||||||
|
|
||||||
$.get("/api/gan/members/list?netid=" + addr, function(data){
|
|
||||||
$(nodeEle).text(data.length);
|
|
||||||
let currentNodesCount = parseInt($("#connectedNodes").attr("count"));
|
|
||||||
currentNodesCount += data.length;
|
|
||||||
$("#connectedNodes").attr("count", currentNodesCount);
|
|
||||||
$("#ganodeCount").text($("#connectedNodes").attr("count"));
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
//Remove the given GANet
|
|
||||||
function removeGANet(netid){
|
|
||||||
if (confirm("Confirm remove Network " + netid + " PERMANENTLY ?"))
|
|
||||||
$.cjax({
|
|
||||||
url: "/api/gan/network/remove",
|
|
||||||
type: "POST",
|
|
||||||
dataType: "json",
|
|
||||||
data: {
|
|
||||||
id: netid,
|
|
||||||
},
|
|
||||||
success: function(data){
|
|
||||||
if (data.error != undefined){
|
|
||||||
msgbox(data.error, false, 5000);
|
|
||||||
}else{
|
|
||||||
msgbox("Net " + netid + " removed");
|
|
||||||
}
|
|
||||||
listGANet();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function openGANetDetails(netid){
|
|
||||||
$("#ganetWindow").load("./components/gandetails.html", function(){
|
|
||||||
setTimeout(function(){
|
|
||||||
initGanetDetails(netid);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//Bind event to tab switch
|
|
||||||
tabSwitchEventBind["gan"] = function(){
|
|
||||||
//On switch over to this page, load info
|
|
||||||
listGANet();
|
|
||||||
initGANetID();
|
|
||||||
}
|
|
||||||
</script>
|
|
@ -1,687 +0,0 @@
|
|||||||
<div class="standardContainer">
|
|
||||||
<button onclick="exitToGanList();" class="ui large circular black icon button"><i class="angle left icon"></i></button>
|
|
||||||
<div style="max-width: 300px; margin-top: 1em;">
|
|
||||||
<button onclick='$("#gannetDetailEdit").slideToggle("fast");' class="ui mini basic right floated circular icon button" style="display: inline-block; margin-top: 2.5em;"><i class="ui edit icon"></i></button>
|
|
||||||
<h1 class="ui header">
|
|
||||||
<span class="ganetID"></span>
|
|
||||||
<div class="sub header ganetName"></div>
|
|
||||||
</h1>
|
|
||||||
<div class="ui divider"></div>
|
|
||||||
<p><span class="ganetDesc"></span></p>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div id="gannetDetailEdit" class="ui form" style="margin-top: 1em; display:none;">
|
|
||||||
<div class="ui divider"></div>
|
|
||||||
<p>You can change the network name and description below. <br>The name and description is only for easy management purpose and will not effect the network operation.</p>
|
|
||||||
<div class="field">
|
|
||||||
<label>Network Name</label>
|
|
||||||
<input type="text" id="gaNetNameInput" placeholder="">
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<label>Network Description</label>
|
|
||||||
<textarea id="gaNetDescInput" style="resize: none;"></textarea>
|
|
||||||
<button onclick="saveNameAndDesc(this);" class="ui basic right floated button" style="margin-top: 0.6em;"><i class="ui save icon"></i> Save</button>
|
|
||||||
<button onclick='$("#gannetDetailEdit").slideUp("fast");' class="ui basic right floated button" style="margin-top: 0.6em;"><i class="ui red remove icon"></i> Cancel</button>
|
|
||||||
</div>
|
|
||||||
<br><br>
|
|
||||||
</div>
|
|
||||||
<div class="ui divider"></div>
|
|
||||||
<h2>Settings</h2>
|
|
||||||
<div class="" style="overflow-x: auto;">
|
|
||||||
<table class="ui basic celled unstackable table" style="min-width: 560px;">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th colspan="4">IPv4 Auto-Assign</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody id="ganetRangeTable">
|
|
||||||
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<br>
|
|
||||||
<div class="ui form">
|
|
||||||
<h3>Custom IP Range</h3>
|
|
||||||
<p>Manual IP Range Configuration. The IP range must be within the selected CIDR range.
|
|
||||||
<br>Use <code>Utilities > IP to CIDR tool</code> if you are not too familiar with CIDR notations.</p>
|
|
||||||
<div class="two fields">
|
|
||||||
<div class="field">
|
|
||||||
<label>IP Start</label>
|
|
||||||
<input type="text" class="ganIpStart" placeholder="">
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<label>IP End</label>
|
|
||||||
<input type="text" class="ganIpEnd" placeholder="">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button onclick="setNetworkRange();" class="ui basic button"><i class="ui blue save icon"></i> Save Settings</button>
|
|
||||||
|
|
||||||
<div class="ui divider"></div>
|
|
||||||
<h2>Members</h2>
|
|
||||||
<p>To join this network using command line, type <code>sudo zerotier-cli join <span class="ganetID"></span></code> on your device terminal</p>
|
|
||||||
<div class="ui checkbox" style="margin-bottom: 1em;">
|
|
||||||
<input id="showUnauthorizedMembers" type="checkbox" onchange="changeUnauthorizedVisibility(this.checked);" checked>
|
|
||||||
<label>Show Unauthorized Members</label>
|
|
||||||
</div>
|
|
||||||
<div class="" style="overflow-x: auto;">
|
|
||||||
<table class="ui celled unstackable table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Auth</th>
|
|
||||||
<th>Address</th>
|
|
||||||
<th>Name</th>
|
|
||||||
<th>Managed IP</th>
|
|
||||||
<th>Authorized Since</th>
|
|
||||||
<th>Version</th>
|
|
||||||
<th>Remove</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody id="networkMemeberTable">
|
|
||||||
<tr>
|
|
||||||
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<div class="ui divider"></div>
|
|
||||||
<h4>Add Controller as Member</h4>
|
|
||||||
<p>Optionally you can add the network controller (ZeroTier running on the Zoraxy node) as member for cross GAN reverse proxy to bypass NAT limitations.</p>
|
|
||||||
<button class="ui basic small button addControllerToNetworkBtn" onclick="ganAddControllerToNetwork(this);"><i class="green add icon"></i> Add Controller as Member</button>
|
|
||||||
<button class="ui basic small button removeControllerFromNetworkBtn" onclick="ganRemoveControllerFromNetwork(this);"><i class="red sign-out icon"></i> Remove Controller from Member</button>
|
|
||||||
<br><br>
|
|
||||||
</div>
|
|
||||||
<script>
|
|
||||||
$(".checkbox").checkbox();
|
|
||||||
var currentGANetID = "";
|
|
||||||
var currentGANNetMemeberListener = undefined;
|
|
||||||
var currentGaNetDetails = {};
|
|
||||||
var currentGANMemberList = [];
|
|
||||||
var netRanges = {
|
|
||||||
"10.147.17.*": "10.147.17.0/24",
|
|
||||||
"10.147.18.*": "10.147.18.0/24",
|
|
||||||
"10.147.19.*": "10.147.19.0/24",
|
|
||||||
"10.147.20.*": "10.147.20.0/24",
|
|
||||||
"10.144.*.*": "10.144.0.0/16",
|
|
||||||
"10.241.*.*": "10.241.0.0/16",
|
|
||||||
"10.242.*.*": "10.242.0.0/16",
|
|
||||||
"10.243.*.*": "10.243.0.0/16",
|
|
||||||
"10.244.*.*": "10.244.0.0/16",
|
|
||||||
"172.22.*.*": "172.22.0.0/15",
|
|
||||||
"172.23.*.*": "172.23.0.0/16",
|
|
||||||
"172.24.*.*": "172.24.0.0/14",
|
|
||||||
"172.25.*.*": "172.25.0.0/16",
|
|
||||||
"172.26.*.*": "172.26.0.0/15",
|
|
||||||
"172.27.*.*": "172.27.0.0/16",
|
|
||||||
"172.28.*.*": "172.28.0.0/15",
|
|
||||||
"172.29.*.*": "172.29.0.0/16",
|
|
||||||
"172.30.*.*": "172.30.0.0/15",
|
|
||||||
"192.168.191.*": "192.168.191.0/24",
|
|
||||||
"192.168.192.*": "192.168.192.0/24",
|
|
||||||
"192.168.193.*": "192.168.193.0/24",
|
|
||||||
"192.168.194.*": "192.168.194.0/24",
|
|
||||||
"192.168.195.*": "192.168.195.0/24",
|
|
||||||
"192.168.196.*": "192.168.196.0/24"
|
|
||||||
}
|
|
||||||
|
|
||||||
function generateIPRangeTable(netRanges) {
|
|
||||||
$("#ganetRangeTable").empty();
|
|
||||||
const tableBody = document.getElementById('ganetRangeTable');
|
|
||||||
const cidrs = Object.values(netRanges);
|
|
||||||
|
|
||||||
// Set the number of rows and columns to display in the table
|
|
||||||
const numRows = 6;
|
|
||||||
const numCols = 4;
|
|
||||||
|
|
||||||
let row = document.createElement('tr');
|
|
||||||
let col = 0;
|
|
||||||
for (let i = 0; i < cidrs.length; i++) {
|
|
||||||
if (col >= numCols) {
|
|
||||||
tableBody.appendChild(row);
|
|
||||||
row = document.createElement('tr');
|
|
||||||
col = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const td = document.createElement('td');
|
|
||||||
td.setAttribute('class', `clickable iprange`);
|
|
||||||
td.setAttribute('CIDR', cidrs[i]);
|
|
||||||
td.innerHTML = cidrs[i];
|
|
||||||
let thisCidr = cidrs[i];
|
|
||||||
td.onclick = function(){
|
|
||||||
selectNetworkRange(thisCidr, td);
|
|
||||||
};
|
|
||||||
|
|
||||||
row.appendChild(td);
|
|
||||||
col++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add any remaining cells to the table
|
|
||||||
if (col > 0) {
|
|
||||||
for (let i = col; i < numCols; i++) {
|
|
||||||
row.appendChild(document.createElement('td'));
|
|
||||||
}
|
|
||||||
tableBody.appendChild(row);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function highlightCurrentGANetCIDR(){
|
|
||||||
var currentCIDR = currentGaNetDetails.routes[0].target;
|
|
||||||
$(".iprange").each(function(){
|
|
||||||
if ($(this).attr("CIDR") == currentCIDR){
|
|
||||||
$(this).addClass("active");
|
|
||||||
populateStartEndIpByCidr(currentCIDR);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function populateStartEndIpByCidr(cidr){
|
|
||||||
function cidrToRange(cidr) {
|
|
||||||
var range = [2];
|
|
||||||
cidr = cidr.split('/');
|
|
||||||
var start = ip2long(cidr[0]);
|
|
||||||
range[0] = long2ip(start);
|
|
||||||
range[1] = long2ip(Math.pow(2, 32 - cidr[1]) + start - 1);
|
|
||||||
return range;
|
|
||||||
}
|
|
||||||
var cidrRange = cidrToRange(cidr);
|
|
||||||
$(".ganIpStart").val(cidrRange[0]);
|
|
||||||
$(".ganIpEnd").val(cidrRange[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function selectNetworkRange(cidr, object){
|
|
||||||
populateStartEndIpByCidr(cidr);
|
|
||||||
|
|
||||||
$(".iprange.active").removeClass("active");
|
|
||||||
$(object).addClass("active");
|
|
||||||
}
|
|
||||||
|
|
||||||
function setNetworkRange(){
|
|
||||||
var ipstart = $(".ganIpStart").val().trim();
|
|
||||||
var ipend = $(".ganIpEnd").val().trim();
|
|
||||||
|
|
||||||
if (ipstart == ""){
|
|
||||||
$(".ganIpStart").parent().addClass("error");
|
|
||||||
}else{
|
|
||||||
$(".ganIpStart").parent().removeClass("error");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ipend == ""){
|
|
||||||
$(".ganIpEnd").parent().addClass("error");
|
|
||||||
}else{
|
|
||||||
$(".ganIpEnd").parent().removeClass("error");
|
|
||||||
}
|
|
||||||
|
|
||||||
//Get CIDR from selected range group
|
|
||||||
var cidr = $(".iprange.active").attr("cidr");
|
|
||||||
|
|
||||||
$.cjax({
|
|
||||||
url: "/api/gan/network/setRange",
|
|
||||||
metohd: "POST",
|
|
||||||
data:{
|
|
||||||
netid: currentGANetID,
|
|
||||||
cidr: cidr,
|
|
||||||
ipstart: ipstart,
|
|
||||||
ipend: ipend
|
|
||||||
},
|
|
||||||
success: function(data){
|
|
||||||
if (data.error != undefined){
|
|
||||||
msgbox(data.error, false, 5000)
|
|
||||||
}else{
|
|
||||||
msgbox("Network Range Updated")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function saveNameAndDesc(object=undefined){
|
|
||||||
var name = $("#gaNetNameInput").val();
|
|
||||||
var desc = $("#gaNetDescInput").val();
|
|
||||||
if (object != undefined){
|
|
||||||
$(object).addClass("loading");
|
|
||||||
}
|
|
||||||
$.cjax({
|
|
||||||
url: "/api/gan/network/name",
|
|
||||||
method: "POST",
|
|
||||||
data: {
|
|
||||||
netid: currentGANetID,
|
|
||||||
name: name,
|
|
||||||
desc: desc,
|
|
||||||
},
|
|
||||||
success: function(data){
|
|
||||||
initNetNameAndDesc();
|
|
||||||
if (object != undefined){
|
|
||||||
$(object).removeClass("loading");
|
|
||||||
msgbox("Network Metadata Updated");
|
|
||||||
}
|
|
||||||
$("#gannetDetailEdit").slideUp("fast");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function initNetNameAndDesc(){
|
|
||||||
//Get the details of the net
|
|
||||||
$.get("/api/gan/network/name?netid=" + currentGANetID, function(data){
|
|
||||||
if (data.error !== undefined){
|
|
||||||
msgbox(data.error, false, 6000);
|
|
||||||
}else{
|
|
||||||
$("#gaNetNameInput").val(data[0]);
|
|
||||||
$(".ganetName").html(data[0]);
|
|
||||||
$("#gaNetDescInput").val(data[1]);
|
|
||||||
$(".ganetDesc").text(data[1]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function initNetDetails(){
|
|
||||||
//Get the details of the net
|
|
||||||
$.get("/api/gan/network/list?netid=" + currentGANetID, function(data){
|
|
||||||
if (data.error !== undefined){
|
|
||||||
msgbox(data.error, false, 6000);
|
|
||||||
}else{
|
|
||||||
currentGaNetDetails = data;
|
|
||||||
highlightCurrentGANetCIDR();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
//Handle delete IP from memeber
|
|
||||||
function deleteIpFromMemeber(memberid, ip){
|
|
||||||
$.cjax({
|
|
||||||
url: "/api/gan/members/ip",
|
|
||||||
metohd: "POST",
|
|
||||||
data: {
|
|
||||||
netid: currentGANetID,
|
|
||||||
memid: memberid,
|
|
||||||
opr: "del",
|
|
||||||
ip: ip,
|
|
||||||
},
|
|
||||||
success: function(data){
|
|
||||||
if (data.error != undefined){
|
|
||||||
msgbox(data.error, false, 5000);
|
|
||||||
}else{
|
|
||||||
msgbox("IP removed from member " + memberid)
|
|
||||||
}
|
|
||||||
renderMemeberTable();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function addIpToMemeberFromInput(memberid, newip){
|
|
||||||
function isValidIPv4Address(address) {
|
|
||||||
// Split the address into its 4 components
|
|
||||||
const parts = address.split('.');
|
|
||||||
|
|
||||||
// Check that there are 4 components
|
|
||||||
if (parts.length !== 4) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check that each component is a number between 0 and 255
|
|
||||||
for (let i = 0; i < 4; i++) {
|
|
||||||
const part = parseInt(parts[i], 10);
|
|
||||||
if (isNaN(part) || part < 0 || part > 255) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The address is valid
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isValidIPv4Address(newip)){
|
|
||||||
msgbox(newip + " is not a valid IPv4 address", false, 5000)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
$.cjax({
|
|
||||||
url: "/api/gan/members/ip",
|
|
||||||
metohd: "POST",
|
|
||||||
data: {
|
|
||||||
netid: currentGANetID,
|
|
||||||
memid: memberid,
|
|
||||||
opr: "add",
|
|
||||||
ip: newip,
|
|
||||||
},
|
|
||||||
success: function(data){
|
|
||||||
if (data.error != undefined){
|
|
||||||
msgbox(data.error, false, 5000);
|
|
||||||
}else{
|
|
||||||
msgbox("IP added to member " + memberid)
|
|
||||||
}
|
|
||||||
renderMemeberTable();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
//Member table populate
|
|
||||||
function renderMemeberTable(forceUpdate = false) {
|
|
||||||
$.ajax({
|
|
||||||
url: '/api/gan/members/list?netid=' + currentGANetID + '&detail=true',
|
|
||||||
type: 'GET',
|
|
||||||
success: function(data) {
|
|
||||||
let tableBody = $('#networkMemeberTable');
|
|
||||||
if (tableBody.length == 0){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
data.sort((a, b) => a.address.localeCompare(b.address));
|
|
||||||
//Check if the new object equal to the old one
|
|
||||||
if (objectEqual(currentGANMemberList, data) && !forceUpdate){
|
|
||||||
//Do not need to update it
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
tableBody.empty();
|
|
||||||
currentGANMemberList = data;
|
|
||||||
|
|
||||||
var authroziedCount = 0;
|
|
||||||
data.forEach((member) => {
|
|
||||||
let lastAuthTime = new Date(member.lastAuthorizedTime).toLocaleString();
|
|
||||||
if (member.lastAuthorizedTime == 0){
|
|
||||||
lastAuthTime = "Never";
|
|
||||||
}
|
|
||||||
|
|
||||||
let version = `${member.vMajor}.${member.vMinor}.${member.vProto}.${member.vRev}`;
|
|
||||||
if (member.vMajor == -1){
|
|
||||||
version = "Unknown";
|
|
||||||
}
|
|
||||||
|
|
||||||
let authorizedCheckbox = `<div class="ui fitted checkbox">
|
|
||||||
<input type="checkbox" addr="${member.address}" name="isAuthrozied" onchange="handleMemberAuth(this);">
|
|
||||||
<label></label>
|
|
||||||
</div>`;
|
|
||||||
if (member.authorized){
|
|
||||||
authorizedCheckbox = `<div class="ui fitted checkbox">
|
|
||||||
<input type="checkbox" addr="${member.address}" name="isAuthrozied" onchange="handleMemberAuth(this);" checked="">
|
|
||||||
<label></label>
|
|
||||||
</div>`
|
|
||||||
}
|
|
||||||
|
|
||||||
let rowClass = "authorized";
|
|
||||||
let unauthorizedStyle = "";
|
|
||||||
if (!$("#showUnauthorizedMembers")[0].checked && !member.authorized){
|
|
||||||
unauthorizedStyle = "display:none;";
|
|
||||||
}
|
|
||||||
if (!member.authorized){
|
|
||||||
rowClass = "unauthorized";
|
|
||||||
}else{
|
|
||||||
authroziedCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
let assignedIp = "";
|
|
||||||
|
|
||||||
if (member.ipAssignments.length == 0){
|
|
||||||
assignedIp = "Not assigned"
|
|
||||||
}else{
|
|
||||||
assignedIp = `<div class="ui list">`
|
|
||||||
member.ipAssignments.forEach(function(thisIp){
|
|
||||||
assignedIp += `<div class="item" style="width: 100%;">${thisIp} <a style="cursor:pointer; float: right;" title="Remove IP" onclick="deleteIpFromMemeber('${member.address}','${thisIp}');"><i class="red remove icon"></i></a></div>`;
|
|
||||||
})
|
|
||||||
assignedIp += `</div>`
|
|
||||||
}
|
|
||||||
const row = $(`<tr class="GANetMemberEntity ${rowClass}" style="${unauthorizedStyle}">`);
|
|
||||||
row.append($(`<td class="GANetMember ${rowClass}" style="text-align: center;">`).html(authorizedCheckbox));
|
|
||||||
row.append($('<td>').text(member.address));
|
|
||||||
row.append($('<td>').html(`<span class="memberName" addr="${member.address}"></span> <a style="cursor:pointer; float: right;" title="Edit Memeber Name" onclick="renameMember('${member.address}');"><i class="grey edit icon"></i></a>`));
|
|
||||||
row.append($('<td>').html(`${assignedIp}
|
|
||||||
<div class="ui action mini fluid input" style="min-width: 200px;">
|
|
||||||
<input type="text" placeholder="IPv4" onchange="$(this).val($(this).val().trim());">
|
|
||||||
<button onclick="addIpToMemeberFromInput('${member.address}',$(this).parent().find('input').val());" class="ui basic icon button">
|
|
||||||
<i class="add icon"></i>
|
|
||||||
</button>
|
|
||||||
</div>`));
|
|
||||||
row.append($('<td>').text(lastAuthTime));
|
|
||||||
row.append($('<td>').text(version));
|
|
||||||
row.append($(`<td title="Deauthorize & Delete Memeber" style="text-align: center;" onclick="handleMemberDelete('${member.address}');">`).html(`<button class="ui basic mini icon button"><i class="red remove icon"></i></button>`));
|
|
||||||
|
|
||||||
tableBody.append(row);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (data.length == 0){
|
|
||||||
tableBody.append(`<tr>
|
|
||||||
<td colspan="7"><i class="green check circle icon"></i> No member has joined this network yet.</td>
|
|
||||||
</tr>`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.length > 0 && authroziedCount == 0 && !$("#showUnauthorizedMembers")[0].checked){
|
|
||||||
//All nodes are unauthorized. Show tips to enable unauthorize display
|
|
||||||
tableBody.append(`<tr>
|
|
||||||
<td colspan="7"><i class="yellow exclamation circle icon"></i> Unauthorized nodes detected. Enable "Show Unauthorized Member" to change member access permission.</td>
|
|
||||||
</tr>`);
|
|
||||||
}
|
|
||||||
|
|
||||||
initNameForMembers();
|
|
||||||
},
|
|
||||||
error: function(xhr, status, error) {
|
|
||||||
console.log('Error:', error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function initNameForMembers(){
|
|
||||||
$(".memberName").each(function(){
|
|
||||||
let addr = $(this).attr("addr");
|
|
||||||
let targetDOM = $(this);
|
|
||||||
$.cjax({
|
|
||||||
url: "/api/gan/members/name",
|
|
||||||
method: "POST",
|
|
||||||
data: {
|
|
||||||
netid: currentGANetID,
|
|
||||||
memid: addr,
|
|
||||||
},
|
|
||||||
success: function(data){
|
|
||||||
if (data.error != undefined){
|
|
||||||
$(targetDOM).text("N/A");
|
|
||||||
}else{
|
|
||||||
$(targetDOM).text(data.Name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function renameMember(targetMemberAddr){
|
|
||||||
if (targetMemberAddr == ""){
|
|
||||||
msgbox("Member address cannot be empty", false, 5000)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let newname = prompt("Enter a easy manageable name for " + targetMemberAddr, "");
|
|
||||||
if (newname != null && newname.trim() != "") {
|
|
||||||
$.cjax({
|
|
||||||
url: "/api/gan/members/name",
|
|
||||||
method: "POST",
|
|
||||||
data: {
|
|
||||||
netid: currentGANetID,
|
|
||||||
memid: targetMemberAddr,
|
|
||||||
name: newname
|
|
||||||
},
|
|
||||||
success: function(data){
|
|
||||||
if (data.error != undefined){
|
|
||||||
msgbox(data.error, false, 6000);
|
|
||||||
}else{
|
|
||||||
msgbox("Member Name Updated");
|
|
||||||
}
|
|
||||||
renderMemeberTable(true);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Helper function to check if two objects are equal recursively
|
|
||||||
function objectEqual(obj1, obj2) {
|
|
||||||
// compare types
|
|
||||||
if (typeof obj1 !== typeof obj2) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// compare values
|
|
||||||
if (typeof obj1 !== 'object' || obj1 === null) {
|
|
||||||
return obj1 === obj2;
|
|
||||||
}
|
|
||||||
|
|
||||||
const keys1 = Object.keys(obj1);
|
|
||||||
const keys2 = Object.keys(obj2);
|
|
||||||
|
|
||||||
// compare keys
|
|
||||||
if (keys1.length !== keys2.length) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const key of keys1) {
|
|
||||||
if (!keys2.includes(key)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// recursively compare values
|
|
||||||
if (!objectEqual(obj1[key], obj2[key])) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function changeUnauthorizedVisibility(visable){
|
|
||||||
if(visable){
|
|
||||||
$(".GANetMemberEntity.unauthorized").show();
|
|
||||||
}else{
|
|
||||||
$(".GANetMemberEntity.unauthorized").hide();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleMemberAuth(object){
|
|
||||||
let targetMemberAddr = $(object).attr("addr");
|
|
||||||
let isAuthed = object.checked;
|
|
||||||
$.cjax({
|
|
||||||
url: "/api/gan/members/authorize",
|
|
||||||
method: "POST",
|
|
||||||
data: {
|
|
||||||
netid:currentGANetID,
|
|
||||||
memid: targetMemberAddr,
|
|
||||||
auth: isAuthed
|
|
||||||
},
|
|
||||||
success: function(data){
|
|
||||||
if (data.error != undefined){
|
|
||||||
msgbox(data.error, false, 6000);
|
|
||||||
}else{
|
|
||||||
if (isAuthed){
|
|
||||||
msgbox("Member Authorized");
|
|
||||||
}else{
|
|
||||||
msgbox("Member Deauthorized");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
renderMemeberTable(true);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleMemberDelete(addr){
|
|
||||||
if (confirm("Confirm delete member " + addr + " ?")){
|
|
||||||
$.cjax({
|
|
||||||
url: "/api/gan/members/delete",
|
|
||||||
method: "POST",
|
|
||||||
data: {
|
|
||||||
netid:currentGANetID,
|
|
||||||
memid: addr,
|
|
||||||
},
|
|
||||||
success: function(data){
|
|
||||||
if (data.error != undefined){
|
|
||||||
msgbox(data.error, false, 6000);
|
|
||||||
}else{
|
|
||||||
msgbox("Member Deleted");
|
|
||||||
}
|
|
||||||
renderMemeberTable(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//Add and remove this controller node to network as member
|
|
||||||
function ganAddControllerToNetwork(){
|
|
||||||
$(".addControllerToNetworkBtn").addClass("disabled");
|
|
||||||
$(".addControllerToNetworkBtn").addClass("loading");
|
|
||||||
|
|
||||||
$.cjax({
|
|
||||||
url: "/api/gan/network/join",
|
|
||||||
method: "POST",
|
|
||||||
data: {
|
|
||||||
netid:currentGANetID,
|
|
||||||
},
|
|
||||||
success: function(data){
|
|
||||||
$(".addControllerToNetworkBtn").removeClass("disabled");
|
|
||||||
$(".addControllerToNetworkBtn").removeClass("loading");
|
|
||||||
if (data.error != undefined){
|
|
||||||
msgbox(data.error, false, 6000);
|
|
||||||
}else{
|
|
||||||
msgbox("Controller joint " + currentGANetID);
|
|
||||||
}
|
|
||||||
setTimeout(function(){
|
|
||||||
renderMemeberTable(true);
|
|
||||||
}, 3000)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function ganRemoveControllerFromNetwork(){
|
|
||||||
$(".removeControllerFromNetworkBtn").addClass("disabled");
|
|
||||||
$(".removeControllerFromNetworkBtn").addClass("loading");
|
|
||||||
|
|
||||||
$.cjax({
|
|
||||||
url: "/api/gan/network/leave",
|
|
||||||
method: "POST",
|
|
||||||
data: {
|
|
||||||
netid:currentGANetID,
|
|
||||||
},
|
|
||||||
success: function(data){
|
|
||||||
if (data.error != undefined){
|
|
||||||
msgbox(data.error, false, 6000);
|
|
||||||
}else{
|
|
||||||
msgbox("Controller left " + currentGANetID);
|
|
||||||
}
|
|
||||||
renderMemeberTable(true);
|
|
||||||
$(".removeControllerFromNetworkBtn").removeClass("disabled");
|
|
||||||
$(".removeControllerFromNetworkBtn").removeClass("loading");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
//Entry points
|
|
||||||
function initGanetDetails(ganetId){
|
|
||||||
currentGANetID = ganetId;
|
|
||||||
$(".ganetID").text(ganetId);
|
|
||||||
initNetNameAndDesc(ganetId);
|
|
||||||
generateIPRangeTable(netRanges);
|
|
||||||
initNetDetails();
|
|
||||||
renderMemeberTable(true);
|
|
||||||
|
|
||||||
//Setup a listener to listen for member list change
|
|
||||||
if (currentGANNetMemeberListener == undefined){
|
|
||||||
currentGANNetMemeberListener = setInterval(function(){
|
|
||||||
if ($('#networkMemeberTable').length > 0 && currentGANetID){
|
|
||||||
renderMemeberTable();
|
|
||||||
}
|
|
||||||
}, 3000);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//Switch from other tabs back to this, exit to GAN list
|
|
||||||
tabSwitchEventBind["gan"] = function(){
|
|
||||||
exitToGanList();
|
|
||||||
}
|
|
||||||
|
|
||||||
//Exit point
|
|
||||||
function exitToGanList(){
|
|
||||||
$("#gan").load("./components/gan.html", function(){
|
|
||||||
if (tabSwitchEventBind["gan"]){
|
|
||||||
tabSwitchEventBind["gan"]();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
</script>
|
|
@ -67,9 +67,6 @@
|
|||||||
<a class="item" tag="access">
|
<a class="item" tag="access">
|
||||||
<i class="simplistic ban icon"></i> Access Control
|
<i class="simplistic ban icon"></i> Access Control
|
||||||
</a>
|
</a>
|
||||||
<a class="item" tag="gan">
|
|
||||||
<i class="simplistic globe icon"></i> Global Area Network
|
|
||||||
</a>
|
|
||||||
<div class="ui divider menudivider">Security</div>
|
<div class="ui divider menudivider">Security</div>
|
||||||
<a class="item" tag="cert">
|
<a class="item" tag="cert">
|
||||||
<i class="simplistic lock icon"></i> TLS / SSL certificates
|
<i class="simplistic lock icon"></i> TLS / SSL certificates
|
||||||
@ -135,9 +132,6 @@
|
|||||||
<!-- Blacklist -->
|
<!-- Blacklist -->
|
||||||
<div id="access" class="functiontab" target="access.html"></div>
|
<div id="access" class="functiontab" target="access.html"></div>
|
||||||
|
|
||||||
<!-- Global Area Networking -->
|
|
||||||
<div id="gan" class="functiontab" target="gan.html"></div>
|
|
||||||
|
|
||||||
<!-- SSO / Oauth services -->
|
<!-- SSO / Oauth services -->
|
||||||
<div id="sso" class="functiontab" target="sso.html"></div>
|
<div id="sso" class="functiontab" target="sso.html"></div>
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user