Skip to content

Add multicast-focused AFTs#1468

Open
bewing wants to merge 1 commit intoopenconfig:masterfrom
bewing:feat/mft
Open

Add multicast-focused AFTs#1468
bewing wants to merge 1 commit intoopenconfig:masterfrom
bewing:feat/mft

Conversation

@bewing
Copy link
Copy Markdown

@bewing bewing commented Apr 2, 2026

As an operator, I need a standard way to monitor the current forwarding behavior of multicast on my network, across multiple vendor types.

Most(?) platforms forward multicast under a input interface / OIL model, keyed by various FECs. This change attempts to provide an initial implementation for IPv4, IPv6, and MPLS forwarding of multicast traffic.

Change Scope

  • Add multicast-focused AFTs
  • This change only adds new AFTs, and should be backwards compatible with existing implementations of AFT

Platform Implementations

Tree View

--- /home/bewing/old-tree.txt   2026-04-02 16:24:26.551457318 -0500
+++ /home/bewing/new-tree.txt   2026-04-02 16:24:02.321811577 -0500
@@ -1,130 +1,217 @@
 module: openconfig-network-instance
   +--rw network-instances
      +--rw network-instance* [name]
         +--ro afts
            +--ro ipv4-unicast
            |  +--ro ipv4-entry* [prefix]
            |     +--ro prefix    -> ../state/prefix
            |     +--ro state
            |        +--ro prefix?               oc-inet:ipv4-prefix
            |        +--ro counters
            |        |  x--ro packets-forwarded?          oc-yang:counter64
            |        |  x--ro octets-forwarded?           oc-yang:counter64
            |        |  x--ro packets-forwarded-backup?   oc-yang:counter64
            |        |  x--ro octets-forwarded-backup?    oc-yang:counter64
            |        +--ro entry-metadata?       binary
            |        +--ro origin-protocol?      identityref
            |        +--ro decapsulate-header?   oc-aftt:encapsulation-header-type
            +--ro ipv6-unicast
            |  +--ro ipv6-entry* [prefix]
            |     +--ro prefix    -> ../state/prefix
            |     +--ro state
            |        +--ro prefix?               oc-inet:ipv6-prefix
            |        +--ro counters
            |        |  x--ro packets-forwarded?          oc-yang:counter64
            |        |  x--ro octets-forwarded?           oc-yang:counter64
            |        |  x--ro packets-forwarded-backup?   oc-yang:counter64
            |        |  x--ro octets-forwarded-backup?    oc-yang:counter64
            |        +--ro entry-metadata?       binary
            |        +--ro origin-protocol?      identityref
            |        +--ro decapsulate-header?   oc-aftt:encapsulation-header-type
+           +--ro ipv4-multicast
+           |  +--ro ipv4-multicast-entry* [source-address group-address]
+           |     +--ro source-address            -> ../state/source-address
+           |     +--ro group-address             -> ../state/group-address
+           |     +--ro state
+           |     |  +--ro source-address?          oc-inet:ipv4-address
+           |     |  +--ro group-address?           oc-inet:ipv4-address
+           |     |  +--ro origin-protocol?         identityref
+           |     |  +--ro entry-metadata?          binary
+           |     |  +--ro decapsulate-header?      oc-aftt:encapsulation-header-type
+           |     |  +--ro rpf-discards?            oc-yang:counter64
+           |     |  +--ro reverse-path-neighbor?   oc-inet:ipv4-address
+           |     +--ro reverse-path-interface
+           |     |  +--ro interface-ref
+           |     |     +--ro state
+           |     |        +--ro interface?      -> /oc-if:interfaces/interface/name
+           |     |        +--ro subinterface?   -> /oc-if:interfaces/interface[oc-if:name=current()/../interface]/subinterfaces/subinterface/index
+           |     +--ro outgoing-interfaces
+           |        +--ro outgoing-interface* [index]
+           |           +--ro index            -> ../state/index
+           |           +--ro state
+           |           |  +--ro index?                     uint64
+           |           |  +--ro active?                    boolean
+           |           |  +--ro pushed-mpls-label-stack*   oc-mplst:mpls-label
+           |           |  +--ro counters
+           |           |     +--ro packets-replicated?   oc-yang:counter64
+           |           |     +--ro octets-replicated?    oc-yang:counter64
+           |           +--ro interface-ref
+           |              +--ro state
+           |                 +--ro interface?      -> /oc-if:interfaces/interface/name
+           |                 +--ro subinterface?   -> /oc-if:interfaces/interface[oc-if:name=current()/../interface]/subinterfaces/subinterface/index
+           +--ro ipv6-multicast
+           |  +--ro ipv6-multicast-entry* [source-address group-address]
+           |     +--ro source-address            -> ../state/source-address
+           |     +--ro group-address             -> ../state/group-address
+           |     +--ro state
+           |     |  +--ro source-address?          oc-inet:ipv6-address
+           |     |  +--ro group-address?           oc-inet:ipv6-address
+           |     |  +--ro origin-protocol?         identityref
+           |     |  +--ro entry-metadata?          binary
+           |     |  +--ro decapsulate-header?      oc-aftt:encapsulation-header-type
+           |     |  +--ro rpf-discards?            oc-yang:counter64
+           |     |  +--ro reverse-path-neighbor?   oc-inet:ipv6-address
+           |     +--ro reverse-path-interface
+           |     |  +--ro interface-ref
+           |     |     +--ro state
+           |     |        +--ro interface?      -> /oc-if:interfaces/interface/name
+           |     |        +--ro subinterface?   -> /oc-if:interfaces/interface[oc-if:name=current()/../interface]/subinterfaces/subinterface/index
+           |     +--ro outgoing-interfaces
+           |        +--ro outgoing-interface* [index]
+           |           +--ro index            -> ../state/index
+           |           +--ro state
+           |           |  +--ro index?                     uint64
+           |           |  +--ro active?                    boolean
+           |           |  +--ro pushed-mpls-label-stack*   oc-mplst:mpls-label
+           |           |  +--ro counters
+           |           |     +--ro packets-replicated?   oc-yang:counter64
+           |           |     +--ro octets-replicated?    oc-yang:counter64
+           |           +--ro interface-ref
+           |              +--ro state
+           |                 +--ro interface?      -> /oc-if:interfaces/interface/name
+           |                 +--ro subinterface?   -> /oc-if:interfaces/interface[oc-if:name=current()/../interface]/subinterfaces/subinterface/index
+           +--ro mpls-multicast
+           |  +--ro mpls-multicast-entry* [incoming-label]
+           |     +--ro incoming-label         -> ../state/incoming-label
+           |     +--ro state
+           |     |  +--ro incoming-label?            oc-mplst:mpls-label
+           |     |  +--ro popped-mpls-label-stack*   oc-mplst:mpls-label
+           |     |  +--ro origin-protocol?           identityref
+           |     |  +--ro counters
+           |     |  |  x--ro packets-forwarded?   oc-yang:counter64
+           |     |  |  x--ro octets-forwarded?    oc-yang:counter64
+           |     |  +--ro entry-metadata?            binary
+           |     +--ro outgoing-interfaces
+           |        +--ro outgoing-interface* [index]
+           |           +--ro index            -> ../state/index
+           |           +--ro state
+           |           |  +--ro index?                     uint64
+           |           |  +--ro active?                    boolean
+           |           |  +--ro pushed-mpls-label-stack*   oc-mplst:mpls-label
+           |           |  +--ro counters
+           |           |     +--ro packets-replicated?   oc-yang:counter64
+           |           |     +--ro octets-replicated?    oc-yang:counter64
+           |           +--ro interface-ref
+           |              +--ro state
+           |                 +--ro interface?      -> /oc-if:interfaces/interface/name
+           |                 +--ro subinterface?   -> /oc-if:interfaces/interface[oc-if:name=current()/../interface]/subinterfaces/subinterface/index
            +--ro policy-forwarding
            |  +--ro policy-forwarding-entry* [index]
            |     +--ro index    -> ../state/index
            |     +--ro state
            |        +--ro index?            uint64
            |        +--ro ip-prefix?        oc-inet:ip-prefix
            |        +--ro mac-address?      oc-yang:mac-address
            |        +--ro mpls-label?       oc-mplst:mpls-label
            |        +--ro mpls-tc?          oc-mplst:mpls-tc
            |        +--ro ip-dscp?          oc-inet:dscp
            |        +--ro ip-protocol?      oc-pkt-match-types:ip-protocol-type
            |        +--ro l4-src-port?      oc-inet:port-number
            |        +--ro l4-dst-port?      oc-inet:port-number
            |        +--ro counters
            |        |  x--ro packets-forwarded?   oc-yang:counter64
            |        |  x--ro octets-forwarded?    oc-yang:counter64
            |        +--ro entry-metadata?   binary
            +--ro mpls
            |  +--ro label-entry* [label]
            |     +--ro label    -> ../state/label
            |     +--ro state
            |        +--ro label?                     oc-mplst:mpls-label
            |        +--ro counters
            |        |  x--ro packets-forwarded?   oc-yang:counter64
            |        |  x--ro octets-forwarded?    oc-yang:counter64
            |        +--ro entry-metadata?            binary
            |        +--ro popped-mpls-label-stack*   oc-mplst:mpls-label
            +--ro ethernet
            |  +--ro mac-entry* [mac-address]
            |     +--ro mac-address    -> ../state/mac-address
            |     +--ro state
            |        +--ro mac-address?      oc-yang:mac-address
            |        +--ro counters
            |        |  x--ro packets-forwarded?   oc-yang:counter64
            |        |  x--ro octets-forwarded?    oc-yang:counter64
            |        +--ro entry-metadata?   binary
            +--ro state-synced
            |  +--ro state
            |     +--ro ipv4-unicast?   boolean
            |     +--ro ipv6-unicast?   boolean
            +--ro next-hop-groups
            |  +--ro next-hop-group* [id]
            |     +--ro id             -> ../state/id
            |     +--ro state
            |     |  +--ro id?                      uint64
            |     |  +--ro next-hop-group-name?     string
            |     |  +--ro programmed-id?           uint64
            |     |  +--ro color?                   uint64
            |     |  +--ro backup-next-hop-group?   -> ../../../next-hop-group/state/id
            |     |  +--ro backup-active?           boolean
            |     +--ro next-hops
            |     |  +--ro next-hop* [index]
            |     |     +--ro index    -> ../state/index
            |     |     +--ro state
            |     |        +--ro index?    -> ../../../../../../next-hops/next-hop/state/index
            |     |        +--ro weight?   uint64
            |     +--ro conditional
            |        +--ro condition* [id]
            |           +--ro id                  -> ../state/id
            |           +--ro state
            |           |  +--ro id?               uint64
            |           |  +--ro dscp*             oc-inet:dscp
            |           |  +--ro next-hop-group?   -> ../../../../../next-hop-group/state/id
            |           +--ro input-interfaces
            |              +--ro input-interface* [id]
            |                 +--ro id       -> ../state/id
            |                 +--ro state
            |                    +--ro id?             string
            |                    +--ro interface?      -> /oc-if:interfaces/interface/name
            |                    +--ro subinterface?   -> /oc-if:interfaces/interface[oc-if:name=current()/../interface]/subinterfaces/subinterface/index
            +--ro next-hops
               +--ro next-hop* [index]
                  +--ro index            -> ../state/index
                  +--ro state
                  |  +--ro index?                     uint64
                  |  +--ro programmed-index?          uint64
                  |  +--ro ip-address?                oc-inet:ip-address
                  |  +--ro mac-address?               oc-yang:mac-address
                  |  +--ro pop-top-label?             boolean
                  |  +--ro pushed-mpls-label-stack*   oc-mplst:mpls-label
                  |  +--ro encapsulate-header?        oc-aftt:encapsulation-header-type
                  |  +--ro decapsulate-header?        oc-aftt:encapsulation-header-type
                  |  +--ro origin-protocol?           identityref
                  |  +--ro lsp-name?                  string
                  |  +--ro counters
                  |  |  x--ro packets-forwarded?   oc-yang:counter64
                  |  |  x--ro octets-forwarded?    oc-yang:counter64
                  |  +--ro vni-label?                 oc-evpn-types:evi-id
                  |  +--ro tunnel-src-ip-address?     oc-inet:ip-address
                  +--ro ip-in-ip
                  |  +--ro state
                  |     +--ro src-ip?   oc-inet:ip-address
                  |     +--ro dst-ip?   oc-inet:ip-address
                  +--ro gre
                  |  +--ro state
                  |     +--ro src-ip?   oc-inet:ip-address
                  |     +--ro dst-ip?   oc-inet:ip-address
                  |     +--ro ttl?      uint8
                  +--ro encap-headers
                  |  +--ro encap-header* [index]

@bewing bewing requested a review from a team as a code owner April 2, 2026 21:24
@bewing
Copy link
Copy Markdown
Author

bewing commented Apr 2, 2026

FWIW, I swear I had opened a similar PR/ticket back in 2018....ish, but I can't find it now.

edit: found it

#197

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces support for multicast forwarding tables (AFT) within the OpenConfig model, specifically adding submodules for IPv4, IPv6, and MPLS P2MP multicast forwarding. It defines new groupings for multicast outgoing interface states and structural lists, which are integrated into the main AFT module. Review feedback recommends utilizing existing common groupings in the new IPv4 and IPv6 submodules to ensure consistency and minimize code duplication.

Comment on lines +110 to +134
leaf origin-protocol {
type identityref {
base "oc-pol-types:INSTALL_PROTOCOL_TYPE";
}
description
"The protocol from which the multicast forwarding entry was
learned, for example PIM, BGP (MVPN), or STATIC.";
}

leaf entry-metadata {
type binary {
length "0..8";
}
description
"Metadata persistently stored with the entry.";
}

leaf decapsulate-header {
type oc-aftt:encapsulation-header-type;
description
"When set, the local system removes the specified encapsulation
header before performing the multicast forwarding lookup. This
leaf is used at MPLS P2MP egress PE nodes where incoming MPLS
labels are popped before the (S,G) table is consulted.";
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To maintain consistency across the AFT model and reduce code duplication, please reuse the common groupings defined in openconfig-aft-common.yang. Specifically, aft-common-ip-state provides the origin-protocol and decapsulate-header leaves, while aft-common-entry-state provides the entry-metadata leaf.

    uses aft-common-entry-state;
    uses aft-common-ip-state;

Comment on lines +110 to +134
leaf origin-protocol {
type identityref {
base "oc-pol-types:INSTALL_PROTOCOL_TYPE";
}
description
"The protocol from which the multicast forwarding entry was
learned, for example PIM, BGP (MVPN), or STATIC.";
}

leaf entry-metadata {
type binary {
length "0..8";
}
description
"Metadata persistently stored with the entry.";
}

leaf decapsulate-header {
type oc-aftt:encapsulation-header-type;
description
"When set, the local system removes the specified encapsulation
header before performing the multicast forwarding lookup. This
leaf is used at MPLS P2MP egress PE nodes where incoming MPLS
labels are popped before the (S,G) table is consulted.";
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To maintain consistency across the AFT model and reduce code duplication, please reuse the common groupings defined in openconfig-aft-common.yang. Specifically, aft-common-ip-state provides the origin-protocol and decapsulate-header leaves, while aft-common-entry-state provides the entry-metadata leaf.

    uses aft-common-entry-state;
    uses aft-common-ip-state;

@dplore dplore moved this to Ready to discuss in OC Operator Review Apr 3, 2026
Comment on lines +119 to +134
leaf entry-metadata {
type binary {
length "0..8";
}
description
"Metadata persistently stored with the entry.";
}

leaf decapsulate-header {
type oc-aftt:encapsulation-header-type;
description
"When set, the local system removes the specified encapsulation
header before performing the multicast forwarding lookup. This
leaf is used at MPLS P2MP egress PE nodes where incoming MPLS
labels are popped before the (S,G) table is consulted.";
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All the forwarding information should be in the NH AFT entry like all the other places. multicast route should be pointing to NHG AFT like IPv4/IPv6 unicast routes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Ready to discuss

Development

Successfully merging this pull request may close these issues.

4 participants