extensions CHANGE use the validate callback of extension plugin

The validate callback was not used so far. It is now used for validating
data nodes by extension instances placed in the nodes themselves or in
their types (compiled types, so not matter where the extension came
from, the node's type itself or a used typedef's type).
diff --git a/src/in.c b/src/in.c
index e72bcbf..0a964c6 100644
--- a/src/in.c
+++ b/src/in.c
@@ -385,6 +385,7 @@
     ly_set_erase(&lydctx->node_types, NULL);
     ly_set_erase(&lydctx->meta_types, NULL);
     ly_set_erase(&lydctx->node_when, NULL);
+    ly_set_erase(&lydctx->node_exts, NULL);
 }
 
 LY_ERR
diff --git a/src/lyb.h b/src/lyb.h
index 30e89fd..e56457d 100644
--- a/src/lyb.h
+++ b/src/lyb.h
@@ -66,6 +66,7 @@
     uint32_t path_len;             /**< used bytes in the path buffer */
     char path[LYD_PARSER_BUFSIZE]; /**< buffer for the generated path */
     struct ly_set node_when;       /**< set of nodes with "when" conditions */
+    struct ly_set node_exts;       /**< set of nodes and extensions connected with a plugin providing own validation callback */
     struct ly_set node_types;      /**< set of nodes validated with LY_EINCOMPLETE result */
     struct ly_set meta_types;      /**< set of metadata validated with LY_EINCOMPLETE result */
     struct lyd_node *op_node;      /**< if an RPC/action/notification is being parsed, store the pointer to it */
diff --git a/src/parser_internal.h b/src/parser_internal.h
index b0f4da0..c7d816a 100644
--- a/src/parser_internal.h
+++ b/src/parser_internal.h
@@ -41,6 +41,8 @@
 
 /**
  * @brief Internal (common) context for YANG data parsers.
+ *
+ * Covers ::lyd_xml_ctx, ::lyd_json_ctx and lyd_lyb_ctx.
  */
 struct lyd_ctx {
     const struct lysc_ext_instance *ext; /**< extension instance possibly changing document root context of the data being parsed */
@@ -51,6 +53,7 @@
 #define LYD_PARSER_BUFSIZE 4078
     char path[LYD_PARSER_BUFSIZE]; /**< buffer for the generated path */
     struct ly_set node_when;       /**< set of nodes with "when" conditions */
+    struct ly_set node_exts;       /**< set of nodes and extensions connected with a plugin providing own validation callback */
     struct ly_set node_types;      /**< set of nodes validated with LY_EINCOMPLETE result */
     struct ly_set meta_types;      /**< set of metadata validated with LY_EINCOMPLETE result */
     struct lyd_node *op_node;      /**< if an RPC/action/notification is being parsed, store the pointer to it */
diff --git a/src/parser_json.c b/src/parser_json.c
index 24f6544..6e6d752 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -48,6 +48,7 @@
     uint32_t path_len;             /**< used bytes in the path buffer */
     char path[LYD_PARSER_BUFSIZE]; /**< buffer for the generated path */
     struct ly_set node_when;       /**< set of nodes with "when" conditions */
+    struct ly_set node_exts;       /**< set of nodes and extensions connected with a plugin providing own validation callback */
     struct ly_set node_types;      /**< set of nodes validated with LY_EINCOMPLETE result */
     struct ly_set meta_types;      /**< set of metadata validated with LY_EINCOMPLETE result */
     struct lyd_node *op_node;      /**< if an RPC/action/notification is being parsed, store the pointer to it */
@@ -610,7 +611,7 @@
                     }
                 }
                 /* add/correct flags */
-                lyd_parse_set_data_flags(node, &lydctx->node_when, &node->meta, lydctx->parse_opts);
+                lyd_parse_set_data_flags(node, &lydctx->node_when, &lydctx->node_exts, &node->meta, lydctx->parse_opts);
 
                 /* done */
                 break;
@@ -788,7 +789,7 @@
             LY_CHECK_GOTO(ret, cleanup);
 
             /* add/correct flags */
-            lyd_parse_set_data_flags(node, &lydctx->node_when, &meta, lydctx->parse_opts);
+            lyd_parse_set_data_flags(node, &lydctx->node_when, &lydctx->node_exts, &meta, lydctx->parse_opts);
         } else {
             /* create attribute */
             const char *module_name;
@@ -1095,9 +1096,8 @@
                 LY_CHECK_ERR_RET(ret, LOG_LOCBACK(1, 1, 0, 0), ret);
 
                 /* add any missing default children */
-                ret = lyd_new_implicit_r(*node, lyd_node_child_p(*node), NULL, NULL, &lydctx->node_when,
-                        &lydctx->node_types, (lydctx->val_opts & LYD_VALIDATE_NO_STATE) ?
-                        LYD_IMPLICIT_NO_STATE : 0, NULL);
+                ret = lyd_new_implicit_r(*node, lyd_node_child_p(*node), NULL, NULL, &lydctx->node_when, &lydctx->node_exts,
+                        &lydctx->node_types, (lydctx->val_opts & LYD_VALIDATE_NO_STATE) ? LYD_IMPLICIT_NO_STATE : 0, NULL);
                 LY_CHECK_ERR_RET(ret, LOG_LOCBACK(1, 1, 0, 0), ret);
             }
 
diff --git a/src/parser_lyb.c b/src/parser_lyb.c
index 9954377..9cebd2d 100644
--- a/src/parser_lyb.c
+++ b/src/parser_lyb.c
@@ -825,7 +825,7 @@
             LY_CHECK_GOTO(ret, cleanup);
 
             /* add any missing default children */
-            ret = lyd_new_implicit_r(node, lyd_node_child_p(node), NULL, NULL, &lybctx->node_when,
+            ret = lyd_new_implicit_r(node, lyd_node_child_p(node), NULL, NULL, &lybctx->node_when, &lybctx->node_exts,
                     &lybctx->node_types, (lybctx->val_opts & LYD_VALIDATE_NO_STATE) ? LYD_IMPLICIT_NO_STATE : 0, NULL);
             LY_CHECK_GOTO(ret, cleanup);
         }
diff --git a/src/parser_xml.c b/src/parser_xml.c
index d685c74..decbaf4 100644
--- a/src/parser_xml.c
+++ b/src/parser_xml.c
@@ -36,7 +36,7 @@
 /**
  * @brief Internal context for XML YANG data parser.
  *
- * Note that the structure maps to the lyd_ctx which is common for all the data parsers
+ * Note that the structure maps to the ::lyd_ctx which is common for all the data parsers
  */
 struct lyd_xml_ctx {
     const struct lysc_ext_instance *ext; /**< extension instance possibly changing document root context of the data being parsed */
@@ -46,6 +46,7 @@
     uint32_t path_len;             /**< used bytes in the path buffer */
     char path[LYD_PARSER_BUFSIZE]; /**< buffer for the generated path */
     struct ly_set node_when;       /**< set of nodes with "when" conditions */
+    struct ly_set node_exts;       /**< set of nodes and extensions connected with a plugin providing own validation callback */
     struct ly_set node_types;      /**< set of nodes validated with LY_EINCOMPLETE result */
     struct ly_set meta_types;      /**< set of metadata validated with LY_EINCOMPLETE result */
     struct lyd_node *op_node;      /**< if an RPC/action/notification is being parsed, store the pointer to it */
@@ -597,8 +598,8 @@
             LY_CHECK_GOTO(ret, error);
 
             /* add any missing default children */
-            ret = lyd_new_implicit_r(node, lyd_node_child_p(node), NULL, NULL, &lydctx->node_when, &lydctx->node_types,
-                    (lydctx->val_opts & LYD_VALIDATE_NO_STATE) ? LYD_IMPLICIT_NO_STATE : 0, NULL);
+            ret = lyd_new_implicit_r(node, lyd_node_child_p(node), NULL, NULL, &lydctx->node_when, &lydctx->node_exts,
+                    &lydctx->node_types, (lydctx->val_opts & LYD_VALIDATE_NO_STATE) ? LYD_IMPLICIT_NO_STATE : 0, NULL);
             LY_CHECK_GOTO(ret, error);
         }
 
@@ -649,7 +650,7 @@
 
     /* add/correct flags */
     if (snode) {
-        lyd_parse_set_data_flags(node, &lydctx->node_when, &meta, lydctx->parse_opts);
+        lyd_parse_set_data_flags(node, &lydctx->node_when, &lydctx->node_exts, &meta, lydctx->parse_opts);
     }
 
     /* parser next */
@@ -1431,7 +1432,7 @@
 cleanup:
     /* there should be no unres stored if validation should be skipped */
     assert(!(parse_opts & LYD_PARSE_ONLY) || (!lydctx->node_types.count && !lydctx->meta_types.count &&
-            !lydctx->node_when.count));
+            !lydctx->node_when.count && !lydctx->node_exts.count));
 
     if (rc) {
         lyd_xml_ctx_free((struct lyd_ctx *)lydctx);
diff --git a/src/plugins_exts.h b/src/plugins_exts.h
index 271d1ef..c6f4b5e 100644
--- a/src/plugins_exts.h
+++ b/src/plugins_exts.h
@@ -90,16 +90,11 @@
 /**
  * @brief Callback to decide if data instance is valid according to the schema.
  *
- * The callback is used only for the extension instances placed in the following parent statements
- * (which is specified as ::lysc_ext_instance.parent_type):
- *     - LYEXT_PAR_NODE - @p node is instance of the schema node where the extension instance was specified.
- *     - LYEXT_PAR_TPDF - @p node is instance of the schema node with the value of the typedef's type where the extension instance was specified.
- *     - LYEXT_PAR_TYPE - @p node is instance of the schema node with the value of the type where the extension instance was specified.
- *     - LYEXT_PAR_TYPE_BIT - @p node is instance of the schema node with the value of the bit where the extension instance was specified.
- *     - LYEXT_PAR_TYPE_ENUM - @p node is instance of the schema node with the value of the enum where the extension instance was specified.
+ * The callback is used only for the extension instances placed in the data nodes or type (the
+ * ::lysc_ext_instance.parent_stmt value must be ::LY_STMT_IS_DATA_NODE() values or ::LY_STMT_TYPE):
  *
  * @param[in] ext Extension instance to be checked.
- * @param[in] node Data node, where the extension data are supposed to be placed.
+ * @param[in] node Data node connected with the extension instance.
  *
  * @return LY_SUCCESS on data validation success.
  * @return LY_EVALID in case the validation fails.
diff --git a/src/schema_compile_node.c b/src/schema_compile_node.c
index 65e0d2b..bf2f209 100644
--- a/src/schema_compile_node.c
+++ b/src/schema_compile_node.c
@@ -1960,6 +1960,9 @@
         }
 
         (*type)->basetype = basetype;
+        /* collect extensions */
+        COMPILE_EXTS_GOTO(ctx, tctx->tpdf->type.exts, (*type)->exts, (*type), ret, cleanup);
+        /* get plugin for the type */
         (*type)->plugin = lys_compile_type_get_plugin(tctx->tpdf->type.pmod->mod, tctx->tpdf->name, basetype);
         prev_type = *type;
         ret = lys_compile_type_(ctx, tctx->node, tctx->tpdf->flags, tctx->tpdf->name,
diff --git a/src/tree_data.c b/src/tree_data.c
index aca0a5d..fbbeed0 100644
--- a/src/tree_data.c
+++ b/src/tree_data.c
@@ -358,7 +358,7 @@
 
     if (!(parse_opts & LYD_PARSE_ONLY)) {
         /* validate data */
-        rc = lyd_validate(first_p, NULL, ctx, val_opts, 0, &lydctx->node_when, &lydctx->node_types,
+        rc = lyd_validate(first_p, NULL, ctx, val_opts, 0, &lydctx->node_when, &lydctx->node_exts, &lydctx->node_types,
                 &lydctx->meta_types, NULL);
         LY_CHECK_GOTO(rc, cleanup);
     }
@@ -1859,8 +1859,8 @@
 
 LY_ERR
 lyd_new_implicit_r(struct lyd_node *parent, struct lyd_node **first, const struct lysc_node *sparent,
-        const struct lys_module *mod, struct ly_set *node_when, struct ly_set *node_types, uint32_t impl_opts,
-        struct lyd_node **diff)
+        const struct lys_module *mod, struct ly_set *node_when, struct ly_set *node_exts, struct ly_set *node_types,
+        uint32_t impl_opts, struct lyd_node **diff)
 {
     LY_ERR ret;
     const struct lysc_node *iter = NULL;
@@ -1893,11 +1893,11 @@
             if (!node && ((struct lysc_node_choice *)iter)->dflt) {
                 /* create default case data */
                 LY_CHECK_RET(lyd_new_implicit_r(parent, first, &((struct lysc_node_choice *)iter)->dflt->node,
-                        NULL, node_when, node_types, impl_opts, diff));
+                        NULL, node_when, node_exts, node_types, impl_opts, diff));
             } else if (node) {
                 /* create any default data in the existing case */
                 assert(node->schema->parent->nodetype == LYS_CASE);
-                LY_CHECK_RET(lyd_new_implicit_r(parent, first, node->schema->parent, NULL, node_when, node_types,
+                LY_CHECK_RET(lyd_new_implicit_r(parent, first, node->schema->parent, NULL, node_when, node_exts, node_types,
                         impl_opts, diff));
             }
             break;
@@ -1912,13 +1912,17 @@
                     /* remember to resolve when */
                     LY_CHECK_RET(ly_set_add(node_when, node, 1, NULL));
                 }
+                if (node_exts) {
+                    /* remember to call all the extension's validation callbacks */
+                    LY_CHECK_RET(lysc_node_ext_tovalidate(node_exts, node));
+                }
                 if (diff) {
                     /* add into diff */
                     LY_CHECK_RET(lyd_val_diff_add(node, LYD_DIFF_OP_CREATE, diff));
                 }
 
                 /* create any default children */
-                LY_CHECK_RET(lyd_new_implicit_r(node, lyd_node_child_p(node), NULL, NULL, node_when, node_types,
+                LY_CHECK_RET(lyd_new_implicit_r(node, lyd_node_child_p(node), NULL, NULL, node_when, node_exts, node_types,
                         impl_opts, diff));
             }
             break;
@@ -1942,6 +1946,10 @@
                     /* remember to resolve when */
                     LY_CHECK_RET(ly_set_add(node_when, node, 1, NULL));
                 }
+                if (node_exts) {
+                    /* remember to call all the extension's validation callbacks */
+                    LY_CHECK_RET(lysc_node_ext_tovalidate(node_exts, node));
+                }
                 if (diff) {
                     /* add into diff */
                     LY_CHECK_RET(lyd_val_diff_add(node, LYD_DIFF_OP_CREATE, diff));
@@ -1970,6 +1978,10 @@
                         /* remember to resolve when */
                         LY_CHECK_RET(ly_set_add(node_when, node, 1, NULL));
                     }
+                    if (node_exts) {
+                        /* remember to call all the extension's validation callbacks */
+                        LY_CHECK_RET(lysc_node_ext_tovalidate(node_exts, node));
+                    }
                     if (diff) {
                         /* add into diff */
                         LY_CHECK_RET(lyd_val_diff_add(node, LYD_DIFF_OP_CREATE, diff));
@@ -1991,7 +2003,7 @@
 {
     LY_ERR ret = LY_SUCCESS;
     struct lyd_node *node;
-    struct ly_set node_when = {0};
+    struct ly_set node_when = {0}, node_exts = {0};
 
     LY_CHECK_ARG_RET(NULL, tree, LY_EINVAL);
     if (diff) {
@@ -2002,7 +2014,7 @@
         /* skip added default nodes */
         if (((node->flags & (LYD_DEFAULT | LYD_NEW)) != (LYD_DEFAULT | LYD_NEW)) &&
                 (node->schema->nodetype & LYD_NODE_INNER)) {
-            LY_CHECK_GOTO(ret = lyd_new_implicit_r(node, lyd_node_child_p(node), NULL, NULL, &node_when,
+            LY_CHECK_GOTO(ret = lyd_new_implicit_r(node, lyd_node_child_p(node), NULL, NULL, &node_when, &node_exts,
                     NULL, implicit_options, diff), cleanup);
         }
 
@@ -2010,10 +2022,11 @@
     }
 
     /* resolve when and remove any invalid defaults */
-    LY_CHECK_GOTO(ret = lyd_validate_unres(&tree, NULL, &node_when, NULL, NULL, diff), cleanup);
+    LY_CHECK_GOTO(ret = lyd_validate_unres(&tree, NULL, &node_when, &node_exts, NULL, NULL, diff), cleanup);
 
 cleanup:
     ly_set_erase(&node_when, NULL);
+    ly_set_erase(&node_exts, NULL);
     if (ret && diff) {
         lyd_free_all(*diff);
         *diff = NULL;
@@ -2065,7 +2078,7 @@
 {
     LY_ERR ret = LY_SUCCESS;
     struct lyd_node *root, *d = NULL;
-    struct ly_set node_when = {0};
+    struct ly_set node_when = {0}, node_exts = {0};
 
     LY_CHECK_ARG_RET(NULL, tree, module, LY_EINVAL);
     if (diff) {
@@ -2073,10 +2086,10 @@
     }
 
     /* add all top-level defaults for this module */
-    LY_CHECK_GOTO(ret = lyd_new_implicit_r(NULL, tree, NULL, module, &node_when, NULL, implicit_options, diff), cleanup);
+    LY_CHECK_GOTO(ret = lyd_new_implicit_r(NULL, tree, NULL, module, &node_when, &node_exts, NULL, implicit_options, diff), cleanup);
 
     /* resolve when and remove any invalid defaults */
-    LY_CHECK_GOTO(ret = lyd_validate_unres(tree, module, &node_when, NULL, NULL, diff), cleanup);
+    LY_CHECK_GOTO(ret = lyd_validate_unres(tree, module, &node_when, &node_exts, NULL, NULL, diff), cleanup);
 
     /* process nested nodes */
     LY_LIST_FOR(*tree, root) {
@@ -2095,6 +2108,7 @@
 
 cleanup:
     ly_set_erase(&node_when, NULL);
+    ly_set_erase(&node_exts, NULL);
     if (ret && diff) {
         lyd_free_all(*diff);
         *diff = NULL;
diff --git a/src/tree_data_helpers.c b/src/tree_data_helpers.c
index 76b890a..7272725 100644
--- a/src/tree_data_helpers.c
+++ b/src/tree_data_helpers.c
@@ -34,6 +34,7 @@
 #include "tree_data_internal.h"
 #include "tree_edit.h"
 #include "tree_schema.h"
+#include "validation.h"
 #include "xml.h"
 
 struct lyd_node *
@@ -222,7 +223,8 @@
 }
 
 void
-lyd_parse_set_data_flags(struct lyd_node *node, struct ly_set *when_check, struct lyd_meta **meta, uint32_t options)
+lyd_parse_set_data_flags(struct lyd_node *node, struct ly_set *when_check, struct ly_set *exts_check, struct lyd_meta **meta,
+        uint32_t options)
 {
     struct lyd_meta *meta2, *prev_meta = NULL;
 
@@ -232,6 +234,7 @@
             LY_CHECK_RET(ly_set_add(when_check, node, 1, NULL), );
         }
     }
+    LY_CHECK_RET(lysc_node_ext_tovalidate(exts_check, node), );
 
     LY_LIST_FOR(*meta, meta2) {
         if (!strcmp(meta2->name, "default") && !strcmp(meta2->annotation->module->name, "ietf-netconf-with-defaults") &&
diff --git a/src/tree_data_internal.h b/src/tree_data_internal.h
index 402f732..e0f0149 100644
--- a/src/tree_data_internal.h
+++ b/src/tree_data_internal.h
@@ -201,13 +201,14 @@
  * @param[in] sparent Schema parent of the siblings, NULL if schema of @p parent can be used.
  * @param[in] mod Module of the default values, NULL for nested siblings.
  * @param[in] node_when Optional set to add nodes with "when" conditions into.
+ * @param[in] node_exts Optional set to add nodes and extension instances having own validation plugin callback into it.
  * @param[in] node_types Optional set to add nodes with unresolved types into.
  * @param[in] impl_opts Implicit options (@ref implicitoptions).
  * @param[in,out] diff Validation diff.
  * @return LY_ERR value.
  */
 LY_ERR lyd_new_implicit_r(struct lyd_node *parent, struct lyd_node **first, const struct lysc_node *sparent,
-        const struct lys_module *mod, struct ly_set *node_when, struct ly_set *node_types,
+        const struct lys_module *mod, struct ly_set *node_when, struct ly_set *node_exts, struct ly_set *node_types,
         uint32_t impl_opts, struct lyd_node **diff);
 
 /**
@@ -403,11 +404,13 @@
  * @brief Set data flags for a newly parsed node.
  *
  * @param[in] node Node to use.
- * @param[in] when_check Set of nodes with unresolved when.
+ * @param[in,out] when_check Set of nodes with unresolved when.
+ * @param[in,out] exts_check Set of nodes and their extension instances if they have own validation callback.
  * @param[in,out] meta Node metadata, may be removed from.
  * @param[in] options Parse options.
  */
-void lyd_parse_set_data_flags(struct lyd_node *node, struct ly_set *when_check, struct lyd_meta **meta, uint32_t options);
+void lyd_parse_set_data_flags(struct lyd_node *node, struct ly_set *when_check, struct ly_set *exts_check,
+        struct lyd_meta **meta, uint32_t options);
 
 /**
  * @brief Append all list key predicates to path.
diff --git a/src/validation.c b/src/validation.c
index 75ada9e..27d5371 100644
--- a/src/validation.c
+++ b/src/validation.c
@@ -225,8 +225,59 @@
     return ret;
 }
 
+struct node_ext {
+    struct lyd_node *node;
+    struct lysc_ext_instance *ext;
+};
+
+static LY_ERR
+node_ext_tovalidate_add(struct ly_set *node_exts, struct lysc_ext_instance *exts, struct lyd_node *node)
+{
+    LY_ERR ret = LY_SUCCESS;
+    struct lysc_ext_instance *ext;
+    struct node_ext *rec;
+
+    LY_ARRAY_FOR(exts, const struct lysc_ext_instance, ext) {
+        if (ext->def->plugin->validate) {
+            rec = malloc(sizeof *rec);
+            LY_CHECK_ERR_RET(!rec, LOGMEM(LYD_CTX(node)), LY_EMEM);
+            rec->ext = ext;
+            rec->node = node;
+
+            ret = ly_set_add(node_exts, rec, 1, NULL);
+            if (ret) {
+                free(rec);
+                break;
+            }
+        }
+    }
+
+    return ret;
+}
+
 LY_ERR
-lyd_validate_unres(struct lyd_node **tree, const struct lys_module *mod, struct ly_set *node_when,
+lysc_node_ext_tovalidate(struct ly_set *node_exts, struct lyd_node *node)
+{
+    const struct lysc_node *schema = node->schema;
+
+    if (!schema) {
+        /* nothing to do */
+        return LY_SUCCESS;
+    }
+
+    /* node's extensions */
+    LY_CHECK_RET(node_ext_tovalidate_add(node_exts, schema->exts, node));
+
+    if (schema->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
+        /* type's extensions */
+        LY_CHECK_RET(node_ext_tovalidate_add(node_exts, ((struct lysc_node_leaf *)schema)->type->exts, node));
+    }
+
+    return LY_SUCCESS;
+}
+
+LY_ERR
+lyd_validate_unres(struct lyd_node **tree, const struct lys_module *mod, struct ly_set *node_when, struct ly_set *node_exts,
         struct ly_set *node_types, struct ly_set *meta_types, struct lyd_node **diff)
 {
     LY_ERR ret = LY_SUCCESS;
@@ -245,6 +296,23 @@
         assert(!node_when->count);
     }
 
+    if (node_exts && node_exts->count) {
+        i = node_exts->count;
+        do {
+            --i;
+
+            struct node_ext *item = node_exts->objs[i];
+
+            LOG_LOCSET(item->node->schema, item->node, NULL, NULL);
+            ret = item->ext->def->plugin->validate(item->ext, item->node);
+            LOG_LOCBACK(item->node->schema ? 1 : 0, 1, 0, 0);
+            LY_CHECK_RET(ret);
+
+            /* remove this node from the set */
+            ly_set_rm_index(node_exts, i, free);
+        } while (i);
+    }
+
     if (node_types && node_types->count) {
         /* finish incompletely validated terminal values (traverse from the end for efficient set removal) */
         i = node_types->count;
@@ -1310,6 +1378,7 @@
  *
  * @param[in] root Subtree root.
  * @param[in,out] node_when Set for nodes with when conditions.
+ * @param[in,out] node_exts Set for nodes and extension instances with validation plugin callback.
  * @param[in,out] node_types Set for unres node types.
  * @param[in,out] meta_types Set for unres metadata types.
  * @param[in] impl_opts Implicit options, see @ref implicitoptions.
@@ -1317,7 +1386,7 @@
  * @return LY_ERR value.
  */
 static LY_ERR
-lyd_validate_subtree(struct lyd_node *root, struct ly_set *node_when, struct ly_set *node_types,
+lyd_validate_subtree(struct lyd_node *root, struct ly_set *node_when, struct ly_set *node_exts, struct ly_set *node_types,
         struct ly_set *meta_types, uint32_t impl_opts, struct lyd_node **diff)
 {
     const struct lyd_meta *meta;
@@ -1339,13 +1408,14 @@
             LY_CHECK_RET(lyd_validate_new(lyd_node_child_p(node), node->schema, NULL, diff));
 
             /* add nested defaults */
-            LY_CHECK_RET(lyd_new_implicit_r(node, lyd_node_child_p(node), NULL, NULL, NULL, NULL, impl_opts, diff));
+            LY_CHECK_RET(lyd_new_implicit_r(node, lyd_node_child_p(node), NULL, NULL, NULL, NULL, NULL, impl_opts, diff));
         }
 
         if (lysc_has_when(node->schema)) {
             /* when evaluation */
             LY_CHECK_RET(ly_set_add(node_when, (void *)node, 1, NULL));
         }
+        LY_CHECK_RET(lysc_node_ext_tovalidate(node_exts, node));
 
         LYD_TREE_DFS_END(root, node);
     }
@@ -1355,20 +1425,22 @@
 
 LY_ERR
 lyd_validate(struct lyd_node **tree, const struct lys_module *module, const struct ly_ctx *ctx, uint32_t val_opts,
-        ly_bool validate_subtree, struct ly_set *node_when_p, struct ly_set *node_types_p, struct ly_set *meta_types_p,
-        struct lyd_node **diff)
+        ly_bool validate_subtree, struct ly_set *node_when_p, struct ly_set *node_exts_p,
+        struct ly_set *node_types_p, struct ly_set *meta_types_p, struct lyd_node **diff)
 {
     LY_ERR ret = LY_SUCCESS;
     struct lyd_node *first, *next, **first2, *iter;
     const struct lys_module *mod;
-    struct ly_set node_types = {0}, meta_types = {0}, node_when = {0};
+    struct ly_set node_types = {0}, meta_types = {0}, node_when = {0}, node_exts = {0};
     uint32_t i = 0;
 
     assert(tree && ctx);
-    assert((node_when_p && node_types_p && meta_types_p) || (!node_when_p && !node_types_p && !meta_types_p));
+    assert((node_when_p && node_exts_p && node_types_p && meta_types_p) ||
+            (!node_when_p && !node_exts_p && !node_types_p && !meta_types_p));
 
     if (!node_when_p) {
         node_when_p = &node_when;
+        node_exts_p = &node_exts;
         node_types_p = &node_types;
         meta_types_p = &meta_types;
     }
@@ -1396,7 +1468,7 @@
 
         /* add all top-level defaults for this module, if going to validate subtree, do not add into unres sets
          * (lyd_validate_subtree() adds all the nodes in that case) */
-        ret = lyd_new_implicit_r(NULL, first2, NULL, mod, validate_subtree ? NULL : node_when_p,
+        ret = lyd_new_implicit_r(NULL, first2, NULL, mod, validate_subtree ? NULL : node_when_p, validate_subtree ? NULL : node_exts_p,
                 validate_subtree ? NULL : node_types_p, (val_opts & LYD_VALIDATE_NO_STATE) ? LYD_IMPLICIT_NO_STATE : 0, diff);
         LY_CHECK_GOTO(ret, cleanup);
 
@@ -1408,14 +1480,14 @@
         if (validate_subtree) {
             /* process nested nodes */
             LY_LIST_FOR(*first2, iter) {
-                ret = lyd_validate_subtree(iter, node_when_p, node_types_p, meta_types_p,
+                ret = lyd_validate_subtree(iter, node_when_p, node_exts_p, node_types_p, meta_types_p,
                         (val_opts & LYD_VALIDATE_NO_STATE) ? LYD_IMPLICIT_NO_STATE : 0, diff);
                 LY_CHECK_GOTO(ret, cleanup);
             }
         }
 
         /* finish incompletely validated terminal values/attributes and when conditions */
-        ret = lyd_validate_unres(first2, mod, node_when_p, node_types_p, meta_types_p, diff);
+        ret = lyd_validate_unres(first2, mod, node_when_p, node_exts_p, node_types_p, meta_types_p, diff);
         LY_CHECK_GOTO(ret, cleanup);
 
         /* perform final validation that assumes the data tree is final */
@@ -1425,6 +1497,7 @@
 
 cleanup:
     ly_set_erase(&node_when, NULL);
+    ly_set_erase(&node_exts, NULL);
     ly_set_erase(&node_types, NULL);
     ly_set_erase(&meta_types, NULL);
     return ret;
@@ -1441,7 +1514,7 @@
         *diff = NULL;
     }
 
-    return lyd_validate(tree, NULL, ctx, val_opts, 1, NULL, NULL, NULL, diff);
+    return lyd_validate(tree, NULL, ctx, val_opts, 1, NULL, NULL, NULL, NULL, diff);
 }
 
 API LY_ERR
@@ -1452,7 +1525,7 @@
         *diff = NULL;
     }
 
-    return lyd_validate(tree, module, (*tree) ? LYD_CTX(*tree) : module->ctx, val_opts, 1, NULL, NULL, NULL, diff);
+    return lyd_validate(tree, module, (*tree) ? LYD_CTX(*tree) : module->ctx, val_opts, 1, NULL, NULL, NULL, NULL, diff);
 }
 
 /**
@@ -1527,6 +1600,7 @@
  * @param[in] int_opts Internal parser options.
  * @param[in] validate_subtree Whether subtree was already validated (as part of data parsing) or not (separate validation).
  * @param[in] node_when_p Set of nodes with when conditions, if NULL a local set is used.
+ * @param[in] node_exts Set of nodes with extension instances with validation plugin callback, if NULL a local set is used.
  * @param[in] node_types_p Set of unres node types, if NULL a local set is used.
  * @param[in] meta_types_p Set of unres metadata types, if NULL a local set is used.
  * @param[out] diff Optional diff with any changes made by the validation.
@@ -1535,18 +1609,20 @@
  */
 static LY_ERR
 _lyd_validate_op(struct lyd_node *op_tree, struct lyd_node *op_node, const struct lyd_node *dep_tree,
-        uint32_t int_opts, ly_bool validate_subtree, struct ly_set *node_when_p, struct ly_set *node_types_p,
-        struct ly_set *meta_types_p, struct lyd_node **diff)
+        uint32_t int_opts, ly_bool validate_subtree, struct ly_set *node_when_p, struct ly_set *node_exts_p,
+        struct ly_set *node_types_p, struct ly_set *meta_types_p, struct lyd_node **diff)
 {
     LY_ERR rc = LY_SUCCESS;
     struct lyd_node *tree_sibling, *tree_parent, *op_subtree, *op_parent, *child;
-    struct ly_set node_types = {0}, meta_types = {0}, node_when = {0};
+    struct ly_set node_types = {0}, meta_types = {0}, node_when = {0}, node_exts = {0};
 
     assert(op_tree && op_node);
-    assert((node_when_p && node_types_p && meta_types_p) || (!node_when_p && !node_types_p && !meta_types_p));
+    assert((node_when_p && node_exts_p && node_types_p && meta_types_p) ||
+            (!node_when_p && !node_exts_p && !node_types_p && !meta_types_p));
 
     if (!node_when_p) {
         node_when_p = &node_when;
+        node_exts_p = &node_exts;
         node_types_p = &node_types;
         meta_types_p = &meta_types;
     }
@@ -1564,26 +1640,28 @@
 
     if (int_opts & LYD_INTOPT_REPLY) {
         /* add output children defaults */
-        rc = lyd_new_implicit_r(op_node, lyd_node_child_p(op_node), NULL, NULL, node_when_p, node_types_p,
+        rc = lyd_new_implicit_r(op_node, lyd_node_child_p(op_node), NULL, NULL, node_when_p, node_exts_p, node_types_p,
                 LYD_IMPLICIT_OUTPUT, diff);
         LY_CHECK_GOTO(rc, cleanup);
 
         if (validate_subtree) {
             /* skip validating the operation itself, go to children directly */
             LY_LIST_FOR(lyd_child(op_node), child) {
-                LY_CHECK_GOTO(rc = lyd_validate_subtree(child, node_when_p, node_types_p, meta_types_p, 0, diff), cleanup);
+                rc = lyd_validate_subtree(child, node_when_p, node_exts_p, node_types_p, meta_types_p, 0, diff);
+                LY_CHECK_GOTO(rc, cleanup);
             }
         }
     } else {
         if (validate_subtree) {
             /* prevalidate whole operation subtree */
-            LY_CHECK_GOTO(rc = lyd_validate_subtree(op_node, node_when_p, node_types_p, meta_types_p, 0, diff), cleanup);
+            rc = lyd_validate_subtree(op_node, node_when_p, node_exts_p, node_types_p, meta_types_p, 0, diff);
+            LY_CHECK_GOTO(rc, cleanup);
         }
     }
 
     /* finish incompletely validated terminal values/attributes and when conditions on the full tree */
-    LY_CHECK_GOTO(rc = lyd_validate_unres((struct lyd_node **)&dep_tree, NULL, node_when_p, node_types_p, meta_types_p,
-            diff), cleanup);
+    LY_CHECK_GOTO(rc = lyd_validate_unres((struct lyd_node **)&dep_tree, NULL,
+            node_when_p, node_exts_p, node_types_p, meta_types_p, diff), cleanup);
 
     /* perform final validation of the operation/notification */
     lyd_validate_obsolete(op_node);
@@ -1601,6 +1679,7 @@
     }
 
     ly_set_erase(&node_when, NULL);
+    ly_set_erase(&node_exts, NULL);
     ly_set_erase(&node_types, NULL);
     ly_set_erase(&meta_types, NULL);
     return rc;
@@ -1648,5 +1727,5 @@
     }
 
     /* validate */
-    return _lyd_validate_op(op_tree, op_node, dep_tree, int_opts, 1, NULL, NULL, NULL, diff);
+    return _lyd_validate_op(op_tree, op_node, dep_tree, int_opts, 1, NULL, NULL, NULL, NULL, diff);
 }
diff --git a/src/validation.h b/src/validation.h
index 5264cc7..aad751e 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -28,6 +28,15 @@
 enum lyd_diff_op;
 
 /**
+ * @brief Add information about the node's extensions having their own validation callback into an unres set.
+ *
+ * @param[in,out] node_exts Unres set for holding information for validating extension instances.
+ * @param[in] node Data node to be examined.
+ * @return LY_ERR values.
+ */
+LY_ERR lysc_node_ext_tovalidate(struct ly_set *node_exts, struct lyd_node *node);
+
+/**
  * @brief Add new changes into a diff. They are always merged.
  *
  * @param[in] node Node/subtree to add.
@@ -47,13 +56,14 @@
  * If set, it is expected @p tree should point to the first node of @p mod. Otherwise it will simply be
  * the first top-level sibling.
  * @param[in] node_when Set with nodes with "when" conditions, can be NULL.
+ * @param[in] node_exts Set with nodes with extension instances with validation plugin callback, can be NULL.
  * @param[in] node_types Set with nodes with unresolved types, can be NULL
  * @param[in] meta_types Set with metdata with unresolved types, can be NULL.
  * @param[in,out] diff Validation diff.
  * @return LY_ERR value.
  */
 LY_ERR lyd_validate_unres(struct lyd_node **tree, const struct lys_module *mod, struct ly_set *node_when,
-        struct ly_set *node_types, struct ly_set *meta_types, struct lyd_node **diff);
+        struct ly_set *node_exts, struct ly_set *node_types, struct ly_set *meta_types, struct lyd_node **diff);
 
 /**
  * @brief Validate new siblings. Specifically, check duplicated instances, autodelete default values and cases.
@@ -78,13 +88,14 @@
  * @param[in] val_opts Validation options, see @ref datavalidationoptions.
  * @param[in] validate_subtree Whether subtree was already validated (as part of data parsing) or not (separate validation).
  * @param[in] node_when_p Set of nodes with when conditions, if NULL a local set is used.
+ * @param[in] node_exts Set with nodes with extension instances with validation plugin callback, if NULL a local set is used.
  * @param[in] node_types_p Set of unres node types, if NULL a local set is used.
  * @param[in] meta_types_p Set of unres metadata types, if NULL a local set is used.
  * @param[out] diff Generated validation diff, not generated if NULL.
  * @return LY_ERR value.
  */
 LY_ERR lyd_validate(struct lyd_node **tree, const struct lys_module *module, const struct ly_ctx *ctx, uint32_t val_opts,
-        ly_bool validate_subtree, struct ly_set *node_when_p, struct ly_set *node_types_p, struct ly_set *meta_types_p,
-        struct lyd_node **diff);
+        ly_bool validate_subtree, struct ly_set *node_when_p, struct ly_set *node_exts_p,
+        struct ly_set *node_types_p, struct ly_set *meta_types_p, struct lyd_node **diff);
 
 #endif /* LY_VALIDATION_H_ */
diff --git a/tests/plugins/CMakeLists.txt b/tests/plugins/CMakeLists.txt
index 3eae019..49f0308 100644
--- a/tests/plugins/CMakeLists.txt
+++ b/tests/plugins/CMakeLists.txt
@@ -15,4 +15,5 @@
 endfunction(ly_add_plugin)
 
 ly_add_plugin(NAME invalid SOURCES invalid.c)
-ly_add_plugin(NAME simple SOURCES simple.c)
\ No newline at end of file
+ly_add_plugin(NAME simple SOURCES simple.c)
+ly_add_plugin(NAME validate SOURCES validate.c)
diff --git a/tests/plugins/validate.c b/tests/plugins/validate.c
new file mode 100644
index 0000000..374f856
--- /dev/null
+++ b/tests/plugins/validate.c
@@ -0,0 +1,69 @@
+/**
+ * @file plugins/simple.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Simple testing plugins implementation
+ *
+ * Copyright (c) 2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libyang.h"
+#include "plugins_exts.h"
+
+/**
+ * @brief Compile NAMC's extension instances.
+ *
+ * Implementation of ::lyplg_ext_compile_clb callback set as ::lyext_plugin.compile.
+ */
+static LY_ERR
+compile(struct lysc_ctx *cctx, const struct lysp_ext_instance *p_ext, struct lysc_ext_instance *c_ext)
+{
+    /* check that the extension is instantiated at an allowed place - data node */
+    if (!LY_STMT_IS_DATA_NODE(c_ext->parent_stmt) && (c_ext->parent_stmt != LY_STMT_TYPE)) {
+        lyplg_ext_log(c_ext, LY_LLWRN, 0, lysc_ctx_get_path(cctx),
+                "Extension %s is allowed only in a data nodes, but it is placed in \"%s\" statement.",
+                p_ext->name, ly_stmt2str(c_ext->parent_stmt));
+        return LY_ENOT;
+    }
+
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Compile NAMC's extension instances.
+ *
+ * Implementation of ::lyplg_ext_data_validation_clb callback set as ::lyext_plugin.validate.
+ */
+static LY_ERR
+validate(struct lysc_ext_instance *ext, struct lyd_node *node)
+{
+    lyplg_ext_log(ext, LY_LLWRN, LY_SUCCESS, NULL, "extra validation callback invoked on %s", node->schema->name);
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Plugin descriptions for the test extensions
+ */
+LYPLG_EXTENSIONS = {
+    {
+        .module = "libyang-plugins-validate",
+        .revision = NULL,
+        .name = "extra-validation",
+
+        .plugin.id = "libyang 2 - validation test, version 1",
+        .plugin.compile = &compile,
+        .plugin.validate = &validate,
+        .plugin.sprinter = NULL,
+        .plugin.free = NULL
+    },
+    {0} /* terminating zeroed item */
+};
diff --git a/tests/utests/basic/test_plugins.c b/tests/utests/basic/test_plugins.c
index 015d4b0..81a991d 100644
--- a/tests/utests/basic/test_plugins.c
+++ b/tests/utests/basic/test_plugins.c
@@ -83,12 +83,68 @@
     assert_int_equal(LY_SUCCESS, lyplg_add(TESTS_BIN "/plugins/plugin_simple" LYPLG_SUFFIX));
 }
 
+static void
+test_validation(void **state)
+{
+    const struct lys_module *mod;
+    struct lyd_node *tree;
+    const char *data;
+    const char *schema = "module libyang-plugins-validate {"
+            "  namespace urn:libyang:tests:plugins:validate;"
+            "  prefix v;"
+            "  extension extra-validation;"
+            "  typedef note { type string { v:extra-validation;}}"
+            "  leaf test1 {"
+            "    type v:note;"
+            "  }"
+            "  leaf test2 {"
+            "    type string;"
+            "    v:extra-validation;"
+            "  }"
+            "  leaf test3 {"
+            "    type string {v:extra-validation;}"
+            "  }"
+            "  leaf test4 {"
+            "    type string;"
+            "  }"
+            "}";
+
+    assert_int_equal(LY_SUCCESS, lyplg_add(TESTS_BIN "/plugins/plugin_validate" LYPLG_SUFFIX));
+
+    UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+
+    /* test1 - extra-validation done based on typedef's extension */
+    data = "<test1 xmlns=\"urn:libyang:tests:plugins:validate\">xxx</test1>";
+    assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, 0, LYD_VALIDATE_PRESENT, &tree));
+    CHECK_LOG_CTX("Extension plugin \"libyang 2 - validation test, version 1\": extra validation callback invoked on test1", NULL);
+    lyd_free_all(tree);
+
+    /* test2 - extra-validation done based on node's extension */
+    data = "<test2 xmlns=\"urn:libyang:tests:plugins:validate\">xxx</test2>";
+    assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, 0, LYD_VALIDATE_PRESENT, &tree));
+    CHECK_LOG_CTX("Extension plugin \"libyang 2 - validation test, version 1\": extra validation callback invoked on test2", NULL);
+    lyd_free_all(tree);
+
+    /* test3 - extra-validation done based on node type's extension */
+    data = "<test3 xmlns=\"urn:libyang:tests:plugins:validate\">xxx</test3>";
+    assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, 0, LYD_VALIDATE_PRESENT, &tree));
+    CHECK_LOG_CTX("Extension plugin \"libyang 2 - validation test, version 1\": extra validation callback invoked on test3", NULL);
+    lyd_free_all(tree);
+
+    /* test4 - extra-validation not done */
+    data = "<test4 xmlns=\"urn:libyang:tests:plugins:validate\">xxx</test4>";
+    assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, 0, LYD_VALIDATE_PRESENT, &tree));
+    CHECK_LOG_CTX(NULL, NULL);
+    lyd_free_all(tree);
+}
+
 int
 main(void)
 {
     const struct CMUnitTest tests[] = {
         UTEST(test_add_invalid),
         UTEST(test_add_simple),
+        UTEST(test_validation),
     };
 
     return cmocka_run_group_tests(tests, NULL, NULL);