Column sorting fixes, thanks to Tony550
[tomato.git] / release / src / router / www / vpn-client.asp
blob273814cb1362432d186e2b68fd46d44b8b8d6fb8
1 <!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.0//EN'>
2 <!--
3 Tomato GUI
4 Copyright (C) 2006-2008 Jonathan Zarate
5 http://www.polarcloud.com/tomato/
7 Portions Copyright (C) 2008-2010 Keith Moyer, tomatovpn@keithmoyer.com
8 Portions Copyright (C) 2010-2011 Jean-Yves Avenard, jean-yves@avenard.org
10 For use with Tomato Firmware only.
11 No part of this file may be used without permission.
12 -->
13 <html>
14 <head>
15 <meta http-equiv='content-type' content='text/html;charset=utf-8'>
16 <meta name='robots' content='noindex,nofollow'>
17 <title>[<% ident(); %>] OpenVPN: Client</title>
18 <link rel='stylesheet' type='text/css' href='tomato.css'>
19 <link rel='stylesheet' type='text/css' href='color.css'>
20 <script type='text/javascript' src='tomato.js'></script>
21 <script type='text/javascript' src='vpn.js'></script>
22 <script type='text/javascript'>
24 // <% nvram("vpn_client_eas,vpn_client1_poll,vpn_client1_if,vpn_client1_bridge,vpn_client1_nat,vpn_client1_proto,vpn_client1_addr,vpn_client1_port,vpn_client1_retry,vpn_client1_firewall,vpn_client1_crypt,vpn_client1_comp,vpn_client1_cipher,vpn_client1_local,vpn_client1_remote,vpn_client1_nm,vpn_client1_reneg,vpn_client1_hmac,vpn_client1_adns,vpn_client1_rgw,vpn_client1_gw,vpn_client1_custom,vpn_client1_static,vpn_client1_ca,vpn_client1_crt,vpn_client1_key,vpn_client1_userauth,vpn_client1_username,vpn_client1_password,vpn_client1_useronly,vpn_client1_tlsremote,vpn_client1_cn,vpn_client2_poll,vpn_client2_if,vpn_client2_bridge,vpn_client2_nat,vpn_client2_proto,vpn_client2_addr,vpn_client2_port,vpn_client2_retry,vpn_client2_firewall,vpn_client2_crypt,vpn_client2_comp,vpn_client2_cipher,vpn_client2_local,vpn_client2_remote,vpn_client2_nm,vpn_client2_reneg,vpn_client2_hmac,vpn_client2_adns,vpn_client2_rgw,vpn_client2_gw,vpn_client2_custom,vpn_client2_static,vpn_client2_ca,vpn_client2_crt,vpn_client2_key,vpn_client2_userauth,vpn_client2_username,vpn_client2_password,vpn_client2_useronly,vpn_client2_tlsremote,vpn_client2_cn"); %>
26 tabs = [['client1', 'Client 1'],['client2', 'Client 2']];
27 sections = [['basic', 'Basic'],['advanced', 'Advanced'],['keys','Keys'],['status','Status']];
28 statusUpdaters = [];
29 for (i = 0; i < tabs.length; ++i) statusUpdaters.push(new StatusUpdater());
30 ciphers = [['default','Use Default'],['none','None']];
31 for (i = 0; i < vpnciphers.length; ++i) ciphers.push([vpnciphers[i],vpnciphers[i]]);
33 changed = 0;
34 vpn1up = parseInt('<% psup("vpnclient1"); %>');
35 vpn2up = parseInt('<% psup("vpnclient2"); %>');
37 function updateStatus(num)
39 var xob = new XmlHttp();
40 xob.onCompleted = function(text, xml)
42 statusUpdaters[num].update(text);
43 xob = null;
45 xob.onError = function(ex)
47 statusUpdaters[num].errors.innerHTML += 'ERROR! '+ex+'<br>';
48 xob = null;
51 xob.post('/vpnstatus.cgi', 'client=' + (num+1));
54 function tabSelect(name)
56 tgHideIcons();
58 tabHigh(name);
60 for (var i = 0; i < tabs.length; ++i)
62 var on = (name == tabs[i][0]);
63 elem.display(tabs[i][0] + '-tab', on);
66 cookie.set('vpn_client_tab', name);
69 function sectSelect(tab, section)
71 tgHideIcons();
73 for (var i = 0; i < sections.length; ++i)
75 if (section == sections[i][0])
77 elem.addClass(tabs[tab][0]+'-'+sections[i][0]+'-tab', 'active');
78 elem.display(tabs[tab][0]+'-'+sections[i][0], true);
80 else
82 elem.removeClass(tabs[tab][0]+'-'+sections[i][0]+'-tab', 'active');
83 elem.display(tabs[tab][0]+'-'+sections[i][0], false);
87 cookie.set('vpn_client'+tab+'_section', section);
90 function toggle(service, isup)
92 if (changed && !confirm("Unsaved changes will be lost. Continue anyway?")) return;
94 E('_' + service + '_button').disabled = true;
95 form.submitHidden('service.cgi', {
96 _redirect: 'vpn-client.asp',
97 _sleep: '3',
98 _service: service + (isup ? '-stop' : '-start')
99 });
102 function verifyFields(focused, quiet)
104 tgHideIcons();
106 var ret = 1;
108 // When settings change, make sure we restart the right client
109 if (focused)
111 changed = 1;
113 var clientindex = focused.name.indexOf("client");
114 if (clientindex >= 0)
116 var clientnumber = focused.name.substring(clientindex+6,clientindex+7);
117 var stripped = focused.name.substring(0,clientindex+6)+focused.name.substring(clientindex+7);
119 if (stripped == 'vpn_client_local')
120 E('_f_vpn_client'+clientnumber+'_local').value = focused.value;
121 else if (stripped == 'f_vpn_client_local')
122 E('_vpn_client'+clientnumber+'_local').value = focused.value;
124 var fom = E('_fom');
125 if (eval('vpn'+clientnumber+'up') && fom._service.value.indexOf('client'+clientnumber) < 0)
127 if ( fom._service.value != "" ) fom._service.value += ",";
128 fom._service.value += 'vpnclient'+clientnumber+'-restart';
133 // Element varification
134 for (i = 0; i < tabs.length; ++i)
136 t = tabs[i][0];
138 if (!v_range('_vpn_'+t+'_poll', quiet, 0, 1440)) ret = 0;
139 if (!v_ip('_vpn_'+t+'_addr', true) && !v_domain('_vpn_'+t+'_addr', true)) { ferror.set(E('_vpn_'+t+'_addr'), "Invalid server address.", quiet); ret = 0; }
140 if (!v_port('_vpn_'+t+'_port', quiet)) ret = 0;
141 if (!v_ip('_vpn_'+t+'_local', quiet, 1)) ret = 0;
142 if (!v_ip('_f_vpn_'+t+'_local', true, 1)) ret = 0;
143 if (!v_ip('_vpn_'+t+'_remote', quiet, 1)) ret = 0;
144 if (!v_netmask('_vpn_'+t+'_nm', quiet)) ret = 0;
145 if (!v_range('_vpn_'+t+'_retry', quiet, -1, 32767)) ret = 0;
146 if (!v_range('_vpn_'+t+'_reneg', quiet, -1, 2147483647)) ret = 0;
147 if (E('_vpn_'+t+'_gw').value.length > 0 && !v_ip('_vpn_'+t+'_gw', quiet, 1)) ret = 0;
150 // Visability changes
151 for (i = 0; i < tabs.length; ++i)
153 t = tabs[i][0];
155 fw = E('_vpn_'+t+'_firewall').value;
156 auth = E('_vpn_'+t+'_crypt').value;
157 iface = E('_vpn_'+t+'_if').value;
158 bridge = E('_f_vpn_'+t+'_bridge').checked;
159 nat = E('_f_vpn_'+t+'_nat').checked;
160 hmac = E('_vpn_'+t+'_hmac').value;
161 rgw = E('_f_vpn_'+t+'_rgw').checked;
163 userauth = E('_f_vpn_'+t+'_userauth').checked && auth == "tls";
164 useronly = userauth && E('_f_vpn_'+t+'_useronly').checked;
166 // Page Basic
167 elem.display(PR('_f_vpn_'+t+'_userauth'), auth == "tls");
168 elem.display(PR('_vpn_'+t+'_username'), PR('_vpn_'+t+'_password'), userauth );
169 elem.display(PR('_f_vpn_'+t+'_useronly'), userauth);
170 elem.display(E(t+'_ca_warn_text'), useronly);
171 elem.display(PR('_vpn_'+t+'_hmac'), auth == "tls");
172 elem.display(E(t+'_custom_crypto_text'), auth == "custom");
173 elem.display(PR('_f_vpn_'+t+'_bridge'), iface == "tap");
174 elem.display(E(t+'_bridge_warn_text'), !bridge);
175 elem.display(PR('_f_vpn_'+t+'_nat'), fw != "custom" && (iface == "tun" || !bridge));
176 elem.display(E(t+'_nat_warn_text'), fw != "custom" && (!nat || (auth == "secret" && iface == "tun")));
177 elem.display(PR('_vpn_'+t+'_local'), iface == "tun" && auth == "secret");
178 elem.display(PR('_f_vpn_'+t+'_local'), iface == "tap" && !bridge && auth == "secret");
180 // Page Advanced
181 elem.display(PR('_vpn_'+t+'_adns'), PR('_vpn_'+t+'_reneg'), auth == "tls");
182 elem.display(E(t+'_gateway'), iface == "tap" && rgw > 0);
184 // Page Key
185 elem.display(PR('_vpn_'+t+'_static'), auth == "secret" || (auth == "tls" && hmac >= 0));
186 elem.display(PR('_vpn_'+t+'_ca'), auth == "tls");
187 elem.display(PR('_vpn_'+t+'_crt'), PR('_vpn_'+t+'_key'), auth == "tls" && !useronly);
188 elem.display(PR('_f_vpn_'+t+'_tlsremote'), auth == "tls");
189 elem.display(E(t+'_cn'), auth == "tls" && E('_f_vpn_'+t+'_tlsremote').checked);
191 keyHelp = E(t+'-keyhelp');
192 switch (auth)
194 case "tls":
195 keyHelp.href = helpURL['TLSKeys'];
196 break;
197 case "secret":
198 keyHelp.href = helpURL['staticKeys'];
199 break;
200 default:
201 keyHelp.href = helpURL['howto'];
202 break;
206 return ret;
209 function save()
211 if (!verifyFields(null, false)) return;
213 var fom = E('_fom');
215 E('vpn_client_eas').value = '';
217 for (i = 0; i < tabs.length; ++i)
219 t = tabs[i][0];
221 if ( E('_f_vpn_'+t+'_eas').checked )
222 E('vpn_client_eas').value += ''+(i+1)+',';
224 E('vpn_'+t+'_bridge').value = E('_f_vpn_'+t+'_bridge').checked ? 1 : 0;
225 E('vpn_'+t+'_nat').value = E('_f_vpn_'+t+'_nat').checked ? 1 : 0;
226 E('vpn_'+t+'_rgw').value = E('_f_vpn_'+t+'_rgw').checked ? 1 : 0;
227 E('vpn_'+t+'_userauth').value = E('_f_vpn_'+t+'_userauth').checked ? 1 : 0;
228 E('vpn_'+t+'_useronly').value = E('_f_vpn_'+t+'_useronly').checked ? 1 : 0;
229 E('vpn_'+t+'_tlsremote').value = E('_f_vpn_'+t+'_tlsremote').checked ? 1 : 0;
232 form.submit(fom, 1);
234 changed = 0;
237 function init()
239 tabSelect(cookie.get('vpn_client_tab') || tabs[0][0]);
241 for (i = 0; i < tabs.length; ++i)
243 sectSelect(i, cookie.get('vpn_client'+i+'_section') || sections[i][0]);
245 t = tabs[i][0];
247 statusUpdaters[i].init(null,null,t+'-status-stats-table',t+'-status-time',t+'-status-content',t+'-no-status',t+'-status-errors');
248 updateStatus(i);
251 verifyFields(null, true);
253 </script>
255 <style type='text/css'>
256 textarea {
257 width: 98%;
258 height: 10em;
260 p.keyhelp
262 font-size: smaller;
263 font-style: italic;
265 div.status-header p
267 font-weight: bold;
268 padding-bottom: 4px;
270 table.status-table
272 width: auto;
273 margin-left: auto;
274 margin-right: auto;
275 text-align: center;
277 </style>
279 </head>
280 <body>
281 <form id='_fom' method='post' action='tomato.cgi'>
282 <table id='container' cellspacing=0>
283 <tr><td colspan=2 id='header'>
284 <div class='title'>Tomato</div>
285 <div class='version'>Version <% version(); %></div>
286 </td></tr>
287 <tr id='body'><td id='navi'><script type='text/javascript'>navi()</script></td>
288 <td id='content'>
289 <div id='ident'><% ident(); %></div>
291 <input type='hidden' name='_nextpage' value='vpn-client.asp'>
292 <input type='hidden' name='_nextwait' value='5'>
293 <input type='hidden' name='_service' value=''>
294 <input type='hidden' name='vpn_client_eas' id='vpn_client_eas' value=''>
296 <div class='section-title'>OpenVPN Client Configuration</div>
297 <div class='section'>
298 <script type='text/javascript'>
299 tabCreate.apply(this, tabs);
301 for (i = 0; i < tabs.length; ++i)
303 t = tabs[i][0];
304 W('<div id=\''+t+'-tab\'>');
305 W('<input type=\'hidden\' id=\'vpn_'+t+'_bridge\' name=\'vpn_'+t+'_bridge\'>');
306 W('<input type=\'hidden\' id=\'vpn_'+t+'_nat\' name=\'vpn_'+t+'_nat\'>');
307 W('<input type=\'hidden\' id=\'vpn_'+t+'_rgw\' name=\'vpn_'+t+'_rgw\'>');
308 W('<input type=\'hidden\' id=\'vpn_'+t+'_userauth\' name=\'vpn_'+t+'_userauth\'>');
309 W('<input type=\'hidden\' id=\'vpn_'+t+'_useronly\' name=\'vpn_'+t+'_useronly\'>');
310 W('<input type=\'hidden\' id=\'vpn_'+t+'_tlsremote\' name=\'vpn_'+t+'_tlsremote\'>');
312 W('<ul class="tabs">');
313 for (j = 0; j < sections.length; j++)
315 W('<li><a href="javascript:sectSelect('+i+', \''+sections[j][0]+'\')" id="'+t+'-'+sections[j][0]+'-tab">'+sections[j][1]+'</a></li>');
317 W('</ul><div class=\'tabs-bottom\'></div>');
319 W('<div id=\''+t+'-basic\'>');
320 createFieldTable('', [
321 { title: 'Start with WAN', name: 'f_vpn_'+t+'_eas', type: 'checkbox', value: nvram.vpn_client_eas.indexOf(''+(i+1)) >= 0 },
322 { title: 'Interface Type', name: 'vpn_'+t+'_if', type: 'select', options: [ ['tap','TAP'], ['tun','TUN'] ], value: eval( 'nvram.vpn_'+t+'_if' ) },
323 { title: 'Protocol', name: 'vpn_'+t+'_proto', type: 'select', options: [ ['udp','UDP'], ['tcp-client','TCP'] ], value: eval( 'nvram.vpn_'+t+'_proto' ) },
324 { title: 'Server Address/Port', multi: [
325 { name: 'vpn_'+t+'_addr', type: 'text', size: 17, value: eval( 'nvram.vpn_'+t+'_addr' ) },
326 { name: 'vpn_'+t+'_port', type: 'text', maxlen: 5, size: 7, value: eval( 'nvram.vpn_'+t+'_port' ) } ] },
327 { title: 'Firewall', name: 'vpn_'+t+'_firewall', type: 'select', options: [ ['auto', 'Automatic'], ['custom', 'Custom'] ], value: eval( 'nvram.vpn_'+t+'_firewall' ) },
328 { title: 'Authorization Mode', name: 'vpn_'+t+'_crypt', type: 'select', options: [ ['tls', 'TLS'], ['secret', 'Static Key'], ['custom', 'Custom'] ], value: eval( 'nvram.vpn_'+t+'_crypt' ),
329 suffix: '<span id=\''+t+'_custom_crypto_text\'>&nbsp;<small>(must configure manually...)</small></span>' },
330 { title: 'Username/Password Authentication', name: 'f_vpn_'+t+'_userauth', type: 'checkbox', value: eval( 'nvram.vpn_'+t+'_userauth' ) != 0 },
331 { title: 'Username: ', indent: 2, name: 'vpn_'+t+'_username', type: 'text', maxlen: 50, size: 54, value: eval( 'nvram.vpn_'+t+'_username' ) },
332 { title: 'Password: ', indent: 2, name: 'vpn_'+t+'_password', type: 'password', maxlen: 50, size: 54, value: eval( 'nvram.vpn_'+t+'_password' ) },
333 { title: 'Username Authen. Only', indent: 2, name: 'f_vpn_'+t+'_useronly', type: 'checkbox', value: eval( 'nvram.vpn_'+t+'_useronly' ) != 0,
334 suffix: '<span style="color: red" id=\''+t+'_ca_warn_text\'>&nbsp<small>Warning: Must define Certificate Authority.<small></span>' },
335 { 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' ) },
336 { title: 'Server is on the same subnet', name: 'f_vpn_'+t+'_bridge', type: 'checkbox', value: eval( 'nvram.vpn_'+t+'_bridge' ) != 0,
337 suffix: '<span style="color: red" id=\''+t+'_bridge_warn_text\'>&nbsp<small>Warning: Cannot bridge distinct subnets. Defaulting to routed mode.<small></span>' },
338 { title: 'Create NAT on tunnel', name: 'f_vpn_'+t+'_nat', type: 'checkbox', value: eval( 'nvram.vpn_'+t+'_nat' ) != 0,
339 suffix: '<span style="font-style: italic" id=\''+t+'_nat_warn_text\'>&nbsp<small>Routes must be configured manually.<small></span>' },
340 { title: 'Local/remote endpoint addresses', multi: [
341 { name: 'vpn_'+t+'_local', type: 'text', maxlen: 15, size: 17, value: eval( 'nvram.vpn_'+t+'_local' ) },
342 { name: 'vpn_'+t+'_remote', type: 'text', maxlen: 15, size: 17, value: eval( 'nvram.vpn_'+t+'_remote' ) } ] },
343 { title: 'Tunnel address/netmask', multi: [
344 { name: 'f_vpn_'+t+'_local', type: 'text', maxlen: 15, size: 17, value: eval( 'nvram.vpn_'+t+'_local' ) },
345 { name: 'vpn_'+t+'_nm', type: 'text', maxlen: 15, size: 17, value: eval( 'nvram.vpn_'+t+'_nm' ) } ] }
347 W('</div>');
348 W('<div id=\''+t+'-advanced\'>');
349 createFieldTable('', [
350 { title: 'Poll Interval', name: 'vpn_'+t+'_poll', type: 'text', maxlen: 4, size: 5, value: eval( 'nvram.vpn_'+t+'_poll' ), suffix: '&nbsp;<small>(in minutes, 0 to disable)</small>' },
351 { title: 'Redirect Internet traffic', multi: [
352 { name: 'f_vpn_'+t+'_rgw', type: 'checkbox', value: eval( 'nvram.vpn_'+t+'_rgw' ) != 0 },
353 { name: 'vpn_'+t+'_gw', type: 'text', maxlen: 15, size: 17, value: eval( 'nvram.vpn_'+t+'_gw' ), prefix: '<span id=\''+t+'_gateway\'> Gateway:&nbsp', suffix: '</span>'} ] },
354 { title: 'Accept DNS configuration', name: 'vpn_'+t+'_adns', type: 'select', options: [[0, 'Disabled'],[1, 'Relaxed'],[2, 'Strict'],[3, 'Exclusive']], value: eval( 'nvram.vpn_'+t+'_adns' ) },
355 { title: 'Encryption cipher', name: 'vpn_'+t+'_cipher', type: 'select', options: ciphers, value: eval( 'nvram.vpn_'+t+'_cipher' ) },
356 { title: 'Compression', name: 'vpn_'+t+'_comp', type: 'select', options: [ ['-1', 'Disabled'], ['no', 'None'], ['yes', 'Enabled'], ['adaptive', 'Adaptive'] ], value: eval( 'nvram.vpn_'+t+'_comp' ) },
357 { title: 'TLS Renegotiation Time', name: 'vpn_'+t+'_reneg', type: 'text', maxlen: 10, size: 7, value: eval( 'nvram.vpn_'+t+'_reneg' ),
358 suffix: '&nbsp;<small>(in seconds, -1 for default)</small>' },
359 { title: 'Connection retry', name: 'vpn_'+t+'_retry', type: 'text', maxlen: 5, size: 7, value: eval( 'nvram.vpn_'+t+'_retry' ),
360 suffix: '&nbsp;<small>(in seconds; -1 for infinite)</small>' },
361 { title: 'Verify server certificate (tls-remote)', multi: [
362 { name: 'f_vpn_'+t+'_tlsremote', type: 'checkbox', value: eval( 'nvram.vpn_'+t+'_tlsremote' ) != 0 },
363 { name: 'vpn_'+t+'_cn', type: 'text', maxlen: 64, size: 54,
364 value: eval( 'nvram.vpn_'+t+'_cn' ), prefix: '<span id=\''+t+'_cn\'> Common Name:&nbsp', suffix: '</span>'} ] },
365 { title: 'Custom Configuration', name: 'vpn_'+t+'_custom', type: 'textarea', value: eval( 'nvram.vpn_'+t+'_custom' ) }
367 W('</div>');
368 W('<div id=\''+t+'-keys\'>');
369 W('<p class=\'keyhelp\'>For help generating keys, refer to the OpenVPN <a id=\''+t+'-keyhelp\'>HOWTO</a>.</p>');
370 createFieldTable('', [
371 { title: 'Static Key', name: 'vpn_'+t+'_static', type: 'textarea', value: eval( 'nvram.vpn_'+t+'_static' ) },
372 { title: 'Certificate Authority', name: 'vpn_'+t+'_ca', type: 'textarea', value: eval( 'nvram.vpn_'+t+'_ca' ) },
373 { title: 'Client Certificate', name: 'vpn_'+t+'_crt', type: 'textarea', value: eval( 'nvram.vpn_'+t+'_crt' ) },
374 { title: 'Client Key', name: 'vpn_'+t+'_key', type: 'textarea', value: eval( 'nvram.vpn_'+t+'_key' ) },
376 W('</div>');
377 W('<div id=\''+t+'-status\'>');
378 W('<div id=\''+t+'-no-status\'><p>Client is not running or status could not be read.</p></div>');
379 W('<div id=\''+t+'-status-content\' style=\'display:none\' class=\'status-content\'>');
380 W('<div id=\''+t+'-status-header\' class=\'status-header\'><p>Data current as of <span id=\''+t+'-status-time\'></span>.</p></div>');
381 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>');
382 W('<div id=\''+t+'-status-errors\' class=\'error\'></div>');
383 W('</div>');
384 W('<div style=\'text-align:right\'><a href=\'javascript:updateStatus('+i+')\'>Refresh Status</a></div>');
385 W('</div>');
386 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">');
387 W('</div>');
390 </script>
391 </div>
393 </td></tr>
394 <tr><td id='footer' colspan=2>
395 <span id='footer-msg'></span>
396 <input type='button' value='Save' id='save-button' onclick='save()'>
397 <input type='button' value='Cancel' id='cancel-button' onclick='javascript:reloadPage();'>
398 </td></tr>
399 </table>
400 </form>
401 <script type='text/javascript'>init();</script>
402 </body>
403 </html>