mirror of
https://github.com/daeuniverse/dae.git
synced 2024-12-23 01:14:46 +07:00
296 lines
6.5 KiB
Go
296 lines
6.5 KiB
Go
/*
|
|
* SPDX-License-Identifier: AGPL-3.0-only
|
|
* Copyright (c) 2022-2024, daeuniverse Organization <dae@v2raya.org>
|
|
*/
|
|
|
|
package cmd
|
|
|
|
import (
|
|
"bytes"
|
|
"io/ioutil"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/vishvananda/netlink"
|
|
"github.com/spf13/cobra"
|
|
"github.com/mholt/archiver/v3"
|
|
"github.com/shirou/gopsutil/v4/net"
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
var (
|
|
sysdumpCmd = &cobra.Command{
|
|
Use: "sysdump",
|
|
Short: "To dump up system network config",
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
dumpNetworkInfo()
|
|
},
|
|
}
|
|
)
|
|
|
|
func dumpNetworkInfo() {
|
|
tempDir, err := ioutil.TempDir("", "sysdump")
|
|
if err != nil {
|
|
fmt.Printf("Failed to create temp directory: %v\n", err)
|
|
return
|
|
}
|
|
defer os.RemoveAll(tempDir)
|
|
|
|
dumpRouting(tempDir)
|
|
dumpNetInterfaces(tempDir)
|
|
dumpSysctl(tempDir)
|
|
dumpNetfilter(tempDir)
|
|
dumpIPTables(tempDir)
|
|
|
|
tarFile := fmt.Sprintf("dae-sysdump.%d.tar.gz",time.Now().Unix())
|
|
if err := archiver.Archive([]string{tempDir}, tarFile); err != nil {
|
|
fmt.Printf("Failed to create tar archive: %v\n", err)
|
|
return
|
|
}
|
|
|
|
fmt.Printf("System network information collected and saved to %s\n", tarFile)
|
|
}
|
|
|
|
|
|
// Translate scope enum into semantic words
|
|
func scopeToString(scope netlink.Scope) string {
|
|
switch scope {
|
|
case unix.RT_SCOPE_UNIVERSE:
|
|
return "universe"
|
|
case unix.RT_SCOPE_SITE:
|
|
return "site"
|
|
case unix.RT_SCOPE_LINK:
|
|
return "link"
|
|
case unix.RT_SCOPE_HOST:
|
|
return "host"
|
|
case unix.RT_SCOPE_NOWHERE:
|
|
return "nowhere"
|
|
default:
|
|
return "unknown"
|
|
}
|
|
}
|
|
|
|
|
|
// Translate protocol enum into semantic words
|
|
func protocolToString(proto int) string {
|
|
switch proto {
|
|
case unix.RTPROT_BABEL:
|
|
return "babel"
|
|
case unix.RTPROT_BGP:
|
|
return "bgp"
|
|
case unix.RTPROT_BIRD:
|
|
return "bird"
|
|
case unix.RTPROT_BOOT:
|
|
return "boot"
|
|
case unix.RTPROT_DHCP:
|
|
return "dhcp"
|
|
case unix.RTPROT_DNROUTED:
|
|
return "dnrouted"
|
|
case unix.RTPROT_EIGRP:
|
|
return "eigrp"
|
|
case unix.RTPROT_GATED:
|
|
return "gated"
|
|
case unix.RTPROT_ISIS:
|
|
return "isis"
|
|
case unix.RTPROT_KERNEL:
|
|
return "kernel"
|
|
case unix.RTPROT_MROUTED:
|
|
return "mrouted"
|
|
case unix.RTPROT_MRT:
|
|
return "mrt"
|
|
case unix.RTPROT_NTK:
|
|
return "ntk"
|
|
case unix.RTPROT_OSPF:
|
|
return "ospf"
|
|
case unix.RTPROT_RA:
|
|
return "ra"
|
|
case unix.RTPROT_REDIRECT:
|
|
return "redirect"
|
|
case unix.RTPROT_RIP:
|
|
return "rip"
|
|
case unix.RTPROT_STATIC:
|
|
return "static"
|
|
case unix.RTPROT_UNSPEC:
|
|
return "unspec"
|
|
case unix.RTPROT_XORP:
|
|
return "xorp"
|
|
case unix.RTPROT_ZEBRA:
|
|
return "zebra"
|
|
default:
|
|
return "unknown"
|
|
}
|
|
}
|
|
|
|
// Translate route.type enum into semantic words
|
|
func typeToString(typ int) string {
|
|
switch typ {
|
|
case unix.RTN_UNSPEC:
|
|
return "unspec"
|
|
case unix.RTN_UNICAST:
|
|
return "unicast"
|
|
case unix.RTN_LOCAL:
|
|
return "local"
|
|
case unix.RTN_BROADCAST:
|
|
return "broadcast"
|
|
case unix.RTN_ANYCAST:
|
|
return "anycast"
|
|
case unix.RTN_MULTICAST:
|
|
return "multicast"
|
|
case unix.RTN_BLACKHOLE:
|
|
return "blackhole"
|
|
case unix.RTN_UNREACHABLE:
|
|
return "unreachable"
|
|
case unix.RTN_PROHIBIT:
|
|
return "prohibit"
|
|
case unix.RTN_THROW:
|
|
return "throw"
|
|
case unix.RTN_NAT:
|
|
return "nat"
|
|
case unix.RTN_XRESOLVE:
|
|
return "xresolve"
|
|
default:
|
|
return "unknown"
|
|
}
|
|
}
|
|
|
|
|
|
func dumpRouting(outputDir string) {
|
|
routes, err := netlink.RouteList(nil, netlink.FAMILY_ALL)
|
|
if err != nil {
|
|
fmt.Printf("Failed to get routing table: %v\n", err)
|
|
return
|
|
}
|
|
|
|
var buffer bytes.Buffer
|
|
buffer.WriteString("Routing:\n")
|
|
for _, route := range routes {
|
|
link, err := netlink.LinkByIndex(route.LinkIndex)
|
|
if err != nil {
|
|
fmt.Printf("Failed to get link by index: %v\n", err)
|
|
continue
|
|
}
|
|
ifaceName := link.Attrs().Name
|
|
|
|
routeStr := ""
|
|
if route.Dst == nil {
|
|
routeStr += "default"
|
|
} else {
|
|
routeStr += route.Dst.String()
|
|
}
|
|
|
|
if route.Gw != nil {
|
|
routeStr += fmt.Sprintf(" via %s", route.Gw.String())
|
|
}
|
|
|
|
routeStr += fmt.Sprintf(" dev %s", ifaceName)
|
|
|
|
if route.Scope != 0 {
|
|
routeStr += fmt.Sprintf(" scope %s", scopeToString(route.Scope))
|
|
}
|
|
|
|
if route.Protocol != 0 {
|
|
routeStr += fmt.Sprintf(" proto %s", protocolToString(route.Protocol))
|
|
}
|
|
|
|
if route.Type != 0 {
|
|
routeStr += fmt.Sprintf(" type %s", typeToString(route.Type))
|
|
}
|
|
|
|
if route.Flags != 0 {
|
|
routeStr += fmt.Sprintf(" flags %d", route.Flags)
|
|
}
|
|
|
|
buffer.WriteString(routeStr + "\n")
|
|
}
|
|
err = ioutil.WriteFile(filepath.Join(outputDir, "routing.txt"), buffer.Bytes(), 0644)
|
|
if err != nil {
|
|
fmt.Printf("Failed to write routing information to file: %v\n", err)
|
|
}
|
|
}
|
|
|
|
func dumpNetInterfaces(outputDir string) {
|
|
interfaces, err := net.Interfaces()
|
|
if err != nil {
|
|
fmt.Printf("Failed to get network interfaces: %v\n", err)
|
|
return
|
|
}
|
|
|
|
var buffer bytes.Buffer
|
|
buffer.WriteString("Network Interfaces:\n")
|
|
for _, iface := range interfaces {
|
|
buffer.WriteString(fmt.Sprintf("Name: %s, MTU: %d, HardwareAddr: %s, Flags: %v\n",
|
|
iface.Name, iface.MTU, iface.HardwareAddr, iface.Flags))
|
|
for _, addr := range iface.Addrs {
|
|
buffer.WriteString(fmt.Sprintf(" Address: %s\n", addr.Addr))
|
|
}
|
|
}
|
|
|
|
ioutil.WriteFile(filepath.Join(outputDir, "interfaces.txt"), buffer.Bytes(), 0644)
|
|
}
|
|
|
|
|
|
func dumpSysctl(outputDir string) {
|
|
sysctlPath := "/proc/sys/net"
|
|
var buffer bytes.Buffer
|
|
|
|
err := filepath.Walk(sysctlPath, func(path string, info os.FileInfo, err error) error {
|
|
if err != nil {
|
|
fmt.Printf("Fail in filepath.Walk: %v\n", err)
|
|
}
|
|
|
|
if !info.IsDir() {
|
|
value, err := ioutil.ReadFile(path)
|
|
if err != nil {
|
|
fmt.Printf("Fail in filepath.Walk: %v\n", err)
|
|
}
|
|
|
|
relativePath := strings.TrimPrefix(path, sysctlPath+"/")
|
|
buffer.WriteString(fmt.Sprintf("%-60s = %s\n", relativePath, string(value)))
|
|
}
|
|
return nil
|
|
})
|
|
|
|
if err != nil {
|
|
fmt.Printf("Failed to get sysctl settings: %v\n", err)
|
|
}
|
|
|
|
ioutil.WriteFile(filepath.Join(outputDir, "sysctl.txt"), buffer.Bytes(), 0644)
|
|
}
|
|
|
|
func dumpNetfilter(outputDir string) {
|
|
cmd := exec.Command("nft", "list", "ruleset")
|
|
output, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
fmt.Printf("Failed to get nftables ruleset: %v\n", err)
|
|
return
|
|
}
|
|
|
|
ioutil.WriteFile(filepath.Join(outputDir, "nftables.txt"), output, 0644)
|
|
}
|
|
|
|
func dumpIPTables(outputDir string) {
|
|
iptables := exec.Command("iptables-save", "-c")
|
|
output, err := iptables.CombinedOutput()
|
|
if err != nil {
|
|
fmt.Printf("Failed to get iptables: %v\n", err)
|
|
} else {
|
|
ioutil.WriteFile(filepath.Join(outputDir, "iptables.txt"), output, 0644)
|
|
}
|
|
|
|
ip6tables := exec.Command("ip6tables-save","-c")
|
|
output, err = ip6tables.CombinedOutput()
|
|
if err != nil {
|
|
fmt.Printf("Failed to get ip6tables: %v\n", err)
|
|
} else {
|
|
ioutil.WriteFile(filepath.Join(outputDir, "ip6tables.txt"), output, 0644)
|
|
}
|
|
}
|
|
|
|
func init() {
|
|
rootCmd.AddCommand(sysdumpCmd)
|
|
}
|