Add code to convert between IPv4/v6 addresses and IPv4/v6 prefixes
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Mon, 15 Dec 2014 01:02:33 +0000 (20:02 -0500)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Mon, 15 Dec 2014 15:15:46 +0000 (10:15 -0500)
src/lib/value.c
src/tests/keywords/cast-ipaddr

index 3922d67..e90a7d6 100644 (file)
@@ -1149,6 +1149,182 @@ ssize_t value_data_cast(TALLOC_CTX *ctx, value_data_t *dst,
        }
 
        /*
+        *      Conversions between IPv4 addresses, IPv6 addresses, IPv4 prefixes and IPv6 prefixes
+        *
+        *      For prefix to ipaddress conversions, we assume that the host portion has already
+        *      been zeroed out.
+        *
+        *      We allow casts from v6 to v4 if the v6 address has the correct mapping prefix.
+        *
+        *      We only allow casts from prefixes to addresses if the prefix is the the length of
+        *      the address, e.g. 32 for ipv4 128 for ipv6.
+        */
+       {
+               /*
+                *      10 bytes of 0x00 2 bytes of 0xff
+                */
+               static uint8_t const v4_v6_map[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                                                    0x00, 0x00, 0x00, 0x00, 0xff, 0xff };
+
+               switch (dst_type) {
+               case PW_TYPE_IPV4_ADDR:
+                       switch (src_type) {
+                       case PW_TYPE_IPV6_ADDR:
+                               if (memcmp(src->ipv6addr.s6_addr, v4_v6_map, sizeof(v4_v6_map)) != 0) {
+                               bad_v6_prefix_map:
+                                       fr_strerror_printf("Invalid cast from %s to %s.  No IPv4-IPv6 mapping prefix",
+                                                          fr_int2str(dict_attr_types, src_type, "<INVALID>"),
+                                                          fr_int2str(dict_attr_types, dst_type, "<INVALID>"));
+                                       return -1;
+                               }
+
+                               memcpy(&dst->ipaddr, &src->ipv6addr.s6_addr[sizeof(v4_v6_map)],
+                                      sizeof(dst->ipaddr));
+                               goto fixed_length;
+
+                       case PW_TYPE_IPV4_PREFIX:
+                               if (src->ipv4prefix[1] != 32) {
+                               bad_v4_prefix_len:
+                                       fr_strerror_printf("Invalid cast from %s to %s.  Only /32 prefixes may be "
+                                                          "cast to IP address types",
+                                                          fr_int2str(dict_attr_types, src_type, "<INVALID>"),
+                                                          fr_int2str(dict_attr_types, dst_type, "<INVALID>"));
+                                       return -1;
+                               }
+
+                               memcpy(&dst->ipaddr, &src->ipv4prefix[2], sizeof(dst->ipaddr));
+                               goto fixed_length;
+
+                       case PW_TYPE_IPV6_PREFIX:
+                               if (src->ipv6prefix[1] != 128) {
+                               bad_v6_prefix_len:
+                                       fr_strerror_printf("Invalid cast from %s to %s.  Only /128 prefixes may be "
+                                                          "cast to IP address types",
+                                                          fr_int2str(dict_attr_types, src_type, "<INVALID>"),
+                                                          fr_int2str(dict_attr_types, dst_type, "<INVALID>"));
+                                       return -1;
+                               }
+                               if (memcmp(&src->ipv6prefix[2], v4_v6_map, sizeof(v4_v6_map)) != 0) {
+                                       goto bad_v6_prefix_map;
+                               }
+                               memcpy(&dst->ipaddr, &src->ipv6prefix[2 + sizeof(v4_v6_map)],
+                                      sizeof(dst->ipaddr));
+                               goto fixed_length;
+
+                       default:
+                               break;
+                       }
+                       break;
+
+               case PW_TYPE_IPV6_ADDR:
+                       switch (src_type) {
+                       case PW_TYPE_IPV4_ADDR:
+                               /* Add the v4/v6 mapping prefix */
+                               memcpy(dst->ipv6addr.s6_addr, v4_v6_map, sizeof(v4_v6_map));
+                               memcpy(&dst->ipv6addr.s6_addr[sizeof(v4_v6_map)], &src->ipaddr,
+                                      sizeof(dst->ipv6addr.s6_addr) - sizeof(v4_v6_map));
+
+                               goto fixed_length;
+
+                       case PW_TYPE_IPV4_PREFIX:
+                               if (src->ipv4prefix[1] != 32) goto bad_v4_prefix_len;
+
+                               /* Add the v4/v6 mapping prefix */
+                               memcpy(dst->ipv6addr.s6_addr, v4_v6_map, sizeof(v4_v6_map));
+                               memcpy(&dst->ipv6addr.s6_addr[sizeof(v4_v6_map)], &src->ipv4prefix[2],
+                                      sizeof(dst->ipv6addr.s6_addr) - sizeof(v4_v6_map));
+                               goto fixed_length;
+
+                       case PW_TYPE_IPV6_PREFIX:
+                               if (src->ipv4prefix[1] != 128) goto bad_v6_prefix_len;
+
+                               memcpy(dst->ipv6addr.s6_addr, &src->ipv6prefix[2], sizeof(dst->ipv6addr.s6_addr));
+                               goto fixed_length;
+
+                       default:
+                               break;
+                       }
+                       break;
+
+               case PW_TYPE_IPV4_PREFIX:
+                       switch (src_type) {
+                       case PW_TYPE_IPV4_ADDR:
+                               memcpy(&dst->ipv4prefix[2], &src->ipaddr, sizeof(dst->ipv4prefix) - 2);
+                               dst->ipv4prefix[0] = 0;
+                               dst->ipv4prefix[1] = 32;
+                               goto fixed_length;
+
+                       case PW_TYPE_IPV6_ADDR:
+                               if (memcmp(src->ipv6addr.s6_addr, v4_v6_map, sizeof(v4_v6_map)) != 0) {
+                                       goto bad_v6_prefix_map;
+                               }
+                               memcpy(&dst->ipv4prefix[2], &src->ipv6addr.s6_addr[sizeof(v4_v6_map)],
+                                      sizeof(dst->ipv4prefix) - 2);
+                               dst->ipv4prefix[0] = 0;
+                               dst->ipv4prefix[1] = 32;
+                               goto fixed_length;
+
+                       case PW_TYPE_IPV6_PREFIX:
+                               if (memcmp(&src->ipv6prefix[2], v4_v6_map, sizeof(v4_v6_map)) != 0) {
+                                       goto bad_v6_prefix_map;
+                               }
+
+                               /*
+                                *      Prefix must be >= 96 bits. If it's < 96 bytes and the
+                                *      above check passed, the v6 address wasn't masked
+                                *      correctly when it was packet into a value_data_t.
+                                */
+                               if (!fr_assert(src->ipv6prefix[1] >= (sizeof(v4_v6_map) * 8))) return -1;
+
+                               memcpy(&dst->ipv4prefix[2], &src->ipv6prefix[2 + sizeof(v4_v6_map)],
+                                      sizeof(dst->ipv4prefix) - 2);
+                               dst->ipv4prefix[0] = 0;
+                               dst->ipv4prefix[1] = src->ipv6prefix[1] - (sizeof(v4_v6_map) * 8);
+                               goto fixed_length;
+
+                       default:
+                               break;
+                       }
+                       break;
+
+               case PW_TYPE_IPV6_PREFIX:
+                       switch (src_type) {
+                       case PW_TYPE_IPV4_ADDR:
+                               /* Add the v4/v6 mapping prefix */
+                               memcpy(&dst->ipv6prefix[2], v4_v6_map, sizeof(v4_v6_map));
+                               memcpy(&dst->ipv6prefix[2 + sizeof(v4_v6_map)], &src->ipaddr,
+                                      (sizeof(dst->ipv6prefix) - 2) - sizeof(v4_v6_map));
+                               dst->ipv6prefix[0] = 0;
+                               dst->ipv6prefix[1] = 128;
+                               goto fixed_length;
+
+                       case PW_TYPE_IPV4_PREFIX:
+                               /* Add the v4/v6 mapping prefix */
+                               memcpy(&dst->ipv6prefix[2], v4_v6_map, sizeof(v4_v6_map));
+                               memcpy(&dst->ipv6prefix[2 + sizeof(v4_v6_map)], &src->ipv4prefix[2],
+                                      (sizeof(dst->ipv6prefix) - 2) - sizeof(v4_v6_map));
+                               dst->ipv6prefix[0] = 0;
+                               dst->ipv6prefix[1] = (sizeof(v4_v6_map) * 8) + src->ipv4prefix[1];
+                               goto fixed_length;
+
+                       case PW_TYPE_IPV6_ADDR:
+                               memcpy(&dst->ipv6prefix[2], &src->ipv6addr, sizeof(dst->ipv6prefix) - 2);
+                               dst->ipv6prefix[0] = 0;
+                               dst->ipv6prefix[1] = 128;
+                               goto fixed_length;
+
+                       default:
+                               break;
+                       }
+
+                       break;
+
+               default:
+                       break;
+               }
+       }
+
+       /*
         *      The attribute we've found has to have a size which is
         *      compatible with the type of the destination cast.
         */
index 4d5ff9f..2cf7a45 100644 (file)
@@ -5,14 +5,416 @@ update {
        control:Cleartext-Password := 'hello'
        request:NAS-IP-Address := 127.0.0.1
        request:Tmp-Integer-0 := 2130706433
+       reply:Filter-Id := "filter"
 }
 
 update request {
        Tmp-String-0 := "%{request:NAS-IP-Address}"
 }
 
-if (<ipaddr>Tmp-Integer-0 == NAS-IP-Address) {
+if (<ipaddr>Tmp-Integer-0 != NAS-IP-Address) {
        update reply {
-               Filter-Id := "filter"
+               Filter-Id += 'Fail 0'
+       }
+}
+
+#
+#  Update statements do implicit casts, so we can check
+#  cast results are correct, by using the update to perform
+#  the cast, and looking at the results.
+#
+update request {
+       Tmp-Cast-Ipaddr  := 203.0.113.1
+       Tmp-Cast-IPv4Prefix := 203.0.113.0/24
+       Tmp-Cast-IPv4Prefix += 203.0.113.1/32
+       Tmp-Cast-IPv6Addr := 2001:DB8::1
+       Tmp-Cast-IPv6Addr += ::ffff:203.0.113.1
+       Tmp-Cast-IPv6Prefix := 2001:DB8::/32
+       Tmp-Cast-IPv6Prefix += ::ffff:203.0.113.1/128
+       Tmp-Cast-IPv6Prefix += ::ffff:203.0.113.1/64
+}
+
+#
+#  IPv4 address to IPv6 address
+#
+update control {
+       Tmp-Cast-IPv6addr := &Tmp-Cast-IPaddr
+}
+
+if (&control:Tmp-Cast-IPv6addr != ::ffff:203.0.113.1) {
+       update reply {
+               Filter-Id += 'Fail 1'
+       }
+}
+
+#
+#  IPv6 address to IPv4 address
+#
+update control {
+       Tmp-Cast-IPaddr := &control:Tmp-Cast-IPv6addr
+}
+
+if (&control:Tmp-Cast-IPaddr != 203.0.113.1) {
+       update reply {
+               Filter-Id += 'Fail 2'
+       }
+}
+
+#
+#  IPv4 prefix to IPv6 prefix
+#
+update control {
+       Tmp-Cast-IPv6Prefix := &Tmp-Cast-IPv4Prefix
+}
+
+if (&control:Tmp-Cast-IPv6Prefix != ::ffff:203.0.113.0/120) {
+       update reply {
+               Filter-Id += 'Fail 31'
+       }
+}
+
+#
+#  IPv6 prefix to IPv4 prefix
+#
+update control {
+       Tmp-Cast-IPv4Prefix := &control:Tmp-Cast-IPv6Prefix
+}
+
+if (&control:Tmp-Cast-IPv4Prefix != 203.0.113.1/24) {
+       update reply {
+               Filter-Id += 'Fail 4'
+       }
+}
+
+#
+#  IPv4 prefix (32) to IPv6 address
+#
+update control {
+       Tmp-Cast-IPv6Addr := &Tmp-Cast-IPv4Prefix[1]
+}
+
+if (&control:Tmp-Cast-IPv6Addr != ::ffff:203.0.113.1) {
+       update reply {
+               Filter-Id += 'Fail 5'
+       }
+}
+
+#
+#  IPv6 prefix (128) to IPv4 address
+#
+update control {
+       Tmp-Cast-Ipaddr := &Tmp-Cast-IPv6Prefix[1]
+}
+
+if (&control:Tmp-Cast-Ipaddr != 203.0.113.1/32) {
+       update reply {
+               Filter-Id += 'Fail 6'
+       }
+}
+
+#
+#  IPv4 address to IPv6 prefix (128)
+#
+update control {
+       Tmp-Cast-IPv6Prefix := &Tmp-Cast-Ipaddr
+}
+
+if (&control:Tmp-Cast-IPv6Prefix != ::ffff:203.0.113.1/128) {
+       update reply {
+               Filter-Id += 'Fail 7'
+       }
+}
+
+#
+#  IPv6 address to IPv4 prefix (32)
+#
+update control {
+       Tmp-Cast-IPv4Prefix := &Tmp-Cast-IPv6Addr[1]
+}
+
+if (&control:Tmp-Cast-IPv4Prefix != 203.0.113.1/32) {
+       update reply {
+               Filter-Id += 'Fail 8'
+       }
+}
+
+#
+#  IPv4 address to IPv4 prefix (32)
+#
+update control {
+       Tmp-Cast-IPv4Prefix := &Tmp-Cast-Ipaddr
+}
+
+if (&control:Tmp-Cast-IPv4Prefix != 203.0.113.1/32) {
+       update reply {
+               Filter-Id += 'Fail 9'
+       }
+}
+
+#
+#  IPv6 address to IPv6 prefix (128)
+#
+update control {
+       Tmp-Cast-IPv6Prefix := Tmp-Cast-Ipv6addr
+}
+
+if (&control:Tmp-Cast-IPv6Prefix != 2001:DB8::1/128) {
+       update reply {
+               Filter-Id += 'Fail 11'
+       }
+}
+
+#
+#  IPv4 prefix (32) to IPv4 address
+#
+update control {
+       Tmp-Cast-Ipaddr := &Tmp-Cast-IPv4Prefix[1]
+}
+
+if (&control:Tmp-Cast-Ipaddr != 203.0.113.1) {
+       update reply {
+               Filter-Id += 'Fail 12'
+       }
+}
+
+#
+#  IPv6 prefix (128) to IPv6 address
+#
+update control {
+       Tmp-Cast-IPv6Addr := &Tmp-Cast-IPv6Prefix[1]
+}
+
+if (&control:Tmp-Cast-IPv6Addr != ::ffff:203.0.113.1) {
+       update reply {
+               Filter-Id += 'Fail 13'
+       }
+}
+
+#
+#  And the invalid cases...
+#
+
+#
+#  IPv6 Prefix < 128 to IPv6 address
+#
+redundant {
+       group {
+               update control {
+                       Tmp-Cast-IPv6Addr := Tmp-Cast-IPv6Prefix
+               }
+               update reply {
+                       Filter-Id += 'Fail 14'
+               }
+       }
+       group {
+               if ("%{Module-Failure-Message}" != 'Attribute conversion failed: Invalid cast from ipv6prefix to ipv6addr.  Only /128 prefixes may be cast to IP address types') {
+                       update reply {
+                               Filter-Id += 'Fail 14.5'
+                       }
+               }
+               update request {
+                       Module-Failure-Message !* ANY
+               }
+               ok
+       }
+}
+
+#
+#  IPv6 Prefix < 128 to IPv4 address
+#
+redundant {
+       group {
+               update control {
+                       Tmp-Cast-Ipaddr := &Tmp-Cast-IPv6Prefix[2]
+               }
+               update reply {
+                       Filter-Id += 'Fail 15'
+               }
+       }
+       group {
+               if ("%{Module-Failure-Message}" != 'Attribute conversion failed: Invalid cast from ipv6prefix to ipaddr.  Only /128 prefixes may be cast to IP address types') {
+                       update reply {
+                               Filter-Id += 'Fail 15.5'
+                       }
+               }
+               update request {
+                       Module-Failure-Message !* ANY
+               }
+               ok
+       }
+}
+
+#
+#  IPv6 Prefix < 96 to IPv4 prefix (causes part of the IPv4/v6 mapping prefix to be masked off)
+#
+redundant {
+       group {
+               update control {
+                       Tmp-Cast-Ipv4Prefix := &Tmp-Cast-IPv6Prefix[2]
+               }
+               update reply {
+                       Filter-Id += 'Fail 16'
+               }
+       }
+       group {
+               if ("%{Module-Failure-Message}" != 'Attribute conversion failed: Invalid cast from ipv6prefix to ipv4prefix.  No IPv4-IPv6 mapping prefix') {
+                       update reply {
+                               Filter-Id += 'Fail 16.5'
+                       }
+               }
+               update request {
+                       Module-Failure-Message !* ANY
+               }
+               ok
+       }
+}
+
+#
+#  IPv4 Prefix < 32 to IPv6 address
+#
+redundant {
+       group {
+               update control {
+                       Tmp-Cast-IPv6Addr := &Tmp-Cast-IPv4Prefix
+               }
+               update reply {
+                       Filter-Id += 'Fail 17'
+               }
+       }
+       group {
+               if ("%{Module-Failure-Message}" != 'Attribute conversion failed: Invalid cast from ipv4prefix to ipv6addr.  Only /32 prefixes may be cast to IP address types') {
+                       update reply {
+                               Filter-Id += 'Fail 17.5'
+                       }
+               }
+               update request {
+                       Module-Failure-Message !* ANY
+               }
+               ok
+       }
+}
+
+#
+#  IPv4 Prefix < 32 to IPv4 address
+#
+redundant {
+       group {
+               update control {
+                       Tmp-Cast-Ipaddr := &Tmp-Cast-IPv4Prefix
+               }
+               update reply {
+                       Filter-Id += 'Fail 17'
+               }
+       }
+       group {
+               if ("%{Module-Failure-Message}" != 'Attribute conversion failed: Invalid cast from ipv4prefix to ipaddr.  Only /32 prefixes may be cast to IP address types') {
+                       update reply {
+                               Filter-Id += 'Fail 17.5'
+                       }
+               }
+               update request {
+                       Module-Failure-Message !* ANY
+               }
+               ok
+       }
+}
+
+#
+#  IPv6 Prefix outside mapping range to IPv4 address
+#
+redundant {
+       group {
+               update control {
+                       Tmp-Cast-Ipaddr := &Tmp-Cast-IPv6Prefix
+               }
+               update reply {
+                       Filter-Id += 'Fail 18'
+               }
+       }
+       group {
+               if ("%{Module-Failure-Message}" != 'Attribute conversion failed: Invalid cast from ipv6prefix to ipaddr.  Only /128 prefixes may be cast to IP address types') {
+                       update reply {
+                               Filter-Id += 'Fail 18.5'
+                       }
+               }
+               update request {
+                       Module-Failure-Message !* ANY
+               }
+               ok
+       }
+}
+
+#
+#  IPv6 Prefix outside mapping range to IPv4 prefix
+#
+redundant {
+       group {
+               update control {
+                       Tmp-Cast-IPv4Prefix := &Tmp-Cast-IPv6Prefix
+               }
+               update reply {
+                       Filter-Id += 'Fail 19'
+               }
+       }
+       group {
+               if ("%{Module-Failure-Message}" != 'Attribute conversion failed: Invalid cast from ipv6prefix to ipv4prefix.  No IPv4-IPv6 mapping prefix') {
+                       update reply {
+                               Filter-Id += 'Fail 19.5'
+                       }
+               }
+               update request {
+                       Module-Failure-Message !* ANY
+               }
+               ok
+       }
+}
+
+#
+#  IPv6 Address outside mapping range to IPv4 address
+#
+redundant {
+       group {
+               update control {
+                       Tmp-Cast-Ipaddr := &Tmp-Cast-IPv6Addr
+               }
+               update reply {
+                       Filter-Id += 'Fail 20'
+               }
+       }
+       group {
+               if ("%{Module-Failure-Message}" != 'Attribute conversion failed: Invalid cast from ipv6addr to ipaddr.  No IPv4-IPv6 mapping prefix') {
+                       update reply {
+                               Filter-Id += 'Fail 20'
+                       }
+               }
+               update request {
+                       Module-Failure-Message !* ANY
+               }
+               ok
+       }
+}
+
+#
+#  IPv6 Address outside mapping range to IPv4 prefix
+#
+redundant {
+       group {
+               update control {
+                       Tmp-Cast-IPv4Prefix := &Tmp-Cast-IPv6Addr
+               }
+               update reply {
+                       Filter-Id += 'Fail 21'
+               }
+       }
+       group {
+               if ("%{Module-Failure-Message}" != 'Attribute conversion failed: Invalid cast from ipv6addr to ipv4prefix.  No IPv4-IPv6 mapping prefix') {
+                       update reply {
+                               Filter-Id += 'Fail 21.5'
+                       }
+               }
+               update request {
+                       Module-Failure-Message !* ANY
+               }
+               ok
        }
 }