Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions scapy/layers/igmp.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,10 @@ def dispatch_hook(cls, _pkt=None, *args, **kargs):
elif typ == 0x32:
return IGMPv3_MRT

# IGMPv3() has no raw bytes yet. Keep that class; falling through to
# IGMP_MQ made the pre-2.8 stack IGMPv3()/IGMPv3mr() a v1/v2 query.
if issubclass(cls, IGMPv3):
return cls
return IGMP_MQ

def answers(self, other):
Expand Down Expand Up @@ -252,6 +256,41 @@ def mysummary(self):
else:
return self.sprintf(f"IGMPv3 %{t}.type%")

def _stacked_full_message(self, payload):
"""True when payload is already a complete IGMPv3 message.

contrib.igmpv3 aliases IGMPv3mr to IGMPv3_MR. Code written for 2.7
still does IGMPv3()/IGMPv3mr(), which used to be one report. IGMPv3_MR
already includes the header, so keep that message and drop this one.
"""
return (
type(self) is IGMPv3
and isinstance(payload, IGMPv3)
and type(payload) is not IGMPv3
)

def add_payload(self, payload):
# IP()/IGMPv3()/IGMPv3_MR() lands here: IP.add_payload delegates to us.
if self._stacked_full_message(payload) and self.underlayer is not None:
under = self.underlayer
under.remove_payload()
under.add_payload(payload)
return
super(IGMPv3, self).add_payload(payload)

def __div__(self, other):
if isinstance(other, Packet) and self._stacked_full_message(other):
other = other.copy()
if self.underlayer is not None:
under = self.underlayer.copy()
under.remove_payload()
under.add_payload(other)
return under
return other
return super(IGMPv3, self).__div__(other)

__truediv__ = __div__


class IGMPv3_MQ(IGMPv3):
"""
Expand Down
14 changes: 14 additions & 0 deletions test/scapy/layers/igmp.uts
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,17 @@ assert isinstance(IGMP(raw(IGMPv3_MQ())), IGMPv3)

pkt = IGMPv3_MR(records=[IGMPv3_MR_Group(maddr="127.0.0.1")])
assert pkt.summary() == 'IGMPv3 Version 3 - Membership Report'

= IGMPv3 - legacy contrib stack is one report

load_contrib("igmpv3")
from scapy.contrib.igmpv3 import IGMPv3mr, IGMPv3gr
group = "232.1.1.12"
sources = ["10.0.0.1", "10.0.0.2"]
legacy = IGMPv3()/IGMPv3mr(records=[IGMPv3gr(rtype=1, maddr=group, srcaddrs=sources)])
current = IGMPv3_MR(records=[IGMPv3_MR_Group(rtype=1, maddr=group, srcaddrs=sources)])
assert bytes(legacy) == bytes(current)
assert bytes(legacy)[0] == 0x22
legacy_ip = IP(src="192.168.10.2", dst="224.0.0.22")/IGMPv3()/IGMPv3mr(records=[IGMPv3gr(rtype=1, maddr=group, srcaddrs=sources)])
assert bytes(legacy_ip[IGMPv3]) == bytes(current)
assert IGMPv3_MR in legacy_ip