Skip to content

Instantly share code, notes, and snippets.

@HON95
Created October 14, 2021 19:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save HON95/ddfd1456d7e9a982afa90031ce2fe118 to your computer and use it in GitHub Desktop.
Save HON95/ddfd1456d7e9a982afa90031ce2fe118 to your computer and use it in GitHub Desktop.
#!/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