Skip to content

Commit

Permalink
user types BUGFIX proper ip prefix canonization
Browse files Browse the repository at this point in the history
Refs #755
  • Loading branch information
michalvasko committed Apr 17, 2019
1 parent 651bf21 commit 71d0b26
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 39 deletions.
107 changes: 79 additions & 28 deletions src/user_types/user_inet_types.c
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,15 @@ static int
ipv4_prefix_store_clb(struct ly_ctx *ctx, const char *UNUSED(type_name), const char **value_str, lyd_val *value, char **err_msg)
{
char *pref_str, *ptr, *result;
int result_len, i, j, num;
unsigned long int pref;
unsigned long int pref, addr_bin, i;
uint32_t mask;

if (sizeof addr_bin < sizeof(struct in_addr)) {
if (asprintf(err_msg, "Internal error (buffer too small for an ip address).") == -1) {
*err_msg = NULL;
}
return 1;
}

pref_str = strchr(*value_str, '/');
if (!pref_str) {
Expand All @@ -129,8 +136,9 @@ ipv4_prefix_store_clb(struct ly_ctx *ctx, const char *UNUSED(type_name), const c
return 1;
}

/* learn prefix */
pref = strtoul(pref_str + 1, &ptr, 10);
if (ptr[0]) {
if (ptr[0] || (pref > 32)) {
if (asprintf(err_msg, "Invalid IPv4 prefix \"%s\".", *value_str) == -1) {
*err_msg = NULL;
}
Expand All @@ -143,20 +151,41 @@ ipv4_prefix_store_clb(struct ly_ctx *ctx, const char *UNUSED(type_name), const c
return 1;
}

/* generate ip prefix mask */
result_len = 0;
for (i = 0; i < 4; ++i) {
num = 0;
for (j = 0; (j < 8) && pref; ++j) {
num += (1 << j);
--pref;
/* copy just the network prefix */
strncpy(result, *value_str, pref_str - *value_str);
result[pref_str - *value_str] = '\0';

/* convert it to binary form */
if (inet_pton(AF_INET, result, (void *)&addr_bin) != 1) {
if (asprintf(err_msg, "Failed to convert IPv4 address \"%s\".", result) == -1) {
*err_msg = NULL;
}
free(result);
return 1;
}

result_len += sprintf(result + result_len, "%s%d", i ? "." : "", num);
/* 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)) {
if (asprintf(err_msg, "Failed to convert IPv4 address (%s).", strerror(errno)) == -1) {
*err_msg = NULL;
}
free(result);
return 1;
}

/* add the prefix */
result_len += sprintf(result + result_len, "%s", pref_str);
strcat(result, pref_str);

if (strcmp(result, *value_str)) {
/* some conversion took place, update the value */
Expand All @@ -174,8 +203,12 @@ static int
ipv6_prefix_store_clb(struct ly_ctx *ctx, const char *UNUSED(type_name), const char **value_str, lyd_val *value, char **err_msg)
{
char *pref_str, *ptr, *result;
int result_len, i, j, num;
unsigned long int pref;
unsigned long int pref, i, j;
union {
struct in6_addr s;
uint32_t a[4];
} addr_bin;
uint32_t mask;

pref_str = strchr(*value_str, '/');
if (!pref_str) {
Expand All @@ -185,8 +218,9 @@ ipv6_prefix_store_clb(struct ly_ctx *ctx, const char *UNUSED(type_name), const c
return 1;
}

/* learn prefix */
pref = strtoul(pref_str + 1, &ptr, 10);
if (ptr[0]) {
if (ptr[0] || (pref > 128)) {
if (asprintf(err_msg, "Invalid IPv6 prefix \"%s\".", *value_str) == -1) {
*err_msg = NULL;
}
Expand All @@ -199,26 +233,43 @@ ipv6_prefix_store_clb(struct ly_ctx *ctx, const char *UNUSED(type_name), const c
return 1;
}

/* generate ipv6 prefix mask */
result_len = 0;
for (i = 0; i < 8; ++i) {
num = 0;
for (j = 0; (j < 16) && pref; ++j) {
num += (1 << j);
--pref;
/* copy just the network prefix */
strncpy(result, *value_str, pref_str - *value_str);
result[pref_str - *value_str] = '\0';

/* convert it to binary form */
if (inet_pton(AF_INET6, result, (void *)&addr_bin.s) != 1) {
if (asprintf(err_msg, "Failed to convert IPv6 address \"%s\".", result) == -1) {
*err_msg = NULL;
}
free(result);
return 1;
}

result_len += sprintf(result + result_len, "%s%x", i ? ":" : "", num);
/* 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;
}

if (!pref && (i < 6)) {
/* shorten ending zeros */
result_len += sprintf(result + result_len, "::");
break;
/* convert back to string */
if (!inet_ntop(AF_INET6, (void *)&addr_bin.s, result, INET6_ADDRSTRLEN)) {
if (asprintf(err_msg, "Failed to convert IPv6 address (%s).", strerror(errno)) == -1) {
*err_msg = NULL;
}
free(result);
return 1;
}

/* add the prefix */
result_len += sprintf(result + result_len, "%s", pref_str);
strcat(result, pref_str);

if (strcmp(result, *value_str)) {
/* some conversion took place, update the value */
Expand Down
22 changes: 11 additions & 11 deletions tests/data/test_user_types.c
Original file line number Diff line number Diff line change
Expand Up @@ -168,51 +168,51 @@ test_inet_types(void **state)
lyd_free_withsiblings(st->dt);

/* ip-prefix */
st->dt = lyd_new_leaf(NULL, st->mod, "inet5", "12.1.58.4/1");
st->dt = lyd_new_leaf(NULL, st->mod, "inet5", "158.1.58.4/1");
assert_non_null(st->dt);
assert_string_equal(((struct lyd_node_leaf_list *)st->dt)->value_str, "1.0.0.0/1");
assert_string_equal(((struct lyd_node_leaf_list *)st->dt)->value_str, "128.0.0.0/1");
lyd_free_withsiblings(st->dt);

st->dt = lyd_new_leaf(NULL, st->mod, "inet5", "12.1.58.4/24");
st->dt = lyd_new_leaf(NULL, st->mod, "inet5", "158.1.58.4/24");
assert_non_null(st->dt);
assert_string_equal(((struct lyd_node_leaf_list *)st->dt)->value_str, "255.255.255.0/24");
assert_string_equal(((struct lyd_node_leaf_list *)st->dt)->value_str, "158.1.58.0/24");
lyd_free_withsiblings(st->dt);

st->dt = lyd_new_leaf(NULL, st->mod, "inet5", "2000:A:B:C:D:E:f:a/16");
assert_non_null(st->dt);
assert_string_equal(((struct lyd_node_leaf_list *)st->dt)->value_str, "ffff::/16");
assert_string_equal(((struct lyd_node_leaf_list *)st->dt)->value_str, "2000::/16");
lyd_free_withsiblings(st->dt);

/* ipv4-prefix */
st->dt = lyd_new_leaf(NULL, st->mod, "inet6", "0.1.58.4/32");
assert_non_null(st->dt);
assert_string_equal(((struct lyd_node_leaf_list *)st->dt)->value_str, "255.255.255.255/32");
assert_string_equal(((struct lyd_node_leaf_list *)st->dt)->value_str, "0.1.58.4/32");
lyd_free_withsiblings(st->dt);

st->dt = lyd_new_leaf(NULL, st->mod, "inet6", "12.1.58.4/8");
assert_non_null(st->dt);
assert_string_equal(((struct lyd_node_leaf_list *)st->dt)->value_str, "255.0.0.0/8");
assert_string_equal(((struct lyd_node_leaf_list *)st->dt)->value_str, "12.0.0.0/8");
lyd_free_withsiblings(st->dt);

/* ipv6-prefix */
st->dt = lyd_new_leaf(NULL, st->mod, "inet7", "::C:D:E:f:a/112");
assert_non_null(st->dt);
assert_string_equal(((struct lyd_node_leaf_list *)st->dt)->value_str, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:0/112");
assert_string_equal(((struct lyd_node_leaf_list *)st->dt)->value_str, "::c:d:e:f:0/112");
lyd_free_withsiblings(st->dt);

st->dt = lyd_new_leaf(NULL, st->mod, "inet7", "::C:D:E:f:a/110");
assert_non_null(st->dt);
assert_string_equal(((struct lyd_node_leaf_list *)st->dt)->value_str, "ffff:ffff:ffff:ffff:ffff:ffff:3fff:0/110");
assert_string_equal(((struct lyd_node_leaf_list *)st->dt)->value_str, "::c:d:e:c:0/110");
lyd_free_withsiblings(st->dt);

st->dt = lyd_new_leaf(NULL, st->mod, "inet7", "::C:D:E:f:a/96");
assert_non_null(st->dt);
assert_string_equal(((struct lyd_node_leaf_list *)st->dt)->value_str, "ffff:ffff:ffff:ffff:ffff:ffff::/96");
assert_string_equal(((struct lyd_node_leaf_list *)st->dt)->value_str, "::c:d:e:0:0/96");
lyd_free_withsiblings(st->dt);

st->dt = lyd_new_leaf(NULL, st->mod, "inet7", "::C:D:E:f:a/55");
assert_non_null(st->dt);
assert_string_equal(((struct lyd_node_leaf_list *)st->dt)->value_str, "ffff:ffff:ffff:7f::/55");
assert_string_equal(((struct lyd_node_leaf_list *)st->dt)->value_str, "::/55");
}

int main(void)
Expand Down

0 comments on commit 71d0b26

Please sign in to comment.