| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222 |
- package configreader
- import (
- "bufio"
- "fmt"
- "net"
- "os"
- "os/exec"
- "path/filepath"
- "strings"
- "quickip/internal/model"
- "gopkg.in/yaml.v3"
- )
- type Service struct{}
- type configuredInterfaceValues struct {
- DHCP4 bool
- IP string
- Prefix int
- Gateway string
- 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.IP = configured.IP
- config.Prefix = configured.Prefix
- config.Gateway = configured.Gateway
- if configured.DNS != nil {
- config.DNS = configured.DNS
- }
- if config.Dhcp4 || config.IP == "" {
- ip, prefix := readInterfaceIPv4(iface)
- if ip != "" {
- config.IP = ip
- config.Prefix = prefix
- }
- }
- if config.Dhcp4 || config.Gateway == "" {
- config.Gateway = readGateway(interfaceName)
- }
- if config.Dhcp4 || len(config.DNS) == 0 {
- config.DNS = readDNS()
- }
- return config, nil
- }
- func readInterfaceIPv4(iface *net.Interface) (string, int) {
- addrs, err := iface.Addrs()
- if err != nil {
- return "", 0
- }
- for _, addr := range addrs {
- ipNet, ok := addr.(*net.IPNet)
- if !ok || ipNet.IP.To4() == nil {
- continue
- }
- prefix, _ := ipNet.Mask.Size()
- return ipNet.IP.String(), prefix
- }
- return "", 0
- }
- func readGateway(interfaceName string) string {
- cmd := exec.Command("ip", "route", "show", "dev", interfaceName)
- output, err := cmd.Output()
- if err != nil {
- return ""
- }
- for _, line := range strings.Split(string(output), "\n") {
- line = strings.TrimSpace(line)
- if !strings.HasPrefix(line, "default via ") {
- continue
- }
- parts := strings.Fields(line)
- if len(parts) >= 3 {
- return parts[2]
- }
- }
- return ""
- }
- func readDNS() []string {
- file, err := os.Open("/etc/resolv.conf")
- if err != nil {
- return []string{}
- }
- defer file.Close()
- result := make([]string, 0, 2)
- scanner := bufio.NewScanner(file)
- for scanner.Scan() {
- line := strings.TrimSpace(scanner.Text())
- if !strings.HasPrefix(line, "nameserver ") {
- continue
- }
- parts := strings.Fields(line)
- if len(parts) >= 2 {
- result = append(result, parts[1])
- }
- }
- 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.IP = ip.String()
- values.Prefix = prefix
- break
- }
- }
- 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)
- if strings.TrimSpace(to) == "default" && strings.TrimSpace(via) != "" {
- values.Gateway = strings.TrimSpace(via)
- break
- }
- }
- }
- if values.Gateway == "" {
- if gateway4, ok := entry["gateway4"].(string); ok {
- values.Gateway = strings.TrimSpace(gateway4)
- }
- }
- return values, values.DHCP4 || values.IP != "" || values.Gateway != "" || len(values.DNS) > 0
- }
- 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
- }
|