Refactor LLDP management addresses and deprecate previous management address leafs
- (M) release/models/lldp/openconfig-lldp-types.yang
- Addition of management address related identities/typedef
- (M) release/models/lldp/openconfig-lldp.yang
- Addition of new management address hierarchy to match IEEE 802.1ab-2016 definition
- Deprecate flat singleton management-address leafs
- Leverage OC types vs. previous IETF types
Change Scope
The previous definition of LLDP management addresses had the following design flaws
- Singular management address (IPv4 or IPv6)
- Only partial support of the Management Address TLV per IEEE 802.1AB-2016
- Non-configurable management-address selection
- Lack of support for conveying local management addresses
This change encompasses:
- A new list structure to accomodate multiple management addresses
- Expansion of the structure to support relevant Management Address TLV fields
- Support to convey local system management addresses
- Commencing deprecation of the previous 2 leafs representing a single management address
- Removal of IETF types in favor of OC types (keeping same underlying types)
While openconfig-lldp has been bumped to 1.0.0 due to the import/type
changes from IETF -> OC, this change is mostly backwards compatible as we are
slowly deprecating the previous management-address related leafs.
This change better aligns Management Address TLV support to the IEEE 802.1AB-2016 spec.
Platform Implementations
For configurable management address interface derivation:
- Juniper JUNOS/EVO: https://www.juniper.net/documentation/us/en/software/junos/multicast-l2/topics/task/layer-2-services-lldp-configuring.html
- Arista EOS: https://www.arista.com/en/um-eos/eos-link-layer-discovery-protocol#xx1149426
Remainder is for IEEE 802.1AB-2016 compliance
New tree:
module: openconfig-lldp
+--rw lldp
+--rw config
| +--rw enabled? boolean
| +--rw hello-timer? uint64
| +--rw suppress-tlv-advertisement* identityref
| +--rw system-name? string
| +--rw system-description? string
| +--rw chassis-id? string
| +--rw chassis-id-type? oc-lldp-types:chassis-id-type
| +--rw management-interface? oc-if:interface-id
+--ro state
| +--ro enabled? boolean
| +--ro hello-timer? uint64
| +--ro suppress-tlv-advertisement* identityref
| +--ro system-name? string
| +--ro system-description? string
| +--ro chassis-id? string
| +--ro chassis-id-type? oc-lldp-types:chassis-id-type
| +--ro management-interface? oc-if:interface-id
| +--ro counters
| +--ro frame-in? oc-yang:counter64
| +--ro frame-out? oc-yang:counter64
| +--ro frame-error-in? oc-yang:counter64
| +--ro frame-discard? oc-yang:counter64
| +--ro tlv-discard? oc-yang:counter64
| +--ro tlv-unknown? oc-yang:counter64
| +--ro last-clear? oc-yang:date-and-time
| +--ro tlv-accepted? oc-yang:counter64
| +--ro entries-aged-out? oc-yang:counter64
+--ro management-addresses
| +--ro management-address* [address]
| +--ro address -> ../state/address
| +--ro state
| +--ro address? union
| +--ro interface-number? uint32
| +--ro interface-number-subtype? oc-lldp-types:mgmt-interface-number-subtype
+--rw interfaces
+--rw interface* [name]
+--rw name -> ../config/name
+--rw config
| +--rw name? oc-if:base-interface-ref
| +--rw enabled? boolean
+--ro state
| +--ro name? oc-if:base-interface-ref
| +--ro enabled? boolean
| +--ro counters
| +--ro frame-in? oc-yang:counter64
| +--ro frame-out? oc-yang:counter64
| +--ro frame-error-in? oc-yang:counter64
| +--ro frame-discard? oc-yang:counter64
| +--ro tlv-discard? oc-yang:counter64
| +--ro tlv-unknown? oc-yang:counter64
| +--ro last-clear? oc-yang:date-and-time
| +--ro frame-error-out? oc-yang:counter64
+--ro neighbors
+--ro neighbor* [id]
+--ro id -> ../state/id
+--ro config
+--ro state
| +--ro system-name? string
| +--ro system-description? string
| +--ro chassis-id? string
| +--ro chassis-id-type? oc-lldp-types:chassis-id-type
| +--ro management-interface? oc-if:interface-id
| +--ro id? string
| +--ro age? uint64
| +--ro last-update? int64
| +--ro ttl? uint16
| +--ro port-id? string
| +--ro port-id-type? oc-lldp-types:port-id-type
| +--ro port-description? string
| x--ro management-address? string
| x--ro management-address-type? string
+--ro management-addresses
| +--ro management-address* [address]
| +--ro address -> ../state/address
| +--ro state
| +--ro address? union
| +--ro interface-number? uint32
| +--ro interface-number-subtype? oc-lldp-types:mgmt-interface-number-subtype
+--ro custom-tlvs
| +--ro tlv* [type oui oui-subtype]
| +--ro type -> ../state/type
| +--ro oui -> ../state/oui
| +--ro oui-subtype -> ../state/oui-subtype
| +--ro config
| +--ro state
| +--ro type? int32
| +--ro oui? string
| +--ro oui-subtype? string
| +--ro value? binary
+--ro capabilities
+--ro capability* [name]
+--ro name -> ../state/name
+--ro config
+--ro state
+--ro name? identityref
+--ro enabled? boolean
Validated related example instance-data
{
"openconfig-interfaces:interfaces": {
"interface": [
{
"name": "et-0/0/0",
"config": {
"name": "et-0/0/0",
"type": "iana-if-type:ethernetCsmacd"
},
"state": {
"name": "et-0/0/0",
"type": "iana-if-type:ethernetCsmacd",
"loopback-mode": "NONE",
"enabled": true,
"admin-status": "UP",
"oper-status": "UP"
},
"hold-time": {
"state": {
"up": 0,
"down": 0
}
},
"penalty-based-aied": {
"state": {
"max-suppress-time": 0,
"decay-half-life": 0,
"suppress-threshold": 0,
"reuse-threshold": 0,
"flap-penalty": 0
}
}
},
{
"name": "et-1/0/3",
"config": {
"name": "et-1/0/3",
"type": "iana-if-type:ethernetCsmacd"
},
"state": {
"name": "et-1/0/3",
"type": "iana-if-type:ethernetCsmacd",
"loopback-mode": "NONE",
"enabled": true,
"admin-status": "UP",
"oper-status": "UP"
},
"hold-time": {
"state": {
"up": 0,
"down": 0
}
},
"penalty-based-aied": {
"state": {
"max-suppress-time": 0,
"decay-half-life": 0,
"suppress-threshold": 0,
"reuse-threshold": 0,
"flap-penalty": 0
}
}
}
]
},
"openconfig-lldp:lldp": {
"config": {
"enabled": true,
"management-interface": "vlan.100"
},
"state": {
"enabled": true,
"management-interface": "vlan.100"
},
"management-addresses": {
"management-address": [
{
"address": "10.100.1.1",
"state": {
"address": "10.100.1.1",
"interface-number": 504,
"interface-number-subtype": "IFINDEX"
}
},
{
"address": "2001:db8:ffff::1",
"state": {
"address": "2001:db8:ffff::1",
"interface-number": 504,
"interface-number-subtype": "IFINDEX"
}
}
]
},
"interfaces": {
"interface": [
{
"name": "et-0/0/0",
"config": {
"name": "et-0/0/0",
"enabled": true
},
"state": {
"name": "et-0/0/0",
"enabled": true
},
"neighbors": {
"neighbor": [
{
"id": "b8:ae:ed:7c:7c:a9-b8:ae:ed:7c:7c:a9",
"state": {
"id": "b8:ae:ed:7c:7c:a9-b8:ae:ed:7c:7c:a9"
},
"management-addresses": {
"management-address": [
{
"address": "10.200.1.32",
"state": {
"address": "10.200.1.32",
"interface-number": 512,
"interface-number-subtype": "IFINDEX"
}
},
{
"address": "2001:db8:f2dd::fe",
"state": {
"address": "2001:db8:f2dd::fe",
"interface-number": 512,
"interface-number-subtype": "IFINDEX"
}
}
]
}
}
]
}
},
{
"name": "et-1/0/3",
"config": {
"name": "et-1/0/3",
"enabled": true
},
"state": {
"name": "et-1/0/3",
"enabled": true
},
"neighbors": {
"neighbor": [
{
"id": "64:87:88:a5:a1:00-ge-0/1/1",
"state": {
"id": "64:87:88:a5:a1:00-ge-0/1/1"
},
"management-addresses": {
"management-address": [
{
"address": "83:b3:30:8f:21:00",
"state": {
"address": "83:b3:30:8f:21:00",
"interface-number": 603,
"interface-number-subtype": "IFINDEX"
}
}
]
}
}
]
}
}
]
}
}
}
/gcbrun
Major YANG version changes in commit b199a8213dad019f3c9927d09f151b166a2550f8:
openconfig-lldp.yang: 0.2.1 -> 1.0.0
/gcbrun
/gcbrun
Addressed structural (state) discussion item from today's OpenConfig community call and updated the tree/instance-data view above. cc: @dplore
/gcbrun
Reviewed in Apr 8, 2025 operators meeting. I think we don't need the SNMP derived fields unless there is strong evidence that operators require this.
+--ro management-addresses
| +--ro management-address* [address subtype]
| +--ro subtype -> ../state/subtype
| +--ro state
| +--ro subtype? identityref
| +--ro interface-number? uint32
| +--ro interface-number-subtype? oc-lldp-types:mgmt-interface-number-subtype
| +--ro oid? string
That just leaves:
+--ro management-addresses
| +--ro management-address* [address subtype]
| +--ro address -> ../state/address
| +--ro state
| +--ro address? union
There is a typo causing the github check errors
Reviewed in Apr 8, 2025 operators meeting. I think we don't need the SNMP derived fields unless there is strong evidence that operators require this.
+--ro management-addresses | +--ro management-address* [address subtype] | +--ro subtype -> ../state/subtype | +--ro state | +--ro subtype? identityref | +--ro interface-number? uint32 | +--ro interface-number-subtype? oc-lldp-types:mgmt-interface-number-subtype | +--ro oid? stringThat just leaves:
+--ro management-addresses | +--ro management-address* [address subtype] | +--ro address -> ../state/address | +--ro state | +--ro address? union
subtype must also remain as that is a key in the multi-key list
But I can remove oid, interface-number and interface-number-subtype if that is what we agree upon.
There is a typo causing the github check errors
Addressed in latest commit - this was due to optical-transport models referencing removed/unused LLDP groupings
/gcbrun
Updated in latest commit per comments from OC community call, tree view updated here: https://github.com/openconfig/public/pull/1250#issuecomment-2632097334
ro management-addresses
+--ro management-address* [address]
+--ro address -> ../state/address
+--ro state
+--ro address? union
+--ro interface-number? uint32
+--ro interface-number-subtype? oc-lldp-types:mgmt-interface-number-subtype
- Removal of OID and address subtype (address subtype can be derived)
- List reduced to single-key
-
addressis of type IPv4, IPv6 or MAC address -
interface-numberandinterface-number-subtypestill remain at this time as there does need to be an association of what the local/remote management-address is attached to (comments? @rgwilton @dplore)
@dplore @ElodinLaarz - looking for an update/review on this PR - thx
Thanks for the ping. I've moved this to "Ready to discuss" status for our invite only OC Operators review. The next review will be Nov 18, 2025.
/gcbrun
Updated in latest commit per comments from OC community call, tree view updated here: #1250 (comment)
ro management-addresses +--ro management-address* [address] +--ro address -> ../state/address +--ro state +--ro address? union +--ro interface-number? uint32 +--ro interface-number-subtype? oc-lldp-types:mgmt-interface-number-subtype
- Removal of OID and address subtype (address subtype can be derived)
- List reduced to single-key
addressis of type IPv4, IPv6 or MAC addressinterface-numberandinterface-number-subtypestill remain at this time as there does need to be an association of what the local/remote management-address is attached to (comments? @rgwilton @dplore)
My instinct is that we should ditch the interface-number and interface-number-subtype.
Does knowing the Ifindex help at all if the devices are being managed by YANG? Really, I think that LLDP needs to be updated to allow a string interface identifier to be used, or perhaps even just a flag to indicate whether the address represents a global management address for the device, or an address allocated to that port/link, which in theory could even be a link-local v6 address.
Updated in latest commit per comments from OC community call, tree view updated here: #1250 (comment)
ro management-addresses +--ro management-address* [address] +--ro address -> ../state/address +--ro state +--ro address? union +--ro interface-number? uint32 +--ro interface-number-subtype? oc-lldp-types:mgmt-interface-number-subtype
- Removal of OID and address subtype (address subtype can be derived)
- List reduced to single-key
addressis of type IPv4, IPv6 or MAC addressinterface-numberandinterface-number-subtypestill remain at this time as there does need to be an association of what the local/remote management-address is attached to (comments? @rgwilton @dplore)My instinct is that we should ditch the interface-number and interface-number-subtype.
Does knowing the Ifindex help at all if the devices are being managed by YANG? Really, I think that LLDP needs to be updated to allow a string interface identifier to be used, or perhaps even just a flag to indicate whether the address represents a global management address for the device, or an address allocated to that port/link, which in theory could even be a link-local v6 address.
I agree in principal but my only argument here is the transition, parity and relationship aspects consumer side/operations may have built a dependence on where the new "world" defines an alternate approach everyone has to adjust to immediately.
Sure ifindex is history brought forward that is questionable if we wipe the slate clean w/ alternate approaches but a piece of data that may need to be relational to how alternate datasets are consumed.
Implementations may also need to alter existing behaviors if ifindex is the only currently supported subtype.
I don't feel strongly about this if we want to reduce down to a scoped use here and not be concerned w/ parity to the underlying protocol definition.
If we need to maintain the relationship to an interface, could we add an interface-ref instead of the snmp interface-number and interface-number-subtype?
If there is evidence of an operational need for the snmp devired attributes in OpenConfig, we could include them. But without that,I think we should leave them out. Currently the PR doesn't have this evidence?
If we need to maintain the relationship to an interface, could we add an interface-ref instead of the snmp
interface-numberandinterface-number-subtype?
Usability wise I'd say sure.... but we have a tie to the underlying LLDP protocol capabilities here and for remote neighbors, a TLV encodes this in such a way that the interface "name" is not sent in the PDU. An interface-ref is also not possible here because for remote neighbors this does not associate back to the local data tree.
If there is evidence of an operational need for the snmp devired attributes in OpenConfig, we could include them. But without that,I think we should leave them out. Currently the PR doesn't have this evidence?
It's about parity to existing implementations and the LLDP protocol capabilites.
To pick this apart a bit more, such proposals are driven by some operational demand at some point in time wondering "How can I do this thing or get at this data via OpenConfig" which in this case is met with - well it's incomplete so changes are necessary or don't use OpenConfig. If we choose not to add such items to OpenConfig modeling, whether good or bad or maybe an alternate way to approach the situation, we will always be incomplete and thus forcing folks either away from OC or taking on complexities of mixed model-sets needing to draw out relationships offline.
/gcbrun
If we need to maintain the relationship to an interface, could we add an interface-ref instead of the snmp
interface-numberandinterface-number-subtype?Usability wise I'd say sure.... but we have a tie to the underlying LLDP protocol capabilities here and for remote neighbors, a TLV encodes this in such a way that the interface "name" is not sent in the PDU. An
interface-refis also not possible here because for remote neighbors this does not associate back to the local data tree.If there is evidence of an operational need for the snmp devired attributes in OpenConfig, we could include them. But without that,I think we should leave them out. Currently the PR doesn't have this evidence?
It's about parity to existing implementations and the LLDP protocol capabilites.
To pick this apart a bit more, such proposals are driven by some operational demand at some point in time wondering "How can I do this thing or get at this data via OpenConfig" which in this case is met with - well it's incomplete so changes are necessary or don't use OpenConfig. If we choose not to add such items to OpenConfig modeling, whether good or bad or maybe an alternate way to approach the situation, we will always be incomplete and thus forcing folks either away from OC or taking on complexities of mixed model-sets needing to draw out relationships offline.
I appreciate the need to support transitioning from snmp to OC and we should make decisions to help support that.
I discovered OC does in fact report the SNMP IfIndex, so I guess we have some precedent of exposing and "interop" with SNMP regarding IfIndex. https://github.com/openconfig/public/blob/834db2b62f8ee66ee6c8f8b567569ad81caac34e/release/models/interfaces/openconfig-interfaces.yang#L611-L619
I think based on that precedence I'm ok with allowing in interface-number and interface-number-subtype.
Reviewed in Dec 9 2025 OC Operators meeting without objection. There was also explicit support to include this as there is a need for SNMP / LLDP interoperability.
Please also fix linter issue: lldp/openconfig-lldp.yang (441): error: All key arguments of a list should be quoted (address is not)
Added to last-call for comments. This will merge on Dec 23, 2025
Please also fix linter issue: lldp/openconfig-lldp.yang (441): error: All key arguments of a list should be quoted (address is not)
Addressed in latest commit
/gcbrun
Diff view with changes highlighted:
module: openconfig-lldp
+--rw lldp
+--rw config
| +--rw enabled? boolean
| +--rw hello-timer? uint64
| +--rw suppress-tlv-advertisement* identityref
| +--rw system-name? string
| +--rw system-description? string
| +--rw chassis-id? string
| +--rw chassis-id-type? oc-lldp-types:chassis-id-type
+ | +--rw management-interface? oc-if:interface-id
+--ro state
| +--ro enabled? boolean
| +--ro hello-timer? uint64
| +--ro suppress-tlv-advertisement* identityref
| +--ro system-name? string
| +--ro system-description? string
| +--ro chassis-id? string
| +--ro chassis-id-type? oc-lldp-types:chassis-id-type
+ | +--ro management-interface? oc-if:interface-id
| +--ro counters
- | +--ro frame-in? yang:counter64
- | +--ro frame-out? yang:counter64
- | +--ro frame-error-in? yang:counter64
- | +--ro frame-discard? yang:counter64
- | +--ro tlv-discard? yang:counter64
- | +--ro tlv-unknown? yang:counter64
- | +--ro last-clear? yang:date-and-time
- | +--ro tlv-accepted? yang:counter64
- | +--ro entries-aged-out? yang:counter64
+ | +--ro frame-in? oc-yang:counter64
+ | +--ro frame-out? oc-yang:counter64
+ | +--ro frame-error-in? oc-yang:counter64
+ | +--ro frame-discard? oc-yang:counter64
+ | +--ro tlv-discard? oc-yang:counter64
+ | +--ro tlv-unknown? oc-yang:counter64
+ | +--ro last-clear? oc-yang:date-and-time
+ | +--ro tlv-accepted? oc-yang:counter64
+ | +--ro entries-aged-out? oc-yang:counter64
+ +--ro management-addresses
+ | +--ro management-address* [address]
+ | +--ro address -> ../state/address
+ | +--ro state
+ | +--ro address? union
+ | +--ro interface-number? uint32
+ | +--ro interface-number-subtype? oc-lldp-types:mgmt-interface-number-subtype
+--rw interfaces
+--rw interface* [name]
+--rw name -> ../config/name
+--rw config
| +--rw name? oc-if:base-interface-ref
| +--rw enabled? boolean
+--ro state
| +--ro name? oc-if:base-interface-ref
| +--ro enabled? boolean
| +--ro counters
- | +--ro frame-in? yang:counter64
- | +--ro frame-out? yang:counter64
- | +--ro frame-error-in? yang:counter64
- | +--ro frame-discard? yang:counter64
- | +--ro tlv-discard? yang:counter64
- | +--ro tlv-unknown? yang:counter64
- | +--ro last-clear? yang:date-and-time
- | +--ro frame-error-out? yang:counter64
+ | +--ro frame-in? oc-yang:counter64
+ | +--ro frame-out? oc-yang:counter64
+ | +--ro frame-error-in? oc-yang:counter64
+ | +--ro frame-discard? oc-yang:counter64
+ | +--ro tlv-discard? oc-yang:counter64
+ | +--ro tlv-unknown? oc-yang:counter64
+ | +--ro last-clear? oc-yang:date-and-time
+ | +--ro frame-error-out? oc-yang:counter64
+--ro neighbors
+--ro neighbor* [id]
+--ro id -> ../state/id
+--ro config
+--ro state
| +--ro system-name? string
| +--ro system-description? string
| +--ro chassis-id? string
| +--ro chassis-id-type? oc-lldp-types:chassis-id-type
+ | +--ro management-interface? oc-if:interface-id
| +--ro id? string
| +--ro age? uint64
| +--ro last-update? int64
| +--ro ttl? uint16
| +--ro port-id? string
| +--ro port-id-type? oc-lldp-types:port-id-type
| +--ro port-description? string
- | +--ro management-address? string
- | +--ro management-address-type? string
+ | x--ro management-address? string
+ | x--ro management-address-type? string
+ +--ro management-addresses
+ | +--ro management-address* [address]
+ | +--ro address -> ../state/address
+ | +--ro state
+ | +--ro address? union
+ | +--ro interface-number? uint32
+ | +--ro interface-number-subtype? oc-lldp-types:mgmt-interface-number-subtype
+--ro custom-tlvs
| +--ro tlv* [type oui oui-subtype]
| +--ro type -> ../state/type
| +--ro oui -> ../state/oui
| +--ro oui-subtype -> ../state/oui-subtype
| +--ro config
| +--ro state
| +--ro type? int32
| +--ro oui? string
| +--ro oui-subtype? string
| +--ro value? binary
+--ro capabilities
+--ro capability* [name]
+--ro name -> ../state/name
+--ro config
+--ro state
+--ro name? identityref
+--ro enabled? boolean
module: openconfig-terminal-device
+--rw terminal-device
+--rw logical-channels
| +--rw channel* [index]
| +--rw ethernet
| | +--rw lldp
| | +--rw config
| | | +--rw enabled? boolean
| | | +--rw snooping? boolean
| | +--ro state
| | | +--ro enabled? boolean
| | | +--ro snooping? boolean
| | | +--ro counters
- | | | +--ro frame-in? yang:counter64
- | | | +--ro frame-out? yang:counter64
- | | | +--ro frame-error-in? yang:counter64
- | | | +--ro frame-discard? yang:counter64
- | | | +--ro tlv-discard? yang:counter64
- | | | +--ro tlv-unknown? yang:counter64
- | | | +--ro last-clear? yang:date-and-time
- | | | +--ro frame-error-out? yang:counter64
+ | | | +--ro frame-in? oc-yang:counter64
+ | | | +--ro frame-out? oc-yang:counter64
+ | | | +--ro frame-error-in? oc-yang:counter64
+ | | | +--ro frame-discard? oc-yang:counter64
+ | | | +--ro tlv-discard? oc-yang:counter64
+ | | | +--ro tlv-unknown? oc-yang:counter64
+ | | | +--ro last-clear? oc-yang:date-and-time
+ | | | +--ro frame-error-out? oc-yang:counter64
| | +--ro neighbors
| | +--ro neighbor* [id]
| | +--ro id -> ../state/id
| | +--ro config
| | +--ro state
| | | +--ro system-name? string
| | | +--ro system-description? string
| | | +--ro chassis-id? string
| | | +--ro chassis-id-type? oc-lldp-types:chassis-id-type
+ | | | +--ro management-interface? oc-if:interface-id
| | | +--ro id? string
| | | +--ro age? uint64
| | | +--ro last-update? int64
| | | +--ro ttl? uint16
| | | +--ro port-id? string
| | | +--ro port-id-type? oc-lldp-types:port-id-type
| | | +--ro port-description? string
- | | | +--ro management-address? string
- | | | +--ro management-address-type? string
+ | | | x--ro management-address? string
+ | | | x--ro management-address-type? string
The OC toolchain has a problem with the state/management-address leaf matching a list, even though the list is in a child container. Can you rename the newly introduced list? Perhaps:
mgmt-addresses/mgmt-address
F1209 22:54:52.331780 3376 generator.go:388] ERROR Generating GoStruct Code: /openconfig-lldp/lldp/interfaces/interface/neighbors/neighbor/state/management-address was duplicate with /openconfig-lldp/lldp/interfaces/interface/neighbors/neighbor/management-addresses/management-address
/gcbrun