def test_igmpv3_encode_kv(self): # Create reports using the new syntax. #c = igmp(type=IGMP_v3_HOST_MEMBERSHIP_REPORT) / \ c = igmp() / \ igmpv3.report(records=[GroupRecordField("", \ group=inet_atol("239.0.1.2"), \ type=IGMP_CHANGE_TO_INCLUDE), GroupRecordField("", \ group=inet_atol("239.3.2.1"), \ type=IGMP_CHANGE_TO_INCLUDE, \ sources=[inet_atol("192.0.2.1")]), \ GroupRecordField("", \ group=inet_atol("224.111.222.111"), \ type=IGMP_CHANGE_TO_EXCLUDE), \ GroupRecordField("", \ group=inet_atol("225.4.3.2"), \ type=IGMP_BLOCK_OLD_SOURCES, \ sources=[inet_atol("192.0.2.99")])]) c.calc_lengths() c.calc_checksums() c.encode() #hd = hexdumper() #print hd.dump2(c.bytes) expected = "\x22\x00\xC5\xA5\x00\x00\x00\x04" \ "\x03\x00\x00\x00\xEF\x00\x01\x02" \ "\x03\x00\x00\x01\xEF\x03\x02\x01" \ "\xC0\x00\x02\x01\x04\x00\x00\x00" \ "\xE0\x6F\xDE\x6F\x06\x00\x00\x01" \ "\xE1\x04\x03\x02\xC0\x00\x02\x63" #print hd.dump2(expected) gotttted = c.bytes self.assertEqual(expected, gotttted, "test encoding")
def main(): from optparse import OptionParser parser = OptionParser() parser.add_option("-I", "--ether_iface", dest="ether_iface", default=None, help="The name of the source interface.") parser.add_option("-e", "--ether_source", dest="ether_source", default=None, help="The host Ethernet source address.") parser.add_option("-s", "--ip_source", dest="ip_source", default=None, help="The IP source address.") parser.add_option("-G", "--igmp_group", dest="igmp_group", default=None, help="The IPv4 group to spoof an IGMPv3 leave for.") (options, args) = parser.parse_args() if options.ether_iface is None or \ options.ether_source is None or \ options.ip_source is None or \ options.igmp_group is None: print "Non-optional argument missing." return # Create an IGMPv3 change-to-include report for the given group # with no sources, which means we're leaving the group. # IGMPv3 Host Reports are always sent to IGMP.MCAST.NET (224.0.0.22), # and must always contain the Router Alert option. c = ethernet(src=ether_atob(options.ether_source), \ dst=ETHER_MAP_IP_MULTICAST(INADDR_ALLRPTS_GROUP)) / \ ipv4(src=inet_atol(options.ip_source), dst=INADDR_ALLRPTS_GROUP, \ ttl=1, flags=IP_DF, options=[ipvt4opt(IPOPT_RA)]) / \ igmp(type=IGMP_v3_HOST_MEMBERSHIP_REPORT) / \ igmpv3.report(records=[GroupRecordField("", \ group=inet_atol(options.igmp_group), \ type=IGMP_CHANGE_TO_INCLUDE)]) c.fixup() # Send it. output = PcapConnector(options.ether_iface) out = output.write(c.bytes, len(c.bytes))
def main(): from optparse import OptionParser parser = OptionParser() parser.add_option("-I", "--ether_iface", dest="ether_iface", default=None, help="The name of the source interface.") parser.add_option("-e", "--ether_source", dest="ether_source", default=None, help="The host Ethernet source address.") parser.add_option("-s", "--ip_source", dest="ip_source", default=None, help="The IP source address.") parser.add_option("-G", "--igmp_group", dest="igmp_group", default=None, help="The IPv4 group to spoof a leave message for.") parser.add_option("-A", "--no-router-alert", action="store_true", dest="no_ra", help="Disable the use of the IP Router Alert option.") (options, args) = parser.parse_args() if options.igmp_group is None or \ options.ether_source is None or \ options.ether_iface is None or \ options.ip_source is None: print "A required argument is missing." return output = PcapConnector(options.ether_iface) c = ethernet(src=ether_atob(options.ether_source), \ dst=ETHER_MAP_IP_MULTICAST(INADDR_ALLRTRS_GROUP)) / \ ipv4(flags=IP_DF, id=123, ttl=1, \ src=inet_atol(options.ip_source), \ dst=INADDR_ALLRTRS_GROUP) / \ igmp(type=IGMP_HOST_LEAVE_MESSAGE) / \ igmpv2(group=inet_atol(options.igmp_group)) c.fixup() out = output.write(c.bytes, len(c.bytes))
def test_igmpv3_encode(self): # create one packet, copy its bytes, then compare their fields. c = igmp(type=IGMP_v3_HOST_MEMBERSHIP_REPORT) / igmpv3.report() rep = c.packets[1] # An ASM/SSM leave. rec0 = GroupRecordField("rec0") rec0.type.value = IGMP_CHANGE_TO_INCLUDE rec0.group.value = inet_atol("239.0.1.2") rep.records.append(rec0) # An SSM join. rec1 = GroupRecordField("rec1") rec1.type.value = IGMP_CHANGE_TO_INCLUDE rec1.group.value = inet_atol("239.3.2.1") rec1.sources.append(pcs.Field("", 32, default = inet_atol("192.0.2.1"))) rec1.nsources.value = len(rec1.sources) rep.records.append(rec1) # An ASM join. rec2 = GroupRecordField("rec2") rec2.type.value = IGMP_CHANGE_TO_EXCLUDE rec2.group.value = inet_atol("224.111.222.111") rep.records.append(rec2) # An ASM filter change. # XXX I can't get auxiliary data embedding to work reliably, # this seems to be because the syntactic sugar for OptionListField # makes it difficult to retrieve the size of the embedded fields. rec3 = GroupRecordField("rec3") rec3.type.value = IGMP_BLOCK_OLD_SOURCES rec3.group.value = inet_atol("225.4.3.2") rec3.sources.append(pcs.Field("", 32, default = inet_atol("192.0.2.99"))) rec3.nsources.value = len(rec3.sources) rep.records.append(rec3) rep.nrecords = len(rep.records) c.calc_checksums() c.encode() #hd = hexdumper() #print hd.dump2(c.bytes) expected = "\x22\x00\xC5\xA5\x00\x00\x00\x04" \ "\x03\x00\x00\x00\xEF\x00\x01\x02" \ "\x03\x00\x00\x01\xEF\x03\x02\x01" \ "\xC0\x00\x02\x01\x04\x00\x00\x00" \ "\xE0\x6F\xDE\x6F\x06\x00\x00\x01" \ "\xE1\x04\x03\x02\xC0\x00\x02\x63" #print hd.dump2(expected) gotttted = c.bytes self.assertEqual(expected, gotttted, "test encoding")
def test_igmpv3_decode(self): # Try decoding the same packet as above, see what we get. input = "\x22\x00\xC5\xA5\x00\x00\x00\x04" \ "\x03\x00\x00\x00\xEF\x00\x01\x02" \ "\x03\x00\x00\x01\xEF\x03\x02\x01" \ "\xC0\x00\x02\x01\x04\x00\x00\x00" \ "\xE0\x6F\xDE\x6F\x06\x00\x00\x01" \ "\xE1\x04\x03\x02\xC0\x00\x02\x63" igh = igmp(input) self.assertEqual(input, igh.chain().bytes, "test decoding")
def test_igmpv3_encode(self): # create one packet, copy its bytes, then compare their fields. c = igmp(type=IGMP_v3_HOST_MEMBERSHIP_REPORT) / igmpv3.report() rep = c.packets[1] # An ASM/SSM leave. rec0 = GroupRecordField("rec0") rec0.type.value = IGMP_CHANGE_TO_INCLUDE rec0.group.value = inet_atol("239.0.1.2") rep.records.append(rec0) # An SSM join. rec1 = GroupRecordField("rec1") rec1.type.value = IGMP_CHANGE_TO_INCLUDE rec1.group.value = inet_atol("239.3.2.1") rec1.sources.append(pcs.Field("", 32, default=inet_atol("192.0.2.1"))) rec1.nsources.value = len(rec1.sources) rep.records.append(rec1) # An ASM join. rec2 = GroupRecordField("rec2") rec2.type.value = IGMP_CHANGE_TO_EXCLUDE rec2.group.value = inet_atol("224.111.222.111") rep.records.append(rec2) # An ASM filter change. # XXX I can't get auxiliary data embedding to work reliably, # this seems to be because the syntactic sugar for OptionListField # makes it difficult to retrieve the size of the embedded fields. rec3 = GroupRecordField("rec3") rec3.type.value = IGMP_BLOCK_OLD_SOURCES rec3.group.value = inet_atol("225.4.3.2") rec3.sources.append(pcs.Field("", 32, default=inet_atol("192.0.2.99"))) rec3.nsources.value = len(rec3.sources) rep.records.append(rec3) rep.nrecords = len(rep.records) c.calc_checksums() c.encode() #hd = hexdumper() #print hd.dump2(c.bytes) expected = "\x22\x00\xC5\xA5\x00\x00\x00\x04" \ "\x03\x00\x00\x00\xEF\x00\x01\x02" \ "\x03\x00\x00\x01\xEF\x03\x02\x01" \ "\xC0\x00\x02\x01\x04\x00\x00\x00" \ "\xE0\x6F\xDE\x6F\x06\x00\x00\x01" \ "\xE1\x04\x03\x02\xC0\x00\x02\x63" #print hd.dump2(expected) gotttted = c.bytes self.assertEqual(expected, gotttted, "test encoding")
def main(): from optparse import OptionParser parser = OptionParser() parser.add_option("-I", "--ether_iface", dest="ether_iface", default=None, help="The name of the source interface.") parser.add_option("-e", "--ether_source", dest="ether_source", default=None, help="The host Ethernet source address.") parser.add_option("-s", "--ip_source", dest="ip_source", default=None, help="The IP source address.") parser.add_option("-G", "--igmp_group", dest="igmp_group", default=None, help="The IPv4 group for a group-specific query. " "If omitted, send a general query.") parser.add_option("-S", "--seconds", dest="seconds", default=1.0, help="Number of seconds between packets.") parser.add_option("-c", "--count", dest="count", default=None, help="Stop after sending count packets.") (options, args) = parser.parse_args() if options.ether_iface is None or \ options.ether_source is None or \ options.ip_source is None or \ options.count is None: print "Non-optional argument missing." return maxresp = 3 * 10 if options.igmp_group is None: # General query. dst = INADDR_ALLHOSTS_GROUP group = INADDR_ANY else: # Group-specific query. dst = inet_atol(options.igmp_group) group = dst # Queries don't contain the Router Alert option as they are # destined for end stations, not routers. c = ethernet(src=ether_atob(options.ether_source), \ dst=ETHER_MAP_IP_MULTICAST(dst)) / \ ipv4(flags=IP_DF, ttl=1, \ src=inet_atol(options.ip_source), \ dst=dst) / \ igmp(type=IGMP_v2_HOST_MEMBERSHIP_REPORT, code=maxresp) / \ igmpv2(group=group) c.fixup() output = PcapConnector(options.ether_iface) # # Send count packets, delayed by seconds # count = int(options.count) if count < 0: count = sys.maxint while count > 0: out = output.write(c.bytes, len(c.bytes)) count -= 1 sleep(float(options.seconds))
def main(): from optparse import OptionParser parser = OptionParser() parser.add_option("-I", "--ether_iface", dest="ether_iface", default=None, help="The name of the source interface.") parser.add_option("-e", "--ether_source", dest="ether_source", default=None, help="The host Ethernet source address.") parser.add_option("-g", "--ether_dest", dest="ether_dest", default=None, help="The host Ethernet destination address.") parser.add_option("-s", "--ip_source", dest="ip_source", default=None, help="The IP source address.") parser.add_option("-D", "--ip_dest", dest="ip_dest", default=None, help="The IP destination address.") parser.add_option("-G", "--igmp_group", dest="igmp_group", default=None, help="The IPv4 group to query.") parser.add_option("-S", "--igmp_sources", dest="igmp_sources", default=None, help="Comma-delimited list of IPv4 sources for " "a group-and-source specific query.") parser.add_option("-M", "--igmp_maxresp", dest="igmp_maxresp", default=None, help="The maximum time for end-stations to " "respond (in seconds).") parser.add_option("-c", "--count", dest="count", default=None, help="Stop after receiving at least count responses.") parser.add_option("-2", "--igmp_v2_listen", action="store_true", dest="igmp_v2_listen", help="Listen for responses from IGMPv2 end-stations.") parser.add_option("-R", "--igmp_robustness", dest="igmp_robustness", default=None, help="Querier Robustness (default 2)") parser.add_option("-Q", "--igmp_qqic", dest="igmp_qqic", default=None, help="Querier's Query Interval (default 10s)") (options, args) = parser.parse_args() if options.ether_iface is None or \ options.ether_source is None or \ options.ip_source is None or \ options.count is None: print "Non-optional argument missing." return #if options.ip_dest is not None and options.ether_dest is None: # print "Non-optional argument missing." # return maxresp = 10 * 10 if options.igmp_maxresp is not None: maxresp = int(options.igmp_maxresp) * 10 # in units of deciseconds # # Parse source list for a GSR query. # sources = [] if options.igmp_sources is not None: if options.igmp_group is None: raise "A group must be specified for a GSR query." else: for source in options.igmp_sources.split(','): sources.append(inet_atol(source)) if len(sources) == 0: raise "Error parsing source list." # Set up the vanilla packet if options.ether_dest is not None: edst = ether_atob(options.ether_dest) else: edst = ETHER_MAP_IP_MULTICAST(INADDR_ALLHOSTS_GROUP) c = ethernet(src=ether_atob(options.ether_source), dst=edst) / \ ipv4(flags=IP_DF, ttl=1, src=inet_atol(options.ip_source)) / \ igmp(type=IGMP_HOST_MEMBERSHIP_QUERY, code=maxresp) / \ igmpv3.query() ip = c.packets[1] q = c.packets[3] # IGMPv3 General Queries are always sent to ALL-SYSTEMS.MCAST.NET. # However we allow this to be overidden for protocol testing -- Windows, # in particular, doesn't seem to respond. # # We expect reports on 224.0.0.22. # Queries don't contain the Router Alert option as they are # destined for end stations, not routers. if options.igmp_robustness is not None: q.qrv = int(options.igmp_robustness) else: q.qrv = 2 # SHOULD NOT be 1, MUST NOT be 0 if options.igmp_qqic is not None: q.qqic = int(options.igmp_qqic) else: q.qqic = 10 # I query every 10 seconds if options.igmp_group is None: # General query. if options.ip_dest is not None: ip.dst = inet_atol(options.ip_dest) else: ip.dst = INADDR_ALLHOSTS_GROUP q.group = INADDR_ANY else: # Group-specific query, possibly with sources. if options.ip_dest is not None: ip.dst = inet_atol(options.ip_dest) else: ip.dst = inet_atol(options.igmp_group) q.group = ip.dst if IN_MULTICAST(ip.dst) is True and \ options.ether_dest is None: c.packets[0].dst = ETHER_MAP_IP_MULTICAST(ip.dst) for src in sources: q.sources.append(pcs.Field("", 32, default = src)) c.fixup() input = PcapConnector(options.ether_iface) input.setfilter("igmp") output = PcapConnector(options.ether_iface) out = output.write(c.bytes, len(c.bytes)) # # Wait for up to 'count' responses to the query to arrive and print them. # If options.igmp_v2_listen is True, also count responses from # end-stations which respond with IGMPv2. # # TODO: Pretty-print IGMPv3 reports. # count = int(options.count) while count > 0: packet = input.readpkt() chain = packet.chain() if ((chain.packets[2].type == IGMP_v3_HOST_MEMBERSHIP_REPORT) or ((chain.packets[2].type == IGMP_v2_HOST_MEMBERSHIP_REPORT) and \ (options.igmp_v2_listen is True))): version = 3 if chain.packets[2].type == IGMP_v2_HOST_MEMBERSHIP_REPORT: version = 2 #print chain.packets[2].println() print "%s responded to query with IGMPv%d." % \ ((inet_ntop(AF_INET, struct.pack('!L', chain.packets[1].src))), version) count -= 1
def main(): from optparse import OptionParser parser = OptionParser() parser.add_option("-I", "--ether_iface", dest="ether_iface", default=None, help="The name of the source interface.") parser.add_option("-e", "--ether_source", dest="ether_source", default=None, help="The host Ethernet source address.") parser.add_option("-s", "--ip_source", dest="ip_source", default=None, help="The IP source address.") parser.add_option("-G", "--igmp_group", dest="igmp_group", default=None, help="The IPv4 group for a group-specific query. " "If omitted, send a general query.") parser.add_option("-M", "--maxresp", dest="igmp_maxresp", default=None, help="The maximum time for end-stations to respond " "(in seconds).") parser.add_option("-c", "--count", dest="count", default=None, help="Stop after receiving at least count responses.") (options, args) = parser.parse_args() if options.ether_iface is None or \ options.ether_source is None or \ options.ip_source is None or \ options.count is None: print "Non-optional argument missing." return maxresp = 3 * 10 if options.igmp_maxresp is not None: maxresp = int(options.igmp_maxresp) * 10 # in units of deciseconds if options.igmp_group is None: # General query. dst = INADDR_ALLHOSTS_GROUP group = INADDR_ANY else: # Group-specific query. dst = inet_atol(options.igmp_group) group = dst # Queries don't contain the Router Alert option as they are # destined for end stations, not routers. c = ethernet(src=ether_atob(options.ether_source), \ dst=ETHER_MAP_IP_MULTICAST(dst)) / \ ipv4(flags=IP_DF, ttl=1, \ src=inet_atol(options.ip_source), \ dst=dst) / \ igmp(type=IGMP_HOST_MEMBERSHIP_QUERY, code=maxresp) / \ igmpv2(group=group) c.fixup() input = PcapConnector(options.ether_iface) input.setfilter("igmp") output = PcapConnector(options.ether_iface) out = output.write(c.bytes, len(c.bytes)) # # Wait for up to 'count' responses to the query to arrive and print them. # count = int(options.count) while count > 0: packet = input.readpkt() chain = packet.chain() if chain.packets[2].type == IGMP_v2_HOST_MEMBERSHIP_REPORT: #print chain.packets[2].println() print "%s is in %s" % \ (inet_ntop(AF_INET, struct.pack('!L', chain.packets[1].src)), \ inet_ntop(AF_INET, struct.pack('!L', chain.packets[3].group))) count -= 1
def main(): from optparse import OptionParser parser = OptionParser() parser.add_option("-I", "--ether_iface", dest="ether_iface", default=None, help="The name of the source interface.") parser.add_option("-e", "--ether_source", dest="ether_source", default=None, help="The host Ethernet source address.") parser.add_option("-g", "--ether_dest", dest="ether_dest", default=None, help="The host Ethernet destination address.") parser.add_option("-s", "--ip_source", dest="ip_source", default=None, help="The IP source address.") parser.add_option("-D", "--ip_dest", dest="ip_dest", default=None, help="The IP destination address.") parser.add_option("-G", "--igmp_group", dest="igmp_group", default=None, help="The IPv4 group to query.") parser.add_option("-S", "--igmp_sources", dest="igmp_sources", default=None, help="Comma-delimited list of IPv4 sources for " "a group-and-source specific query.") parser.add_option("-M", "--igmp_maxresp", dest="igmp_maxresp", default=None, help="The maximum time for end-stations to " "respond (in seconds).") parser.add_option("-c", "--count", dest="count", default=None, help="Stop after receiving at least count responses.") parser.add_option("-2", "--igmp_v2_listen", action="store_true", dest="igmp_v2_listen", help="Listen for responses from IGMPv2 end-stations.") parser.add_option("-R", "--igmp_robustness", dest="igmp_robustness", default=None, help="Querier Robustness (default 2)") parser.add_option("-Q", "--igmp_qqic", dest="igmp_qqic", default=None, help="Querier's Query Interval (default 10s)") (options, args) = parser.parse_args() if options.ether_iface is None or \ options.ether_source is None or \ options.ip_source is None or \ options.count is None: print "Non-optional argument missing." return #if options.ip_dest is not None and options.ether_dest is None: # print "Non-optional argument missing." # return maxresp = 10 * 10 if options.igmp_maxresp is not None: maxresp = int(options.igmp_maxresp) * 10 # in units of deciseconds # # Parse source list for a GSR query. # sources = [] if options.igmp_sources is not None: if options.igmp_group is None: raise "A group must be specified for a GSR query." else: for source in options.igmp_sources.split(','): sources.append(inet_atol(source)) if len(sources) == 0: raise "Error parsing source list." # Set up the vanilla packet if options.ether_dest is not None: edst = ether_atob(options.ether_dest) else: edst = ETHER_MAP_IP_MULTICAST(INADDR_ALLHOSTS_GROUP) c = ethernet(src=ether_atob(options.ether_source), dst=edst) / \ ipv4(flags=IP_DF, ttl=1, src=inet_atol(options.ip_source)) / \ igmp(type=IGMP_HOST_MEMBERSHIP_QUERY, code=maxresp) / \ igmpv3.query() ip = c.packets[1] q = c.packets[3] # IGMPv3 General Queries are always sent to ALL-SYSTEMS.MCAST.NET. # However we allow this to be overidden for protocol testing -- Windows, # in particular, doesn't seem to respond. # # We expect reports on 224.0.0.22. # Queries don't contain the Router Alert option as they are # destined for end stations, not routers. if options.igmp_robustness is not None: q.qrv = int(options.igmp_robustness) else: q.qrv = 2 # SHOULD NOT be 1, MUST NOT be 0 if options.igmp_qqic is not None: q.qqic = int(options.igmp_qqic) else: q.qqic = 10 # I query every 10 seconds if options.igmp_group is None: # General query. if options.ip_dest is not None: ip.dst = inet_atol(options.ip_dest) else: ip.dst = INADDR_ALLHOSTS_GROUP q.group = INADDR_ANY else: # Group-specific query, possibly with sources. if options.ip_dest is not None: ip.dst = inet_atol(options.ip_dest) else: ip.dst = inet_atol(options.igmp_group) q.group = ip.dst if IN_MULTICAST(ip.dst) is True and \ options.ether_dest is None: c.packets[0].dst = ETHER_MAP_IP_MULTICAST(ip.dst) for src in sources: q.sources.append(pcs.Field("", 32, default=src)) c.fixup() input = PcapConnector(options.ether_iface) input.setfilter("igmp") output = PcapConnector(options.ether_iface) out = output.write(c.bytes, len(c.bytes)) # # Wait for up to 'count' responses to the query to arrive and print them. # If options.igmp_v2_listen is True, also count responses from # end-stations which respond with IGMPv2. # # TODO: Pretty-print IGMPv3 reports. # count = int(options.count) while count > 0: packet = input.readpkt() chain = packet.chain() if ((chain.packets[2].type == IGMP_v3_HOST_MEMBERSHIP_REPORT) or ((chain.packets[2].type == IGMP_v2_HOST_MEMBERSHIP_REPORT) and \ (options.igmp_v2_listen is True))): version = 3 if chain.packets[2].type == IGMP_v2_HOST_MEMBERSHIP_REPORT: version = 2 #print chain.packets[2].println() print "%s responded to query with IGMPv%d." % \ ((inet_ntop(AF_INET, struct.pack('!L', chain.packets[1].src))), version) count -= 1
def main(): from optparse import OptionParser parser = OptionParser() parser.add_option("-I", "--ether_iface", dest="ether_iface", default=None, help="The name of the source interface.") parser.add_option("-e", "--ether_source", dest="ether_source", default=None, help="The host Ethernet source address.") parser.add_option("-G", "--ether_dest", dest="ether_dest", default=None, help="The gateway Ethernet destination address.") parser.add_option("-s", "--ip_source", dest="ip_source", default=None, help="The IP source address.") parser.add_option("-d", "--ip_dest", dest="ip_dest", default=None, help="The IP destination address.") parser.add_option("-c", "--count", dest="count", default=None, help="Stop after receiving at least count responses.") (options, args) = parser.parse_args() if options.ether_iface is None or \ options.ether_source is None or \ options.ether_dest is None or \ options.ip_source is None or \ options.count is None: print "Non-optional argument missing." return if options.ip_dest is None: idst = INADDR_DVMRP_GROUP else: idst = inet_atol(options.ip_dest) c = ethernet(src=ether_atob(options.ether_source), \ dst=ether_atob(options.ether_dest)) / \ ipv4(ttl=1, src=inet_atol(options.ip_source), dst=idst) / \ igmp(type=IGMP_DVMRP, code=DVMRP_ASK_NEIGHBORS2) / \ dvmrp(capabilities=DVMRP_CAP_DEFAULT, minor=0xFF, major=3) # # DVMRP "ask neighbors" does not contain the Router Alert option, # because DVMRP traffic is usually tunneled, and we don't need to # wake up every router on the path. # # The Ask_neighbors2 payload is: reserved, caps, minor, major: # 0x00, 0E, 0xFF, 0x03 is how mrinfo fills this out (DVMRPv3 compliant). # # PIM itself knows nothing about these messages, however, a DVMRP router # which handles these messages MAY tell you if it's peering with other # PIM routers (I believe only Ciscos do this). # c.calc_lengths() c.calc_checksums() c.encode() input = PcapConnector(options.ether_iface) input.setfilter("igmp") output = PcapConnector(options.ether_iface) out = output.write(c.bytes, len(c.bytes)) # # Wait for up to 'count' responses to the query to arrive and print them. # count = int(options.count) while count > 0: packet = input.readpkt() chain = packet.chain() if chain.packets[2].type == IGMP_DVMRP: print chain.packets[2].println() #print "%s is in %s" % \ # (inet_ntop(AF_INET, struct.pack('!L', chain.packets[1].src)), \ # inet_ntop(AF_INET, struct.pack('!L', chain.packets[2].group))) count -= 1
def main(): from optparse import OptionParser parser = OptionParser() parser.add_option("-I", "--ether_iface", dest="ether_iface", default=None, help="The name of the source interface.") parser.add_option("-e", "--ether_source", dest="ether_source", default=None, help="The host Ethernet source address.") parser.add_option("-s", "--ip_source", dest="ip_source", default=None, help="The IP source address.") parser.add_option("-G", "--igmp_group", dest="igmp_group", default=None, help="The IPv4 group for a group-specific query. " "If omitted, send a general query.") parser.add_option("-M", "--maxresp", dest="igmp_maxresp", default=None, help="The maximum time for end-stations to respond " "(in seconds).") parser.add_option("-l", "--host_list", dest="hostlist", action="append", help="List of hosts we expect responses from.") parser.add_option("-n", "--number", dest="number", default = 1, type=int, help="Query a number of groups starting at " "the one given by -G") parser.add_option("-c", "--count", dest="count", default=None, help="Stop after receiving at least count responses.") (options, args) = parser.parse_args() if options.ether_iface is None or \ options.ether_source is None or \ options.ip_source is None or \ options.count is None: print "Non-optional argument missing." return maxresp = 3 * 10 if options.igmp_maxresp is not None: maxresp = int(options.igmp_maxresp) * 10 # in units of deciseconds if options.igmp_group is None: # General query. dst = INADDR_ALLHOSTS_GROUP group = INADDR_ANY else: # Group-specific query. dst = inet_atol(options.igmp_group) group = dst # Set up our match table global match match = {} for host in options.hostlist: for addr in range(group, group + options.number): match[(inet_atol(host), (addr))] = False signal.signal(signal.SIGINFO, results) while (options.number >= 0): # Queries don't contain the Router Alert option as they are # destined for end stations, not routers. c = ethernet(src=ether_atob(options.ether_source), \ dst=ETHER_MAP_IP_MULTICAST(dst)) / \ ipv4(flags=IP_DF, ttl=1, \ src=inet_atol(options.ip_source), \ dst=dst + options.number) / \ igmp(type=IGMP_HOST_MEMBERSHIP_QUERY, code=maxresp) / \ igmpv2(group=(group + options.number)) c.fixup() input = PcapConnector(options.ether_iface) input.setfilter("igmp") output = PcapConnector(options.ether_iface) out = output.write(c.bytes, len(c.bytes)) options.number -= 1 # # Wait for up to 'count' responses to the query to arrive and print them. # count = int(options.count) while count > 0: packet = input.readpkt() chain = packet.chain() if chain.packets[2].type == IGMP_v2_HOST_MEMBERSHIP_REPORT: #print chain.packets[2].println() # print "%s is in %s" % \ # (inet_ntop(AF_INET, struct.pack('!L', chain.packets[1].src)), \ # inet_ntop(AF_INET, struct.pack('!L', chain.packets[3].group))) match[(chain.packets[1].src, chain.packets[3].group)] = True count -= 1 results(0, 0)