| #!/usr/bin/env python3 |
| # |
| # Copyright 2008, 2009 (C) Jose Vasconcellos <jvasco@verizon.net> |
| # |
| # A script that can communicate with jungo-based routers |
| # (such as MI424-WR, USR8200 and WRV54G) to backup the installed |
| # firmware and replace the boot loader. |
| # |
| # Tested with Python 2.5 on Linux and Windows |
| # |
| """Usage: %s [options] <IP_address> [image.bin | url] |
| Valid options: |
| \t-h | --help: usage statement |
| \t-d | --dump: create a flash dump |
| \t-f | --file: use <filename> to store dump contents |
| \t-u | --user: provide username (default admin) |
| \t-p | --pass: provide password (default password1) |
| \t --port: set port for http (default 8080) |
| \t-q | --quiet: don't display unnecessary information |
| \t-r | --reboot: reboot target on successful transfer |
| \t-V | --version: display version information |
| |
| If no image (or url) is given, a flash dump is created. |
| A built-in http server is used when an image file is provided. |
| """ |
| |
| import os |
| import sys |
| import getopt |
| import getpass |
| import telnetlib |
| import string |
| import binascii |
| import socket |
| import _thread |
| import socketserver |
| import http.server |
| |
| reboot = 0 |
| HOST = "192.168.1.1" |
| PORT = 8080 |
| user = "admin" |
| #password = getpass.getpass() |
| password = "password1" |
| proto = "http" |
| url = "" |
| imagefile = "" |
| dumpfile = "" |
| verbose = 1 |
| do_dump = 0 |
| dumplen = 0x10000 |
| flashsize=4*1024*1024 |
| #device="br0" |
| device="ixp0" |
| |
| #################### |
| |
| def start_server(server): |
| httpd = socketserver.TCPServer((server,PORT),http.server.SimpleHTTPRequestHandler) |
| _thread.start_new_thread(httpd.serve_forever,()) |
| |
| #################### |
| |
| def get_flash_size(): |
| # make sure we don't have an A0 stepping |
| tn.write("cat /proc/cpuinfo\n") |
| buf = tn.read_until("Returned 0", 3) |
| if not buf: |
| print("Unable to obtain CPU information; make sure to not use A0 stepping!") |
| elif buf.find('rev 0') > 0: |
| print("Warning: IXP42x stepping A0 detected!") |
| if imagefile or url: |
| print("Error: No linux support for A0 stepping!") |
| sys.exit(2) |
| |
| # now get flash size |
| tn.write("cat /proc/mtd\n") |
| buf = tn.read_until("Returned 0", 3) |
| if buf: |
| i = buf.find('mtd0:') |
| if i > 0: |
| return int(buf[i+6:].split()[0],16) |
| # use different command |
| tn.write("flash_layout\n") |
| buf = tn.read_until("Returned 0", 3) |
| i = buf.rfind('Range ') |
| if i > 0: |
| return int(buf[i+17:].split()[0],16) |
| print("Can't determine flash size!") |
| else: |
| print("Unable to obtain flash size!") |
| sys.exit(2) |
| |
| def image_dump(tn, dumpfile): |
| if not dumpfile: |
| tn.write("ver\n"); |
| buf = tn.read_until("Returned 0",2) |
| i = buf.find("Platform:") |
| if i < 0: |
| platform="jungo" |
| else: |
| line=buf[i+9:] |
| i=line.find('\n') |
| platform=line[:i].split()[-1] |
| |
| tn.write("rg_conf_print /dev/%s/mac\n" % device); |
| buf = tn.read_until("Returned 0",3) |
| |
| i = buf.find("mac(") |
| if i > 0: |
| i += 4 |
| else: |
| print("No MAC address found! (use -f option)") |
| sys.exit(1) |
| dumpfile = "%s-%s.bin" % (platform, buf[i:i+17].replace(':','')) |
| else: |
| tn.write("\n") |
| |
| print("Dumping flash contents (%dMB) to %s" % (flashsize/1048576, dumpfile)) |
| f = open(dumpfile, "wb") |
| |
| t=flashsize/dumplen |
| for addr in range(t): |
| if verbose: |
| sys.stdout.write('\r%d%%'%(100*addr/t)) |
| sys.stdout.flush() |
| |
| tn.write("flash_dump -r 0x%x -l %d -4\n" % (addr*dumplen, dumplen)) |
| tn.read_until("\n") |
| |
| count = addr*dumplen |
| while 1: |
| buf = tn.read_until("\n") |
| if buf.strip() == "Returned 0": |
| break |
| s = buf.split() |
| if s and s[0][-1] == ':': |
| a=int(s[0][:-1],16) |
| if a != count: |
| print("Format error: %x != %x"%(a,count)) |
| sys.exit(2) |
| count += 16 |
| f.write(binascii.a2b_hex(string.join(s[1:],''))) |
| tn.read_until(">",1) |
| |
| f.close() |
| if verbose: |
| print("") |
| |
| def telnet_option(sock,cmd,option): |
| #print "Option: %d %d" % (ord(cmd), ord(option)) |
| if cmd == telnetlib.DO: |
| c=telnetlib.WILL |
| elif cmd == telnetlib.WILL: |
| c=telnetlib.DO |
| sock.sendall(telnetlib.IAC + c + option) |
| |
| def telnet_timeout(): |
| print("Fatal error: telnet timeout!") |
| sys.exit(1) |
| |
| def usage(): |
| print(__doc__ % os.path.basename(sys.argv[0])) |
| |
| #################### |
| |
| try: |
| opts, args = getopt.getopt(sys.argv[1:], "hdf:qp:P:rvV", \ |
| ["help", "dump", "file=", "user=", "pass=", "port=", |
| "quiet=", "reboot", "verbose", "version"]) |
| except getopt.GetoptError: |
| # print help information and exit: |
| usage() |
| sys.exit(1) |
| |
| for o, a in opts: |
| if o in ("-h", "--help"): |
| usage() |
| sys.exit(1) |
| elif o in ("-V", "--version"): |
| print("%s: 0.11" % sys.argv[0]) |
| sys.exit(1) |
| elif o in ("-d", "--no-dump"): |
| do_dump = 1 |
| elif o in ("-f", "--file"): |
| dumpfile = a |
| elif o in ("-u", "--user"): |
| user = a |
| elif o in ("-p", "--pass"): |
| password = a |
| elif o == "--port": |
| PORT = int(a) |
| elif o in ("-q", "--quiet"): |
| verbose = 0 |
| elif o in ("-r", "--reboot"): |
| reboot = 1 |
| elif o in ("-v", "--verbose"): |
| verbose = 1 |
| |
| # make sure we have enough arguments |
| if len(args) > 0: |
| HOST = args[0] |
| |
| if len(args) == 2: |
| if args[1].split(':')[0] in ("tftp", "http", "ftp"): |
| url = args[1] |
| else: |
| imagefile = args[1] |
| else: |
| do_dump = 1; |
| |
| #################### |
| # create a telnet session to the router |
| try: |
| tn = telnetlib.Telnet(HOST) |
| except socket.error as msg: |
| print("Unable to establish telnet session to %s: %s" % (HOST, msg)) |
| sys.exit(1) |
| |
| tn.set_option_negotiation_callback(telnet_option) |
| |
| buf = tn.read_until("Username: ", 3) |
| if not buf: |
| telnet_timeout() |
| tn.write(user+"\n") |
| if password: |
| buf = tn.read_until("Password: ", 3) |
| if not buf: |
| telnet_timeout() |
| tn.write(password+"\n") |
| |
| # wait for prompt |
| buf = tn.read_until("> ", 3) |
| if not buf: |
| telnet_timeout() |
| |
| flashsize = get_flash_size() |
| |
| if do_dump: |
| image_dump(tn, dumpfile) |
| |
| if imagefile or url: |
| splitpath = os.path.split(imagefile) |
| |
| # create load command |
| if url: |
| cmd = "load -u %s -r 0\n" % (url) |
| else: |
| server = tn.get_socket().getsockname()[0] |
| cmd = "load -u http://%s:%d/%s -r 0\n" % (server, PORT, splitpath[1]) |
| |
| if not os.access(imagefile, os.R_OK): |
| print("File access error: %s" % (imagefile)) |
| sys.exit(3) |
| |
| # make sure we're in the directory where the image is located |
| if splitpath[0]: |
| os.chdir(splitpath[0]) |
| |
| start_server(server) |
| |
| if verbose: |
| print("Unlocking flash...") |
| tn.write("unlock 0 0x%x\n" % flashsize) |
| buf = tn.read_until("Returned 0",5) |
| |
| if verbose: |
| print("Writing new image...") |
| print(cmd, end=' ') |
| tn.write(cmd) |
| buf = tn.read_until("Returned 0",10) |
| |
| # wait till the transfer completed |
| buf = tn.read_until("Download completed successfully",20) |
| if buf: |
| print("Flash update complete!") |
| if reboot: |
| tn.write("reboot\n") |
| print("Rebooting...") |
| |
| tn.write("exit\n") |
| tn.close() |
| |