feat(networks): Live-Traffic-Zähler in System-Interfaces-Card
GET /system/interfaces liefert jetzt rx_bytes/tx_bytes/rx_packets/ tx_packets/rx_drop/tx_drop aus /proc/net/dev. Die System-Interfaces- Card in Netzwerk → Interfaces zeigt die Werte als Mini-Tabelle mit 10s-Refetch; Hover auf den Bytes-Werten zeigt Paketzähler + Drops. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -765,13 +765,58 @@ type addrInfo struct {
|
||||
}
|
||||
|
||||
type interfaceInfo struct {
|
||||
IfIndex int `json:"ifindex"`
|
||||
IfName string `json:"ifname"`
|
||||
Flags []string `json:"flags"`
|
||||
MTU int `json:"mtu"`
|
||||
LinkType string `json:"link_type,omitempty"`
|
||||
Address string `json:"address,omitempty"`
|
||||
AddrInfo []addrInfo `json:"addr_info"`
|
||||
IfIndex int `json:"ifindex"`
|
||||
IfName string `json:"ifname"`
|
||||
Flags []string `json:"flags"`
|
||||
MTU int `json:"mtu"`
|
||||
LinkType string `json:"link_type,omitempty"`
|
||||
Address string `json:"address,omitempty"`
|
||||
AddrInfo []addrInfo `json:"addr_info"`
|
||||
RxBytes int64 `json:"rx_bytes"`
|
||||
TxBytes int64 `json:"tx_bytes"`
|
||||
RxPackets int64 `json:"rx_packets"`
|
||||
TxPackets int64 `json:"tx_packets"`
|
||||
RxDrop int64 `json:"rx_drop"`
|
||||
TxDrop int64 `json:"tx_drop"`
|
||||
}
|
||||
|
||||
type netDevStat struct {
|
||||
RxBytes, TxBytes int64
|
||||
RxPackets, TxPackets int64
|
||||
RxDrop, TxDrop int64
|
||||
}
|
||||
|
||||
// readNetDevStats parses /proc/net/dev and returns per-interface counters.
|
||||
// Missing or unreadable: returns empty map (caller gets zero-value stats).
|
||||
func readNetDevStats() map[string]netDevStat {
|
||||
out := map[string]netDevStat{}
|
||||
data, err := os.ReadFile("/proc/net/dev")
|
||||
if err != nil {
|
||||
return out
|
||||
}
|
||||
scanner := bufio.NewScanner(strings.NewReader(string(data)))
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
idx := strings.Index(line, ":")
|
||||
if idx < 0 {
|
||||
continue
|
||||
}
|
||||
name := strings.TrimSpace(line[:idx])
|
||||
fields := strings.Fields(line[idx+1:])
|
||||
if len(fields) < 12 {
|
||||
continue
|
||||
}
|
||||
p := func(s string) int64 { n, _ := strconv.ParseInt(s, 10, 64); return n }
|
||||
out[name] = netDevStat{
|
||||
RxBytes: p(fields[0]),
|
||||
RxPackets: p(fields[1]),
|
||||
RxDrop: p(fields[3]),
|
||||
TxBytes: p(fields[8]),
|
||||
TxPackets: p(fields[9]),
|
||||
TxDrop: p(fields[11]),
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Interfaces enumerates the kernel-side network interfaces using
|
||||
@@ -788,16 +833,24 @@ func (h *SystemHandler) Interfaces(c *gin.Context) {
|
||||
response.OK(c, gin.H{"interfaces": []interfaceInfo{}})
|
||||
return
|
||||
}
|
||||
devStats := readNetDevStats()
|
||||
out := make([]interfaceInfo, 0, len(ifaces))
|
||||
for _, ifc := range ifaces {
|
||||
st := devStats[ifc.Name]
|
||||
info := interfaceInfo{
|
||||
IfIndex: ifc.Index,
|
||||
IfName: ifc.Name,
|
||||
MTU: ifc.MTU,
|
||||
Address: ifc.HardwareAddr.String(),
|
||||
LinkType: classifyLinkType(ifc),
|
||||
Flags: flagsToList(ifc.Flags),
|
||||
AddrInfo: []addrInfo{},
|
||||
IfIndex: ifc.Index,
|
||||
IfName: ifc.Name,
|
||||
MTU: ifc.MTU,
|
||||
Address: ifc.HardwareAddr.String(),
|
||||
LinkType: classifyLinkType(ifc),
|
||||
Flags: flagsToList(ifc.Flags),
|
||||
AddrInfo: []addrInfo{},
|
||||
RxBytes: st.RxBytes,
|
||||
TxBytes: st.TxBytes,
|
||||
RxPackets: st.RxPackets,
|
||||
TxPackets: st.TxPackets,
|
||||
RxDrop: st.RxDrop,
|
||||
TxDrop: st.TxDrop,
|
||||
}
|
||||
addrs, err := ifc.Addrs()
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user