1/* 2** Zabbix 3** Copyright (C) 2001-2021 Zabbix SIA 4** 5** This program is free software; you can redistribute it and/or modify 6** it under the terms of the GNU General Public License as published by 7** the Free Software Foundation; either version 2 of the License, or 8** (at your option) any later version. 9** 10** This program is distributed in the hope that it will be useful, 11** but WITHOUT ANY WARRANTY; without even the implied warranty of 12** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13** GNU General Public License for more details. 14** 15** You should have received a copy of the GNU General Public License 16** along with this program; if not, write to the Free Software 17** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18**/ 19 20package netif 21 22import ( 23 "encoding/json" 24 "errors" 25 "fmt" 26 "net" 27 "unsafe" 28 29 "golang.org/x/sys/windows" 30 "zabbix.com/pkg/plugin" 31 "zabbix.com/pkg/win32" 32) 33 34const ( 35 errorEmptyIpTable = "Empty IP address table returned." 36 errorCannotFindIf = "Cannot obtain network interface information." 37 guidStringLen = 38 38) 39 40func (p *Plugin) nToIP(addr uint32) net.IP { 41 b := (*[4]byte)(unsafe.Pointer(&addr)) 42 return net.IPv4(b[0], b[1], b[2], b[3]) 43} 44 45func (p *Plugin) getIpAddrTable() (addrs []win32.MIB_IPADDRROW, err error) { 46 var ipTable *win32.MIB_IPADDRTABLE 47 var sizeIn, sizeOut uint32 48 if sizeOut, err = win32.GetIpAddrTable(nil, 0, false); err != nil { 49 return 50 } 51 if sizeOut == 0 { 52 return 53 } 54 for sizeOut > sizeIn { 55 sizeIn = sizeOut 56 buf := make([]byte, sizeIn) 57 ipTable = (*win32.MIB_IPADDRTABLE)(unsafe.Pointer(&buf[0])) 58 if sizeOut, err = win32.GetIpAddrTable(ipTable, sizeIn, false); err != nil { 59 return 60 } 61 } 62 return (*win32.RGMIB_IPADDRROW)(unsafe.Pointer(&ipTable.Table[0]))[:ipTable.NumEntries:ipTable.NumEntries], nil 63} 64 65func (p *Plugin) getIfRowByIP(ipaddr string, ifs []win32.MIB_IF_ROW2) (row *win32.MIB_IF_ROW2) { 66 var ip net.IP 67 if ip = net.ParseIP(ipaddr); ip == nil { 68 return 69 } 70 71 var err error 72 var ips []win32.MIB_IPADDRROW 73 if ips, err = p.getIpAddrTable(); err != nil { 74 return 75 } 76 77 for i := range ips { 78 if ip.Equal(p.nToIP(ips[i].Addr)) { 79 for j := range ifs { 80 if ifs[j].InterfaceIndex == ips[i].Index { 81 return &ifs[j] 82 } 83 } 84 } 85 } 86 return 87} 88 89func (p *Plugin) getGuidString(winGuid win32.GUID) string { 90 return fmt.Sprintf("{%08X-%04X-%04X-%02X-%02X}", winGuid.Data1, winGuid.Data2, winGuid.Data3, winGuid.Data4[:2], winGuid.Data4[2:]) 91} 92 93func (p *Plugin) getNetStats(networkIf string, statName string, dir dirFlag) (result uint64, err error) { 94 var ifTable *win32.MIB_IF_TABLE2 95 if ifTable, err = win32.GetIfTable2(); err != nil { 96 return 97 } 98 defer win32.FreeMibTable(ifTable) 99 100 ifs := (*win32.RGMIB_IF_ROW2)(unsafe.Pointer(&ifTable.Table[0]))[:ifTable.NumEntries:ifTable.NumEntries] 101 102 var row *win32.MIB_IF_ROW2 103 for i := range ifs { 104 if len(networkIf) == guidStringLen && networkIf[0] == '{' && networkIf[guidStringLen-1] == '}' && 105 networkIf == p.getGuidString(ifs[i].InterfaceGuid) || 106 networkIf == windows.UTF16ToString(ifs[i].Description[:]) { 107 row = &ifs[i] 108 break 109 } 110 } 111 if row == nil { 112 row = p.getIfRowByIP(networkIf, ifs) 113 } 114 if row == nil { 115 return 0, errors.New(errorCannotFindIf) 116 } 117 118 var value uint64 119 switch statName { 120 case "bytes": 121 if dir&dirIn != 0 { 122 value += row.InOctets 123 } 124 if dir&dirOut != 0 { 125 value += row.OutOctets 126 } 127 case "packets": 128 if dir&dirIn != 0 { 129 value += row.InUcastPkts + row.InNUcastPkts 130 } 131 if dir&dirOut != 0 { 132 value += row.OutUcastPkts + row.OutNUcastPkts 133 } 134 case "errors": 135 if dir&dirIn != 0 { 136 value += row.InErrors 137 } 138 if dir&dirOut != 0 { 139 value += row.OutErrors 140 } 141 case "dropped": 142 if dir&dirIn != 0 { 143 value += row.InDiscards + row.InUnknownProtos 144 } 145 if dir&dirOut != 0 { 146 value += row.OutDiscards 147 } 148 default: 149 return 0, errors.New(errorInvalidSecondParam) 150 } 151 return value, nil 152} 153 154func (p *Plugin) getDevDiscovery() (devices []msgIfDiscovery, err error) { 155 var table *win32.MIB_IF_TABLE2 156 if table, err = win32.GetIfTable2(); err != nil { 157 return 158 } 159 defer win32.FreeMibTable(table) 160 161 devices = make([]msgIfDiscovery, 0, table.NumEntries) 162 rows := (*win32.RGMIB_IF_ROW2)(unsafe.Pointer(&table.Table[0]))[:table.NumEntries:table.NumEntries] 163 for i := range rows { 164 guid := p.getGuidString(rows[i].InterfaceGuid) 165 devices = append(devices, msgIfDiscovery{windows.UTF16ToString(rows[i].Description[:]), &guid}) 166 } 167 return 168} 169 170func (p *Plugin) getIfType(iftype uint32) string { 171 switch iftype { 172 case windows.IF_TYPE_OTHER: 173 return "Other" 174 case windows.IF_TYPE_ETHERNET_CSMACD: 175 return "Ethernet" 176 case windows.IF_TYPE_ISO88025_TOKENRING: 177 return "Token Ring" 178 case windows.IF_TYPE_PPP: 179 return "PPP" 180 case windows.IF_TYPE_SOFTWARE_LOOPBACK: 181 return "Software Loopback" 182 case windows.IF_TYPE_ATM: 183 return "ATM" 184 case windows.IF_TYPE_IEEE80211: 185 return "IEEE 802.11 Wireless" 186 case windows.IF_TYPE_TUNNEL: 187 return "Tunnel type encapsulation" 188 case windows.IF_TYPE_IEEE1394: 189 return "IEEE 1394 Firewire" 190 default: 191 return "unknown" 192 } 193} 194 195func (p *Plugin) getAdminStatus(status int32) string { 196 switch status { 197 case 0: 198 return "disabled" 199 case 1: 200 return "enabled" 201 default: 202 return "unknown" 203 } 204} 205 206func (p *Plugin) getIP(index uint32, ips []win32.MIB_IPADDRROW) string { 207 for i := range ips { 208 if ips[i].Index == index { 209 return fmt.Sprintf(" %-15s", p.nToIP(ips[i].Addr)) 210 } 211 } 212 return " -" 213} 214 215func (p *Plugin) getDevList() (devices string, err error) { 216 var ifTable *win32.MIB_IF_TABLE2 217 if ifTable, err = win32.GetIfTable2(); err != nil { 218 return 219 } 220 defer win32.FreeMibTable(ifTable) 221 ifs := (*win32.RGMIB_IF_ROW2)(unsafe.Pointer(&ifTable.Table[0]))[:ifTable.NumEntries:ifTable.NumEntries] 222 223 var ips []win32.MIB_IPADDRROW 224 if ips, err = p.getIpAddrTable(); err != nil { 225 return 226 } 227 228 for i := range ifs { 229 devices += fmt.Sprintf("%-25s", p.getIfType(ifs[i].Type)) 230 devices += fmt.Sprintf(" %-8s", p.getAdminStatus(ifs[i].AdminStatus)) 231 devices += p.getIP(ifs[i].InterfaceIndex, ips) 232 devices += fmt.Sprintf(" %s\n", windows.UTF16ToString(ifs[i].Description[:])) 233 } 234 235 return 236} 237 238// Export - 239func (p *Plugin) Export(key string, params []string, ctx plugin.ContextProvider) (result interface{}, err error) { 240 var direction dirFlag 241 var mode string 242 243 switch key { 244 case "net.if.discovery": 245 if len(params) > 0 { 246 return nil, errors.New(errorParametersNotAllowed) 247 } 248 var devices []msgIfDiscovery 249 if devices, err = p.getDevDiscovery(); err != nil { 250 return 251 } 252 var b []byte 253 if b, err = json.Marshal(devices); err != nil { 254 return 255 } 256 return string(b), nil 257 case "net.if.list": 258 if len(params) > 0 { 259 return nil, errors.New(errorParametersNotAllowed) 260 } 261 return p.getDevList() 262 case "net.if.in": 263 direction = dirIn 264 case "net.if.out": 265 direction = dirOut 266 case "net.if.total": 267 direction = dirIn | dirOut 268 default: 269 /* SHOULD_NEVER_HAPPEN */ 270 return nil, errors.New(errorUnsupportedMetric) 271 } 272 273 if len(params) < 1 || params[0] == "" { 274 return nil, errors.New(errorEmptyIfName) 275 } 276 277 if len(params) > 2 { 278 return nil, errors.New(errorTooManyParams) 279 } 280 281 if len(params) == 2 && params[1] != "" { 282 mode = params[1] 283 } else { 284 mode = "bytes" 285 } 286 287 return p.getNetStats(params[0], mode, direction) 288} 289 290func init() { 291 plugin.RegisterMetrics(&impl, "NetIf", 292 "net.if.list", "Returns a list of network interfaces in text format.", 293 "net.if.in", "Returns incoming traffic statistics on network interface.", 294 "net.if.out", "Returns outgoing traffic statistics on network interface.", 295 "net.if.total", "Returns sum of incoming and outgoing traffic statistics on network interface.", 296 "net.if.discovery", "Returns list of network interfaces. Used for low-level discovery.") 297 298} 299