Merge branch 'QOS-Classnames' into Toastman-RT
[tomato.git] / release / src / router / www / qos-classify.asp
blob39a615f988ae9e369860556ba85cdac4602d63d1
1 <!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.0//EN'>
2 <!--
3 Tomato GUI
4 Copyright (C) 2006-2010 Jonathan Zarate
5 http://www.polarcloud.com/tomato/
7 For use with Tomato Firmware only.
8 No part of this file may be used without permission.
9 -->
10 <html>
11 <head>
12 <meta http-equiv='content-type' content='text/html;charset=utf-8'>
13 <meta name='robots' content='noindex,nofollow'>
14 <title>[<% ident(); %>] QoS: Classification</title>
15 <link rel='stylesheet' type='text/css' href='tomato.css'>
16 <link rel='stylesheet' type='text/css' href='color.css'>
17 <script type='text/javascript' src='tomato.js'></script>
18 <script type='text/javascript' src='protocols.js'></script>
20 <!-- / / / -->
22 <style type='text/css'>
23 #qg div {
24 padding: 0 0 1px 0;
25 margin: 0;
28 #qg .co1 {
29 width: 370px;
31 #qg .co2 {
32 width: 80px;
34 #qg .co3 {
35 width: 300px;
37 #qg .co4 {
38 width: 40px;
40 #qg .x1a {
41 width: 34%;
42 float: left;
44 #qg .x1b {
45 width: 66%;
46 float: left;
49 #qg .x2a {
50 width: 35%;
51 float: left;
52 clear: left;
54 #qg .x2b {
55 width: 23%;
56 float: left;
58 #qg .x2c {
59 width: 41%;
60 float: left;
63 #qg .x3a {
64 width: 40%;
65 float: left;
66 clear: left;
68 #qg .x3b {
69 width: 60%;
70 float: left;
73 #qg .x4a {
74 width: 58%;
75 float: left;
76 clear: left;
78 #qg .x4b {
79 width: 41%;
80 float: left;
82 #qg .x5a {
83 float: left;
84 clear: left;
85 width: 70px;
88 #qg .x5b {
89 float: left;
90 padding: 2px 8px 0 8px;
91 width: 10px;
92 text-align: center;
94 #qg .x5c {
95 float: left;
96 width: 70px;
98 #qg .x5d {
99 float: left;
100 padding: 2px 0 0 8px;
101 width: 100px;
104 </style>
106 <script type='text/javascript' src='debug.js'></script>
108 <script type='text/javascript'>
110 // <% nvram("qos_classnames,qos_enable,qos_orules"); %>
114 var abc = nvram.qos_classnames.split(' '); //Toastman Class Labels
116 // var abc = ['Highest'], 'High', 'Medium', 'Low', 'Lowest', 'A','B','C','D','E';
118 var ipp2p = [
119 [0,'IPP2P (disabled)'],[0xFFF,'All IPP2P filters'],[1,'AppleJuice'],[2,'Ares'],[4,'BitTorrent'],[8,'Direct Connect'],
120 [16,'eDonkey'],[32,'Gnutella'],[64,'Kazaa'],[128,'Mute'],[256,'SoulSeek'],[512,'Waste'],[1024,'WinMX'],[2048,'XDCC']];
122 var dscp = [
123 ['','DSCP (any)'],['0x00','BE'],
124 ['0x08','CS1'],['0x10','CS2'],['0x18','CS3'],['0x20','CS4'],['0x28','CS5'],['0x30','CS6'],['0x38','CS7'],
125 ['0x0a','AF11'],['0x0c','AF12'],['0x0e','AF13'],['0x12','AF21'],['0x14','AF22'],['0x16','AF23'],
126 ['0x1a','AF31'],['0x1c','AF32'],['0x1e','AF33'],['0x22','AF41'],['0x24','AF42'],['0x26','AF43'],
127 ['0x2e','EF'],['*','DSCP value']];
128 for (i = 1; i < dscp.length - 1; ++i)
129 dscp[i][1] = 'DSCP Class ' + dscp[i][1];
131 // <% layer7(); %>
132 layer7.sort();
133 for (i = 0; i < layer7.length; ++i)
134 layer7[i] = [layer7[i],layer7[i]];
135 layer7.unshift(['', 'Layer 7 (disabled)']);
137 var class1 = [[-1,'Disabled']];
138 for (i = 0; i < 10; ++i) class1.push([i, abc[i]]);
139 var class2 = class1.slice(1);
140 var ruleCounter = 0;
142 var qosg = new TomatoGrid();
144 function dscpClass(v)
146 var s, i;
148 s = '';
149 if (v != '') {
150 for (i = 1; i < dscp.length - 1; ++i) // skip 1st and last elements
151 if (dscp[i][0] * 1 == v * 1) {
152 s = dscp[i][1];
153 break;
156 return s;
159 qosg.dataToView = function(data) {
160 var b = [];
161 var s, i;
163 if (data[0] != 0) {
164 b.push(((data[0] == 1) ? 'To ' : 'From ') + data[1]);
166 if (data[2] >= -1) {
167 if (data[2] == -1) b.push('TCP/UDP');
168 else if (data[2] >= 0) b.push(protocols[data[2]] || data[2]);
169 if (data[3] != 'a') {
170 if (data[3] == 'd') s = 'Dst ';
171 else if (data[3] == 's') s = 'Src ';
172 else s = '';
173 b.push(s + 'Port: ' + data[4].replace(/:/g, '-'));
176 if (data[5] != 0) {
177 for (i = 0; i < ipp2p.length; ++i)
178 if (ipp2p[i][0] == data[5]) {
179 b.push('IPP2P: ' + ipp2p[i][1]);
180 break;
184 else if (data[6] != '') {
185 b.push('L7: ' + data[6])
188 if (data[9] != '') {
189 s = dscpClass(data[9]);
190 if (s != '') b.push(s);
191 else b.push('DSCP Value: ' + data[9]);
194 if (data[7] != '') {
195 b.push('Transferred: ' + data[7] + ((data[8] == '') ? '<small>KB+</small>' : (' - ' + data[8] + '<small>KB</small>')));
198 return [b.join('<br>'), class1[(data[10] * 1) + 1][1], escapeHTML(data[11]), (ruleCounter >= 0) ? ''+ ++ruleCounter : ''];
201 qosg.fieldValuesToData = function(row) {
202 var f = fields.getAll(row);
203 var proto = f[2].value;
204 var dir = f[3].value;
205 var vdscp = (f[7].value == '*') ? f[8].value : f[7].value;
206 if ((proto != -1) && (proto != 6) && (proto != 17)) dir = 'a';
207 return [f[0].value, f[0].selectedIndex ? f[1].value : '',
208 proto, dir, (dir != 'a') ? f[4].value : '',
209 f[5].value, f[6].value, f[9].value, f[10].value,
210 vdscp, f[11].value, f[12].value];
213 qosg.dataToFieldValues = function(data) {
214 var s = '';
216 if (data[9] != '') {
217 if (dscpClass(data[9]) == '') s = '*';
218 else s = data[9].toLowerCase();
221 return [data[0], data[1], data[2], data[3], data[4], data[5], data[6], s, data[9], data[7], data[8], data[10], data[11]];
224 qosg.resetNewEditor = function() {
225 var f = fields.getAll(this.newEditor);
226 f[0].selectedIndex = 0;
227 f[1].value = '';
228 f[2].selectedIndex = 1;
229 f[3].selectedIndex = 0;
230 f[4].value = '';
231 f[5].selectedIndex = 0;
232 f[6].selectedIndex = 0;
233 f[7].selectedIndex = 0;
234 f[8].value = '';
235 f[9].value = '';
236 f[10].value = '';
237 f[11].selectedIndex = 5;
238 f[12].value = '';
239 this.enDiFields(this.newEditor);
240 ferror.clearAll(fields.getAll(this.newEditor));
243 qosg._disableNewEditor = qosg.disableNewEditor;
244 qosg.disableNewEditor = function(disable) {
245 qosg._disableNewEditor(disable);
246 if (!disable) {
247 this.enDiFields(this.newEditor);
251 qosg.enDiFields = function(row) {
252 var f = fields.getAll(row);
253 var x;
255 f[1].disabled = (f[0].selectedIndex == 0);
256 x = f[2].value;
257 x = ((x != -1) && (x != 6) && (x != 17));
258 f[3].disabled = x;
259 if (f[3].selectedIndex == 0) x = 1;
260 f[4].disabled = x;
262 f[6].disabled = (f[5].selectedIndex != 0);
263 f[5].disabled = (f[6].selectedIndex != 0);
265 f[8].disabled = (f[7].value != '*');
268 function v_dscp(e, quiet)
270 if ((e = E(e)) == null) return 0;
271 var v = e.value;
272 if ((!v.match(/^ *(0x)?[0-9A-Fa-f]+ *$/)) || (v < 0) || (v > 63)) {
273 ferror.set(e, 'Invalid DSCP value. Valid range: 0x00-0x3F', quiet);
274 return 0;
276 e.value = '0x' + (v * 1).hex(2);
277 ferror.clear(e);
278 return 1;
281 qosg.verifyFields = function(row, quiet) {
282 var f = fields.getAll(row);
283 var a, b, e;
285 this.enDiFields(row);
286 ferror.clearAll(f);
288 a = f[0].value * 1;
289 if ((a == 1) || (a == 2)) {
290 if (!v_length(f[1], quiet, 1)) return 0;
291 if (!_v_iptaddr(f[1], quiet, 0, 1, 1)) return 0;
293 else if ((a == 3) && (!v_mac(f[1], quiet))) return 0;
295 b = f[2].selectedIndex;
296 if ((b > 0) && (b <= 3) && (f[3].selectedIndex != 0) && (!v_iptport(f[4], quiet))) return 0;
298 var BMAX = 1024 * 1024;
300 e = f[9];
301 a = e.value = e.value.trim();
302 if (a != '') {
303 if (!v_range(e, quiet, 0, BMAX)) return 0;
304 a *= 1;
307 e = f[10];
308 b = e.value = e.value.trim();
309 if (b != '') {
310 b *= 1;
311 if (b >= BMAX) e.value = '';
312 else if (!v_range(e, quiet, 0, BMAX)) return 0;
313 if (a == '') f[9].value = a = 0;
315 else if (a != '') {
316 b = BMAX;
319 if ((b != '') && (a >= b)) {
320 ferror.set(f[9], 'Invalid range', quiet);
321 return 0;
324 if (f[7].value == '*') {
325 if (!v_dscp(f[8], quiet)) return 0;
327 else f[8].value = f[7].value;
329 if (!v_nodelim(f[12], quiet, 'Description', 1)) return 0;
330 return v_length(f[12], quiet);
333 qosg.setup = function() {
334 var i, a, b;
335 a = [[-2, 'Any Protocol'],[-1,'TCP/UDP'],[6,'TCP'],[17,'UDP']];
336 for (i = 0; i < 256; ++i) {
337 if ((i != 6) && (i != 17)) a.push([i, protocols[i] || i]);
340 // what a mess...
341 this.init('qg', 'move', 50, [
342 { multi: [
343 { type: 'select', options: [[0,'Any Address'],[1,'Dst IP'],[2,'Src IP'],[3,'Src MAC']],
344 prefix: '<div class="x1a">', suffix: '</div>' },
345 { type: 'text', prefix: '<div class="x1b">', suffix: '</div>' },
347 { type: 'select', prefix: '<div class="x2a">', suffix: '</div>', options: a },
348 { type: 'select', prefix: '<div class="x2b">', suffix: '</div>',
349 options: [['a','Any Port'],['d','Dst Port'],['s','Src Port'],['x','Src or Dst']] },
350 { type: 'text', prefix: '<div class="x2c">', suffix: '</div>' },
352 { type: 'select', prefix: '<div class="x3a">', suffix: '</div>', options: ipp2p },
353 { type: 'select', prefix: '<div class="x3b">', suffix: '</div>', options: layer7 },
355 { type: 'select', prefix: '<div class="x4a">', suffix: '</div>', options: dscp },
356 { type: 'text', prefix: '<div class="x4b">', suffix: '</div>' },
358 { type: 'text', prefix: '<div class="x5a">', suffix: '</div>' },
359 { type: 'text', prefix: '<div class="x5b"> - </div><div class="x5c">', suffix: '</div><div class="x5d">KB Transferred</div>' }
360 ] },
361 { type: 'select', options: class1, vtop: 1 },
362 { type: 'text', maxlen: 32, vtop: 1 }
365 this.headerSet(['Match Rule', 'Class', 'Description', '#']);
367 // addr_type < addr < proto < port_type < port < ipp2p < L7 < bcount < dscp < class < desc
369 a = nvram.qos_orules.split('>');
370 for (i = 0; i < a.length; ++i) {
371 b = a[i].split('<');
373 if (b.length == 9) {
374 // fixup < 0.08 !!! temp
375 b.splice(7, 0, '', '', '');
377 else if (b.length == 10) {
378 // fixup < 1.28.xx55
379 b.splice(8, 0, '');
382 if (b.length == 11) {
383 c = b[7].split(':');
384 b.splice(7, 1, c[0], (c.length == 1) ? '' : c[1]);
385 b[11] = unescape(b[11]);
387 else continue;
388 b[4] = b[4].replace(/:/g, '-');
389 qosg.insertData(-1, b);
391 ruleCounter = -1;
393 this.showNewEditor();
394 this.resetNewEditor();
397 function verifyFields(focused, quiet)
399 return 1;
402 function save()
404 if (qosg.isEditing()) return;
406 var fom = E('_fom');
407 var i, a, b, c;
409 c = qosg.getAllData();
410 a = [];
411 for (i = 0; i < c.length; ++i) {
412 b = c[i].slice(0);
413 b[4] = b[4].replace(/-/g, ':');
414 b.splice(7, 2, (b[7] == '') ? '' : [b[7],b[8]].join(':'));
415 b[10] = escapeD(b[10]);
416 a.push(b.join('<'));
418 fom.qos_orules.value = a.join('>');
420 form.submit(fom, 0);
423 function init()
425 qosg.recolor();
427 </script>
429 </head>
430 <body onload='init()'>
431 <form id='_fom' method='post' action='tomato.cgi'>
432 <table id='container' cellspacing=0>
433 <tr><td colspan=2 id='header'>
434 <div class='title'>Tomato</div>
435 <div class='version'>Version <% version(); %></div>
436 </td></tr>
437 <tr id='body'><td id='navi'><script type='text/javascript'>navi()</script></td>
438 <td id='content'>
439 <div id='ident'><% ident(); %></div>
441 <!-- / / / -->
443 <input type='hidden' name='_nextpage' value='qos-classify.asp'>
444 <input type='hidden' name='_service' value='qos-restart'>
445 <input type='hidden' name='qos_orules'>
447 <div class='section-title'>Outbound Direction</div>
448 <div class='section'>
449 <table class='tomato-grid' cellspacing=1 id='qg'></table>
450 </div>
452 <br>
453 <script type='text/javascript'>
454 if (nvram.qos_enable != '1') {
455 W('<div class="note-disabled"><b>QoS disabled.</b> &nbsp; <a href="qos-settings.asp">Enable &raquo;</a></div>');
457 else {
458 show_notice1('<% notice("iptables"); %>');
460 </script>
462 <!-- / / / -->
464 </td></tr>
465 <tr><td id='footer' colspan=2>
466 <span id='footer-msg'></span>
467 <input type='button' value='Save' id='save-button' onclick='save()'>
468 <input type='button' value='Cancel' id='cancel-button' onclick='reloadPage();'>
469 </td></tr>
470 </table>
471 </form>
472 <script type='text/javascript'>qosg.setup();</script>
473 </body>
474 </html>