Signed-off-by: Hadi <[email protected]>
This commit is contained in:
Hadi
2026-08-13 00:58:37 +02:00
commit 59b31e5ca3
103 changed files with 8577 additions and 0 deletions
+122
View File
@@ -0,0 +1,122 @@
package network
import "strings"
// Device is one row of `nmcli device status`.
type Device struct {
Name string
Type string // e.g. "wifi", "ethernet", "wireguard", "loopback"
State string // e.g. "connected", "disconnected", "unavailable"
Connection string // active connection profile name, empty if none
}
func (d Device) Connected() bool {
return strings.HasPrefix(d.State, "connected")
}
// ListDevices returns every network device known to NetworkManager.
func ListDevices() ([]Device, error) {
rows, err := runTerse("DEVICE,TYPE,STATE,CONNECTION", "device", "status")
if err != nil {
return nil, err
}
devices := make([]Device, 0, len(rows))
for _, row := range rows {
devices = append(devices, Device{
Name: field(row, 0),
Type: field(row, 1),
State: field(row, 2),
Connection: field(row, 3),
})
}
return devices, nil
}
// DeviceConnect asks NetworkManager to bring a device up using whichever
// saved connection profile fits it best. Unlike VPNUp (which reactivates a
// specific, already-known connection by name), this works even right after
// a disconnect, when `device status` no longer reports which connection
// profile was last active on that device.
func DeviceConnect(device string) error {
_, err := run("device", "connect", device)
return err
}
// DeviceIP4 returns the device's primary private IPv4 address without its
// CIDR suffix (e.g. "192.168.1.54"), or "" if the device has none.
func DeviceIP4(device string) (string, error) {
out, err := getField("IP4.ADDRESS", "device", "show", device)
if err != nil {
return "", err
}
if out == "" {
return "", nil
}
// nmcli -g can return multiple addresses newline-separated; keep the first.
if i := strings.IndexByte(out, '\n'); i >= 0 {
out = out[:i]
}
if i := strings.IndexByte(out, '/'); i >= 0 {
out = out[:i]
}
return out, nil
}
// DeviceDetail holds everything settuings shows on the "network info" screen
// for a connected device: addressing, gateway, DNS and hardware details.
type DeviceDetail struct {
HWAddr string
MTU string
IPv4 string // address + CIDR, e.g. "192.168.1.54/24"
Gateway4 string
DNS4 []string
IPv6 []string
Gateway6 string
DNS6 []string
}
// DeviceDetails fetches full addressing/hardware info for device via
// `nmcli device show`. Unlike the tabular nmcli commands elsewhere in this
// package, `device show` prints one "KEY:VALUE" line per property (with
// "KEY[n]" for multi-valued ones) and does not escape colons inside values,
// so it's parsed differently from runTerse.
func DeviceDetails(device string) (DeviceDetail, error) {
out, err := run("-t", "-f",
"GENERAL.HWADDR,GENERAL.MTU,IP4.ADDRESS,IP4.GATEWAY,IP4.DNS,IP6.ADDRESS,IP6.GATEWAY,IP6.DNS",
"device", "show", device)
if err != nil {
return DeviceDetail{}, err
}
var d DeviceDetail
for _, line := range strings.Split(strings.TrimRight(out, "\n"), "\n") {
key, value, ok := strings.Cut(line, ":")
if !ok {
continue
}
if i := strings.IndexByte(key, '['); i >= 0 {
key = key[:i] // strip the "[n]" multi-value index
}
switch key {
case "GENERAL.HWADDR":
d.HWAddr = value
case "GENERAL.MTU":
d.MTU = value
case "IP4.ADDRESS":
if d.IPv4 == "" {
d.IPv4 = value
}
case "IP4.GATEWAY":
d.Gateway4 = value
case "IP4.DNS":
d.DNS4 = append(d.DNS4, value)
case "IP6.ADDRESS":
d.IPv6 = append(d.IPv6, value)
case "IP6.GATEWAY":
d.Gateway6 = value
case "IP6.DNS":
d.DNS6 = append(d.DNS6, value)
}
}
return d, nil
}