summaryrefslogtreecommitdiffstats
path: root/scripts/external_libs/scapy-2.3.1/python2/scapy/contrib/igmpv3.py
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/external_libs/scapy-2.3.1/python2/scapy/contrib/igmpv3.py')
-rw-r--r--scripts/external_libs/scapy-2.3.1/python2/scapy/contrib/igmpv3.py270
1 files changed, 270 insertions, 0 deletions
diff --git a/scripts/external_libs/scapy-2.3.1/python2/scapy/contrib/igmpv3.py b/scripts/external_libs/scapy-2.3.1/python2/scapy/contrib/igmpv3.py
new file mode 100644
index 00000000..1ab1bae4
--- /dev/null
+++ b/scripts/external_libs/scapy-2.3.1/python2/scapy/contrib/igmpv3.py
@@ -0,0 +1,270 @@
+#! /usr/bin/env python
+
+# http://trac.secdev.org/scapy/ticket/31
+
+# scapy.contrib.description = IGMPv3
+# scapy.contrib.status = loads
+
+from scapy.packet import *
+
+""" Based on the following references
+ http://www.iana.org/assignments/igmp-type-numbers
+ http://www.rfc-editor.org/rfc/pdfrfc/rfc3376.txt.pdf
+
+"""
+
+# TODO: Merge IGMPv3 packet Bindlayers correct for
+# membership source/Group records
+# ConditionalField parameters for IGMPv3 commented out
+#
+# See RFC3376, Section 4. Message Formats for definitions of proper IGMPv3 message format
+# http://www.faqs.org/rfcs/rfc3376.html
+#
+# See RFC4286, For definitions of proper messages for Multicast Router Discovery.
+# http://www.faqs.org/rfcs/rfc4286.html
+#
+
+#import sys, socket, struct, time
+from scapy.all import *
+print "IGMPv3 is still under development - Nov 2010"
+
+
+class IGMPv3gr(Packet):
+ """IGMP Group Record for IGMPv3 Membership Report
+
+ This class is derived from class Packet and should be concatenated to an
+ instantiation of class IGMPv3. Within the IGMPv3 instantiation, the numgrp
+ element will need to be manipulated to indicate the proper number of
+ group records.
+ """
+ name = "IGMPv3gr"
+ igmpv3grtypes = { 1 : "Mode Is Include",
+ 2 : "Mode Is Exclude",
+ 3 : "Change To Include Mode",
+ 4 : "Change To Exclude Mode",
+ 5 : "Allow New Sources",
+ 6 : "Block Old Sources"}
+
+ fields_desc = [ ByteEnumField("rtype", 1, igmpv3grtypes),
+ ByteField("auxdlen",0),
+ FieldLenField("numsrc", None, "srcaddrs"),
+ IPField("maddr", "0.0.0.0"),
+ FieldListField("srcaddrs", None, IPField("sa", "0.0.0.0"), "numsrc") ]
+ #show_indent=0
+#--------------------------------------------------------------------------
+ def post_build(self, p, pay):
+ """Called implicitly before a packet is sent.
+ """
+ p += pay
+ if self.auxdlen != 0:
+ print "NOTICE: A properly formatted and complaint V3 Group Record should have an Auxiliary Data length of zero (0)."
+ print " Subsequent Group Records are lost!"
+ return p
+#--------------------------------------------------------------------------
+ def mysummary(self):
+ """Display a summary of the IGMPv3 group record."""
+ return self.sprintf("IGMPv3 Group Record %IGMPv3gr.type% %IGMPv3gr.maddr%")
+
+
+class IGMPv3(Packet):
+ """IGMP Message Class for v3.
+
+ This class is derived from class Packet.
+ The fields defined below are a
+ direct interpretation of the v3 Membership Query Message.
+ Fields 'type' through 'qqic' are directly assignable.
+ For 'numsrc', do not assign a value.
+ Instead add to the 'srcaddrs' list to auto-set 'numsrc'. To
+ assign values to 'srcaddrs', use the following methods:
+ c = IGMPv3()
+ c.srcaddrs = ['1.2.3.4', '5.6.7.8']
+ c.srcaddrs += ['192.168.10.24']
+ At this point, 'c.numsrc' is three (3)
+
+ 'chksum' is automagically calculated before the packet is sent.
+
+ 'mrcode' is also the Advertisement Interval field
+
+ """
+ name = "IGMPv3"
+ igmpv3types = { 0x11 : "Membership Query",
+ 0x22 : "Version 3 Membership Report",
+ 0x30 : "Multicast Router Advertisement",
+ 0x31 : "Multicast Router Solicitation",
+ 0x32 : "Multicast Router Termination"}
+
+ fields_desc = [ ByteEnumField("type", 0x11, igmpv3types),
+ ByteField("mrcode",0),
+ XShortField("chksum", None),
+ IPField("gaddr", "0.0.0.0")
+ ]
+ # use float_encode()
+
+ # if type = 0x11 (Membership Query), the next field is group address
+ # ConditionalField(IPField("gaddr", "0.0.0.0"), "type", lambda x:x==0x11),
+ # else if type = 0x22 (Membership Report), the next fields are
+ # reserved and number of group records
+ #ConditionalField(ShortField("rsvd2", 0), "type", lambda x:x==0x22),
+ #ConditionalField(ShortField("numgrp", 0), "type", lambda x:x==0x22),
+# FieldLenField("numgrp", None, "grprecs")]
+ # else if type = 0x30 (Multicast Router Advertisement), the next fields are
+ # query interval and robustness
+ #ConditionalField(ShortField("qryIntvl", 0), "type", lambda x:x==0x30),
+ #ConditionalField(ShortField("robust", 0), "type", lambda x:x==0x30),
+# The following are only present for membership queries
+ # ConditionalField(BitField("resv", 0, 4), "type", lambda x:x==0x11),
+ # ConditionalField(BitField("s", 0, 1), "type", lambda x:x==0x11),
+ # ConditionalField(BitField("qrv", 0, 3), "type", lambda x:x==0x11),
+ # ConditionalField(ByteField("qqic",0), "type", lambda x:x==0x11),
+ # ConditionalField(FieldLenField("numsrc", None, "srcaddrs"), "type", lambda x:x==0x11),
+ # ConditionalField(FieldListField("srcaddrs", None, IPField("sa", "0.0.0.0"), "numsrc"), "type", lambda x:x==0x11),
+
+#--------------------------------------------------------------------------
+ def float_encode(self, value):
+ """Convert the integer value to its IGMPv3 encoded time value if needed.
+
+ If value < 128, return the value specified. If >= 128, encode as a floating
+ point value. Value can be 0 - 31744.
+ """
+ if value < 128:
+ code = value
+ elif value > 31743:
+ code = 255
+ else:
+ exp=0
+ value>>=3
+ while(value>31):
+ exp+=1
+ value>>=1
+ exp<<=4
+ code = 0x80 | exp | (value & 0x0F)
+ return code
+
+#--------------------------------------------------------------------------
+ def post_build(self, p, pay):
+ """Called implicitly before a packet is sent to compute and place IGMPv3 checksum.
+
+ Parameters:
+ self The instantiation of an IGMPv3 class
+ p The IGMPv3 message in hex in network byte order
+ pay Additional payload for the IGMPv3 message
+ """
+ p += pay
+ if self.type in [0, 0x31, 0x32, 0x22]: # for these, field is reserved (0)
+ p = p[:1]+chr(0)+p[2:]
+ if self.chksum is None:
+ ck = checksum(p)
+ p = p[:2]+chr(ck>>8)+chr(ck&0xff)+p[4:]
+ return p
+
+#--------------------------------------------------------------------------
+ def mysummary(self):
+ """Display a summary of the IGMPv3 object."""
+
+ if isinstance(self.underlayer, IP):
+ return self.underlayer.sprintf("IGMPv3: %IP.src% > %IP.dst% %IGMPv3.type% %IGMPv3.gaddr%")
+ else:
+ return self.sprintf("IGMPv3 %IGMPv3.type% %IGMPv3.gaddr%")
+
+#--------------------------------------------------------------------------
+ def igmpize(self, ip=None, ether=None):
+ """Called to explicitely fixup associated IP and Ethernet headers
+
+ Parameters:
+ self The instantiation of an IGMP class.
+ ip The instantiation of the associated IP class.
+ ether The instantiation of the associated Ethernet.
+
+ Returns:
+ True The tuple ether/ip/self passed all check and represents
+ a proper IGMP packet.
+ False One of more validation checks failed and no fields
+ were adjusted.
+
+ The function will examine the IGMP message to assure proper format.
+ Corrections will be attempted if possible. The IP header is then properly
+ adjusted to ensure correct formatting and assignment. The Ethernet header
+ is then adjusted to the proper IGMP packet format.
+ """
+
+# The rules are:
+# 1. ttl = 1 (RFC 2236, section 2)
+# igmp_binds = [ (IP, IGMP, { "proto": 2 , "ttl": 1 }),
+# 2. tos = 0xC0 (RFC 3376, section 4)
+# (IP, IGMPv3, { "proto": 2 , "ttl": 1, "tos":0xc0 }),
+# (IGMPv3, IGMPv3gr, { }) ]
+# The rules are:
+# 1. the Max Response time is meaningful only in Membership Queries and should be zero
+# otherwise (RFC 2236, section 2.2)
+
+ if (self.type != 0x11): #rule 1
+ self.mrtime = 0
+
+ if (self.adjust_ip(ip) == True):
+ if (self.adjust_ether(ip, ether) == True): return True
+ return False
+
+#--------------------------------------------------------------------------
+ def adjust_ether (self, ip=None, ether=None):
+ """Called to explicitely fixup an associated Ethernet header
+
+ The function adjusts the ethernet header destination MAC address based on
+ the destination IP address.
+ """
+# The rules are:
+# 1. send to the group mac address address corresponding to the IP.dst
+ if ip != None and ip.haslayer(IP) and ether != None and ether.haslayer(Ether):
+ iplong = atol(ip.dst)
+ ether.dst = "01:00:5e:%02x:%02x:%02x" % ( (iplong>>16)&0x7F, (iplong>>8)&0xFF, (iplong)&0xFF )
+ # print "igmpize ip " + ip.dst + " as mac " + ether.dst
+ return True
+ else:
+ return False
+
+#--------------------------------------------------------------------------
+ def adjust_ip (self, ip=None):
+ """Called to explicitely fixup an associated IP header
+
+ The function adjusts the IP header based on conformance rules
+ and the group address encoded in the IGMP message.
+ The rules are:
+ 1. Send General Group Query to 224.0.0.1 (all systems)
+ 2. Send Leave Group to 224.0.0.2 (all routers)
+ 3a.Otherwise send the packet to the group address
+ 3b.Send reports/joins to the group address
+ 4. ttl = 1 (RFC 2236, section 2)
+ 5. send the packet with the router alert IP option (RFC 2236, section 2)
+ """
+ if ip != None and ip.haslayer(IP):
+ if (self.type == 0x11):
+ if (self.gaddr == "0.0.0.0"):
+ ip.dst = "224.0.0.1" # IP rule 1
+ retCode = True
+ elif isValidMCAddr(self.gaddr):
+ ip.dst = self.gaddr # IP rule 3a
+ retCode = True
+ else:
+ print "Warning: Using invalid Group Address"
+ retCode = False
+ elif ((self.type == 0x17) and isValidMCAddr(self.gaddr)):
+ ip.dst = "224.0.0.2" # IP rule 2
+ retCode = True
+ elif ((self.type == 0x12) or (self.type == 0x16)) and (isValidMCAddr(self.gaddr)):
+ ip.dst = self.gaddr # IP rule 3b
+ retCode = True
+ else:
+ print "Warning: Using invalid IGMP Type"
+ retCode = False
+ else:
+ print "Warning: No IGMP Group Address set"
+ retCode = False
+ if retCode == True:
+ ip.ttl=1 # IP Rule 4
+ ip.options=[IPOption_Router_Alert()] # IP rule 5
+ return retCode
+
+
+bind_layers( IP, IGMPv3, frag=0, proto=2, ttl=1, tos=0xc0)
+bind_layers( IGMPv3, IGMPv3gr, frag=0, proto=2)
+bind_layers( IGMPv3gr, IGMPv3gr, frag=0, proto=2)
+