IGMPv3 - Internet Group Management Protocol Version 3

pcapkit.protocols.internet.igmpv3 contains IGMPv3, which implements an extractor for Internet Group Management Protocol version 3 (IGMPv3) [RFC 3376]. Unlike IGMPv1 and IGMPv2, which reuse the abstract base’s own 8-octet schema and data model (see igmp), IGMPv3 defines two message shapes of its own:

An IGMPv3 Membership Query (type 0x11, shared with v1/v2) reuses the 8-octet layout IGMPv1/v2 share and, when at least 12 octets long overall, appends a source-list extension:

Octets

Bits

Name

Description

0

0

igmpv3.type

Type

1

8

igmpv3.max_resp_code

Max Response Code

2

16

igmpv3.checksum

Checksum

4

32

igmpv3.group

Group Address

8

64

igmpv3.resv

Reserved (Resv)

8

68

igmpv3.s_flag

Suppress Router-Side Processing (S)

8

69

igmpv3.qrv

Querier’s Robustness Variable (QRV)

9

72

igmpv3.qqic

Querier’s Query Interval Code

10

80

Number of Sources (N)

12

96

igmpv3.sources

Source Address [1..N]

An IGMPv3 Membership Report (type 0x22) has an unrelated, materially different shape of its own:

Octets

Bits

Name

Description

0

0

igmpv3.type

Type

1

8

igmpv3.reserved_1

Reserved

2

16

igmpv3.checksum

Checksum

4

32

igmpv3.reserved_2

Reserved

6

48

igmpv3.num_group_records

Number of Group Records (M)

8

64

igmpv3.records

Group Record [1..M]

class pcapkit.protocols.internet.igmpv3.IGMPv3(file=None, length=None, **kwargs)[source]

Bases: IGMP[IGMPv3, IGMPv3]

This class implements Internet Group Management Protocol version 3.

Unlike IGMPv1 and IGMPv2, which share the base’s own 8-octet schema and data model, IGMPv3’s wire format differs materially by message – a Query’s source-list extension, a Report’s group-record list – so this class carries its own (#2439, #2448).

property name: Literal['Internet Group Management Protocol version 3']

Name of corresponding protocol.

property version: Literal[3]

IGMP version this class reads, 3.

property length: int

Header length of current protocol.

Every message starts with an 8-octet header. A Query may carry the source-list extension, and a Report’s group-record list has no fixed size, so this is taken during read() from the octets consumed (see length) rather than returned as a constant, unlike the base’s own length.

classmethod id()[source]

Index ID of the protocol.

RFC 3376 names the protocol IGMPv3, carrying its own version.

Return type:

tuple[Literal['IGMPv3']]

Returns:

Index ID of the protocol.

read(length=None, **kwargs)[source]

Read Internet Group Management Protocol version 3.

Parameters:
  • length (int | None) – Length of packet data.

  • **kwargs (Any) – Arbitrary keyword arguments.

Return type:

IGMPv3

Returns:

Parsed packet data.

Raises:

ProtocolError – If _classify() does not place the message in version 3 – an IGMPv1/v2-shaped Query, or a type neither version of IGMP defines.

Note

A message the data ends inside records the cut on its body, with or without an enclosing length, so from_data(info) rebuilds it as captured.

make(type=Type.IGMP_Membership_Query, max_resp_code=None, max_resp_time=0, checksum=None, group='0.0.0.0', resv=0, s_flag=False, qrv=0, qqic=None, qqi=None, sources=None, reserved_1=0, reserved_2=b'\x00\x00', num_group_records=None, records=None, payload=b'', **kwargs)[source]

Make (construct) packet data.

Building a Query (the default) always writes the source-list extension: this class is specifically IGMPv3, unlike the base’s own make(), which writes the extension only when asked, since there a plain 8-octet message is the more natural default.

Parameters:
  • type (Type | int) – Message type.

  • max_resp_code (int | None) – Max response code, as transmitted on the wire. When not given, derived from max_resp_time via _encode_resp_code(); only meaningful for a Query.

  • max_resp_time (int) – Max response time, in units of 1/10 second: an int from 0 to 255 (not a bool), else ProtocolError. Ignored when max_resp_code is given.

  • checksum (bytes | None) – Checksum; the one’s complement of the one’s complement sum of the message, with this field taken as zero and no pseudo-header – the whole IP payload (RFC 3376 Section 4.1.2, RFC 3376 Section 4.2.2). If None, computed so, over payload as well, when CHECKSUM_COMPUTE is on, zero when it is off.

  • group (IPv4Address | str | bytes | int) – Group address; 0.0.0.0 for a General Query; only meaningful for a Query.

  • resv (int) – The 4 reserved high-order bits of the flags octet (RFC 3376 Section 4.1.4); 0 on a standards-conformant sender; only meaningful for a Query.

  • s_flag (bool) – Suppress Router-Side Processing flag (RFC 3376 Section 4.1.5); only meaningful for a Query.

  • qrv (int) – Querier’s Robustness Variable (RFC 3376 Section 4.1.6); only meaningful for a Query.

  • qqic (int | None) – Querier’s Query Interval Code, as transmitted on the wire; derived from qqi via _encode_resp_code() when not given; only meaningful for a Query.

  • qqi (int | None) – Querier’s Query Interval, in seconds; ignored when qqic is given; only meaningful for a Query.

  • sources (Iterable[IPv4Address | str | bytes | int] | None) – Source addresses being queried (RFC 3376 Section 4.1.9); only meaningful for a Query.

  • reserved_1 (int) – The reserved octet following type (RFC 3376 Section 4.2.1); only meaningful for a Report.

  • reserved_2 (bytes) – The reserved 16 bits before the group-record count (RFC 3376 Section 4.2.1); only meaningful for a Report.

  • num_group_records (int | None) – Number of group records, as transmitted on the wire; the length of records when not given, so a capture cut inside its record list rebuilds as captured; only meaningful for a Report. A count above the length of records is written as given, as on a Report the capture cut short.

  • records (Iterable[dict[str, Any] | GroupRecord] | None) – Group records (RFC 3376 Section 4.2.4), each either a GroupRecord or a dict of the keyword arguments _make_group_record() takes; only meaningful for a Report.

  • payload (bytes | ProtocolBase | Schema) – Payload of current instance.

  • **kwargs (Any) – Arbitrary keyword arguments.

Return type:

IGMPv3

Returns:

Constructed packet data.

Raises:

ProtocolError – If type is a name, a bool, or a member or code outside 0 to 255, or is neither a Query nor a Report; if max_resp_time is not an int from 0 to 255; if num_group_records is not an int from the length of records to 65535; if checksum or reserved_2 is not two octets; or if a record’s record_type is refused, see _make_group_record().

_make_group_record(record=None, *, record_type=RecordType.MODE_IS_INCLUDE, aux_data_len=None, num_sources=None, multicast_address='0.0.0.0', sources=None, aux_data=b'', **kwargs)[source]

Make an IGMPv3 group record (RFC 3376 Section 4.2.4).

Parameters:
Return type:

GroupRecord

Returns:

Group record schema.

Raises:

ProtocolError – If record_type is a name, a bool, or a member or code outside 0 to 255.

classmethod _make_data(data)[source]

Create key-value pairs from data for protocol construction.

Parameters:

data (IGMPv3) – protocol data

Return type:

dict[str, Any]

Returns:

Key-value pairs for protocol construction.

Note

Dispatches on data.type rather than on isinstance(data, Data_Report), so that a hand-built dict carrying the right type but not actually one of the two concrete data classes – as a test double legitimately might – still rebuilds correctly, the same way make() itself dispatches on the type it is given rather than requiring a particular input class.

Header Schemas

class pcapkit.protocols.schema.internet.igmpv3.IGMPv3(type=NO_VALUE, body=NO_VALUE, *, __packet__=None, **kwargs)[source]

Bases: Schema

Header schema for an IGMPv3 packet.

type is common to both shapes IGMPv3 defines; everything past it is switched, by igmpv3_body_selector(), between Query and Report (#2439, #2448).

Note

No PayloadField here: IGMP has no header field bounding the message, so body gets the whole length and keeps its trailing octets itself – in Query.payload (or QueryExtension.payload when Query.ext is present), or Report.payload. get_payload() delegates there.

type: Type = <EnumField type>

Message type.

body: Query | Report = <SwitchField body>

Message body, switched on type.

get_payload(name=None)[source]

Get payload of IGMPv3.

Parameters:

name (str | None) – Unused; kept for signature compatibility with Schema.get_payload.

Return type:

bytes

Returns:

Trailing octets of body – see the class Note above for exactly where each shape keeps them.

class pcapkit.protocols.schema.internet.igmpv3.Query(max_resp_code=NO_VALUE, checksum=NO_VALUE, group=NO_VALUE, ext=NO_VALUE, payload=NO_VALUE, *, __packet__=None, **kwargs)[source]

Bases: Schema

Header schema for an IGMPv3 Membership Query (RFC 3376 Section 4.1).

The first 8 octets are the same shape IGMPv1/v2 share (IGMP); the extension is present only on a Query of 12 or more octets overall – see query_extension_present(). An 8-octet Query, which this schema unpacks too (with ext None), is not actually IGMPv3; IGMPv3.read is what refuses it.

max_resp_code: int = <UInt8Field max_resp_code>

Max response code; the raw octet IGMPv3.read decodes per RFC 3376 Section 4.1.1.

checksum: bytes = <BytesField checksum>

Checksum. The 16-bit one’s complement of the one’s complement sum of the message, with the checksum field itself taken as zero and no pseudo-header involved – the whole IP payload (RFC 3376 Section 4.1.2).

group: IPv4Address = <IPv4AddressField group>

Group address.

ext: QueryExtension | None = <ConditionalField ext>

Source-list extension (RFC 3376 Section 4.1), present only on a Query at least 12 octets long overall; None otherwise – see query_extension_present().

payload: bytes = <PayloadField payload>

Payload, empty for a well-formed IGMPv3 Query. Only ever actually holds anything when ext is None: when it is not, ext was handed the whole remaining length, so this field’s own share of it is always empty, and get_payload() reads QueryExtension.payload instead – see there.

get_payload(name=None)[source]

Get payload of Query.

Parameters:

name (str | None) – Unused; kept for signature compatibility with Schema.get_payload.

Return type:

bytes

Returns:

ext’s own payload when ext is not None (see payload’s docstring for why); payload itself otherwise.

class pcapkit.protocols.schema.internet.igmpv3.QueryExtension(flags=NO_VALUE, qqic=NO_VALUE, num_sources=NO_VALUE, sources=NO_VALUE, payload=NO_VALUE, *, __packet__=None, **kwargs)[source]

Bases: Schema

Header schema for the IGMPv3 Query’s source-list extension.

Follows the 8-octet header every IGMP Query shares with IGMPv1/v2 (RFC 3376 Section 4.1).

flags: QueryFlags = <BitField flags>

Resv/S Flag/QRV.

qqic: int = <UInt8Field qqic>

Querier’s Query Interval Code, as received on the wire; decoded into seconds by IGMPv3.read per RFC 3376 Section 4.1.7.

num_sources: int = <UInt16Field num_sources>

Number of source addresses.

sources: list[IPv4Address] = <ListField sources>

Source addresses (RFC 3376 Section 4.1.9).

payload: bytes = <PayloadField payload>

Additional data (RFC 3376 Section 4.1.10) – octets past the source list that a received Query may carry and that must be kept (to verify the checksum) but not otherwise acted on. Empty for a standards-conformant Query, since IGMPv3.make never writes any.

class pcapkit.protocols.schema.internet.igmpv3.Report(reserved_1=NO_VALUE, checksum=NO_VALUE, reserved_2=NO_VALUE, num_group_records=NO_VALUE, records=NO_VALUE, payload=NO_VALUE, *, __packet__=None, **kwargs)[source]

Bases: Schema

Header schema for an IGMPv3 Membership Report’s body (RFC 3376 Section 4.2).

Note

records is first read with the whole remaining length, since the declared count cannot size it in octets (cf. LocatorSetParameter). post_process() keeps only the first num_group_records records and gives the rest to payload, which is RFC 3376 Section 4.2.11’s “Additional Data”. Where the data ends before the records counted, see report_record_count().

reserved_1: int = <UInt8Field reserved_1>

Reserved.

checksum: bytes = <BytesField checksum>

Checksum.

reserved_2: bytes = <BytesField reserved_2>

Reserved.

num_group_records: int = <UInt16Field num_group_records>

Number of group records, as received on the wire.

records: list[GroupRecord] = <ListField records>

Group records; see post_process() for how this is kept to exactly num_group_records entries.

payload: bytes = <PayloadField payload>

Payload; empty for a standards-conformant message with num_group_records matching the octets actually present.

post_process(packet)[source]

Trim records to num_group_records and reclaim the rest as payload.

Parameters:

packet (dict[str, Any]) – Unpacked data.

Return type:

Schema

Returns:

Revised schema.

A capture that ends past the records kept ends inside payload, so the short read unpack() recorded on records is moved there (#2687).

class pcapkit.protocols.schema.internet.igmpv3.GroupRecord(record_type=NO_VALUE, aux_data_len=NO_VALUE, num_sources=NO_VALUE, multicast_address=NO_VALUE, sources=NO_VALUE, aux_data=NO_VALUE, *, __packet__=None, **kwargs)[source]

Bases: Schema

Header schema for an IGMPv3 group record (RFC 3376 Section 4.2.4).

record_type: RecordType = <EnumField record_type>

Record type.

aux_data_len: int = <UInt8Field aux_data_len>

Auxiliary data length, in 32-bit words.

num_sources: int = <UInt16Field num_sources>

Number of source addresses.

multicast_address: IPv4Address = <IPv4AddressField multicast_address>

Multicast address.

sources: list[IPv4Address] = <ListField sources>

Source addresses (RFC 3376 Section 4.2.9).

aux_data: bytes = <BytesField aux_data>

Auxiliary data (RFC 3376 Section 4.2.10).

Data Models

class pcapkit.protocols.data.internet.igmpv3.IGMPv3(*args, **kwargs)[source]

Bases: Protocol

Data model for Internet Group Management Protocol version 3.

This is the common ancestor of the two message shapes IGMPv3 defines: Query, its Membership Query, extending the 8-octet group-address header IGMPv1/v2 also use with a source-list extension; and Report, its Membership Report, whose body is a list of group records instead (#2439, #2448).

version: Literal[3]

Version of IGMP, always 3. It is on no octet; it names the class that read or built the message, as on the IGMPv1/v2 data model, and IGMP.from_data dispatches on it (#2574).

type: Type

Message type.

length: int

Header length, in octets. IGMP has no header field of its own that states this – unlike AH, it is computed from what IGMPv3.read actually consumed, the same way length is.

checksum_status: ChecksumStatus

Outcome of verifying the message’s checksum, present only when CHECKSUM_VERIFY is on.

class pcapkit.protocols.data.internet.igmpv3.Query(version, type, length, max_resp_code, max_resp_time, checksum, group, resv, s_flag, qrv, qqic, qqi, sources)[source]

Bases: IGMPv3

Data model for an IGMPv3 Membership Query (RFC 3376 Section 4.1).

Reached only once IGMPv3.read has confirmed the message classifies as IGMPv3 (at least 12 octets), so the source-list extension fields below are always populated – never None, unlike the single-class design IGMPv1/v2 predate this one with.

max_resp_code: int

Max response code, as received on the wire; the raw octet max_resp_time is decoded from (RFC 3376 Section 4.1.1).

max_resp_time: int

Max response time, in units of 1/10 second, decoded from max_resp_code: literal if below 128, else floating-point, (mant | 0x10) << (exp + 3) (RFC 3376 Section 4.1.1).

checksum: bytes

Checksum.

group: IPv4Address

Group address – the IPv4 multicast group being queried, or 0.0.0.0 on a General Query.

resv: int

Resv, the 4 reserved high-order bits of the flags octet (RFC 3376 Section 4.1.4), kept as read rather than assumed zero so a round trip reproduces a non-conformant sender’s octet exactly.

s_flag: bool

Suppress Router-Side Processing flag (RFC 3376 Section 4.1.5).

qrv: int

Querier’s Robustness Variable (RFC 3376 Section 4.1.6).

qqic: int

Querier’s Query Interval Code, as received on the wire.

qqi: int

Querier’s Query Interval, in seconds, decoded from qqic by the same formula as max_resp_time (RFC 3376 Section 4.1.7).

sources: tuple[IPv4Address, ...]

Source addresses being queried (RFC 3376 Section 4.1.9); an empty tuple for a General or Group-Specific Query.

class pcapkit.protocols.data.internet.igmpv3.Report(version, type, length, reserved_1, checksum, reserved_2, num_group_records, records)[source]

Bases: IGMPv3

Data model for an IGMPv3 Membership Report (RFC 3376 Section 4.2).

reserved_1: int

Reserved octet after type (RFC 3376 Section 4.2.1), kept as read rather than assumed zero so a round trip reproduces a non-conformant sender’s octet exactly.

checksum: bytes

Checksum.

reserved_2: bytes

Reserved 16 bits before num_group_records (RFC 3376 Section 4.2.1); see reserved_1.

num_group_records: int

Number of group records, as received on the wire.

records: tuple[GroupRecord, ...]

Group records (RFC 3376 Section 4.2.4).

class pcapkit.protocols.data.internet.igmpv3.GroupRecord(record_type, aux_data_len, num_sources, multicast_address, sources, aux_data)[source]

Bases: Data

Data model for an IGMPv3 group record (RFC 3376 Section 4.2.4).

record_type: RecordType

Record type.

aux_data_len: int

Auxiliary data length, in 32-bit words.

num_sources: int

Number of source addresses.

multicast_address: IPv4Address

Multicast address this record pertains to.

sources: tuple[IPv4Address, ...]

Source addresses (RFC 3376 Section 4.2.9).

aux_data: bytes

Auxiliary data (RFC 3376 Section 4.2.10); always empty for a standards-conformant IGMPv3 sender, but kept as read for a receiver.