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(); %>] <% translate(
"VPN"); %
>: <% translate(
"Client"); %
></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_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_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"); %>
25 tabs
= [['client1', '<% translate("Client"); %> 1'],['client2', '<% translate("Client"); %> 2']];
26 sections
= [['basic', '<% translate("Basic"); %>'],['advanced', '<% translate("Advanced"); %>'],['keys','<% translate("Keys"); %>'],['status','<% translate("Status"); %>']];
28 for (i
= 0; i
< tabs
.length
; ++i
) statusUpdaters
.push(new StatusUpdater());
29 ciphers
= [['default','<% translate("Use Default"); %>'],['none','<% translate("None"); %>']];
30 for (i
= 0; i
< vpnciphers
.length
; ++i
) ciphers
.push([vpnciphers
[i
],vpnciphers
[i
]]);
33 vpn1up
= parseInt('<% psup("vpnclient1"); %>');
34 vpn2up
= parseInt('<% psup("vpnclient2"); %>');
36 function updateStatus(num
)
38 var xob
= new XmlHttp();
39 xob
.onCompleted = function(text
, xml
)
41 statusUpdaters
[num
].update(text
);
44 xob
.onError = function(ex
)
46 statusUpdaters
[num
].errors
.innerHTML
+= 'ERROR! '+ex
+'<br>';
50 xob
.post('/vpnstatus.cgi', 'client=' + (num
+1));
53 function tabSelect(name
)
59 for (var i
= 0; i
< tabs
.length
; ++i
)
61 var on
= (name
== tabs
[i
][0]);
62 elem
.display(tabs
[i
][0] + '-tab', on
);
65 cookie
.set('vpn_client_tab', name
);
68 function sectSelect(tab
, section
)
72 for (var i
= 0; i
< sections
.length
; ++i
)
74 if (section
== sections
[i
][0])
76 elem
.addClass(tabs
[tab
][0]+'-'+sections
[i
][0]+'-tab', 'active');
77 elem
.display(tabs
[tab
][0]+'-'+sections
[i
][0], true);
81 elem
.removeClass(tabs
[tab
][0]+'-'+sections
[i
][0]+'-tab', 'active');
82 elem
.display(tabs
[tab
][0]+'-'+sections
[i
][0], false);
86 cookie
.set('vpn_client'+tab
+'_section', section
);
89 function toggle(service
, isup
)
91 if (changed
&& !confirm("<% translate("Unsaved changes will be lost
. Continue anyway
"); %>?")) return;
93 E('_' + service
+ '_button').disabled
= true;
94 form
.submitHidden('service.cgi', {
95 _redirect
: 'vpn-client.asp',
97 _service
: service
+ (isup
? '-stop' : '-start')
101 function verifyFields(focused
, quiet
)
107 // When settings change, make sure we restart the right client
112 var clientindex
= focused
.name
.indexOf("client");
113 if (clientindex
>= 0)
115 var clientnumber
= focused
.name
.substring(clientindex
+6,clientindex
+7);
116 var stripped
= focused
.name
.substring(0,clientindex
+6)+focused
.name
.substring(clientindex
+7);
118 if (stripped
== 'vpn_client_local')
119 E('_f_vpn_client'+clientnumber
+'_local').value
= focused
.value
;
120 else if (stripped
== 'f_vpn_client_local')
121 E('_vpn_client'+clientnumber
+'_local').value
= focused
.value
;
124 if (eval('vpn'+clientnumber
+'up') && fom
._service
.value
.indexOf('client'+clientnumber
) < 0)
126 if ( fom
._service
.value
!= "" ) fom
._service
.value
+= ",";
127 fom
._service
.value
+= 'vpnclient'+clientnumber
+'-restart';
132 // Element varification
133 for (i
= 0; i
< tabs
.length
; ++i
)
137 if (!v_range('_vpn_'+t
+'_poll', quiet
, 0, 1440)) ret
= 0;
138 if (!v_ip('_vpn_'+t
+'_addr', true) && !v_domain('_vpn_'+t
+'_addr', true)) { ferror
.set(E('_vpn_'+t
+'_addr'), "<% translate("Invalid server address
"); %>.", quiet
); ret
= 0; }
139 if (!v_port('_vpn_'+t
+'_port', quiet
)) ret
= 0;
140 if (!v_ip('_vpn_'+t
+'_local', quiet
, 1)) ret
= 0;
141 if (!v_ip('_f_vpn_'+t
+'_local', true, 1)) ret
= 0;
142 if (!v_ip('_vpn_'+t
+'_remote', quiet
, 1)) ret
= 0;
143 if (!v_netmask('_vpn_'+t
+'_nm', quiet
)) ret
= 0;
144 if (!v_range('_vpn_'+t
+'_retry', quiet
, -1, 32767)) ret
= 0;
145 if (!v_range('_vpn_'+t
+'_reneg', quiet
, -1, 2147483647)) ret
= 0;
146 if (E('_vpn_'+t
+'_gw').value
.length
> 0 && !v_ip('_vpn_'+t
+'_gw', quiet
, 1)) ret
= 0;
149 // Visability changes
150 for (i
= 0; i
< tabs
.length
; ++i
)
154 fw
= E('_vpn_'+t
+'_firewall');
155 auth
= E('_vpn_'+t
+'_crypt');
156 iface
= E('_vpn_'+t
+'_if');
157 bridge
= E('_f_vpn_'+t
+'_bridge');
158 nat
= E('_f_vpn_'+t
+'_nat');
159 hmac
= E('_vpn_'+t
+'_hmac');
160 rgw
= E('_f_vpn_'+t
+'_rgw');
162 elem
.display(PR('_vpn_'+t
+'_ca'), PR('_vpn_'+t
+'_crt'), PR('_vpn_'+t
+'_key'), PR('_vpn_'+t
+'_hmac'),
163 PR('_vpn_'+t
+'_adns'), PR('_vpn_'+t
+'_reneg'), auth
.value
== "tls");
164 elem
.display(PR('_vpn_'+t
+'_static'), auth
.value
== "secret" || (auth
.value
== "tls" && hmac
.value
>= 0));
165 elem
.display(E(t
+'_custom_crypto_text'), auth
.value
== "custom");
166 elem
.display(PR('_f_vpn_'+t
+'_bridge'), iface
.value
== "tap");
167 elem
.display(E(t
+'_bridge_warn_text'), !bridge
.checked
);
168 elem
.display(PR('_f_vpn_'+t
+'_nat'), fw
.value
!= "custom" && (iface
.value
== "tun" || !bridge
.checked
));
169 elem
.display(E(t
+'_nat_warn_text'), fw
.value
!= "custom" && (!nat
.checked
|| (auth
.value
== "secret" && iface
.value
== "tun")));
170 elem
.display(PR('_vpn_'+t
+'_local'), auth
.value
== "secret" && iface
.value
== "tun");
171 elem
.display(PR('_f_vpn_'+t
+'_local'), auth
.value
== "secret" && (iface
.value
== "tap" && !bridge
.checked
));
172 elem
.display(E(t
+'_gateway'), iface
.value
== "tap" && rgw
.checked
);
174 keyHelp
= E(t
+'-keyhelp');
178 keyHelp
.href
= helpURL
['TLSKeys'];
181 keyHelp
.href
= helpURL
['staticKeys'];
184 keyHelp
.href
= helpURL
['howto'];
194 if (!verifyFields(null, false)) return;
198 E('vpn_client_eas').value
= '';
200 for (i
= 0; i
< tabs
.length
; ++i
)
204 if ( E('_f_vpn_'+t
+'_eas').checked
)
205 E('vpn_client_eas').value
+= ''+(i
+1)+',';
207 E('vpn_'+t
+'_bridge').value
= E('_f_vpn_'+t
+'_bridge').checked
? 1 : 0;
208 E('vpn_'+t
+'_nat').value
= E('_f_vpn_'+t
+'_nat').checked
? 1 : 0;
209 E('vpn_'+t
+'_rgw').value
= E('_f_vpn_'+t
+'_rgw').checked
? 1 : 0;
219 tabSelect(cookie
.get('vpn_client_tab') || tabs
[0][0]);
221 for (i
= 0; i
< tabs
.length
; ++i
)
223 sectSelect(i
, cookie
.get('vpn_client'+i
+'_section') || sections
[i
][0]);
227 statusUpdaters
[i
].init(null,null,t
+'-status-stats-table',t
+'-status-time',t
+'-status-content',t
+'-no-status',t
+'-status-errors');
231 verifyFields(null, true);
235 <style type='text/css'
>
261 <form id='_fom' method='post' action='tomato.cgi'
>
262 <table id='container' cellspacing=
0>
263 <tr><td colspan=
2 id='header'
>
264 <div class='title'
>Tomato
</div>
265 <div class='version'
><%
translate("Version"); %> <% version(); %
></div>
267 <tr id='body'
><td id='navi'
><script type='text/javascript'
>navi()</script></td>
269 <div id='ident'
><%
ident(); %></div>
271 <input type='hidden' name='_nextpage' value='vpn-client.asp'
>
272 <input type='hidden' name='_nextwait' value='
5'
>
273 <input type='hidden' name='_service' value=''
>
274 <input type='hidden' name='vpn_client_eas' id='vpn_client_eas' value=''
>
276 <div class='section-title'
><%
translate("VPN Client Configuration"); %></div>
277 <div class='section'
>
278 <script type='text/javascript'
>
279 tabCreate
.apply(this, tabs
);
281 for (i
= 0; i
< tabs
.length
; ++i
)
284 W('<div id=\''+t
+'-tab\'>');
285 W('<input type=\'hidden\' id=\'vpn_'+t
+'_bridge\' name=\'vpn_'+t
+'_bridge\'>');
286 W('<input type=\'hidden\' id=\'vpn_'+t
+'_nat\' name=\'vpn_'+t
+'_nat\'>');
287 W('<input type=\'hidden\' id=\'vpn_'+t
+'_rgw\' name=\'vpn_'+t
+'_rgw\'>');
289 W('<ul class="tabs">');
290 for (j
= 0; j
< sections
.length
; j
++)
292 W('<li><a href="javascript:sectSelect('+i
+', \''+sections
[j
][0]+'\')" id="'+t
+'-'+sections
[j
][0]+'-tab">'+sections
[j
][1]+'</a></li>');
294 W('</ul><div class=\'tabs-bottom\'></div>');
296 W('<div id=\''+t
+'-basic\'>');
297 createFieldTable('', [
298 { title
: '<% translate("Start with WAN"); %>', name
: 'f_vpn_'+t
+'_eas', type
: 'checkbox', value
: nvram
.vpn_client_eas
.indexOf(''+(i
+1)) >= 0 },
299 { title
: '<% translate("Interface Type"); %>', name
: 'vpn_'+t
+'_if', type
: 'select', options
: [ ['tap','TAP'], ['tun','TUN'] ], value
: eval( 'nvram.vpn_'+t
+'_if' ) },
300 { title
: '<% translate("Protocol"); %>', name
: 'vpn_'+t
+'_proto', type
: 'select', options
: [ ['udp','UDP'], ['tcp-client','TCP'] ], value
: eval( 'nvram.vpn_'+t
+'_proto' ) },
301 { title
: '<% translate("Server Address/Port"); %>', multi
: [
302 { name
: 'vpn_'+t
+'_addr', type
: 'text', size
: 17, value
: eval( 'nvram.vpn_'+t
+'_addr' ) },
303 { name
: 'vpn_'+t
+'_port', type
: 'text', maxlen
: 5, size
: 7, value
: eval( 'nvram.vpn_'+t
+'_port' ) } ] },
304 { title
: '<% translate("Firewall"); %>', name
: 'vpn_'+t
+'_firewall', type
: 'select', options
: [ ['auto', '<% translate("Automatic"); %>'], ['custom', '<% translate("Custom"); %>'] ], value
: eval( 'nvram.vpn_'+t
+'_firewall' ) },
305 { title
: '<% translate("Authorization Mode"); %>', name
: 'vpn_'+t
+'_crypt', type
: 'select', options
: [ ['tls', 'TLS'], ['secret', '<% translate("Static Key"); %>'], ['custom', '<% translate("Custom"); %>'] ], value
: eval( 'nvram.vpn_'+t
+'_crypt' ),
306 suffix
: '<span id=\''+t
+'_custom_crypto_text\'> <small>(<% translate("must configure manually"); %>...)</small></span>' },
307 { title
: '<% translate("Extra HMAC authorization"); %> (tls-auth)', name
: 'vpn_'+t
+'_hmac', type
: 'select', options
: [ [-1, '<% translate("Disabled"); %>'], [2, '<% translate("Bi-directional"); %>'], [0, '<% translate("Incoming"); %> (0)'], [1, '<% translate("Outgoing"); %> (1)'] ], value
: eval( 'nvram.vpn_'+t
+'_hmac' ) },
308 { title
: '<% translate("Server is on the same subnet"); %>', name
: 'f_vpn_'+t
+'_bridge', type
: 'checkbox', value
: eval( 'nvram.vpn_'+t
+'_bridge' ) != 0,
309 suffix
: '<span style="color: red" id=\''+t
+'_bridge_warn_text\'> <small><% translate("Warning: Cannot bridge distinct subnets. Defaulting to routed mode"); %>.<small></span>' },
310 { title
: '<% translate("Create NAT on tunnel"); %>', name
: 'f_vpn_'+t
+'_nat', type
: 'checkbox', value
: eval( 'nvram.vpn_'+t
+'_nat' ) != 0,
311 suffix
: '<span style="font-style: italic" id=\''+t
+'_nat_warn_text\'> <small><% translate("Routes must be configured manually"); %>.<small></span>' },
312 { title
: '<% translate("Local/remote endpoint addresses"); %>', multi
: [
313 { name
: 'vpn_'+t
+'_local', type
: 'text', maxlen
: 15, size
: 17, value
: eval( 'nvram.vpn_'+t
+'_local' ) },
314 { name
: 'vpn_'+t
+'_remote', type
: 'text', maxlen
: 15, size
: 17, value
: eval( 'nvram.vpn_'+t
+'_remote' ) } ] },
315 { title
: '<% translate("Tunnel address/netmask"); %>', multi
: [
316 { name
: 'f_vpn_'+t
+'_local', type
: 'text', maxlen
: 15, size
: 17, value
: eval( 'nvram.vpn_'+t
+'_local' ) },
317 { name
: 'vpn_'+t
+'_nm', type
: 'text', maxlen
: 15, size
: 17, value
: eval( 'nvram.vpn_'+t
+'_nm' ) } ] }
320 W('<div id=\''+t
+'-advanced\'>');
321 createFieldTable('', [
322 { title
: '<% translate("Poll Interval"); %>', name
: 'vpn_'+t
+'_poll', type
: 'text', maxlen
: 4, size
: 5, value
: eval( 'nvram.vpn_'+t
+'_poll' ), suffix
: ' <small>(<% translate("in minutes, 0 to disable"); %>)</small>' },
323 { title
: '<% translate("Redirect Internet traffic"); %>', multi
: [
324 { name
: 'f_vpn_'+t
+'_rgw', type
: 'checkbox', value
: eval( 'nvram.vpn_'+t
+'_rgw' ) != 0 },
325 { name
: 'vpn_'+t
+'_gw', type
: 'text', maxlen
: 15, size
: 17, value
: eval( 'nvram.vpn_'+t
+'_gw' ), prefix
: '<span id=\''+t
+'_gateway\'><% translate("Gateway"); %>: ', suffix
: '</span>'} ] },
326 { title
: '<% translate("Accept DNS configuration"); %>', name
: 'vpn_'+t
+'_adns', type
: 'select', options
: [[0, '<% translate("Disabled"); %>'],[1, '<% translate("Relaxed"); %>'],[2, '<% translate("Strict"); %>'],[3, '<% translate("Exclusive"); %>']], value
: eval( 'nvram.vpn_'+t
+'_adns' ) },
327 { title
: '<% translate("Encryption cipher"); %>', name
: 'vpn_'+t
+'_cipher', type
: 'select', options
: ciphers
, value
: eval( 'nvram.vpn_'+t
+'_cipher' ) },
328 { title
: '<% translate("Compression"); %>', name
: 'vpn_'+t
+'_comp', type
: 'select', options
: [ ['-1', '<% translate("Disabled"); %>'], ['no', '<% translate("None"); %>'], ['yes', '<% translate("Enabled"); %>'], ['adaptive', '<% translate("Adaptive"); %>'] ], value
: eval( 'nvram.vpn_'+t
+'_comp' ) },
329 { title
: '<% translate("TLS Renegotiation Time"); %>', name
: 'vpn_'+t
+'_reneg', type
: 'text', maxlen
: 10, size
: 7, value
: eval( 'nvram.vpn_'+t
+'_reneg' ),
330 suffix
: ' <small>(<% translate("in seconds, -1 for default"); %>)</small>' },
331 { title
: '<% translate("Connection retry"); %>', name
: 'vpn_'+t
+'_retry', type
: 'text', maxlen
: 5, size
: 7, value
: eval( 'nvram.vpn_'+t
+'_retry' ),
332 suffix
: ' <small>(<% translate("in seconds; -1 for infinite"); %>)</small>' },
333 { title
: '<% translate("Custom Configuration"); %>', name
: 'vpn_'+t
+'_custom', type
: 'textarea', value
: eval( 'nvram.vpn_'+t
+'_custom' ) }
336 W('<div id=\''+t
+'-keys\'>');
337 W('<p class=\'keyhelp\'><% translate("For help generating keys, refer to the OpenVPN"); %> <a id=\''+t
+'-keyhelp\'><% translate("HOWTO"); %></a>.</p>');
338 createFieldTable('', [
339 { title
: '<% translate("Static Key"); %>', name
: 'vpn_'+t
+'_static', type
: 'textarea', value
: eval( 'nvram.vpn_'+t
+'_static' ) },
340 { title
: '<% translate("Certificate Authority"); %>', name
: 'vpn_'+t
+'_ca', type
: 'textarea', value
: eval( 'nvram.vpn_'+t
+'_ca' ) },
341 { title
: '<% translate("Client Certificate"); %>', name
: 'vpn_'+t
+'_crt', type
: 'textarea', value
: eval( 'nvram.vpn_'+t
+'_crt' ) },
342 { title
: '<% translate("Client Key"); %>', name
: 'vpn_'+t
+'_key', type
: 'textarea', value
: eval( 'nvram.vpn_'+t
+'_key' ) }
345 W('<div id=\''+t
+'-status\'>');
346 W('<div id=\''+t
+'-no-status\'><p><% translate("Client is not running or status could not be read"); %>.</p></div>');
347 W('<div id=\''+t
+'-status-content\' style=\'display:none\' class=\'status-content\'>');
348 W('<div id=\''+t
+'-status-header\' class=\'status-header\'><p><% translate("Data current as of"); %> <span id=\''+t
+'-status-time\'></span>.</p></div>');
349 W('<div id=\''+t
+'-status-stats\'><div class=\'section-title\'><% translate("General Statistics"); %></div><table class=\'tomato-grid status-table\' id=\''+t
+'-status-stats-table\'></table><br></div>');
350 W('<div id=\''+t
+'-status-errors\' class=\'error\'></div>');
352 W('<div style=\'text-align:right\'><a href=\'javascript:updateStatus('+i
+')\'><% translate("Refresh Status"); %></a></div>');
354 W('<input type="button" value="' + (eval('vpn'+(i
+1)+'up') ? '<% translate("Stop"); %>' : '<% translate("Start"); %>') + ' <% translate("Now"); %>" onclick="toggle(\'vpn'+t
+'\', vpn'+(i
+1)+'up)" id="_vpn'+t
+'_button">');
362 <tr><td id='footer' colspan=
2>
363 <span id='footer-msg'
></span>
364 <input type='button' value='<%
translate("Save"); %>' id='save-button' onclick='save()'
>
365 <input type='button' value='<%
translate("Cancel"); %>' id='cancel-button' onclick='javascript:reloadPage();'
>
369 <script type='text/javascript'
>init();</script>