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 } } }