1 /*----------------------------------------------------------------------------*/
2 /* Xymon RRD handler module. */
3 /* */
4 /* Copyright (C) 2005-2011 Henrik Storner <henrik@hswn.dk> */
5 /* */
6 /* This program is released under the GNU General Public License (GPL), */
7 /* version 2. See the file "COPYING" for details. */
8 /* */
9 /*----------------------------------------------------------------------------*/
10
11 static char ifstat_rcsid[] = "$Id: do_ifstat.c 7847 2015-12-16 15:13:03Z jccleaver $";
12
13 static char *ifstat_params[] = { "DS:bytesSent:DERIVE:600:0:U",
14 "DS:bytesReceived:DERIVE:600:0:U",
15 NULL };
16 static void *ifstat_tpl = NULL;
17
18
19 /* eth0 Link encap: */
20 /* RX bytes: 1829192 (265.8 MiB) TX bytes: 1827320 (187.7 MiB */
21 static const char *ifstat_linux_exprs[] = {
22 "^([a-z0-9]+(_[0-9]+)?:*|lo:?)\\s",
23 "^\\s+RX bytes:([0-9]+) .*TX bytes.([0-9]+) ",
24 "^\\s+RX packets\\s+[0-9]+\\s+bytes\\s+([0-9]+) ",
25 "^\\s+TX packets\\s+[0-9]+\\s+bytes\\s+([0-9]+) "
26 };
27
28 /* Name Mtu Network Address Ipkts Ierrs Idrop Ibytes Opkts Oerrs Obytes Coll */
29 /* em0 1500 <Link#1> 14:da:e9:d2:10:b8 505128976 54 0 610016288902 294395839 14 290951791879 0 */
30 /* tun0 1500 <Link#3> tun0 0 0 0 0 5 0 436 0 */
31 /* Note: FreeBSD 9 and 10 have a blank column for "Address" when the interface doesn't have a MAC address */
32 static const char *ifstat_freebsd_exprs[] = {
33 "^([a-z0-9]+)\\s+\\d+\\s+<Link#\\d+>\\s+.*\\s+\\d+\\s+[0-9-]+\\s+[0-9-]+\\s+(\\d+)\\s+\\d+\\s+[0-9-]+\\s+(\\d+)\\s+[0-9-]+"
34 };
35
36 /* Name Mtu Network Address Ipkts Ierrs Idrop Ibytes Opkts Oerrs Obytes Coll */
37 /* bge0 1500 192.168.X.X 192.168.X.X 29292829 - - 1130285651 26543376 - 2832025203 - */
38 static const char *ifstat_freebsdV8_exprs[] = {
39 "^([a-z0-9]+)\\s+\\d+\\s+[0-9.\\/]+\\s+[0-9.]+\\s+\\d+\\s+[0-9-]+\\s+[0-9-]+\\s+(\\d+)\\s+\\d+\\s+[0-9-]+\\s+(\\d+)\\s+[0-9-]+"
40 };
41
42 /* Name MTU Network IP Ibytes Obytes */
43 /* lnc0 1500 172.16.10.0/24 172.16.10.151 1818 1802 */
44 static const char *ifstat_openbsd_exprs[] = {
45 "^([a-z0-9]+)\\s+\\d+\\s+[0-9.\\/]+\\s+[0-9.]+\\s+(\\d+)\\s+(\\d+)"
46 };
47
48 /* Name MTU Network IP Ibytes Obytes */
49 /* lnc0 1500 172.16.10.0/24 172.16.10.151 1818 1802 */
50 static const char *ifstat_netbsd_exprs[] = {
51 "^([a-z0-9]+)\\s+\\d+\\s+[0-9.\\/]+\\s+[0-9.]+\\s+(\\d+)\\s+(\\d+)"
52 };
53
54 /*
55 Name Mtu Network Address Ipkts Ierrs Ibytes Opkts Oerrs Obytes Coll
56 en0 1500 fe80::20d:9 fe80::20d:93ff:fe 2013711826 - 2131205566781 331648829 - 41815551289 -
57 en0 1500 130.223.20/24 130.223.20.20 2013711826 - 2131205566781 331648829 - 41815551289 -
58 */
59 static const char *ifstat_darwin_exprs[] = {
60 "^([a-z0-9]+)\\s+\\d+\\s+[0-9.\\/]+\\s+[0-9.]+\\s+\\d+\\s+[0-9-]+\\s+(\\d+)\\s+\\d+\\s+[0-9-]+\\s+(\\d+)\\s+[0-9-]+"
61 };
62
63 /* dmfe:0:dmfe0:obytes64 107901705585 */
64 /* dmfe:0:dmfe0:rbytes64 1224808818952 */
65 /* dmfe:1:dmfe1:obytes64 0 */
66 /* dmfe:1:dmfe1:rbytes64 0 */
67 static const char *ifstat_solaris_exprs[] = {
68 "^[a-z0-9]+:\\d+:([a-z0-9]+):obytes64\\s+(\\d+)",
69 "^[a-z0-9]+:\\d+:([a-z0-9]+):rbytes64\\s+(\\d+)"
70 };
71
72 /*
73 ETHERNET STATISTICS (ent0) :
74 Device Type: 2-Port 10/100/1000 Base-TX PCI-X Adapter (14108902)
75 Hardware Address: 00:11:25:e6:0d:36
76 Elapsed Time: 45 days 20 hours 18 minutes 41 seconds
77
78 Transmit Statistics: Receive Statistics:
79 -------------------- -------------------
80 Packets: 1652404 Packets: 768800
81 Bytes: 1966314449 Bytes: 78793615
82 */
83 static const char *ifstat_aix_exprs[] = {
84 "^ETHERNET STATISTICS \\(([a-z0-9]+)\\) :",
85 "^Bytes:\\s+(\\d+)\\s+Bytes:\\s+(\\d+)"
86 };
87
88
89 /* (lines dropped)
90 PPA Number = 0
91 Description = lan0 Hewlett-Packard LAN Interface Hw Rev 0
92 Type (value) = ethernet-csmacd(6)
93 MTU Size = 1500
94 Operation Status (value) = up(1)
95 Inbound Octets = 3111235429
96 Outbound Octets = 3892111463
97 */
98 static const char *ifstat_hpux_exprs[] = {
99 "^PPA Number\\s+= (\\d+)",
100 "^Inbound Octets\\s+= (\\d+)",
101 "^Outbound Octets\\s+= (\\d+)",
102 };
103
104 /*
105 Name Mtu Network Address Ipkts Ierrs Opkts Oerrs Coll
106 net0 1500 195.75.9 10.1.1.2 13096313 0 12257642 0 0
107 lo0 8232 127 127.0.0.1 26191 0 26191 0 0
108 Attention, theses numbers are packets, not bytes !
109 */
110 static const char *ifstat_sco_sv_exprs[] = {
111 "^([a-z]+[0-9]+)\\s+[0-9]+\\s+[0-9.]+\\s+[0-9.]+\\s+([0-9]+)\\s+[0-9]+\\s+([0-9]+)\\s+"
112 };
113
114 /* IP Ibytes Obytes */
115 /* 192.168.0.1 1818 1802 */
116 static const char *ifstat_bbwin_exprs[] = {
117 "^([a-zA-Z0-9.:]+)\\s+([0-9]+)\\s+([0-9]+)"
118 };
119
do_ifstat_rrd(char * hostname,char * testname,char * classname,char * pagepaths,char * msg,time_t tstamp)120 int do_ifstat_rrd(char *hostname, char *testname, char *classname, char *pagepaths, char *msg, time_t tstamp)
121 {
122 static int pcres_compiled = 0;
123 static pcre **ifstat_linux_pcres = NULL;
124 static pcre **ifstat_freebsd_pcres = NULL;
125 static pcre **ifstat_freebsdV8_pcres = NULL;
126 static pcre **ifstat_openbsd_pcres = NULL;
127 static pcre **ifstat_netbsd_pcres = NULL;
128 static pcre **ifstat_darwin_pcres = NULL;
129 static pcre **ifstat_solaris_pcres = NULL;
130 static pcre **ifstat_aix_pcres = NULL;
131 static pcre **ifstat_hpux_pcres = NULL;
132 static pcre **ifstat_sco_sv_pcres = NULL;
133 static pcre **ifstat_bbwin_pcres = NULL;
134
135 enum ostype_t ostype;
136 char *datapart = msg;
137 char *bol, *eoln, *ifname, *rxstr, *txstr, *dummy;
138 int dmatch;
139
140 void *xmh;
141 pcre *ifname_filter_pcre = NULL;
142
143 xmh = hostinfo(hostname);
144 if (xmh) {
145 char *ifname_filter_expr = xmh_item(xmh, XMH_INTERFACES);
146 if (ifname_filter_expr && *ifname_filter_expr)
147 ifname_filter_pcre = compileregex(ifname_filter_expr);
148 }
149
150 if (pcres_compiled == 0) {
151 pcres_compiled = 1;
152 ifstat_linux_pcres = compile_exprs("LINUX", ifstat_linux_exprs,
153 (sizeof(ifstat_linux_exprs) / sizeof(ifstat_linux_exprs[0])));
154 ifstat_freebsd_pcres = compile_exprs("FREEBSD", ifstat_freebsd_exprs,
155 (sizeof(ifstat_freebsd_exprs) / sizeof(ifstat_freebsd_exprs[0])));
156 ifstat_freebsdV8_pcres = compile_exprs("FREEBSD", ifstat_freebsdV8_exprs,
157 (sizeof(ifstat_freebsdV8_exprs) / sizeof(ifstat_freebsdV8_exprs[0])));
158 ifstat_openbsd_pcres = compile_exprs("OPENBSD", ifstat_openbsd_exprs,
159 (sizeof(ifstat_openbsd_exprs) / sizeof(ifstat_openbsd_exprs[0])));
160 ifstat_netbsd_pcres = compile_exprs("NETBSD", ifstat_netbsd_exprs,
161 (sizeof(ifstat_netbsd_exprs) / sizeof(ifstat_netbsd_exprs[0])));
162 ifstat_darwin_pcres = compile_exprs("DARWIN", ifstat_darwin_exprs,
163 (sizeof(ifstat_darwin_exprs) / sizeof(ifstat_darwin_exprs[0])));
164 ifstat_solaris_pcres = compile_exprs("SOLARIS", ifstat_solaris_exprs,
165 (sizeof(ifstat_solaris_exprs) / sizeof(ifstat_solaris_exprs[0])));
166 ifstat_aix_pcres = compile_exprs("AIX", ifstat_aix_exprs,
167 (sizeof(ifstat_aix_exprs) / sizeof(ifstat_aix_exprs[0])));
168 ifstat_hpux_pcres = compile_exprs("HPUX", ifstat_hpux_exprs,
169 (sizeof(ifstat_hpux_exprs) / sizeof(ifstat_hpux_exprs[0])));
170 ifstat_sco_sv_pcres = compile_exprs("SCO_SV", ifstat_sco_sv_exprs,
171 (sizeof(ifstat_sco_sv_exprs) / sizeof(ifstat_sco_sv_exprs[0])));
172 ifstat_bbwin_pcres = compile_exprs("BBWIN", ifstat_bbwin_exprs,
173 (sizeof(ifstat_bbwin_exprs) / sizeof(ifstat_bbwin_exprs[0])));
174 }
175
176
177 if (ifstat_tpl == NULL) ifstat_tpl = setup_template(ifstat_params);
178
179 if ((strncmp(msg, "status", 6) == 0) || (strncmp(msg, "data", 4) == 0)) {
180 /* Skip the first line of full status- and data-messages. */
181 datapart = strchr(msg, '\n');
182 if (datapart) datapart++; else datapart = msg;
183 }
184
185 ostype = get_ostype(datapart);
186 datapart = strchr(datapart, '\n');
187 if (datapart) {
188 datapart++;
189 }
190 else {
191 errprintf("Too few lines in ifstat report from %s\n", hostname);
192 return -1;
193 }
194
195 dmatch = 0;
196 ifname = rxstr = txstr = dummy = NULL;
197
198 bol = datapart;
199 while (bol) {
200 eoln = strchr(bol, '\n'); if (eoln) *eoln = '\0';
201
202 switch (ostype) {
203 case OS_LINUX22:
204 case OS_LINUX:
205 case OS_RHEL3:
206 case OS_ZVM:
207 case OS_ZVSE:
208 case OS_ZOS:
209 if (pickdata(bol, ifstat_linux_pcres[0], 1, &ifname)) {
210 /*
211 * Linux' netif aliases mess up things.
212 * Clear everything when we see an interface name.
213 * But we don't want to track the "lo" interface.
214 */
215
216 /* Strip off the last character if it is a colon (:) */
217 if (ifname[strlen(ifname)-1] == ':') ifname[strlen(ifname)-1] = '\0';
218
219 if (strcmp(ifname, "lo") == 0) {
220 xfree(ifname); ifname = NULL;
221 }
222 else {
223 dmatch = 1;
224 if (rxstr) { xfree(rxstr); rxstr = NULL; }
225 if (txstr) { xfree(txstr); txstr = NULL; }
226 }
227 }
228 else if (pickdata(bol, ifstat_linux_pcres[1], 1, &rxstr, &txstr)) dmatch |= 6;
229 else if (pickdata(bol, ifstat_linux_pcres[2], 1, &rxstr)) dmatch |= 2;
230 else if (pickdata(bol, ifstat_linux_pcres[3], 1, &txstr)) dmatch |= 4;
231 break;
232
233 case OS_FREEBSD:
234 /*
235 * FreeBSD 8 added an "Idrop" counter in the middle of the data.
236 * See if we match this expression, and if not then fall back to
237 * the old regex without that field.
238 */
239 if (pickdata(bol, ifstat_freebsdV8_pcres[0], 0, &ifname, &rxstr, &txstr)) dmatch = 7;
240 else if (pickdata(bol, ifstat_freebsd_pcres[0], 0, &ifname, &rxstr, &txstr)) dmatch = 7;
241 break;
242
243 case OS_OPENBSD:
244 if (pickdata(bol, ifstat_openbsd_pcres[0], 0, &ifname, &rxstr, &txstr)) dmatch = 7;
245 break;
246
247 case OS_NETBSD:
248 if (pickdata(bol, ifstat_netbsd_pcres[0], 0, &ifname, &rxstr, &txstr)) dmatch = 7;
249 break;
250
251 case OS_SOLARIS:
252 if (pickdata(bol, ifstat_solaris_pcres[0], 0, &ifname, &txstr)) dmatch |= 1;
253 else if (pickdata(bol, ifstat_solaris_pcres[1], 0, &dummy, &rxstr)) dmatch |= 6;
254
255 if (ifname && dummy && (strcmp(ifname, dummy) != 0)) {
256 /* They must match, drop the data */
257 errprintf("Host %s has weird ifstat data - device name mismatch %s:%s\n", hostname, ifname, dummy);
258 xfree(ifname); xfree(txstr); xfree(rxstr); xfree(dummy);
259 dmatch = 0;
260 }
261
262 /* Ignore "mac" and "wrsmd" entries - these are for sub-devices for multiple nic's aggregated into one */
263 /* See http://www.xymon.com/archive/2009/06/msg00204.html for more info */
264 if (ifname && ((strcmp(ifname, "mac") == 0) || (strcmp(ifname, "wrsmd") == 0)) ) {
265 xfree(ifname); xfree(txstr);
266 dmatch = 0;
267 }
268 if (dummy && ((strcmp(dummy, "mac") == 0) || (strcmp(dummy, "wrsmd") == 0)) ) {
269 xfree(dummy); xfree(rxstr);
270 dmatch = 0;
271 }
272 break;
273
274 case OS_AIX:
275 if (pickdata(bol, ifstat_aix_pcres[0], 1, &ifname)) {
276 /* Interface names comes first, so any rx/tx data is discarded */
277 dmatch |= 1;
278 if (rxstr) { xfree(rxstr); rxstr = NULL; }
279 if (txstr) { xfree(txstr); txstr = NULL; }
280 }
281 else if (pickdata(bol, ifstat_aix_pcres[1], 1, &txstr, &rxstr)) dmatch |= 6;
282 break;
283
284 case OS_HPUX:
285 if (pickdata(bol, ifstat_hpux_pcres[0], 1, &ifname)) {
286 /* Interface names comes first, so any rx/tx data is discarded */
287 dmatch |= 1;
288 if (rxstr) { xfree(rxstr); rxstr = NULL; }
289 if (txstr) { xfree(txstr); txstr = NULL; }
290 }
291 else if (pickdata(bol, ifstat_hpux_pcres[1], 1, &rxstr)) dmatch |= 2;
292 else if (pickdata(bol, ifstat_hpux_pcres[2], 1, &txstr)) dmatch |= 4;
293 break;
294
295 case OS_DARWIN:
296 if (pickdata(bol, ifstat_darwin_pcres[0], 0, &ifname, &rxstr, &txstr)) dmatch = 7;
297 break;
298
299 case OS_SCO_SV:
300 if (pickdata(bol, ifstat_sco_sv_pcres[0], 0, &ifname, &rxstr, &txstr)) dmatch = 7;
301 break;
302
303 case OS_WIN32_BBWIN:
304 case OS_WIN_POWERSHELL:
305 if (pickdata(bol, ifstat_bbwin_pcres[0], 0, &ifname, &rxstr, &txstr)) dmatch = 7;
306 break;
307
308 default:
309 break;
310 }
311
312 if ((dmatch == 7) && ifname && rxstr && txstr) {
313 if (!ifname_filter_pcre || matchregex(ifname, ifname_filter_pcre)) {
314 setupfn2("%s.%s.rrd", "ifstat", ifname);
315 snprintf(rrdvalues, sizeof(rrdvalues), "%d:%s:%s", (int)tstamp, txstr, rxstr);
316 create_and_update_rrd(hostname, testname, classname, pagepaths, ifstat_params, ifstat_tpl);
317 }
318
319 xfree(ifname); xfree(rxstr); xfree(txstr);
320 if (dummy) xfree(dummy);
321 ifname = rxstr = txstr = dummy = NULL;
322 dmatch = 0;
323 }
324
325 if (eoln) {
326 *eoln = '\n';
327 bol = eoln+1;
328 if (*bol == '\0') bol = NULL;
329 }
330 else {
331 bol = NULL;
332 }
333 }
334
335 if (ifname_filter_pcre) freeregex(ifname_filter_pcre);
336
337 if (ifname) xfree(ifname);
338 if (rxstr) xfree(rxstr);
339 if (txstr) xfree(txstr);
340 if (dummy) xfree(dummy);
341
342 return 0;
343 }
344
345