WLAN can now be assigned to any LAN bridge. Changing VLAN/bridge settings no longer...
[tomato.git] / release / src / router / www / advanced-vlan.asp
blobf9749ea7287bb7ecbb373873b10d6623b9fb46b7
1 <!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.0//EN'>
2 <!--
3 Tomato GUI
4 Copyright (C) 2006-2007 Jonathan Zarate
5 http://www.polarcloud.com/tomato/
6 For use with Tomato Firmware only.
7 No part of this file may be used without permission.
8 VLAN admin module by Augusto Bott
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(); %>] Advanced: VLAN</title>
15 <link rel='stylesheet' type='text/css' href='tomato.css'>
16 <% css(); %>
17 <script type='text/javascript' src='tomato.js'></script>
18 <style type='text/css'>
19 #vlan-grid .co1,
20 #vlan-grid .co2,
21 #vlan-grid .co3,
22 #vlan-grid .co4,
23 #vlan-grid .co5,
24 #vlan-grid .co6,
25 #vlan-grid .co7,
26 #vlan-grid .co8,
27 #vlan-grid .co9,
28 #vlan-grid .co10,
29 #vlan-grid .co11,
30 #vlan-grid .co12,
31 #vlan-grid .co13 {
32 text-align: center;
34 #vlan-grid .centered {
35 text-align: center;
37 </style>
38 <script type='text/javascript' src='wireless.jsx?_http_id=<% nv(http_id); %>'></script>
39 <script type='text/javascript'>
40 <% nvram ("vlan0ports,vlan1ports,vlan2ports,vlan3ports,vlan4ports,vlan5ports,vlan6ports,vlan7ports,vlan8ports,vlan9ports,vlan10ports,vlan11ports,vlan12ports,vlan13ports,vlan14ports,vlan15ports,vlan0hwname,vlan1hwname,vlan2hwname,vlan3hwname,vlan4hwname,vlan5hwname,vlan6hwname,vlan7hwname,vlan8hwname,vlan9hwname,vlan10hwname,vlan11hwname,vlan12hwname,vlan13hwname,vlan14hwname,vlan15hwname,wan_ifnameX,manual_boot_nv,boardtype,boardflags,trunk_vlan_so,lan_ifname,lan_ifnames,lan1_ifname,lan1_ifnames,lan2_ifname,lan2_ifnames,lan3_ifname,lan3_ifnames");%>
42 var port_vlan_supported = 0;
43 var trunk_vlan_supported = 0;
45 // does not seem to be strictly necessary for boardflags as it's supposed to be a bitmap
46 nvram['boardflags'] = ((nvram['boardflags'].toLowerCase().indexOf('0x') != -1) ? '0x' : '') + String('0000' + ((nvram['boardflags'].toLowerCase()).replace('0x',''))).slice(-4);
47 // but the contents of router/shared/id.c seem to indicate string formatting/padding might be required for some models as we check if strings match
48 nvram['boardtype'] = ((nvram['boardtype'].toLowerCase().indexOf('0x') != -1) ? '0x' : '') + String('0000' + ((nvram['boardtype'].toLowerCase()).replace('0x',''))).slice(-4);
50 // see http://www.dd-wrt.com/wiki/index.php/Hardware#Boardflags and router/shared/id.c
51 if(nvram['boardflags'] & 0x0100) { // BFL_ENETVLAN = this board has vlan capability
52 port_vlan_supported = 1;
55 // TESTED ONLY ON WRT54G v2 (boardtype 0x0101) and WRT54GL v1.1 (boardtype 0x0467)
56 // attempt of cross-referencing boardtypes/routers mentioned on id.c and the wiki page above
57 switch(nvram['boardtype']) {
58 case '0x0467': // WRT54GL 1.x, WRT54GS 3.x/4.x
59 case '0x048e': // WL-520GU, WL-500G Premium v2
60 case '0x04ef': // WRT320N/E2000
61 case '0x04cf': // WRT610Nv2/E3000, RT-N16
62 trunk_vlan_supported = 1;
63 break;
64 default:
65 break;
68 // TESTED ONLY ON WRT54G v2 (boardtype 0x0101) and WRT54GL v1.1 (boardtype 0x0467)
69 // info on some of these boardtypes/routers obtained from
70 // http://wiki.openwrt.org/toh/asus/start
71 // http://wiki.openwrt.org/toh/linksys/start
72 // http://wiki.openwrt.org/toh/start
73 switch(nvram['boardtype']) {
74 case '0x0467': // WRT54GL 1.x, WRT54GS 3.x/4.x
75 case '0x048e': // WL-520GU, WL-500G Premium v2
76 COL_P0N = '3';
77 COL_P1N = '2';
78 COL_P2N = '1';
79 COL_P3N = '0';
80 COL_P4N = '4';
81 break;
82 case '0x04ef': // WRT320N/E2000
83 case '0x04cf': // WRT610Nv2/E3000, RT-N16
84 COL_P0N = '4';
85 COL_P1N = '3';
86 COL_P2N = '2';
87 COL_P3N = '1';
88 COL_P4N = '0';
89 break;
90 // should work on WRT54G v2/v3, WRT54GS v1/v2 and others
91 default:
92 COL_P0N = '1';
93 COL_P1N = '2';
94 COL_P2N = '3';
95 COL_P3N = '4';
96 COL_P4N = '0';
97 break;
100 var MAX_PORT_ID = 4;
101 var MAX_VLAN_ID = 15;
102 var MAX_BRIDGE_ID = 4;
104 var COL_VID = 0;
105 var COL_P0 = 1;
106 var COL_P0T = 2;
107 var COL_P1 = 3;
108 var COL_P1T = 4;
109 var COL_P2 = 5;
110 var COL_P2T = 6;
111 var COL_P3 = 7;
112 var COL_P3T = 8;
113 var COL_P4 = 9;
114 var COL_P4T = 10;
115 var COL_VID_DEF = 11;
116 var COL_BRI = 12;
118 var SWITCH_INTERNAL_PORT=0; // set to either 5 or 8 when nvram settings are read (FastE or GigE routers)
119 var PORT_VLAN_SUPPORT_OVERRIDE=0; // option made available for experimental purposes on routers known to support port-based VLANs, but not confirmed to support 801.11q trunks
121 function verifyFields(focused, quiet){
122 PORT_VLAN_SUPPORT_OVERRIDE=(E('_f_trunk_vlan_so').checked ? 1 : 0);
123 var wlan = E('_f_bridge_wlan_to');
124 if (wl_ifaces.length < 1) {
125 wlan.disabled=true;
126 } else {
127 if(nvram.lan_ifname.length < 1)
128 wlan.options[0].disabled=true;
129 if(nvram.lan1_ifname.length < 1)
130 wlan.options[1].disabled=true;
131 if(nvram.lan2_ifname.length < 1)
132 wlan.options[2].disabled=true;
133 if(nvram.lan3_ifname.length < 1)
134 wlan.options[3].disabled=true;
138 function save()
140 if (vlg.isEditing()) return;
142 var fom = E('_fom');
143 fom.trunk_vlan_so.value = (E('_f_trunk_vlan_so').checked ? 1 : 0);
144 // wipe out relevant fields just in case this is not the first time we try to submit
145 for (var i = 0 ; i <= MAX_VLAN_ID ; i++) {
146 fom['vlan' + i + 'ports'].value = '';
147 fom['vlan' + i + 'hwname'].value = '';
149 fom['wan_ifnameX'].value = '';
150 fom['lan_ifnames'].value = '';
151 fom['lan1_ifnames'].value = '';
152 fom['lan2_ifnames'].value = '';
153 fom['lan3_ifnames'].value = '';
155 var v = '';
156 var d = vlg.getAllData();
158 for (var i = 0; i < d.length; ++i) {
159 var p = '';
160 p += (d[i][COL_P0].toString() != '0') ? COL_P0N : '';
161 p += (((trunk_vlan_supported) || (PORT_VLAN_SUPPORT_OVERRIDE)) && (d[i][COL_P0T].toString() != '0')) ? 't' : '';
162 p += trailingSpace(p);
164 p += (d[i][COL_P1].toString() != '0') ? COL_P1N : '';
165 p += (((trunk_vlan_supported) || (PORT_VLAN_SUPPORT_OVERRIDE)) && (d[i][COL_P1T].toString() != '0')) ? 't' : '';
166 p += trailingSpace(p);
168 p += (d[i][COL_P2].toString() != '0') ? COL_P2N : '';
169 p += (((trunk_vlan_supported) || (PORT_VLAN_SUPPORT_OVERRIDE)) && (d[i][COL_P2T].toString() != '0')) ? 't' : '';
170 p += trailingSpace(p);
172 p += (d[i][COL_P3].toString() != '0') ? COL_P3N : '';
173 p += (((trunk_vlan_supported) || (PORT_VLAN_SUPPORT_OVERRIDE)) && (d[i][COL_P3T].toString() != '0')) ? 't' : '';
174 p += trailingSpace(p);
176 p += (d[i][COL_P4].toString() != '0') ? COL_P4N : '';
177 p += (((trunk_vlan_supported) || (PORT_VLAN_SUPPORT_OVERRIDE)) && (d[i][COL_P4T].toString() != '0')) ? 't' : '';
178 p += trailingSpace(p);
180 p += (d[i][COL_VID_DEF].toString() != '0') ? (SWITCH_INTERNAL_PORT + '*') : SWITCH_INTERNAL_PORT;
182 // arrange port numbers in ascending order just to be safe (not sure if this is really needed... mostly, cosmetics?)
183 p = p.split(" ");
184 p = p.sort(cmpInt);
185 p = p.join(" ");
187 v += (d[i][COL_VID_DEF].toString() != '0') ? d[i][0] : '';
189 fom['vlan'+d[i][COL_VID]+'ports'].value = p;
190 fom['vlan'+d[i][COL_VID]+'hwname'].value = 'et0';
192 fom['wan_ifnameX'].value += (d[i][COL_BRI] == '2') ? 'vlan'+d[i][0] : '';
193 fom['lan_ifnames'].value += (d[i][COL_BRI] == '3') ? 'vlan'+d[i][0] : '';
194 // fom['lan_ifnames'].value += trailingSpace(fom['lan_ifnames'].value);
195 // alert('vlan'+d[i][0]+'ports='+fom['vlan'+d[i][0]+'ports'].value+'\nvlan'+d[i][0]+'hwname='+fom['vlan'+d[i][0]+'hwname'].value);
196 fom['lan1_ifnames'].value += (d[i][COL_BRI] == '4') ? 'vlan'+d[i][0] : '';
197 fom['lan2_ifnames'].value += (d[i][COL_BRI] == '5') ? 'vlan'+d[i][0] : '';
198 fom['lan3_ifnames'].value += (d[i][COL_BRI] == '6') ? 'vlan'+d[i][0] : '';
201 var wlan = E('_f_bridge_wlan_to');
202 // alert(wlan.selectedIndex);
203 switch(parseInt(wlan.selectedIndex)) {
204 case 0:
205 fom['lan_ifnames'].value += ' ' + wl_ifaces[0][0];
206 break;
207 case 1:
208 fom['lan1_ifnames'].value += ' ' + wl_ifaces[0][0];
209 break;
210 case 2:
211 fom['lan2_ifnames'].value += ' ' + wl_ifaces[0][0];
212 break;
213 case 3:
214 fom['lan3_ifnames'].value += ' ' + wl_ifaces[0][0];
215 break;
217 // var lif = nvram['lan_ifnames'].split(' ');
218 // for (var j = 0; j < lif.length; j++) {
219 // fom['lan_ifnames'].value += (lif[j].indexOf('vlan') != -1) ? '' : lif[j];
220 // fom['lan_ifnames'].value += trailingSpace(fom['lan_ifnames'].value);
221 // }
222 // alert('lan_ifnames=' + fom['lan_ifnames'].value);
223 // alert('lan1_ifnames=' + fom['lan1_ifnames'].value);
224 // alert('lan2_ifnames=' + fom['lan2_ifnames'].value);
225 // alert('lan3_ifnames=' + fom['lan3_ifnames'].value);
227 // for some models, Tomato checks for a few vital/crucial nvram settings at init time
228 // in some cases, if some/any of them are not found, a full nvram reset/clean could be triggered
229 // so, to (try to) play it safe, we check for the 1st needed/available/required
230 // VLAN for FastE (vlan0 is usually LAN) and GigE routers (vlan1 is usually LAN)
231 if((fom['vlan0ports'].value.length < 1) || (fom['vlan0hwname'].value.length < 1) ||
232 (fom['vlan1ports'].value.length < 1) || (fom['vlan1hwname'].value.length < 1))
233 fom['manual_boot_nv'].value = '1';
234 else
235 fom['manual_boot_nv'].value = nvram['manual_boot_nv'];
237 var e = E('footer-msg');
239 if(vlg.countWan() != 1) {
240 e.innerHTML = 'Cannot proceed: one VID must be assigned to WAN.';
241 e.style.visibility = 'visible';
242 setTimeout(
243 function() {
244 e.innerHTML = '';
245 e.style.visibility = 'hidden';
246 }, 5000);
247 return;
250 if(vlg.countLan(0) != 1) {
251 e.innerHTML = 'Cannot proceed: one and only one VID must be assigned to the primary LAN (br0).';
252 e.style.visibility = 'visible';
253 setTimeout(
254 function() {
255 e.innerHTML = '';
256 e.style.visibility = 'hidden';
257 }, 5000);
258 return;
261 if (v.length < 1) {
262 e.innerHTML = 'Cannot proceed without setting a default VID';
263 e.style.visibility = 'visible';
264 setTimeout(
265 function() {
266 e.innerHTML = '';
267 e.style.visibility = 'hidden';
268 }, 5000);
269 return;
272 if (confirm("Network will be restarted on this router. Proceed?"))
273 form.submit(fom, 0);
276 function trailingSpace(s)
278 return ((s.length>0)&&(s.charAt(s.length-1) != ' ')) ? ' ' : '';
281 if(port_vlan_supported) { // aka if(supported_hardware) block
282 var vlg = new TomatoGrid();
283 vlg.setup = function() {
284 this.init('vlan-grid', '', (MAX_VLAN_ID + 1), [
285 { type: 'select', options: [[0, '0'],[1, '1'],[2, '2'],[3, '3'],[4, '4'],[5, '5'],[6, '6'],[7, '7'],[8, '8'],[9, '9'],[10, '10'],[11, '11'],[12, '12'],[13, '13'],[14, '14'],[15, '15']], prefix: '<div class="centered">', suffix: '</div>' },
286 { type: 'checkbox', prefix: '<div class="centered">', suffix: '</div>' },
287 { type: 'checkbox', prefix: '<div class="centered">', suffix: '</div>' },
288 { type: 'checkbox', prefix: '<div class="centered">', suffix: '</div>' },
289 { type: 'checkbox', prefix: '<div class="centered">', suffix: '</div>' },
290 { type: 'checkbox', prefix: '<div class="centered">', suffix: '</div>' },
291 { type: 'checkbox', prefix: '<div class="centered">', suffix: '</div>' },
292 { type: 'checkbox', prefix: '<div class="centered">', suffix: '</div>' },
293 { type: 'checkbox', prefix: '<div class="centered">', suffix: '</div>' },
294 { type: 'checkbox', prefix: '<div class="centered">', suffix: '</div>' },
295 { type: 'checkbox', prefix: '<div class="centered">', suffix: '</div>' },
296 { type: 'checkbox', prefix: '<div class="centered">', suffix: '</div>' },
297 { type: 'select', options: [[1, 'none'],[2, 'WAN'],[3, 'LAN (br0)'],[4, 'LAN1 (br1)'],[5, 'LAN2 (br2)'],[6, 'LAN3 (br3)']], prefix: '<div class="centered">', suffix: '</div>' }]);
299 this.headerSet(['VID', 'Port 1', 'Tagged', 'Port 2', 'Tagged', 'Port 3', 'Tagged', 'Port 4', 'Tagged', 'WAN Port', 'Tagged', 'Default', 'Bridge']);
301 // find out which vlans are supposed to be bridged to each LAN
302 var bridged = [];
304 for (var i = 0 ; i < MAX_BRIDGE_ID ; i++) {
305 var j = (i == 0) ? '' : i.toString();
306 var l = nvram['lan' + j + '_ifnames'].split(' ');
307 // alert('lan' + j + '_ifnames=' + l);
308 for (var k = 0 ; k < l.length; k++) {
309 // alert("bridge br" + i + "=vlan" + parseInt(l[k].replace('vlan','')));
310 if(l[k].indexOf('vlan') != -1) {
311 // alert('lan' + j + '_ifname=br' + nvram['lan' + j + '_ifname'].replace('br',''));
312 bridged[parseInt(l[k].replace('vlan',''))] = (3 + parseInt(nvram['lan' + j + '_ifname'].replace('br',''))).toString();
314 // WLAN
315 for (var uidx = 0; uidx < wl_ifaces.length; ++uidx) {
316 if(l[k].indexOf(wl_ifaces[uidx][0]) != -1) {
317 E('_f_bridge_wlan_to').selectedIndex=i;
323 // WAN port
324 bridged[parseInt(nvram['wan_ifnameX'].replace('vlan',''))] = '2';
326 // go thru all possible VLANs
327 for (var i = 0 ; i <= MAX_VLAN_ID ; i++) {
328 var port = [];
329 var tagged = [];
330 if ((nvram['vlan' + i + 'hwname'].length > 0) || (nvram['vlan' + i + 'ports'].length > 0)) {
331 // (re)initialize our bitmap for this particular iteration
332 for (var j=0; j <= MAX_PORT_ID ; j++) {
333 port[j] = '0';
334 tagged[j] = '0';
336 // which ports are members of this VLAN?
337 var m=nvram['vlan' + i + 'ports'].split(' ');
338 for (var j = 0; j < (m.length) ; j++) {
339 port[parseInt(m[j].charAt(0))] = '1';
340 tagged[parseInt(m[j].charAt(0))] = (((trunk_vlan_supported) || (PORT_VLAN_SUPPORT_OVERRIDE)) && (m[j].indexOf('t') != -1)) ? '1' : '0';
343 if (port_vlan_supported) {
344 if((nvram['vlan' + i + 'ports']).indexOf('*') != -1)
345 SWITCH_INTERNAL_PORT=(nvram['vlan' + i + 'ports']).charAt((nvram['vlan' + i + 'ports']).indexOf('*')-1);
347 vlg.insertData(-1, [ i.toString(),
348 port[COL_P0N], tagged[COL_P0N],
349 port[COL_P1N], tagged[COL_P1N],
350 port[COL_P2N], tagged[COL_P2N],
351 port[COL_P3N], tagged[COL_P3N],
352 port[COL_P4N], tagged[COL_P4N],
353 (((nvram['vlan' + i + 'ports']).indexOf('*') != -1) ? '1' : '0' ),
354 (bridged[i] != null) ? bridged[i] : '1' ]);
358 vlg.canDelete = false;
359 vlg.sort(0);
360 vlg.showNewEditor();
361 vlg.resetNewEditor();
364 vlg.countElem = function(f, v)
366 var data = this.getAllData();
367 var total = 0;
368 for (var i = 0; i < data.length; ++i) {
369 total += (data[i][f] == v) ? 1 : 0;
371 return total;
374 vlg.countDefaultVID = function()
376 return this.countElem(COL_VID_DEF,1);
379 vlg.countVID = function (v)
381 return this.countElem(COL_VID,v);
384 vlg.countWan = function()
386 return this.countElem(COL_BRI,2);
389 vlg.countLan = function(l)
391 return this.countElem(COL_BRI,l+3);
394 vlg.verifyFields = function(row, quiet) {
395 var valid = 1;
396 var f = fields.getAll(row);
398 if(((trunk_vlan_supported) || (PORT_VLAN_SUPPORT_OVERRIDE)) && (f[COL_P0].checked == 1)) {
399 f[COL_P0T].disabled=0;
400 } else {
401 f[COL_P0T].disabled=1;
402 f[COL_P0T].checked=0;
404 if(((trunk_vlan_supported) || (PORT_VLAN_SUPPORT_OVERRIDE)) && (f[COL_P1].checked == 1)) {
405 f[COL_P1T].disabled=0;
406 } else {
407 f[COL_P1T].disabled=1;
408 f[COL_P1T].checked=0;
410 if(((trunk_vlan_supported) || (PORT_VLAN_SUPPORT_OVERRIDE)) && (f[COL_P2].checked == 1)) {
411 f[COL_P2T].disabled=0;
412 } else {
413 f[COL_P2T].disabled=1;
414 f[COL_P2T].checked=0;
416 if(((trunk_vlan_supported) || (PORT_VLAN_SUPPORT_OVERRIDE)) && (f[COL_P3].checked == 1)) {
417 f[COL_P3T].disabled=0;
418 } else {
419 f[COL_P3T].disabled=1;
420 f[COL_P3T].checked=0;
422 if(((trunk_vlan_supported) || (PORT_VLAN_SUPPORT_OVERRIDE)) && (f[COL_P4].checked == 1)) {
423 f[COL_P4T].disabled=0;
424 } else {
425 f[COL_P4T].disabled=1;
426 f[COL_P4T].checked=0;
429 if(this.countDefaultVID() > 0) {
430 f[COL_VID_DEF].disabled=1;
431 f[COL_VID_DEF].checked=0;
434 if((this.countDefaultVID() > 0) && (f[COL_VID_DEF].checked ==1)) {
435 ferror.set(f[COL_VID_DEF], 'Only one VID can be selected as the default VID', quiet);
436 valid = 0;
437 } else {
438 ferror.clear(f[COL_VID_DEF]);
441 if(this.countVID(f[COL_VID].selectedIndex) > 0) {
442 ferror.set(f[COL_VID], 'Cannot add more than one entry with VID ' + f[0].selectedIndex, quiet);
443 valid = 0;
444 } else {
445 ferror.clear(f[COL_VID]);
448 if ((this.countWan() > 0) && (f[COL_BRI].selectedIndex == 1)) {
449 ferror.set(f[COL_BRI],'Only one VID can be used as WAN at any time', quiet);
450 valid = 0;
451 } else {
452 ferror.clear(f[COL_BRI]);
455 for(var i=0; i<4; i++) {
456 if ((this.countLan(i) > 0) && (f[COL_BRI].selectedIndex == (i+2))) {
457 ferror.set(f[COL_BRI],'One and only one VID can be used for LAN' + ((i==0) ? '' : i ) + ' (br'+i+') at any time', quiet);
458 valid = 0;
459 } else {
460 ferror.clear(f[COL_BRI]);
464 return valid;
467 vlg.dataToView = function(data) {
468 return [data[COL_VID],
469 (data[COL_P0].toString() != '0') ? 'Yes' : '',
470 (data[COL_P0T].toString() != '0') ? 'On' : '',
471 (data[COL_P1].toString() != '0') ? 'Yes' : '',
472 (data[COL_P1T].toString() != '0') ? 'On' : '',
473 (data[COL_P2].toString() != '0') ? 'Yes' : '',
474 (data[COL_P2T].toString() != '0') ? 'On' : '',
475 (data[COL_P3].toString() != '0') ? 'Yes' : '',
476 (data[COL_P3T].toString() != '0') ? 'On' : '',
477 (data[COL_P4].toString() != '0') ? 'Yes' : '',
478 (data[COL_P4T].toString() != '0') ? 'On' : '',
479 (data[COL_VID_DEF].toString() != '0') ? '*' : '',
480 ['', 'WAN', 'LAN (br0)', 'LAN1 (br1)', 'LAN2 (br2)', 'LAN3 (br3)' ][data[COL_BRI] - 1]];
483 vlg.dataToFieldValues = function (data) {
484 return [data[COL_VID],
485 (data[COL_P0] != 0) ? 'checked' : '',
486 (data[COL_P0T] != 0) ? 'checked' : '',
487 (data[COL_P1] != 0) ? 'checked' : '',
488 (data[COL_P1T] != 0) ? 'checked' : '',
489 (data[COL_P2] != 0) ? 'checked' : '',
490 (data[COL_P2T] != 0) ? 'checked' : '',
491 (data[COL_P3] != 0) ? 'checked' : '',
492 (data[COL_P3T] != 0) ? 'checked' : '',
493 (data[COL_P4] != 0) ? 'checked' : '',
494 (data[COL_P4T] != 0) ? 'checked' : '',
495 (data[COL_VID_DEF] != 0) ? 'checked' : '',
496 data[COL_BRI]];
499 vlg.fieldValuesToData = function(row) {
500 var f = fields.getAll(row);
501 return [f[COL_VID].value,
502 f[COL_P0].checked ? 1 : 0,
503 f[COL_P0T].checked ? 1 : 0,
504 f[COL_P1].checked ? 1 : 0,
505 f[COL_P1T].checked ? 1 : 0,
506 f[COL_P2].checked ? 1 : 0,
507 f[COL_P2T].checked ? 1 : 0,
508 f[COL_P3].checked ? 1 : 0,
509 f[COL_P3T].checked ? 1 : 0,
510 f[COL_P4].checked ? 1 : 0,
511 f[COL_P4T].checked ? 1 : 0,
512 f[COL_VID_DEF].checked ? 1 : 0,
513 f[COL_BRI].value];
516 vlg.onCancel = function() {
517 this.removeEditor();
518 this.showSource();
519 this.disableNewEditor(false);
521 this.resetNewEditor();
524 vlg.onAdd = function() {
525 var data;
527 this.moving = null;
528 this.rpHide();
530 if (!this.verifyFields(this.newEditor, false)) return;
532 data = this.fieldValuesToData(this.newEditor);
533 this.insertData(-1, data);
535 this.disableNewEditor(false);
536 this.resetNewEditor();
538 this.resort();
541 vlg.onOK = function() {
542 var i, data, view;
544 if (!this.verifyFields(this.editor, false)) return;
546 data = this.fieldValuesToData(this.editor);
547 view = this.dataToView(data);
549 this.source.setRowData(data);
550 for (i = 0; i < this.source.cells.length; ++i) {
551 this.source.cells[i].innerHTML = view[i];
554 this.removeEditor();
555 this.showSource();
556 this.disableNewEditor(false);
558 this.resetNewEditor();
559 this.resort();
562 vlg.onDelete = function() {
563 this.removeEditor();
564 elem.remove(this.source);
565 this.source = null;
566 this.disableNewEditor(false);
568 this.resetNewEditor();
571 vlg.sortCompare = function(a, b) {
572 var obj = TGO(a);
573 var col = obj.sortColumn;
574 if (this.sortColumn == 0) {
575 var r = cmpInt(parseInt(a.cells[col].innerHTML), parseInt(b.cells[col].innerHTML));
576 } else {
577 var r = cmpText(a.cells[col].innerHTML, b.cells[col].innerHTML);
579 return obj.sortAscending ? r : -r;
582 vlg.resetNewEditor = function() {
583 var f = fields.getAll(this.newEditor);
584 if(nvram.lan_ifname.length < 1)
585 f[COL_BRI].options[2].disabled=true;
586 else
587 f[COL_BRI].options[2].disabled=false;
589 if(nvram.lan1_ifname.length < 1)
590 f[COL_BRI].options[3].disabled=true;
591 else
592 f[COL_BRI].options[3].disabled=false;
594 if(nvram.lan2_ifname.length < 1)
595 f[COL_BRI].options[4].disabled=true;
596 else
597 f[COL_BRI].options[4].disabled=false;
599 if(nvram.lan3_ifname.length < 1)
600 f[COL_BRI].options[5].disabled=true;
601 else
602 f[COL_BRI].options[5].disabled=false;
604 if (f[COL_VID].selectedIndex < 0)
605 f[COL_VID].selectedIndex=0;
606 while(this.countVID(f[COL_VID].selectedIndex) > 0)
607 f[COL_VID].selectedIndex = (f[COL_VID].selectedIndex%(MAX_VLAN_ID+1))+1;
608 f[COL_P0].checked = 0;
609 f[COL_P0T].checked = 0;
610 f[COL_P0T].disabled = 1;
611 f[COL_P1].checked = 0;
612 f[COL_P1T].checked = 0;
613 f[COL_P1T].disabled = 1;
614 f[COL_P2].checked = 0;
615 f[COL_P2T].checked = 0;
616 f[COL_P2T].disabled = 1;
617 f[COL_P3].checked = 0;
618 f[COL_P3T].checked = 0;
619 f[COL_P3T].disabled = 1;
620 f[COL_P4].checked = 0;
621 f[COL_P4T].checked = 0;
622 f[COL_P4T].disabled = 1;
623 f[COL_VID_DEF].checked = 0;
624 if (this.countDefaultVID()>0)
625 f[COL_VID_DEF].disabled = 1;
626 f[COL_BRI].selectedIndex = 0;
627 ferror.clearAll(fields.getAll(this.newEditor));
629 } // end of the so-called if(supported_device) block
631 function init()
633 if(port_vlan_supported) {
634 vlg.recolor();
635 vlg.resetNewEditor();
639 function earlyInit()
641 if(!port_vlan_supported) {
642 E('save-button').disabled = 1;
643 return;
645 PORT_VLAN_SUPPORT_OVERRIDE = ((nvram['trunk_vlan_so'] == '1') ? 1 : 0);
648 </script>
649 </head>
650 <body onload='init()'>
651 <form id='_fom' method='post' action='tomato.cgi'>
652 <table id='container' cellspacing=0>
653 <tr><td colspan=2 id='header'>
654 <div class='title'>Tomato</div>
655 <div class='version'>Version <% version(); %></div>
656 </td></tr>
657 <tr id='body'><td id='navi'><script type='text/javascript'>navi()</script></td>
658 <td id='content'>
659 <div id='ident'><% ident(); %></div>
660 <input type='hidden' name='_nextpage' value='advanced-vlan.asp'>
661 <input type='hidden' name='_nextwait' value='30'>
662 <input type='hidden' name='_service' value='net-restart'>
664 <input type='hidden' name='vlan0ports'>
665 <input type='hidden' name='vlan1ports'>
666 <input type='hidden' name='vlan2ports'>
667 <input type='hidden' name='vlan3ports'>
668 <input type='hidden' name='vlan4ports'>
669 <input type='hidden' name='vlan5ports'>
670 <input type='hidden' name='vlan6ports'>
671 <input type='hidden' name='vlan7ports'>
672 <input type='hidden' name='vlan8ports'>
673 <input type='hidden' name='vlan9ports'>
674 <input type='hidden' name='vlan10ports'>
675 <input type='hidden' name='vlan11ports'>
676 <input type='hidden' name='vlan12ports'>
677 <input type='hidden' name='vlan13ports'>
678 <input type='hidden' name='vlan14ports'>
679 <input type='hidden' name='vlan15ports'>
680 <input type='hidden' name='vlan0hwname'>
681 <input type='hidden' name='vlan1hwname'>
682 <input type='hidden' name='vlan2hwname'>
683 <input type='hidden' name='vlan3hwname'>
684 <input type='hidden' name='vlan4hwname'>
685 <input type='hidden' name='vlan5hwname'>
686 <input type='hidden' name='vlan6hwname'>
687 <input type='hidden' name='vlan7hwname'>
688 <input type='hidden' name='vlan8hwname'>
689 <input type='hidden' name='vlan9hwname'>
690 <input type='hidden' name='vlan10hwname'>
691 <input type='hidden' name='vlan11hwname'>
692 <input type='hidden' name='vlan12hwname'>
693 <input type='hidden' name='vlan13hwname'>
694 <input type='hidden' name='vlan14hwname'>
695 <input type='hidden' name='vlan15hwname'>
696 <input type='hidden' name='wan_ifnameX'>
697 <input type='hidden' name='manual_boot_nv'>
698 <input type='hidden' name='lan_ifnames'>
699 <input type='hidden' name='lan1_ifnames'>
700 <input type='hidden' name='lan2_ifnames'>
701 <input type='hidden' name='lan3_ifnames'>
702 <input type='hidden' name='trunk_vlan_so'>
704 <div id='sesdiv' style='display:none'>
705 <div class='section-title'>VLAN</div>
706 <div class='section'>
707 <table class='tomato-grid' cellspacing=1 id='vlan-grid'></table>
708 <script type='text/javascript'>
709 createFieldTable('', [
710 { title: 'Bridge WLAN to', name: 'f_bridge_wlan_to', type: 'select', options: [[0,'LAN'],[1,'LAN1'],[2,'LAN2'],[3,'LAN3'],[4,'none']] }
712 if(port_vlan_supported) vlg.setup();
713 </script>
714 </div>
716 <div class='section-title'>Notes</div>
717 <div class='section'>
718 <ul>
719 <li><b>VID</b> - Unique identifier of a VLAN.</li>
720 <li><b>Ports 1-4 &amp; WAN</b> - Which ethernet ports on the router should be members of this VLAN.</li>
721 <li><b>Tagged</b> - Enable 802.1q tagging of ethernet frames on a particular port/VLAN
722 <script type='text/javascript'>
723 if(!trunk_vlan_supported)
724 W(' <i>(not known to be supported on this model)</i>');
725 </script>
726 </li>
727 <li><b>Default</b> - VLAN ID assigned to untagged frames received by the router.</li>
728 <li><b>Bridge</b> - Determines if this VLAN ID should be treated as WAN, part of a LAN bridge or just left alone (i.e. member of a 802.1q trunk, being managed manually via scripts, etc...).</li>
729 </ul>
730 <small>
731 <ul>
732 <li><b>Other relevant notes/hints:</b>
733 <ul>
734 <li>One VID <i>must</i> be assigned to WAN.</li>
735 <li>One VID <i>must</i> be selected as the default.</li>
736 <script type='text/javascript'>
737 if((trunk_vlan_supported) || (nvram.trunk_vlan_so == '1'))
738 W('<li>To prevent 802.1q compatibility issues, avoid using VID "0" as 802.1q specifies that frames with a tag of "0" do not belong to any VLAN.</li>');
739 </script>
740 </ul>
741 </ul>
742 <div id='trunk_vlan_override' style='display:none'>
743 <div class='section-title'>Trunk VLAN support override (experimental)</div>
744 <div class='section'>
745 <script type='text/javascript'>
746 createFieldTable('', [
747 { title: 'Enable', name: 'f_trunk_vlan_so', type: 'checkbox', value: nvram.trunk_vlan_so == '1' },
749 </script>
750 </div>
751 </div>
752 </div>
753 </small>
754 </div>
755 </div>
756 <script type='text/javascript'>
757 if(!port_vlan_supported)
758 W('<i>This feature is not supported on this router.</i>');
759 else {
760 E('sesdiv').style.display = '';
761 if(!trunk_vlan_supported)
762 E('trunk_vlan_override').style.display = '';
764 </script>
765 </td></tr>
766 <tr><td id='footer' colspan=2>
767 <span id='footer-msg'></span>
768 <input type='button' value='Save' id='save-button' onclick='save()'>
769 <input type='button' value='Cancel' id='cancel-button' onclick='javascript:reloadPage();'>
770 </td></tr>
771 </table>
772 </form>
773 <script type='text/javascript'>earlyInit(); verifyFields(null,1);</script>
774 </body>
775 </html>