Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ set(type_plugins
src/plugins_types/union.c
src/plugins_types/ipv4_address.c
src/plugins_types/ipv4_address_no_zone.c
src/plugins_types/ipv6_common.c
src/plugins_types/ipv6_address.c
src/plugins_types/ipv6_address_no_zone.c
src/plugins_types/ipv4_address_prefix.c
Expand Down
22 changes: 12 additions & 10 deletions src/plugins_types/ipv6_address.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

#define _GNU_SOURCE /* strndup */

#include "ipv6_common.h"
#include "plugins_internal.h"
#include "plugins_types.h"

Expand Down Expand Up @@ -60,12 +61,13 @@ static void lyplg_type_free_ipv6_address(const struct ly_ctx *ctx, struct lyd_va
* @param[in] ctx libyang context with dictionary.
* @param[in,out] addr Allocated value for the address.
* @param[out] zone Ipv6 address zone in dictionary.
* @param[out] dual stack format.
* @param[out] err Error information on error.
* @return LY_ERR value.
*/
static LY_ERR
ipv6address_str2ip(const char *value, uint32_t value_len, uint32_t options, const struct ly_ctx *ctx,
struct in6_addr *addr, const char **zone, struct ly_err_item **err)
struct in6_addr *addr, const char **zone, uint8_t *dual_stack_format, struct ly_err_item **err)
{
LY_ERR ret = LY_SUCCESS;
const char *addr_no_zone;
Expand Down Expand Up @@ -107,6 +109,9 @@ ipv6address_str2ip(const char *value, uint32_t value_len, uint32_t options, cons
goto cleanup;
}

/* store dual stack IPv6 format or compressed format */
*dual_stack_format = ly_strnchr(value, '.', value_len) != NULL;

/* restore the value */
if ((options & LYPLG_TYPE_STORE_DYNAMIC) && zone_ptr) {
*zone_ptr = '%';
Expand Down Expand Up @@ -174,7 +179,8 @@ lyplg_type_store_ipv6_address(const struct ly_ctx *ctx, const struct lysc_type *
LY_CHECK_GOTO(ret, cleanup);

/* get the network-byte order address */
ret = ipv6address_str2ip(value, value_size, options, ctx, &val->addr, &val->zone, err);
ret = ipv6address_str2ip(value, value_size, options, ctx, &val->addr, &val->zone,
&val->dual_stack_format, err);
LY_CHECK_GOTO(ret, cleanup);

if (format == LY_VALUE_CANON) {
Expand Down Expand Up @@ -258,6 +264,7 @@ lyplg_type_print_ipv6_address(const struct ly_ctx *ctx, const struct lyd_value *
struct lyd_value_ipv6_address *val;
uint32_t zone_len;
char *ret;
LY_ERR rc;

LYD_VALUE_GET(value, val);

Expand Down Expand Up @@ -290,15 +297,10 @@ lyplg_type_print_ipv6_address(const struct ly_ctx *ctx, const struct lyd_value *
if (!value->_canonical) {
/* '%' + zone */
zone_len = val->zone ? strlen(val->zone) + 1 : 0;
ret = malloc(INET6_ADDRSTRLEN + zone_len);
LY_CHECK_RET(!ret, NULL);

/* get the address in string */
if (!inet_ntop(AF_INET6, &val->addr, ret, INET6_ADDRSTRLEN)) {
free(ret);
LOGERR(ctx, LY_ESYS, "Failed to get IPv6 address in string (%s).", strerror(errno));
return NULL;
}
rc = ipv6address_ip2str(&val->addr, val->dual_stack_format,
&ret, INET6_ADDRSTRLEN + zone_len);
LY_CHECK_RET(!ret || rc != LY_SUCCESS, NULL);

/* add zone */
if (zone_len) {
Expand Down
24 changes: 13 additions & 11 deletions src/plugins_types/ipv6_address_no_zone.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

#define _GNU_SOURCE /* strndup */

#include "ipv6_common.h"
#include "plugins_internal.h"
#include "plugins_types.h"

Expand Down Expand Up @@ -56,11 +57,13 @@ static void lyplg_type_free_ipv6_address_no_zone(const struct ly_ctx *ctx, struc
* @param[in] value_len Length of @p value.
* @param[in] options Type store callback options.
* @param[in,out] addr Allocated value for the address.
* @param[out] dual stack format.
* @param[out] err Error information on error.
* @return LY_ERR value.
*/
static LY_ERR
ipv6addressnozone_str2ip(const char *value, uint32_t value_len, uint32_t options, struct in6_addr *addr, struct ly_err_item **err)
ipv6addressnozone_str2ip(const char *value, uint32_t value_len, uint32_t options,
struct in6_addr *addr, uint8_t *dual_stack_format, struct ly_err_item **err)
{
LY_ERR ret = LY_SUCCESS;
const char *addr_str;
Expand All @@ -81,6 +84,9 @@ ipv6addressnozone_str2ip(const char *value, uint32_t value_len, uint32_t options
goto cleanup;
}

/* store dual stack IPv6 format or compressed format */
*dual_stack_format = ly_strnchr(value, '.', value_len) != NULL;

cleanup:
free(addr_dyn);
return ret;
Expand Down Expand Up @@ -143,7 +149,8 @@ lyplg_type_store_ipv6_address_no_zone(const struct ly_ctx *ctx, const struct lys
LY_CHECK_GOTO(ret, cleanup);

/* get the network-byte order address, validates the value */
ret = ipv6addressnozone_str2ip(value, value_size, options, &val->addr, err);
ret = ipv6addressnozone_str2ip(value, value_size, options, &val->addr,
&val->dual_stack_format, err);
LY_CHECK_GOTO(ret, cleanup);

if (format == LY_VALUE_CANON) {
Expand Down Expand Up @@ -211,6 +218,7 @@ lyplg_type_print_ipv6_address_no_zone(const struct ly_ctx *ctx, const struct lyd
{
struct lyd_value_ipv6_address_no_zone *val;
char *ret;
LY_ERR rc;

LYD_VALUE_GET(value, val);

Expand All @@ -225,15 +233,9 @@ lyplg_type_print_ipv6_address_no_zone(const struct ly_ctx *ctx, const struct lyd
/* generate canonical value if not already */
if (!value->_canonical) {
/* '%' + zone */
ret = malloc(INET6_ADDRSTRLEN);
LY_CHECK_RET(!ret, NULL);

/* get the address in string */
if (!inet_ntop(AF_INET6, &val->addr, ret, INET6_ADDRSTRLEN)) {
free(ret);
LOGERR(ctx, LY_ESYS, "Failed to get IPv6 address in string (%s).", strerror(errno));
return NULL;
}
rc = ipv6address_ip2str(&val->addr, val->dual_stack_format,
&ret, INET6_ADDRSTRLEN);
LY_CHECK_RET(!ret || rc != LY_SUCCESS, NULL);

/* store it */
if (lydict_insert_zc(ctx, ret, (const char **)&value->_canonical)) {
Expand Down
23 changes: 13 additions & 10 deletions src/plugins_types/ipv6_address_prefix.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

#define _GNU_SOURCE /* strndup */

#include "ipv6_common.h"
#include "plugins_internal.h"
#include "plugins_types.h"

Expand Down Expand Up @@ -58,11 +59,13 @@ static void lyplg_type_free_ipv6_address_prefix(const struct ly_ctx *ctx, struct
* @param[in] value_len Length of @p value.
* @param[in,out] addr Allocated address value to fill.
* @param[out] prefix Prefix length.
* @param[out] dual stack format.
* @param[out] err Error information on error.
* @return LY_ERR value.
*/
static LY_ERR
ipv6prefix_str2ip(const char *value, uint32_t value_len, struct in6_addr *addr, uint8_t *prefix, struct ly_err_item **err)
ipv6prefix_str2ip(const char *value, uint32_t value_len, struct in6_addr *addr, uint8_t *prefix,
uint8_t *dual_stack_format, struct ly_err_item **err)
{
LY_ERR ret = LY_SUCCESS;
const char *pref_str;
Expand All @@ -82,6 +85,9 @@ ipv6prefix_str2ip(const char *value, uint32_t value_len, struct in6_addr *addr,
goto cleanup;
}

/* store dual stack IPv6 format or compressed format */
*dual_stack_format = ly_strnchr(value, '.', value_len) != NULL;

cleanup:
free(mask_str);
return ret;
Expand Down Expand Up @@ -194,7 +200,8 @@ lyplg_type_store_ipv6_address_prefix(const struct ly_ctx *ctx, const struct lysc
}

/* get the mask in network-byte order */
ret = ipv6prefix_str2ip(value, value_size, &val->addr, &val->prefix, err);
ret = ipv6prefix_str2ip(value, value_size, &val->addr, &val->prefix,
&val->dual_stack_format, err);
LY_CHECK_GOTO(ret, cleanup);

if (!strcmp(type->name, "ipv6-prefix")) {
Expand Down Expand Up @@ -267,6 +274,7 @@ lyplg_type_print_ipv6_address_prefix(const struct ly_ctx *ctx, const struct lyd_
{
struct lyd_value_ipv6_prefix *val;
char *ret;
LY_ERR rc;

LYD_VALUE_GET(value, val);

Expand All @@ -281,14 +289,9 @@ lyplg_type_print_ipv6_address_prefix(const struct ly_ctx *ctx, const struct lyd_
/* generate canonical value if not already */
if (!value->_canonical) {
/* IPv6 mask + '/' + prefix */
ret = malloc(INET6_ADDRSTRLEN + 4);
LY_CHECK_RET(!ret, NULL);

/* convert back to string */
if (!inet_ntop(AF_INET6, &val->addr, ret, INET6_ADDRSTRLEN)) {
free(ret);
return NULL;
}
rc = ipv6address_ip2str(&val->addr, val->dual_stack_format,
&ret, INET6_ADDRSTRLEN + 4);
LY_CHECK_RET(!ret || rc != LY_SUCCESS, NULL);

/* add the prefix */
sprintf(ret + strlen(ret), "/%" PRIu8, val->prefix);
Expand Down
64 changes: 64 additions & 0 deletions src/plugins_types/ipv6_common.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright 2026 6WIND S.A.
*/

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "compat.h"
#include "ipv6_common.h"
#include "ly_common.h"

static int
ipv6_dual_to_compressed(char *ipv6_str)
{
char *ipv4_str = NULL;
uint16_t parts[2];
uint8_t bytes[4];

ipv4_str = strrchr(ipv6_str, ':');
if (ipv4_str == NULL) {
return -1;
}

/* Parse the IPv4 part */
if (sscanf(ipv4_str + 1, "%hhu.%hhu.%hhu.%hhu",
&bytes[0], &bytes[1], &bytes[2], &bytes[3]) != 4) {
return -1;
}

parts[0] = (bytes[0] << 8) | bytes[1];
parts[1] = (bytes[2] << 8) | bytes[3];

sprintf(ipv4_str + 1, "%x:%x", parts[0], parts[1]);

return 0;
}

LY_ERR
ipv6address_ip2str(struct in6_addr *addr, uint8_t dual_stack_format,
char **str_addr, int str_addr_len)
{
*str_addr = malloc(str_addr_len);
if (!*str_addr) {
return LY_EMEM;
}

/* convert back to string */
if (!inet_ntop(AF_INET6, addr, *str_addr, INET6_ADDRSTRLEN)) {
free(*str_addr);
return LY_EINVAL;
}

/* if compress format is expected convert from dual to compress format */
if (!dual_stack_format && strchr(*str_addr, '.')) {
if (ipv6_dual_to_compressed(*str_addr)) {
free(*str_addr);
return LY_EINVAL;
}
}

return LY_SUCCESS;
}
22 changes: 22 additions & 0 deletions src/plugins_types/ipv6_common.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright 2026 6WIND S.A.
*/

#ifdef _WIN32
# include <winsock2.h>
# include <ws2tcpip.h>
#else
# include <arpa/inet.h>
# if defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__)
# include <netinet/in.h>
# include <sys/socket.h>
# endif
#endif

#include "libyang.h"

/**
* @brief convert dual format ipv6 address inside to compressed ipv6 address
*/
LY_ERR ipv6address_ip2str(struct in6_addr *addr, uint8_t dual_stack_format,
char **str_addr, int str_addr_len);
7 changes: 5 additions & 2 deletions src/tree_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -635,14 +635,16 @@ struct lyd_value_ipv4_prefix {
*/
struct lyd_value_ipv6_address_no_zone {
struct in6_addr addr; /**< IPv6 address in binary */
uint8_t dual_stack_format; /**< Dual stack IPv6 format or compressed format */
};

/**
* @brief Special lyd_value structure for ietf-inet-types ipv6-address values.
*/
struct lyd_value_ipv6_address {
struct in6_addr addr; /**< IPv6 address in binary */
const char *zone; /**< Optional address zone */
struct in6_addr addr; /**< IPv6 address in binary */
const char *zone; /**< Optional address zone */
uint8_t dual_stack_format; /**< Dual stack IPv6 format or compressed format */
};

/**
Expand All @@ -651,6 +653,7 @@ struct lyd_value_ipv6_address {
struct lyd_value_ipv6_prefix {
struct in6_addr addr; /**< IPv6 host address in binary */
uint8_t prefix; /**< prefix length (0 - 128) */
uint8_t dual_stack_format; /**< Dual stack IPv6 format or compressed format */
};

/**
Expand Down
6 changes: 6 additions & 0 deletions tests/utests/types/inet_types.c
Original file line number Diff line number Diff line change
Expand Up @@ -115,13 +115,17 @@ test_data_xml(void **state)

/* ipv6-address */
TEST_SUCCESS_XML("a", "l2", "FAAC:21:011:Da85::87:daaF%1", STRING, "faac:21:11:da85::87:daaf%1");
TEST_SUCCESS_XML("a", "l2", "::2121:3737%2", STRING, "::2121:3737%2");
TEST_SUCCESS_XML("a", "l2", "::21.21.37.37%3", STRING, "::21.21.37.37%3");

/* ip-address-no-zone */
TEST_SUCCESS_XML("a", "l3", "127.0.0.1", UNION, "127.0.0.1", STRING, "127.0.0.1");
TEST_SUCCESS_XML("a", "l3", "0:00:000:0000:000:00:0:1", UNION, "::1", STRING, "::1");

/* ipv6-address-no-zone */
TEST_SUCCESS_XML("a", "l4", "A:B:c:D:e:f:1:0", STRING, "a:b:c:d:e:f:1:0");
TEST_SUCCESS_XML("a", "l4", "::2121:3737", STRING, "::2121:3737");
TEST_SUCCESS_XML("a", "l4", "::21.21.37.37", STRING, "::21.21.37.37");

/* ip-prefix */
TEST_SUCCESS_XML("a", "l5", "158.1.58.4/1", UNION, "128.0.0.0/1", STRING, "128.0.0.0/1");
Expand All @@ -137,6 +141,8 @@ test_data_xml(void **state)
TEST_SUCCESS_XML("a", "l7", "::C:D:E:f:a/110", STRING, "::c:d:e:c:0/110");
TEST_SUCCESS_XML("a", "l7", "::C:D:E:f:a/96", STRING, "::c:d:e:0:0/96");
TEST_SUCCESS_XML("a", "l7", "::C:D:E:f:a/55", STRING, "::/55");
TEST_SUCCESS_XML("a", "l7", "::2121:3737/128", STRING, "::2121:3737/128");
TEST_SUCCESS_XML("a", "l7", "::21.21.37.37/127", STRING, "::21.21.37.36/127");
}

static void
Expand Down
Loading