WikiDevi.Wi-Cat.RU:DD-WRT/Remote Scripts
Jump to navigation
Jump to search
This script runs on a LAN-side host. It interacts with a dd-wrt router for tasks like launching a VPN tunnel on a one-time basis, configuring the router to persistently launch a VPN on boot, configuring the SES button to send a wake-on-lan signal, turning the radio on/off, etc.
This is a Tcl/Tk/Expect script.
Usage:
Syntax
ddwrt.exp [ help | [-host <host>] [-pw <password>] <command> ]
Commands
reboot
login
show [logs [-noexit] | vpncfg <vpn config file>]
logs: shows the most recent log file in /var/log/
vpncfg: shows the VPN config file that would be sent by the start or boot vpn commands, without actually taking any action.
start <vpn config file> [-noexit]
Starts an openvpn session on the router using the configuration file provided.
Paths in the configuration file are modified to suit the dd-wrt configuration,
and all keys and other files needed are collected and pushed to the router in one aggregated command.
The VPN session does not become a persistent configuration upon boot (see the boot command).
stop [-noexit]
Stops the VPN server.
boot { wos <IP> <MAC> | clear | vpncfg <vpn config file> } [-noexit]
wos: Configures the SES button to send a wake-on-lan signal. Setting is persistent.
IP: ip address of machine to wake.
MAC: mac address of machine to wake.
clear: Removes the boot script.
vpncfg: Configures the router to start a tunnel on boot. Setting is persistent.
radio [on | off] (default: on)
Environment variables
DDWRT_HOST : specifies the dd-wrt host in the absense of the -host option
DDWRT_PW : specifies the dd-wrt login password in the absense of the -pw option. If -pw is not used, and this variable is not set, then there will be a prompt for entry.
The script:
#!/usr/bin/expect --
#
# (copyleft) Justin Gombos
#
# Script to interact with a dd-wrt router.
#
proc put_help {} {
puts "Syntax\n"
puts "\tddwrt.exp \[ help | \[-host <host>] \[-pw <password>] <command> ]\n"
puts "Commands\n"
puts "\treboot\n"
puts "\tlogin\n"
puts "\tshow \[logs \[-noexit] | vpncfg <vpn config file>]\n"
puts "\t\tlogs: shows the most recent log file in /var/log/"
puts "\t\tvpncfg: shows the VPN config file that would be sent by the start or boot vpn commands, without actually taking any action.\n"
puts "\tstart <vpn config file> \[-noexit]\n"
puts "\t\tStarts an openvpn session on the router using the configuration file provided.\n\t\tPaths in the configuration file are modified to suit the dd-wrt configuration,\n\t\tand all keys and other files needed are collected and pushed to the router in one aggregated command.\n\t\tThe VPN session does not become a persistent configuration upon boot (see the boot command).\n"
puts "\tstop \[-noexit]\n"
puts "\t\tStops the VPN server.\n"
puts "\tboot { wos <IP> <MAC> | clear | vpncfg <vpn config file> } \[-noexit]\n"
puts "\t\twos: Configures the SES button to send a wake-on-lan signal. Setting is persistent."
puts "\t\t\t IP: ip address of machine to wake."
puts "\t\t\t MAC: mac address of machine to wake."
puts "\t\tclear: Removes the boot script."
puts "\t\tvpncfg: Configures the router to start a tunnel on boot. Setting is persistent.\n"
puts "\tradio \[on | off] (default: on)"
puts ""
puts "Environment variables"
puts ""
puts "\tDDWRT_HOST : specifies the dd-wrt host in the absense of the -host option"
puts "\tDDWRT_PW : specifies the dd-wrt login password in the absense of the -pw option. If -pw is not used, and this variable is not set, then there will be a prompt for entry."
puts "\n\n"
}
proc initializeGlobals {envName argvName} {
global host
upvar 1 $envName lenv
upvar 1 $argvName largs
if {[set pos [lsearch $largs -host]] != -1} {
set host(ip) [lindex $largs [expr $pos + 1]]
set largs [lreplace $largs $pos [expr $pos + 1]]
} elseif {[array name lenv DDWRT_HOST] != ""} {
set host(ip) $lenv(DDWRT_HOST)
} else {
set host(ip) "UNKNOWN"
}
if {[set pos [lsearch $largs -pw]] != -1} {
set host(pw) [lindex $largs [expr $pos + 1]]
set largs [lreplace $largs $pos [expr $pos + 1]]
} elseif {[array name lenv DDWRT_PW] != ""} {
set host(pw) $lenv(DDWRT_PW)
} else {
set host(pw) "UNKNOWN"
}
}
proc getpass pwprompt {
# taken from http://wiki.tcl.tk/3594
set oldmode [stty -echo -raw]
send_user "\n $pwprompt"
set timeout -1
expect_user -re "(.*)\n"
send_user "\n"
eval stty $oldmode
return $expect_out(1,string)
}
proc login {target_ip {target_pw "UNKNOWN"}} {
set target(ip) $target_ip
set target(user) root
set target(host) "DEADBEEF"
if {$target_pw == "UNKNOWN"} {
set target(pw) [getpass "Enter password for $target(user): "]
} else {
set target(pw) $target_pw
}
expect {
"telnet>" {
send "open $target(ip)\n"
exp_continue
}
-re {([[:alnum:]]*)\ (.ogin: )} {
set target(host) $expect_out(1,string)
send "$target(user)\n"
exp_continue
}
-glob "assword: " {
exp_send "$target(pw)\n"
exp_continue
}
-re {root@.*:} {
return $target(host)
}
}
}
# Returns the path to a file, first checking the existence of the
# supplied file. If it's not found, the candidate path is checked.
#
proc pathname {target candidate_path} {
if { [file exist $target] } {
set return_data $target
} else {
set return_data [file join $candidate_path $target]
}
return $return_data
}
proc filetext {filename} {
if { [file exist $filename] } {
set fileptr [open $filename "r"]
set return_data "echo \'[read $fileptr]\' > [file join \$OVPN_DIR [file tail $filename]]"
close $fileptr
} else {
set return_data "echo \'no $filename exists to write to\'"
}
return $return_data
}
proc rawtext {filename contents} {
return "echo \'$contents\n\' > [file join \$OVPN_DIR $filename]"
}
proc aggregated_launch_string {config_file loop_timeout} {
set dir(ovpn.target) [file join / tmp openvpn]
set filename(conf) $config_file
set filetail(conf) [file tail $filename(conf)]
set fileptr(conf) [open $filename(conf) "r"]
set filetext(conf) [read $fileptr(conf)]
regexp -nocase {[\n^][[:blank:]]*\mkey[[:blank:]]+([[:graph:]]+)} $filetext(conf) junk keyfile
regexp -nocase {\mca[[:blank:]]+([[:graph:]]+)} $filetext(conf) junk cafile
regexp -nocase {\mcert[[:blank:]]+([[:graph:]]+)} $filetext(conf) junk certfile
regexp -nocase {\mtls-auth[[:blank:]]+([[:graph:]]+)} $filetext(conf) junk takeyfile
regexp -nocase {\mauth-user-pass[[:blank:]]+([[:graph:]]+)} $filetext(conf) junk authfile
set keydump ""
set keyfilelist ""
if { [info exists cafile]} {set keyfilename(ca) [pathname $cafile [file dirname $filename(conf)]]}
if { [info exists certfile]} {set keyfilename(cert) [pathname $certfile [file dirname $filename(conf)]]}
if { [info exists keyfile]} {set keyfilename(key) [pathname $keyfile [file dirname $filename(conf)]]}
if { [info exists takeyfile]} {set keyfilename(takey) [pathname $takeyfile [file dirname $filename(conf)]]}
if { [info exists authfile]} {set keyfilename(auth) [pathname $authfile [file dirname $filename(conf)]]}
foreach keyindex [array names keyfilename] {
if {[string length $keydump] == 0} {
set keydump "[filetext $keyfilename($keyindex)]"
} else {
set keydump "[filetext $keyfilename($keyindex)] && $keydump"
}
set keyfilelist "[file join \$OVPN_DIR [file tail $keyfilename($keyindex)]] $keyfilelist"
}
# Remove the pathnames from the certificates and keys in the config file
#
set target_config $filetext(conf)
regsub {(\mca[[:blank:]]+)([[:graph:]]*/)([^/]+)} $target_config {\1\3} target_config
regsub {(\mcert[[:blank:]]+)([[:graph:]]*/)([^/]+)} $target_config {\1\3} target_config
regsub {(\mkey[[:blank:]]+)([[:graph:]]*/)([^/]+)} $target_config {\1\3} target_config
set return_data "OVPN_DIR=$dir(ovpn.target) && \\
mkdir \$OVPN_DIR ; \\
cd \$OVPN_DIR ; \\
ln -s [file join / usr sbin openvpn] [file join \$OVPN_DIR openvpn] ; \\
[rawtext $filetail(conf) $target_config] && $keydump && \\
[rawtext route-up.sh {iptables -A POSTROUTING -t nat -o tun0 -j MASQUERADE}] && \\
[rawtext route-down.sh {iptables -D POSTROUTING -t nat -o tun0 -j MASQUERADE}] && \\
chmod 600 $keyfilelist [file join \$OVPN_DIR $filetail(conf)] && \\
chmod 700 [file join \$OVPN_DIR route-up.sh] [file join \$OVPN_DIR route-down.sh] ; \\
killall openvpn ; starttime=\$(date +%s) && \\
while \[\[ \\( \$(nvram get wanup) -eq 0 \\
-o ! -f [file join \$OVPN_DIR [file tail $keyfilename(cert)]] \\) \\
-a \$(date +%s) -lt \$((\$starttime+$loop_timeout)) ]] ; do sleep 1; done && \\
sleep 2 && openvpn --config [file join \$OVPN_DIR $filetail(conf)] \\
--route-up [file join \$OVPN_DIR route-up.sh] \\
--down [file join \$OVPN_DIR route-down.sh] --daemon &
"
return $return_data
}
rename puts tcl::puts
proc puts string {if [catch {tcl::puts $string}] exit}
initializeGlobals env argv
if {$argc == 0 || [lsearch $argv help] != -1 || $host(ip) == "UNKNOWN"} {
put_help
} else {
set command [lindex $argv 0]
spawn telnet
set target(sid) $spawn_id
switch $command {
reboot {
set target_hostname [login $host(ip) $host(pw)]
send "reboot\n"
expect {
-re {root@.*:} {exp_send "exit\n"}
}
}
login {
set target_hostname [login $host(ip) $host(pw)]
interact
}
show {
set subcommand [lindex $argv 1]
if {$subcommand == "vpncfg"} {
set ovpn_config_filename [lindex $argv 2]
puts "[aggregated_launch_string $ovpn_config_filename 90]"
} else {
set target_hostname [login $host(ip) $host(pw)]
send "ls -1rt /var/log | while read i; do tail -n 5 /var/log/\$i; done\n"
if {[lindex $argv 2] == "-noexit"} {
interact
} else {
expect {
-re {root@.*:} {exp_send "exit\n"}
}
}
}
}
radio {
set target_hostname [login $host(ip) $host(pw)]
set subcommand [lindex $argv 1]
if {$subcommand == "off"} {
send "wl radio off\n"
} else {
send "wl radio on\n"
}
if {[lindex $argv 2] == "-noexit"} {
interact
} else {
expect {
-re {root@.*:} {exp_send "exit\n"}
}
}
}
start {
set target_hostname [login $host(ip) $host(pw)]
set ovpn_config_filename [lindex $argv 1]
send "[aggregated_launch_string $ovpn_config_filename 90]"
if {[lindex $argv 2] == "-noexit"} {
interact
} else {
expect {
-re {root@.*:} {exp_send "exit\n"}
}
}
}
stop {
set target_hostname [login $host(ip) $host(pw)]
send "killall openvpn\n"
if {[lindex $argv 1] == "-noexit"} {
interact
} else {
expect {
-re {root@.*:} {exp_send "exit\n"}
}
}
}
boot {
set target_hostname [login $host(ip) $host(pw)]
set subcommand [lindex $argv 1]
switch $subcommand {
vpncfg {
send "nvram set rc_startup=\"[aggregated_launch_string [lindex $argv 2] 90]\"\n"
expect {
-re {root@.*:} {exp_send "nvram commit\n"}
}
if {[lindex $argv 3] == "-noexit"} {
interact
} else {
expect {
-re {root@.*:} {exp_send "exit\n"}
}
}
}
wos {
send "nvram set rc_startup=\"\
SCRIPT_FILE=/tmp/etc/config/wake_on_lan.sesbutton && mkdir -p \$(dirname \$SCRIPT_FILE) ;\
echo '#!/bin/sh ,,/usr/sbin/wol -i [lindex $argv 2] [lindex $argv 3] ,' \
| tr ',' '\n' > \$SCRIPT_FILE && chmod +x \$SCRIPT_FILE\"\n"
expect {
-re {root@.*:} {exp_send "nvram commit\n"}
}
if {[lindex $argv 3] == "-noexit"} {
interact
} else {
expect {
-re {root@.*:} {exp_send "exit\n"}
}
}
}
clear {
send "nvram unset rc_startup\n"
expect {
-re {root@.*:} {exp_send "nvram commit\n"}
}
if {[lindex $argv 3] == "-noexit"} {
interact
} else {
expect {
-re {root@.*:} {exp_send "exit\n"}
}
}
}
}
}
default {
put_help
}
}
}