Home | History | Annotate | Download | only in contrib
      1 # This file is part of Scapy
      2 # Scapy is free software: you can redistribute it and/or modify
      3 # it under the terms of the GNU General Public License as published by
      4 # the Free Software Foundation, either version 2 of the License, or
      5 # any later version.
      6 #
      7 # Scapy is distributed in the hope that it will be useful,
      8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
      9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     10 # GNU General Public License for more details.
     11 #
     12 # You should have received a copy of the GNU General Public License
     13 # along with Scapy. If not, see <http://www.gnu.org/licenses/>.
     14 
     15 # author: <jellch (at] harris.com>
     16 
     17 # scapy.contrib.description = PPI GEOLOCATION
     18 # scapy.contrib.status = loads
     19 
     20 
     21 """
     22 PPI-GEOLOCATION tags
     23 """
     24 from __future__ import absolute_import
     25 import struct, time
     26 from scapy.packet import *
     27 from scapy.fields import *
     28 from scapy.contrib.ppi import PPIGenericFldHdr,addPPIType
     29 from scapy.error import warning
     30 import scapy.modules.six as six
     31 from scapy.modules.six.moves import range
     32 
     33 CURR_GEOTAG_VER = 2 #Major revision of specification
     34 
     35 PPI_GPS     = 30002
     36 PPI_VECTOR  = 30003
     37 PPI_SENSOR  = 30004
     38 PPI_ANTENNA = 30005
     39 #The FixedX_Y Fields are used to store fixed point numbers in a variety of fields in the GEOLOCATION-TAGS specification
     40 class Fixed3_6Field(LEIntField):
     41     def i2h(self, pkt, x):
     42         if x is not None:
     43             if (x < 0):
     44                 warning("Fixed3_6: Internal value too negative: %d", x)
     45                 x = 0
     46             elif (x > 999999999):
     47                 warning("Fixed3_6: Internal value too positive: %d", x)
     48                 x = 999999999
     49             x = x * 1e-6
     50         return x
     51     def h2i(self, pkt, x):
     52         if x is not None:
     53             if (x <= -0.5e-6):
     54                 warning("Fixed3_6: Input value too negative: %.7f", x)
     55                 x = 0
     56             elif (x >= 999.9999995):
     57                 warning("Fixed3_6: Input value too positive: %.7f", x)
     58                 x = 999.999999
     59             x = int(round(x * 1e6))
     60         return x
     61     def i2m(self, pkt, x):
     62         """Convert internal value to machine value"""
     63         if x is None:
     64             #Try to return zero if undefined
     65             x = self.h2i(pkt, 0)
     66         return x
     67 
     68     def i2repr(self,pkt,x):
     69         if x is None:
     70             y=0
     71         else:
     72             y=self.i2h(pkt,x)
     73         return "%3.6f"%(y)
     74 class Fixed3_7Field(LEIntField):
     75     def i2h(self, pkt, x):
     76         if x is not None:
     77             if (x < 0):
     78                 warning("Fixed3_7: Internal value too negative: %d",  x)
     79                 x = 0
     80             elif (x > 3600000000):
     81                 warning("Fixed3_7: Internal value too positive: %d",  x)
     82                 x = 3600000000
     83             x = (x - 1800000000) * 1e-7
     84         return x
     85     def h2i(self, pkt, x):
     86         if x is not None:
     87             if (x <= -180.00000005):
     88                 warning("Fixed3_7: Input value too negative: %.8f",  x)
     89                 x = -180.0
     90             elif (x >= 180.00000005):
     91                 warning("Fixed3_7: Input value too positive: %.8f",  x)
     92                 x = 180.0
     93             x = int(round((x + 180.0) * 1e7))
     94         return x
     95     def i2m(self, pkt, x):
     96         """Convert internal value to machine value"""
     97         if x is None:
     98             #Try to return zero if undefined
     99             x = self.h2i(pkt, 0)
    100         return x
    101     def i2repr(self,pkt,x):
    102         if x is None:
    103             y=0
    104         else:
    105             y=self.i2h(pkt,x)
    106         return "%3.7f"%(y)
    107 
    108 class Fixed6_4Field(LEIntField):
    109     def i2h(self, pkt, x):
    110         if x is not None:
    111             if (x < 0):
    112                 warning("Fixed6_4: Internal value too negative: %d",  x)
    113                 x = 0
    114             elif (x > 3600000000):
    115                 warning("Fixed6_4: Internal value too positive: %d",  x)
    116                 x = 3600000000
    117             x = (x - 1800000000) * 1e-4
    118         return x
    119     def h2i(self, pkt, x):
    120         if x is not None:
    121             if (x <= -180000.00005):
    122                 warning("Fixed6_4: Input value too negative: %.5f",  x)
    123                 x = -180000.0
    124             elif (x >= 180000.00005):
    125                 warning("Fixed6_4: Input value too positive: %.5f",  x)
    126                 x = 180000.0
    127             x = int(round((x + 180000.0) * 1e4))
    128         return x
    129     def i2m(self, pkt, x):
    130         """Convert internal value to machine value"""
    131         if x is None:
    132             #Try to return zero if undefined
    133             x = self.h2i(pkt, 0)
    134         return x
    135     def i2repr(self,pkt,x):
    136         if x is None:
    137             y=0
    138         else:
    139             y=self.i2h(pkt,x)
    140         return "%6.4f"%(y)
    141 #The GPS timestamps fractional time counter is stored in a 32-bit unsigned ns counter.
    142 #The ept field is as well,
    143 class NSCounter_Field(LEIntField):
    144     def i2h(self, pkt, x): #converts nano-seconds to seconds for output
    145         if x is not None:
    146             if (x < 0):
    147                 warning("NSCounter_Field: Internal value too negative: %d",  x)
    148                 x = 0
    149             elif (x >= 2**32):
    150                 warning("NSCounter_Field: Internal value too positive: %d",  x)
    151                 x = 2**32-1
    152             x = (x / 1e9)
    153         return x
    154     def h2i(self, pkt, x): #converts input in seconds into nano-seconds for storage
    155         if x is not None:
    156             if (x < 0):
    157                 warning("NSCounter_Field: Input value too negative: %.10f",  x)
    158                 x = 0
    159             elif (x >= (2**32) / 1e9):
    160                 warning("NSCounter_Field: Input value too positive: %.10f",  x)
    161                 x = (2**32-1) / 1e9
    162             x = int(round((x * 1e9)))
    163         return x
    164     def i2repr(self,pkt,x):
    165         if x is None:
    166             y=0
    167         else:
    168             y=self.i2h(pkt,x)
    169         return "%1.9f"%(y)
    170 
    171 class LETimeField(UTCTimeField,LEIntField):
    172     __slots__ = ["epoch", "delta", "strf"]
    173     def __init__(self, name, default, epoch=None, strf="%a, %d %b %Y %H:%M:%S +0000"):
    174         LEIntField.__init__(self, name, default)
    175         UTCTimeField.__init__(self, name, default, epoch=epoch, strf=strf)
    176 
    177 class SignedByteField(Field):
    178     def __init__(self, name, default):
    179         Field.__init__(self, name, default, "b")
    180     def randval(self):
    181         return RandSByte()
    182 
    183 class XLEShortField(LEShortField,XShortField):
    184     def i2repr(self, pkt, x):
    185         return XShortField.i2repr(self, pkt, x)
    186 
    187 class XLEIntField(LEIntField,XIntField):
    188     def i2repr(self, pkt, x):
    189         return XIntField.i2repr(self, pkt, x)
    190 
    191 class GPSTime_Field(LETimeField):
    192     def __init__(self, name, default):
    193         return LETimeField.__init__(self, name, default, strf="%a, %d %b %Y %H:%M:%S UTC")
    194 
    195 class VectorFlags_Field(XLEIntField):
    196     """Represents te VectorFlags field. Handles the RelativeTo:sub-field"""
    197     _fwdstr   = "DefinesForward"
    198     _resmask  = 0xfffffff8
    199     _relmask  = 0x6
    200     _relnames = ["RelativeToForward", "RelativeToEarth", "RelativeToCurrent", "RelativeToReserved"]
    201     _relvals  = [0x00, 0x02, 0x04, 0x06]
    202     def i2repr(self, pkt, x):
    203         if x is None:
    204             return str(x)
    205         r = []
    206         if (x & 0x1):
    207             r.append(self._fwdstr)
    208         i = (x & self._relmask) >> 1
    209         r.append(self._relnames[i])
    210         i = x & self._resmask
    211         if (i):
    212             r.append("ReservedBits:%08X" % i)
    213         sout = "+".join(r)
    214         return sout
    215     def any2i(self, pkt, x):
    216         if isinstance(x, str):
    217             r = x.split("+")
    218             y = 0
    219             for value in r:
    220                 if (value == self._fwdstr):
    221                     y |= 0x1
    222                 elif (value in self._relnames):
    223                     i = self._relnames.index(value)
    224                     y &= (~self._relmask)
    225                     y |= self._relvals[i]
    226                 else:
    227                     #logging.warning("Unknown VectorFlags Argument: %s",  value)
    228                     pass
    229         else:
    230             y = x
    231         #print "any2i: %s --> %s" % (str(x), str(y))
    232         return y
    233 
    234 class HCSIFlagsField(FlagsField):
    235     """ A FlagsField where each bit/flag turns a conditional field on or off.
    236     If the value is None when building a packet, i2m() will check the value of
    237     every field in self.names.  If the field's value is not None, the corresponding
    238     flag will be set. """
    239     def i2m(self, pkt, val):
    240         if val is None:
    241             val = 0
    242             if (pkt):
    243                 for i, name in enumerate(self.names):
    244                     value = pkt.getfieldval(name)
    245                     if value is not None:
    246                         val |= 1 << i
    247         return val
    248 
    249 class HCSINullField(StrFixedLenField):
    250     def __init__(self, name, default):
    251         return StrFixedLenField.__init__(self, name, default, length=0)
    252 
    253 class HCSIDescField(StrFixedLenField):
    254     def __init__(self, name, default):
    255         return StrFixedLenField.__init__(self, name, default, length=32)
    256 
    257 class HCSIAppField(StrFixedLenField):
    258     def __init__(self, name, default):
    259         return StrFixedLenField.__init__(self, name, default, length=60)
    260 
    261 def _FlagsList(myfields):
    262     flags = ["Reserved%02d" % i for i in range(32)]
    263     for i, value in six.iteritems(myfields):
    264         flags[i] = value
    265     return flags
    266 
    267 # Define all geolocation-tag flags lists
    268 _hcsi_gps_flags = _FlagsList({0:"No Fix Available", 1:"GPS", 2:"Differential GPS",
    269                               3:"Pulse Per Second", 4:"Real Time Kinematic",
    270                               5:"Float Real Time Kinematic", 6:"Estimated (Dead Reckoning)",
    271                               7:"Manual Input", 8:"Simulation"})
    272 
    273 #_hcsi_vector_flags = _FlagsList({0:"ForwardFrame", 1:"RotationsAbsoluteXYZ", 5:"OffsetFromGPS_XYZ"})
    274 #This has been replaced with the VectorFlags_Field class, in order to handle the RelativeTo:subfield
    275 
    276 _hcsi_vector_char_flags = _FlagsList({0:"Antenna", 1:"Direction of Travel",
    277                                       2:"Front of Vehicle", 3:"Angle of Arrival", 4:"Transmitter Position",
    278                                       8:"GPS Derived", 9:"INS Derived", 10:"Compass Derived",
    279                                      11:"Acclerometer Derived", 12:"Human Derived"})
    280 
    281 _hcsi_antenna_flags = _FlagsList({ 1:"Horizontal Polarization",     2:"Vertical Polarization",
    282                                    3:"Circular Polarization Left",  4:"Circular Polarization Right",
    283                                   16:"Electronically Steerable",   17:"Mechanically Steerable"})
    284 
    285 """ HCSI PPI Fields are similar to RadioTap.  A mask field called "present" specifies if each field
    286 is present.  All other fields are conditional.  When dissecting a packet, each field is present if
    287 "present" has the corresponding bit set.  When building a packet, if "present" is None, the mask is
    288 set to include every field that does not have a value of None.  Otherwise, if the mask field is
    289 not None, only the fields specified by "present" will be added to the packet.
    290 
    291 To build each Packet type, build a list of the fields normally, excluding the present bitmask field.
    292 The code will then construct conditional versions of each field and add the present field.
    293 See GPS_Fields as an example. """
    294 
    295 # Conditional test for all HCSI Fields
    296 def _HCSITest(pkt, ibit, name):
    297     if pkt.present is None:
    298         return (pkt.getfieldval(name) is not None)
    299     return pkt.present & ibit
    300 
    301 # Wrap optional fields in ConditionalField, add HCSIFlagsField
    302 def _HCSIBuildFields(fields):
    303     names = [f.name for f in fields]
    304     cond_fields = [HCSIFlagsField('present', None, -len(names), names)]
    305     for i, name in enumerate(names):
    306         ibit = 1 << i
    307         seval = "lambda pkt:_HCSITest(pkt,%s,'%s')" % (ibit, name)
    308         test = eval(seval)
    309         cond_fields.append(ConditionalField(fields[i], test))
    310     return cond_fields
    311 
    312 class HCSIPacket(Packet):
    313     name = "PPI HCSI"
    314     fields_desc = [ LEShortField('pfh_type', None),
    315                     LEShortField('pfh_length', None),
    316                     ByteField('geotag_ver', CURR_GEOTAG_VER),
    317                     ByteField('geotag_pad', 0),
    318                     LEShortField('geotag_len', None)]
    319     def post_build(self, p, pay):
    320         if self.pfh_length is None:
    321             l = len(p) - 4
    322             sl = struct.pack('<H',l)
    323             p = p[:2] + sl + p[4:]
    324         if self.geotag_len is None:
    325             l_g = len(p) - 4
    326             sl_g = struct.pack('<H',l_g)
    327             p = p[:6] + sl_g + p[8:]
    328         p += pay
    329         return p
    330     def extract_padding(self, p):
    331         return b"", p
    332 
    333 #GPS Fields
    334 GPS_Fields = [FlagsField("GPSFlags", None, -32, _hcsi_gps_flags),
    335               Fixed3_7Field("Latitude", None),
    336               Fixed3_7Field("Longitude", None),    Fixed6_4Field("Altitude", None),
    337               Fixed6_4Field("Altitude_g", None),   GPSTime_Field("GPSTime", None),
    338               NSCounter_Field("FractionalTime", None),  Fixed3_6Field("eph", None),
    339               Fixed3_6Field("epv", None),          NSCounter_Field("ept", None),
    340               HCSINullField("Reserved10", None),   HCSINullField("Reserved11", None),
    341               HCSINullField("Reserved12", None),   HCSINullField("Reserved13", None),
    342               HCSINullField("Reserved14", None),   HCSINullField("Reserved15", None),
    343               HCSINullField("Reserved16", None),   HCSINullField("Reserved17", None),
    344               HCSINullField("Reserved18", None),   HCSINullField("Reserved19", None),
    345               HCSINullField("Reserved20", None),   HCSINullField("Reserved21", None),
    346               HCSINullField("Reserved22", None),   HCSINullField("Reserved23", None),
    347               HCSINullField("Reserved24", None),   HCSINullField("Reserved25", None),
    348               HCSINullField("Reserved26", None),   HCSINullField("Reserved27", None),
    349               HCSIDescField("DescString", None),   XLEIntField("AppId", None),
    350               HCSIAppField("AppData", None),       HCSINullField("Extended", None)]
    351 
    352 class GPS(HCSIPacket):
    353     name = "PPI GPS"
    354     fields_desc = [ LEShortField('pfh_type', PPI_GPS), #pfh_type
    355                     LEShortField('pfh_length', None), #pfh_len
    356                     ByteField('geotag_ver', CURR_GEOTAG_VER), #base_geotag_header.ver
    357                     ByteField('geotag_pad', 0), #base_geotag_header.pad
    358                     LEShortField('geotag_len', None)] + _HCSIBuildFields(GPS_Fields)
    359 
    360 
    361 #Vector Fields
    362 VEC_Fields = [VectorFlags_Field("VectorFlags", None),
    363               FlagsField("VectorChars", None, -32, _hcsi_vector_char_flags),
    364               Fixed3_6Field("Pitch", None),       Fixed3_6Field("Roll", None),
    365               Fixed3_6Field("Heading", None),     Fixed6_4Field("Off_X", None),
    366               Fixed6_4Field("Off_Y", None),       Fixed6_4Field("Off_Z", None),
    367               HCSINullField("Reserved08", None),  HCSINullField("Reserved09", None),
    368               HCSINullField("Reserved10", None),  HCSINullField("Reserved11", None),
    369               HCSINullField("Reserved12", None),  HCSINullField("Reserved13", None),
    370               HCSINullField("Reserved14", None),  HCSINullField("Reserved15", None),
    371               Fixed3_6Field("Err_Rot", None),     Fixed6_4Field("Err_Off", None),
    372               HCSINullField("Reserved18", None),  HCSINullField("Reserved19", None),
    373               HCSINullField("Reserved20", None),  HCSINullField("Reserved21", None),
    374               HCSINullField("Reserved22", None),  HCSINullField("Reserved23", None),
    375               HCSINullField("Reserved24", None),  HCSINullField("Reserved25", None),
    376               HCSINullField("Reserved26", None),  HCSINullField("Reserved27", None),
    377               HCSIDescField("DescString", None),  XLEIntField("AppId", None),
    378               HCSIAppField("AppData", None),      HCSINullField("Extended", None)]
    379 
    380 class Vector(HCSIPacket):
    381     name = "PPI Vector"
    382     fields_desc = [ LEShortField('pfh_type', PPI_VECTOR), #pfh_type
    383                     LEShortField('pfh_length', None), #pfh_len
    384                     ByteField('geotag_ver', CURR_GEOTAG_VER), #base_geotag_header.ver
    385                     ByteField('geotag_pad', 0), #base_geotag_header.pad
    386                     LEShortField('geotag_len', None)] + _HCSIBuildFields(VEC_Fields)
    387 
    388 #Sensor Fields
    389 # http://www.iana.org/assignments/icmp-parameters
    390 sensor_types= { 1   : "Velocity",
    391                 2   : "Acceleration",
    392                 3   : "Jerk",
    393                 100 : "Rotation",
    394                 101 : "Magnetic",
    395                 1000: "Temperature",
    396                 1001: "Barometer",
    397                 1002: "Humidity",
    398                 2000: "TDOA_Clock",
    399                 2001: "Phase"
    400                 }
    401 SENS_Fields = [  LEShortEnumField('SensorType', None, sensor_types),
    402                  SignedByteField('ScaleFactor', None),
    403                  Fixed6_4Field('Val_X', None),
    404                  Fixed6_4Field('Val_Y', None),
    405                  Fixed6_4Field('Val_Z', None),
    406                  Fixed6_4Field('Val_T', None),
    407                  Fixed6_4Field('Val_E', None),
    408               HCSINullField("Reserved07", None),  HCSINullField("Reserved08", None),
    409               HCSINullField("Reserved09", None),  HCSINullField("Reserved10", None),
    410               HCSINullField("Reserved11", None),  HCSINullField("Reserved12", None),
    411               HCSINullField("Reserved13", None),  HCSINullField("Reserved14", None),
    412               HCSINullField("Reserved15", None),  HCSINullField("Reserved16", None),
    413               HCSINullField("Reserved17", None),  HCSINullField("Reserved18", None),
    414               HCSINullField("Reserved19", None),  HCSINullField("Reserved20", None),
    415               HCSINullField("Reserved21", None),  HCSINullField("Reserved22", None),
    416               HCSINullField("Reserved23", None),  HCSINullField("Reserved24", None),
    417               HCSINullField("Reserved25", None),  HCSINullField("Reserved26", None),
    418               HCSINullField("Reserved27", None),
    419               HCSIDescField("DescString", None),  XLEIntField("AppId", None),
    420               HCSIAppField("AppData", None),      HCSINullField("Extended", None)]
    421 
    422               
    423 
    424 class Sensor(HCSIPacket):
    425     name = "PPI Sensor"
    426     fields_desc = [ LEShortField('pfh_type', PPI_SENSOR), #pfh_type
    427                     LEShortField('pfh_length', None), #pfh_len
    428                     ByteField('geotag_ver', CURR_GEOTAG_VER ), #base_geotag_header.ver
    429                     ByteField('geotag_pad', 0), #base_geotag_header.pad
    430                     LEShortField('geotag_len', None)] + _HCSIBuildFields(SENS_Fields)
    431 
    432 # HCSIAntenna Fields
    433 ANT_Fields = [FlagsField("AntennaFlags", None, -32, _hcsi_antenna_flags),
    434               ByteField("Gain", None),
    435               Fixed3_6Field("HorizBw", None),              Fixed3_6Field("VertBw", None),
    436               Fixed3_6Field("PrecisionGain",None),         XLEShortField("BeamID", None),
    437               HCSINullField("Reserved06", None),           HCSINullField("Reserved07", None),
    438               HCSINullField("Reserved08", None),           HCSINullField("Reserved09", None),
    439               HCSINullField("Reserved10", None),           HCSINullField("Reserved11", None),
    440               HCSINullField("Reserved12", None),           HCSINullField("Reserved13", None),
    441               HCSINullField("Reserved14", None),           HCSINullField("Reserved15", None),
    442               HCSINullField("Reserved16", None),           HCSINullField("Reserved17", None),
    443               HCSINullField("Reserved18", None),           HCSINullField("Reserved19", None),
    444               HCSINullField("Reserved20", None),           HCSINullField("Reserved21", None),
    445               HCSINullField("Reserved22", None),           HCSINullField("Reserved23", None),
    446               HCSINullField("Reserved24", None),           HCSINullField("Reserved25", None),
    447               HCSIDescField("SerialNumber", None),         HCSIDescField("ModelName", None),
    448               HCSIDescField("DescString", None),           XLEIntField("AppId", None),
    449               HCSIAppField("AppData", None),               HCSINullField("Extended", None)]
    450 
    451 class Antenna(HCSIPacket):
    452     name = "PPI Antenna"
    453     fields_desc = [ LEShortField('pfh_type', PPI_ANTENNA), #pfh_type
    454                     LEShortField('pfh_length', None), #pfh_len
    455                     ByteField('geotag_ver', CURR_GEOTAG_VER), #base_geotag_header.ver
    456                     ByteField('geotag_pad', 0), #base_geotag_header.pad
    457                     LEShortField('geotag_len', None)] + _HCSIBuildFields(ANT_Fields)
    458 
    459 addPPIType(PPI_GPS, GPS)
    460 addPPIType(PPI_VECTOR, Vector)
    461 addPPIType(PPI_SENSOR, Sensor)
    462 addPPIType(PPI_ANTENNA,Antenna)
    463