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