2026-04-13 09:46:02 +03:00
|
|
|
package network
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
|
|
|
|
"os/exec"
|
|
|
|
|
"strings"
|
|
|
|
|
)
|
|
|
|
|
|
2026-04-15 11:38:26 +03:00
|
|
|
// LinkDown sets admin state down without deconfiguring (ip link set <name> down).
|
|
|
|
|
func LinkDown(name string) error {
|
|
|
|
|
out, err := exec.Command("ip", "link", "set", name, "down").CombinedOutput()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("ip link set down %s: %s", name, strings.TrimSpace(string(out)))
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// LinkUp sets admin state up without re-running ifup (ip link set <name> up).
|
|
|
|
|
func LinkUp(name string) error {
|
|
|
|
|
out, err := exec.Command("ip", "link", "set", name, "up").CombinedOutput()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("ip link set up %s: %s", name, strings.TrimSpace(string(out)))
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// IfDown brings an interface down via ifdown --force.
|
2026-04-13 09:46:02 +03:00
|
|
|
func IfDown(name string) error {
|
|
|
|
|
out, err := exec.Command("ifdown", "--force", name).CombinedOutput()
|
|
|
|
|
if err != nil {
|
2026-04-15 11:38:26 +03:00
|
|
|
return fmt.Errorf("ifdown %s: %s", name, strings.TrimSpace(string(out)))
|
2026-04-13 09:46:02 +03:00
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-15 11:38:26 +03:00
|
|
|
// IfUp brings an interface up via ifup --force.
|
2026-04-13 09:46:02 +03:00
|
|
|
func IfUp(name string) error {
|
2026-04-15 11:38:26 +03:00
|
|
|
out, err := exec.Command("ifup", "--force", name).CombinedOutput()
|
2026-04-13 09:46:02 +03:00
|
|
|
if err != nil {
|
2026-04-15 11:38:26 +03:00
|
|
|
return fmt.Errorf("ifup %s: %s", name, strings.TrimSpace(string(out)))
|
2026-04-13 09:46:02 +03:00
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// IfRestart brings an interface down then up.
|
|
|
|
|
func IfRestart(name string) error {
|
2026-04-15 11:38:26 +03:00
|
|
|
_ = IfDown(name)
|
2026-04-13 09:46:02 +03:00
|
|
|
return IfUp(name)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ApplyPending merges pending configs into /etc/network/interfaces,
|
|
|
|
|
// writes the file, and restarts affected interfaces.
|
|
|
|
|
// Returns a per-interface error map (nil key = write error).
|
|
|
|
|
func ApplyPending() map[string]error {
|
|
|
|
|
errs := map[string]error{}
|
|
|
|
|
|
|
|
|
|
pending := GetAllPending()
|
|
|
|
|
if len(pending) == 0 {
|
|
|
|
|
return errs
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Read current file config
|
|
|
|
|
configs, err := ParseConfig()
|
|
|
|
|
if err != nil {
|
|
|
|
|
errs["__parse__"] = err
|
|
|
|
|
return errs
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Merge
|
|
|
|
|
for name, cfg := range pending {
|
|
|
|
|
configs[name] = cfg
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Write file
|
|
|
|
|
if err := WriteConfig(configs); err != nil {
|
|
|
|
|
errs["__write__"] = err
|
|
|
|
|
return errs
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Restart each changed interface
|
|
|
|
|
for name := range pending {
|
|
|
|
|
if name == "lo" {
|
|
|
|
|
ClearPendingConfig(name)
|
|
|
|
|
continue
|
|
|
|
|
}
|
2026-04-13 12:40:49 +03:00
|
|
|
// For VLAN interfaces ensure the kernel interface exists before ifup.
|
|
|
|
|
if IsVLAN(name) {
|
|
|
|
|
if err := EnsureVLANExists(name); err != nil {
|
|
|
|
|
errs[name] = err
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-04-13 09:46:02 +03:00
|
|
|
_ = IfDown(name)
|
|
|
|
|
if cfg := configs[name]; cfg != nil && cfg.Auto {
|
|
|
|
|
if err := IfUp(name); err != nil {
|
|
|
|
|
errs[name] = err
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ClearPendingConfig(name)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return errs
|
|
|
|
|
}
|