Skip to content

Commit 735100c

Browse files
committed
plugins types BUGFIX print same IPv6 format as stored
The stored IPv6 address is sometimes not the same as the printed one. For instance, if a compressed IPv6 address ::2222:1111 is stored, then a dual stack IPv6 address ::34.34.17.17 will be printed. This is due to the inet_ntop function implementation. The aim of this patch is to store the IPv6 format and print the same format. Store the IPv6 format with the value. Introduce ipv6address_ip2str that converts back the IPv4 part if necessary. Signed-off-by: Jeremie Leska <jeremie.leska@6wind.com>
1 parent b4d24aa commit 735100c

8 files changed

Lines changed: 136 additions & 33 deletions

File tree

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ set(type_plugins
100100
src/plugins_types/union.c
101101
src/plugins_types/ipv4_address.c
102102
src/plugins_types/ipv4_address_no_zone.c
103+
src/plugins_types/ipv6_common.c
103104
src/plugins_types/ipv6_address.c
104105
src/plugins_types/ipv6_address_no_zone.c
105106
src/plugins_types/ipv4_address_prefix.c

src/plugins_types/ipv6_address.c

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
#define _GNU_SOURCE /* strndup */
1616

17+
#include "ipv6_common.h"
1718
#include "plugins_internal.h"
1819
#include "plugins_types.h"
1920

@@ -60,12 +61,13 @@ static void lyplg_type_free_ipv6_address(const struct ly_ctx *ctx, struct lyd_va
6061
* @param[in] ctx libyang context with dictionary.
6162
* @param[in,out] addr Allocated value for the address.
6263
* @param[out] zone Ipv6 address zone in dictionary.
64+
* @param[out] dual stack format.
6365
* @param[out] err Error information on error.
6466
* @return LY_ERR value.
6567
*/
6668
static LY_ERR
6769
ipv6address_str2ip(const char *value, uint32_t value_len, uint32_t options, const struct ly_ctx *ctx,
68-
struct in6_addr *addr, const char **zone, struct ly_err_item **err)
70+
struct in6_addr *addr, const char **zone, uint8_t *dual_stack_format, struct ly_err_item **err)
6971
{
7072
LY_ERR ret = LY_SUCCESS;
7173
const char *addr_no_zone;
@@ -107,6 +109,9 @@ ipv6address_str2ip(const char *value, uint32_t value_len, uint32_t options, cons
107109
goto cleanup;
108110
}
109111

112+
/* store dual stack IPv6 format or compressed format */
113+
*dual_stack_format = ly_strnchr(value, '.', value_len) != NULL;
114+
110115
/* restore the value */
111116
if ((options & LYPLG_TYPE_STORE_DYNAMIC) && zone_ptr) {
112117
*zone_ptr = '%';
@@ -174,7 +179,8 @@ lyplg_type_store_ipv6_address(const struct ly_ctx *ctx, const struct lysc_type *
174179
LY_CHECK_GOTO(ret, cleanup);
175180

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

180186
if (format == LY_VALUE_CANON) {
@@ -258,6 +264,7 @@ lyplg_type_print_ipv6_address(const struct ly_ctx *ctx, const struct lyd_value *
258264
struct lyd_value_ipv6_address *val;
259265
uint32_t zone_len;
260266
char *ret;
267+
LY_ERR rc;
261268

262269
LYD_VALUE_GET(value, val);
263270

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

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

303305
/* add zone */
304306
if (zone_len) {

src/plugins_types/ipv6_address_no_zone.c

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
#define _GNU_SOURCE /* strndup */
1616

17+
#include "ipv6_common.h"
1718
#include "plugins_internal.h"
1819
#include "plugins_types.h"
1920

@@ -56,11 +57,13 @@ static void lyplg_type_free_ipv6_address_no_zone(const struct ly_ctx *ctx, struc
5657
* @param[in] value_len Length of @p value.
5758
* @param[in] options Type store callback options.
5859
* @param[in,out] addr Allocated value for the address.
60+
* @param[out] dual stack format.
5961
* @param[out] err Error information on error.
6062
* @return LY_ERR value.
6163
*/
6264
static LY_ERR
63-
ipv6addressnozone_str2ip(const char *value, uint32_t value_len, uint32_t options, struct in6_addr *addr, struct ly_err_item **err)
65+
ipv6addressnozone_str2ip(const char *value, uint32_t value_len, uint32_t options,
66+
struct in6_addr *addr, uint8_t *dual_stack_format, struct ly_err_item **err)
6467
{
6568
LY_ERR ret = LY_SUCCESS;
6669
const char *addr_str;
@@ -81,6 +84,9 @@ ipv6addressnozone_str2ip(const char *value, uint32_t value_len, uint32_t options
8184
goto cleanup;
8285
}
8386

87+
/* store dual stack IPv6 format or compressed format */
88+
*dual_stack_format = ly_strnchr(value, '.', value_len) != NULL;
89+
8490
cleanup:
8591
free(addr_dyn);
8692
return ret;
@@ -143,7 +149,8 @@ lyplg_type_store_ipv6_address_no_zone(const struct ly_ctx *ctx, const struct lys
143149
LY_CHECK_GOTO(ret, cleanup);
144150

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

149156
if (format == LY_VALUE_CANON) {
@@ -211,6 +218,7 @@ lyplg_type_print_ipv6_address_no_zone(const struct ly_ctx *ctx, const struct lyd
211218
{
212219
struct lyd_value_ipv6_address_no_zone *val;
213220
char *ret;
221+
LY_ERR rc;
214222

215223
LYD_VALUE_GET(value, val);
216224

@@ -225,15 +233,9 @@ lyplg_type_print_ipv6_address_no_zone(const struct ly_ctx *ctx, const struct lyd
225233
/* generate canonical value if not already */
226234
if (!value->_canonical) {
227235
/* '%' + zone */
228-
ret = malloc(INET6_ADDRSTRLEN);
229-
LY_CHECK_RET(!ret, NULL);
230-
231-
/* get the address in string */
232-
if (!inet_ntop(AF_INET6, &val->addr, ret, INET6_ADDRSTRLEN)) {
233-
free(ret);
234-
LOGERR(ctx, LY_ESYS, "Failed to get IPv6 address in string (%s).", strerror(errno));
235-
return NULL;
236-
}
236+
rc = ipv6address_ip2str(&val->addr, val->dual_stack_format,
237+
&ret, INET6_ADDRSTRLEN);
238+
LY_CHECK_RET(!ret || rc != LY_SUCCESS, NULL);
237239

238240
/* store it */
239241
if (lydict_insert_zc(ctx, ret, (const char **)&value->_canonical)) {

src/plugins_types/ipv6_address_prefix.c

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
#define _GNU_SOURCE /* strndup */
1616

17+
#include "ipv6_common.h"
1718
#include "plugins_internal.h"
1819
#include "plugins_types.h"
1920

@@ -58,11 +59,13 @@ static void lyplg_type_free_ipv6_address_prefix(const struct ly_ctx *ctx, struct
5859
* @param[in] value_len Length of @p value.
5960
* @param[in,out] addr Allocated address value to fill.
6061
* @param[out] prefix Prefix length.
62+
* @param[out] dual stack format.
6163
* @param[out] err Error information on error.
6264
* @return LY_ERR value.
6365
*/
6466
static LY_ERR
65-
ipv6prefix_str2ip(const char *value, uint32_t value_len, struct in6_addr *addr, uint8_t *prefix, struct ly_err_item **err)
67+
ipv6prefix_str2ip(const char *value, uint32_t value_len, struct in6_addr *addr, uint8_t *prefix,
68+
uint8_t *dual_stack_format, struct ly_err_item **err)
6669
{
6770
LY_ERR ret = LY_SUCCESS;
6871
const char *pref_str;
@@ -82,6 +85,9 @@ ipv6prefix_str2ip(const char *value, uint32_t value_len, struct in6_addr *addr,
8285
goto cleanup;
8386
}
8487

88+
/* store dual stack IPv6 format or compressed format */
89+
*dual_stack_format = ly_strnchr(value, '.', value_len) != NULL;
90+
8591
cleanup:
8692
free(mask_str);
8793
return ret;
@@ -194,7 +200,8 @@ lyplg_type_store_ipv6_address_prefix(const struct ly_ctx *ctx, const struct lysc
194200
}
195201

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

200207
if (!strcmp(type->name, "ipv6-prefix")) {
@@ -267,6 +274,7 @@ lyplg_type_print_ipv6_address_prefix(const struct ly_ctx *ctx, const struct lyd_
267274
{
268275
struct lyd_value_ipv6_prefix *val;
269276
char *ret;
277+
LY_ERR rc;
270278

271279
LYD_VALUE_GET(value, val);
272280

@@ -281,14 +289,9 @@ lyplg_type_print_ipv6_address_prefix(const struct ly_ctx *ctx, const struct lyd_
281289
/* generate canonical value if not already */
282290
if (!value->_canonical) {
283291
/* IPv6 mask + '/' + prefix */
284-
ret = malloc(INET6_ADDRSTRLEN + 4);
285-
LY_CHECK_RET(!ret, NULL);
286-
287-
/* convert back to string */
288-
if (!inet_ntop(AF_INET6, &val->addr, ret, INET6_ADDRSTRLEN)) {
289-
free(ret);
290-
return NULL;
291-
}
292+
rc = ipv6address_ip2str(&val->addr, val->dual_stack_format,
293+
&ret, INET6_ADDRSTRLEN + 4);
294+
LY_CHECK_RET(!ret || rc != LY_SUCCESS, NULL);
292295

293296
/* add the prefix */
294297
sprintf(ret + strlen(ret), "/%" PRIu8, val->prefix);

src/plugins_types/ipv6_common.c

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright 2026 6WIND S.A.
3+
*/
4+
5+
#include <stdint.h>
6+
#include <stdio.h>
7+
#include <string.h>
8+
#include <stdlib.h>
9+
10+
#include "ipv6_common.h"
11+
#include "compat.h"
12+
#include "ly_common.h"
13+
14+
static int
15+
ipv6_dual_to_compressed(char *ipv6_str)
16+
{
17+
char *ipv4_str = NULL;
18+
uint16_t parts[2];
19+
uint8_t bytes[4];
20+
21+
ipv4_str = strrchr(ipv6_str, ':');
22+
if (ipv4_str == NULL) {
23+
return -1;
24+
}
25+
26+
/* Parse the IPv4 part */
27+
if (sscanf(ipv4_str + 1, "%hhu.%hhu.%hhu.%hhu",
28+
&bytes[0], &bytes[1], &bytes[2], &bytes[3]) != 4) {
29+
return -1;
30+
}
31+
32+
parts[0] = (bytes[0] << 8) | bytes[1];
33+
parts[1] = (bytes[2] << 8) | bytes[3];
34+
35+
sprintf(ipv4_str + 1, "%x:%x", parts[0], parts[1]);
36+
37+
return 0;
38+
}
39+
40+
LY_ERR
41+
ipv6address_ip2str(struct in6_addr *addr, uint8_t dual_stack_format,
42+
char **str_addr, int str_addr_len)
43+
{
44+
*str_addr = malloc(str_addr_len);
45+
if (!*str_addr) {
46+
return LY_EMEM;
47+
}
48+
49+
/* convert back to string */
50+
if (!inet_ntop(AF_INET6, addr, *str_addr, INET6_ADDRSTRLEN)) {
51+
free(*str_addr);
52+
return LY_EINVAL;
53+
}
54+
55+
/* if compress format is expected convert from dual to compress format */
56+
if (!dual_stack_format && strchr(*str_addr, '.')) {
57+
if (ipv6_dual_to_compressed(*str_addr)) {
58+
free(*str_addr);
59+
return LY_EINVAL;
60+
}
61+
}
62+
63+
return LY_SUCCESS;
64+
}

src/plugins_types/ipv6_common.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
* Copyright 2026 6WIND S.A.
3+
*/
4+
5+
#ifdef _WIN32
6+
# include <winsock2.h>
7+
# include <ws2tcpip.h>
8+
#else
9+
# include <arpa/inet.h>
10+
# if defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__)
11+
# include <netinet/in.h>
12+
# include <sys/socket.h>
13+
# endif
14+
#endif
15+
16+
#include "libyang.h"
17+
18+
/**
19+
* @brief convert dual format ipv6 address inside to compressed ipv6 address
20+
*/
21+
LY_ERR ipv6address_ip2str(struct in6_addr *addr, uint8_t dual_stack_format,
22+
char **str_addr, int str_addr_len);

src/tree_data.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -635,14 +635,16 @@ struct lyd_value_ipv4_prefix {
635635
*/
636636
struct lyd_value_ipv6_address_no_zone {
637637
struct in6_addr addr; /**< IPv6 address in binary */
638+
uint8_t dual_stack_format; /**< Dual stack IPv6 format or compressed format */
638639
};
639640

640641
/**
641642
* @brief Special lyd_value structure for ietf-inet-types ipv6-address values.
642643
*/
643644
struct lyd_value_ipv6_address {
644-
struct in6_addr addr; /**< IPv6 address in binary */
645-
const char *zone; /**< Optional address zone */
645+
struct in6_addr addr; /**< IPv6 address in binary */
646+
const char *zone; /**< Optional address zone */
647+
uint8_t dual_stack_format; /**< Dual stack IPv6 format or compressed format */
646648
};
647649

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

656659
/**

tests/utests/types/inet_types.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,13 +115,17 @@ test_data_xml(void **state)
115115

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

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

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

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

142148
static void

0 commit comments

Comments
 (0)