2015-06-07 00:15:03 +03:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
|
|
|
# To use python v2.7 change the first line to:
|
|
|
|
#!/usr/bin/env python
|
|
|
|
|
|
|
|
# Copyright (C) 2015 Bernhard Ehlers
|
|
|
|
#
|
|
|
|
# This program is free software: you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU General Public License as published by
|
|
|
|
# the Free Software Foundation, either version 2 of the License, or
|
|
|
|
# (at your option) any later version.
|
|
|
|
#
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU General Public License
|
|
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
"""
|
|
|
|
iou_import imports startup/private configuration into IOU NVRAM file.
|
|
|
|
|
|
|
|
usage: iou_import [-h] [-c size] NVRAM startup-config [private-config]
|
|
|
|
|
|
|
|
positional arguments:
|
|
|
|
NVRAM NVRAM file
|
|
|
|
startup-config startup configuration
|
|
|
|
private-config private configuration
|
|
|
|
|
|
|
|
optional arguments:
|
|
|
|
-h, --help show this help message and exit
|
|
|
|
-c size, --create size
|
|
|
|
create NVRAM file, size in kByte
|
|
|
|
"""
|
|
|
|
|
2020-07-17 15:07:25 +03:00
|
|
|
import struct
|
2015-06-07 00:15:03 +03:00
|
|
|
|
|
|
|
|
|
|
|
# calculate padding
|
2020-07-17 15:07:25 +03:00
|
|
|
def padding(length, start_address):
|
2021-04-13 12:16:50 +03:00
|
|
|
pad = -length % 4 # padding to alignment of 4
|
2020-07-17 15:07:25 +03:00
|
|
|
# extra padding if pad != 0 and big start_address
|
|
|
|
if pad != 0 and (start_address & 0x80000000) != 0:
|
2015-06-07 00:15:03 +03:00
|
|
|
pad += 4
|
|
|
|
return pad
|
|
|
|
|
|
|
|
|
|
|
|
# update checksum
|
|
|
|
def checksum(data, start, end):
|
|
|
|
chk = 0
|
2020-07-17 15:07:25 +03:00
|
|
|
# calculate checksum of first two words
|
2021-04-13 12:16:50 +03:00
|
|
|
for word in struct.unpack_from(">2H", data, start):
|
2020-07-17 15:07:25 +03:00
|
|
|
chk += word
|
|
|
|
|
|
|
|
# add remaining words, ignoring old checksum at offset 4
|
2021-04-13 12:16:50 +03:00
|
|
|
struct_format = f">{(end - start - 6) // 2:d}H"
|
|
|
|
for word in struct.unpack_from(struct_format, data, start + 6):
|
2020-07-17 15:07:25 +03:00
|
|
|
chk += word
|
2015-06-07 00:15:03 +03:00
|
|
|
|
2020-07-17 15:07:25 +03:00
|
|
|
# handle 16 bit overflow
|
2015-06-07 00:15:03 +03:00
|
|
|
while chk >> 16:
|
2021-04-13 12:16:50 +03:00
|
|
|
chk = (chk & 0xFFFF) + (chk >> 16)
|
|
|
|
chk = chk ^ 0xFFFF
|
2020-07-17 15:07:25 +03:00
|
|
|
|
|
|
|
# save checksum
|
2021-04-13 12:16:50 +03:00
|
|
|
struct.pack_into(">H", data, start + 4, chk)
|
2015-06-07 00:15:03 +03:00
|
|
|
|
|
|
|
|
|
|
|
# import IOU NVRAM
|
2020-07-17 15:07:25 +03:00
|
|
|
# NVRAM format: https://github.com/ehlers/IOUtools/blob/master/NVRAM.md
|
2015-06-07 00:15:03 +03:00
|
|
|
def nvram_import(nvram, startup, private, size):
|
2021-04-13 12:16:50 +03:00
|
|
|
DEFAULT_IOS = 0x0F04 # IOS 15.4
|
2020-07-17 15:07:25 +03:00
|
|
|
base_address = 0x10000000
|
2015-06-07 00:15:03 +03:00
|
|
|
|
2015-06-08 19:07:54 +03:00
|
|
|
# check size parameter
|
|
|
|
if size is not None and (size < 8 or size > 1024):
|
2021-04-13 12:16:50 +03:00
|
|
|
raise ValueError("invalid size")
|
2015-06-08 19:07:54 +03:00
|
|
|
|
|
|
|
# create new nvram if nvram is empty or has wrong size
|
2021-04-13 12:16:50 +03:00
|
|
|
if nvram is None or (size is not None and len(nvram) != size * 1024):
|
|
|
|
nvram = bytearray([0] * (size * 1024))
|
2015-06-07 00:15:03 +03:00
|
|
|
else:
|
2015-06-08 19:07:54 +03:00
|
|
|
nvram = bytearray(nvram)
|
2015-06-07 00:15:03 +03:00
|
|
|
|
|
|
|
# check nvram size
|
|
|
|
nvram_len = len(nvram)
|
2021-04-13 12:16:50 +03:00
|
|
|
if nvram_len < 8 * 1024 or nvram_len > 1024 * 1024 or nvram_len % 1024 != 0:
|
|
|
|
raise ValueError("invalid NVRAM length")
|
2015-06-07 00:15:03 +03:00
|
|
|
nvram_len = nvram_len // 2
|
|
|
|
|
|
|
|
# get size of current config
|
|
|
|
config_len = 0
|
|
|
|
try:
|
2021-04-13 12:16:50 +03:00
|
|
|
(magic, _, _, ios, start_addr, _, length, _, _, _, _, _) = struct.unpack_from(">HHHHIIIIIHHI", nvram, offset=0)
|
2020-07-17 15:07:25 +03:00
|
|
|
if magic == 0xABCD:
|
|
|
|
base_address = start_addr - 36
|
|
|
|
config_len = 36 + length + padding(length, base_address)
|
2021-04-13 12:16:50 +03:00
|
|
|
(magic, _, _, _, length) = struct.unpack_from(">HHIII", nvram, offset=config_len)
|
2020-07-17 15:07:25 +03:00
|
|
|
if magic == 0xFEDC:
|
|
|
|
config_len += 16 + length
|
|
|
|
else:
|
|
|
|
ios = None
|
|
|
|
except struct.error:
|
2021-04-13 12:16:50 +03:00
|
|
|
raise ValueError("unknown nvram format")
|
2015-06-07 00:15:03 +03:00
|
|
|
if config_len > nvram_len:
|
2021-04-13 12:16:50 +03:00
|
|
|
raise ValueError("unknown nvram format")
|
2015-06-07 00:15:03 +03:00
|
|
|
|
|
|
|
# calculate max. config size
|
2021-04-13 12:16:50 +03:00
|
|
|
max_config = nvram_len - 2 * 1024 # reserve 2k for files
|
2015-06-07 00:15:03 +03:00
|
|
|
idx = max_config
|
2015-06-08 19:07:54 +03:00
|
|
|
empty_sector = bytearray([0] * 1024)
|
2015-06-07 00:15:03 +03:00
|
|
|
while True:
|
|
|
|
idx -= 1024
|
|
|
|
if idx < config_len:
|
|
|
|
break
|
|
|
|
# if valid file header:
|
2021-04-13 12:16:50 +03:00
|
|
|
(magic, _, flags, length, _) = struct.unpack_from(">HHHH24s", nvram, offset=idx)
|
2020-07-17 15:07:25 +03:00
|
|
|
if magic == 0xDCBA and flags < 8 and length <= 992:
|
2015-06-07 00:15:03 +03:00
|
|
|
max_config = idx
|
2021-04-13 12:16:50 +03:00
|
|
|
elif nvram[idx : idx + 1024] != empty_sector:
|
2015-06-07 00:15:03 +03:00
|
|
|
break
|
|
|
|
|
|
|
|
# import startup config
|
2020-07-17 15:07:25 +03:00
|
|
|
new_nvram = bytearray()
|
2015-06-07 00:15:03 +03:00
|
|
|
if ios is None:
|
|
|
|
# Target IOS version is unknown. As some IOU don't work nicely with
|
|
|
|
# the padding of a different version, the startup config is padded
|
|
|
|
# with '\n' to the alignment of 4.
|
|
|
|
ios = DEFAULT_IOS
|
2021-04-13 12:16:50 +03:00
|
|
|
startup += b"\n" * (-len(startup) % 4)
|
|
|
|
new_nvram.extend(
|
|
|
|
struct.pack(
|
|
|
|
">HHHHIIIIIHHI",
|
|
|
|
0xABCD, # magic
|
|
|
|
1, # raw data
|
|
|
|
0, # checksum, not yet calculated
|
|
|
|
ios, # IOS version
|
|
|
|
base_address + 36, # start address
|
|
|
|
base_address + 36 + len(startup), # end address
|
|
|
|
len(startup), # length
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
)
|
|
|
|
)
|
2015-06-07 00:15:03 +03:00
|
|
|
new_nvram.extend(startup)
|
2020-07-17 15:07:25 +03:00
|
|
|
new_nvram.extend([0] * padding(len(new_nvram), base_address))
|
2015-06-07 00:15:03 +03:00
|
|
|
|
|
|
|
# import private config
|
|
|
|
if private is None:
|
2021-04-13 12:16:50 +03:00
|
|
|
private = b""
|
2015-06-07 00:15:03 +03:00
|
|
|
offset = len(new_nvram)
|
2021-04-13 12:16:50 +03:00
|
|
|
new_nvram.extend(
|
|
|
|
struct.pack(
|
|
|
|
">HHIII",
|
|
|
|
0xFEDC, # magic
|
|
|
|
1, # raw data
|
|
|
|
base_address + offset + 16, # start address
|
|
|
|
base_address + offset + 16 + len(private), # end address
|
|
|
|
len(private),
|
|
|
|
)
|
|
|
|
) # length
|
2015-06-07 00:15:03 +03:00
|
|
|
new_nvram.extend(private)
|
|
|
|
|
|
|
|
# add rest
|
|
|
|
if len(new_nvram) > max_config:
|
2021-04-13 12:16:50 +03:00
|
|
|
raise ValueError("NVRAM size too small")
|
2015-06-07 00:15:03 +03:00
|
|
|
new_nvram.extend([0] * (max_config - len(new_nvram)))
|
|
|
|
new_nvram.extend(nvram[max_config:])
|
|
|
|
|
|
|
|
checksum(new_nvram, 0, nvram_len)
|
|
|
|
|
2015-06-08 19:07:54 +03:00
|
|
|
return new_nvram
|
2015-06-07 00:15:03 +03:00
|
|
|
|
|
|
|
|
2021-04-13 12:16:50 +03:00
|
|
|
if __name__ == "__main__":
|
2015-06-07 00:15:03 +03:00
|
|
|
# Main program
|
2020-07-17 15:07:25 +03:00
|
|
|
import argparse
|
|
|
|
import sys
|
2015-06-07 00:15:03 +03:00
|
|
|
|
|
|
|
def check_size(string):
|
|
|
|
try:
|
|
|
|
value = int(string)
|
|
|
|
except ValueError:
|
2021-04-13 12:16:50 +03:00
|
|
|
raise argparse.ArgumentTypeError("invalid int value: " + string)
|
2015-06-07 00:15:03 +03:00
|
|
|
if value < 8 or value > 1024:
|
2021-04-13 12:16:50 +03:00
|
|
|
raise argparse.ArgumentTypeError("size must be 8..1024")
|
2015-06-07 00:15:03 +03:00
|
|
|
return value
|
|
|
|
|
2021-04-13 12:16:50 +03:00
|
|
|
parser = argparse.ArgumentParser(description="%(prog)s imports startup/private configuration into IOU NVRAM file.")
|
|
|
|
parser.add_argument("-c", "--create", metavar="size", type=check_size, help="create NVRAM file, size in kByte")
|
|
|
|
parser.add_argument("nvram", metavar="NVRAM", help="NVRAM file")
|
|
|
|
parser.add_argument("startup", metavar="startup-config", help="startup configuration")
|
|
|
|
parser.add_argument("private", metavar="private-config", nargs="?", help="private configuration")
|
2015-06-07 00:15:03 +03:00
|
|
|
args = parser.parse_args()
|
|
|
|
|
|
|
|
try:
|
|
|
|
if args.create is None:
|
2021-04-13 12:16:50 +03:00
|
|
|
fd = open(args.nvram, "rb")
|
2015-06-07 00:15:03 +03:00
|
|
|
nvram = fd.read()
|
|
|
|
fd.close()
|
|
|
|
else:
|
|
|
|
nvram = None
|
2021-04-13 12:16:50 +03:00
|
|
|
fd = open(args.startup, "rb")
|
2015-06-07 00:15:03 +03:00
|
|
|
startup = fd.read()
|
|
|
|
fd.close()
|
|
|
|
if args.private is None:
|
|
|
|
private = None
|
|
|
|
else:
|
2021-04-13 12:16:50 +03:00
|
|
|
fd = open(args.private, "rb")
|
2015-06-07 00:15:03 +03:00
|
|
|
private = fd.read()
|
|
|
|
fd.close()
|
2021-04-13 12:07:58 +03:00
|
|
|
except OSError as err:
|
|
|
|
sys.stderr.write(f"Error reading file: {err}\n")
|
2015-06-07 00:15:03 +03:00
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
try:
|
|
|
|
nvram = nvram_import(nvram, startup, private, args.create)
|
|
|
|
except ValueError as err:
|
2021-04-13 12:07:58 +03:00
|
|
|
sys.stderr.write(f"nvram_import: {err}\n")
|
2015-06-07 00:15:03 +03:00
|
|
|
sys.exit(3)
|
|
|
|
|
|
|
|
try:
|
2021-04-13 12:16:50 +03:00
|
|
|
fd = open(args.nvram, "wb")
|
2015-06-07 00:15:03 +03:00
|
|
|
fd.write(nvram)
|
|
|
|
fd.close()
|
2021-04-13 12:07:58 +03:00
|
|
|
except OSError as err:
|
|
|
|
sys.stderr.write(f"Error writing file: {err}\n")
|
2015-06-07 00:15:03 +03:00
|
|
|
sys.exit(1)
|