types FEATURE support identityref data type values
diff --git a/src/parser_xml.c b/src/parser_xml.c
index 3d86181..cf85cf7 100644
--- a/src/parser_xml.c
+++ b/src/parser_xml.c
@@ -47,6 +47,24 @@
 };
 
 /**
+ * @brief XML-parser's implementation of ly_type_resolve_prefix() callback to provide mapping between prefixes used in the values to the schema
+ * via XML namespaces.
+ */
+static const struct lys_module *
+lydxml_resolve_prefix(struct ly_ctx *ctx, const char *prefix, size_t prefix_len, void *parser)
+{
+    const struct lyxml_ns *ns;
+    struct lyxml_context *xmlctx = (struct lyxml_context*)parser;
+
+    ns = lyxml_ns_get(xmlctx, prefix, prefix_len);
+    if (!ns) {
+        return NULL;
+    }
+
+    return ly_ctx_get_module_implemented_ns(ctx, ns->uri);
+}
+
+/**
  * @brief Parse XML attributes of the XML element of YANG data.
  *
  * @param[in] ctx XML YANG data parser context.
@@ -235,7 +253,7 @@
                 value = "";
                 value_len = 0;
             }
-            LY_CHECK_ERR_GOTO(ret = lyd_value_parse((struct lyd_node_term*)cur, value, value_len, dynamic),
+            LY_CHECK_ERR_GOTO(ret = lyd_value_parse((struct lyd_node_term*)cur, value, value_len, dynamic, lydxml_resolve_prefix, ctx),
                               if (dynamic){free(value);}, cleanup);
         } else if (snode->nodetype & LYD_NODE_INNER) {
             int dynamic = 0;
diff --git a/src/plugins_types.c b/src/plugins_types.c
index 4e29fb0..4a80a51 100644
--- a/src/plugins_types.c
+++ b/src/plugins_types.c
@@ -357,7 +357,7 @@
  */
 static LY_ERR
 ly_type_validate_int(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
-                     const char **canonized, struct ly_err_item **err, void **priv)
+                     ly_type_resolve_prefix UNUSED(get_prefix), void *UNUSED(parser), const char **canonized, struct ly_err_item **err, void **priv)
 {
     LY_ERR ret;
     int64_t i;
@@ -445,7 +445,7 @@
  */
 static LY_ERR
 ly_type_validate_uint(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
-                      const char **canonized, struct ly_err_item **err, void **priv)
+                      ly_type_resolve_prefix UNUSED(get_prefix), void *UNUSED(parser), const char **canonized, struct ly_err_item **err, void **priv)
 {
     LY_ERR ret;
     uint64_t u;
@@ -515,7 +515,7 @@
  */
 static LY_ERR
 ly_type_validate_decimal64(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
-                           const char **canonized, struct ly_err_item **err, void **priv)
+                           ly_type_resolve_prefix UNUSED(get_prefix), void *UNUSED(parser), const char **canonized, struct ly_err_item **err, void **priv)
 {
     int64_t d;
     struct lysc_type_dec* type_dec = (struct lysc_type_dec*)type;
@@ -613,7 +613,7 @@
  */
 static LY_ERR
 ly_type_validate_binary(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
-                        const char **canonized, struct ly_err_item **err, void **UNUSED(priv))
+                        ly_type_resolve_prefix UNUSED(get_prefix), void *UNUSED(parser), const char **canonized, struct ly_err_item **err, void **UNUSED(priv))
 {
     size_t start = 0, stop = 0, count = 0, u, termination = 0;
     struct lysc_type_bin *type_bin = (struct lysc_type_bin *)type;
@@ -707,13 +707,10 @@
  */
 static LY_ERR
 ly_type_validate_string(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
-                        const char **canonized, struct ly_err_item **err, void **UNUSED(priv))
+                        ly_type_resolve_prefix UNUSED(get_prefix), void *UNUSED(parser), const char **canonized, struct ly_err_item **err, void **UNUSED(priv))
 {
     struct lysc_type_str *type_str = (struct lysc_type_str *)type;
 
-    /* initiate */
-    *err = NULL;
-
     /* length restriction of the string */
     if (type_str->length) {
         char buf[22];
@@ -747,7 +744,7 @@
  */
 static LY_ERR
 ly_type_validate_bits(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
-                       const char **canonized, struct ly_err_item **err, void **priv)
+                      ly_type_resolve_prefix UNUSED(get_prefix), void *UNUSED(parser), const char **canonized, struct ly_err_item **err, void **priv)
 {
     LY_ERR ret = LY_EVALID;
     size_t item_len;
@@ -939,7 +936,7 @@
  */
 static LY_ERR
 ly_type_validate_enum(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
-                       const char **canonized, struct ly_err_item **err, void **priv)
+                      ly_type_resolve_prefix UNUSED(get_prefix), void *UNUSED(parser), const char **canonized, struct ly_err_item **err, void **priv)
 {
     unsigned int u, v;
     char *errmsg = NULL;
@@ -1022,7 +1019,7 @@
  */
 static LY_ERR
 ly_type_validate_boolean(struct ly_ctx *ctx, struct lysc_type *UNUSED(type), const char *value, size_t value_len, int options,
-                         const char **canonized, struct ly_err_item **err, void **priv)
+                         ly_type_resolve_prefix UNUSED(get_prefix), void *UNUSED(parser), const char **canonized, struct ly_err_item **err, void **priv)
 {
     int8_t i;
 
@@ -1091,7 +1088,7 @@
  */
 static LY_ERR
 ly_type_validate_empty(struct ly_ctx *ctx, struct lysc_type *UNUSED(type), const char *value, size_t value_len, int options,
-                       const char **canonized, struct ly_err_item **err, void **UNUSED(priv))
+                       ly_type_resolve_prefix UNUSED(get_prefix), void *UNUSED(parser), const char **canonized, struct ly_err_item **err, void **UNUSED(priv))
 {
     if (value_len) {
         char *errmsg;
@@ -1106,6 +1103,131 @@
     return LY_SUCCESS;
 }
 
+API LY_ERR
+ly_type_identity_isderived(struct lysc_ident *base, struct lysc_ident *der)
+{
+    unsigned int u;
+
+    LY_ARRAY_FOR(base->derived, u) {
+        if (der == base->derived[u]) {
+            return LY_SUCCESS;
+        }
+        if (!ly_type_identity_isderived(base->derived[u], der)) {
+            return LY_SUCCESS;
+        }
+    }
+    return LY_ENOTFOUND;
+}
+
+/**
+ * @brief Validate value of the YANG built-in identiytref type.
+ *
+ * Implementation of the ly_type_validate_clb.
+ */
+static LY_ERR
+ly_type_validate_identityref(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
+                             ly_type_resolve_prefix get_prefix, void *parser, const char **canonized, struct ly_err_item **err, void **priv)
+{
+    struct lysc_type_identityref *type_ident = (struct lysc_type_identityref *)type;
+    const char *id_name, *prefix = value;
+    size_t id_len, prefix_len;
+    char *errmsg = NULL;
+    const struct lys_module *mod;
+    unsigned int u;
+    struct lysc_ident *ident;
+
+    /* locate prefix if any */
+    for (prefix_len = 0; prefix_len < value_len && value[prefix_len] != ':'; ++prefix_len);
+    if (prefix_len < value_len) {
+        id_name = &value[prefix_len + 1];
+        id_len = value_len - (prefix_len + 1);
+    } else {
+        prefix_len = 0;
+        id_name = value;
+        id_len = value_len;
+    }
+
+    if (!id_len) {
+        errmsg = strdup("Invalid empty identityref value.");
+        goto error;
+    }
+
+    mod = get_prefix(ctx, prefix, prefix_len, parser);
+    if (!mod) {
+        asprintf(&errmsg, "Invalid identityref \"%.*s\" value - unable to map prefix to YANG schema.", (int)value_len, value);
+        goto error;
+    }
+    LY_ARRAY_FOR(mod->compiled->identities, u) {
+        ident = &mod->compiled->identities[u]; /* shortcut */
+        if (!strncmp(ident->name, id_name, id_len) && ident->name[id_len] == '\0') {
+            /* we have match */
+            break;
+        }
+    }
+    if (u == LY_ARRAY_SIZE(mod->compiled->identities)) {
+        /* no match */
+        asprintf(&errmsg, "Invalid identityref \"%.*s\" value - identity not found.", (int)value_len, value);
+        goto error;
+    }
+
+    /* check that the identity matches some of the type's base identities */
+    LY_ARRAY_FOR(type_ident->bases, u) {
+        if (!ly_type_identity_isderived(type_ident->bases[u], ident)) {
+            /* we have match */
+            break;
+        }
+    }
+    if (u == LY_ARRAY_SIZE(type_ident->bases)) {
+        /* no match */
+        asprintf(&errmsg, "Invalid identityref \"%.*s\" value - identity not accepted by the type specification.", (int)value_len, value);
+        goto error;
+    }
+
+    if (options & LY_TYPE_OPTS_CANONIZE) {
+        if (id_name == value && (options & LY_TYPE_OPTS_DYNAMIC)) {
+            *canonized = lydict_insert_zc(ctx, (char*)value);
+            value = NULL;
+        } else {
+            *canonized = lydict_insert(ctx, id_name, id_len);
+        }
+    }
+
+    if (options & LY_TYPE_OPTS_STORE) {
+        *priv = ident;
+    }
+
+    if (options & LY_TYPE_OPTS_DYNAMIC) {
+        free((char*)value);
+    }
+
+    return LY_SUCCESS;
+
+error:
+    *err = ly_err_new(LY_LLERR, LY_EVALID, LYVE_RESTRICTION, errmsg, NULL, NULL);
+    return LY_EVALID;
+}
+
+/**
+ * @brief Store value of the YANG built-in identityref type.
+ *
+ * Implementation of the ly_type_store_clb.
+ */
+static LY_ERR
+ly_type_store_identityref(struct ly_ctx *UNUSED(ctx), struct lysc_type *UNUSED(type), int options,
+                          struct lyd_value *value, struct ly_err_item **UNUSED(err), void **priv)
+{
+    if (options & LY_TYPE_OPTS_VALIDATE) {
+        /* the value was prepared by ly_type_validate_enum() */
+        value->ident = *priv;
+    } else {
+        /* TODO if there is usecase for store without validate */
+        LOGINT(NULL);
+        return LY_EINT;
+    }
+
+    return LY_SUCCESS;
+}
+
 struct lysc_type_plugin ly_builtin_type_plugins[LY_DATA_TYPE_COUNT] = {
     {0}, /* LY_TYPE_UNKNOWN */
     {.type = LY_TYPE_BINARY, .validate = ly_type_validate_binary, .store = NULL, .free = NULL},
@@ -1119,7 +1241,7 @@
     {.type = LY_TYPE_DEC64, .validate = ly_type_validate_decimal64, .store = ly_type_store_decimal64, .free = NULL},
     {.type = LY_TYPE_EMPTY, .validate = ly_type_validate_empty, .store = NULL, .free = NULL},
     {.type = LY_TYPE_ENUM, .validate = ly_type_validate_enum, .store = ly_type_store_enum, .free = NULL},
-    {0}, /* TODO LY_TYPE_IDENT */
+    {.type = LY_TYPE_IDENT, .validate = ly_type_validate_identityref, .store = ly_type_store_identityref, .free = NULL},
     {0}, /* TODO LY_TYPE_INST */
     {0}, /* TODO LY_TYPE_LEAFREF */
     {0}, /* TODO LY_TYPE_UNION */
diff --git a/src/plugins_types.h b/src/plugins_types.h
index 9c48fd8..a4cbfbb 100644
--- a/src/plugins_types.h
+++ b/src/plugins_types.h
@@ -78,6 +78,20 @@
  */
 
 /**
+ * @brief Callback provided by the data parsers to type plugins to resolve (format-specific) mapping between prefixes used in the value strings
+ * to the YANG schemas.
+ *
+ * XML uses XML namespaces, JSON uses schema names as prefixes.
+ *
+ * @param[in] ctx libyang context to find the schema.
+ * @param[in] prefix Prefix found in the value string
+ * @param[in] prefix_len Length of the @p prefix.
+ * @param[in] parser Internal parser's data.
+ * @return Pointer to the YANG schema identified by the provided prefix or NULL if no mapping found.
+ */
+typedef const struct lys_module *(*ly_type_resolve_prefix)(struct ly_ctx *ctx, const char *prefix, size_t prefix_len, void *parser);
+
+/**
  * @brief Callback to validate the given @p value according to the given @p type. Optionaly, it can be requested to canonize the value.
  *
  * Note that the \p value string is not necessarily zero-terminated. The provided \p value_len is always correct.
@@ -87,6 +101,10 @@
  * @param[in] value Lexical representation of the value to be validated (and canonized).
  * @param[in] value_len Length of the given \p value.
  * @param[in] options [Type plugin options ](@ref plugintypeopts).
+ *
+ * @param[in] get_prefix Parser-specific getter to resolve prefixes used in the value strings.
+ * @param[in] parser Parser's data for @p get_prefix
+ *
  * @param[out] canonized If LY_TYPE_VALIDATE_CANONIZE option set, the canonized string stored in the @p ctx dictionary is returned via this parameter.
  * @param[out] err Optionally provided error information in case of failure. If not provided to the caller, a generic error message is prepared instead.
  * The error structure can be created by ly_err_new().
@@ -95,6 +113,7 @@
  * @return LY_ERR value if an error occurred and the value could not be canonized following the type's rules.
  */
 typedef LY_ERR (*ly_type_validate_clb)(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
+                                       ly_type_resolve_prefix get_prefix, void *parser,
                                        const char **canonized, struct ly_err_item **err, void **priv);
 
 /**
@@ -191,6 +210,16 @@
 LY_ERR parse_dec64(uint8_t fraction_digits, const char *value, size_t value_len, int64_t *ret, struct ly_err_item **err);
 
 /**
+ * @brief Decide if the @p derived identity is derived from (based on) the @p base identity.
+ *
+ * @param[in] base Expected base identity.
+ * @param[in] derived Expected derived identity.
+ * @return LY_SUCCESS if @p derived IS based on the @p base identity.
+ * @return LY_ENOTFOUND if @p derived IS NOT not based on the @p base identity.
+ */
+LY_ERR ly_type_identity_isderived(struct lysc_ident *base, struct lysc_ident *derived);
+
+/**
  * @brief Data type validator for a range/length-restricted values.
  *
  * @param[in] basetype Base built-in type of the type with the range specified to get know if the @p range structure represents range or length restriction.
diff --git a/src/tree_data_helpers.c b/src/tree_data_helpers.c
index fb9c3da..36f5ce7 100644
--- a/src/tree_data_helpers.c
+++ b/src/tree_data_helpers.c
@@ -84,7 +84,8 @@
 }
 
 LY_ERR
-lyd_value_parse(struct lyd_node_term *node, const char *value, size_t value_len, int dynamic)
+lyd_value_parse(struct lyd_node_term *node, const char *value, size_t value_len, int dynamic,
+                ly_type_resolve_prefix get_prefix, void *parser)
 {
     LY_ERR ret = LY_SUCCESS;
     struct ly_err_item *err = NULL;
@@ -97,7 +98,7 @@
     ctx = node->schema->module->ctx;
     type = ((struct lysc_node_leaf*)node->schema)->type;
     if (type->plugin->validate) {
-        ret = type->plugin->validate(ctx, type, value, value_len, options, &node->value.canonized, &err, &priv);
+        ret = type->plugin->validate(ctx, type, value, value_len, options, get_prefix, parser, &node->value.canonized, &err, &priv);
         if (ret) {
             ly_err_print(err);
             LOGVAL(ctx, LY_VLOG_STR, err->path, err->vecode, err->msg);
diff --git a/src/tree_data_internal.h b/src/tree_data_internal.h
index ec11578..d6225dc 100644
--- a/src/tree_data_internal.h
+++ b/src/tree_data_internal.h
@@ -16,6 +16,7 @@
 #define LY_TREE_DATA_INTERNAL_H_
 
 #include "tree_data.h"
+#include "plugins_types.h"
 
 /**
  * @brief Get address of a node's child pointer if any.
@@ -44,9 +45,11 @@
  * @param[in] value String value to be parsed.
  * @param[in] value_len Length of the give @p value (mandatory).
  * @param[in] dynamic Flag if @p value is a dynamically allocated memory and should be directly consumed/freed inside the function.
+ * @param[in] get_prefix Parser-specific getter to resolve prefixes used in the value strings.
+ * @param[in] parser Parser's data for @p get_prefix
  * @return LY_ERR value.
  */
-LY_ERR lyd_value_parse(struct lyd_node_term *node, const char *value, size_t value_len, int dynamic);
+LY_ERR lyd_value_parse(struct lyd_node_term *node, const char *value, size_t value_len, int dynamic, ly_type_resolve_prefix get_prefix, void *parser);
 
 /**
  * @brief Parse XML string as YANG data tree.
diff --git a/src/tree_schema_compile.c b/src/tree_schema_compile.c
index 2d73a01..160153b 100644
--- a/src/tree_schema_compile.c
+++ b/src/tree_schema_compile.c
@@ -710,7 +710,7 @@
 /**
  * @brief Find and process the referenced base identities from another identity or identityref
  *
- * For bases in identity se backlinks to them from the base identities. For identityref, store
+ * For bases in identity set backlinks to them from the base identities. For identityref, store
  * the array of pointers to the base identities. So one of the ident or bases parameter must be set
  * to distinguish these two use cases.
  *
diff --git a/tests/features/test_types.c b/tests/features/test_types.c
index faf6d70..1d0ad79 100644
--- a/tests/features/test_types.c
+++ b/tests/features/test_types.c
@@ -56,7 +56,10 @@
 setup(void **state)
 {
     struct state_s *s;
-    const char *schema_a = "module types {namespace urn:tests:types;prefix t;yang-version 1.1; feature f;"
+    const char *schema_a = "module defs {namespace urn:tests:defs;prefix d;yang-version 1.1;"
+            "identity crypto-alg; identity interface-type; identity ethernet {base interface-type;} identity fast-ethernet {base ethernet;}}";
+    const char *schema_b = "module types {namespace urn:tests:types;prefix t;yang-version 1.1; import defs {prefix defs;}"
+            "feature f; identity gigabit-ethernet { base defs:ethernet;}"
             "leaf binary {type binary {length 5 {error-message \"This base64 value must be of length 5.\";}}}"
             "leaf binary-norestr {type binary;}"
             "leaf int8 {type int8 {range 10..20;}}"
@@ -74,7 +77,8 @@
             "leaf str {type string {length 8..10; pattern '[a-z ]*';}}"
             "leaf str-norestr {type string;}"
             "leaf bool {type boolean;}"
-            "leaf empty {type empty;}}";
+            "leaf empty {type empty;}"
+            "leaf ident {type identityref {base defs:interface-type;}}}";
 
     s = calloc(1, sizeof *s);
     assert_non_null(s);
@@ -85,6 +89,7 @@
 
     assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, 0, &s->ctx));
     assert_non_null(lys_parse_mem(s->ctx, schema_a, LYS_IN_YANG));
+    assert_non_null(lys_parse_mem(s->ctx, schema_b, LYS_IN_YANG));
 
     *state = s;
 
@@ -589,6 +594,53 @@
     s->func = NULL;
 }
 
+static void
+test_identityref(void **state)
+{
+    struct state_s *s = (struct state_s*)(*state);
+    s->func = test_identityref;
+
+    struct lyd_node *tree;
+    struct lyd_node_term *leaf;
+
+    const char *data = "<ident xmlns=\"urn:tests:types\">gigabit-ethernet</ident>";
+
+    /* valid data */
+    assert_non_null(tree = lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    assert_int_equal(LYS_LEAF, tree->schema->nodetype);
+    assert_string_equal("ident", tree->schema->name);
+    leaf = (struct lyd_node_term*)tree;
+    assert_string_equal("gigabit-ethernet", leaf->value.canonized);
+    lyd_free_all(tree);
+
+    data = "<ident xmlns=\"urn:tests:types\" xmlns:x=\"urn:tests:defs\">x:fast-ethernet</ident>";
+    assert_non_null(tree = lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    assert_int_equal(LYS_LEAF, tree->schema->nodetype);
+    assert_string_equal("ident", tree->schema->name);
+    leaf = (struct lyd_node_term*)tree;
+    assert_string_equal("fast-ethernet", leaf->value.canonized);
+    lyd_free_all(tree);
+
+    /* invalid value */
+    data = "<ident xmlns=\"urn:tests:types\">fast-ethernet</ident>";
+    assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    logbuf_assert("Invalid identityref \"fast-ethernet\" value - identity not found. /");
+
+    data = "<ident xmlns=\"urn:tests:types\" xmlns:x=\"urn:tests:defs\">x:slow-ethernet</ident>";
+    assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    logbuf_assert("Invalid identityref \"x:slow-ethernet\" value - identity not found. /");
+
+    data = "<ident xmlns=\"urn:tests:types\" xmlns:x=\"urn:tests:defs\">x:crypto-alg</ident>";
+    assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    logbuf_assert("Invalid identityref \"x:crypto-alg\" value - identity not accepted by the type specification. /");
+
+    data = "<ident xmlns=\"urn:tests:types\" xmlns:x=\"urn:tests:unknown\">x:fast-ethernet</ident>";
+    assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    logbuf_assert("Invalid identityref \"x:fast-ethernet\" value - unable to map prefix to YANG schema. /");
+
+    s->func = NULL;
+}
+
 int main(void)
 {
     const struct CMUnitTest tests[] = {
@@ -601,6 +653,7 @@
         cmocka_unit_test_setup_teardown(test_binary, setup, teardown),
         cmocka_unit_test_setup_teardown(test_boolean, setup, teardown),
         cmocka_unit_test_setup_teardown(test_empty, setup, teardown),
+        cmocka_unit_test_setup_teardown(test_identityref, setup, teardown),
     };
 
     return cmocka_run_group_tests(tests, NULL, NULL);
diff --git a/tools/re/main.c b/tools/re/main.c
index fa76662..bb6e13a 100644
--- a/tools/re/main.c
+++ b/tools/re/main.c
@@ -272,7 +272,7 @@
     }
 
     type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
-    match = type->plugin->validate(ctx, type, str, strlen(str), LY_TYPE_OPTS_VALIDATE, NULL, &err, NULL);
+    match = type->plugin->validate(ctx, type, str, strlen(str), LY_TYPE_OPTS_VALIDATE, NULL, NULL, NULL, &err, NULL);
     if (verbose) {
         for (i = 0; i < patterns_count; i++) {
             fprintf(stdout, "pattern  %d: %s\n", i + 1, patterns[i]);