From: Alan T. DeKok Date: Mon, 6 Jun 2022 19:39:17 +0000 (-0400) Subject: add ipv6 prefix calculations X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e16e37934f52b210ec8004d5baf9eee96317d2dd;p=thirdparty%2Ffreeradius-server.git add ipv6 prefix calculations --- diff --git a/src/lib/util/calc.c b/src/lib/util/calc.c index 7a1449828b1..bbe464874c6 100644 --- a/src/lib/util/calc.c +++ b/src/lib/util/calc.c @@ -1287,10 +1287,15 @@ static int calc_ipv6_addr(UNUSED TALLOC_CTX *ctx, fr_value_box_t *dst, fr_value_ if (b->type != FR_TYPE_UINT64) return ERR_INVALID; + /* + * If we're adding a UINT64, the prefix can't be shorter than 64. + */ + if (a->vb_ip.prefix <= 64) return ERR_OVERFLOW; + /* * Trying to add a number outside of the given prefix. That's not allowed. */ - if (b->vb_uint64 >= (((uint64_t) 1) << a->vb_ip.prefix)) return ERR_OVERFLOW; + if (b->vb_uint64 >= (((uint64_t) 1) << (128 - a->vb_ip.prefix))) return ERR_OVERFLOW; /* * Add in the relevant low bits. @@ -1313,6 +1318,87 @@ static int calc_ipv6_addr(UNUSED TALLOC_CTX *ctx, fr_value_box_t *dst, fr_value_ return 0; } +static int get_ipv6_prefix(uint8_t const *in) +{ + int i, j, prefix; + + prefix = 128; + for (i = 15; i >= 0; i--) { + if (!in[i]) { + prefix -= 8; + continue; + } + + for (j = 0; j < 8; j++) { + if ((in[i] & (1 << j)) == 0) { + prefix--; + continue; + } + return prefix; + } + } + + return prefix; +} + +static int calc_ipv6_prefix(UNUSED TALLOC_CTX *ctx, fr_value_box_t *dst, fr_value_box_t const *a, fr_token_t op, fr_value_box_t const *b) +{ + int i, prefix = 128; + uint8_t const *pa, *pb; + uint8_t *pdst; + + fr_assert(dst->type == FR_TYPE_IPV6_PREFIX); + + if (a->type == FR_TYPE_OCTETS) { + if (a->vb_length != (128 / 8)) { + fr_strerror_printf("Invalid length %zu for octets network mask", a->vb_length); + return -1; + } + pa = a->vb_octets; + prefix = get_ipv6_prefix(pa); + + } else if (a->type == FR_TYPE_IPV6_ADDR) { + pa = (const uint8_t *) &a->vb_ip.addr.v6.s6_addr; + + } else { + return ERR_INVALID; + } + + if (b->type == FR_TYPE_OCTETS) { + if (b->vb_length != (128 / 8)) { + fr_strerror_printf("Invalid length %zu for octets network mask", b->vb_length); + return -1; + } + pb = b->vb_octets; + prefix = get_ipv6_prefix(pb); + + } else if (a->type == FR_TYPE_IPV6_ADDR) { + pb = (const uint8_t *) &b->vb_ip.addr.v6; + + } else { + return ERR_INVALID; + } + + switch (op) { + case T_AND: + fr_value_box_init(dst, FR_TYPE_IPV6_PREFIX, NULL, a->tainted | b->tainted); + pdst = (uint8_t *) &dst->vb_ip.addr.v6; + + for (i = 0; i < 16; i++) { + pdst[i] = pa[i] & pb[i]; + } + + dst->vb_ip.af = AF_INET6; + dst->vb_ip.prefix = prefix; + break; + + default: + return ERR_INVALID; + } + + return 0; +} + static int calc_float32(UNUSED TALLOC_CTX *ctx, fr_value_box_t *dst, fr_value_box_t const *in1, fr_token_t op, fr_value_box_t const *in2) { fr_value_box_t one, two; @@ -1575,6 +1661,7 @@ static const fr_binary_op_t calc_type[FR_TYPE_MAX + 1] = { [FR_TYPE_IPV4_ADDR] = calc_ipv4_addr, [FR_TYPE_IPV4_PREFIX] = calc_ipv4_prefix, [FR_TYPE_IPV6_ADDR] = calc_ipv6_addr, + [FR_TYPE_IPV6_PREFIX] = calc_ipv6_prefix, [FR_TYPE_UINT8] = calc_integer, [FR_TYPE_UINT16] = calc_integer, @@ -1650,12 +1737,26 @@ int fr_value_calc_binary_op(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t hint hint = FR_TYPE_BOOL; break; + case T_AND: + /* + * Get mask from IP + number + */ + if ((a->type == FR_TYPE_IPV4_ADDR) || (b->type == FR_TYPE_IPV4_ADDR)) { + hint = FR_TYPE_IPV4_PREFIX; + break; + } + + if ((a->type == FR_TYPE_IPV6_ADDR) || (b->type == FR_TYPE_IPV6_ADDR)) { + hint = FR_TYPE_IPV6_PREFIX; + break; + } + FALL_THROUGH; + + case T_OR: case T_ADD: case T_SUB: case T_MUL: case T_DIV: - case T_AND: - case T_OR: case T_XOR: /* * Nothing else set it. If the input types are diff --git a/src/tests/unit/calc.txt b/src/tests/unit/calc.txt index 9b57b8e631b..1036c94a7ee 100644 --- a/src/tests/unit/calc.txt +++ b/src/tests/unit/calc.txt @@ -131,6 +131,15 @@ match 192.168.0.0/16 calc ipv4addr 192.168.2.1 & uint32 0xffff0080 -> ipv4prefix match Invalid network mask '0xffff0080' +calc ipv6addr 2001:DB8::1 & octets 0xffff0000000000000000000000000000 +match 2001::/16 + +calc ipv6addr 2001:DB8::1 & octets 0xffffff00000000000000000000000000 +match 2001:d00::/24 + +calc ipv6addr 2001:DB8::1 & octets 0xffffffff000000000000000000000000 +match 2001:db8::/32 + ###################################################################### # # Float @@ -155,4 +164,4 @@ calc string "2" += string "test" match 2test count -match 64 +match 70