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
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.
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']];
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
]]);
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
);
45 xob
.onError = function(ex
)
47 statusUpdaters
[num
].errors
.innerHTML
+= 'ERROR! '+ex
+'<br>';
51 xob
.post('/vpnstatus.cgi', 'client=' + (num
+1));
54 function tabSelect(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
)
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);
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',
98 _service
: service
+ (isup
? '-stop' : '-start')
102 function verifyFields(focused
, quiet
)
108 // When settings change, make sure we restart the right client
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
;
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
)
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
)
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
;
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");
181 elem
.display(PR('_vpn_'+t
+'_adns'), PR('_vpn_'+t
+'_reneg'), auth
== "tls");
182 elem
.display(E(t
+'_gateway'), iface
== "tap" && rgw
> 0);
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');
195 keyHelp
.href
= helpURL
['TLSKeys'];
198 keyHelp
.href
= helpURL
['staticKeys'];
201 keyHelp
.href
= helpURL
['howto'];
211 if (!verifyFields(null, false)) return;
215 E('vpn_client_eas').value
= '';
217 for (i
= 0; i
< tabs
.length
; ++i
)
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;
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]);
247 statusUpdaters
[i
].init(null,null,t
+'-status-stats-table',t
+'-status-time',t
+'-status-content',t
+'-no-status',t
+'-status-errors');
251 verifyFields(null, true);
255 <style type='text/css'
>
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>
287 <tr id='body'
><td id='navi'
><script type='text/javascript'
>navi()</script></td>
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
)
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\'> <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\'> <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\'> <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\'> <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' ) } ] }
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
: ' <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: ', 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
: ' <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
: ' <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: ', suffix
: '</span>'} ] },
365 { title
: 'Custom Configuration', name
: 'vpn_'+t
+'_custom', type
: 'textarea', value
: eval( 'nvram.vpn_'+t
+'_custom' ) }
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' ) },
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>');
384 W('<div style=\'text-align:right\'><a href=\'javascript:updateStatus('+i
+')\'>Refresh Status</a></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">');
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();'
>
401 <script type='text/javascript'
>init();</script>