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
32 changes: 32 additions & 0 deletions aci-preupgrade-validation-script.py
Original file line number Diff line number Diff line change
Expand Up @@ -6007,6 +6007,37 @@ def apic_vmm_inventory_sync_faults_check(**kwargs):
recommended_action=recommended_action,
doc_url=doc_url)


@check_wrapper(check_title='OSPFv3 IPSec ESP ESN stuck in 0')
def ospfv3_ipsec_esn_check(tversion, **kwargs):
result = PASS
headers = ["dn","Policy Name"]
data = []
recommended_action = 'Contact cisco TAC for Support.'
doc_url = 'https://datacenter.github.io/ACI-Pre-Upgrade-Validation-Script/validations/#ospfv3-ipsec-esn-stuck-in-0'

if not tversion:
return Result(result=MANUAL, msg=TVER_MISSING)

if (tversion.newer_than("6.1(1f)") or tversion.same_as("6.1(1f)")) and (tversion.older_than("6.1(4h)") or tversion.same_as("6.1(4h)")):
esp_config_api = 'fvProtoAuthPol.json?query-target-filter=and(wcard(fvProtoAuthPol.proto,"esp"))'

esp_configs = icurl('class', esp_config_api)

if esp_configs:
for esp in esp_configs:
policy_name = esp['fvProtoAuthPol']['attributes']['name']
dn = esp['fvProtoAuthPol']['attributes']['dn']
data.append([dn, policy_name])

else:
return Result(result=NA, msg=VER_NOT_AFFECTED)

if data:
result = FAIL_O

return Result(result=result, headers=headers, data=data, recommended_action=recommended_action, doc_url=doc_url)

# ---- Script Execution ----


Expand Down Expand Up @@ -6168,6 +6199,7 @@ class CheckManager:
standby_sup_sync_check,
isis_database_byte_check,
configpush_shard_check,
ospfv3_ipsec_esn_check,

]
ssh_checks = [
Expand Down
14 changes: 13 additions & 1 deletion docs/docs/validations.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,8 @@ Items | Defect | This Script
[Observer Database Size][d25] | CSCvw45531 | :white_check_mark: | :no_entry_sign:
[Stale pconsRA Object][d26] | CSCwp22212 | :warning:{title="Deprecated"} | :no_entry_sign:
[ISIS DTEPs Byte Size][d27] | CSCwp15375 | :white_check_mark: | :no_entry_sign:
[Policydist configpushShardCont Crash][d28] | CSCwp95515 | :white_check_mark: |
[Policydist configpushShardCont Crash][d28] | CSCwp95515 | :white_check_mark: | :no_entry_sign:
[OSPFv3 IPSec ESP ESN stuck in 0][d29] | CSCwp66238 | :white_check_mark: | :no_entry_sign:

[d1]: #ep-announce-compatibility
[d2]: #eventmgr-db-size-defect-susceptibility
Expand Down Expand Up @@ -220,6 +221,7 @@ Items | Defect | This Script
[d26]: #stale-pconsra-object
[d27]: #isis-dteps-byte-size
[d28]: #policydist-configpushshardcont-crash
[d29]: #ospfv3-ipsec-esp-esn-stuck-in-0


## General Check Details
Expand Down Expand Up @@ -2614,6 +2616,15 @@ Due to [CSCwp95515][59], upgrading to an affected version while having any `conf
If any instances of `configpushShardCont` are flagged by this script, Cisco TAC must be contacted to identify and resolve the underlying issue before performing the upgrade.


### OSPFv3 IPSec ESP ESN stuck in 0

OSPFv3 (Open Shortest Path First for IPv6) can be protected with IPSec ESP, which encrypts and authenticates routing protocol packets. According to RFC4303, the ESP sequence number is a per-SA (Security Association) 32-bit counter that must increment by one for each packet sent.

The bug [CSCwp66238][62] states that all ESP packets sent between ACI and NX-OS have seq=0, no matter how many packets are sent. The ESP implementation in both ACI and NX-OS is not incrementing the ESP sequence number per packet. The value stays at 0 after the Security Association (SA) is established. OSPFv3 adjacencies with ESP encryption may fail or become unstable when traversing devices that check for ESP sequence number increments, such as security appliances.

The script identifies if the ESP protocol is configurd by checking the `fvProtoAuthPol` object. If present, then upgrade to the fixed version.


[0]: https://github.com/datacenter/ACI-Pre-Upgrade-Validation-Script
[1]: https://www.cisco.com/c/dam/en/us/td/docs/Website/datacenter/apicmatrix/index.html
[2]: https://www.cisco.com/c/en/us/support/switches/nexus-9000-series-switches/products-release-notes-list.html
Expand Down Expand Up @@ -2676,3 +2687,4 @@ If any instances of `configpushShardCont` are flagged by this script, Cisco TAC
[59]: https://bst.cloudapps.cisco.com/bugsearch/bug/CSCwp95515
[60]: https://www.cisco.com/c/en/us/solutions/collateral/data-center-virtualization/application-centric-infrastructure/white-paper-c11-743951.html#Inter
[61]: https://www.cisco.com/c/en/us/solutions/collateral/data-center-virtualization/application-centric-infrastructure/white-paper-c11-743951.html#EnablePolicyCompression
[62]: https://bst.cloudapps.cisco.com/bugsearch/bug/CSCwp66238
71 changes: 71 additions & 0 deletions tests/checks/ospfv3_ipsec_esn_check/esp_enabled.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
[
{
"fvProtoAuthPol": {
"attributes": {
"annotation": "",
"childAction": "",
"descr": "",
"dn": "uni/tn-ten1ant/protoAuthPol-ipsec_pol1",
"extMngdBy": "",
"lcOwn": "local",
"modTs": "2025-12-23T10:13:32.132+00:00",
"monPolDn": "uni/tn-common/monepg-default",
"name": "ipsec_pol1",
"nameAlias": "",
"ownerKey": "",
"ownerTag": "",
"proto": "esp",
"spi": "256",
"status": "",
"uid": "15374",
"userdom": ":all:"
}
}
},
{
"fvProtoAuthPol": {
"attributes": {
"annotation": "",
"childAction": "",
"descr": "",
"dn": "uni/tn-ten1ant/protoAuthPol-ipsec_pol2",
"extMngdBy": "",
"lcOwn": "local",
"modTs": "2025-12-23T10:13:32.132+00:00",
"monPolDn": "uni/tn-common/monepg-pol1_1",
"name": "ipsec_pol2",
"nameAlias": "",
"ownerKey": "",
"ownerTag": "",
"proto": "esp",
"spi": "256",
"status": "",
"uid": "15374",
"userdom": ":all:"
}
}
},
{
"fvProtoAuthPol": {
"attributes": {
"annotation": "",
"childAction": "",
"descr": "",
"dn": "uni/tn-ten2ant/protoAuthPol-ipsec_pol3",
"extMngdBy": "",
"lcOwn": "local",
"modTs": "2025-12-23T10:13:32.132+00:00",
"monPolDn": "uni/tn-common/monepg-pol5_2",
"name": "ipsec_pol3",
"nameAlias": "",
"ownerKey": "",
"ownerTag": "",
"proto": "esp",
"spi": "256",
"status": "",
"uid": "15374",
"userdom": ":all:"
}
}
}
]
1 change: 1 addition & 0 deletions tests/checks/ospfv3_ipsec_esn_check/esp_not_enabled.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
93 changes: 93 additions & 0 deletions tests/checks/ospfv3_ipsec_esn_check/test_ospfv3_ipsec_esn_check.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import os
import pytest
import logging
import importlib
from helpers.utils import read_data

log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))

script = importlib.import_module("aci-preupgrade-validation-script")

test_function = "ospfv3_ipsec_esn_check"

esp_config_api = 'fvProtoAuthPol.json?query-target-filter=and(wcard(fvProtoAuthPol.proto,"esp"))'

@pytest.mark.parametrize(
"icurl_outputs, tversion, expected_result",
[
# PASS cases
# Targer version in affected range, IPSec ESP not enabled
(
{esp_config_api: read_data(dir, "esp_not_enabled.json")},
"6.1(3f)",
script.PASS,
),
# Target version in lower affected version, IPSec ESP not enabled
(
{esp_config_api: read_data(dir, "esp_not_enabled.json")},
"6.1(1f)",
script.PASS,
),
# Target version in upper affected version, IPSec ESP not enabled
(
{esp_config_api: read_data(dir, "esp_not_enabled.json")},
"6.1(4h)",
script.PASS,
),
# Not Applicable cases
# Target version below affected range
(
{esp_config_api: read_data(dir, "esp_enabled.json")},
"6.0(9e)",
script.NA,
),
# Target version above affected range
(
{esp_config_api: read_data(dir, "esp_enabled.json")},
"6.2(2f)",
script.NA,
),
# Manual cases
# Target version not provided
(
{esp_config_api: read_data(dir, "esp_enabled.json")},
"",
script.MANUAL,
),
# Fail cases
# Target version in affected range, IPSec ESP enabled
(
{esp_config_api: read_data(dir, "esp_enabled.json")},
"6.1(3f)",
script.FAIL_O,
),
# Target version in lower affected version, IPSec ESP enabled
(
{esp_config_api: read_data(dir, "esp_enabled.json")},
"6.1(1f)",
script.FAIL_O,
),
# Target version in upper affected version, IPSec ESP enabled
(
{esp_config_api: read_data(dir, "esp_enabled.json")},
"6.1(4h)",
script.FAIL_O,
),
],
ids = [
"PASS - Target version in affected range, IPSec ESP not enabled",
"PASS - Target version in lower affected version, IPSec ESP not enabled",
"PASS - Target version in upper affected version, IPSec ESP not enabled",
"NA - Target version below affected range",
"NA - Target version above affected range",
"MANUAL - Target version not provided",
"FAIL - Target version in affected range, IPSec ESP enabled",
"FAIL - Target version in lower affected version, IPSec ESP enabled",
"FAIL - Target version in upper affected version, IPSec ESP enabled",
]
)
def test_ospfv3_ipsec_esn_check(run_check, mock_icurl, tversion, expected_result):
"""Test OSPFv3 IPsec ESN Check"""
result = run_check(tversion = script.AciVersion(tversion) if tversion else None)
assert result.result == expected_result