plugin types FEATURE ietf-inet-types plugins
diff --git a/src/plugins.c b/src/plugins.c
index 3b96c3f..dc0bba0 100644
--- a/src/plugins.c
+++ b/src/plugins.c
@@ -51,6 +51,9 @@
extern const struct lyplg_type_record plugins_string[];
extern const struct lyplg_type_record plugins_union[];
+extern const struct lyplg_type_record plugins_ip_address[];
+extern const struct lyplg_type_record plugins_ip_prefix[];
+
/*
* internal extension plugins records
*/
@@ -406,6 +409,9 @@
LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_string), error);
LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_union), error);
+ LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_ip_address), error);
+ LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_ip_prefix), error);
+
/* internal extensions */
LY_CHECK_GOTO(ret = plugins_insert(LYPLG_EXTENSION, plugins_metadata), error);
LY_CHECK_GOTO(ret = plugins_insert(LYPLG_EXTENSION, plugins_nacm), error);
diff --git a/src/plugins_types/ip_address.c b/src/plugins_types/ip_address.c
new file mode 100644
index 0000000..a5195bd
--- /dev/null
+++ b/src/plugins_types/ip_address.c
@@ -0,0 +1,166 @@
+/**
+ * @file ip_address.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief ietf-inet-types ip-address type plugin.
+ *
+ * Copyright (c) 2019-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
+ */
+
+#define _GNU_SOURCE
+
+#include "plugins_types.h"
+
+#include <arpa/inet.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libyang.h"
+
+#include "compat.h"
+
+/**
+ * @brief Canonize a valid IPv6 address.
+ *
+ * @param[in] ipv6_addr IPv6 address to canonize.
+ * @param[out] canonical Canonical format of @p ipv6_addr.
+ * @param[out] err Error information on error.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+canonize_ipv6_addr(const char *ipv6_addr, char **canonical, struct ly_err_item **err)
+{
+ char buf[sizeof(struct in6_addr)], *str;
+
+ *canonical = NULL;
+ *err = NULL;
+
+ str = malloc(INET6_ADDRSTRLEN);
+ if (!str) {
+ return LY_EMEM;
+ }
+
+ if (!inet_pton(AF_INET6, ipv6_addr, buf)) {
+ free(str);
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Failed to convert IPv6 address \"%s\".", ipv6_addr);
+ }
+
+ if (!inet_ntop(AF_INET6, buf, str, INET6_ADDRSTRLEN)) {
+ free(str);
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Failed to convert IPv6 address (%s).", strerror(errno));
+ }
+
+ *canonical = str;
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Validate, canonize and store value of the ietf-inet-types ip(v4/v6)-address type.
+ * Implementation of the ::lyplg_type_store_clb.
+ */
+static LY_ERR
+lyplg_type_store_ip_address(const struct ly_ctx *ctx, const struct lysc_type *type, const char *value, size_t value_len,
+ uint32_t options, LY_PREFIX_FORMAT format, void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node,
+ struct lyd_value *storage, struct lys_glob_unres *unres, struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ const char *ptr;
+ char *ipv6_addr, *result, *tmp;
+
+ /* store as a string */
+ ret = lyplg_type_store_string(ctx, type, value, value_len, options, format, prefix_data, hints, ctx_node,
+ storage, unres, err);
+ if (ret) {
+ return ret;
+ }
+
+ if (strchr(storage->canonical, ':')) {
+ /* canonize IPv6 address */
+ if ((ptr = strchr(storage->canonical, '%'))) {
+ /* there is a zone index */
+ ipv6_addr = strndup(storage->canonical, ptr - storage->canonical);
+ } else {
+ ipv6_addr = (char *)storage->canonical;
+ }
+
+ /* convert to canonical format */
+ ret = canonize_ipv6_addr(ipv6_addr, &result, err);
+ if (ptr) {
+ free(ipv6_addr);
+ }
+ if (ret) {
+ goto cleanup;
+ }
+
+ if (strncmp(storage->canonical, result, strlen(result))) {
+ /* some conversion took place */
+ if (ptr) {
+ /* concatenate the zone, if any */
+ tmp = result;
+ if (asprintf(&result, "%s%s", tmp, ptr) == -1) {
+ free(tmp);
+ ret = LY_EMEM;
+ goto cleanup;
+ }
+ free(tmp);
+ }
+
+ /* update the value */
+ lydict_remove(ctx, storage->canonical);
+ lydict_insert_zc(ctx, result, &storage->canonical);
+ } else {
+ free(result);
+ }
+ }
+
+cleanup:
+ if (ret) {
+ type->plugin->free(ctx, storage);
+ }
+ return ret;
+}
+
+/**
+ * @brief Plugin information for ip-address type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_ip_address[] = {
+ {
+ .module = "ietf-inet-types",
+ .revision = "2013-07-15",
+ .name = "ipv6-address",
+
+ .plugin.id = "libyang 2 - ipv6-address, version 1",
+ .plugin.store = lyplg_type_store_ip_address,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_simple,
+ .plugin.print = lyplg_type_print_simple,
+ .plugin.duplicate = lyplg_type_dup_simple,
+ .plugin.free = lyplg_type_free_simple
+ },
+ {
+ .module = "ietf-inet-types",
+ .revision = "2013-07-15",
+ .name = "ipv6-address-no-zone",
+
+ .plugin.id = "libyang 2 - ipv6-address-no-zone, version 1",
+ .plugin.store = lyplg_type_store_ip_address,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_simple,
+ .plugin.print = lyplg_type_print_simple,
+ .plugin.duplicate = lyplg_type_dup_simple,
+ .plugin.free = lyplg_type_free_simple
+ },
+ {0}
+};
diff --git a/src/plugins_types/ip_prefix.c b/src/plugins_types/ip_prefix.c
new file mode 100644
index 0000000..975be51
--- /dev/null
+++ b/src/plugins_types/ip_prefix.c
@@ -0,0 +1,298 @@
+/**
+ * @file ip_prefix.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief ietf-inet-types ip-prefix type plugin.
+ *
+ * Copyright (c) 2019-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
+ */
+
+#define _GNU_SOURCE
+
+#include "plugins_types.h"
+
+#include <arpa/inet.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libyang.h"
+
+#include "common.h"
+#include "compat.h"
+
+/**
+ * @brief Canonize an ipv4-prefix value.
+ *
+ * @param[in] ipv4_prefix Prefix to canonize.
+ * @param[out] canonical Canonical format of @p ipv4_prefix.
+ * @param[out] err Error structure on error.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+canonize_ipv4_prefix(const char *ipv4_prefix, char **canonical, struct ly_err_item **err)
+{
+ LY_ERR ret;
+ const char *pref_str;
+ char *ptr, *result = NULL;
+ uint32_t pref, addr_bin, i, mask;
+
+ *canonical = NULL;
+ *err = NULL;
+
+ pref_str = strchr(ipv4_prefix, '/');
+ if (!pref_str) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid IPv4 prefix \"%s\".", ipv4_prefix);
+ goto error;
+ }
+
+ /* learn prefix */
+ pref = strtoul(pref_str + 1, &ptr, 10);
+ if (ptr[0] || (pref > 32)) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid IPv4 prefix \"%s\".", ipv4_prefix);
+ goto error;
+ }
+
+ result = malloc(INET_ADDRSTRLEN + 3);
+ if (!result) {
+ ret = LY_EMEM;
+ goto error;
+ }
+
+ /* copy just the network prefix */
+ strncpy(result, ipv4_prefix, pref_str - ipv4_prefix);
+ result[pref_str - ipv4_prefix] = '\0';
+
+ /* convert it to binary form */
+ if (inet_pton(AF_INET, result, (void *)&addr_bin) != 1) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Failed to convert IPv4 address \"%s\".", result);
+ goto error;
+ }
+
+ /* zero host bits */
+ mask = 0;
+ for (i = 0; i < 32; ++i) {
+ mask <<= 1;
+ if (pref > i) {
+ mask |= 1;
+ }
+ }
+ mask = htonl(mask);
+ addr_bin &= mask;
+
+ /* convert back to string */
+ if (!inet_ntop(AF_INET, (void *)&addr_bin, result, INET_ADDRSTRLEN)) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Failed to convert IPv4 address (%s).", strerror(errno));
+ goto error;
+ }
+
+ /* add the prefix */
+ strcat(result, pref_str);
+
+ *canonical = result;
+ return LY_SUCCESS;
+
+error:
+ free(result);
+ return ret;
+}
+
+/**
+ * @brief Canonize an ipv6-prefix value.
+ *
+ * @param[in] ipv6_prefix Prefix to canonize.
+ * @param[out] canonical Canonical format of @p ipv6_prefix.
+ * @param[out] err Error structure on error.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+canonize_ipv6_prefix(const char *ipv4_prefix, char **canonical, struct ly_err_item **err)
+{
+ LY_ERR ret;
+ const char *pref_str;
+ char *ptr, *result = NULL;
+ unsigned long int pref, i, j;
+
+ union {
+ struct in6_addr s;
+ uint32_t a[4];
+ } addr_bin;
+ uint32_t mask;
+
+ *canonical = NULL;
+ *err = NULL;
+
+ pref_str = strchr(ipv4_prefix, '/');
+ if (!pref_str) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid IPv6 prefix \"%s\".", ipv4_prefix);
+ goto error;
+ }
+
+ /* learn prefix */
+ pref = strtoul(pref_str + 1, &ptr, 10);
+ if (ptr[0] || (pref > 128)) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid IPv6 prefix \"%s\".", ipv4_prefix);
+ goto error;
+ }
+
+ result = malloc(INET6_ADDRSTRLEN + 4);
+ if (!result) {
+ ret = LY_EMEM;
+ goto error;
+ }
+
+ /* copy just the network prefix */
+ strncpy(result, ipv4_prefix, pref_str - ipv4_prefix);
+ result[pref_str - ipv4_prefix] = '\0';
+
+ /* convert it to binary form */
+ if (inet_pton(AF_INET6, result, (void *)&addr_bin.s) != 1) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Failed to convert IPv6 address \"%s\".", result);
+ goto error;
+ }
+
+ /* zero host bits */
+ for (i = 0; i < 4; ++i) {
+ mask = 0;
+ for (j = 0; j < 32; ++j) {
+ mask <<= 1;
+ if (pref > (i * 32) + j) {
+ mask |= 1;
+ }
+ }
+ mask = htonl(mask);
+ addr_bin.a[i] &= mask;
+ }
+
+ /* convert back to string */
+ if (!inet_ntop(AF_INET6, (void *)&addr_bin.s, result, INET6_ADDRSTRLEN)) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Failed to convert IPv6 address (%s).", strerror(errno));
+ goto error;
+ }
+
+ /* add the prefix */
+ strcat(result, pref_str);
+
+ *canonical = result;
+ return LY_SUCCESS;
+
+error:
+ free(result);
+ return ret;
+}
+
+/**
+ * @brief Validate, canonize and store value of the ietf-inet-types ipv4-prefix type.
+ * Implementation of the ::lyplg_type_store_clb.
+ */
+static LY_ERR
+lyplg_type_store_ipv4_prefix(const struct ly_ctx *ctx, const struct lysc_type *type, const char *value, size_t value_len,
+ uint32_t options, LY_PREFIX_FORMAT format, void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node,
+ struct lyd_value *storage, struct lys_glob_unres *unres, struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *canonical;
+
+ /* store as a string */
+ ret = lyplg_type_store_string(ctx, type, value, value_len, options, format, prefix_data, hints, ctx_node,
+ storage, unres, err);
+ LY_CHECK_RET(ret);
+
+ /* canonize */
+ ret = canonize_ipv4_prefix(storage->canonical, &canonical, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ if (strcmp(canonical, storage->canonical)) {
+ /* some conversion took place, update the value */
+ lydict_remove(ctx, storage->canonical);
+ lydict_insert_zc(ctx, canonical, &storage->canonical);
+ } else {
+ free(canonical);
+ }
+
+cleanup:
+ if (ret) {
+ type->plugin->free(ctx, storage);
+ }
+ return ret;
+}
+
+/**
+ * @brief Validate, canonize and store value of the ietf-inet-types ipv6-prefix type.
+ * Implementation of the ::lyplg_type_store_clb.
+ */
+static LY_ERR
+lyplg_type_store_ipv6_prefix(const struct ly_ctx *ctx, const struct lysc_type *type, const char *value, size_t value_len,
+ uint32_t options, LY_PREFIX_FORMAT format, void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node,
+ struct lyd_value *storage, struct lys_glob_unres *unres, struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *canonical;
+
+ /* store as a string */
+ ret = lyplg_type_store_string(ctx, type, value, value_len, options, format, prefix_data, hints, ctx_node,
+ storage, unres, err);
+ LY_CHECK_RET(ret);
+
+ /* canonize */
+ ret = canonize_ipv6_prefix(storage->canonical, &canonical, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ if (strcmp(canonical, storage->canonical)) {
+ /* some conversion took place, update the value */
+ lydict_remove(ctx, storage->canonical);
+ lydict_insert_zc(ctx, canonical, &storage->canonical);
+ } else {
+ free(canonical);
+ }
+
+cleanup:
+ if (ret) {
+ type->plugin->free(ctx, storage);
+ }
+ return ret;
+}
+
+/**
+ * @brief Plugin information for ip-prefix type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_ip_prefix[] = {
+ {
+ .module = "ietf-inet-types",
+ .revision = "2013-07-15",
+ .name = "ipv4-prefix",
+
+ .plugin.id = "libyang 2 - ipv4-prefix, version 1",
+ .plugin.store = lyplg_type_store_ipv4_prefix,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_simple,
+ .plugin.print = lyplg_type_print_simple,
+ .plugin.duplicate = lyplg_type_dup_simple,
+ .plugin.free = lyplg_type_free_simple
+ },
+ {
+ .module = "ietf-inet-types",
+ .revision = "2013-07-15",
+ .name = "ipv6-prefix",
+
+ .plugin.id = "libyang 2 - ipv6-prefix, version 1",
+ .plugin.store = lyplg_type_store_ipv6_prefix,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_simple,
+ .plugin.print = lyplg_type_print_simple,
+ .plugin.duplicate = lyplg_type_dup_simple,
+ .plugin.free = lyplg_type_free_simple
+ },
+ {0}
+};