DD-WRT:Remote Scripts

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 ] [-pw ] ]

Commands

reboot

login

show [logs [-noexit] | vpncfg ]

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 [-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   | clear | vpncfg } [-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:
 * 1) !/usr/bin/expect --
 * 2) (copyleft) Justin Gombos
 * 3) Script to interact with a dd-wrt router.
 * 1) Script to interact with a dd-wrt router.
 * 1) Script to interact with a dd-wrt router.

proc put_help {} { puts "Syntax\n" puts "\tddwrt.exp \[ help | \[-host ] \[-pw ] ]\n" puts "Commands\n" puts "\treboot\n" puts "\tlogin\n" puts "\tshow \[logs \[-noexit] | vpncfg ]\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 \[-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   | clear | vpncfg } \[-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) }   } }

proc pathname {target candidate_path} {
 * 1) Returns the path to a file, first checking the existence of the
 * 2) supplied file.  If it's not found, the candidate path is checked.

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:*\mkeyblank:+(graph:+)} $filetext(conf) junk keyfile regexp -nocase {\mcablank:+(graph:+)}  	 	      $filetext(conf) junk cafile regexp -nocase {\mcertblank:+(graph:+)} 		     $filetext(conf) junk certfile regexp -nocase {\mtls-authblank:+(graph:+)} 	     $filetext(conf) junk takeyfile regexp -nocase {\mauth-user-passblank:+(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 {(\mcablank:+)(graph:*/)([^/]+)}  $target_config {\1\3} target_config regsub {(\mcertblank:+)(graph:*/)([^/]+)} $target_config {\1\3} target_config regsub {(\mkeyblank:+)(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 }   } }

Top