OpenWrt – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | #!/usr/bin/env python |
2 | # |
||
3 | # Copyright 2008, 2009 (C) Jose Vasconcellos <jvasco@verizon.net> |
||
4 | # |
||
5 | # A script that can communicate with jungo-based routers |
||
6 | # (such as MI424-WR, USR8200 and WRV54G) to backup the installed |
||
7 | # firmware and replace the boot loader. |
||
8 | # |
||
9 | # Tested with Python 2.5 on Linux and Windows |
||
10 | # |
||
11 | """Usage: %s [options] <IP_address> [image.bin | url] |
||
12 | Valid options: |
||
13 | \t-h | --help: usage statement |
||
14 | \t-d | --dump: create a flash dump |
||
15 | \t-f | --file: use <filename> to store dump contents |
||
16 | \t-u | --user: provide username (default admin) |
||
17 | \t-p | --pass: provide password (default password1) |
||
18 | \t --port: set port for http (default 8080) |
||
19 | \t-q | --quiet: don't display unnecessary information |
||
20 | \t-r | --reboot: reboot target on successful transfer |
||
21 | \t-V | --version: display version information |
||
22 | |||
23 | If no image (or url) is given, a flash dump is created. |
||
24 | A built-in http server is used when an image file is provided. |
||
25 | """ |
||
26 | |||
27 | import os |
||
28 | import sys |
||
29 | import getopt |
||
30 | import getpass |
||
31 | import telnetlib |
||
32 | import string |
||
33 | import binascii |
||
34 | import socket |
||
35 | import thread |
||
36 | import SocketServer |
||
37 | import SimpleHTTPServer |
||
38 | |||
39 | reboot = 0 |
||
40 | HOST = "192.168.1.1" |
||
41 | PORT = 8080 |
||
42 | user = "admin" |
||
43 | #password = getpass.getpass() |
||
44 | password = "password1" |
||
45 | proto = "http" |
||
46 | url = "" |
||
47 | imagefile = "" |
||
48 | dumpfile = "" |
||
49 | verbose = 1 |
||
50 | do_dump = 0 |
||
51 | dumplen = 0x10000 |
||
52 | flashsize=4*1024*1024 |
||
53 | #device="br0" |
||
54 | device="ixp0" |
||
55 | |||
56 | #################### |
||
57 | |||
58 | def start_server(server): |
||
59 | httpd = SocketServer.TCPServer((server,PORT),SimpleHTTPServer.SimpleHTTPRequestHandler) |
||
60 | thread.start_new_thread(httpd.serve_forever,()) |
||
61 | |||
62 | #################### |
||
63 | |||
64 | def get_flash_size(): |
||
65 | # make sure we don't have an A0 stepping |
||
66 | tn.write("cat /proc/cpuinfo\n") |
||
67 | buf = tn.read_until("Returned 0", 3) |
||
68 | if not buf: |
||
69 | print "Unable to obtain CPU information; make sure to not use A0 stepping!" |
||
70 | elif buf.find('rev 0') > 0: |
||
71 | print "Warning: IXP42x stepping A0 detected!" |
||
72 | if imagefile or url: |
||
73 | print "Error: No linux support for A0 stepping!" |
||
74 | sys.exit(2) |
||
75 | |||
76 | # now get flash size |
||
77 | tn.write("cat /proc/mtd\n") |
||
78 | buf = tn.read_until("Returned 0", 3) |
||
79 | if buf: |
||
80 | i = buf.find('mtd0:') |
||
81 | if i > 0: |
||
82 | return int(buf[i+6:].split()[0],16) |
||
83 | # use different command |
||
84 | tn.write("flash_layout\n") |
||
85 | buf = tn.read_until("Returned 0", 3) |
||
86 | i = buf.rfind('Range ') |
||
87 | if i > 0: |
||
88 | return int(buf[i+17:].split()[0],16) |
||
89 | print "Can't determine flash size!" |
||
90 | else: |
||
91 | print "Unable to obtain flash size!" |
||
92 | sys.exit(2) |
||
93 | |||
94 | def image_dump(tn, dumpfile): |
||
95 | if not dumpfile: |
||
96 | tn.write("ver\n"); |
||
97 | buf = tn.read_until("Returned 0",2) |
||
98 | i = buf.find("Platform:") |
||
99 | if i < 0: |
||
100 | platform="jungo" |
||
101 | else: |
||
102 | line=buf[i+9:] |
||
103 | i=line.find('\n') |
||
104 | platform=line[:i].split()[-1] |
||
105 | |||
106 | tn.write("rg_conf_print /dev/%s/mac\n" % device); |
||
107 | buf = tn.read_until("Returned 0",3) |
||
108 | |||
109 | i = buf.find("mac(") |
||
110 | if i > 0: |
||
111 | i += 4 |
||
112 | else: |
||
113 | print "No MAC address found! (use -f option)" |
||
114 | sys.exit(1) |
||
115 | dumpfile = "%s-%s.bin" % (platform, buf[i:i+17].replace(':','')) |
||
116 | else: |
||
117 | tn.write("\n") |
||
118 | |||
119 | print "Dumping flash contents (%dMB) to %s" % (flashsize/1048576, dumpfile) |
||
120 | f = open(dumpfile, "wb") |
||
121 | |||
122 | t=flashsize/dumplen |
||
123 | for addr in range(t): |
||
124 | if verbose: |
||
125 | sys.stdout.write('\r%d%%'%(100*addr/t)) |
||
126 | sys.stdout.flush() |
||
127 | |||
128 | tn.write("flash_dump -r 0x%x -l %d -4\n" % (addr*dumplen, dumplen)) |
||
129 | tn.read_until("\n") |
||
130 | |||
131 | count = addr*dumplen |
||
132 | while 1: |
||
133 | buf = tn.read_until("\n") |
||
134 | if buf.strip() == "Returned 0": |
||
135 | break |
||
136 | s = buf.split() |
||
137 | if s and s[0][-1] == ':': |
||
138 | a=int(s[0][:-1],16) |
||
139 | if a != count: |
||
140 | print "Format error: %x != %x"%(a,count) |
||
141 | sys.exit(2) |
||
142 | count += 16 |
||
143 | f.write(binascii.a2b_hex(string.join(s[1:],''))) |
||
144 | tn.read_until(">",1) |
||
145 | |||
146 | f.close() |
||
147 | if verbose: |
||
148 | print "" |
||
149 | |||
150 | def telnet_option(sock,cmd,option): |
||
151 | #print "Option: %d %d" % (ord(cmd), ord(option)) |
||
152 | if cmd == telnetlib.DO: |
||
153 | c=telnetlib.WILL |
||
154 | elif cmd == telnetlib.WILL: |
||
155 | c=telnetlib.DO |
||
156 | sock.sendall(telnetlib.IAC + c + option) |
||
157 | |||
158 | def telnet_timeout(): |
||
159 | print "Fatal error: telnet timeout!" |
||
160 | sys.exit(1) |
||
161 | |||
162 | def usage(): |
||
163 | print __doc__ % os.path.basename(sys.argv[0]) |
||
164 | |||
165 | #################### |
||
166 | |||
167 | try: |
||
168 | opts, args = getopt.getopt(sys.argv[1:], "hdf:qp:P:rvV", \ |
||
169 | ["help", "dump", "file=", "user=", "pass=", "port=", |
||
170 | "quiet=", "reboot", "verbose", "version"]) |
||
171 | except getopt.GetoptError: |
||
172 | # print help information and exit: |
||
173 | usage() |
||
174 | sys.exit(1) |
||
175 | |||
176 | for o, a in opts: |
||
177 | if o in ("-h", "--help"): |
||
178 | usage() |
||
179 | sys.exit(1) |
||
180 | elif o in ("-V", "--version"): |
||
181 | print "%s: 0.11" % sys.argv[0] |
||
182 | sys.exit(1) |
||
183 | elif o in ("-d", "--no-dump"): |
||
184 | do_dump = 1 |
||
185 | elif o in ("-f", "--file"): |
||
186 | dumpfile = a |
||
187 | elif o in ("-u", "--user"): |
||
188 | user = a |
||
189 | elif o in ("-p", "--pass"): |
||
190 | password = a |
||
191 | elif o == "--port": |
||
192 | PORT = int(a) |
||
193 | elif o in ("-q", "--quiet"): |
||
194 | verbose = 0 |
||
195 | elif o in ("-r", "--reboot"): |
||
196 | reboot = 1 |
||
197 | elif o in ("-v", "--verbose"): |
||
198 | verbose = 1 |
||
199 | |||
200 | # make sure we have enough arguments |
||
201 | if len(args) > 0: |
||
202 | HOST = args[0] |
||
203 | |||
204 | if len(args) == 2: |
||
205 | if args[1].split(':')[0] in ("tftp", "http", "ftp"): |
||
206 | url = args[1] |
||
207 | else: |
||
208 | imagefile = args[1] |
||
209 | else: |
||
210 | do_dump = 1; |
||
211 | |||
212 | #################### |
||
213 | # create a telnet session to the router |
||
214 | try: |
||
215 | tn = telnetlib.Telnet(HOST) |
||
216 | except socket.error, msg: |
||
217 | print "Unable to establish telnet session to %s: %s" % (HOST, msg) |
||
218 | sys.exit(1) |
||
219 | |||
220 | tn.set_option_negotiation_callback(telnet_option) |
||
221 | |||
222 | buf = tn.read_until("Username: ", 3) |
||
223 | if not buf: |
||
224 | telnet_timeout() |
||
225 | tn.write(user+"\n") |
||
226 | if password: |
||
227 | buf = tn.read_until("Password: ", 3) |
||
228 | if not buf: |
||
229 | telnet_timeout() |
||
230 | tn.write(password+"\n") |
||
231 | |||
232 | # wait for prompt |
||
233 | buf = tn.read_until("> ", 3) |
||
234 | if not buf: |
||
235 | telnet_timeout() |
||
236 | |||
237 | flashsize = get_flash_size() |
||
238 | |||
239 | if do_dump: |
||
240 | image_dump(tn, dumpfile) |
||
241 | |||
242 | if imagefile or url: |
||
243 | splitpath = os.path.split(imagefile) |
||
244 | |||
245 | # create load command |
||
246 | if url: |
||
247 | cmd = "load -u %s -r 0\n" % (url) |
||
248 | else: |
||
249 | server = tn.get_socket().getsockname()[0] |
||
250 | cmd = "load -u http://%s:%d/%s -r 0\n" % (server, PORT, splitpath[1]) |
||
251 | |||
252 | if not os.access(imagefile, os.R_OK): |
||
253 | print "File access error: %s" % (imagefile) |
||
254 | sys.exit(3) |
||
255 | |||
256 | # make sure we're in the directory where the image is located |
||
257 | if splitpath[0]: |
||
258 | os.chdir(splitpath[0]) |
||
259 | |||
260 | start_server(server) |
||
261 | |||
262 | if verbose: |
||
263 | print "Unlocking flash..." |
||
264 | tn.write("unlock 0 0x%x\n" % flashsize) |
||
265 | buf = tn.read_until("Returned 0",5) |
||
266 | |||
267 | if verbose: |
||
268 | print "Writing new image..." |
||
269 | print cmd, |
||
270 | tn.write(cmd) |
||
271 | buf = tn.read_until("Returned 0",10) |
||
272 | |||
273 | # wait till the transfer completed |
||
274 | buf = tn.read_until("Download completed successfully",20) |
||
275 | if buf: |
||
276 | print "Flash update complete!" |
||
277 | if reboot: |
||
278 | tn.write("reboot\n") |
||
279 | print "Rebooting..." |
||
280 | |||
281 | tn.write("exit\n") |
||
282 | tn.close() |
||
283 |