Home | History | Annotate | Download | only in contrib
      1 #! /usr/bin/env python
      2 
      3 # This file is part of Scapy
      4 # Scapy is free software: you can redistribute it and/or modify
      5 # it under the terms of the GNU General Public License as published by
      6 # the Free Software Foundation, either version 2 of the License, or
      7 # any later version.
      8 #
      9 # Scapy is distributed in the hope that it will be useful,
     10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
     11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     12 # GNU General Public License for more details.
     13 #
     14 # You should have received a copy of the GNU General Public License
     15 # along with Scapy. If not, see <http://www.gnu.org/licenses/>.
     16 
     17 # scapy.contrib.description = IGMPv3
     18 # scapy.contrib.status = loads
     19 
     20 from __future__ import print_function
     21 from scapy.packet import *
     22 from scapy.fields import *
     23 from scapy.compat import orb
     24 from scapy.layers.inet import *
     25 from scapy.contrib.igmp import IGMP
     26 
     27 """ Based on the following references
     28  http://www.iana.org/assignments/igmp-type-numbers
     29  http://www.rfc-editor.org/rfc/pdfrfc/rfc3376.txt.pdf
     30 
     31 """
     32 
     33 # See RFC3376, Section 4. Message Formats for definitions of proper IGMPv3 message format
     34 #   http://www.faqs.org/rfcs/rfc3376.html
     35 #
     36 # See RFC4286, For definitions of proper messages for Multicast Router Discovery.
     37 #   http://www.faqs.org/rfcs/rfc4286.html
     38 #
     39 
     40 class IGMPv3(IGMP):
     41     """IGMP Message Class for v3.
     42 
     43     This class is derived from class Packet. 
     44     The fields defined below are a 
     45     direct interpretation of the v3 Membership Query Message. 
     46     Fields 'type'  through 'qqic' are directly assignable. 
     47     For 'numsrc', do not assign a value. 
     48     Instead add to the 'srcaddrs' list to auto-set 'numsrc'. To 
     49     assign values to 'srcaddrs', use the following methods:
     50       c = IGMPv3()
     51       c.srcaddrs = ['1.2.3.4', '5.6.7.8']
     52       c.srcaddrs += ['192.168.10.24']
     53     At this point, 'c.numsrc' is three (3)
     54 
     55     'chksum' is automagically calculated before the packet is sent.
     56 
     57     'mrcode' is also the Advertisement Interval field
     58 
     59     """
     60     name = "IGMPv3"
     61     igmpv3types = { 0x11 : "Membership Query",
     62                     0x22 : "Version 3 Membership Report",
     63                     0x30 : "Multicast Router Advertisement",
     64                     0x31 : "Multicast Router Solicitation",
     65                     0x32 : "Multicast Router Termination"}
     66 
     67     fields_desc = [ ByteEnumField("type", 0x11, igmpv3types),
     68                     ByteField("mrcode", 20),
     69                     XShortField("chksum", None)]
     70 
     71     def encode_maxrespcode(self):
     72         """Encode and replace the mrcode value to its IGMPv3 encoded time value if needed,
     73         as specified in rfc3376#section-4.1.1.
     74 
     75         If value < 128, return the value specified. If >= 128, encode as a floating 
     76         point value. Value can be 0 - 31744.
     77         """
     78         value = self.mrcode
     79         if value < 128:
     80             code = value
     81         elif value > 31743:
     82             code = 255
     83         else:
     84             exp=0
     85             value>>=3
     86             while(value>31):
     87                 exp+=1
     88                 value>>=1
     89             exp<<=4
     90             code = 0x80 | exp | (value & 0x0F)
     91         self.mrcode = code
     92 
     93 
     94     def mysummary(self):
     95         """Display a summary of the IGMPv3 object."""
     96         if isinstance(self.underlayer, IP):
     97             return self.underlayer.sprintf("IGMPv3: %IP.src% > %IP.dst% %IGMPv3.type%")
     98         else:
     99             return self.sprintf("IGMPv3 %IGMPv3.type%")
    100 
    101     @classmethod
    102     def dispatch_hook(cls, _pkt=None, *args, **kargs):
    103         if _pkt and len(_pkt) >= 4:
    104             if orb(_pkt[0]) in [0x12, 0x16, 0x17]:
    105                 return IGMP
    106             elif orb(_pkt[0]) == 0x11 and len(_pkt) < 12:
    107                 return IGMP
    108         return IGMPv3
    109 
    110 class IGMPv3mq(Packet):
    111     """IGMPv3 Membership Query.
    112     Payload of IGMPv3 when type=0x11"""
    113     name = "IGMPv3mq"
    114     fields_desc = [ IPField("gaddr", "0.0.0.0"),
    115                     BitField("resv", 0, 4),
    116                     BitField("s", 0, 1),
    117                     BitField("qrv", 0, 3),
    118                     ByteField("qqic",0),
    119                     FieldLenField("numsrc", None, count_of="srcaddrs"),
    120                     FieldListField("srcaddrs", None, IPField("sa", "0.0.0.0"), count_from=lambda x: x.numsrc)]
    121 
    122 class IGMPv3gr(Packet):
    123     """IGMP Group Record for IGMPv3 Membership Report
    124 
    125     This class is derived from class Packet and should be added in the records
    126     of an instantiation of class IGMPv3mr.
    127     """
    128     name = "IGMPv3gr"
    129     igmpv3grtypes = { 1 : "Mode Is Include",
    130                       2 : "Mode Is Exclude",
    131                       3 : "Change To Include Mode",
    132                       4 : "Change To Exclude Mode",
    133                       5 : "Allow New Sources",
    134                       6 : "Block Old Sources"}
    135 
    136     fields_desc = [ ByteEnumField("rtype", 1, igmpv3grtypes),
    137                     ByteField("auxdlen",0),
    138                     FieldLenField("numsrc", None, count_of="srcaddrs"),
    139                     IPField("maddr", "0.0.0.0"),
    140                     FieldListField("srcaddrs", [], IPField("sa", "0.0.0.0"), count_from=lambda x: x.numsrc) ]
    141 
    142     def mysummary(self):
    143         """Display a summary of the IGMPv3 group record."""
    144         return self.sprintf("IGMPv3 Group Record %IGMPv3gr.type% %IGMPv3gr.maddr%")
    145 
    146     def default_payload_class(self, payload):
    147         return conf.padding_layer
    148 
    149 class IGMPv3mr(Packet):
    150     """IGMP Membership Report extension for IGMPv3.
    151     Payload of IGMPv3 when type=0x22"""
    152     name = "IGMPv3mr"
    153     fields_desc = [ XShortField("res2", 0),
    154                     FieldLenField("numgrp", None, count_of="records"),
    155                     PacketListField("records", [], IGMPv3gr, count_from=lambda x: x.numgrp)]
    156 
    157 class IGMPv3mra(Packet):
    158     """IGMP Multicas Router Advertisement extension for IGMPv3.
    159     Payload of IGMPv3 when type=0x30"""
    160     name = "IGMPv3mra"
    161     fields_desc = [ ShortField("qryIntvl", 0),
    162                     ShortField("robust", 0)]
    163 
    164 bind_layers(IP,       IGMPv3,   frag=0,
    165                                 proto=2,
    166                                 ttl=1,
    167                                 tos=0xc0,
    168                                 dst='224.0.0.22')
    169 
    170 bind_layers(IGMPv3,   IGMPv3mq, type=0x11)
    171 bind_layers(IGMPv3,   IGMPv3mr, type=0x22, mrcode=0x0)
    172 bind_layers(IGMPv3,   IGMPv3mra, type=0x30)
    173