| # This file is part of Scapy |
| # Scapy 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 |
| # any later version. |
| # |
| # Scapy 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 Scapy. If not, see <http://www.gnu.org/licenses/>. |
| |
| # author: <jellch@harris.com> |
| |
| # scapy.contrib.description = PPI GEOLOCATION |
| # scapy.contrib.status = loads |
| |
| |
| """ |
| PPI-GEOLOCATION tags |
| """ |
| from __future__ import absolute_import |
| import struct, time |
| from scapy.packet import * |
| from scapy.fields import * |
| from scapy.contrib.ppi import PPIGenericFldHdr,addPPIType |
| from scapy.error import warning |
| import scapy.modules.six as six |
| from scapy.modules.six.moves import range |
| |
| 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 LETimeField(UTCTimeField,LEIntField): |
| __slots__ = ["epoch", "delta", "strf"] |
| def __init__(self, name, default, epoch=None, strf="%a, %d %b %Y %H:%M:%S +0000"): |
| LEIntField.__init__(self, name, default) |
| UTCTimeField.__init__(self, name, default, epoch=epoch, 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 isinstance(x, 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, name in enumerate(self.names): |
| 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 = ["Reserved%02d" % i for i in range(32)] |
| for i, value in six.iteritems(myfields): |
| flags[i] = value |
| 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, name in enumerate(names): |
| ibit = 1 << i |
| seval = "lambda pkt:_HCSITest(pkt,%s,'%s')" % (ibit, name) |
| 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('<H',l) |
| p = p[:2] + sl + p[4:] |
| if self.geotag_len is None: |
| l_g = len(p) - 4 |
| sl_g = struct.pack('<H',l_g) |
| p = p[:6] + sl_g + p[8:] |
| p += pay |
| return p |
| def extract_padding(self, p): |
| return b"", p |
| |
| #GPS Fields |
| GPS_Fields = [FlagsField("GPSFlags", None, -32, _hcsi_gps_flags), |
| Fixed3_7Field("Latitude", None), |
| Fixed3_7Field("Longitude", None), Fixed6_4Field("Altitude", None), |
| Fixed6_4Field("Altitude_g", None), GPSTime_Field("GPSTime", None), |
| NSCounter_Field("FractionalTime", None), Fixed3_6Field("eph", None), |
| Fixed3_6Field("epv", None), NSCounter_Field("ept", None), |
| HCSINullField("Reserved10", None), HCSINullField("Reserved11", None), |
| HCSINullField("Reserved12", None), HCSINullField("Reserved13", None), |
| HCSINullField("Reserved14", None), HCSINullField("Reserved15", None), |
| HCSINullField("Reserved16", None), HCSINullField("Reserved17", None), |
| HCSINullField("Reserved18", None), HCSINullField("Reserved19", None), |
| HCSINullField("Reserved20", None), HCSINullField("Reserved21", None), |
| HCSINullField("Reserved22", None), HCSINullField("Reserved23", None), |
| HCSINullField("Reserved24", None), HCSINullField("Reserved25", None), |
| HCSINullField("Reserved26", None), HCSINullField("Reserved27", None), |
| HCSIDescField("DescString", None), XLEIntField("AppId", None), |
| HCSIAppField("AppData", None), HCSINullField("Extended", None)] |
| |
| class GPS(HCSIPacket): |
| name = "PPI GPS" |
| fields_desc = [ LEShortField('pfh_type', PPI_GPS), #pfh_type |
| LEShortField('pfh_length', None), #pfh_len |
| ByteField('geotag_ver', CURR_GEOTAG_VER), #base_geotag_header.ver |
| ByteField('geotag_pad', 0), #base_geotag_header.pad |
| LEShortField('geotag_len', None)] + _HCSIBuildFields(GPS_Fields) |
| |
| |
| #Vector Fields |
| VEC_Fields = [VectorFlags_Field("VectorFlags", None), |
| FlagsField("VectorChars", None, -32, _hcsi_vector_char_flags), |
| Fixed3_6Field("Pitch", None), Fixed3_6Field("Roll", None), |
| Fixed3_6Field("Heading", None), Fixed6_4Field("Off_X", None), |
| Fixed6_4Field("Off_Y", None), Fixed6_4Field("Off_Z", None), |
| HCSINullField("Reserved08", None), HCSINullField("Reserved09", None), |
| HCSINullField("Reserved10", None), HCSINullField("Reserved11", None), |
| HCSINullField("Reserved12", None), HCSINullField("Reserved13", None), |
| HCSINullField("Reserved14", None), HCSINullField("Reserved15", None), |
| Fixed3_6Field("Err_Rot", None), Fixed6_4Field("Err_Off", None), |
| HCSINullField("Reserved18", None), HCSINullField("Reserved19", None), |
| HCSINullField("Reserved20", None), HCSINullField("Reserved21", None), |
| HCSINullField("Reserved22", None), HCSINullField("Reserved23", None), |
| HCSINullField("Reserved24", None), HCSINullField("Reserved25", None), |
| HCSINullField("Reserved26", None), HCSINullField("Reserved27", None), |
| HCSIDescField("DescString", None), XLEIntField("AppId", None), |
| HCSIAppField("AppData", None), HCSINullField("Extended", None)] |
| |
| class Vector(HCSIPacket): |
| name = "PPI Vector" |
| fields_desc = [ LEShortField('pfh_type', PPI_VECTOR), #pfh_type |
| LEShortField('pfh_length', None), #pfh_len |
| ByteField('geotag_ver', CURR_GEOTAG_VER), #base_geotag_header.ver |
| ByteField('geotag_pad', 0), #base_geotag_header.pad |
| LEShortField('geotag_len', None)] + _HCSIBuildFields(VEC_Fields) |
| |
| #Sensor Fields |
| # http://www.iana.org/assignments/icmp-parameters |
| sensor_types= { 1 : "Velocity", |
| 2 : "Acceleration", |
| 3 : "Jerk", |
| 100 : "Rotation", |
| 101 : "Magnetic", |
| 1000: "Temperature", |
| 1001: "Barometer", |
| 1002: "Humidity", |
| 2000: "TDOA_Clock", |
| 2001: "Phase" |
| } |
| SENS_Fields = [ LEShortEnumField('SensorType', None, sensor_types), |
| SignedByteField('ScaleFactor', None), |
| Fixed6_4Field('Val_X', None), |
| Fixed6_4Field('Val_Y', None), |
| Fixed6_4Field('Val_Z', None), |
| Fixed6_4Field('Val_T', None), |
| Fixed6_4Field('Val_E', None), |
| HCSINullField("Reserved07", None), HCSINullField("Reserved08", None), |
| HCSINullField("Reserved09", None), HCSINullField("Reserved10", None), |
| HCSINullField("Reserved11", None), HCSINullField("Reserved12", None), |
| HCSINullField("Reserved13", None), HCSINullField("Reserved14", None), |
| HCSINullField("Reserved15", None), HCSINullField("Reserved16", None), |
| HCSINullField("Reserved17", None), HCSINullField("Reserved18", None), |
| HCSINullField("Reserved19", None), HCSINullField("Reserved20", None), |
| HCSINullField("Reserved21", None), HCSINullField("Reserved22", None), |
| HCSINullField("Reserved23", None), HCSINullField("Reserved24", None), |
| HCSINullField("Reserved25", None), HCSINullField("Reserved26", None), |
| HCSINullField("Reserved27", None), |
| HCSIDescField("DescString", None), XLEIntField("AppId", None), |
| HCSIAppField("AppData", None), HCSINullField("Extended", None)] |
| |
| |
| |
| class Sensor(HCSIPacket): |
| name = "PPI Sensor" |
| fields_desc = [ LEShortField('pfh_type', PPI_SENSOR), #pfh_type |
| LEShortField('pfh_length', None), #pfh_len |
| ByteField('geotag_ver', CURR_GEOTAG_VER ), #base_geotag_header.ver |
| ByteField('geotag_pad', 0), #base_geotag_header.pad |
| LEShortField('geotag_len', None)] + _HCSIBuildFields(SENS_Fields) |
| |
| # HCSIAntenna Fields |
| ANT_Fields = [FlagsField("AntennaFlags", None, -32, _hcsi_antenna_flags), |
| ByteField("Gain", None), |
| Fixed3_6Field("HorizBw", None), Fixed3_6Field("VertBw", None), |
| Fixed3_6Field("PrecisionGain",None), XLEShortField("BeamID", None), |
| HCSINullField("Reserved06", None), HCSINullField("Reserved07", None), |
| HCSINullField("Reserved08", None), HCSINullField("Reserved09", None), |
| HCSINullField("Reserved10", None), HCSINullField("Reserved11", None), |
| HCSINullField("Reserved12", None), HCSINullField("Reserved13", None), |
| HCSINullField("Reserved14", None), HCSINullField("Reserved15", None), |
| HCSINullField("Reserved16", None), HCSINullField("Reserved17", None), |
| HCSINullField("Reserved18", None), HCSINullField("Reserved19", None), |
| HCSINullField("Reserved20", None), HCSINullField("Reserved21", None), |
| HCSINullField("Reserved22", None), HCSINullField("Reserved23", None), |
| HCSINullField("Reserved24", None), HCSINullField("Reserved25", None), |
| HCSIDescField("SerialNumber", None), HCSIDescField("ModelName", None), |
| HCSIDescField("DescString", None), XLEIntField("AppId", None), |
| HCSIAppField("AppData", None), HCSINullField("Extended", None)] |
| |
| class Antenna(HCSIPacket): |
| name = "PPI Antenna" |
| fields_desc = [ LEShortField('pfh_type', PPI_ANTENNA), #pfh_type |
| LEShortField('pfh_length', None), #pfh_len |
| ByteField('geotag_ver', CURR_GEOTAG_VER), #base_geotag_header.ver |
| ByteField('geotag_pad', 0), #base_geotag_header.pad |
| LEShortField('geotag_len', None)] + _HCSIBuildFields(ANT_Fields) |
| |
| addPPIType(PPI_GPS, GPS) |
| addPPIType(PPI_VECTOR, Vector) |
| addPPIType(PPI_SENSOR, Sensor) |
| addPPIType(PPI_ANTENNA,Antenna) |