diff --git a/CMakeLists.txt b/CMakeLists.txt
index c8e2e918..f62f96bb 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -41,7 +41,7 @@ endif()
# Generic version of not only the library. Major version is reserved for really big changes of the project,
# minor version changes with added functionality (new tool, functionality of the tool or library, ...) and
# micro version is changed with a set of small changes or bugfixes anywhere in the project.
-set(NP2SRV_VERSION 2.8.10)
+set(NP2SRV_VERSION 2.8.11)
# libyang required version
set(LIBYANG_DEP_VERSION 5.1.3)
diff --git a/modules/ietf-yp-transport-capabilities@2026-05-15.yang b/modules/ietf-yp-transport-capabilities@2026-05-15.yang
new file mode 100644
index 00000000..513c3ac1
--- /dev/null
+++ b/modules/ietf-yp-transport-capabilities@2026-05-15.yang
@@ -0,0 +1,146 @@
+module ietf-yp-transport-capabilities {
+ yang-version 1.1;
+ namespace
+ "urn:ietf:params:xml:ns:yang:ietf-yp-transport-capabilities";
+ prefix ntc;
+
+ import ietf-subscribed-notifications {
+ prefix sn;
+ reference
+ "RFC 8639: Subscription to YANG Notifications";
+ }
+ import ietf-system-capabilities {
+ prefix sysc;
+ reference
+ "RFC 9196: YANG Modules Describing Capabilities for
+ Systems and Datastore Update Notifications";
+ }
+ import ietf-notification-capabilities {
+ prefix notc;
+ reference
+ "RFC 9196: YANG Modules Describing Capabilities for
+ Systems and Datastore Update Notifications";
+ }
+ import ietf-tls-common {
+ prefix tlscmn;
+ reference
+ "RFC 9645: YANG Groupings for TLS Clients and TLS Servers";
+ }
+
+ organization
+ "IETF NETCONF (Network Configuration) Working Group";
+ contact
+ "WG Web:
+ WG List:
+
+ Authors: Qin Wu
+
+ Qiufang Ma
+
+ Alex Huang Feng
+
+ Thomas Graf
+ ";
+ description
+ "This module defines an extension to YANG-Push
+ Notification Capabilities model that provides additional
+ transport specific capabilities for YANG notifications.
+
+ The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', 'SHALL
+ NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', 'NOT RECOMMENDED',
+ 'MAY', and 'OPTIONAL' in this document are to be interpreted as
+ described in BCP 14 (RFC 2119) (RFC 8174) when, and only when,
+ they appear in all capitals, as shown here.
+
+ Copyright (c) 2026 IETF Trust and the persons identified as
+ authors of the code. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, is permitted pursuant to, and subject
+ to the license terms contained in, the Revised BSD License
+ set forth in Section 4.c of the IETF Trust's Legal Provisions
+ Relating to IETF Documents
+ (http://trustee.ietf.org/license-info).
+
+ This version of this YANG module is part of RFC XXXX
+ (https://www.rfc-editor.org/info/rfcXXXX); see the RFC itself
+ for full legal notices.";
+
+ revision 2026-05-15 {
+ description
+ "Initial revision.";
+ reference
+ "RFC XXXX: YANG Notifications Transport Capabilities";
+ }
+
+ identity security-protocol {
+ description
+ "Identity for YANG notifications security protocols.";
+ }
+
+ identity dtls12 {
+ base security-protocol;
+ description
+ "Indicates DTLS Protocol Version 1.2. DTLS 1.2 is obsolete,
+ and thus it is NOT RECOMMENDED to enable this feature.";
+ reference
+ "RFC 6347: The Datagram Transport Layer Security (DTLS)
+ Protocol Version 1.2.";
+ }
+
+ identity dtls13 {
+ base security-protocol;
+ description
+ "Indicates DTLS Protocol Version 1.3.";
+ reference
+ "RFC 9147: The Datagram Transport Layer Security (DTLS)
+ Protocol Version 1.3.";
+ }
+
+ augment "/sysc:system-capabilities"
+ + "/notc:subscription-capabilities" {
+ description
+ "Adds subscription-related system level capabilities.";
+ container transport-capabilities {
+ description
+ "Specifies capabilities related to YANG-Push transports.";
+ list transport-capability {
+ key "transport-protocol";
+ description
+ "Indicates supported YANG notifications transport protocol
+ for Subscribed Notifications [RFC8639] and YANG-Push
+ [RFC8641]. Defines the supported transports, security
+ protocols and supported notification encodings.";
+ leaf transport-protocol {
+ type identityref {
+ base sn:transport;
+ }
+ description
+ "Indicates the supported YANG notifications transport
+ protocol for Subscribed Notifications [RFC8639] and
+ YANG-Push [RFC8641].";
+ }
+ leaf-list security-protocol {
+ type union {
+ type identityref {
+ base security-protocol;
+ }
+ type identityref {
+ base tlscmn:tls-version-base;
+ }
+ }
+ description
+ "Indicates the supported YANG notifications transport
+ encryption protocols.";
+ }
+ leaf-list encoding-format {
+ type identityref {
+ base sn:encoding;
+ }
+ description
+ "Indicates supported encoding formats for YANG notifications.";
+ }
+ }
+ }
+ }
+}
diff --git a/scripts/common.sh b/scripts/common.sh
index e2e49758..2d4ffabb 100644
--- a/scripts/common.sh
+++ b/scripts/common.sh
@@ -15,6 +15,7 @@ NP2_MODULES=(
"netopeer-notifications@2026-01-05.yang"
"ietf-system-capabilities@2022-02-17.yang"
"ietf-notification-capabilities@2022-02-17.yang"
+"ietf-yp-transport-capabilities@2026-05-15.yang"
"ietf-netconf-private-candidate@2026-02-03.yang -e private-candidate"
)
diff --git a/src/main.c b/src/main.c
index 083dfb12..3e98889f 100644
--- a/src/main.c
+++ b/src/main.c
@@ -531,6 +531,114 @@ np2srv_content_id_cb(void *UNUSED(user_data))
return strdup(buf);
}
+/**
+ * @brief Add UDP-Notif transport capability to the transport-capabilities node.
+ *
+ * @param[in] transport_capas transport-capabilities container node.
+ * @param[in] yptc_mod ietf-yp-transport-capabilities module.
+ * @param[in] unt_mod ietf-udp-notif-transport module.
+ * @param[in] sn_mod ietf-subscribed-notifications module, may be NULL.
+ * @return 0 on success, -1 on error.
+ */
+static int
+np2srv_add_udp_notif_transport_capability(struct lyd_node *transport_capas,
+ const struct lys_module *yptc_mod, const struct lys_module *unt_mod,
+ const struct lys_module *sn_mod)
+{
+ struct lyd_node *cap = NULL;
+ int rc = 0;
+
+ if (lyd_new_list(transport_capas, yptc_mod, "transport-capability", 0, &cap,
+ "ietf-udp-notif-transport:udp-notif")) {
+ ERR("Failed to create transport-capability.");
+ rc = -1;
+ goto cleanup;
+ }
+
+ /* security-protocol */
+ if (lys_feature_value(unt_mod, "dtls") == LY_SUCCESS) {
+ if (lyd_new_term(cap, yptc_mod, "security-protocol", "ietf-yp-transport-capabilities:dtls12", 0, NULL)) {
+ ERR("Failed to create security-protocol dtls12.");
+ rc = -1;
+ goto cleanup;
+ }
+ if (lyd_new_term(cap, yptc_mod, "security-protocol", "ietf-yp-transport-capabilities:dtls13", 0, NULL)) {
+ ERR("Failed to create security-protocol dtls13.");
+ rc = -1;
+ goto cleanup;
+ }
+ }
+
+ /* encoding-format */
+ if (sn_mod && (lys_feature_value(sn_mod, "encode-xml") == LY_SUCCESS)) {
+ if (lyd_new_term(cap, yptc_mod, "encoding-format", "ietf-subscribed-notifications:encode-xml", 0, NULL)) {
+ ERR("Failed to create encoding-format encode-xml.");
+ rc = -1;
+ goto cleanup;
+ }
+ }
+ if (sn_mod && (lys_feature_value(sn_mod, "encode-json") == LY_SUCCESS)) {
+ if (lyd_new_term(cap, yptc_mod, "encoding-format", "ietf-subscribed-notifications:encode-json", 0, NULL)) {
+ ERR("Failed to create encoding-format encode-json.");
+ rc = -1;
+ goto cleanup;
+ }
+ }
+ if (lys_feature_value(unt_mod, "encode-cbor") == LY_SUCCESS) {
+ if (lyd_new_term(cap, yptc_mod, "encoding-format", "ietf-udp-notif-transport:encode-cbor", 0, NULL)) {
+ ERR("Failed to create encoding-format encode-cbor.");
+ rc = -1;
+ goto cleanup;
+ }
+ }
+
+cleanup:
+ return rc;
+}
+
+/**
+ * @brief Add transport capabilities to a subscription-capabilities node.
+ *
+ * @param[in] subs_capas subscription-capabilities node.
+ * @param[in] ly_ctx libyang context.
+ * @return SR_ERR_OK on success, -1 on error.
+ */
+static int
+np2srv_add_transport_capabilities(struct lyd_node *subs_capas, const struct ly_ctx *ly_ctx)
+{
+ const struct lys_module *yptc_mod, *unt_mod, *sn_mod;
+ struct lyd_node *transport_capas = NULL;
+ int rc = SR_ERR_OK;
+
+ yptc_mod = ly_ctx_get_module_implemented(ly_ctx, "ietf-yp-transport-capabilities");
+ if (!yptc_mod) {
+ goto cleanup;
+ }
+
+ unt_mod = ly_ctx_get_module_implemented(ly_ctx, "ietf-udp-notif-transport");
+ if (!unt_mod) {
+ /* no transport module implemented, do not create an empty container */
+ goto cleanup;
+ }
+
+ /* transport-capabilities */
+ if (lyd_new_path(subs_capas, ly_ctx, "ietf-yp-transport-capabilities:transport-capabilities",
+ NULL, 0, &transport_capas)) {
+ ERR("Failed to create transport-capabilities.");
+ rc = -1;
+ goto cleanup;
+ }
+
+ sn_mod = ly_ctx_get_module_implemented(ly_ctx, "ietf-subscribed-notifications");
+
+ if ((rc = np2srv_add_udp_notif_transport_capability(transport_capas, yptc_mod, unt_mod, sn_mod))) {
+ goto cleanup;
+ }
+
+cleanup:
+ return rc;
+}
+
/**
* @brief Add subscription capabilities to a node
* @param[in,out] node is a part of ietf-system-capabilities.
@@ -568,6 +676,7 @@ np2srv_add_subscription_capabilities(struct lyd_node *node, const struct ly_ctx
goto cleanup;
}
}
+
cleanup:
return rc;
}
@@ -580,7 +689,7 @@ np2srv_capabilities_oper_cb(sr_session_ctx_t *session, uint32_t sub_id,
const char *module_name, const char *path, const char *request_xpath,
uint32_t request_id, struct lyd_node **parent, void *private_data)
{
- struct lyd_node *sys_capas = NULL, *datastore_capas, *per_node_capas;
+ struct lyd_node *sys_capas = NULL, *datastore_capas, *per_node_capas, *global_subs_capas;
const struct ly_ctx *ly_ctx;
int rc = SR_ERR_OK;
uint32_t ds;
@@ -645,6 +754,15 @@ np2srv_capabilities_oper_cb(sr_session_ctx_t *session, uint32_t sub_id,
goto cleanup;
}
+ /* transport capabilities (subscription-capabilities was just created above, so it must be found) */
+ lyd_find_path(sys_capas, "ietf-notification-capabilities:subscription-capabilities", 0, &global_subs_capas);
+ assert(global_subs_capas);
+ if (np2srv_add_transport_capabilities(global_subs_capas, ly_ctx)) {
+ ERR("Failed to add transport-capabilities.");
+ rc = -1;
+ goto cleanup;
+ }
+
cleanup:
if (rc) {
lyd_free_tree(sys_capas);
diff --git a/tests/modules/ietf-subscribed-notif-receivers@2024-02-01.yang b/tests/modules/ietf-subscribed-notif-receivers@2024-02-01.yang
new file mode 100644
index 00000000..b3ebb8f9
--- /dev/null
+++ b/tests/modules/ietf-subscribed-notif-receivers@2024-02-01.yang
@@ -0,0 +1,110 @@
+module ietf-subscribed-notif-receivers {
+ yang-version 1.1;
+ namespace
+ "urn:ietf:params:xml:ns:yang:ietf-subscribed-notif-receivers";
+ prefix "snr";
+
+ import ietf-subscribed-notifications {
+ prefix sn;
+ reference
+ "RFC 8639: Subscription to YANG Notifications";
+ }
+
+ organization
+ "IETF NETCONF Working Group";
+
+ contact
+ "WG Web:
+ WG List:
+
+ Authors: Mahesh Jethanandani (mjethanandani at gmail dot com)
+ Kent Watsen (kent plus ietf at watsen dot net)";
+
+ description
+ "This YANG module is implemented by Publishers implementing
+ the 'ietf-subscribed-notifications' module defined in RFC 8639.
+
+ While this module is defined in RFC XXXX, which primarily
+ defines an HTTPS-based transport for notifications, this module
+ is not HTTP-specific. It is a generic extension that can be
+ used by any 'notif' transport.
+
+ This module defines two 'augment' statements. One statement
+ augments a 'container' statement called 'receiver-instances'
+ into the top-level 'subscriptions' container. The other
+ statement, called 'receiver-instance-ref', augments a 'leaf'
+ statement into each 'receiver' that references one of the
+ afore mentioned receiver instances. This indirection enables
+ multiple configured subscriptions to send notifications to
+ the same receiver instance.
+
+ Copyright (c) 2024 IETF Trust and the persons identified as
+ authors of the code. All rights reserved.
+ Redistribution and use in source and binary forms, with or
+ without modification, is permitted pursuant to, and subject
+ to the license terms contained in, the Revised BSD
+ License set forth in Section 4.c of the IETF Trust's Legal
+ Provisions Relating to IETF Documents
+ (http://trustee.ietf.org/license-info).
+
+ This version of this YANG module is part of RFC XXXX; see
+ the RFC itself for full legal notices.
+
+ The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', 'SHALL
+ NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', 'NOT RECOMMENDED',
+ 'MAY', and 'OPTIONAL' in this document are to be interpreted as
+ described in BCP 14 (RFC 2119) (RFC 8174) when, and only when,
+ they appear in all capitals, as shown here.";
+
+ revision "2024-02-01" {
+ description
+ "Initial Version.";
+ reference
+ "RFC XXXX: An HTTPS-based Transport for YANG Notifications.";
+ }
+
+ augment "/sn:subscriptions" {
+ container receiver-instances {
+ description
+ "A container for all instances of receivers.";
+
+ list receiver-instance {
+ key "name";
+
+ leaf name {
+ type string;
+ description
+ "An arbitrary but unique name for this receiver
+ instance.";
+ }
+
+ choice transport-type {
+ mandatory true;
+ description
+ "Choice of different types of transports used to
+ send notifications. The 'case' statements must
+ be augmented in by other modules.";
+ }
+ description
+ "A list of all receiver instances.";
+ }
+ }
+ description
+ "Augment the subscriptions container to define the
+ transport type.";
+ }
+ augment
+ "/sn:subscriptions/sn:subscription/sn:receivers/sn:receiver" {
+ leaf receiver-instance-ref {
+ type leafref {
+ path "/sn:subscriptions/snr:receiver-instances/" +
+ "snr:receiver-instance/snr:name";
+ }
+ description
+ "Reference to a receiver instance.";
+ }
+ description
+ "Augment the subscriptions container to define an optional
+ reference to a receiver instance.";
+ }
+}
diff --git a/tests/modules/ietf-tls-client@2024-03-16.yang b/tests/modules/ietf-tls-client@2024-03-16.yang
new file mode 100644
index 00000000..447185b2
--- /dev/null
+++ b/tests/modules/ietf-tls-client@2024-03-16.yang
@@ -0,0 +1,525 @@
+module ietf-tls-client {
+ yang-version 1.1;
+ namespace "urn:ietf:params:xml:ns:yang:ietf-tls-client";
+ prefix tlsc;
+
+ import ietf-netconf-acm {
+ prefix nacm;
+ reference
+ "RFC 8341: Network Configuration Access Control Model";
+ }
+
+ import ietf-crypto-types {
+ prefix ct;
+ reference
+ "RFC AAAA: YANG Data Types and Groupings for Cryptography";
+ }
+
+ import ietf-truststore {
+ prefix ts;
+ reference
+ "RFC BBBB: A YANG Data Model for a Truststore";
+ }
+
+ import ietf-keystore {
+ prefix ks;
+ reference
+ "RFC CCCC: A YANG Data Model for a Keystore";
+ }
+
+ import ietf-tls-common {
+ prefix tlscmn;
+ reference
+ "RFC FFFF: YANG Groupings for TLS Clients and TLS Servers";
+ }
+
+ organization
+ "IETF NETCONF (Network Configuration) Working Group";
+
+ contact
+ "WG List: NETCONF WG list
+ WG Web: https://datatracker.ietf.org/wg/netconf
+ Author: Kent Watsen
+ Author: Jeff Hartley ";
+
+ description
+ "This module defines reusable groupings for TLS clients that
+ can be used as a basis for specific TLS client instances.
+
+ Copyright (c) 2024 IETF Trust and the persons identified
+ as authors of the code. All rights reserved.
+
+ Redistribution and use in source and binary forms, with
+ or without modification, is permitted pursuant to, and
+ subject to the license terms contained in, the Revised
+ BSD License set forth in Section 4.c of the IETF Trust's
+ Legal Provisions Relating to IETF Documents
+ (https://trustee.ietf.org/license-info).
+
+ This version of this YANG module is part of RFC FFFF
+ (https://www.rfc-editor.org/info/rfcFFFF); see the RFC
+ itself for full legal notices.
+
+ The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL',
+ 'SHALL NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED',
+ 'NOT RECOMMENDED', 'MAY', and 'OPTIONAL' in this document
+ are to be interpreted as described in BCP 14 (RFC 2119)
+ (RFC 8174) when, and only when, they appear in all
+ capitals, as shown here.";
+
+ revision 2024-03-16 {
+ description
+ "Initial version";
+ reference
+ "RFC FFFF: YANG Groupings for TLS Clients and TLS Servers";
+ }
+
+ // Features
+
+ feature tls-client-keepalives {
+ description
+ "Per socket TLS keepalive parameters are configurable for
+ TLS clients on the server implementing this feature.";
+ }
+
+ feature client-ident-x509-cert {
+ description
+ "Indicates that the client supports identifying itself
+ using X.509 certificates.";
+ reference
+ "RFC 5280:
+ Internet X.509 Public Key Infrastructure Certificate
+ and Certificate Revocation List (CRL) Profile";
+ }
+
+ feature client-ident-raw-public-key {
+ description
+ "Indicates that the client supports identifying itself
+ using raw public keys.";
+ reference
+ "RFC 7250:
+ Using Raw Public Keys in Transport Layer Security (TLS)
+ and Datagram Transport Layer Security (DTLS)";
+ }
+
+ feature client-ident-tls12-psk {
+ if-feature "tlscmn:tls12";
+ description
+ "Indicates that the client supports identifying itself
+ using TLS-1.2 PSKs (pre-shared or pairwise-symmetric keys).";
+ reference
+ "RFC 4279:
+ Pre-Shared Key Ciphersuites for Transport Layer Security
+ (TLS)";
+ }
+
+ feature client-ident-tls13-epsk {
+ if-feature "tlscmn:tls13";
+ description
+ "Indicates that the client supports identifying itself
+ using TLS-1.3 External PSKs (pre-shared keys).";
+ reference
+ "RFC 8446:
+ The Transport Layer Security (TLS) Protocol Version 1.3";
+ }
+
+ feature server-auth-x509-cert {
+ description
+ "Indicates that the client supports authenticating servers
+ using X.509 certificates.";
+ reference
+ "RFC 5280:
+ Internet X.509 Public Key Infrastructure Certificate
+ and Certificate Revocation List (CRL) Profile";
+ }
+
+ feature server-auth-raw-public-key {
+ description
+ "Indicates that the client supports authenticating servers
+ using raw public keys.";
+ reference
+ "RFC 7250:
+ Using Raw Public Keys in Transport Layer Security (TLS)
+ and Datagram Transport Layer Security (DTLS)";
+ }
+
+ feature server-auth-tls12-psk {
+ description
+ "Indicates that the client supports authenticating servers
+ using PSKs (pre-shared or pairwise-symmetric keys).";
+ reference
+ "RFC 4279:
+ Pre-Shared Key Ciphersuites for Transport Layer Security
+ (TLS)";
+ }
+
+ feature server-auth-tls13-epsk {
+ description
+ "Indicates that the client supports authenticating servers
+ using TLS-1.3 External PSKs (pre-shared keys).";
+ reference
+ "RFC 8446:
+ The Transport Layer Security (TLS) Protocol Version 1.3";
+ }
+
+ // Groupings
+
+ grouping tls-client-grouping {
+ description
+ "A reusable grouping for configuring a TLS client without
+ any consideration for how an underlying TCP session is
+ established.
+
+ Note that this grouping uses fairly typical descendant
+ node names such that a stack of 'uses' statements will
+ have name conflicts. It is intended that the consuming
+ data model will resolve the issue (e.g., by wrapping
+ the 'uses' statement in a container called
+ 'tls-client-parameters'). This model purposely does
+ not do this itself so as to provide maximum flexibility
+ to consuming models.";
+
+ container client-identity {
+ nacm:default-deny-write;
+ presence
+ "Indicates that a TLS-level client identity has been
+ configured. This statement is present so the mandatory
+ descendant do not imply that this node must be configured.";
+ description
+ "Identity credentials the TLS client MAY present when
+ establishing a connection to a TLS server. If not
+ configured, then client authentication is presumed to
+ occur in a protocol layer above TLS. When configured,
+ and requested by the TLS server when establishing a
+ TLS session, these credentials are passed in the
+ Certificate message defined in Section 7.4.2 of
+ RFC 5246 and Section 4.4.2 in RFC 8446.";
+ reference
+ "RFC 5246: The Transport Layer Security (TLS)
+ Protocol Version 1.2
+ RFC 8446: The Transport Layer Security (TLS)
+ Protocol Version 1.3
+ RFC CCCC: A YANG Data Model for a Keystore";
+ choice auth-type {
+ mandatory true;
+ description
+ "A choice amongst authentication types, of which one must
+ be enabled (via its associated 'feature') and selected.";
+ case certificate {
+ if-feature "client-ident-x509-cert";
+ container certificate {
+ description
+ "Specifies the client identity using a certificate.";
+ uses
+ "ks:inline-or-keystore-end-entity-cert-with-key-"
+ + "grouping" {
+ refine "inline-or-keystore/inline/inline-definition" {
+ must 'not(public-key-format) or derived-from-or-self'
+ + '(public-key-format, "ct:subject-public-key-'
+ + 'info-format")';
+ }
+ refine "inline-or-keystore/central-keystore/"
+ + "central-keystore-reference/asymmetric-key" {
+ must 'not(deref(.)/../ks:public-key-format) or '
+ + 'derived-from-or-self(deref(.)/../ks:public-'
+ + 'key-format, "ct:subject-public-key-info-'
+ + 'format")';
+ }
+ }
+ }
+ }
+ case raw-public-key {
+ if-feature "client-ident-raw-public-key";
+ container raw-private-key {
+ description
+ "Specifies the client identity using a raw
+ private key.";
+ uses ks:inline-or-keystore-asymmetric-key-grouping {
+ refine "inline-or-keystore/inline/inline-definition" {
+ must 'not(public-key-format) or derived-from-or-self'
+ + '(public-key-format, "ct:subject-public-key-'
+ + 'info-format")';
+ }
+ refine "inline-or-keystore/central-keystore/"
+ + "central-keystore-reference" {
+ must 'not(deref(.)/../ks:public-key-format) or '
+ + 'derived-from-or-self(deref(.)/../ks:public-'
+ + 'key-format, "ct:subject-public-key-info-'
+ + 'format")';
+ }
+ }
+ }
+ }
+ case tls12-psk {
+ if-feature "client-ident-tls12-psk";
+ container tls12-psk {
+ description
+ "Specifies the client identity using a PSK (pre-shared
+ or pairwise-symmetric key).";
+ uses ks:inline-or-keystore-symmetric-key-grouping;
+ leaf id {
+ type string;
+ description
+ "The key 'psk_identity' value used in the TLS
+ 'ClientKeyExchange' message.";
+ reference
+ "RFC 4279: Pre-Shared Key Ciphersuites for
+ Transport Layer Security (TLS)";
+ }
+ }
+ }
+ case tls13-epsk {
+ if-feature "client-ident-tls13-epsk";
+ container tls13-epsk {
+ description
+ "An External Pre-Shared Key (EPSK) is established
+ or provisioned out-of-band, i.e., not from a TLS
+ connection. An EPSK is a tuple of (Base Key,
+ External Identity, Hash). External PSKs MUST NOT
+ be imported for (D)TLS 1.2 or prior versions. When
+ PSKs are provisioned out of band, the PSK identity
+ and the KDF hash algorithm to be used with the PSK
+ MUST also be provisioned.
+
+ The structure of this container is designed to
+ satisfy the requirements of RFC 8446 Section
+ 4.2.11, the recommendations from Section 6 in
+ RFC 9257, and the EPSK input fields detailed in
+ Section 5.1 in RFC 9258. The base-key is based
+ upon ks:inline-or-keystore-symmetric-key-grouping
+ in order to provide users with flexible and
+ secure storage options.";
+ reference
+ "RFC 8446: The Transport Layer Security (TLS)
+ Protocol Version 1.3
+ RFC 9257: Guidance for External Pre-Shared Key
+ (PSK) Usage in TLS
+ RFC 9258: Importing External Pre-Shared Keys
+ (PSKs) for TLS 1.3";
+ uses ks:inline-or-keystore-symmetric-key-grouping;
+ leaf external-identity {
+ type string;
+ mandatory true;
+ description
+ "As per Section 4.2.11 of RFC 8446, and Section 4.1
+ of RFC 9257, a sequence of bytes used to identify
+ an EPSK. A label for a pre-shared key established
+ externally.";
+ reference
+ "RFC 8446: The Transport Layer Security (TLS)
+ Protocol Version 1.3
+ RFC 9257: Guidance for External Pre-Shared Key
+ (PSK) Usage in TLS";
+ }
+ leaf hash {
+ type tlscmn:epsk-supported-hash;
+ default sha-256;
+ description
+ "As per Section 4.2.11 of RFC 8446, for externally
+ established PSKs, the Hash algorithm MUST be set
+ when the PSK is established or default to SHA-256
+ if no such algorithm is defined. The server MUST
+ ensure that it selects a compatible PSK (if any)
+ and cipher suite. Each PSK MUST only be used with
+ a single hash function.";
+ reference
+ "RFC 8446: The Transport Layer Security (TLS)
+ Protocol Version 1.3";
+ }
+ leaf context {
+ type string;
+ description
+ "Per Section 5.1 of RFC 9258, context MUST include
+ the context used to determine the EPSK, if
+ any exists. For example, context may include
+ information about peer roles or identities
+ to mitigate Selfie-style reflection attacks.
+ Since the EPSK is a key derived from an external
+ protocol or sequence of protocols, context MUST
+ include a channel binding for the deriving
+ protocols [RFC5056]. The details of this
+ binding are protocol specfic and out of scope
+ for this document.";
+ reference
+ "RFC 9258: Importing External Pre-Shared Keys
+ (PSKs) for TLS 1.3";
+ }
+ leaf target-protocol {
+ type uint16;
+ description
+ "As per Section 3 of RFC 9258, the protocol
+ for which a PSK is imported for use.";
+ reference
+ "RFC 9258: Importing External Pre-Shared Keys
+ (PSKs) for TLS 1.3";
+ }
+ leaf target-kdf {
+ type uint16;
+ description
+ "As per Section 3 of RFC 9258, the KDF for
+ which a PSK is imported for use.";
+ reference
+ "RFC 9258: Importing External Pre-Shared Keys
+ (PSKs) for TLS 1.3";
+ }
+ }
+ }
+ }
+ } // container client-identity
+
+ container server-authentication {
+ nacm:default-deny-write;
+ must 'ca-certs or ee-certs or raw-public-keys or tls12-psks
+ or tls13-epsks';
+ description
+ "Specifies how the TLS client can authenticate TLS servers.
+ Any combination of credentials is additive and unordered.
+
+ Note that no configuration is required for PSK (pre-shared
+ or pairwise-symmetric key) based authentication as the key
+ is necessarily the same as configured in the '../client-
+ identity' node.";
+ container ca-certs {
+ if-feature "server-auth-x509-cert";
+ presence
+ "Indicates that CA certificates have been configured.
+ This statement is present so the mandatory descendant
+ nodes do not imply that this node must be configured.";
+ description
+ "A set of certificate authority (CA) certificates used by
+ the TLS client to authenticate TLS server certificates.
+ A server certificate is authenticated if it has a valid
+ chain of trust to a configured CA certificate.";
+ reference
+ "RFC BBBB: A YANG Data Model for a Truststore";
+ uses ts:inline-or-truststore-certs-grouping;
+ }
+ container ee-certs {
+ if-feature "server-auth-x509-cert";
+ presence
+ "Indicates that EE certificates have been configured.
+ This statement is present so the mandatory descendant
+ nodes do not imply that this node must be configured.";
+ description
+ "A set of server certificates (i.e., end entity
+ certificates) used by the TLS client to authenticate
+ certificates presented by TLS servers. A server
+ certificate is authenticated if it is an exact
+ match to a configured server certificate.";
+ reference
+ "RFC BBBB: A YANG Data Model for a Truststore";
+ uses ts:inline-or-truststore-certs-grouping;
+ }
+ container raw-public-keys {
+ if-feature "server-auth-raw-public-key";
+ presence
+ "Indicates that raw public keys have been configured.
+ This statement is present so the mandatory descendant
+ nodes do not imply that this node must be configured.";
+ description
+ "A set of raw public keys used by the TLS client to
+ authenticate raw public keys presented by the TLS
+ server. A raw public key is authenticated if it
+ is an exact match to a configured raw public key.";
+ reference
+ "RFC BBBB: A YANG Data Model for a Truststore";
+ uses ts:inline-or-truststore-public-keys-grouping {
+ refine "inline-or-truststore/inline/inline-definition/"
+ + "public-key" {
+ must 'derived-from-or-self(public-key-format,'
+ + ' "ct:subject-public-key-info-format")';
+ }
+ refine "inline-or-truststore/central-truststore/"
+ + "central-truststore-reference" {
+ must 'not(deref(.)/../ts:public-key/ts:public-key-'
+ + 'format[not(derived-from-or-self(., "ct:subject-'
+ + 'public-key-info-format"))])';
+ }
+ }
+ }
+ leaf tls12-psks {
+ if-feature "server-auth-tls12-psk";
+ type empty;
+ description
+ "Indicates that the TLS client can authenticate TLS servers
+ using configured PSKs (pre-shared or pairwise-symmetric
+ keys).
+
+ No configuration is required since the PSK value is the
+ same as PSK value configured in the 'client-identity'
+ node.";
+ }
+ leaf tls13-epsks {
+ if-feature "server-auth-tls13-epsk";
+ type empty;
+ description
+ "Indicates that the TLS client can authenticate TLS servers
+ using configured external PSKs (pre-shared keys).
+
+ No configuration is required since the PSK value is the
+ same as PSK value configured in the 'client-identity'
+ node.";
+ }
+ } // container server-authentication
+
+ container hello-params {
+ nacm:default-deny-write;
+ if-feature "tlscmn:hello-params";
+ uses tlscmn:hello-params-grouping;
+ description
+ "Configurable parameters for the TLS hello message.";
+ } // container hello-params
+ container keepalives {
+ nacm:default-deny-write;
+ if-feature "tls-client-keepalives";
+ description
+ "Configures the keepalive policy for the TLS client.";
+ leaf peer-allowed-to-send {
+ type empty;
+ description
+ "Indicates that the remote TLS server is allowed to send
+ HeartbeatRequest messages, as defined by RFC 6520
+ to this TLS client.";
+ reference
+ "RFC 6520: Transport Layer Security (TLS) and Datagram
+ Transport Layer Security (DTLS) Heartbeat Extension";
+ }
+ container test-peer-aliveness {
+ presence
+ "Indicates that the TLS client proactively tests the
+ aliveness of the remote TLS server.";
+ description
+ "Configures the keep-alive policy to proactively test
+ the aliveness of the TLS server. An unresponsive
+ TLS server is dropped after approximately max-wait
+ * max-attempts seconds. The TLS client MUST send
+ HeartbeatRequest messages, as defined by RFC 6520.";
+ reference
+ "RFC 6520: Transport Layer Security (TLS) and Datagram
+ Transport Layer Security (DTLS) Heartbeat Extension";
+ leaf max-wait {
+ type uint16 {
+ range "1..max";
+ }
+ units "seconds";
+ default "30";
+ description
+ "Sets the amount of time in seconds after which if
+ no data has been received from the TLS server, a
+ TLS-level message will be sent to test the
+ aliveness of the TLS server.";
+ }
+ leaf max-attempts {
+ type uint8;
+ default "3";
+ description
+ "Sets the maximum number of sequential keep-alive
+ messages that can fail to obtain a response from
+ the TLS server before assuming the TLS server is
+ no longer alive.";
+ }
+ }
+ }
+ } // grouping tls-client-grouping
+
+}
diff --git a/tests/modules/ietf-udp-client@2025-05-14.yang b/tests/modules/ietf-udp-client@2025-05-14.yang
new file mode 100644
index 00000000..d2302290
--- /dev/null
+++ b/tests/modules/ietf-udp-client@2025-05-14.yang
@@ -0,0 +1,104 @@
+module ietf-udp-client {
+ yang-version 1.1;
+ namespace
+ "urn:ietf:params:xml:ns:yang:ietf-udp-client";
+ prefix udpc;
+ import ietf-inet-types {
+ prefix inet;
+ reference
+ "RFC 6991: Common YANG Data Types";
+ }
+
+ organization "IETF NETCONF (Network Configuration) Working Group";
+ contact
+ "WG Web:
+ WG List:
+
+ Authors: Alex Huang Feng
+
+ Pierre Francois
+ ";
+
+ description
+ "Defines a generic grouping for UDP-based client applications.
+
+ Copyright (c) 2025 IETF Trust and the persons identified as
+ authors of the code. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, is permitted pursuant to, and subject to the license
+ terms contained in, the Revised BSD License set forth in Section
+ 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents
+ (https://trustee.ietf.org/license-info).
+
+ This version of this YANG module is part of RFC-to-be; see the RFC
+ itself for full legal notices.";
+
+ revision 2025-05-14 {
+ description
+ "Initial revision";
+ reference
+ "RFC-to-be: YANG Groupings for UDP Clients and UDP Servers";
+ }
+
+ feature local-binding {
+ description
+ "Indicates that the UDP client supports configuring local
+ bindings (i.e., the local address and local port number)
+ for UDP clients.";
+ }
+
+ grouping udp-client {
+ description
+ "A reusable grouping for UDP clients.
+
+ Note that this grouping uses fairly typical descendant
+ node names such that a stack of 'uses' statements will
+ have name conflicts. It is intended that the consuming
+ data model will resolve the issue (e.g., by wrapping
+ the 'uses' statement in a container called
+ 'udp-client-parameters'). This model purposely does
+ not do this itself so as to provide maximum flexibility
+ to consuming models.";
+
+ leaf remote-address {
+ type inet:host;
+ mandatory true;
+ description
+ "The IP address or hostname of the remote UDP server.
+ If a domain name is configured, then the name resolution
+ should happen before each datagram is sent, unless a
+ previously resolved address is cached and still valid.
+ If the name resolution results in multiple IP addresses,
+ the IP addresses are tried until a connection has been
+ established or until all IP addresses have failed. ";
+ }
+
+ leaf remote-port {
+ type inet:port-number;
+ description
+ "The port number of the remote UDP server.";
+ }
+
+ leaf local-address {
+ if-feature "local-binding";
+ type inet:ip-address;
+ description
+ "The local IP address to bind to when sending UDP
+ datagrams to the remote server. INADDR_ANY ('0.0.0.0') or
+ INADDR6_ANY ('0:0:0:0:0:0:0:0' a.k.a. '::') may be used
+ so that the client can bind to any IPv4 or IPv6 address.";
+ }
+
+ leaf local-port {
+ if-feature "local-binding";
+ type inet:port-number;
+ default "0";
+ description
+ "The local port number to bind to when sending UDP
+ datagrams to the remote server. The port number '0',
+ which is the default value, indicates that any available
+ local port number may be used.";
+ }
+ }
+}
diff --git a/tests/modules/ietf-udp-notif-transport@2025-06-04.yang b/tests/modules/ietf-udp-notif-transport@2025-06-04.yang
new file mode 100644
index 00000000..4078d877
--- /dev/null
+++ b/tests/modules/ietf-udp-notif-transport@2025-06-04.yang
@@ -0,0 +1,189 @@
+module ietf-udp-notif-transport {
+ yang-version 1.1;
+ namespace "urn:ietf:params:xml:ns:yang:ietf-udp-notif-transport";
+ prefix unt;
+
+ import ietf-subscribed-notifications {
+ prefix sn;
+ reference
+ "RFC 8639: Subscription to YANG Notifications";
+ }
+ import ietf-subscribed-notif-receivers {
+ prefix snr;
+ reference
+ "draft-ietf-netconf-https-notif: An HTTPS-based Transport
+ for Configured Subscriptions";
+ }
+ import ietf-udp-client {
+ prefix udpc;
+ reference
+ "draft-ietf-netconf-udp-client-server: YANG Grouping for
+ UDP Clients and UDP Servers";
+ }
+ import ietf-tls-client {
+ prefix tlsc;
+ reference
+ "RFC 9645: YANG Groupings for TLS Clients and TLS Servers";
+ }
+
+ organization
+ "IETF NETCONF (Network Configuration) Working Group";
+ contact
+ "WG Web:
+ WG List:
+
+ Authors: Guangying Zheng
+
+ Tianran Zhou
+
+ Thomas Graf
+
+ Pierre Francois
+
+ Alex Huang Feng
+
+ Paolo Lucente
+ ";
+ description
+ "Defines a model for configuring UDP-Notif as a transport
+ for configured subscriptions [RFC8639].
+
+ Copyright (c) 2025 IETF Trust and the persons identified as
+ authors of the code. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, is permitted pursuant to, and subject to
+ the license terms contained in, the Revised BSD License set
+ forth in Section 4.c of the IETF Trust's Legal Provisions
+ Relating to IETF Documents
+ (https://trustee.ietf.org/license-info).
+
+ This version of this YANG module is part of RFC XXXX
+ (https://www.rfc-editor.org/info/rfcXXXX); see the RFC itself
+ for full legal notices.
+
+ The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', 'SHALL
+ NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', 'NOT RECOMMENDED',
+ 'MAY', and 'OPTIONAL' in this document are to be interpreted as
+ described in BCP 14 (RFC 2119) (RFC 8174) when, and only when,
+ they appear in all capitals, as shown here.";
+
+ revision 2025-06-04 {
+ description
+ "Initial revision";
+ reference
+ "RFC XXXX: UDP-based Transport for Configured Subscriptions";
+ }
+
+ /*
+ * FEATURES
+ */
+
+ feature encode-cbor {
+ description
+ "Indicates that CBOR encoding of notification
+ messages is supported.";
+ reference
+ "RFC 9254: CBOR Encoding of Data Modeled with YANG";
+ }
+
+ feature dtls {
+ description
+ "Indicates that DTLS encryption of UDP
+ packets is supported. UDP-Notif mandates that, in
+ unsecured networks, DTLS 1.2 or later MUST be supported,
+ and DTLS 1.3 SHOULD be supported.";
+ reference
+ "RFC6347: Datagram Transport Layer Security Version 1.2,
+ RFC 9147: The Datagram Transport Layer Security (DTLS)
+ Protocol Version 1.3";
+ }
+
+ /*
+ * IDENTITIES
+ */
+
+ identity udp-notif {
+ base sn:transport;
+ base sn:configurable-encoding;
+ description
+ "UDP-Notif is used as transport for notification messages
+ and state change notifications.";
+ }
+
+ identity encode-cbor {
+ base sn:encoding;
+ description
+ "Encode data using CBOR.";
+ reference
+ "RFC 9254: CBOR Encoding of Data Modeled with YANG";
+ }
+
+ identity unsupported-max-segment-size {
+ base sn:establish-subscription-error;
+ base sn:modify-subscription-error;
+ description
+ "Error triggered when the specified value 'max-segment-size'
+ is not supported by the publisher. An implementation may
+ only support a subset of the uint16.";
+ reference
+ "RFC XXXX: UDP-based Transport for Configured Subscriptions";
+ }
+
+ grouping udp-notif-receiver {
+ description
+ "Provides a reusable identification of a UDP-Notif target
+ receiver.";
+ uses udpc:udp-client {
+ refine "remote-port" {
+ mandatory true;
+ }
+ }
+ container dtls {
+ if-feature "dtls";
+ presence "dtls";
+ uses tlsc:tls-client-grouping {
+ // Remove keep-alives for DTLS
+ refine "keepalives" {
+ if-feature "not tlsc:tls-client-keepalives";
+ }
+ }
+ description
+ "Container for configuring DTLS parameters.";
+ }
+ leaf enable-segmentation {
+ type boolean;
+ default "true";
+ description
+ "When disabled, the publisher will not segment UDP-Notif
+ messages. This may cause IP-layer fragmentation when
+ messages are larger than the MTU. IP fragmentation is
+ discouraged (RFC 8085, RFC 8900) and generally unsafe.
+ Disabling is not recommended.";
+ }
+ leaf max-segment-size {
+ type uint16;
+ description
+ "UDP-Notif provides a configurable max-segment-size to
+ control the size of each segment (UDP-Notif header, with
+ options, included).
+ The publisher may trigger an 'unsupported-max-segment-size'
+ error if the publisher does not support the configured
+ value.";
+ }
+ }
+
+ augment "/sn:subscriptions/snr:receiver-instances/"
+ + "snr:receiver-instance/snr:transport-type" {
+ case udp-notif {
+ container udp-notif-receiver {
+ description
+ "The UDP-Notif receiver to send notifications to.";
+ uses udp-notif-receiver;
+ }
+ }
+ description
+ "Augments the transport-type choice to include the 'udp-notif'
+ transport.";
+ }
+}
diff --git a/tests/np2_test.c b/tests/np2_test.c
index 17703696..863148b5 100644
--- a/tests/np2_test.c
+++ b/tests/np2_test.c
@@ -240,6 +240,18 @@ np2_glob_test_setup_sess_ctx(struct nc_session *sess, const char **modules)
SETUP_FAIL_LOG;
return 1;
}
+ if (!ly_ctx_load_module(ctx, "ietf-system-capabilities", "2022-02-17", NULL)) {
+ SETUP_FAIL_LOG;
+ return 1;
+ }
+ if (!ly_ctx_load_module(ctx, "ietf-notification-capabilities", "2022-02-17", NULL)) {
+ SETUP_FAIL_LOG;
+ return 1;
+ }
+ if (!ly_ctx_load_module(ctx, "ietf-yp-transport-capabilities", "2026-05-15", NULL)) {
+ SETUP_FAIL_LOG;
+ return 1;
+ }
/* test module searchdir */
ly_ctx_set_searchdir(ctx, NP_TEST_MODULE_DIR);
diff --git a/tests/test_sub_ntf_advanced.c b/tests/test_sub_ntf_advanced.c
index 20ac3b07..59020d95 100644
--- a/tests/test_sub_ntf_advanced.c
+++ b/tests/test_sub_ntf_advanced.c
@@ -38,8 +38,19 @@ static int
local_setup(void **state)
{
struct np2_test *st;
- const char *modules[] = {NP_TEST_MODULE_DIR "/notif1.yang", NP_TEST_MODULE_DIR "/notif2.yang", NULL};
+ const char *modules[] = {
+ NP_TEST_MODULE_DIR "/ietf-subscribed-notif-receivers@2024-02-01.yang",
+ NP_TEST_MODULE_DIR "/ietf-udp-client@2025-05-14.yang",
+ NP_TEST_MODULE_DIR "/ietf-tls-client@2024-03-16.yang",
+ NP_TEST_MODULE_DIR "/ietf-udp-notif-transport@2025-06-04.yang",
+ NP_TEST_MODULE_DIR "/notif1.yang", NP_TEST_MODULE_DIR "/notif2.yang", NULL
+ };
+ /* features per module; only ietf-udp-notif-transport needs dtls and encode-cbor */
+ const char *unt_features[] = {"dtls", "encode-cbor", NULL};
+ const char **features[] = {NULL, NULL, NULL, unt_features, NULL, NULL};
+ const char *search_dirs = NP_ROOT_DIR "/modules:" NP_TEST_MODULE_DIR ":" LN2_YANG_MODULE_DIR;
char test_name[256];
+ sr_conn_ctx_t *conn;
int rc;
/* get test name */
@@ -49,6 +60,11 @@ local_setup(void **state)
rc = np2_glob_test_setup_env(test_name);
assert_int_equal(rc, 0);
+ /* install UDP-Notif modules with features before the server starts */
+ assert_int_equal(SR_ERR_OK, sr_connect(0, &conn));
+ assert_int_equal(SR_ERR_OK, sr_install_modules(conn, modules, search_dirs, features));
+ sr_disconnect(conn);
+
/* setup netopeer2 server */
rc = np2_glob_test_setup_server(state, test_name, modules, NULL, 0);
assert_int_equal(rc, 0);
@@ -89,7 +105,10 @@ static int
local_teardown(void **state)
{
struct np2_test *st = *state;
- const char *modules[] = {"notif1", "notif2", NULL};
+ const char *modules[] = {
+ "ietf-udp-notif-transport", "ietf-tls-client", "ietf-udp-client",
+ "ietf-subscribed-notif-receivers", "notif1", "notif2", NULL
+ };
if (!st) {
return 0;
@@ -947,6 +966,34 @@ test_killsub_diff_sess(void **state)
nc_session_free(tmp, NULL);
}
+static void
+test_transport_capabilities(void **state)
+{
+ struct np2_test *st = *state;
+ const char *template =
+ "\n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " unt:udp-notif\n"
+ " dtls12\n"
+ " dtls13\n"
+ " sn:encode-xml\n"
+ " unt:encode-cbor\n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ "\n";
+
+ GET_FILTER(st, "/ietf-system-capabilities:system-capabilities/ietf-notification-capabilities:subscription-capabilities/ietf-yp-transport-capabilities:transport-capabilities");
+ assert_string_equal(st->str, template);
+ FREE_TEST_VARS(st);
+}
+
int
main(int argc, char **argv)
{
@@ -962,6 +1009,7 @@ main(int argc, char **argv)
cmocka_unit_test_teardown(test_ds_subscriptions, teardown_common),
cmocka_unit_test_teardown(test_ds_subscriptions_sent_event, teardown_common),
cmocka_unit_test_teardown(test_ds_subscriptions_excluded_event, teardown_common),
+ cmocka_unit_test_teardown(test_transport_capabilities, teardown_common),
cmocka_unit_test_teardown(test_multiple_subscriptions, teardown_common),
cmocka_unit_test_teardown(test_multiple_subscriptions_notif, teardown_common),
cmocka_unit_test_setup_teardown(test_multiple_subscriptions_notif_interlaced, setup_notif2_data, teardown_common),