]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
add support for split prefixes for Policy-Filter
authorAlan T. DeKok <aland@freeradius.org>
Thu, 17 Mar 2022 13:54:25 +0000 (09:54 -0400)
committerAlan T. DeKok <aland@freeradius.org>
Thu, 17 Mar 2022 14:46:55 +0000 (10:46 -0400)
made abstract for now, just in case other options are defined
with the same stupid format.

share/dictionary/dhcpv4/dictionary.rfc2131
src/protocols/dhcpv4/base.c
src/protocols/dhcpv4/decode.c
src/protocols/dhcpv4/dhcpv4.h
src/protocols/dhcpv4/encode.c
src/tests/unit/protocols/dhcpv4/prefix.txt [new file with mode: 0644]

index b0bab52ecc404ca44d4c07d518b0f24700e5d31f..d2ea938cd12e284b6d320caf62156e2749ca215a 100644 (file)
@@ -51,7 +51,7 @@ ATTRIBUTE     Bootp-Extensions-Path                   18      string
 ATTRIBUTE      IP-Forward-Enable                       19      bool
 ATTRIBUTE      Source-Route-Enable                     20      bool
 # Routing Policy Filters
-ATTRIBUTE      Policy-Filter                           21      octets
+ATTRIBUTE      Policy-Filter                           21      ipv4prefix      prefix=split,array
 ATTRIBUTE      Max-Datagram-Reassembly-Size            22      short
 ATTRIBUTE      Default-IP-TTL                          23      octets
 ATTRIBUTE      Path-MTU-Aging-Timeout                  24      integer
index d06b81b37c3fbd2d0d2f7fb497082d8e3554acd7..e9c9437e437f77d349d84e80d5b20a8c39e090f7 100644 (file)
@@ -262,6 +262,10 @@ size_t fr_dhcpv4_option_len(fr_pair_t const *vp)
                fr_assert_fail(NULL);
                return 0;
 
+       case FR_TYPE_IPV4_PREFIX:
+               if (da_is_split_prefix(vp->da)) return 8;
+               FALL_THROUGH;
+
        default:
                return fr_dhcpv4_attr_sizes[vp->vp_type][0];
        }
@@ -741,6 +745,7 @@ void fr_dhcpv4_print_hex(FILE *fp, uint8_t const *packet, size_t packet_len)
 static fr_table_num_ordered_t const subtype_table[] = {
        { L("dns_label"),                       FLAG_ENCODE_DNS_LABEL },
        { L("encode=dns_label"),                FLAG_ENCODE_DNS_LABEL },
+       { L("prefix=split"),                    FLAG_ENCODE_SPLIT_PREFIX },
 };
 
 static bool attr_valid(UNUSED fr_dict_t *dict, UNUSED fr_dict_attr_t const *parent,
@@ -773,6 +778,11 @@ static bool attr_valid(UNUSED fr_dict_t *dict, UNUSED fr_dict_attr_t const *pare
                return false;
        }
 
+       if ((type != FR_TYPE_IPV4_PREFIX) && (flags->subtype == FLAG_ENCODE_SPLIT_PREFIX)) {
+               fr_strerror_const("The 'split' flag can only be used with attributes of type 'ipv4prefix'");
+               return false;
+       }
+
        return true;
 }
 
index 89673ec35cc270c7b11bb9b6bdcb235b801f4715..c350907870d85b8274f00c620d2af5216a2f9c47 100644 (file)
@@ -166,6 +166,51 @@ static ssize_t decode_value(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t
                talloc_free(vp);
                return 0;
 
+       case FR_TYPE_IPV4_PREFIX:
+               fr_value_box_init(&vp->data, FR_TYPE_IPV4_PREFIX, vp->da, true);
+               vp->vp_ip.af = AF_INET;
+
+               /*
+                *      4 octets of address
+                *      4 octets of mask
+                */
+               if (da_is_split_prefix(da)) {
+                       uint32_t ipaddr, mask;
+
+                       if (data_len != 8) goto raw;
+
+                       ipaddr = fr_net_to_uint32(p);
+                       mask = fr_net_to_uint32(p + 4);
+                       p += 8;
+
+                       /*
+                        *      0/0 means a prefix of 0, too.
+                        */
+                       if (!mask) {
+                               break;
+                       }
+
+                       /*
+                        *      Try to figure out the prefix value from the mask.
+                        */
+                       while (mask) {
+                               vp->vp_ip.prefix++;
+                               mask <<= 1;
+                       }
+
+                       /*
+                        *      Mash the IP based on the calculated mask.  We don't really care if the mask
+                        *      has holes, or if the IP address overlaps with the mask.  We just fix it all up
+                        *      so it's sane.
+                        */
+                       mask = ~(uint32_t) 0;
+                       mask <<= (32 - vp->vp_ip.prefix);
+
+                       vp->vp_ipv4addr = htonl(ipaddr & mask);
+                       break;
+               }
+               FALL_THROUGH;
+
        default:
        {
                ssize_t ret;
@@ -376,6 +421,11 @@ static ssize_t decode_array(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t
        element_len = fr_dhcpv4_attr_sizes[parent->type][0];
        if (!element_len) element_len = parent->flags.length;
 
+       /*
+        *      Fix up insane nonsense.
+        */
+       if (da_is_split_prefix(parent)) element_len = 8;
+
        if (element_len > 0) {
                size_t num_elements = (end - p) / element_len;
 
index 5743165027fb46345ad2e42c131dc62e455246d2..bf8445fa42c455e00b639a06cf491c07223291c9 100644 (file)
@@ -71,10 +71,11 @@ typedef enum {
 enum {
        FLAG_ENCODE_NONE = 0,                           //!< no particular encoding for DHCPv6 strings
        FLAG_ENCODE_DNS_LABEL,                          //!< encode as DNS label
+       FLAG_ENCODE_SPLIT_PREFIX,                       //!< encode IPv4 prefixes as Policy-Filter, split into IP/mask
 };
 
 #define da_is_dns_label(_da) (!(_da)->flags.extra && ((_da)->flags.subtype == FLAG_ENCODE_DNS_LABEL))
-
+#define da_is_split_prefix(_da) (!(_da)->flags.extra && ((_da)->flags.subtype == FLAG_ENCODE_SPLIT_PREFIX))
 
 typedef struct {
        uint8_t         opcode;
index 981931cb41d8fe0ab2b168190262bf85ac4d7a09..45bb7bb541ff0eaa15df5410d96d1afa083be7eb 100644 (file)
@@ -121,10 +121,26 @@ static ssize_t encode_value(fr_dbuff_t *dbuff,
                 *      "option does not exist" == false
                 *
                 *      fr_dhcpv4_next_encodable() takes care of skipping bools which are false.
+                *
+                *      Rapid-Commit does this.  Options 19/20 require encoding as one byte of 0/1.
                 */
        case FR_TYPE_BOOL:
                break;
 
+       case FR_TYPE_IPV4_PREFIX:
+               if (da_is_split_prefix(vp->da)) {
+                       uint32_t mask;
+
+                       mask = ~((~(uint32_t) 0) >> vp->vp_ip.prefix);
+
+                       FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff,
+                                                 (uint8_t const *)&vp->vp_ipv4addr,
+                                                 sizeof(vp->vp_ipv4addr));
+                       fr_dbuff_in(&work_dbuff, mask);
+                       break;
+               }
+               goto from_network;
+
        case FR_TYPE_STRING:
                /*
                 *      DNS labels get a special encoder.  DNS labels
@@ -144,6 +160,7 @@ static ssize_t encode_value(fr_dbuff_t *dbuff,
                FALL_THROUGH;
 
        default:
+       from_network:
                slen = fr_value_box_to_network(&work_dbuff, &vp->data);
                if (slen < 0) return slen;
                break;
diff --git a/src/tests/unit/protocols/dhcpv4/prefix.txt b/src/tests/unit/protocols/dhcpv4/prefix.txt
new file mode 100644 (file)
index 0000000..36b58be
--- /dev/null
@@ -0,0 +1,15 @@
+#
+#  Test vectors for umpteen different stupid IPv4 prefix formats.
+#
+proto dhcpv4
+proto-dictionary dhcpv4
+fuzzer-out dhcpv4
+
+decode-pair 15 08 7f 01 02 03 ff 00 00 00
+match Policy-Filter = 127.0.0.0/8
+
+encode-pair -
+match 15 08 7f 00 00 00 ff 00 00 00
+
+count
+match 7