NED File src/inet/networklayer/icmpv6/Mldv2.ned

Name

Type

Description

Mldv2

simple module

Implements MLDv2 (RFC 3810): Multicast Listener Discovery version 2 for IPv6 – the IPv6 equivalent of Igmpv3, and the source-filtering successor to Mldv1. Beyond plain group membership, MLDv2 lets a host request a group’s traffic while INCLUDING or EXCLUDING a specific set of sources – the basis for Source-Specific Multicast (SSM). A single module performs both the host listener role and the multicast-router (querier) role; the router role is active only on multicast routers (i.e. when IPv6 multicast forwarding is enabled on the node).

Source code

//
// Copyright (C) 2026 OpenSim Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//

package inet.networklayer.icmpv6;

import inet.common.SimpleModule;

//
// Implements MLDv2 (RFC 3810): Multicast Listener Discovery version 2 for IPv6 --
// the IPv6 equivalent of ~Igmpv3, and the source-filtering successor to ~Mldv1.
// Beyond plain group membership, MLDv2 lets a host request a group's traffic
// while INCLUDING or EXCLUDING a specific set of sources -- the basis for
// Source-Specific Multicast (SSM). A single module performs both the host
// listener role and the multicast-router (querier) role; the router role is
// active only on multicast routers (i.e. when IPv6 multicast forwarding is
// enabled on the node).
//
// MLDv2 messages (ICMPv6 types 130/143, see ~Mldv2Query and ~Mldv2Report) are
// carried as ICMPv6 and exchanged with the IPv6 layer through the `ipIn`/`ipOut`
// gates, which attach to the `lp` MessageDispatcher in ~Ipv6NetworkLayer. On the
// host side, membership and source filters follow the interface's multicast
// socket state (Ipv6InterfaceData), delivered to this module through the
// `ipv6MulticastChange` notification.
//
// <b>Host behavior</b> (RFC 3810 Section 5/6)
//
// For each (interface, group) the host keeps a filter mode (INCLUDE or EXCLUDE)
// and a source list. When the application changes its membership or filter, the
// host sends a State-Change Report carrying the relevant Multicast Address
// Records (ALLOW_NEW_SOURCES, BLOCK_OLD_SOURCES, CHANGE_TO_INCLUDE_MODE,
// CHANGE_TO_EXCLUDE_MODE). In response to a General, Multicast-Address-Specific,
// or Multicast-Address-and-Source-Specific Query it schedules a Current-State
// Report (MODE_IS_INCLUDE / MODE_IS_EXCLUDE) after a random delay bounded by the
// Query's Maximum Response Code. Reports are sent to the all-MLDv2-routers
// address (ff02::16); General Queries go to the all-nodes address (ff02::1). The
// link-scope all-nodes group (ff02::1) is never reported.
//
// <b>Router (querier) behavior</b> (RFC 3810 Section 7)
//
// A querier sends periodic General Queries (every `queryInterval`) and, when a
// group or source is leaving, Multicast-Address-Specific and
// Multicast-Address-and-Source-Specific Queries (`lastMemberQueryCount` of them,
// `lastMemberQueryInterval` apart). Per (interface, group) it keeps a filter
// mode, a group timer, and per-source timers; together these determine which
// sources are forwarded onto the link (pushed into Ipv6InterfaceData via
// setMulticastListeners). Where several routers share a link, the one with the
// lowest IP address is the querier; another router takes over only if it hears no
// General Query for `otherQuerierPresentInterval`.
//
// When `enabled` is false the module is inert: it sends nothing and runs no
// querier (it stays instantiated and registered, but performs no MLD activity).
//
// <b>Parity gaps</b> (matching the IPv4 ~Igmpv3 this module is translated from):
// state-change Reports are sent once, not retransmitted [Robustness Variable]-1
// more times; Multicast-Address-Specific Queries after a leave are not
// retransmitted `lastMemberQueryCount` times; and MLDv1<->MLDv2 interoperation
// (parsing/answering older-version messages) is not implemented -- unexpected
// message versions are logged and dropped.
//
// @see ~IMld, ~Mldv1, ~Igmpv3, ~Mldv2Query, ~Mldv2Report, Ipv6InterfaceData
//
simple Mldv2 extends SimpleModule like IMld
{
    parameters:
        @class(Mldv2);
        bool enabled = default(true);  // when false the module is inert (sends nothing, runs no querier)
        string interfaceTableModule;   // path to the InterfaceTable module
        string routingTableModule;     // path to the Ipv6RoutingTable module
        string checksumMode @enum("declared","computed") = default("declared");  // how the ICMPv6 checksum is handled
        int    robustnessVariable = default(2);  // loss tolerance; only supplies the defaults of the params marked (RV) below
        double queryInterval @unit(s) = default(125s);  // querier: interval between periodic General Queries
        double queryResponseInterval @unit(s) = default(10s);  // querier: max delay a host may wait before answering a General Query
        double groupMembershipInterval @unit(s) = default((robustnessVariable * queryInterval) + queryResponseInterval);  // querier: how long a group stays active without a new Report (RV)
        double otherQuerierPresentInterval @unit(s) = default((robustnessVariable * queryInterval) + (queryResponseInterval / 2));  // querier: wait this long for another querier before taking over (RV)
        double startupQueryInterval @unit(s) = default(queryInterval / 4);  // querier: interval between the initial General Queries at startup
        int    startupQueryCount = default(robustnessVariable);  // querier: number of General Queries sent at startup (RV)
        double lastMemberQueryInterval @unit(s) = default(1s);  // querier: interval between Multicast-Address-/-and-Source-Specific Queries after a leave
        int    lastMemberQueryCount = default(robustnessVariable);  // querier: number of such Queries sent after a leave (RV)
        double unsolicitedReportInterval @unit(s) = default(10s);  // host: delay between repeated State-Change Reports
        @display("i=block/cogwheel");
        @selfMessageKinds(inet::Mldv2TimerKind);

    gates:
        input ipIn @labels(Ipv6ControlInfo/up);   // delivered MLD (ICMPv6) packets
        output ipOut @labels(Ipv6ControlInfo/down); // to ~Ipv6
}