| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201 |
- package interfaces
- import (
- "fmt"
- "net"
- "os"
- "os/exec"
- "path/filepath"
- "sort"
- "strings"
- "networktool/internal/config"
- "networktool/internal/model"
- )
- type Service struct {
- cfg config.Config
- }
- func New(cfg config.Config) *Service {
- return &Service{cfg: cfg}
- }
- func (s *Service) List() (model.InterfacesResponse, error) {
- items, err := s.listPhysicalInterfaces()
- if err != nil {
- return model.InterfacesResponse{}, err
- }
- management := s.detectManagement(items)
- for i := range items {
- items[i].IsManagement = items[i].SystemName == management
- if items[i].IsManagement {
- items[i].Name = "LAN2"
- items[i].Role = "control"
- } else {
- items[i].Name = "候选接口"
- items[i].Role = "business"
- }
- }
- suggested, requiresSelection := suggestTarget(items, management)
- for i := range items {
- items[i].IsSuggestedTarget = items[i].SystemName == suggested
- if items[i].IsSuggestedTarget {
- items[i].Name = "LAN1"
- }
- }
- return model.InterfacesResponse{
- ManagementInterface: management,
- SuggestedTargetInterface: suggested,
- RequiresTargetSelection: requiresSelection,
- Interfaces: items,
- }, nil
- }
- func (s *Service) EnsureMaintenanceAddress() error {
- items, err := s.listPhysicalInterfaces()
- if err != nil {
- return err
- }
- if len(items) == 0 {
- return fmt.Errorf("no physical ethernet interfaces found")
- }
- management := s.detectManagement(items)
- if management == "" {
- return fmt.Errorf("failed to detect management interface")
- }
- for _, item := range items {
- if item.SystemName != management {
- continue
- }
- for _, addr := range item.IPv4 {
- if addr.Address == s.cfg.MaintenanceIP {
- return nil
- }
- }
- break
- }
- cmd := exec.Command("ip", "addr", "add", s.cfg.MaintenanceCIDR, "dev", management)
- output, err := cmd.CombinedOutput()
- if err != nil {
- if strings.Contains(string(output), "File exists") {
- return nil
- }
- return fmt.Errorf("ip addr add failed on %s: %s", management, strings.TrimSpace(string(output)))
- }
- return nil
- }
- func (s *Service) listPhysicalInterfaces() ([]model.NetworkInterface, error) {
- ifaces, err := net.Interfaces()
- if err != nil {
- return nil, err
- }
- items := make([]model.NetworkInterface, 0)
- for _, iface := range ifaces {
- if !isPhysicalEthernet(iface.Name) {
- continue
- }
- item := model.NetworkInterface{
- SystemName: iface.Name,
- MAC: iface.HardwareAddr.String(),
- LinkUp: readLinkUp(iface.Name),
- Gateway: "",
- DNS: []string{},
- }
- item.IPv4 = readIPv4(iface)
- items = append(items, item)
- }
- sort.Slice(items, func(i, j int) bool { return items[i].SystemName < items[j].SystemName })
- return items, nil
- }
- func (s *Service) detectManagement(items []model.NetworkInterface) string {
- for _, item := range items {
- for _, addr := range item.IPv4 {
- if addr.Address == s.cfg.MaintenanceIP {
- return item.SystemName
- }
- }
- }
- if len(items) == 2 {
- return items[1].SystemName
- }
- if len(items) > 0 {
- return items[len(items)-1].SystemName
- }
- return ""
- }
- func suggestTarget(items []model.NetworkInterface, management string) (string, bool) {
- candidates := make([]string, 0)
- for _, item := range items {
- if item.SystemName != management {
- candidates = append(candidates, item.SystemName)
- }
- }
- if len(candidates) == 1 {
- return candidates[0], false
- }
- return "", len(candidates) > 1
- }
- func readIPv4(iface net.Interface) []model.IPv4Address {
- addrs, err := iface.Addrs()
- if err != nil {
- return nil
- }
- result := make([]model.IPv4Address, 0)
- for _, addr := range addrs {
- ipNet, ok := addr.(*net.IPNet)
- if !ok || ipNet.IP.To4() == nil {
- continue
- }
- prefix, _ := ipNet.Mask.Size()
- source := "unknown"
- if ipNet.IP.String() == "169.254.100.2" {
- source = "static"
- }
- result = append(result, model.IPv4Address{Address: ipNet.IP.String(), Prefix: prefix, Source: source})
- }
- return result
- }
- func isPhysicalEthernet(name string) bool {
- if name == "lo" {
- return false
- }
- for _, prefix := range []string{"docker", "br-", "veth", "virbr", "tun", "tap", "wg", "zt", "vmnet"} {
- if strings.HasPrefix(name, prefix) {
- return false
- }
- }
- devicePath := filepath.Join("/sys/class/net", name, "device")
- if _, err := os.Stat(devicePath); err != nil {
- return false
- }
- return true
- }
- func readLinkUp(name string) bool {
- carrierPath := filepath.Join("/sys/class/net", name, "carrier")
- data, err := os.ReadFile(carrierPath)
- if err == nil {
- return strings.TrimSpace(string(data)) == "1"
- }
- statePath := filepath.Join("/sys/class/net", name, "operstate")
- data, err = os.ReadFile(statePath)
- if err != nil {
- return false
- }
- return strings.TrimSpace(string(data)) == "up"
- }
|