浏览代码

auto update script executed

Toby Chui 1 年之前
父节点
当前提交
314a143bb2
共有 9 个文件被更改,包括 489 次插入1 次删除
  1. 1 1
      main.go
  2. 9 0
      mod/ganserv/dhcp.go
  3. 104 0
      mod/ganserv/ganserv.go
  4. 59 0
      mod/ganserv/handlers.go
  5. 39 0
      mod/ganserv/network.go
  6. 55 0
      mod/ganserv/network_test.go
  7. 87 0
      mod/ganserv/utils.go
  8. 102 0
      web/components/gan.html
  9. 33 0
      web/main.css

+ 1 - 1
main.go

@@ -34,7 +34,7 @@ var (
 	name        = "Zoraxy"
 	version     = "2.11"
 	nodeUUID    = "generic"
-	development = false //Set this to false to use embedded web fs
+	development = true //Set this to false to use embedded web fs
 
 	/*
 		Binary Embedding File System

+ 9 - 0
mod/ganserv/dhcp.go

@@ -0,0 +1,9 @@
+package ganserv
+
+/*
+	DHCP.go
+
+	This script is use to emulate the use of DHCP of a
+	GAN network that assign local IP address to the connected
+	nodes via GET request
+*/

+ 104 - 0
mod/ganserv/ganserv.go

@@ -0,0 +1,104 @@
+package ganserv
+
+import (
+	"errors"
+	"fmt"
+	"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
+}
+
+type NetworkManager struct {
+	option   *NetworkManagerOptions
+	networks []*Network
+}
+
+//Create a new GAN manager
+func NewNetworkManager(option *NetworkManagerOptions) *NetworkManager {
+	return &NetworkManager{
+		option: option,
+	}
+}
+
+//List all the networks within this network manager
+func (m *NetworkManager) ListNetworks() []*Network {
+	return m.networks
+}
+
+func (m *NetworkManager) AddNetwork(n *Network) error {
+	if m.NetworkExists(n.UID) {
+		return errors.New("network with given id already exists")
+	}
+	m.networks = append(m.networks, n)
+	return nil
+}
+
+func (m *NetworkManager) RemoveNetwork(id string) error {
+	for i, n := range m.networks {
+		if n.UID == id {
+			m.networks = append(m.networks[:i], m.networks[i+1:]...)
+			return nil
+		}
+	}
+	return fmt.Errorf("network not found: %s", id)
+}
+
+func (m *NetworkManager) GetNetworkByID(id string) (*Network, error) {
+	for _, n := range m.networks {
+		if n.UID == id {
+			return n, nil
+		}
+	}
+	return nil, fmt.Errorf("network not found: %s", id)
+}
+
+func (m *NetworkManager) NetworkExists(id string) bool {
+	_, err := m.GetNetworkByID(id)
+	return err == nil
+}

+ 59 - 0
mod/ganserv/handlers.go

@@ -0,0 +1,59 @@
+package ganserv
+
+import (
+	"encoding/json"
+	"net/http"
+
+	"imuslab.com/zoraxy/mod/utils"
+)
+
+func (m *NetworkManager) HandleAddNetwork(w http.ResponseWriter, r *http.Request) {
+	networkName, err := utils.PostPara(r, "name")
+	if err != nil {
+		utils.SendErrorResponse(w, "invalid or empty name given")
+		return
+	}
+
+	// Generate a new UUID for this network
+	networkUID := newNetworkID()
+
+	// Create a new network object and add it to the list of networks
+	newNetwork := &Network{
+		UID:         networkUID,
+		Name:        networkName,
+		CIDR:        "",
+		Description: "",
+		Nodes:       []*Node{},
+	}
+	m.networks = append(m.networks, newNetwork)
+
+	// Return the new network ID
+	js, _ := json.Marshal(networkUID)
+	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
+	}
+
+	// Find the network with the given ID and remove it from the list of networks
+	for i, network := range m.networks {
+		if network.UID == networkID {
+			m.networks = append(m.networks[:i], m.networks[i+1:]...)
+			utils.SendOK(w)
+			return
+		}
+	}
+
+	// If the network is not found, return an error response
+	utils.SendErrorResponse(w, "network not found")
+}
+
+func (m *NetworkManager) HandleListNetwork(w http.ResponseWriter, r *http.Request) {
+	// Return the list of networks as JSON
+	js, _ := json.Marshal(m.networks)
+	utils.SendJSONResponse(w, string(js))
+}

+ 39 - 0
mod/ganserv/network.go

@@ -0,0 +1,39 @@
+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
+}

+ 55 - 0
mod/ganserv/network_test.go

@@ -0,0 +1,55 @@
+package ganserv_test
+
+import (
+	"fmt"
+	"net"
+	"strconv"
+	"testing"
+
+	"imuslab.com/zoraxy/mod/ganserv"
+)
+
+func TestGetRandomFreeIP(t *testing.T) {
+	n := ganserv.Network{
+		CIDR: "192.168.22.0/24",
+		Nodes: []*ganserv.Node{
+			{
+				Name:      "nodeC1",
+				ManagedIP: net.ParseIP("192.168.0.2"),
+			},
+			{
+				Name:      "nodeC2",
+				ManagedIP: net.ParseIP("192.168.0.3"),
+			},
+		},
+	}
+
+	// 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,
+		})
+	}
+
+}

+ 87 - 0
mod/ganserv/utils.go

@@ -0,0 +1,87 @@
+package ganserv
+
+import (
+	"fmt"
+	"math/rand"
+	"net"
+	"time"
+)
+
+//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
+		}
+	}
+}
+
+//Validate if a network struct is correct
+func validateNetwork(n *Network) error {
+	// Check if UID is a valid 16 char random ASCII string
+	if len(n.UID) != 16 {
+		return fmt.Errorf("UID must be 16 characters long")
+	}
+	for _, r := range n.UID {
+		if r < '0' || ('9' < r && r < 'A') || ('Z' < r && r < 'a') || r > 'z' {
+			return fmt.Errorf("UID must be a 16 char random ASCII string")
+		}
+	}
+
+	// Check if Name is ASCII only
+	if !isASCII(n.Name) {
+		return fmt.Errorf("Name must be ASCII only")
+	}
+
+	// Check if Subnet is valid
+	if n.CIDR == "" {
+		return fmt.Errorf("Subnet is not set")
+	}
+	_, _, err := net.ParseCIDR(n.CIDR)
+	if err != nil {
+		//This is not a valid CIDR
+		return fmt.Errorf("CIDR is not valid")
+	}
+
+	return nil
+}
+
+//Check if a string is ascii
+func isASCII(s string) bool {
+	for _, r := range s {
+		if r > 127 {
+			return false
+		}
+	}
+	return true
+}
+
+//Generate a new random network ID
+func newNetworkID() string {
+	rand.Seed(time.Now().UnixNano())
+
+	const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
+	const length = 16
+
+	var result string
+	for i := 0; i < length; i++ {
+		result += string(chars[rand.Intn(len(chars))])
+	}
+
+	return result
+}

+ 102 - 0
web/components/gan.html

@@ -0,0 +1,102 @@
+<div 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="gansnetworks">
+        <div class="ganstats">
+            <div class="ui list">
+                <div class="item">
+                    <i class="exchange icon"></i>
+                    <div class="content">
+                    <div class="header" style="font-size: 1.2em;">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;">0 / 0</div>
+                    <div class="description">Connected Nodes</div>
+                    </div>
+                </div>
+            </div>
+        </div>
+        <div class="ganlist">
+            <button class="ui basic orange button">Create New Network</button>
+            <div class="ui icon input">
+                <input type="text" placeholder="Search a Network">
+                <i class="circular search link icon"></i>
+            </div>
+            <div class="ui segment">
+                <div class="ui input">
+                    <input type="text" placeholder="Network Name">
+                </div>
+                <button class="ui basic button">
+                    <i class="blue add icon"></i> Add Network
+                </button>
+            </div>
+           
+            <div class="ui divider"></div>
+            <table class="ui celled basic striped table">
+                <thead>
+                    <tr>
+                        <th>Network ID</th>
+                        <th>Name</th>
+                        <th>Description</th>
+                        <th>Subnet</th>
+                        <th>Nodes</th>
+                        <th>Created</th>
+                    </tr>
+                </thead>
+                <tbody>
+                    <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>
+<script>
+    /*
+        Network Management Functions
+    */
+    function handleAddNetwork(){
+
+    }
+
+    function wildcardToSubnetMask(wildcard) {
+        var octets = wildcard.split('.');
+        var binary = '';
+        for (var i = 0; i < 4; i++) {
+            if (octets[i] === '*') {
+            binary += '00000000';
+            } else {
+            binary += parseInt(octets[i], 10).toString(2).padStart(8, '0');
+            }
+        }
+        var subnetBits = binary.replace(/0+$/, '').length;
+        var subnetMask = subnetBits === 32 ? '255.255.255.255' : (new Array(5).join('0') + parseInt(binary.substr(0, subnetBits), 2).toString(10)).slice(-3).split('').join('.');
+        return subnetMask;
+    }
+
+    function addNetwork(name, subnetMask) {
+        $.ajax({
+            url: "/api/gan/network/add",
+            type: "POST",
+            dataType: "json",
+            data: {
+                name: name,
+                subnetMask: subnetMask
+            },
+            success: function(response) {
+            console.log("Network added successfully:", response);
+            },
+            error: function(xhr, status, error) {
+            console.log("Error adding network:", error);
+            }
+        });
+    }
+</script>

+ 33 - 0
web/main.css

@@ -425,4 +425,37 @@ body{
 }
 .padding.statusDot{
     cursor: auto;
+}
+
+/*
+    Global Area Network
+    gan.html
+*/
+
+.gansnetworks {
+    display: flex;
+    flex-direction: row;
+}
+
+.ganstats {
+    width: 170px;
+}
+
+.ganlist {
+    flex-grow: 1;
+}
+
+@media screen and (max-width: 750px) {
+    .gansnetworks {
+        flex-direction: column;
+    }
+
+    .ganstats, .ganlist {
+        width: auto;
+    }
+    
+    .ganlist{
+        margin-top: 1em;
+        margin-bottom: 1em;
+    }
 }