Project

General

Profile

Download (14 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
	graph.php
4
	part of m0n0wall (http://m0n0.ch/wall)
5
	
6
	Copyright (C) 2004-2006 T. Lechat <dev@lechat.org>, Manuel Kasper <mk@neon1.net>
7
	and Jonathan Watt <jwatt@jwatt.org>.
8
	All rights reserved.
9
	
10
	Redistribution and use in source and binary forms, with or without
11
	modification, are permitted provided that the following conditions are met:
12
	
13
	1. Redistributions of source code must retain the above copyright notice,
14
	   this list of conditions and the following disclaimer.
15
	
16
	2. Redistributions in binary form must reproduce the above copyright
17
	   notice, this list of conditions and the following disclaimer in the
18
	   documentation and/or other materials provided with the distribution.
19
	
20
	THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
21
	INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
22
	AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23
	AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
24
	OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25
	SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26
	INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27
	CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28
	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29
	POSSIBILITY OF SUCH DAMAGE.
30
*/
31

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

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

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

    
50
/********* Other conf *******/
51
$scale_type="up";               //Autoscale default setup : "up" = only increase scale; "follow" = increase and decrease scale according to current graphed datas
52
$nb_plot=120;                   //NB plot in graph
53
if ($_GET["timeint"])
54
	$time_interval = $_GET["timeint"];		//Refresh time Interval
55
else
56
	$time_interval = 3;
57

    
58
$fetch_link = "ifstats.php?if={$ifnum}";
59

    
60
//SVG attributes
61
$attribs['axis']='fill="black" stroke="black"';
62
$attribs['in']='fill="#FF0000" font-family="Tahoma, Verdana, Arial, Helvetica, sans-serif" font-size="7"';
63
$attribs['out']='fill="#000000" font-family="Tahoma, Verdana, Arial, Helvetica, sans-serif" font-size="7"';
64
$attribs['graph_in']='fill="none" stroke="#FF0000" stroke-opacity="0.8"';
65
$attribs['graph_out']='fill="none" stroke="#000000" stroke-opacity="0.8"';
66
$attribs['legend']='fill="black" font-family="Tahoma, Verdana, Arial, Helvetica, sans-serif" font-size="4"';
67
$attribs['graphname']='fill="#FF0000" font-family="Tahoma, Verdana, Arial, Helvetica, sans-serif" font-size="8"';
68
$attribs['grid_txt']='fill="gray" font-family="Tahoma, Verdana, Arial, Helvetica, sans-serif" font-size="6"';
69
$attribs['grid']='stroke="gray" stroke-opacity="0.5"';
70
$attribs['switch_unit']='fill="#FF0000" font-family="Tahoma, Verdana, Arial, Helvetica, sans-serif" font-size="4" text-decoration="underline"';
71
$attribs['switch_scale']='fill="#FF0000" font-family="Tahoma, Verdana, Arial, Helvetica, sans-serif" font-size="4" text-decoration="underline"';
72
$attribs['error']='fill="blue" font-family="Arial" font-size="4"';
73
$attribs['collect_initial']='fill="gray" font-family="Tahoma, Verdana, Arial, Helvetica, sans-serif" font-size="4"';
74

    
75
//Error text if we cannot fetch data : depends on which method is used
76
$error_text = "Cannot get data about interface $ifnum";
77

    
78
$height=100;            //SVG internal height : do not modify
79
$width=200;             //SVG internal width : do not modify
80

    
81
/********* Graph DATA **************/
82
print('<?xml version="1.0" encoding="iso-8859-1"?>' . "\n");?>
83
<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)">
84
  <g id="graph">
85
    <rect id="bg" x1="0" y1="0" width="100%" height="100%" fill="white"/>
86
    <line id="axis_x" x1="0" y1="0" x2="0" y2="100%" <?=$attribs['axis']?>/>
87
    <line id="axis_y" x1="0" y1="100%" x2="100%" y2="100%" <?=$attribs['axis']?>/>
88
    <path id="graph_out" d="M0 <?=$height?> L 0 <?=$height?>" <?=$attribs['graph_out']?>/>
89
    <path id="graph_in"  d="M0 <?=$height?> L 0 <?=$height?>" <?=$attribs['graph_in']?>/>
90
    <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']?>/>
91
    <text id="grid_txt1" x="<?=$width?>" y="<?=$height/4*1?>" <?=$attribs['grid_txt']?> text-anchor="end"> </text>
92
    <text id="grid_txt2" x="<?=$width?>" y="<?=$height/4*2?>" <?=$attribs['grid_txt']?> text-anchor="end"> </text>
93
    <text id="grid_txt3" x="<?=$width?>" y="<?=$height/4*3?>" <?=$attribs['grid_txt']?> text-anchor="end"> </text>
94
    <text id="graph_in_lbl" x="5" y="8" <?=$attribs['in']?>>In</text>
95
    <text id="graph_out_lbl" x="5" y="16" <?=$attribs['out']?>>Out</text>
96
    <text id="graph_in_txt" x="20" y="8" <?=$attribs['in']?>> </text>
97
    <text id="graph_out_txt" x="20" y="16" <?=$attribs['out']?>> </text>
98
    <text id="ifname" x="<?=$width?>" y="8" <?=$attribs['graphname']?> text-anchor="end"><?=$ifname?></text>
99
    <text id="switch_unit" x="<?=$width*0.55?>" y="5" <?=$attribs['switch_unit']?>>Switch to bytes/s</text>
100
    <text id="switch_scale" x="<?=$width*0.55?>" y="11" <?=$attribs['switch_scale']?>>AutoScale (<?=$scale_type?>)</text>
101
    <text id="datetime" x="<?=$width*0.33?>" y="5" <?=$attribs['legend']?>> </text>
102
    <text id="graphlast" x="<?=$width*0.55?>" y="17" <?=$attribs['legend']?>>Graph shows last <?=$time_interval*$nb_plot?> seconds</text>
103
    <polygon id="axis_arrow_x" <?=$attribs['axis']?> points="<?=($width) . "," . ($height)?> <?=($width-2) . "," . ($height-2)?> <?=($width-2) . "," . $height?>"/>
104
    <text id="error" x="<?=$width*0.5?>" y="<?=$height*0.5?>"  visibility="hidden" <?=$attribs['error']?> text-anchor="middle"><?=$error_text?></text>
105
    <text id="collect_initial" x="<?=$width*0.5?>" y="<?=$height*0.5?>"  visibility="hidden" <?=$attribs['collect_initial']?> text-anchor="middle">Collecting initial data, please wait...</text>
106
  </g>
107
  <script type="text/ecmascript">
108
    <![CDATA[
109

    
110
/**
111
 * getURL is a proprietary Adobe function, but it's simplicity has made it very
112
 * popular. If getURL is undefined we spin our own by wrapping XMLHttpRequest.
113
 */
114
if (typeof getURL == 'undefined') {
115
  getURL = function(url, callback) {
116
    if (!url)
117
      throw 'No URL for getURL';
118

    
119
    try {
120
      if (typeof callback.operationComplete == 'function')
121
        callback = callback.operationComplete;
122
    } catch (e) {}
123
    if (typeof callback != 'function')
124
      throw 'No callback function for getURL';
125

    
126
    var http_request = null;
127
    if (typeof XMLHttpRequest != 'undefined') {
128
      http_request = new XMLHttpRequest();
129
    }
130
    else if (typeof ActiveXObject != 'undefined') {
131
      try {
132
        http_request = new ActiveXObject('Msxml2.XMLHTTP');
133
      } catch (e) {
134
        try {
135
          http_request = new ActiveXObject('Microsoft.XMLHTTP');
136
        } catch (e) {}
137
      }
138
    }
139
    if (!http_request)
140
      throw 'Both getURL and XMLHttpRequest are undefined';
141

    
142
    http_request.onreadystatechange = function() {
143
      if (http_request.readyState == 4) {
144
        callback( { success : true,
145
                    content : http_request.responseText,
146
                    contentType : http_request.getResponseHeader("Content-Type") } );
147
      }
148
    }
149
    http_request.open('GET', url, true);
150
    http_request.send(null);
151
  }
152
}
153

    
154
var SVGDoc = null;
155
var last_ifin = 0;
156
var last_ifout = 0;
157
var last_ugmt = 0;
158
var max = 0;
159
var plot_in = new Array();
160
var plot_out = new Array();
161

    
162
var max_num_points = <?=$nb_plot?>;  // maximum number of plot data points
163
var step = <?=$width?> / max_num_points ;
164
var unit = 'bits';
165
var scale_type = '<?=$scale_type?>';
166

    
167
function init(evt) {
168
  SVGDoc = evt.target.ownerDocument;
169
  SVGDoc.getElementById("switch_unit").addEventListener("mousedown", switch_unit, false);
170
  SVGDoc.getElementById("switch_scale").addEventListener("mousedown", switch_scale, false);
171

    
172
  fetch_data();
173
}
174

    
175
function switch_unit(event)
176
{
177
  SVGDoc.getElementById('switch_unit').firstChild.data = 'Switch to ' + unit + '/s';
178
  unit = (unit == 'bits') ? 'bytes' : 'bits';
179
}
180

    
181
function switch_scale(event)
182
{
183
  scale_type = (scale_type == 'up') ? 'follow' : 'up';
184
  SVGDoc.getElementById('switch_scale').firstChild.data = 'AutoScale (' + scale_type + ')';
185
}
186

    
187
function fetch_data() {
188
  getURL('<?=$fetch_link?>', plot_data);
189
}
190

    
191
function plot_data(obj) {
192
  // Show datetimelegend
193
  var now = new Date();
194
  var datetime = (now.getMonth()+1) + "/" + now.getDate() + "/" + now.getFullYear() + ' ' + 
195
    LZ(now.getHours()) + ":" + LZ(now.getMinutes()) + ":" + LZ(now.getSeconds());
196
  SVGDoc.getElementById('datetime').firstChild.data = datetime;
197

    
198
  if (!obj.success)
199
    return handle_error();  // getURL failed to get data
200

    
201
  var t = obj.content.split("|");
202
  var ugmt = parseFloat(t[0]);  // ugmt is an unixtimestamp style
203
  var ifin = parseInt(t[1]);    // number of bytes received by the interface
204
  var ifout = parseInt(t[2]);   // number of bytes sent by the interface
205
  var scale;
206

    
207
  if (!isNumber(ifin) || !isNumber(ifout))
208
    return handle_error();
209

    
210
  var diff_ugmt  = ugmt - last_ugmt;
211
  var diff_ifin  = ifin - last_ifin;
212
  var diff_ifout = ifout - last_ifout;
213

    
214
  if (diff_ugmt == 0)
215
    diff_ugmt = 1;  /* avoid division by zero */
216

    
217
  last_ugmt = ugmt;
218
  last_ifin = ifin;
219
  last_ifout = ifout;
220
  
221
  switch (plot_in.length) {
222
  	case 0:
223
  		SVGDoc.getElementById("collect_initial").setAttributeNS(null, 'visibility', 'visible');
224
		plot_in[0] = diff_ifin / diff_ugmt;
225
		plot_out[0] = diff_ifout / diff_ugmt;
226
		setTimeout('fetch_data()',<?=1000*$time_interval?>);
227
		return;
228
	case 1:
229
    	SVGDoc.getElementById("collect_initial").setAttributeNS(null, 'visibility', 'hidden');
230
    	break;
231
    case max_num_points:
232
		// shift plot to left if the maximum number of plot points has been reached
233
		var i = 0;
234
		while (i < max_num_points) {
235
		  plot_in[i] = plot_in[i+1];
236
		  plot_out[i] = plot_out[++i];
237
		}
238
		plot_in.length--;
239
		plot_out.length--;
240
  }
241

    
242
  plot_in[plot_in.length] = diff_ifin / diff_ugmt;
243
  plot_out[plot_out.length]= diff_ifout / diff_ugmt;
244
  var index_plot = plot_in.length - 1;
245

    
246
  SVGDoc.getElementById('graph_in_txt').firstChild.data = formatSpeed(plot_in[index_plot], unit);
247
  SVGDoc.getElementById('graph_out_txt').firstChild.data = formatSpeed(plot_out[index_plot], unit);
248

    
249
  /* determine peak for sensible scaling */
250
  if (scale_type == 'up') {
251
    if (plot_in[index_plot] > max)
252
      max = plot_in[index_plot];
253
    if (plot_out[index_plot] > max)
254
      max = plot_out[index_plot];
255
  }
256
  else if (scale_type == 'follow') {
257
    i = 0;
258
    max = 0;
259
    while (i < plot_in.length) {
260
      if (plot_in[i] > max)
261
        max = plot_in[i];
262
      if (plot_out[i] > max)
263
        max = plot_out[i];
264
      i++;
265
    }
266
  }
267

    
268
  var rmax;  // max, rounded up
269

    
270
  if (unit == 'bits') {
271
    /* round up max, such that
272
         100 kbps -> 200 kbps -> 400 kbps -> 800 kbps -> 1 Mbps -> 2 Mbps -> ... */
273
    rmax = 12500;
274
    i = 0;
275
    while (max > rmax) {
276
      i++;
277
      if (i && (i % 4 == 0))
278
        rmax *= 1.25;
279
      else
280
        rmax *= 2;
281
    }
282
  } else {
283
    /* round up max, such that
284
         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 ... */
285
    rmax = 10240;
286
    i = 0;
287
    while (max > rmax) {
288
      i++;
289
      if (i && (i % 4 == 0))
290
        rmax *= 1.25;
291
      else
292
        rmax *= 2;
293
      
294
      if (i == 8)
295
        rmax *= 1.024;
296
    }
297
  }
298

    
299
  scale = <?=$height?> / rmax;
300

    
301
  /* change labels accordingly */
302
  SVGDoc.getElementById('grid_txt1').firstChild.data = formatSpeed(3*rmax/4,unit);
303
  SVGDoc.getElementById('grid_txt2').firstChild.data = formatSpeed(2*rmax/4,unit);
304
  SVGDoc.getElementById('grid_txt3').firstChild.data = formatSpeed(rmax/4,unit);
305

    
306
  var path_in = "M 0 " + (<?=$height?> - (plot_in[0] * scale));
307
  var path_out = "M 0 " + (<?=$height?> - (plot_out[0] * scale));
308
  for (i = 1; i < plot_in.length; i++)
309
  {
310
    var x = step * i;
311
    var y_in = <?=$height?> - (plot_in[i] * scale);
312
    var y_out = <?=$height?> - (plot_out[i] * scale);
313
    path_in += " L" + x + " " + y_in;
314
    path_out += " L" + x + " " + y_out;
315
  }
316

    
317
  SVGDoc.getElementById('error').setAttributeNS(null, 'visibility', 'hidden');
318
  SVGDoc.getElementById('graph_in').setAttributeNS(null, 'd', path_in);
319
  SVGDoc.getElementById('graph_out').setAttributeNS(null, 'd', path_out);
320

    
321
  setTimeout('fetch_data()',<?=1000*$time_interval?>);
322
}
323

    
324
function handle_error() {
325
  SVGDoc.getElementById("error").setAttributeNS(null, 'visibility', 'visible');
326
  setTimeout('fetch_data()',<?=1000*$time_interval?>);
327
}
328

    
329
function isNumber(a) {
330
  return typeof a == 'number' && isFinite(a);
331
}
332

    
333
function formatSpeed(speed, unit) {
334
  if (unit == 'bits')
335
    return formatSpeedBits(speed);
336
  if (unit == 'bytes')
337
    return formatSpeedBytes(speed);
338
}
339

    
340
function formatSpeedBits(speed) {
341
  // format speed in bits/sec, input: bytes/sec
342
  if (speed < 125000)
343
    return Math.round(speed / 125) + " Kbps";
344
  if (speed < 125000000)
345
    return Math.round(speed / 1250)/100 + " Mbps";
346
  // else
347
  return Math.round(speed / 1250000)/100 + " Gbps";  /* wow! */
348
}
349

    
350
function formatSpeedBytes(speed) {
351
  // format speed in bytes/sec, input:  bytes/sec
352
  if (speed < 1048576)
353
    return Math.round(speed / 10.24)/100 + " KB/s";
354
  if (speed < 1073741824)
355
    return Math.round(speed / 10485.76)/100 + " MB/s";
356
  // else
357
  return Math.round(speed / 10737418.24)/100 + " GB/s";  /* wow! */
358
}
359

    
360
function LZ(x) {
361
  return (x < 0 || x > 9 ? "" : "0") + x;
362
}
363

    
364
    ]]>
365
  </script>
366
</svg>
(62-62/209)