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(); %>] VPN: 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', 'Client 1'],['client2', 'Client 2']];
26 sections
= [['basic', 'Basic'],['advanced', 'Advanced'],['keys','Keys'],['status','Status']];
28 for (i
= 0; i
< tabs
.length
; ++i
) statusUpdaters
.push(new StatusUpdater());
29 ciphers
= [['default','Use Default'],['none','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("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'), "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'
>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'
>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
: 'Start with WAN', name
: 'f_vpn_'+t
+'_eas', type
: 'checkbox', value
: nvram
.vpn_client_eas
.indexOf(''+(i
+1)) >= 0 },
299 { title
: 'Interface Type', name
: 'vpn_'+t
+'_if', type
: 'select', options
: [ ['tap','TAP'], ['tun','TUN'] ], value
: eval( 'nvram.vpn_'+t
+'_if' ) },
300 { title
: 'Protocol', name
: 'vpn_'+t
+'_proto', type
: 'select', options
: [ ['udp','UDP'], ['tcp-client','TCP'] ], value
: eval( 'nvram.vpn_'+t
+'_proto' ) },
301 { title
: '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
: 'Firewall', name
: 'vpn_'+t
+'_firewall', type
: 'select', options
: [ ['auto', 'Automatic'], ['custom', 'Custom'] ], value
: eval( 'nvram.vpn_'+t
+'_firewall' ) },
305 { title
: 'Authorization Mode', name
: 'vpn_'+t
+'_crypt', type
: 'select', options
: [ ['tls', 'TLS'], ['secret', 'Static Key'], ['custom', 'Custom'] ], value
: eval( 'nvram.vpn_'+t
+'_crypt' ),
306 suffix
: '<span id=\''+t
+'_custom_crypto_text\'> <small>(must configure manually...)</small></span>' },
307 { 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' ) },
308 { title
: '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>Warning: Cannot bridge distinct subnets. Defaulting to routed mode.<small></span>' },
310 { title
: '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>Routes must be configured manually.<small></span>' },
312 { title
: '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
: '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
: '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>' },
323 { title
: '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\'>Gateway: ', suffix
: '</span>'} ] },
326 { 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' ) },
327 { title
: 'Encryption cipher', name
: 'vpn_'+t
+'_cipher', type
: 'select', options
: ciphers
, value
: eval( 'nvram.vpn_'+t
+'_cipher' ) },
328 { title
: 'Compression', name
: 'vpn_'+t
+'_comp', type
: 'select', options
: [ ['-1', 'Disabled'], ['no', 'None'], ['yes', 'Enabled'], ['adaptive', 'Adaptive'] ], value
: eval( 'nvram.vpn_'+t
+'_comp' ) },
329 { title
: 'TLS Renegotiation Time', name
: 'vpn_'+t
+'_reneg', type
: 'text', maxlen
: 10, size
: 7, value
: eval( 'nvram.vpn_'+t
+'_reneg' ),
330 suffix
: ' <small>(in seconds, -1 for default)</small>' },
331 { title
: 'Connection retry', name
: 'vpn_'+t
+'_retry', type
: 'text', maxlen
: 5, size
: 7, value
: eval( 'nvram.vpn_'+t
+'_retry' ),
332 suffix
: ' <small>(in seconds; -1 for infinite)</small>' },
333 { title
: '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\'>For help generating keys, refer to the OpenVPN <a id=\''+t
+'-keyhelp\'>HOWTO</a>.</p>');
338 createFieldTable('', [
339 { title
: 'Static Key', name
: 'vpn_'+t
+'_static', type
: 'textarea', value
: eval( 'nvram.vpn_'+t
+'_static' ) },
340 { title
: 'Certificate Authority', name
: 'vpn_'+t
+'_ca', type
: 'textarea', value
: eval( 'nvram.vpn_'+t
+'_ca' ) },
341 { title
: 'Client Certificate', name
: 'vpn_'+t
+'_crt', type
: 'textarea', value
: eval( 'nvram.vpn_'+t
+'_crt' ) },
342 { title
: '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>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>Data current as of <span id=\''+t
+'-status-time\'></span>.</p></div>');
349 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>');
350 W('<div id=\''+t
+'-status-errors\' class=\'error\'></div>');
352 W('<div style=\'text-align:right\'><a href=\'javascript:updateStatus('+i
+')\'>Refresh Status</a></div>');
354 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">');
362 <tr><td id='footer' colspan=
2>
363 <span id='footer-msg'
></span>
364 <input type='button' value='Save' id='save-button' onclick='save()'
>
365 <input type='button' value='Cancel' id='cancel-button' onclick='javascript:reloadPage();'
>
369 <script type='text/javascript'
>init();</script>