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