Created
October 14, 2021 19:40
-
-
Save HON95/ddfd1456d7e9a982afa90031ce2fe118 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/expect -f | |
# Dependencies: expect cu | |
# Access to serial devices requires you to be in the dialout group. | |
# If you're not in tha groups, run "sudo chmod 666 /dev/ttyUSB*" first instead. | |
puts "Bootstrap script for TP-Link T2600G variants." | |
puts "" | |
if {$argc < 3 || $argc > 4} { | |
puts "Usage: ./bootstrap <dev> <model> <config> \[login-password\]" | |
puts "Example: ./bootstrap /dev/ttyUSB0 TL-SG3428 tl-sg3428-bootstrap.txt" | |
puts "The config must consist of a list of commands to enter directly into config mode." | |
exit 1 | |
} | |
# Custom vars | |
set log_path bootstrap.log | |
set hostname_regex {[a-zA-Z0-9_-]+} | |
set unpriv_exec_mode_regex "${hostname_regex}>" | |
set exec_mode_regex "${hostname_regex}#" | |
set config_mode_regex "${hostname_regex}\\(\[a-z-\]+\\)#" | |
set dev_path [lindex $argv 0] | |
set dev_baud 38400 | |
set device_username "admin" | |
set device_password "admin" | |
set device_password_tmp "password123" | |
set device_password_override [lindex $argv 3] | |
if {$device_password_override != ""} { | |
set device_password $device_password_override | |
} | |
set device_model [lindex $argv 1] | |
set config_path [lindex $argv 2] | |
set verbose 0 | |
# Reboot is generally required, disable only for debugging | |
set no_reboot 0 | |
set default_timeout 10 | |
# Special vars | |
set timeout $default_timeout | |
# Avoid typing too fast (m delay per 10 characters) | |
set send_slow {10 .01} | |
log_user 0 | |
# General abort procedure. | |
proc abort {} { | |
puts "" | |
puts "Timed out or EOF." | |
puts "Device may be left in inconsistent state and may require manual cleanup." | |
exit 1 | |
} | |
# Procedure to logout the device and exit this script. | |
proc logout_and_exit {levels} { | |
for {set i 0} {$i < $levels} {incr i} { | |
exp_send "exit\n" | |
} | |
expect -ex "User Access Login" {} default { | |
puts "Failed to log out of device." | |
} | |
exit 1 | |
} | |
puts "Reading config: $config_path" | |
set config_fd [open $config_path r] | |
set config_lines [split [read $config_fd] "\n"] | |
close $config_fd | |
set line_count 0 | |
foreach line $config_lines { | |
#puts "Line: '$line'" | |
incr line_count | |
} | |
puts "Read $line_count lines." | |
puts "Opening log file: $log_path" | |
log_file -a -noappend [file tail $log_path] | |
puts "Opening serial device: $dev_path" | |
if {![file exists "$dev_path"]} { | |
puts "Serial device does not exist." | |
exit 1 | |
} | |
spawn cu --line $dev_path --speed $dev_baud --nortscts | |
expect -timeout 1 eof { | |
puts "Failed to open serial device." | |
puts "Open in other program?" | |
exit 1 | |
} | |
puts "Finding out where we are ..." | |
# Exra newlines to clear any ongoing logins or whatever | |
exp_send "\n\n" | |
set timeout 300 | |
expect { | |
-ex "tplink>" { | |
puts "Found bootutil. Continuing boot ..." | |
exp_send "3\n" | |
expect -ex "Starting kernel" { | |
puts "Start successful." | |
puts "Going back to the start ..." | |
} default { abort } | |
exp_continue | |
} | |
-ex "User Access Login" { | |
puts "Found login prompt. Excellent!" | |
} | |
-re "$unpriv_exec_mode_regex" { | |
puts "Found unprivileged exec mode prompt." | |
puts "Exiting ..." | |
exp_send "exit\n" | |
} | |
-re "$exec_mode_regex" { | |
puts "Found privileged exec mode prompt." | |
puts "Exiting ..." | |
exp_send "exit\n" | |
exp_send "exit\n" | |
} | |
-re "$config_mode_regex" { | |
puts "Found config mode prompt." | |
puts "Exiting ..." | |
exp_send "exit\n" | |
exp_send "exit\n" | |
exp_send "exit\n" | |
# Config level unknown, so add an extra exit and some login prompt cleanup | |
exp_send "exit\n" | |
exp_send "\n" | |
} | |
default { | |
puts "Unknown device or location." | |
exit 1 | |
} | |
} | |
set timeout $default_timeout | |
# Catch up to all extras ("expect *" doesn't work) | |
while {1} { | |
expect -timeout 1 -ex "User Access Login" {} timeout { break } | |
} | |
# Make sure we're actually ate the login prompt | |
exp_send "\n" | |
expect -timeout 1 -ex "User Access Login" {} timeout { abort } | |
puts "Logging in ..." | |
set new_password_done 0 | |
expect { | |
-ex "Login invalid." { | |
# Check first since the user prompt will re-appear on failed login | |
puts "Invalid username or password." | |
puts "Device already configured?" | |
exit 1 | |
} | |
-ex "User:" { | |
exp_send "$device_username\n" | |
exp_continue | |
} | |
-ex "Password:" { | |
exp_send "$device_password\n" | |
exp_continue | |
} | |
-ex "Change now?" { | |
# Set new temporary admin password | |
exp_send "y\n" | |
exp_send "$device_password_tmp\n" | |
exp_send "$device_password_tmp\n" | |
exp_send "\n" | |
set new_password_done 1 | |
exp_continue | |
} | |
-re "$unpriv_exec_mode_regex" { | |
# Check if a new admin password was set first or if using an override password | |
if {$new_password_done == 0 && $device_password_override == ""} { | |
puts "Login didn't prompt for new password." | |
puts "Device already configured?" | |
logout_and_exit 1 | |
} | |
puts "Entering privileged exec mode ..." | |
exp_send "enable\n" | |
exp_continue | |
} | |
-re "$exec_mode_regex" { | |
# Done | |
} | |
default { abort } | |
} | |
puts "Getting device model ..." | |
exp_send "\n" | |
expect -re "(${hostname_regex})#$" {} default { abort } | |
set found_device_model "$expect_out(1,string)" | |
puts "Detected device model: '$found_device_model'" | |
if {$found_device_model != $device_model} { | |
puts "Found device model doesn't match specified model!" | |
logout_and_exit 2 | |
} | |
# Get device info for the log | |
exp_send "show system-info\n" | |
expect -re "$exec_mode_regex" {} default { abort } | |
puts "Entering config mode ..." | |
exp_send "conf\n" | |
expect -re "$config_mode_regex" {} default { abort } | |
puts "Uploading config lines (slowly) ..." | |
set line_count 0 | |
foreach line $config_lines { | |
# Skip empty lines | |
if {[regexp {^ *$} $line]} { | |
continue | |
} | |
# Skip comments | |
if {[regexp {^ *#} $line]} { | |
continue | |
} | |
# Ship it | |
if {$verbose == 1} { | |
puts "Writing line: '$line'" | |
} | |
# Send with slow mode so it catches everything | |
exp_send -s -- "$line\n" | |
# Wait for slow commands | |
set timeout 60 | |
expect { | |
-re "$config_mode_regex" { | |
# Line OK | |
} | |
-re {[Ee]rror|[Ff]ail} { | |
puts "Got error when entering command." | |
puts "Line: $line" | |
logout_and_exit 3 | |
} | |
default { | |
puts "Timed out or EOF." | |
puts "Line: $line" | |
logout_and_exit 3 | |
} | |
} | |
incr line_count | |
} | |
set timeout $default_timeout | |
puts "Wrote $line_count lines." | |
puts "Exiting config mode ..." | |
exp_send "exit\n" | |
expect -re "$exec_mode_regex" {} default { abort } | |
puts "Saving config ..." | |
exp_send "copy run start\n" | |
expect -ex "Saving user config OK!" {} default { abort } | |
expect -re "$exec_mode_regex" {} default { abort } | |
if {$no_reboot == 1} { | |
puts "WARNING: No-reboot flag set, will just log out instead." | |
puts "Logging out ..." | |
exp_send "exit\n" | |
exp_send "exit\n" | |
expect -ex "User Access Login" {} default { abort } | |
} else { | |
puts "Rebooting ..." | |
exp_send "reboot\n" | |
# Don't save (again) and conform reboot | |
exp_send "n\n" | |
exp_send "y\n" | |
expect -ex "System Rebooting" {} default { abort } | |
} | |
puts "" | |
puts "Done!" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment