Project

General

Profile

Download (14.4 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
 * graph.php
4
 *
5
 * part of pfSense (https://www.pfsense.org)
6
 * Copyright (c) 2004-2013 BSD Perimeter
7
 * Copyright (c) 2013-2016 Electric Sheep Fencing
8
 * Copyright (c) 2014-2022 Rubicon Communications, LLC (Netgate)
9
 * Copyright (c) 2004-2006 T. Lechat <dev@lechat.org>
10
 * Copyright (c) 2004-2006 Jonathan Watt <jwatt@jwatt.org>
11
 * All rights reserved.
12
 *
13
 * originally based on m0n0wall (http://m0n0.ch/wall)
14
 * Copyright (c) 2003-2004 Manuel Kasper <mk@neon1.net>.
15
 * All rights reserved.
16
 *
17
 * Licensed under the Apache License, Version 2.0 (the "License");
18
 * you may not use this file except in compliance with the License.
19
 * You may obtain a copy of the License at
20
 *
21
 * http://www.apache.org/licenses/LICENSE-2.0
22
 *
23
 * Unless required by applicable law or agreed to in writing, software
24
 * distributed under the License is distributed on an "AS IS" BASIS,
25
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
26
 * See the License for the specific language governing permissions and
27
 * limitations under the License.
28
 */
29

    
30
##|+PRIV
31
##|*IDENT=page-diagnostics-interfacetraffic
32
##|*NAME=Diagnostics: Interface Traffic
33
##|*DESCR=Allow access to the 'Diagnostics: Interface Traffic' page.
34
##|*MATCH=graph.php*
35
##|-PRIV
36

    
37
require_once("globals.inc");
38
require_once("guiconfig.inc");
39

    
40
header("Last-Modified: " . gmdate("D, j M Y H:i:s") . " GMT");
41
header("Expires: " . gmdate("D, j M Y H:i:s", time()) . " GMT");
42
header("Cache-Control: no-cache, no-store, must-revalidate"); // HTTP/1.1
43
header("Pragma: no-cache"); // HTTP/1.0
44
header("Content-type: image/svg+xml");
45

    
46
/********** HTTP REQUEST Based Conf ***********/
47
$ifnum = @$_REQUEST["ifnum"];  // BSD / SNMP interface name / number
48
$ifnum = get_real_interface($ifnum);
49
$ifname = @$_REQUEST["ifname"]?$_REQUEST["ifname"]:"Interface $ifnum";  //Interface name that will be showed on top right of graph
50

    
51
/********* Other conf *******/
52
if (isset($config["widgets"]["trafficgraphs"]["scale_type"])) {
53
	$scale_type = $config["widgets"]["trafficgraphs"]["scale_type"];
54
} else {
55
	$scale_type = "up";
56
}
57

    
58
$nb_plot=120;                   //NB plot in graph
59
if ($_REQUEST["timeint"]) {
60
	$time_interval = $_REQUEST["timeint"];		//Refresh time Interval
61
} else {
62
	$time_interval = 3;
63
}
64

    
65
if ($_REQUEST["initdelay"]) {
66
	$init_delay = $_REQUEST["initdelay"];		//Initial Delay
67
} else {
68
	$init_delay = 3;
69
}
70

    
71
//SVG attributes
72
$attribs['axis']='fill="black" stroke="black"';
73
$attribs['in']='fill="#FF0000" font-family="Tahoma, Verdana, Arial, Helvetica, sans-serif" font-size="7"';
74
$attribs['out']='fill="#000000" font-family="Tahoma, Verdana, Arial, Helvetica, sans-serif" font-size="7"';
75
$attribs['graph_in']='fill="none" stroke="#FF0000" stroke-opacity="0.8"';
76
$attribs['graph_out']='fill="none" stroke="#000000" stroke-opacity="0.8"';
77
$attribs['legend']='fill="black" font-family="Tahoma, Verdana, Arial, Helvetica, sans-serif" font-size="4"';
78
$attribs['graphname']='fill="#FF0000" font-family="Tahoma, Verdana, Arial, Helvetica, sans-serif" font-size="8"';
79
$attribs['grid_txt']='fill="gray" font-family="Tahoma, Verdana, Arial, Helvetica, sans-serif" font-size="6"';
80
$attribs['grid']='stroke="gray" stroke-opacity="0.5"';
81
$attribs['switch_unit']='fill="#FF0000" font-family="Tahoma, Verdana, Arial, Helvetica, sans-serif" font-size="4" text-decoration="underline"';
82
$attribs['switch_scale']='fill="#FF0000" font-family="Tahoma, Verdana, Arial, Helvetica, sans-serif" font-size="4" text-decoration="underline"';
83
$attribs['error']='fill="blue" font-family="Arial" font-size="4"';
84
$attribs['collect_initial']='fill="gray" font-family="Tahoma, Verdana, Arial, Helvetica, sans-serif" font-size="4"';
85

    
86
//Error text if we cannot fetch data : depends on which method is used
87
$error_text = sprintf(gettext("Cannot get data about interface %s"), htmlspecialchars($ifnum));
88

    
89
$height=100;            //SVG internal height : do not modify
90
$width=200;             //SVG internal width : do not modify
91

    
92
$fetch_link = "ifstats.php?if=" . htmlspecialchars($ifnum);
93

    
94
/********* Graph DATA **************/
95
print('<?xml version="1.0" encoding="UTF-8"?>' . "\n");?>
96
<svg width="100%" height="100%" viewBox="0 0 <?=$width?> <?=$height?>" preserveAspectRatio="none" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" onload="init(evt)">
97
	<g id="graph">
98
		<rect id="bg" x1="0" y1="0" width="100%" height="100%" fill="white"/>
99
		<line id="axis_x" x1="0" y1="0" x2="0" y2="100%" <?=$attribs['axis']?>/>
100
		<line id="axis_y" x1="0" y1="100%" x2="100%" y2="100%" <?=$attribs['axis']?>/>
101
		<path id="graph_out" d="M0 <?=$height?> L 0 <?=$height?>" <?=$attribs['graph_out']?>/>
102
		<path id="graph_in" d="M0 <?=$height?> L 0 <?=$height?>" <?=$attribs['graph_in']?>/>
103
		<path id="grid" d="M0 <?=$height/4*1?> L <?=$width?> <?=$height/4*1?> M0 <?=$height/4*2?> L <?=$width?> <?=$height/4*2?> M0 <?=$height/4*3?> L <?=$width?> <?=$height/4*3?>" <?=$attribs['grid']?>/>
104
		<text id="grid_txt1" x="<?=$width?>" y="<?=$height/4*1?>" <?=$attribs['grid_txt']?> text-anchor="end"> </text>
105
		<text id="grid_txt2" x="<?=$width?>" y="<?=$height/4*2?>" <?=$attribs['grid_txt']?> text-anchor="end"> </text>
106
		<text id="grid_txt3" x="<?=$width?>" y="<?=$height/4*3?>" <?=$attribs['grid_txt']?> text-anchor="end"> </text>
107
		<text id="graph_in_lbl" x="5" y="8" <?=$attribs['in']?>><?=gettext("In"); ?></text>
108
		<text id="graph_out_lbl" x="5" y="16" <?=$attribs['out']?>><?=gettext("Out"); ?></text>
109
		<text id="graph_in_txt" x="20" y="8" <?=$attribs['in']?>> </text>
110
		<text id="graph_out_txt" x="20" y="16" <?=$attribs['out']?>> </text>
111
		<text id="ifname" x="<?=$width?>" y="8" <?=$attribs['graphname']?> text-anchor="end"><?=htmlspecialchars($ifname)?></text>
112
		<text id="switch_unit" x="<?=$width*0.55?>" y="5" <?=$attribs['switch_unit']?>><?=gettext("Switch to bytes/s"); ?></text>
113
		<text id="switch_scale" x="<?=$width*0.55?>" y="11" <?=$attribs['switch_scale']?>><?=gettext("AutoScale"); ?> (<?=gettext($scale_type);?>)</text>
114
		<text id="date" x="<?=$width*0.33?>" y="5" <?=$attribs['legend']?>> </text>
115
		<text id="time" x="<?=$width*0.33?>" y="11" <?=$attribs['legend']?>> </text>
116
		<text id="graphlast" x="<?=$width*0.55?>" y="17" <?=$attribs['legend']?>><?=sprintf(gettext("Graph shows last %s seconds"), $time_interval*$nb_plot)?></text>
117
		<polygon id="axis_arrow_x" <?=$attribs['axis']?> points="<?=($width) . "," . ($height)?> <?=($width-2) . "," . ($height-2)?> <?=($width-2) . "," . $height?>"/>
118
		<text id="error" x="<?=$width*0.5?>" y="<?=$height*0.5?>" visibility="hidden" <?=$attribs['error']?> text-anchor="middle"><?=$error_text?></text>
119
		<text id="collect_initial" x="<?=$width*0.5?>" y="<?=$height*0.5?>" visibility="hidden" <?=$attribs['collect_initial']?> text-anchor="middle"><?=gettext("Collecting initial data, please wait"); ?>...</text>
120
	</g>
121
	<script type="text/ecmascript">
122
	<![CDATA[
123

    
124
/**
125
 * getURL is a proprietary Adobe function, but it's simplicity has made it very
126
 * popular. If getURL is undefined we spin our own by wrapping XMLHttpRequest.
127
 */
128
if (typeof getURL == 'undefined') {
129
	getURL = function(url, callback) {
130
		if (!url) {
131
			throw '<?=gettext("No URL for getURL"); ?>';
132
		}
133

    
134
		try {
135
			if (typeof callback.operationComplete == 'function') {
136
				callback = callback.operationComplete;
137
			}
138
		} catch (e) {}
139
		if (typeof callback != 'function') {
140
			throw '<?=gettext("No callback function for getURL"); ?>';
141
		}
142

    
143
		var http_request = null;
144
		if (typeof XMLHttpRequest != 'undefined') {
145
			http_request = new XMLHttpRequest();
146
		} else if (typeof ActiveXObject != 'undefined') {
147
			try {
148
				http_request = new ActiveXObject('Msxml2.XMLHTTP');
149
			} catch (e) {
150
				try {
151
				http_request = new ActiveXObject('Microsoft.XMLHTTP');
152
				} catch (e) {}
153
			}
154
		}
155
		if (!http_request) {
156
			throw '<?=gettext("Both getURL and XMLHttpRequest are undefined"); ?>';
157
		}
158

    
159
		http_request.onreadystatechange = function() {
160
			if (http_request.readyState == 4) {
161
				callback( { success : true,
162
					content : http_request.responseText,
163
					contentType : http_request.getResponseHeader("Content-Type") } );
164
			}
165
		}
166
		http_request.open('REQUEST', url, true);
167
		http_request.send(null);
168
	}
169
}
170

    
171
var SVGDoc = null;
172
var last_ifin = 0;
173
var last_ifout = 0;
174
var last_ugmt = 0;
175
var max = 0;
176
var plot_in = new Array();
177
var plot_out = new Array();
178

    
179
var max_num_points = <?=$nb_plot?>;  // maximum number of plot data points
180
var step = <?=$width?> / max_num_points ;
181
var unit = 'bits';
182
var scale_type = '<?=$scale_type?>';
183
var scale_type_text = '<?=gettext($scale_type); ?>';
184

    
185
function init(evt) {
186
	SVGDoc = evt.target.ownerDocument;
187
	SVGDoc.getElementById("switch_unit").addEventListener("mousedown", switch_unit, false);
188
	SVGDoc.getElementById("switch_scale").addEventListener("mousedown", switch_scale, false);
189

    
190
	fetch_data();
191
}
192

    
193
function switch_unit(event) {
194
	SVGDoc.getElementById('switch_unit').firstChild.data = (unit == 'bits') ? '<?=gettext("Switch to bits/s"); ?>' : '<?=gettext("Switch to bytes/s"); ?>';
195
	unit = (unit == 'bits') ? 'bytes' : 'bits';
196
}
197

    
198
function switch_scale(event) {
199
	scale_type = (scale_type == 'up') ? 'follow' : 'up';
200
	scale_type_text = (scale_type == 'up') ? '<?=gettext("up"); ?>' : '<?=gettext("follow"); ?>';
201
	SVGDoc.getElementById('switch_scale').firstChild.data = '<?=gettext("AutoScale"); ?>' + ' (' + scale_type_text + ')';
202
}
203

    
204
function fetch_data() {
205
	getURL('<?=$fetch_link?>', plot_data);
206
}
207

    
208
function plot_data(obj) {
209
	// Show datetimelegend
210
	var now = new Date();
211
	var time = LZ(now.getHours()) + ":" + LZ(now.getMinutes()) + ":" + LZ(now.getSeconds());
212
	SVGDoc.getElementById('time').firstChild.data = time;
213
	var date = (now.getMonth()+1) + "/" + now.getDate() + "/" + now.getFullYear();
214
	SVGDoc.getElementById('date').firstChild.data = date;
215

    
216
	if (!obj.success) {
217
		return handle_error();  // getURL failed to get data
218
	}
219

    
220
	var t = obj.content.split("|");
221
	var ugmt = parseFloat(t[0]);  // ugmt is an unixtimestamp style
222
	var ifin = parseInt(t[1], 10);    // number of bytes received by the interface
223
	var ifout = parseInt(t[2], 10);   // number of bytes sent by the interface
224
	var scale;
225

    
226
	if (!isNumber(ifin) || !isNumber(ifout)) {
227
		return handle_error();
228
	}
229

    
230
	var diff_ugmt  = ugmt - last_ugmt;
231
	var diff_ifin  = ifin - last_ifin;
232
	var diff_ifout = ifout - last_ifout;
233

    
234
	if (diff_ugmt == 0) {
235
		diff_ugmt = 1;  /* avoid division by zero */
236
	}
237

    
238
	last_ugmt = ugmt;
239
	last_ifin = ifin;
240
	last_ifout = ifout;
241
	var graphTimerId = 0;
242
	switch (plot_in.length) {
243
		case 0:
244
			SVGDoc.getElementById("collect_initial").setAttributeNS(null, 'visibility', 'visible');
245
			plot_in[0] = diff_ifin / diff_ugmt;
246
			plot_out[0] = diff_ifout / diff_ugmt;
247
			setTimeout('fetch_data()', <?=1000*($time_interval + $init_delay)?>);
248
			return;
249
		case 1:
250
			SVGDoc.getElementById("collect_initial").setAttributeNS(null, 'visibility', 'hidden');
251
			break;
252
		case max_num_points:
253
			// shift plot to left if the maximum number of plot points has been reached
254
			var i = 0;
255
			while (i < max_num_points) {
256
				plot_in[i] = plot_in[i+1];
257
				plot_out[i] = plot_out[++i];
258
			}
259
			plot_in.length--;
260
			plot_out.length--;
261
	}
262

    
263
	plot_in[plot_in.length] = diff_ifin / diff_ugmt;
264
	plot_out[plot_out.length]= diff_ifout / diff_ugmt;
265
	var index_plot = plot_in.length - 1;
266

    
267
	SVGDoc.getElementById('graph_in_txt').firstChild.data = formatSpeed(plot_in[index_plot], unit);
268
	SVGDoc.getElementById('graph_out_txt').firstChild.data = formatSpeed(plot_out[index_plot], unit);
269

    
270
	/* determine peak for sensible scaling */
271
	if (scale_type == 'up') {
272
		if (plot_in[index_plot] > max) {
273
			max = plot_in[index_plot];
274
		}
275
		if (plot_out[index_plot] > max) {
276
			max = plot_out[index_plot];
277
		}
278
	} else if (scale_type == 'follow') {
279
		i = 0;
280
		max = 0;
281
		while (i < plot_in.length) {
282
			if (plot_in[i] > max) {
283
				max = plot_in[i];
284
			}
285
			if (plot_out[i] > max) {
286
				max = plot_out[i];
287
			}
288
			i++;
289
		}
290
	}
291

    
292
	var rmax;  // max, rounded up
293

    
294
	if (unit == 'bits') {
295
		/* round up max, such that
296
		   100 kbps -> 200 kbps -> 400 kbps -> 800 kbps -> 1 Mbps -> 2 Mbps -> ... */
297
		rmax = 12500;
298
		i = 0;
299
		while (max > rmax) {
300
			i++;
301
			if (i && (i % 4 == 0)) {
302
				rmax *= 1.25;
303
			} else {
304
				rmax *= 2;
305
			}
306
		}
307
	} else {
308
		/* round up max, such that
309
		   10 KB/s -> 20 KB/s -> 40 KB/s -> 80 KB/s -> 100 KB/s -> 200 KB/s -> 400 KB/s -> 800 KB/s -> 1 MB/s ... */
310
		rmax = 10240;
311
		i = 0;
312
		while (max > rmax) {
313
			i++;
314
			if (i && (i % 4 == 0)) {
315
				rmax *= 1.25;
316
			} else {
317
				rmax *= 2;
318
			}
319

    
320
			if (i == 8) {
321
				rmax *= 1.024;
322
			}
323
		}
324
	}
325

    
326
	scale = <?=$height?> / rmax;
327

    
328
	/* change labels accordingly */
329
	SVGDoc.getElementById('grid_txt1').firstChild.data = formatSpeed(3*rmax/4, unit);
330
	SVGDoc.getElementById('grid_txt2').firstChild.data = formatSpeed(2*rmax/4, unit);
331
	SVGDoc.getElementById('grid_txt3').firstChild.data = formatSpeed(rmax/4, unit);
332

    
333
	var path_in = "M 0 " + (<?=$height?> - (plot_in[0] * scale));
334
	var path_out = "M 0 " + (<?=$height?> - (plot_out[0] * scale));
335
	for (i = 1; i < plot_in.length; i++) {
336
		var x = step * i;
337
		var y_in = <?=$height?> - (plot_in[i] * scale);
338
		var y_out = <?=$height?> - (plot_out[i] * scale);
339
		path_in += " L" + x + " " + y_in;
340
		path_out += " L" + x + " " + y_out;
341
	}
342

    
343
	SVGDoc.getElementById('error').setAttributeNS(null, 'visibility', 'hidden');
344
	SVGDoc.getElementById('graph_in').setAttributeNS(null, 'd', path_in);
345
	SVGDoc.getElementById('graph_out').setAttributeNS(null, 'd', path_out);
346

    
347
	setTimeout('fetch_data()', <?=1000*$time_interval?>);
348
}
349

    
350
function handle_error() {
351
	SVGDoc.getElementById("error").setAttributeNS(null, 'visibility', 'visible');
352
	setTimeout('fetch_data()', <?=1000*$time_interval?>);
353
}
354

    
355
function isNumber(a) {
356
	return typeof a == 'number' && isFinite(a);
357
}
358

    
359
function formatSpeed(speed, unit) {
360
	if (unit == 'bits') {
361
		return formatSpeedBits(speed);
362
	}
363
	if (unit == 'bytes') {
364
		return formatSpeedBytes(speed);
365
	}
366
}
367

    
368
function formatSpeedBits(speed) {
369
	// format speed in bits/sec, input: bytes/sec
370
	if (speed < 125000) {
371
		return Math.round(speed / 125) + " <?=gettext("Kbps"); ?>";
372
	}
373
	if (speed < 125000000) {
374
		return Math.round(speed / 1250)/100 + " <?=gettext("Mbps"); ?>";
375
	}
376
	// else
377
	return Math.round(speed / 1250000)/100 + " <?=gettext("Gbps"); ?>";  /* wow! */
378
}
379

    
380
function formatSpeedBytes(speed) {
381
	// format speed in bytes/sec, input:  bytes/sec
382
	if (speed < 1048576) {
383
		return Math.round(speed / 10.24)/100 + " <?=gettext("KB/s"); ?>";
384
	}
385
	if (speed < 1073741824) {
386
		return Math.round(speed / 10485.76)/100 + " <?=gettext("MB/s"); ?>";
387
	}
388
	// else
389
	return Math.round(speed / 10737418.24)/100 + " <?=gettext("GB/s"); ?>";  /* wow! */
390
}
391

    
392
function LZ(x) {
393
	return (x < 0 || x > 9 ? "" : "0") + x;
394
}
395

    
396
    ]]>
397
	</script>
398
</svg>
(64-64/228)