configreader.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. package configreader
  2. import (
  3. "fmt"
  4. "net"
  5. "os"
  6. "os/exec"
  7. "path/filepath"
  8. "strings"
  9. "nettool/internal/model"
  10. "gopkg.in/yaml.v3"
  11. )
  12. type Service struct{}
  13. type configuredInterfaceValues struct {
  14. DHCP4 bool
  15. Addresses []model.InterfaceAddressConfig
  16. Routes []model.InterfaceRouteConfig
  17. DNS []string
  18. }
  19. func New() *Service { return &Service{} }
  20. func (s *Service) Read(interfaceName string) (model.InterfaceConfig, error) {
  21. iface, err := net.InterfaceByName(interfaceName)
  22. if err != nil {
  23. return model.InterfaceConfig{}, err
  24. }
  25. config := model.InterfaceConfig{Interface: interfaceName, DNS: []string{}}
  26. configured := readConfiguredInterfaceValues(interfaceName)
  27. config.Dhcp4 = configured.DHCP4
  28. config.Addresses = configured.Addresses
  29. config.Routes = configured.Routes
  30. if configured.DNS != nil {
  31. config.DNS = configured.DNS
  32. }
  33. if config.Dhcp4 || len(config.Addresses) == 0 {
  34. addresses := readInterfaceIPv4(iface)
  35. if len(addresses) > 0 {
  36. config.Addresses = addresses
  37. }
  38. }
  39. if len(config.Addresses) > 0 {
  40. config.IP = config.Addresses[0].IP
  41. config.Prefix = config.Addresses[0].Prefix
  42. }
  43. if config.Dhcp4 || len(config.Routes) == 0 {
  44. config.Routes = readRoutes(interfaceName)
  45. }
  46. for _, route := range config.Routes {
  47. if route.To == "default" {
  48. config.Gateway = route.Via
  49. break
  50. }
  51. }
  52. if config.Addresses == nil {
  53. config.Addresses = []model.InterfaceAddressConfig{}
  54. }
  55. if config.Routes == nil {
  56. config.Routes = []model.InterfaceRouteConfig{}
  57. }
  58. if config.DNS == nil {
  59. config.DNS = []string{}
  60. }
  61. return config, nil
  62. }
  63. func readInterfaceIPv4(iface *net.Interface) []model.InterfaceAddressConfig {
  64. addrs, err := iface.Addrs()
  65. if err != nil {
  66. return nil
  67. }
  68. result := make([]model.InterfaceAddressConfig, 0)
  69. for _, addr := range addrs {
  70. ipNet, ok := addr.(*net.IPNet)
  71. if !ok || ipNet.IP.To4() == nil {
  72. continue
  73. }
  74. prefix, _ := ipNet.Mask.Size()
  75. result = append(result, model.InterfaceAddressConfig{IP: ipNet.IP.String(), Prefix: prefix})
  76. }
  77. return result
  78. }
  79. func readRoutes(interfaceName string) []model.InterfaceRouteConfig {
  80. cmd := exec.Command("ip", "route", "show", "dev", interfaceName)
  81. output, err := cmd.Output()
  82. if err != nil {
  83. return nil
  84. }
  85. result := make([]model.InterfaceRouteConfig, 0)
  86. for _, line := range strings.Split(string(output), "\n") {
  87. line = strings.TrimSpace(line)
  88. if line == "" {
  89. continue
  90. }
  91. parts := strings.Fields(line)
  92. if len(parts) >= 3 && parts[0] == "default" && parts[1] == "via" {
  93. result = append(result, model.InterfaceRouteConfig{To: "default", Via: parts[2]})
  94. continue
  95. }
  96. if len(parts) >= 3 && strings.Contains(parts[0], "/") && parts[1] == "via" {
  97. result = append(result, model.InterfaceRouteConfig{To: parts[0], Via: parts[2]})
  98. }
  99. }
  100. return result
  101. }
  102. func readConfiguredInterfaceValues(interfaceName string) configuredInterfaceValues {
  103. files, err := filepath.Glob("/etc/netplan/*.yaml")
  104. if err != nil || len(files) == 0 {
  105. return configuredInterfaceValues{}
  106. }
  107. for _, filePath := range files {
  108. data, err := os.ReadFile(filePath)
  109. if err != nil {
  110. continue
  111. }
  112. var raw map[string]any
  113. if err := yaml.Unmarshal(data, &raw); err != nil {
  114. continue
  115. }
  116. values, ok := findInterfaceValues(raw, interfaceName)
  117. if ok {
  118. return values
  119. }
  120. }
  121. return configuredInterfaceValues{}
  122. }
  123. func findInterfaceValues(raw map[string]any, interfaceName string) (configuredInterfaceValues, bool) {
  124. network, ok := raw["network"].(map[string]any)
  125. if !ok {
  126. return configuredInterfaceValues{}, false
  127. }
  128. ethernets, ok := network["ethernets"].(map[string]any)
  129. if !ok {
  130. return configuredInterfaceValues{}, false
  131. }
  132. entry, ok := ethernets[interfaceName].(map[string]any)
  133. if !ok {
  134. return configuredInterfaceValues{}, false
  135. }
  136. values := configuredInterfaceValues{}
  137. if dhcp4, ok := entry["dhcp4"].(bool); ok {
  138. values.DHCP4 = dhcp4
  139. }
  140. addresses, ok := entry["addresses"].([]any)
  141. if ok {
  142. for _, item := range addresses {
  143. text, ok := item.(string)
  144. if !ok || strings.TrimSpace(text) == "" {
  145. continue
  146. }
  147. ip, ipNet, err := net.ParseCIDR(text)
  148. if err != nil || ip == nil || ip.To4() == nil {
  149. continue
  150. }
  151. prefix, _ := ipNet.Mask.Size()
  152. values.Addresses = append(values.Addresses, model.InterfaceAddressConfig{IP: ip.String(), Prefix: prefix})
  153. }
  154. }
  155. if nameservers, ok := entry["nameservers"].(map[string]any); ok {
  156. values.DNS = anyToStringSlice(nameservers["addresses"])
  157. }
  158. if routes, ok := entry["routes"].([]any); ok {
  159. for _, item := range routes {
  160. route, ok := item.(map[string]any)
  161. if !ok {
  162. continue
  163. }
  164. to, _ := route["to"].(string)
  165. via, _ := route["via"].(string)
  166. to = strings.TrimSpace(to)
  167. via = strings.TrimSpace(via)
  168. if to != "" && via != "" {
  169. values.Routes = append(values.Routes, model.InterfaceRouteConfig{To: to, Via: via})
  170. }
  171. }
  172. }
  173. if !hasDefaultRoute(values.Routes) {
  174. if gateway4, ok := entry["gateway4"].(string); ok {
  175. gateway4 = strings.TrimSpace(gateway4)
  176. if gateway4 != "" {
  177. values.Routes = append(values.Routes, model.InterfaceRouteConfig{To: "default", Via: gateway4})
  178. }
  179. }
  180. }
  181. return values, values.DHCP4 || len(values.Addresses) > 0 || len(values.Routes) > 0 || len(values.DNS) > 0
  182. }
  183. func hasDefaultRoute(routes []model.InterfaceRouteConfig) bool {
  184. for _, route := range routes {
  185. if route.To == "default" {
  186. return true
  187. }
  188. }
  189. return false
  190. }
  191. func anyToStringSlice(value any) []string {
  192. switch typed := value.(type) {
  193. case []string:
  194. return append([]string(nil), typed...)
  195. case []any:
  196. result := make([]string, 0, len(typed))
  197. for _, item := range typed {
  198. text, ok := item.(string)
  199. if ok && strings.TrimSpace(text) != "" {
  200. result = append(result, text)
  201. }
  202. }
  203. return result
  204. default:
  205. return []string{}
  206. }
  207. }
  208. func MustRead(interfaceName string) model.InterfaceConfig {
  209. config, err := New().Read(interfaceName)
  210. if err != nil {
  211. panic(fmt.Sprintf("read interface config failed: %v", err))
  212. }
  213. return config
  214. }