| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655 |
- package cli
- import (
- "fmt"
- "os/exec"
- "strconv"
- "strings"
- "proxmox-router/internal/auth"
- "proxmox-router/internal/dhcp"
- "proxmox-router/internal/dns"
- "proxmox-router/internal/firewall"
- "proxmox-router/internal/network"
- "proxmox-router/internal/routing"
- "proxmox-router/internal/system"
- )
- type Executor struct {
- authMgr *auth.Manager
- netMgr *network.Manager
- routingMgr *routing.RoutingManager
- fwMgr *firewall.FirewallManager
- dhcpMgr *dhcp.Manager
- dhcpServMgr *dhcp.ServerManager
- dnsMgr *dns.Manager
- }
- func NewExecutor(
- authMgr *auth.Manager,
- netMgr *network.Manager,
- routingMgr *routing.RoutingManager,
- fwMgr *firewall.FirewallManager,
- dhcpMgr *dhcp.Manager,
- dhcpServMgr *dhcp.ServerManager,
- dnsMgr *dns.Manager,
- ) *Executor {
- return &Executor{
- authMgr: authMgr,
- netMgr: netMgr,
- routingMgr: routingMgr,
- fwMgr: fwMgr,
- dhcpMgr: dhcpMgr,
- dhcpServMgr: dhcpServMgr,
- dnsMgr: dnsMgr,
- }
- }
- func (e *Executor) Execute(rawCmd string, user *auth.PublicUser) (string, bool) {
- cmd := strings.TrimSpace(rawCmd)
- if cmd == "" {
- return "", false
- }
- // Remove leading slash if present (RouterOS compatibility e.g. /ip route print)
- cmd = strings.TrimPrefix(cmd, "/")
- parts := strings.Fields(cmd)
- if len(parts) == 0 {
- return "", false
- }
- mainCmd := strings.ToLower(parts[0])
- switch mainCmd {
- case "quit", "exit":
- return ColorYellow + "Connection closed. Goodbye!\n" + ColorReset, true
- case "help", "?":
- return e.showHelp(), false
- case "clear":
- return "\033[2J\033[H", false
- case "ping":
- return e.handlePing(parts[1:]), false
- case "traceroute":
- return e.handleTraceroute(parts[1:]), false
- case "system":
- return e.handleSystem(parts[1:], user), false
- case "interface":
- return e.handleInterface(parts[1:], user), false
- case "ip":
- return e.handleIP(parts[1:], user), false
- case "user":
- return e.handleUser(parts[1:], user), false
- default:
- return fmt.Sprintf(ColorRed+"Unknown command or syntax error: %q. Type 'help' or '?' for command list.\n"+ColorReset, cmd), false
- }
- }
- func (e *Executor) handleInterface(parts []string, user *auth.PublicUser) string {
- if len(parts) == 0 || parts[0] == "print" || parts[0] == "pr" || parts[0] == "show" || parts[0] == "list" {
- ifaces, err := e.netMgr.ListInterfaces()
- if err != nil {
- return ColorRed + "Error listing interfaces: " + err.Error() + ColorReset + "\n"
- }
- tbl := NewTable("#", "NAME", "TYPE", "STATE", "MTU", "MAC", "IP ADDRESSES")
- for idx, iface := range ifaces {
- stateColor := ColorGreen
- if iface.State != "UP" {
- stateColor = ColorRed
- }
- stateStr := fmt.Sprintf("%s[%s]%s", stateColor, iface.State, ColorReset)
- var ips []string
- for _, a := range iface.IPv4 {
- ips = append(ips, a)
- }
- tbl.AddRow(
- fmt.Sprintf("%d", idx),
- ColorBold+iface.Name+ColorReset,
- iface.Type,
- stateStr,
- fmt.Sprintf("%d", iface.MTU),
- iface.MAC,
- strings.Join(ips, ", "),
- )
- }
- return tbl.Render()
- }
- if parts[0] == "set" {
- if user.Role == auth.RoleRead {
- return ColorRed + "Permission denied: read-only user cannot modify interface state\n" + ColorReset
- }
- kv := parseKeyValues(parts[1:])
- name := ""
- if len(parts) > 1 && !strings.Contains(parts[1], "=") {
- name = parts[1]
- }
- if n, ok := kv["name"]; ok {
- name = n
- }
- if name == "" {
- return ColorRed + "Usage: interface set <name> disabled=yes|no\n" + ColorReset
- }
- if dis, ok := kv["disabled"]; ok {
- up := strings.ToLower(dis) != "yes"
- if err := e.netMgr.SetInterfaceState(name, up); err != nil {
- return ColorRed + "Error: " + err.Error() + ColorReset + "\n"
- }
- return ColorGreen + fmt.Sprintf("Interface %s state updated\n", name) + ColorReset
- }
- }
- return ColorYellow + "Usage: interface print | interface set <name> disabled=yes|no\n" + ColorReset
- }
- func (e *Executor) handleIP(parts []string, user *auth.PublicUser) string {
- if len(parts) == 0 {
- return ColorYellow + "Usage: ip [address|route|dhcp-client|dhcp-server|firewall|dns] print\n" + ColorReset
- }
- sub := strings.ToLower(parts[0])
- switch sub {
- case "address", "addr":
- return e.handleIPAddress(parts[1:], user)
- case "route":
- return e.handleIPRoute(parts[1:], user)
- case "dhcp-client":
- return e.handleDHCPClient(parts[1:], user)
- case "dhcp-server":
- return e.handleDHCPServer(parts[1:], user)
- case "firewall":
- return e.handleFirewall(parts[1:], user)
- case "dns":
- return e.handleDNS(parts[1:], user)
- default:
- return ColorRed + fmt.Sprintf("Unknown ip subsystem: %q. Try 'ip address', 'ip route', 'ip firewall', etc.\n", sub) + ColorReset
- }
- }
- func (e *Executor) handleIPAddress(parts []string, user *auth.PublicUser) string {
- if len(parts) == 0 || parts[0] == "print" || parts[0] == "pr" || parts[0] == "show" {
- ifaces, err := e.netMgr.ListInterfaces()
- if err != nil {
- return ColorRed + "Error: " + err.Error() + ColorReset + "\n"
- }
- tbl := NewTable("#", "INTERFACE", "ADDRESS", "TYPE", "STATUS")
- idx := 0
- for _, iface := range ifaces {
- for _, ip := range iface.IPv4 {
- tbl.AddRow(
- fmt.Sprintf("%d", idx),
- ColorBold+iface.Name+ColorReset,
- ColorGreen+ip+ColorReset,
- iface.Type,
- iface.State,
- )
- idx++
- }
- }
- return tbl.Render()
- }
- if parts[0] == "add" {
- if user.Role == auth.RoleRead {
- return ColorRed + "Permission denied: read-only user cannot add IP address\n" + ColorReset
- }
- kv := parseKeyValues(parts[1:])
- addr := kv["address"]
- iface := kv["interface"]
- if addr == "" || iface == "" {
- return ColorYellow + "Usage: ip address add address=X.X.X.X/YY interface=<iface>\n" + ColorReset
- }
- if err := e.netMgr.AddInterfaceIP(iface, addr); err != nil {
- return ColorRed + "Failed to add address: " + err.Error() + ColorReset + "\n"
- }
- return ColorGreen + fmt.Sprintf("Assigned %s to %s successfully\n", addr, iface) + ColorReset
- }
- if parts[0] == "remove" || parts[0] == "del" {
- if user.Role == auth.RoleRead {
- return ColorRed + "Permission denied: read-only user cannot remove IP address\n" + ColorReset
- }
- kv := parseKeyValues(parts[1:])
- addr := kv["address"]
- iface := kv["interface"]
- if addr == "" || iface == "" {
- return ColorYellow + "Usage: ip address remove address=X.X.X.X/YY interface=<iface>\n" + ColorReset
- }
- if err := e.netMgr.DeleteInterfaceIP(iface, addr); err != nil {
- return ColorRed + "Failed to remove address: " + err.Error() + ColorReset + "\n"
- }
- return ColorGreen + fmt.Sprintf("Removed %s from %s successfully\n", addr, iface) + ColorReset
- }
- return ColorYellow + "Usage: ip address print | ip address add address=... interface=... | ip address remove address=... interface=...\n" + ColorReset
- }
- func (e *Executor) handleIPRoute(parts []string, user *auth.PublicUser) string {
- if len(parts) == 0 || parts[0] == "print" || parts[0] == "pr" || parts[0] == "show" {
- routes := e.routingMgr.ListRoutes()
- tbl := NewTable("#", "DST-ADDRESS", "GATEWAY", "INTERFACE", "METRIC", "TABLE", "FLAGS")
- for idx, r := range routes {
- flags := "DAC" // Dynamic Active Connected
- if r.Protocol == "static" {
- flags = "DAS" // Dynamic Active Static
- } else if r.Protocol == "dhcp" {
- flags = "DAD" // Dynamic Active DHCP
- }
- tbl.AddRow(
- fmt.Sprintf("%d", idx),
- ColorBold+r.DstAddress+ColorReset,
- r.Gateway,
- r.Interface,
- fmt.Sprintf("%d", r.Distance),
- r.RoutingTable,
- ColorGreen+flags+ColorReset,
- )
- }
- return tbl.Render()
- }
- if parts[0] == "add" {
- if user.Role == auth.RoleRead {
- return ColorRed + "Permission denied: read-only user cannot add routes\n" + ColorReset
- }
- kv := parseKeyValues(parts[1:])
- dst := kv["dst-address"]
- if dst == "" {
- dst = kv["dst"]
- }
- gw := kv["gateway"]
- if gw == "" {
- gw = kv["gw"]
- }
- dist := 1
- if dStr, ok := kv["distance"]; ok {
- if parsed, err := strconv.Atoi(dStr); err == nil && parsed > 0 {
- dist = parsed
- }
- }
- if dst == "" || gw == "" {
- return ColorYellow + "Usage: ip route add dst-address=X.X.X.X/YY gateway=Z.Z.Z.Z [distance=N]\n" + ColorReset
- }
- newRoute := routing.RouteItem{
- DstAddress: dst,
- Gateway: gw,
- Distance: dist,
- RoutingTable: "main",
- Active: true,
- }
- if err := e.routingMgr.SaveRoute(newRoute); err != nil {
- return ColorRed + "Failed to add route: " + err.Error() + ColorReset + "\n"
- }
- return ColorGreen + fmt.Sprintf("Added route to %s via %s (distance %d)\n", dst, gw, dist) + ColorReset
- }
- return ColorYellow + "Usage: ip route print | ip route add dst-address=... gateway=... [distance=...]\n" + ColorReset
- }
- func (e *Executor) handleDHCPClient(parts []string, user *auth.PublicUser) string {
- clients := e.dhcpMgr.ListClients()
- tbl := NewTable("#", "INTERFACE", "STATUS", "IP ADDRESS", "GATEWAY", "DHCP-SERVER", "EXPIRES")
- for idx, c := range clients {
- stColor := ColorYellow
- if c.Status == "bound" {
- stColor = ColorGreen
- } else if c.Status == "disabled" || c.Status == "stopped" {
- stColor = ColorRed
- }
- tbl.AddRow(
- fmt.Sprintf("%d", idx),
- ColorBold+c.Interface+ColorReset,
- stColor+c.Status+ColorReset,
- c.IPAddress,
- c.Gateway,
- c.DHCPServer,
- c.ExpiresIn,
- )
- }
- return tbl.Render()
- }
- func (e *Executor) handleDHCPServer(parts []string, user *auth.PublicUser) string {
- if len(parts) > 0 && parts[0] == "lease" {
- leases := e.dhcpServMgr.ListLeases()
- tbl := NewTable("#", "ADDRESS", "MAC-ADDRESS", "HOSTNAME", "EXPIRES-IN", "SERVER")
- for idx, l := range leases {
- tbl.AddRow(
- fmt.Sprintf("%d", idx),
- ColorGreen+l.Address+ColorReset,
- l.MACAddress,
- l.HostName,
- l.ExpiresIn,
- l.Server,
- )
- }
- return tbl.Render()
- }
- servers := e.dhcpServMgr.ListServers()
- tbl := NewTable("#", "INTERFACE", "SUBNET", "IP-POOL", "LEASE-TIME", "STATUS")
- for idx, s := range servers {
- st := ColorGreen + "running" + ColorReset
- if s.Disabled {
- st = ColorRed + "disabled" + ColorReset
- }
- tbl.AddRow(
- fmt.Sprintf("%d", idx),
- ColorBold+s.Interface+ColorReset,
- s.Network,
- s.AddressPool,
- s.LeaseTime,
- st,
- )
- }
- return tbl.Render()
- }
- func (e *Executor) handleFirewall(parts []string, user *auth.PublicUser) string {
- if len(parts) == 0 {
- return ColorYellow + "Usage: ip firewall [filter|nat|mangle] print\n" + ColorReset
- }
- sub := strings.ToLower(parts[0])
- switch sub {
- case "filter":
- rules := e.fwMgr.ListFilterRules()
- tbl := NewTable("#", "CHAIN", "ACTION", "PROTOCOL", "SRC-ADDRESS", "DST-ADDRESS", "IN-IFACE", "OUT-IFACE", "COMMENT")
- for idx, r := range rules {
- tbl.AddRow(
- fmt.Sprintf("%d", idx),
- r.Chain,
- ColorBold+r.Action+ColorReset,
- r.Protocol,
- r.SrcAddress,
- r.DstAddress,
- r.InInterface,
- r.OutInterface,
- ColorDim+r.Comment+ColorReset,
- )
- }
- return tbl.Render()
- case "nat":
- rules := e.fwMgr.ListNATRules()
- tbl := NewTable("#", "CHAIN", "ACTION", "OUT-INTERFACE", "TO-ADDRESS", "COMMENT")
- for idx, r := range rules {
- out := r.OutInterface
- if r.OutInterfaceList != "" {
- out = fmt.Sprintf("list:%s", r.OutInterfaceList)
- }
- tbl.AddRow(
- fmt.Sprintf("%d", idx),
- r.Chain,
- ColorGreen+r.Action+ColorReset,
- out,
- r.ToAddresses,
- ColorDim+r.Comment+ColorReset,
- )
- }
- return tbl.Render()
- default:
- return ColorYellow + "Usage: ip firewall filter print | ip firewall nat print\n" + ColorReset
- }
- }
- func (e *Executor) handleDNS(parts []string, user *auth.PublicUser) string {
- if len(parts) > 0 && parts[0] == "cache" {
- if len(parts) > 1 && parts[1] == "flush" {
- if user.Role == auth.RoleRead {
- return ColorRed + "Permission denied: read-only user cannot flush DNS cache\n" + ColorReset
- }
- if err := e.dnsMgr.FlushCache(); err != nil {
- return ColorRed + "Error flushing DNS cache: " + err.Error() + ColorReset + "\n"
- }
- return ColorGreen + "DNS Cache flushed successfully\n" + ColorReset
- }
- }
- settings := e.dnsMgr.GetSettings()
- var sb strings.Builder
- sb.WriteString(ColorCyan + ColorBold + "DNS Settings:\n" + ColorReset)
- sb.WriteString(fmt.Sprintf(" Upstream Servers : %s\n", strings.Join(settings.Servers, ", ")))
- sb.WriteString(fmt.Sprintf(" Dynamic Servers : %s\n", strings.Join(settings.DynamicServers, ", ")))
- sb.WriteString(fmt.Sprintf(" Domain : %s\n", settings.Domain))
- sb.WriteString(fmt.Sprintf(" Cache Size : %d entries\n", settings.CacheSize))
- sb.WriteString(fmt.Sprintf(" Allow Remote Reqs : %t\n", settings.AllowRemoteRequests))
- return sb.String()
- }
- func (e *Executor) handleUser(parts []string, user *auth.PublicUser) string {
- if len(parts) == 0 || parts[0] == "print" || parts[0] == "pr" {
- if user.Role != auth.RoleFull {
- return ColorRed + "Permission denied: administrative privileges required\n" + ColorReset
- }
- users := e.authMgr.ListUsers()
- tbl := NewTable("#", "USERNAME", "ROLE", "DISABLED", "COMMENT", "LAST LOGIN")
- for idx, u := range users {
- disStr := ColorGreen + "no" + ColorReset
- if u.Disabled {
- disStr = ColorRed + "yes" + ColorReset
- }
- last := ColorDim + "never" + ColorReset
- if !u.LastLoginAt.IsZero() {
- last = u.LastLoginAt.Format("2006-01-02 15:04")
- }
- tbl.AddRow(
- fmt.Sprintf("%d", idx),
- ColorBold+u.Username+ColorReset,
- ColorYellow+strings.ToUpper(string(u.Role))+ColorReset,
- disStr,
- u.Comment,
- last,
- )
- }
- return tbl.Render()
- }
- if parts[0] == "add" {
- if user.Role != auth.RoleFull {
- return ColorRed + "Permission denied: administrative privileges required\n" + ColorReset
- }
- kv := parseKeyValues(parts[1:])
- name := kv["name"]
- pass := kv["password"]
- role := auth.Role(kv["role"])
- if role == "" {
- role = auth.RoleRead
- }
- comment := kv["comment"]
- if name == "" || pass == "" {
- return ColorYellow + "Usage: user add name=<username> password=<password> [role=full|write|read] [comment=\"...\"]\n" + ColorReset
- }
- req := auth.CreateUserRequest{
- Username: name,
- Password: pass,
- Role: role,
- Comment: comment,
- }
- if err := e.authMgr.CreateUser(req); err != nil {
- return ColorRed + "Failed to create user: " + err.Error() + ColorReset + "\n"
- }
- return ColorGreen + fmt.Sprintf("User %q (%s) created successfully\n", name, role) + ColorReset
- }
- if parts[0] == "remove" || parts[0] == "del" {
- if user.Role != auth.RoleFull {
- return ColorRed + "Permission denied: administrative privileges required\n" + ColorReset
- }
- if len(parts) < 2 {
- return ColorYellow + "Usage: user remove <username>\n" + ColorReset
- }
- target := parts[1]
- if err := e.authMgr.DeleteUser(target, user.Username); err != nil {
- return ColorRed + "Failed to remove user: " + err.Error() + ColorReset + "\n"
- }
- return ColorGreen + fmt.Sprintf("User %q removed successfully\n", target) + ColorReset
- }
- return ColorYellow + "Usage: user print | user add name=... password=... [role=...] | user remove <username>\n" + ColorReset
- }
- func (e *Executor) handleSystem(parts []string, user *auth.PublicUser) string {
- if len(parts) == 0 {
- return ColorYellow + "Usage: system [resource|identity|reboot]\n" + ColorReset
- }
- sub := strings.ToLower(parts[0])
- switch sub {
- case "resource":
- st, err := system.GetStatus()
- if err != nil {
- return ColorRed + "Error getting system status: " + err.Error() + ColorReset + "\n"
- }
- var sb strings.Builder
- sb.WriteString(ColorCyan + ColorBold + "System Resources:\n" + ColorReset)
- sb.WriteString(fmt.Sprintf(" Appliance Hostname : %s\n", st.Hostname))
- sb.WriteString(fmt.Sprintf(" Operating System : %s\n", st.OS))
- sb.WriteString(fmt.Sprintf(" Kernel Version : %s\n", st.Kernel))
- sb.WriteString(fmt.Sprintf(" Uptime : %s\n", st.UptimeHuman))
- sb.WriteString(fmt.Sprintf(" CPU Cores : %d cores\n", st.CPUCores))
- sb.WriteString(fmt.Sprintf(" CPU Utilization : %.1f%%\n", st.CPUPct))
- sb.WriteString(fmt.Sprintf(" Load Average : %s\n", strings.Join(st.CPULoad, ", ")))
- sb.WriteString(fmt.Sprintf(" Total Memory : %d MB\n", st.MemTotalMB))
- sb.WriteString(fmt.Sprintf(" Used Memory : %d MB (%.1f%%)\n", st.MemUsedMB, st.MemUsagePct))
- sb.WriteString(fmt.Sprintf(" Default Gateway : %s\n", st.DefaultGateway))
- return sb.String()
- case "identity":
- st, _ := system.GetStatus()
- if len(parts) > 1 && parts[1] == "set" {
- if user.Role == auth.RoleRead {
- return ColorRed + "Permission denied: read-only user cannot change identity\n" + ColorReset
- }
- kv := parseKeyValues(parts[2:])
- newName := kv["name"]
- if newName == "" {
- return ColorYellow + "Usage: system identity set name=<new-name>\n" + ColorReset
- }
- _ = exec.Command("hostnamectl", "set-hostname", newName).Run()
- return ColorGreen + fmt.Sprintf("Identity set to %q\n", newName) + ColorReset
- }
- return fmt.Sprintf(ColorCyan+"name: %s%s\n", st.Hostname, ColorReset)
- case "reboot":
- if user.Role != auth.RoleFull {
- return ColorRed + "Permission denied: full administrative privileges required to reboot\n" + ColorReset
- }
- _ = exec.Command("/sbin/reboot").Start()
- return ColorRed + ColorBold + "System is going down for reboot NOW...\n" + ColorReset
- default:
- return ColorYellow + "Usage: system resource print | system identity print | system reboot\n" + ColorReset
- }
- }
- func (e *Executor) handlePing(args []string) string {
- if len(args) == 0 {
- return ColorYellow + "Usage: ping <host> [count=N]\n" + ColorReset
- }
- host := args[0]
- count := "4"
- for _, a := range args[1:] {
- if strings.HasPrefix(a, "count=") {
- count = strings.TrimPrefix(a, "count=")
- }
- }
- cmd := exec.Command("ping", "-c", count, "-W", "2", host)
- out, err := cmd.CombinedOutput()
- if err != nil {
- return fmt.Sprintf("%s%s%s\n", ColorRed, string(out), ColorReset)
- }
- return string(out)
- }
- func (e *Executor) handleTraceroute(args []string) string {
- if len(args) == 0 {
- return ColorYellow + "Usage: traceroute <host>\n" + ColorReset
- }
- host := args[0]
- cmd := exec.Command("traceroute", "-m", "15", "-w", "1", host)
- out, err := cmd.CombinedOutput()
- if err != nil {
- // Fallback to ping/tracepath if traceroute not found
- return fmt.Sprintf("%s%s%s\n", ColorRed, string(out), ColorReset)
- }
- return string(out)
- }
- func (e *Executor) showHelp() string {
- var sb strings.Builder
- sb.WriteString(ColorCyan + ColorBold + "\n--- DebTik RouterOS CLI Commands Reference ---\n" + ColorReset)
- sb.WriteString(ColorBold + "Interfaces:\n" + ColorReset)
- sb.WriteString(" interface print List all interfaces, MACs and IPs\n")
- sb.WriteString(" interface set <name> disabled=yes|no Enable or disable interface\n")
- sb.WriteString(ColorBold + "IP & Routing:\n" + ColorReset)
- sb.WriteString(" ip address print List configured IP addresses\n")
- sb.WriteString(" ip address add address=X/Y interface=DEV Add IP address to interface\n")
- sb.WriteString(" ip address remove address=X/Y interface=DEV Remove IP address\n")
- sb.WriteString(" ip route print Display routing table\n")
- sb.WriteString(" ip route add dst-address=X/Y gateway=Z Add static route\n")
- sb.WriteString(ColorBold + "DHCP & Services:\n" + ColorReset)
- sb.WriteString(" ip dhcp-client print Show DHCP client leases\n")
- sb.WriteString(" ip dhcp-server print Show DHCP server instances\n")
- sb.WriteString(" ip dhcp-server lease print Show active client leases\n")
- sb.WriteString(" ip dns print Show DNS cache and forwarders\n")
- sb.WriteString(" ip dns cache flush Flush DNS cache\n")
- sb.WriteString(ColorBold + "Firewall:\n" + ColorReset)
- sb.WriteString(" ip firewall filter print List Filter rules\n")
- sb.WriteString(" ip firewall nat print List NAT rules\n")
- sb.WriteString(ColorBold + "Users & Management:\n" + ColorReset)
- sb.WriteString(" user print List system users (Admin)\n")
- sb.WriteString(" user add name=X password=Y role=full|write Add local user\n")
- sb.WriteString(" user remove <name> Delete local user\n")
- sb.WriteString(" password Change current password\n")
- sb.WriteString(ColorBold + "System & Utilities:\n" + ColorReset)
- sb.WriteString(" system resource print CPU, RAM, Uptime and load\n")
- sb.WriteString(" system identity print Show router hostname\n")
- sb.WriteString(" system identity set name=<name> Set router hostname\n")
- sb.WriteString(" system reboot Reboot system\n")
- sb.WriteString(" ping <host> [count=N] Send ICMP echo requests\n")
- sb.WriteString(" traceroute <host> Trace route to target\n")
- sb.WriteString(" clear Clear screen\n")
- sb.WriteString(" exit / quit Disconnect from session\n")
- sb.WriteString("\n")
- return sb.String()
- }
- func parseKeyValues(parts []string) map[string]string {
- res := make(map[string]string)
- for _, p := range parts {
- if eq := strings.Index(p, "="); eq != -1 {
- k := strings.ToLower(strings.TrimSpace(p[:eq]))
- v := strings.TrimSpace(p[eq+1:])
- v = strings.Trim(v, "\"")
- res[k] = v
- }
- }
- return res
- }
|