| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220 |
- package configreader
- import (
- "fmt"
- "net"
- "os"
- "os/exec"
- "path/filepath"
- "strings"
- "networktool/internal/model"
- "gopkg.in/yaml.v3"
- )
- type Service struct{}
- type configuredInterfaceValues struct {
- DHCP4 bool
- Addresses []model.InterfaceAddressConfig
- Routes []model.InterfaceRouteConfig
- DNS []string
- }
- func New() *Service { return &Service{} }
- func (s *Service) Read(interfaceName string) (model.InterfaceConfig, error) {
- iface, err := net.InterfaceByName(interfaceName)
- if err != nil {
- return model.InterfaceConfig{}, err
- }
- config := model.InterfaceConfig{Interface: interfaceName, DNS: []string{}}
- configured := readConfiguredInterfaceValues(interfaceName)
- config.Dhcp4 = configured.DHCP4
- config.Addresses = configured.Addresses
- config.Routes = configured.Routes
- if configured.DNS != nil {
- config.DNS = configured.DNS
- }
- if config.Dhcp4 || len(config.Addresses) == 0 {
- addresses := readInterfaceIPv4(iface)
- if len(addresses) > 0 {
- config.Addresses = addresses
- }
- }
- if len(config.Addresses) > 0 {
- config.IP = config.Addresses[0].IP
- config.Prefix = config.Addresses[0].Prefix
- }
- if config.Dhcp4 || len(config.Routes) == 0 {
- config.Routes = readRoutes(interfaceName)
- }
- for _, route := range config.Routes {
- if route.To == "default" {
- config.Gateway = route.Via
- break
- }
- }
- return config, nil
- }
- func readInterfaceIPv4(iface *net.Interface) []model.InterfaceAddressConfig {
- addrs, err := iface.Addrs()
- if err != nil {
- return nil
- }
- result := make([]model.InterfaceAddressConfig, 0)
- for _, addr := range addrs {
- ipNet, ok := addr.(*net.IPNet)
- if !ok || ipNet.IP.To4() == nil {
- continue
- }
- prefix, _ := ipNet.Mask.Size()
- result = append(result, model.InterfaceAddressConfig{IP: ipNet.IP.String(), Prefix: prefix})
- }
- return result
- }
- func readRoutes(interfaceName string) []model.InterfaceRouteConfig {
- cmd := exec.Command("ip", "route", "show", "dev", interfaceName)
- output, err := cmd.Output()
- if err != nil {
- return nil
- }
- result := make([]model.InterfaceRouteConfig, 0)
- for _, line := range strings.Split(string(output), "\n") {
- line = strings.TrimSpace(line)
- if line == "" {
- continue
- }
- parts := strings.Fields(line)
- if len(parts) >= 3 && parts[0] == "default" && parts[1] == "via" {
- result = append(result, model.InterfaceRouteConfig{To: "default", Via: parts[2]})
- continue
- }
- if len(parts) >= 3 && strings.Contains(parts[0], "/") && parts[1] == "via" {
- result = append(result, model.InterfaceRouteConfig{To: parts[0], Via: parts[2]})
- }
- }
- return result
- }
- func readConfiguredInterfaceValues(interfaceName string) configuredInterfaceValues {
- files, err := filepath.Glob("/etc/netplan/*.yaml")
- if err != nil || len(files) == 0 {
- return configuredInterfaceValues{}
- }
- for _, filePath := range files {
- data, err := os.ReadFile(filePath)
- if err != nil {
- continue
- }
- var raw map[string]any
- if err := yaml.Unmarshal(data, &raw); err != nil {
- continue
- }
- values, ok := findInterfaceValues(raw, interfaceName)
- if ok {
- return values
- }
- }
- return configuredInterfaceValues{}
- }
- func findInterfaceValues(raw map[string]any, interfaceName string) (configuredInterfaceValues, bool) {
- network, ok := raw["network"].(map[string]any)
- if !ok {
- return configuredInterfaceValues{}, false
- }
- ethernets, ok := network["ethernets"].(map[string]any)
- if !ok {
- return configuredInterfaceValues{}, false
- }
- entry, ok := ethernets[interfaceName].(map[string]any)
- if !ok {
- return configuredInterfaceValues{}, false
- }
- values := configuredInterfaceValues{}
- if dhcp4, ok := entry["dhcp4"].(bool); ok {
- values.DHCP4 = dhcp4
- }
- addresses, ok := entry["addresses"].([]any)
- if ok {
- for _, item := range addresses {
- text, ok := item.(string)
- if !ok || strings.TrimSpace(text) == "" {
- continue
- }
- ip, ipNet, err := net.ParseCIDR(text)
- if err != nil || ip == nil || ip.To4() == nil {
- continue
- }
- prefix, _ := ipNet.Mask.Size()
- values.Addresses = append(values.Addresses, model.InterfaceAddressConfig{IP: ip.String(), Prefix: prefix})
- }
- }
- if nameservers, ok := entry["nameservers"].(map[string]any); ok {
- values.DNS = anyToStringSlice(nameservers["addresses"])
- }
- if routes, ok := entry["routes"].([]any); ok {
- for _, item := range routes {
- route, ok := item.(map[string]any)
- if !ok {
- continue
- }
- to, _ := route["to"].(string)
- via, _ := route["via"].(string)
- to = strings.TrimSpace(to)
- via = strings.TrimSpace(via)
- if to != "" && via != "" {
- values.Routes = append(values.Routes, model.InterfaceRouteConfig{To: to, Via: via})
- }
- }
- }
- if !hasDefaultRoute(values.Routes) {
- if gateway4, ok := entry["gateway4"].(string); ok {
- gateway4 = strings.TrimSpace(gateway4)
- if gateway4 != "" {
- values.Routes = append(values.Routes, model.InterfaceRouteConfig{To: "default", Via: gateway4})
- }
- }
- }
- return values, values.DHCP4 || len(values.Addresses) > 0 || len(values.Routes) > 0 || len(values.DNS) > 0
- }
- func hasDefaultRoute(routes []model.InterfaceRouteConfig) bool {
- for _, route := range routes {
- if route.To == "default" {
- return true
- }
- }
- return false
- }
- func anyToStringSlice(value any) []string {
- switch typed := value.(type) {
- case []string:
- return append([]string(nil), typed...)
- case []any:
- result := make([]string, 0, len(typed))
- for _, item := range typed {
- text, ok := item.(string)
- if ok && strings.TrimSpace(text) != "" {
- result = append(result, text)
- }
- }
- return result
- default:
- return []string{}
- }
- }
- func MustRead(interfaceName string) model.InterfaceConfig {
- config, err := New().Read(interfaceName)
- if err != nil {
- panic(fmt.Sprintf("read interface config failed: %v", err))
- }
- return config
- }
|