## This file is (hopefully) part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## ## This program is published under a GPLv2 license # scapy.contrib.description = PPI GEOLOCATION # scapy.contrib.status = loads """ PPI-GEOLOCATION tags """ import struct from scapy.packet import * from scapy.fields import * from scapy.contrib.ppi import PPIGenericFldHdr,addPPIType CURR_GEOTAG_VER = 2 #Major revision of specification PPI_GPS = 30002 PPI_VECTOR = 30003 PPI_SENSOR = 30004 PPI_ANTENNA = 30005 #The FixedX_Y Fields are used to store fixed point numbers in a variety of fields in the GEOLOCATION-TAGS specification class Fixed3_6Field(LEIntField): def i2h(self, pkt, x): if x is not None: if (x < 0): warning("Fixed3_6: Internal value too negative: %d" % x) x = 0 elif (x > 999999999): warning("Fixed3_6: Internal value too positive: %d" % x) x = 999999999 x = x * 1e-6 return x def h2i(self, pkt, x): if x is not None: if (x <= -0.5e-6): warning("Fixed3_6: Input value too negative: %.7f" % x) x = 0 elif (x >= 999.9999995): warning("Fixed3_6: Input value too positive: %.7f" % x) x = 999.999999 x = int(round(x * 1e6)) return x def i2m(self, pkt, x): """Convert internal value to machine value""" if x is None: #Try to return zero if undefined x = self.h2i(pkt, 0) return x def i2repr(self,pkt,x): if x is None: y=0 else: y=self.i2h(pkt,x) return "%3.6f"%(y) class Fixed3_7Field(LEIntField): def i2h(self, pkt, x): if x is not None: if (x < 0): warning("Fixed3_7: Internal value too negative: %d" % x) x = 0 elif (x > 3600000000): warning("Fixed3_7: Internal value too positive: %d" % x) x = 3600000000 x = (x - 1800000000) * 1e-7 return x def h2i(self, pkt, x): if x is not None: if (x <= -180.00000005): warning("Fixed3_7: Input value too negative: %.8f" % x) x = -180.0 elif (x >= 180.00000005): warning("Fixed3_7: Input value too positive: %.8f" % x) x = 180.0 x = int(round((x + 180.0) * 1e7)) return x def i2m(self, pkt, x): """Convert internal value to machine value""" if x is None: #Try to return zero if undefined x = self.h2i(pkt, 0) return x def i2repr(self,pkt,x): if x is None: y=0 else: y=self.i2h(pkt,x) return "%3.7f"%(y) class Fixed6_4Field(LEIntField): def i2h(self, pkt, x): if x is not None: if (x < 0): warning("Fixed6_4: Internal value too negative: %d" % x) x = 0 elif (x > 3600000000): warning("Fixed6_4: Internal value too positive: %d" % x) x = 3600000000 x = (x - 1800000000) * 1e-4 return x def h2i(self, pkt, x): if x is not None: if (x <= -180000.00005): warning("Fixed6_4: Input value too negative: %.5f" % x) x = -180000.0 elif (x >= 180000.00005): warning("Fixed6_4: Input value too positive: %.5f" % x) x = 180000.0 x = int(round((x + 180000.0) * 1e4)) return x def i2m(self, pkt, x): """Convert internal value to machine value""" if x is None: #Try to return zero if undefined x = self.h2i(pkt, 0) return x def i2repr(self,pkt,x): if x is None: y=0 else: y=self.i2h(pkt,x) return "%6.4f"%(y) #The GPS timestamps fractional time counter is stored in a 32-bit unsigned ns counter. #The ept field is as well, class NSCounter_Field(LEIntField): def i2h(self, pkt, x): #converts nano-seconds to seconds for output if x is not None: if (x < 0): warning("NSCounter_Field: Internal value too negative: %d" % x) x = 0 elif (x >= 2**32): warning("NSCounter_Field: Internal value too positive: %d" % x) x = 2**32-1 x = (x / 1e9) return x def h2i(self, pkt, x): #converts input in seconds into nano-seconds for storage if x is not None: if (x < 0): warning("NSCounter_Field: Input value too negative: %.10f" % x) x = 0 elif (x >= (2**32) / 1e9): warning("NSCounter_Field: Input value too positive: %.10f" % x) x = (2**32-1) / 1e9 x = int(round((x * 1e9))) return x def i2repr(self,pkt,x): if x is None: y=0 else: y=self.i2h(pkt,x) return "%1.9f"%(y) class UTCTimeField(IntField): def __init__(self, name, default, epoch=time.gmtime(0), strf="%a, %d %b %Y %H:%M:%S +0000"): IntField.__init__(self, name, default) self.epoch = epoch self.delta = time.mktime(epoch) - time.mktime(time.gmtime(0)) self.strf = strf def i2repr(self, pkt, x): if x is None: x = 0 x = int(x) + self.delta t = time.strftime(self.strf, time.gmtime(x)) return "%s (%d)" % (t, x) class LETimeField(UTCTimeField,LEIntField): def __init__(self, name, default, epoch=time.gmtime(0), strf="%a, %d %b %Y %H:%M:%S +0000"): LEIntField.__init__(self, name, default) self.epoch = epoch self.delta = time.mktime(epoch) - time.mktime(time.gmtime(0)) self.strf = strf class SignedByteField(Field): def __init__(self, name, default): Field.__init__(self, name, default, "b") def randval(self): return RandSByte() class XLEShortField(LEShortField,XShortField): def i2repr(self, pkt, x): return XShortField.i2repr(self, pkt, x) class XLEIntField(LEIntField,XIntField): def i2repr(self, pkt, x): return XIntField.i2repr(self, pkt, x) class GPSTime_Field(LETimeField): def __init__(self, name, default): return LETimeField.__init__(self, name, default, strf="%a, %d %b %Y %H:%M:%S UTC") class VectorFlags_Field(XLEIntField): """Represents te VectorFlags field. Handles the RelativeTo:sub-field""" _fwdstr = "DefinesForward" _resmask = 0xfffffff8 _relmask = 0x6 _relnames = ["RelativeToForward", "RelativeToEarth", "RelativeToCurrent", "RelativeToReserved"] _relvals = [0x00, 0x02, 0x04, 0x06] def i2repr(self, pkt, x): if x is None: return str(x) r = [] if (x & 0x1): r.append(self._fwdstr) i = (x & self._relmask) >> 1 r.append(self._relnames[i]) i = x & self._resmask if (i): r.append("ReservedBits:%08X" % i) sout = "+".join(r) return sout def any2i(self, pkt, x): if type(x) is str: r = x.split("+") y = 0 for value in r: if (value == self._fwdstr): y |= 0x1 elif (value in self._relnames): i = self._relnames.index(value) y &= (~self._relmask) y |= self._relvals[i] else: #logging.warning("Unknown VectorFlags Argument: %s" % value) pass else: y = x #print "any2i: %s --> %s" % (str(x), str(y)) return y class HCSIFlagsField(FlagsField): """ A FlagsField where each bit/flag turns a conditional field on or off. If the value is None when building a packet, i2m() will check the value of every field in self.names. If the field's value is not None, the corresponding flag will be set. """ def i2m(self, pkt, val): if val is None: val = 0 if (pkt): for i in range(len(self.names)): name = self.names[i][0] value = pkt.getfieldval(name) if value is not None: val |= 1 << i return val class HCSINullField(StrFixedLenField): def __init__(self, name, default): return StrFixedLenField.__init__(self, name, default, length=0) class HCSIDescField(StrFixedLenField): def __init__(self, name, default): return StrFixedLenField.__init__(self, name, default, length=32) class HCSIAppField(StrFixedLenField): def __init__(self, name, default): return StrFixedLenField.__init__(self, name, default, length=60) def _FlagsList(myfields): flags = [] for i in range(32): flags.append("Reserved%02d" % i) for i in myfields.keys(): flags[i] = myfields[i] return flags # Define all geolocation-tag flags lists _hcsi_gps_flags = _FlagsList({0:"No Fix Available", 1:"GPS", 2:"Differential GPS", 3:"Pulse Per Second", 4:"Real Time Kinematic", 5:"Float Real Time Kinematic", 6:"Estimated (Dead Reckoning)", 7:"Manual Input", 8:"Simulation"}) #_hcsi_vector_flags = _FlagsList({0:"ForwardFrame", 1:"RotationsAbsoluteXYZ", 5:"OffsetFromGPS_XYZ"}) #This has been replaced with the VectorFlags_Field class, in order to handle the RelativeTo:subfield _hcsi_vector_char_flags = _FlagsList({0:"Antenna", 1:"Direction of Travel", 2:"Front of Vehicle", 3:"Angle of Arrival", 4:"Transmitter Position", 8:"GPS Derived", 9:"INS Derived", 10:"Compass Derived", 11:"Acclerometer Derived", 12:"Human Derived"}) _hcsi_antenna_flags = _FlagsList({ 1:"Horizontal Polarization", 2:"Vertical Polarization", 3:"Circular Polarization Left", 4:"Circular Polarization Right", 16:"Electronically Steerable", 17:"Mechanically Steerable"}) """ HCSI PPI Fields are similar to RadioTap. A mask field called "present" specifies if each field is present. All other fields are conditional. When dissecting a packet, each field is present if "present" has the corresponding bit set. When building a packet, if "present" is None, the mask is set to include every field that does not have a value of None. Otherwise, if the mask field is not None, only the fields specified by "present" will be added to the packet. To build each Packet type, build a list of the fields normally, excluding the present bitmask field. The code will then construct conditional versions of each field and add the present field. See GPS_Fields as an example. """ # Conditional test for all HCSI Fields def _HCSITest(pkt, ibit, name): if pkt.present is None: return (pkt.getfieldval(name) is not None) return pkt.present & ibit # Wrap optional fields in ConditionalField, add HCSIFlagsField def _HCSIBuildFields(fields): names = [f.name for f in fields] cond_fields = [ HCSIFlagsField('present', None, -len(names), names)] for i in range(len(names)): ibit = 1 << i seval = "lambda pkt:_HCSITest(pkt,%s,'%s')" % (ibit, names[i]) test = eval(seval) cond_fields.append(ConditionalField(fields[i], test)) return cond_fields class HCSIPacket(Packet): name = "PPI HCSI" fields_desc = [ LEShortField('pfh_type', None), LEShortField('pfh_length', None), ByteField('geotag_ver', CURR_GEOTAG_VER), ByteField('geotag_pad', 0), LEShortField('geotag_len', None)] def post_build(self, p, pay): if self.pfh_length is None: l = len(p) - 4 sl = struct.pack('