summaryrefslogtreecommitdiff
path: root/cleopatre
diff options
context:
space:
mode:
authorlefranc2010-06-14 12:50:06 +0000
committerlefranc2010-06-14 12:50:06 +0000
commit43dc87c394e59e34eb5333179375acd2b6004272 (patch)
tree2a9ebefc0f0dfb486c145fc438fb77b95d933700 /cleopatre
parentffc16a2daff75f6cda4a3c8792cc5c6abfc3495c (diff)
cleo/tools/spidupd: add spidupdate python client, no refs
git-svn-id: svn+ssh://pessac/svn/cesar/trunk@7210 017c9cb6-072f-447c-8318-d5b54f68fe89
Diffstat (limited to 'cleopatre')
-rwxr-xr-xcleopatre/tools/spidupd/spidupdate.py406
1 files changed, 406 insertions, 0 deletions
diff --git a/cleopatre/tools/spidupd/spidupdate.py b/cleopatre/tools/spidupd/spidupdate.py
new file mode 100755
index 0000000000..daf5924c0d
--- /dev/null
+++ b/cleopatre/tools/spidupd/spidupdate.py
@@ -0,0 +1,406 @@
+#!/usr/bin/python
+
+import os
+import hashlib
+import sys
+import getopt
+from scapy.all import *
+from stat import *
+
+ETH_P_HPAV = 0x88e1
+OUI_SPIDCOM = 0x0013d7
+
+class Mme(Packet):
+ name = "MME"
+ fields_desc = [
+ XByteField("mmv", 1),
+ LEShortField("mmtype", 0),
+ XBitField("nf_mi", 0, 4),
+ XBitField("fn_mi", 0, 4),
+ XByteField("fmsn", 0),
+ ]
+
+ TYPE_REQ = 0x00
+ TYPE_CNF = 0x01
+ TYPE_IND = 0x02
+ TYPE_RSP = 0x03
+
+ TYPE_VS_UPDATE_START = 0xA064
+ TYPE_VS_UPDATE_TRANSFER = 0xA068
+ TYPE_VS_UPDATE_END = 0xA06C
+
+class VsUpdateStartReq(Packet):
+ name = "VS_UPDATE_START_REQ"
+ mmtype = Mme.TYPE_VS_UPDATE_START | Mme.TYPE_REQ
+ arch_enum = {0:"SPC300"}
+ type_enum = {0:"NORMAL"}
+
+ fields_desc = [
+ X3BytesField("oui", OUI_SPIDCOM),
+ ByteField("protocol", 1),
+ StrFixedLenField("version", "SPiDImage\0", 16),
+ LEIntEnumField("arch", 0, arch_enum),
+ LEIntEnumField("type", 0, type_enum),
+ ]
+
+ def mysummary(self):
+ return self.sprintf("%VsUpdateStartReq.mmtype%")
+
+
+class VsUpdateStartCnf(Packet):
+ name = "VS_UPDATE_START_CNF"
+ mmtype = Mme.TYPE_VS_UPDATE_START | Mme.TYPE_CNF
+ result_enum = {0:"SUCCESS", 1:"ALREADY_EXISTS", 2:"BAD_ARCH", 3:"BAD_TYPE", 4:"BUSY", 5:"PROTOCOL"}
+ fields_desc = [
+ X3BytesField("oui", OUI_SPIDCOM),
+ ByteEnumField("result", 0, result_enum),
+ ]
+
+class VsUpdateTransferReq(Packet):
+ name = "VS_UPDATE_TRANSFER_REQ"
+ mmtype = Mme.TYPE_VS_UPDATE_TRANSFER | Mme.TYPE_REQ
+ fields_desc = [
+ X3BytesField("oui", OUI_SPIDCOM),
+ LEIntField("block_nb", 0),
+ LEFieldLenField("length", None, length_of="data", fmt="<I"),
+ StrLenField("data", "", length_from=lambda pkt:pkt.length),
+ ]
+
+class VsUpdateTransferCnf(Packet):
+ name = "VS_UPDATE_TRANSFER_CNF"
+ mmtype = Mme.TYPE_VS_UPDATE_TRANSFER | Mme.TYPE_CNF
+ result_enum = {0:"SUCCESS", 1:"FAILURE"}
+ fields_desc = [
+ X3BytesField("oui", OUI_SPIDCOM),
+ ByteEnumField("result", 0, result_enum),
+ LEIntField("next", 0),
+ ]
+
+class VsUpdateEndReq(Packet):
+ name = "VS_UPDATE_END_REQ"
+ mmtype = Mme.TYPE_VS_UPDATE_END | Mme.TYPE_REQ
+ fields_desc = [
+ X3BytesField("oui", OUI_SPIDCOM),
+ StrFixedLenField("md5sum", "", length = 16),
+ ]
+
+class VsUpdateEndCnf(Packet):
+ name = "VS_UPDATE_END_CNF"
+ mmtype = Mme.TYPE_VS_UPDATE_END | Mme.TYPE_CNF
+ result_enum = {0:"SUCCESS", 1:"MD5", 2:"VERSION"}
+ fields_desc = [
+ X3BytesField("oui", OUI_SPIDCOM),
+ ByteEnumField("result", 0, result_enum),
+ ]
+
+class VsUpdateEndInd(Packet):
+ name = "VS_UPDATE_END_IND"
+ mmtype = Mme.TYPE_VS_UPDATE_END | Mme.TYPE_IND
+ result_enum = {0:"SUCCESS", 1:"FLASH", 2:"TIMEOUT"}
+ fields_desc = [
+ X3BytesField("oui", OUI_SPIDCOM),
+ ByteEnumField("result", 0, result_enum),
+ ]
+
+bind_layers( Ether, Mme, type = ETH_P_HPAV );
+bind_layers( Mme, VsUpdateStartReq, mmtype = VsUpdateStartReq.mmtype)
+bind_layers( Mme, VsUpdateStartCnf, mmtype = VsUpdateStartCnf.mmtype)
+bind_layers( Mme, VsUpdateTransferReq, mmtype = VsUpdateTransferReq.mmtype)
+bind_layers( Mme, VsUpdateTransferCnf, mmtype = VsUpdateTransferCnf.mmtype)
+bind_layers( Mme, VsUpdateEndReq, mmtype = VsUpdateEndReq.mmtype)
+bind_layers( Mme, VsUpdateEndCnf, mmtype = VsUpdateEndCnf.mmtype)
+bind_layers( Mme, VsUpdateEndInd, mmtype = VsUpdateEndInd.mmtype)
+
+#######################################
+# SPidUpdate
+#######################################
+
+class SpidUpdate:
+ """The SpidUpdate application class"""
+ name = "SpidUpdate"
+
+ START_RETRY_NB = 50
+ START_RETRY_DELAY_S = 0.2
+ TRANSFER_RETRY_NB = 20
+ TRANSFER_RETRY_DELAY_S = 0.2
+ END_RETRY_NB = 5
+ END_RETRY_DELAY_S = 1
+ IND_TIMEOUT_S = 180
+
+ TRANSFER_DATA_SIZE = 1024
+ IMAGE_VERSION_OFFSET = 40
+ IMAGE_VERSION_LEN = 16
+
+ IFACE_DEFAULT = "eth0"
+
+ dst_addr = ETHER_BROADCAST
+ src_addr = None
+ iface = IFACE_DEFAULT
+
+ def __init__(self, filename, iface=None, dest=None):
+ try:
+ self.filename = filename
+ self.file = open(filename, "r")
+
+ except IOError,msg:
+ raise IOError("Failed to open '%s'" % (filename))
+
+ if dest is not None:
+ self.dst_addr = mac2str(dest)
+
+ if(iface is None):
+ self.iface = get_if_list()[0]
+ elif (iface not in get_if_list()):
+ raise AttributeError("interface '%s' is unknown" % (iface))
+ else:
+ self.iface = iface
+
+ self.src_addr = get_if_hwaddr(self.iface)
+
+
+ def srp1(self, s, x, *args,**kargs):
+ a,b,c=sndrcv(s, x, *args,**kargs)
+ if len(a) > 0:
+ return a[0][1]
+ else:
+ return None
+
+ def sniff(self, s, count=0, store=1, offline=None, prn = None, lfilter=None, L2socket=None, timeout=None, *arg, **karg):
+ c = 0
+
+ lst = []
+ if timeout is not None:
+ stoptime = time.time()+timeout
+ remain = None
+ while 1:
+ try:
+ if timeout is not None:
+ remain = stoptime-time.time()
+ if remain <= 0:
+ break
+ sel = select([s],[],[],remain)
+ if s in sel[0]:
+ p = s.recv(MTU)
+ if p is None:
+ break
+ if lfilter and not lfilter(p):
+ continue
+ if store:
+ lst.append(p)
+ c += 1
+ if prn:
+ r = prn(p)
+ if r is not None:
+ print r
+ if count > 0 and c >= count:
+ break
+ except KeyboardInterrupt:
+ break
+ return plist.PacketList(lst,"Sniffed")
+
+ def send_receive(self, socket, mme_packet, filter_class, **args):
+ sendp(
+ Ether (dst = self.dst_addr, src = self.src_addr, type = ETH_P_HPAV) / Mme(mmtype = mme_packet.mmtype) / mme_packet,
+ iface = self.iface,
+ verbose = 0
+ )
+
+ p = self.sniff(socket, iface = self.iface, lfilter = lambda x: x.getlayer(filter_class), count = 1, **args)
+
+ if(len(p) == 0):
+ return None
+
+# print("found %s packets" % (len(p)));
+ return p[0].getlayer(filter_class)
+
+ def start(self):
+ L2socket = conf.L2listen
+ sniff_socket = L2socket(type = ETH_P_HPAV, iface = self.iface)
+ data = self.file.read()
+
+ # send the START request and wait for the answer,
+ # maximum START_RETRY_NB times
+ print "Starting connection with %s..." % (str2mac(self.dst_addr))
+ start_cnf = None
+ for i in range(SpidUpdate.START_RETRY_NB):
+ start_cnf = self.send_receive(
+ sniff_socket,
+ VsUpdateStartReq(version = data[SpidUpdate.IMAGE_VERSION_OFFSET:SpidUpdate.IMAGE_VERSION_OFFSET+SpidUpdate.IMAGE_VERSION_LEN]),
+ VsUpdateStartCnf,
+ timeout = SpidUpdate.START_RETRY_DELAY_S)
+ if(start_cnf is not None):
+ break
+
+ if(start_cnf is None):
+ # no answer: exit
+ print("No answer from station %s" % (str2mac(self.dst_addr)))
+ return
+
+ if((start_cnf.oui != OUI_SPIDCOM)
+ or (VsUpdateStartCnf.result_enum[start_cnf.result] != "SUCCESS")):
+ print("Start upgrade failed: %s" % (VsUpdateStartCnf.result_enum[start_cnf.result]))
+ return
+
+ # now transmit the image file block after block
+ print "Start download of '%s'" % (self.filename)
+ current_block = 0
+ total_block = (os.stat(self.filename)[ST_SIZE] - 1) / SpidUpdate.TRANSFER_DATA_SIZE + 1
+ retry_nb = 0
+ while((current_block < total_block)
+ and (retry_nb < SpidUpdate.TRANSFER_RETRY_NB)):
+ transfer_cnf = self.send_receive(
+ sniff_socket,
+ VsUpdateTransferReq(
+ block_nb = current_block,
+ data = data[current_block * SpidUpdate.TRANSFER_DATA_SIZE:(current_block + 1) * SpidUpdate.TRANSFER_DATA_SIZE]),
+ VsUpdateTransferCnf,
+ timeout = SpidUpdate.TRANSFER_RETRY_DELAY_S)
+ if(transfer_cnf is not None):
+ if((transfer_cnf.oui == OUI_SPIDCOM)
+ and (VsUpdateTransferCnf.result_enum[transfer_cnf.result] == "SUCCESS")):
+ current_block = transfer_cnf.next
+ if(current_block % 10 == 0):
+ print ".",
+ sys.stdout.flush()
+ else:
+ print "R",
+ else:
+ print "T",
+ retry_nb += 1
+
+ if(retry_nb >= SpidUpdate.TRANSFER_RETRY_NB):
+ print("\nToo many retries... give up")
+ return
+
+ # compute MD5 of image and send the End message
+ md5 = hashlib.md5()
+ md5.update(data[:])
+ retry_nb = 0
+ end_cnf = None
+ while((end_cnf is None)
+ and (retry_nb < SpidUpdate.END_RETRY_NB)):
+ end_cnf = self.send_receive(
+ sniff_socket,
+ VsUpdateEndReq(md5sum = md5.digest()),
+ VsUpdateEndCnf,
+ timeout = SpidUpdate.END_RETRY_DELAY_S)
+ if(end_cnf is None):
+ retry_nb += 1
+ if(end_cnf is None):
+ print ("No end confirm... failed")
+ return
+
+ if(VsUpdateEndCnf.result_enum[end_cnf.result] != "SUCCESS"):
+ print("Upgrade failed: %s" % (VsUpdateEndCnf.result_enum[end_cnf.result]))
+ return
+
+ # now wait for the final result
+ print("\nTransfer completed, writing to flash...")
+ p = self.sniff(
+ sniff_socket,
+ iface = self.iface,
+ lfilter = lambda x: x.getlayer(VsUpdateEndInd),
+ count = 1,
+ timeout = SpidUpdate.IND_TIMEOUT_S)
+
+ if(len(p) == 0):
+ print("Upgrade result timeout")
+ return
+
+ end_ind = p[0].getlayer(VsUpdateEndInd)
+ if(VsUpdateEndInd.result_enum[end_ind.result] != "SUCCESS"):
+ print("Upgrade failed: %s" % (VsUpdateEndInd.result_enum[end_ind.result]))
+ return
+
+ print("Upgrade has been successfull !")
+
+#########################################################################
+### main command
+#########################################################################
+def main():
+ """
+spidupdate [options] filename
+with options:
+ -d | --dest: address of device to upgrade (default: broadcast)
+ -i | --iface: network interface name (default: eth0)
+ -h | --help: this help
+ """
+
+ # parse command line options
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], "hd:i:", ["help", "dest=", "iface="])
+ except getopt.error, msg:
+ print msg
+ print "for help use --help"
+ sys.exit(1)
+ # process options
+ iface = None
+ dest = None
+ for o, a in opts:
+ if o in ("-h", "--help"):
+ print main.__doc__
+ sys.exit(0)
+ if o in ("-d", "--dest"):
+ dest = a
+ if o in ("-i", "--iface"):
+ iface = a
+ # process arguments
+ if(len(args) == 0):
+ print main.__doc__
+ sys.exit(1)
+
+ try:
+ update = SpidUpdate(args[0], iface = iface, dest = dest)
+ update.start()
+ except (AttributeError, IOError),msg:
+ print("%s" % (msg))
+ sys.exit(2)
+
+
+if __name__ == "__main__":
+ main()
+
+
+class SpidTest:
+ """Test class for SpidUpdate"""
+ name = "SpidTest"
+ FSM_STATE_INIT = 0
+ FSM_STATE_START = 1
+ FSM_STATE_TRANSFER = 2
+ FSM_STATE_END = 3
+
+ IFF_TAP = 0x0002
+ IFF_NO_PI = 0x1000
+ TUNSETIFF = 0x400454ca
+ TUNSETNOCSUM = 0x400454c8
+
+ def __init__(self, iface=None):
+ #socket = conf.L2socket(type=ETH_P_HPAV, iface=iface)
+
+ #tun0 = open("/dev/net/tun", mode='rw')
+ #ifreq0 = struct.pack('16sh', "tap0", SpidTest.IFF_TAP | SpidTest.IFF_NO_PI)
+ #ioctl(tun0, SpidTest.TUNSETIFF, ifreq0)
+ #ioctl(tun0, SpidTest.TUNSETNOCSUM, 1)
+
+ #tun1 = open("/dev/net/tun", mode='rw')
+ #ifreq1 = struct.pack('16sh', "tap1", SpidTest.IFF_TAP | SpidTest.IFF_NO_PI)
+ #ioctl(tun1, SpidTest.TUNSETIFF, ifreq1)
+ #ioctl(tun1, SpidTest.TUNSETNOCSUM, 1)
+
+ while 1:
+ p = sniff(iface = iface, lfilter = lambda x: hasattr(x, 'type') and (x.type == ETH_P_HPAV), count = 1)
+ #mme = p[0].getlayer(Mme)
+ start = p[0].getlayer(VsUpdateStartReq)
+ if(start is not None):
+ if((start.oui == OUI_SPIDCOM)
+ and (start.version == 1)
+ and (VsUpdateStartReq.ARCH_ENUM[start.arch] is "SPC300")
+ and (VsUpdateStartReq.TYPE_ENUM[start.type] is "NORMAL")):
+ break
+
+ print("Found start")
+ ether = p[0].getlayer(Ether)
+ sendp (Ether (dst = ETHER_BROADCAST, type = ETH_P_HPAV) / Mme(mmtype = (Mme.TYPE_VS_UPDATE_START | Mme.TYPE_CNF)) / VsUpdateStartCnf(), iface=iface)
+# if mme.mmtype == Mme.TYPE_VS_UPDATE_START | Mme.TYPE_REQ:
+# packet.dissect(s);
+ print("Exit")