-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathopenwrt_ctl.py
executable file
·366 lines (320 loc) · 12.2 KB
/
openwrt_ctl.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
#!/usr/bin/python3
'''
make sure pexpect is installed:
$ sudo yum install python3-pexpect
You might need to install pexpect-serial using pip:
$ pip3 install pexpect-serial
./openwrt_ctl.py -l stdout -u root -p TIP -s serial --tty ttyUSB0
# Set up reverse ssh tunnel
./openwrt_ctl.py --tty /dev/ttyAP1 --action ssh-tunnel \
--value "ssh -y -y -f -N -T -M -R 9999:localhost:22 [email protected]" \
--value2 password-for-10.28.3.100 --log stdout --scheme serial --prompt root@Open
'''
import sys
if sys.version_info[0] != 3:
print("This script requires Python 3")
exit()
import re
import logging
import time
from time import sleep
import pprint
import telnetlib
import argparse
import pexpect
default_host = "localhost"
default_ports = {
"serial": None,
"ssh": 22,
"telnet": 23
}
NL = "\n"
CR = "\r\n"
Q = '"'
A = "'"
FORMAT = '%(asctime)s %(name)s %(levelname)s: %(message)s'
prompt = "root@OpenWrt:"
def usage():
print("$0 used connect to OpenWrt AP or similar Linux machine:")
print("-d|--dest IP address of the OpenWrt AP, for ssh/telnet scheme")
print("-o|--port IP port of the OpenWrt AP, for ssh/telnet scheme")
print("-t|--tty Serial port, if using serial scheme")
print("-u|--user login name")
print("-p|--pass password")
print("--prompt Prompt to look for when commands are done (default: root@OpenWrt)")
print("-s|--scheme (serial|telnet|ssh): connect via serial, ssh or telnet")
print("-l|--log file log messages here")
print("--action (logread | journalctl | lurk | sysupgrade | download | upload | reboot | cmd | ssh-tunnel")
print("--value (option to help complete the action")
print("--value2 (option to help complete the action, dest filename for download, passwd for ssh-tunnel")
print("-h|--help")
# see https://stackoverflow.com/a/13306095/11014343
class FileAdapter(object):
def __init__(self, logger):
self.logger = logger
def write(self, data):
# NOTE: data can be a partial line, multiple lines
data = data.strip() # ignore leading/trailing whitespace
if data: # non-blank
self.logger.info(data)
def flush(self):
pass # leave it to logging to flush properly
def main():
global prompt
parser = argparse.ArgumentParser(description="OpenWrt AP Control Script")
parser.add_argument("-d", "--dest", type=str, help="address of the cisco controller")
parser.add_argument("-o", "--port", type=int, help="control port on the controller")
parser.add_argument("-u", "--user", type=str, help="credential login/username")
parser.add_argument("-p", "--passwd", type=str, help="credential password")
parser.add_argument("-P", "--prompt", type=str, help="Prompt to look for")
parser.add_argument("-s", "--scheme", type=str, choices=["serial", "ssh", "telnet"], help="Connect via serial, ssh or telnet")
parser.add_argument("-t", "--tty", type=str, help="tty serial device")
parser.add_argument("-l", "--log", type=str, help="logfile for messages, stdout means output to console")
parser.add_argument("--action", type=str, help="perform action",
choices=["logread", "journalctl", "lurk", "sysupgrade", "sysupgrade-n", "download", "upload", "reboot", "cmd", "ssh-tunnel" ])
parser.add_argument("--value", type=str, help="set value")
parser.add_argument("--value2", type=str, help="set value2")
tty = None
args = None
try:
args = parser.parse_args()
host = args.dest
scheme = args.scheme
port = args.port
#port = (default_ports[scheme], args.port)[args.port != None]
user = args.user
passwd = args.passwd
logfile = args.log
tty = args.tty;
if (args.prompt != None):
prompt = args.prompt
filehandler = None
except Exception as e:
logging.exception(e);
usage()
exit(2);
console_handler = logging.StreamHandler()
formatter = logging.Formatter(FORMAT)
logg = logging.getLogger(__name__)
logg.setLevel(logging.DEBUG)
file_handler = None
if (logfile is not None):
if (logfile != "stdout"):
file_handler = logging.FileHandler(logfile, "w")
file_handler.setLevel(logging.DEBUG)
file_handler.setFormatter(formatter)
logg.addHandler(file_handler)
logging.basicConfig(format=FORMAT, handlers=[file_handler])
else:
# stdout logging
logging.basicConfig(format=FORMAT, handlers=[console_handler])
CCPROMPT=prompt
ser = None
egg = None # think "eggpect"
try:
if (scheme == "serial"):
#eggspect = pexpect.fdpexpect.fdspan(telcon, logfile=sys.stdout.buffer)
import serial
from pexpect_serial import SerialSpawn
ser = serial.Serial(tty, 115200, timeout=5)
egg = SerialSpawn(ser);
egg.logfile = FileAdapter(logg)
egg.sendline(NL)
has_reset = False
try:
logg.info("prompt: %s user: %s passwd: %s"%(prompt, user, passwd))
while True:
i = egg.expect([prompt, "Please press Enter to activate", "login:", "Password:", "IPQ6018#"], timeout=3)
logg.info("expect-0: %i"%(i))
if (i == 0):
logg.info("Found prompt, login complete.")
break
if (i == 1):
logg.info("Sending newline")
egg.setdline(NL)
if (i == 2):
logg.info("Sending username: %s"%(user))
egg.sendline(user)
if (i == 3):
logg.info("Sending password: %s"%(passwd))
egg.sendline(passwd)
if (i == 4): # in bootloader
if has_reset:
logg.info("ERROR: Have reset once already, back in bootloader?")
sys.exit(1)
has_reset = True
logg.info("In boot loader, will reset and sleep 30 seconds")
egg.sendline("reset")
time.sleep(30)
egg.sendline(NL)
except Exception as e:
# maybe something like 'logread -f' is running?
# send ctrl-c
egg.send(chr(3))
elif (scheme == "ssh"):
# Not implemented/tested currently. --Ben
if (port is None):
port = 22
cmd = "ssh -p%d %s@%s"%(port, user, host)
logg.info("Spawn: "+cmd+NL)
egg = pexpect.spawn(cmd)
#egg.logfile_read = sys.stdout.buffer
egg.logfile = FileAdapter(logg)
i = egg.expect(["password:", "continue connecting (yes/no)?"], timeout=3)
time.sleep(0.1)
if i == 1:
egg.sendline('yes')
egg.expect('password:')
sleep(0.1)
egg.sendline(passwd)
elif (scheme == "telnet"):
# Not implemented/tested currently. --Ben
if (port is None):
port = 23
cmd = "telnet %s %d"%(host, port)
logg.info("Spawn: "+cmd+NL)
egg = pexpect.spawn(cmd)
egg.logfile = FileAdapter(logg)
time.sleep(0.1)
egg.sendline(' ')
egg.expect('User\:')
egg.sendline(user)
egg.expect('Password\:')
egg.sendline(passwd)
egg.sendline('config paging disable')
else:
usage()
exit(1)
except Exception as e:
logging.exception(e);
command = None
CLOSEDBYREMOTE = "closed by remote host."
CLOSEDCX = "Connection to .* closed."
try:
egg.expect(CCPROMPT)
except Exception as e:
egg.sendline(NL)
TO=10
wait_forever = False
# Clean pending output
egg.sendline("echo __hello__")
egg.expect("__hello__")
egg.expect(CCPROMPT)
logg.info("Action[%s] Value[%s] Value2[%s]"%(args.action, args.value, args.value2))
if (args.action == "reboot"):
command = "reboot"
TO=60
if (args.action == "cmd"):
if (args.value is None):
raise Exception("cmd requires value to be set.")
command = "%s"%(args.value)
if (args.action == "logread"):
command = "logread -f"
TO=1
wait_forever = True
if (args.action == "journalctl"):
command = "journalctl -f"
TO=1
wait_forever = True
if (args.action == "lurk"):
command = "date"
TO=1
wait_forever = True
if (args.action == "ssh-tunnel"):
command = "%s"%(args.value)
passwd = "%s"%(args.value2)
logg.info("Command[%s]"%command)
egg.sendline(command);
i = egg.expect(["password:", "Do you want to continue connecting"], timeout=5)
if i == 1:
egg.sendline("y")
egg.expect("password:", timeout=5)
egg.sendline(passwd)
egg.expect(CCPROMPT, timeout=20)
return
if ((args.action == "sysupgrade") or (args.action == "sysupgrade-n")):
command = "scp %s /tmp/new_img.bin"%(args.value)
logg.info("Command[%s]"%command)
egg.sendline(command);
i = egg.expect(["password:", "Do you want to continue connecting"], timeout=5)
if i == 1:
egg.sendline("y")
egg.expect("password:", timeout=5)
egg.sendline("lanforge")
egg.expect(CCPROMPT, timeout=20)
if (args.action == "sysupgrade-n"):
egg.sendline("sysupgrade -n /tmp/new_img.bin")
else:
egg.sendline("sysupgrade /tmp/new_img.bin")
egg.expect("link becomes ready", timeout=100)
return
if (args.action == "download"):
command = "scp %s /tmp/%s"%(args.value, args.value2)
logg.info("Command[%s]"%command)
egg.sendline(command);
i = egg.expect(["password:", "Do you want to continue connecting", "Network unreachable"], timeout=5)
if i == 2:
print("Network unreachable, wait 15 seconds and try again.")
time.sleep(15)
command = "scp %s /tmp/%s"%(args.value, args.value2)
logg.info("Command[%s]"%command)
egg.sendline(command);
i = egg.expect(["password:", "Do you want to continue connecting", "Network unreachable"], timeout=5)
if i == 2:
print("ERROR: Could not connect to LANforge to get download file")
exit(2)
if i == 1:
egg.sendline("y")
egg.expect("password:", timeout=5)
egg.sendline("lanforge")
egg.expect(CCPROMPT, timeout=20)
return
if (args.action == "upload"):
command = "scp %s %s"%(args.value, args.value2)
logg.info("Command[%s]"%command)
egg.sendline(command);
i = egg.expect(["password:", "Do you want to continue connecting", "Network unreachable"], timeout=5)
if i == 2:
print("Network unreachable, wait 15 seconds and try again.")
time.sleep(15)
command = "scp /tmp/%s %s"%(args.value, args.value2)
logg.info("Command[%s]"%command)
egg.sendline(command);
i = egg.expect(["password:", "Do you want to continue connecting", "Network unreachable"], timeout=5)
if i == 2:
print("ERROR: Could not connect to LANforge to put upload file")
exit(2)
if i == 1:
egg.sendline("y")
egg.expect("password:", timeout=5)
egg.sendline("lanforge")
egg.expect(CCPROMPT, timeout=20)
return
if (command is None):
logg.info("No command specified, going to log out.")
else:
logg.info("Command[%s]"%command)
egg.sendline(command);
while True:
try:
i = egg.expect([CCPROMPT, "kmodloader: done loading kernel", "\n"], timeout=TO)
print (egg.before.decode('utf-8', 'ignore'))
if i == 1:
egg.sendline(' ')
egg.expect(CCPROMPT, timeout=20)
print (egg.before.decode('utf-8', 'ignore'))
if i == 2: # new line of text, just print and continue
continue
if not wait_forever:
break
except Exception as e:
# Some commands take a long time (logread -f)
if not wait_forever:
logging.exception(e)
break
# ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- -----
if __name__ == '__main__':
main()
####
####
####