package network import ( "fmt" "os" "os/exec" "strconv" "strings" ) // IsVLAN reports whether name is a VLAN interface (e.g. "eth0.100"). func IsVLAN(name string) bool { idx := strings.LastIndex(name, ".") if idx <= 0 { return false } suffix := name[idx+1:] if suffix == "" { return false } _, err := strconv.Atoi(suffix) return err == nil } // VLANParent returns the parent interface (e.g. "eth0.100" → "eth0"). func VLANParent(name string) string { idx := strings.LastIndex(name, ".") if idx < 0 { return "" } return name[:idx] } // VLANId returns the VLAN ID (e.g. "eth0.100" → 100). func VLANId(name string) int { idx := strings.LastIndex(name, ".") if idx < 0 { return 0 } id, _ := strconv.Atoi(name[idx+1:]) return id } // EnsureVLANExists creates the VLAN interface in the kernel if it doesn't exist. func EnsureVLANExists(name string) error { if _, err := os.Stat("/sys/class/net/" + name); err == nil { return nil } parent := VLANParent(name) id := VLANId(name) if parent == "" || id == 0 { return fmt.Errorf("invalid VLAN name: %s", name) } out, err := exec.Command("ip", "link", "add", "link", parent, "name", name, "type", "vlan", "id", strconv.Itoa(id)).CombinedOutput() if err != nil { return fmt.Errorf("ip link add %s: %s", name, strings.TrimSpace(string(out))) } return nil } // DeleteVLAN brings down and removes a VLAN interface and its /etc/network/interfaces stanza. func DeleteVLAN(name string) error { _ = IfDown(name) if _, err := os.Stat("/sys/class/net/" + name); err == nil { out, err2 := exec.Command("ip", "link", "delete", name).CombinedOutput() if err2 != nil { return fmt.Errorf("ip link delete %s: %s", name, strings.TrimSpace(string(out))) } } configs, err := ParseConfig() if err != nil { return err } if _, ok := configs[name]; ok { delete(configs, name) return WriteConfig(configs) } return nil }