def igmpize(self): """Called to explicitly fixup the packet according to the IGMP RFC The rules are: - General: 1. the Max Response time is meaningful only in Membership Queries and should be zero - IP: 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) - Ether: 1. Recalculate destination 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. """ from scapy.contrib.igmpv3 import IGMPv3 gaddr = self.gaddr if hasattr(self, "gaddr") and self.gaddr else "0.0.0.0" # noqa: E501 underlayer = self.underlayer if self.type not in [0x11, 0x30]: # General Rule 1 # noqa: E501 self.mrcode = 0 if isinstance(underlayer, IP): if (self.type == 0x11): if (gaddr == "0.0.0.0"): underlayer.dst = "224.0.0.1" # IP rule 1 # noqa: E501 elif isValidMCAddr(gaddr): underlayer.dst = gaddr # IP rule 3a # noqa: E501 else: warning("Invalid IGMP Group Address detected !") return False elif ((self.type == 0x17) and isValidMCAddr(gaddr)): underlayer.dst = "224.0.0.2" # IP rule 2 # noqa: E501 elif ((self.type == 0x12) or (self.type == 0x16)) and (isValidMCAddr(gaddr)): # noqa: E501 underlayer.dst = gaddr # IP rule 3b # noqa: E501 elif (self.type in [0x11, 0x22, 0x30, 0x31, 0x32] and isinstance(self, IGMPv3)): pass else: warning("Invalid IGMP Type detected !") return False if not any(isinstance(x, IPOption_Router_Alert) for x in underlayer.options): # noqa: E501 underlayer.options.append(IPOption_Router_Alert()) underlayer.ttl = 1 # IP rule 4 _root = self.firstlayer() if _root.haslayer(Ether): # Force recalculate Ether dst _root[Ether].dst = getmacbyip(underlayer.dst) # Ether rule 1 # noqa: E501 if isinstance(self, IGMPv3): self.encode_maxrespcode() return True
def fixup(pkt): """Fixes up the underlying IP() and Ether() headers.""" assert pkt.haslayer(IGMPv3), \ "This packet is not an IGMPv4 packet; cannot fix it up" igmp = pkt.getlayer(IGMPv3) if pkt.haslayer(IP): ip = pkt.getlayer(IP) ip.ttl = 1 ip.proto = 2 ip.tos = 0xc0 ip.options = [IPOption_Router_Alert()] if igmp.type == IGMP_TYPE_MEMBERSHIP_QUERY: if igmp.gaddr == "0.0.0.0": ip.dst = "224.0.0.1" else: assert IGMPv3.is_valid_mcaddr(igmp.gaddr), \ "IGMP membership query with invalid mcast address" ip.dst = igmp.gaddr elif igmp.type == IGMP_TYPE_V2_LEAVE_GROUP and \ IGMPv3.is_valid_mcaddr(igmp.gaddr): ip.dst = "224.0.0.2" elif igmp.type in (IGMP_TYPE_V1_MEMBERSHIP_REPORT, IGMP_TYPE_V2_MEMBERSHIP_REPORT) and \ IGMPv3.is_valid_mcaddr(igmp.gaddr): ip.dst = igmp.gaddr # We do not need to fixup the ether layer, it is done by scapy # # if pkt.haslayer(Ether): # eth = pkt.getlayer(Ether) # ip_long = atol(ip.dst) # ether.dst = '01:00:5e:%02x:%02x:%02x' % ( # (ip_long >> 16) & 0x7f, (ip_long >> 8) & 0xff, # ip_long & 0xff ) return pkt