1 <!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML
4.0//EN'
>
4 Copyright (C) 2006-2008 Jonathan Zarate
5 http://www.polarcloud.com/tomato/
7 Portions Copyright (C) 2008-2010 Keith Moyer, tomatovpn@keithmoyer.com
9 For use with Tomato Firmware only.
10 No part of this file may be used without permission.
14 <meta http-equiv='content-type' content='text/html;charset=utf-
8'
>
15 <meta name='robots' content='noindex,nofollow'
>
16 <title>[<%
ident(); %>] OpenVPN: Server
</title>
17 <link rel='stylesheet' type='text/css' href='tomato.css'
>
18 <link rel='stylesheet' type='text/css' href='color.css'
>
19 <script type='text/javascript' src='tomato.js'
></script>
20 <script type='text/javascript' src='vpn.js'
></script>
21 <script type='text/javascript'
>
23 // <% nvram("vpn_server_eas,vpn_server_dns,vpn_server1_poll,vpn_server1_if,vpn_server1_proto,vpn_server1_port,vpn_server1_firewall,vpn_server1_sn,vpn_server1_nm,vpn_server1_local,vpn_server1_remote,vpn_server1_dhcp,vpn_server1_r1,vpn_server1_r2,vpn_server1_crypt,vpn_server1_comp,vpn_server1_cipher,vpn_server1_reneg,vpn_server1_hmac,vpn_server1_plan,vpn_server1_ccd,vpn_server1_c2c,vpn_server1_ccd_excl,vpn_server1_ccd_val,vpn_server1_pdns,vpn_server1_rgw,vpn_server1_custom,vpn_server1_static,vpn_server1_ca,vpn_server1_crt,vpn_server1_key,vpn_server1_dh,vpn_server2_poll,vpn_server2_if,vpn_server2_proto,vpn_server2_port,vpn_server2_firewall,vpn_server2_sn,vpn_server2_nm,vpn_server2_local,vpn_server2_remote,vpn_server2_dhcp,vpn_server2_r1,vpn_server2_r2,vpn_server2_crypt,vpn_server2_comp,vpn_server2_cipher,vpn_server2_reneg,vpn_server2_hmac,vpn_server2_plan,vpn_server2_ccd,vpn_server2_c2c,vpn_server2_ccd_excl,vpn_server2_ccd_val,vpn_server2_pdns,vpn_server2_rgw,vpn_server2_custom,vpn_server2_static,vpn_server2_ca,vpn_server2_crt,vpn_server2_key,vpn_server2_dh"); %>
25 function CCDGrid() { return this; }
26 CCDGrid
.prototype = new TomatoGrid
;
28 tabs
= [['server1', 'Server 1'],['server2', 'Server 2']];
29 sections
= [['basic', 'Basic'],['advanced', 'Advanced'],['keys','Keys'],['status','Status']];
32 for (i
= 0; i
< tabs
.length
; ++i
)
34 ccdTables
.push(new CCDGrid());
35 statusUpdaters
.push(new StatusUpdater());
37 ciphers
= [['default','Use Default'],['none','None']];
38 for (i
= 0; i
< vpnciphers
.length
; ++i
) ciphers
.push([vpnciphers
[i
],vpnciphers
[i
]]);
41 vpn1up
= parseInt('<% psup("vpnserver1"); %>');
42 vpn2up
= parseInt('<% psup("vpnserver2"); %>');
44 function updateStatus(num
)
46 var xob
= new XmlHttp();
47 xob
.onCompleted = function(text
, xml
)
49 statusUpdaters
[num
].update(text
);
52 xob
.onError = function(ex
)
54 statusUpdaters
[num
].errors
.innerHTML
+= 'ERROR! '+ex
+'<br>';
58 xob
.post('/vpnstatus.cgi', 'server=' + (num
+1));
61 function tabSelect(name
)
67 for (var i
= 0; i
< tabs
.length
; ++i
)
69 var on
= (name
== tabs
[i
][0]);
70 elem
.display(tabs
[i
][0] + '-tab', on
);
73 cookie
.set('vpn_server_tab', name
);
76 function sectSelect(tab
, section
)
80 for (var i
= 0; i
< sections
.length
; ++i
)
82 if (section
== sections
[i
][0])
84 elem
.addClass(tabs
[tab
][0]+'-'+sections
[i
][0]+'-tab', 'active');
85 elem
.display(tabs
[tab
][0]+'-'+sections
[i
][0], true);
89 elem
.removeClass(tabs
[tab
][0]+'-'+sections
[i
][0]+'-tab', 'active');
90 elem
.display(tabs
[tab
][0]+'-'+sections
[i
][0], false);
94 cookie
.set('vpn_server'+tab
+'_section', section
);
97 function toggle(service
, isup
)
99 if (changed
&& !confirm("Unsaved changes will be lost. Continue anyway?")) return;
101 E('_' + service
+ '_button').disabled
= true;
102 form
.submitHidden('service.cgi', {
103 _redirect
: 'vpn-server.asp',
105 _service
: service
+ (isup
? '-stop' : '-start')
109 function verifyFields(focused
, quiet
)
115 // When settings change, make sure we restart the right services
121 var serverindex
= focused
.name
.indexOf("server");
122 if (serverindex
>= 0)
124 var servernumber
= focused
.name
.substring(serverindex
+6,serverindex
+7);
125 if (eval('vpn'+servernumber
+'up') && fom
._service
.value
.indexOf('server'+servernumber
) < 0)
127 if ( fom
._service
.value
!= "" ) fom
._service
.value
+= ",";
128 fom
._service
.value
+= 'vpnserver'+servernumber
+'-restart';
131 if ((focused
.name
.indexOf("_dns")>=0 || (focused
.name
.indexOf("_if")>=0 && E('_f_vpn_server'+servernumber
+'_dns').checked
)) &&
132 fom
._service
.value
.indexOf('dnsmasq') < 0)
134 if ( fom
._service
.value
!= "" ) fom
._service
.value
+= ",";
135 fom
._service
.value
+= 'dnsmasq-restart';
138 if (focused
.name
.indexOf("_c2c") >= 0)
139 ccdTables
[servernumber
-1].reDraw();
143 // Element varification
144 for (i
= 0; i
< tabs
.length
; ++i
)
148 if (!v_range('_vpn_'+t
+'_poll', quiet
, 0, 1440)) ret
= 0;
149 if (!v_port('_vpn_'+t
+'_port', quiet
)) ret
= 0;
150 if (!v_ip('_vpn_'+t
+'_sn', quiet
, 0)) ret
= 0;
151 if (!v_netmask('_vpn_'+t
+'_nm', quiet
)) ret
= 0;
152 if (!v_ip('_vpn_'+t
+'_r1', quiet
, 1)) ret
= 0;
153 if (!v_ip('_vpn_'+t
+'_r2', quiet
, 1)) ret
= 0;
154 if (!v_ip('_vpn_'+t
+'_local', quiet
, 1)) ret
= 0;
155 if (!v_ip('_vpn_'+t
+'_remote', quiet
, 1)) ret
= 0;
156 if (!v_range('_vpn_'+t
+'_reneg', quiet
, -1, 2147483647)) ret
= 0;
159 // Visability changes
160 for (i
= 0; i
< tabs
.length
; ++i
)
164 auth
= E('_vpn_'+t
+'_crypt');
165 iface
= E('_vpn_'+t
+'_if');
166 hmac
= E('_vpn_'+t
+'_hmac');
167 dhcp
= E('_f_vpn_'+t
+'_dhcp');
168 ccd
= E('_f_vpn_'+t
+'_ccd');
169 dns
= E('_f_vpn_'+t
+'_dns');
171 elem
.display(PR('_vpn_'+t
+'_ca'), PR('_vpn_'+t
+'_crt'), PR('_vpn_'+t
+'_dh'), PR('_vpn_'+t
+'_key'),
172 PR('_vpn_'+t
+'_hmac'), PR('_f_vpn_'+t
+'_rgw'), PR('_vpn_'+t
+'_reneg'), auth
.value
== "tls");
173 elem
.display(PR('_vpn_'+t
+'_static'), auth
.value
== "secret" || (auth
.value
== "tls" && hmac
.value
>= 0));
174 elem
.display(E(t
+'_custom_crypto_text'), auth
.value
== "custom");
175 elem
.display(PR('_vpn_'+t
+'_sn'), PR('_f_vpn_'+t
+'_plan'), auth
.value
== "tls" && iface
.value
== "tun");
176 elem
.display(PR('_f_vpn_'+t
+'_dhcp'), auth
.value
== "tls" && iface
.value
== "tap");
177 elem
.display(E(t
+'_range'), !dhcp
.checked
);
178 elem
.display(PR('_vpn_'+t
+'_local'), auth
.value
== "secret" && iface
.value
== "tun");
179 elem
.display(PR('_f_vpn_'+t
+'_ccd'), auth
.value
== "tls");
180 elem
.display(PR('_f_vpn_'+t
+'_c2c'),PR('_f_vpn_'+t
+'_ccd_excl'),PR('table_'+t
+'_ccd'), auth
.value
== "tls" && ccd
.checked
);
181 elem
.display(PR('_f_vpn_'+t
+'_pdns'), auth
.value
== "tls" && dns
.checked
);
183 keyHelp
= E(t
+'-keyhelp');
187 keyHelp
.href
= helpURL
['TLSKeys'];
190 keyHelp
.href
= helpURL
['staticKeys'];
193 keyHelp
.href
= helpURL
['howto'];
201 CCDGrid
.prototype.verifyFields = function(row
, quiet
)
205 // When settings change, make sure we restart the right server
208 for (i
= 0; i
< tabs
.length
; ++i
)
210 if (ccdTables
[i
] == this)
213 if (eval('vpn'+(i
+1)+'up') && fom
._service
.value
.indexOf('server'+(i
+1)) < 0)
215 if ( fom
._service
.value
!= "" ) fom
._service
.value
+= ",";
216 fom
._service
.value
+= 'vpnserver'+(i
+1)+'-restart';
221 var f
= fields
.getAll(row
);
223 // Verify fields in this row of the table
224 if (f
[1].value
== "") { ferror
.set(f
[1], "Common name is mandatory.", quiet
); ret
= 0; }
225 if (f
[1].value
.indexOf('>') >= 0 || f
[1].value
.indexOf('<') >= 0) { ferror
.set(f
[1], "Common name cannot contain '<' or '>' characters.", quiet
); ret
= 0; }
226 if (f
[2].value
!= "" && !v_ip(f
[2],quiet
,0)) ret
= 0;
227 if (f
[3].value
!= "" && !v_netmask(f
[3],quiet
)) ret
= 0;
228 if (f
[2].value
== "" && f
[3].value
!= "" ) { ferror
.set(f
[2], "Either both or neither subnet and netmask must be provided.", quiet
); ret
= 0; }
229 if (f
[3].value
== "" && f
[2].value
!= "" ) { ferror
.set(f
[3], "Either both or neither subnet and netmask must be provided.", quiet
); ret
= 0; }
230 if (f
[4].checked
&& (f
[2].value
== "" || f
[3].value
== "")) { ferror
.set(f
[4], "Cannot push routes if they're not given. Please provide subnet/netmask.", quiet
); ret
= 0; }
235 CCDGrid
.prototype.fieldValuesToData = function(row
)
237 var f
= fields
.getAll(row
);
238 return [f
[0].checked
?1:0, f
[1].value
, f
[2].value
, f
[3].value
, f
[4].checked
?1:0];
241 CCDGrid
.prototype.dataToView = function(data
)
244 for (i
= 0; i
< tabs
.length
; ++i
)
246 if (ccdTables
[i
] == this && E('_f_vpn_server'+(i
+1)+'_c2c').checked
)
250 var temp
= ['<input type=\'checkbox\' style="opacity:1" disabled'+(data
[0]!=0?' checked':'')+'>',
254 c2c
?'<input type=\'checkbox\' style="opacity:1" disabled'+(data
[4]!=0?' checked':'')+'>':'N/A'];
257 for (var i
= 0; i
< temp
.length
; ++i
)
258 v
.push(i
==0||i
==4?temp
[i
]:escapeHTML('' + temp
[i
]));
263 CCDGrid
.prototype.dataToFieldValues = function(data
)
265 return [data
[0] == 1, data
[1], data
[2], data
[3], data
[4] == 1];
268 CCDGrid
.prototype.reDraw = function()
270 var i
, j
, header
, data
, view
;
271 data
= this.getAllData();
272 header
= this.header
? this.header
.rowIndex
+ 1 : 0;
273 for (i
= 0; i
< data
.length
; ++i
)
275 view
= this.dataToView(data
[i
]);
276 for (j
= 0; j
< view
.length
; ++j
)
277 this.tb
.rows
[i
+header
].cells
[j
].innerHTML
= view
[j
];
283 if (!verifyFields(null, false)) return;
287 E('vpn_server_eas').value
= '';
288 E('vpn_server_dns').value
= '';
290 for (i
= 0; i
< tabs
.length
; ++i
)
292 if (ccdTables
[i
].isEditing()) return;
296 if ( E('_f_vpn_'+t
+'_eas').checked
)
297 E('vpn_server_eas').value
+= ''+(i
+1)+',';
299 if ( E('_f_vpn_'+t
+'_dns').checked
)
300 E('vpn_server_dns').value
+= ''+(i
+1)+',';
302 var data
= ccdTables
[i
].getAllData();
305 for (j
= 0; j
< data
.length
; ++j
)
306 ccd
+= data
[j
].join('<') + '>';
308 E('vpn_'+t
+'_dhcp').value
= E('_f_vpn_'+t
+'_dhcp').checked
? 1 : 0;
309 E('vpn_'+t
+'_plan').value
= E('_f_vpn_'+t
+'_plan').checked
? 1 : 0;
310 E('vpn_'+t
+'_ccd').value
= E('_f_vpn_'+t
+'_ccd').checked
? 1 : 0;
311 E('vpn_'+t
+'_c2c').value
= E('_f_vpn_'+t
+'_c2c').checked
? 1 : 0;
312 E('vpn_'+t
+'_ccd_excl').value
= E('_f_vpn_'+t
+'_ccd_excl').checked
? 1 : 0;
313 E('vpn_'+t
+'_ccd_val').value
= ccd
;
314 E('vpn_'+t
+'_pdns').value
= E('_f_vpn_'+t
+'_pdns').checked
? 1 : 0;
315 E('vpn_'+t
+'_rgw').value
= E('_f_vpn_'+t
+'_rgw').checked
? 1 : 0;
325 tabSelect(cookie
.get('vpn_server_tab') || tabs
[0][0]);
327 for (i
= 0; i
< tabs
.length
; ++i
)
329 sectSelect(i
, cookie
.get('vpn_server'+i
+'_section') || sections
[0][0]);
333 ccdTables
[i
].init('table_'+t
+'_ccd', 'sort', 0, [{ type
: 'checkbox' }, { type
: 'text' }, { type
: 'text', maxlen
: 15 }, { type
: 'text', maxlen
: 15 }, { type
: 'checkbox' }]);
334 ccdTables
[i
].headerSet(['Enable', 'Common Name', 'Subnet', 'Netmask', 'Push']);
335 var ccdVal
= eval( 'nvram.vpn_'+t
+'_ccd_val' );
338 var s
= ccdVal
.split('>');
339 for (var j
= 0; j
< s
.length
; ++j
)
341 if (!s
[j
].length
) continue;
342 var row
= s
[j
].split('<');
344 ccdTables
[i
].insertData(-1, row
);
347 ccdTables
[i
].showNewEditor();
348 ccdTables
[i
].resetNewEditor();
350 statusUpdaters
[i
].init(t
+'-status-clients-table',t
+'-status-routing-table',t
+'-status-stats-table',t
+'-status-time',t
+'-status-content',t
+'-no-status',t
+'-status-errors');
354 verifyFields(null, true);
358 <style type='text/css'
>
385 <form id='_fom' method='post' action='tomato.cgi'
>
386 <table id='container' cellspacing=
0>
387 <tr><td colspan=
2 id='header'
>
388 <div class='title'
>Tomato
</div>
389 <div class='version'
>Version <%
version(); %></div>
391 <tr id='body'
><td id='navi'
><script type='text/javascript'
>navi()</script></td>
393 <div id='ident'
><%
ident(); %></div>
395 <input type='hidden' name='_nextpage' value='vpn-server.asp'
>
396 <input type='hidden' name='_nextwait' value='
5'
>
397 <input type='hidden' name='_service' value=''
>
398 <input type='hidden' name='vpn_server_eas' id='vpn_server_eas' value=''
>
399 <input type='hidden' name='vpn_server_dns' id='vpn_server_dns' value=''
>
401 <div class='section-title'
>OpenVPN Server Configuration
</div>
402 <div class='section'
>
403 <script type='text/javascript'
>
404 tabCreate
.apply(this, tabs
);
406 for (i
= 0; i
< tabs
.length
; ++i
)
409 W('<div id=\''+t
+'-tab\'>');
410 W('<input type=\'hidden\' id=\'vpn_'+t
+'_dhcp\' name=\'vpn_'+t
+'_dhcp\'>');
411 W('<input type=\'hidden\' id=\'vpn_'+t
+'_plan\' name=\'vpn_'+t
+'_plan\'>');
412 W('<input type=\'hidden\' id=\'vpn_'+t
+'_ccd\' name=\'vpn_'+t
+'_ccd\'>');
413 W('<input type=\'hidden\' id=\'vpn_'+t
+'_c2c\' name=\'vpn_'+t
+'_c2c\'>');
414 W('<input type=\'hidden\' id=\'vpn_'+t
+'_ccd_excl\' name=\'vpn_'+t
+'_ccd_excl\'>');
415 W('<input type=\'hidden\' id=\'vpn_'+t
+'_ccd_val\' name=\'vpn_'+t
+'_ccd_val\'>');
416 W('<input type=\'hidden\' id=\'vpn_'+t
+'_pdns\' name=\'vpn_'+t
+'_pdns\'>');
417 W('<input type=\'hidden\' id=\'vpn_'+t
+'_rgw\' name=\'vpn_'+t
+'_rgw\'>');
419 W('<ul class="tabs">');
420 for (j
= 0; j
< sections
.length
; j
++)
422 W('<li><a href="javascript:sectSelect('+i
+', \''+sections
[j
][0]+'\')" id="'+t
+'-'+sections
[j
][0]+'-tab">'+sections
[j
][1]+'</a></li>');
424 W('</ul><div class=\'tabs-bottom\'></div>');
426 W('<div id=\''+t
+'-basic\'>');
427 createFieldTable('', [
428 { title
: 'Start with WAN', name
: 'f_vpn_'+t
+'_eas', type
: 'checkbox', value
: nvram
.vpn_server_eas
.indexOf(''+(i
+1)) >= 0 },
429 { title
: 'Interface Type', name
: 'vpn_'+t
+'_if', type
: 'select', options
: [ ['tap','TAP'], ['tun','TUN'] ], value
: eval( 'nvram.vpn_'+t
+'_if' ) },
430 { title
: 'Protocol', name
: 'vpn_'+t
+'_proto', type
: 'select', options
: [ ['udp','UDP'], ['tcp-server','TCP'] ], value
: eval( 'nvram.vpn_'+t
+'_proto' ) },
431 { title
: 'Port', name
: 'vpn_'+t
+'_port', type
: 'text', value
: eval( 'nvram.vpn_'+t
+'_port' ) },
432 { title
: 'Firewall', name
: 'vpn_'+t
+'_firewall', type
: 'select', options
: [ ['auto', 'Automatic'], ['external', 'External Only'], ['custom', 'Custom'] ], value
: eval( 'nvram.vpn_'+t
+'_firewall' ) },
433 { title
: 'Authorization Mode', name
: 'vpn_'+t
+'_crypt', type
: 'select', options
: [ ['tls', 'TLS'], ['secret', 'Static Key'], ['custom', 'Custom'] ], value
: eval( 'nvram.vpn_'+t
+'_crypt' ),
434 suffix
: '<span id=\''+t
+'_custom_crypto_text\'> <small>(must configure manually...)</small></span>' },
435 { title
: 'Extra HMAC authorization (tls-auth)', name
: 'vpn_'+t
+'_hmac', type
: 'select', options
: [ [-1, 'Disabled'], [2, 'Bi-directional'], [0, 'Incoming (0)'], [1, 'Outgoing (1)'] ], value
: eval( 'nvram.vpn_'+t
+'_hmac' ) },
436 { title
: 'VPN subnet/netmask', multi
: [
437 { name
: 'vpn_'+t
+'_sn', type
: 'text', maxlen
: 15, size
: 17, value
: eval( 'nvram.vpn_'+t
+'_sn' ) },
438 { name
: 'vpn_'+t
+'_nm', type
: 'text', maxlen
: 15, size
: 17, value
: eval( 'nvram.vpn_'+t
+'_nm' ) } ] },
439 { title
: 'Client address pool', multi
: [
440 { name
: 'f_vpn_'+t
+'_dhcp', type
: 'checkbox', value
: eval( 'nvram.vpn_'+t
+'_dhcp' ) != 0, suffix
: ' DHCP ' },
441 { name
: 'vpn_'+t
+'_r1', type
: 'text', maxlen
: 15, size
: 17, value
: eval( 'nvram.vpn_'+t
+'_r1' ), prefix
: '<span id=\''+t
+'_range\'>', suffix
: '-' },
442 { name
: 'vpn_'+t
+'_r2', type
: 'text', maxlen
: 15, size
: 17, value
: eval( 'nvram.vpn_'+t
+'_r2' ), suffix
: '</span>' } ] },
443 { title
: 'Local/remote endpoint addresses', multi
: [
444 { name
: 'vpn_'+t
+'_local', type
: 'text', maxlen
: 15, size
: 17, value
: eval( 'nvram.vpn_'+t
+'_local' ) },
445 { name
: 'vpn_'+t
+'_remote', type
: 'text', maxlen
: 15, size
: 17, value
: eval( 'nvram.vpn_'+t
+'_remote' ) } ] }
448 W('<div id=\''+t
+'-advanced\'>');
449 createFieldTable('', [
450 { title
: 'Poll Interval', name
: 'vpn_'+t
+'_poll', type
: 'text', maxlen
: 4, size
: 5, value
: eval( 'nvram.vpn_'+t
+'_poll' ), suffix
: ' <small>(in minutes, 0 to disable)</small>' },
451 { title
: 'Push LAN to clients', name
: 'f_vpn_'+t
+'_plan', type
: 'checkbox', value
: eval( 'nvram.vpn_'+t
+'_plan' ) != 0 },
452 { title
: 'Direct clients to<br>redirect Internet traffic', name
: 'f_vpn_'+t
+'_rgw', type
: 'checkbox', value
: eval( 'nvram.vpn_'+t
+'_rgw' ) != 0 },
453 { title
: 'Respond to DNS', name
: 'f_vpn_'+t
+'_dns', type
: 'checkbox', value
: nvram
.vpn_server_dns
.indexOf(''+(i
+1)) >= 0 },
454 { title
: 'Advertise DNS to clients', name
: 'f_vpn_'+t
+'_pdns', type
: 'checkbox', value
: eval( 'nvram.vpn_'+t
+'_pdns' ) != 0 },
455 { title
: 'Encryption cipher', name
: 'vpn_'+t
+'_cipher', type
: 'select', options
: ciphers
, value
: eval( 'nvram.vpn_'+t
+'_cipher' ) },
456 { title
: 'Compression', name
: 'vpn_'+t
+'_comp', type
: 'select', options
: [ ['-1', 'Disabled'], ['no', 'None'], ['yes', 'Enabled'], ['adaptive', 'Adaptive'] ], value
: eval( 'nvram.vpn_'+t
+'_comp' ) },
457 { title
: 'TLS Renegotiation Time', name
: 'vpn_'+t
+'_reneg', type
: 'text', maxlen
: 10, size
: 7, value
: eval( 'nvram.vpn_'+t
+'_reneg' ),
458 suffix
: ' <small>(in seconds, -1 for default)</small>' },
459 { title
: 'Manage Client-Specific Options', name
: 'f_vpn_'+t
+'_ccd', type
: 'checkbox', value
: eval( 'nvram.vpn_'+t
+'_ccd' ) != 0 },
460 { title
: 'Allow Client<->Client', name
: 'f_vpn_'+t
+'_c2c', type
: 'checkbox', value
: eval( 'nvram.vpn_'+t
+'_c2c' ) != 0 },
461 { title
: 'Allow Only These Clients', name
: 'f_vpn_'+t
+'_ccd_excl', type
: 'checkbox', value
: eval( 'nvram.vpn_'+t
+'_ccd_excl' ) != 0 },
462 { title
: '', suffix
: '<table class=\'tomato-grid\' id=\'table_'+t
+'_ccd\'></table>' },
463 { title
: 'Custom Configuration', name
: 'vpn_'+t
+'_custom', type
: 'textarea', value
: eval( 'nvram.vpn_'+t
+'_custom' ) }
466 W('<div id=\''+t
+'-keys\'>');
467 W('<p class=\'keyhelp\'>For help generating keys, refer to the OpenVPN <a id=\''+t
+'-keyhelp\'>HOWTO</a>.</p>');
468 createFieldTable('', [
469 { title
: 'Static Key', name
: 'vpn_'+t
+'_static', type
: 'textarea', value
: eval( 'nvram.vpn_'+t
+'_static' ) },
470 { title
: 'Certificate Authority', name
: 'vpn_'+t
+'_ca', type
: 'textarea', value
: eval( 'nvram.vpn_'+t
+'_ca' ) },
471 { title
: 'Server Certificate', name
: 'vpn_'+t
+'_crt', type
: 'textarea', value
: eval( 'nvram.vpn_'+t
+'_crt' ) },
472 { title
: 'Server Key', name
: 'vpn_'+t
+'_key', type
: 'textarea', value
: eval( 'nvram.vpn_'+t
+'_key' ) },
473 { title
: 'Diffie Hellman parameters', name
: 'vpn_'+t
+'_dh', type
: 'textarea', value
: eval( 'nvram.vpn_'+t
+'_dh' ) }
476 W('<div id=\''+t
+'-status\'>');
477 W('<div id=\''+t
+'-no-status\'><p>Server is not running or status could not be read.</p></div>');
478 W('<div id=\''+t
+'-status-content\' style=\'display:none\' class=\'status-content\'>');
479 W('<div id=\''+t
+'-status-header\' class=\'status-header\'><p>Data current as of <span id=\''+t
+'-status-time\'></span>.</p></div>');
480 W('<div id=\''+t
+'-status-clients\'><div class=\'section-title\'>Client List</div><table class=\'tomato-grid status-table\' id=\''+t
+'-status-clients-table\'></table><br></div>');
481 W('<div id=\''+t
+'-status-routing\'><div class=\'section-title\'>Routing Table</div><table class=\'tomato-grid status-table\' id=\''+t
+'-status-routing-table\'></table><br></div>');
482 W('<div id=\''+t
+'-status-stats\'><div class=\'section-title\'>General Statistics</div><table class=\'tomato-grid status-table\' id=\''+t
+'-status-stats-table\'></table><br></div>');
483 W('<div id=\''+t
+'-status-errors\' class=\'error\'></div>');
485 W('<div style=\'text-align:right\'><a href=\'javascript:updateStatus('+i
+')\'>Refresh Status</a></div>');
487 W('<input type="button" value="' + (eval('vpn'+(i
+1)+'up') ? 'Stop' : 'Start') + ' Now" onclick="toggle(\'vpn'+t
+'\', vpn'+(i
+1)+'up)" id="_vpn'+t
+'_button">');
495 <tr><td id='footer' colspan=
2>
496 <span id='footer-msg'
></span>
497 <input type='button' value='Save' id='save-button' onclick='save()'
>
498 <input type='button' value='Cancel' id='cancel-button' onclick='javascript:reloadPage();'
>
502 <script type='text/javascript'
>init();</script>