]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Switch DHCPv4 to using custom flag parsers
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Fri, 25 Oct 2024 05:03:32 +0000 (23:03 -0600)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Fri, 25 Oct 2024 05:03:32 +0000 (23:03 -0600)
share/dictionary/dhcpv4/dictionary.rfc2242
share/dictionary/dhcpv4/dictionary.rfc4039
share/dictionary/dhcpv4/dictionary.rfc8357
src/protocols/dhcpv4/base.c
src/protocols/dhcpv4/decode.c
src/protocols/dhcpv4/dhcpv4.h
src/protocols/dhcpv4/encode.c

index e8ef68f2296ae31541883265163ad2657765a8af..821880722c0d4edf1749cf650679705a11539f83 100644 (file)
@@ -20,10 +20,10 @@ ATTRIBUTE   Netware-Sub-Options                     63      tlv
 #   sub-option to be present in option 63 encoding. Each of them is
 #   simply a type length pair with length set to zero....
 #
-ATTRIBUTE      NWIP-Does-Not-Exist                     .1      bool    encode=exists
-ATTRIBUTE      NWIP-Exist-In-Options-Area              .2      bool    encode=exists
-ATTRIBUTE      NWIP-Exist-In-Same-File                 .3      bool    encode=exists
-ATTRIBUTE      NWIP-Exist-But-Too-Big                  .4      bool    encode=exists
+ATTRIBUTE      NWIP-Does-Not-Exist                     .1      bool    exists
+ATTRIBUTE      NWIP-Exist-In-Options-Area              .2      bool    exists
+ATTRIBUTE      NWIP-Exist-In-Same-File                 .3      bool    exists
+ATTRIBUTE      NWIP-Exist-But-Too-Big                  .4      bool    exists
 
 # Also from RFC2242:
 #
index cb5cc90663605f5389376d806aeb4a0dd3eb83c2..4f8ddf6e5158024968ffc59e67926adc7cb3f2cb 100644 (file)
@@ -12,4 +12,4 @@
 ##############################################################################
 
 # Rapid Commit
-ATTRIBUTE      Rapid-Commit                            80      bool            encode=exists
+ATTRIBUTE      Rapid-Commit                            80      bool            exists
index 4ce2222d23335904106e26fe2eded0bba512dc64..d52e8821a1ab45f1e07de9fea8f8b7a7be39d54b 100644 (file)
@@ -7,5 +7,4 @@
 #  If this attribute exists, then the reply is sent to the relay agent on the UDP
 #  source port, and not to port 67.
 #
-ATTRIBUTE      Remember-Relay-Port                     82.19   bool    encode=exists
-
+ATTRIBUTE      Remember-Relay-Port                     82.19   bool    exists
index 09c290e0ec55dcec5742107a8e80c7b18c866ccc..9c48b5e1e31947ee4511f98e2803dd623cb91421 100644 (file)
@@ -163,6 +163,36 @@ int dhcp_header_sizes[] = {
 
 uint8_t        eth_bcast[ETH_ADDR_LEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
 
+FR_DICT_ATTR_FLAG_FUNC(fr_dhcpv4_attr_flags_t, dns_label)
+FR_DICT_ATTR_FLAG_FUNC(fr_dhcpv4_attr_flags_t, exists)
+
+static int dict_flag_prefix(fr_dict_attr_t **da_p, char const *value, UNUSED fr_dict_flag_parser_rule_t const *rules)
+{
+       static fr_table_num_sorted_t const table[] = {
+               { L("bits"),                    DHCPV4_FLAG_PREFIX_BITS },
+               { L("split"),                   DHCPV4_FLAG_PREFIX_SPLIT }
+       };
+       static size_t table_len = NUM_ELEMENTS(table);
+
+       fr_dhcpv4_attr_flags_t *flags = fr_dict_attr_ext(*da_p, FR_DICT_ATTR_EXT_PROTOCOL_SPECIFIC);
+       fr_dhcpv4_attr_flags_prefix_t flag;
+
+       flag = fr_table_value_by_str(table, value, DHCPV4_FLAG_PREFIX_INVALID);
+       if (flag == DHCPV4_FLAG_PREFIX_INVALID) {
+               fr_strerror_printf("Unknown prefix type '%s'", value);
+               return -1;
+       }
+       flags->prefix = flag;
+
+       return 0;
+}
+
+static fr_dict_flag_parser_t const dhcpv4_flags[] = {
+       { L("dns_label"),       { .func = dict_flag_dns_label } },
+       { L("exists"),          { .func = dict_flag_exists } },
+       { L("prefix"),          { .func = dict_flag_prefix } }
+};
+
 int8_t fr_dhcpv4_attr_cmp(void const *a, void const *b)
 {
        fr_pair_t const *my_a = a, *my_b = b;
@@ -295,7 +325,7 @@ void *fr_dhcpv4_next_encodable(fr_dlist_head_t *list, void *current, void *uctx)
                PAIR_VERIFY(c);
                if (c->da->dict != dict || c->da->flags.internal) continue;
 
-               if (c->vp_type == FR_TYPE_BOOL && da_is_bool_exists(c->da) && !c->vp_bool) continue;
+               if (c->vp_type == FR_TYPE_BOOL && fr_dhcpv4_flag_exists(c->da) && !c->vp_bool) continue;
 
                break;
        }
@@ -675,14 +705,6 @@ void fr_dhcpv4_print_hex(FILE *fp, uint8_t const *packet, size_t packet_len)
        fprintf(fp, "\n");
 }
 
-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 },
-       { L("prefix=bits"),                     FLAG_ENCODE_BITS_PREFIX },
-       { L("encode=exists"),                   FLAG_ENCODE_BOOL_EXISTS },
-};
-
 static bool attr_valid(fr_dict_attr_t *da)
 {
        /*
@@ -709,19 +731,19 @@ static bool attr_valid(fr_dict_attr_t *da)
         */
        if (da->flags.extra || !da->flags.subtype) return true;
 
-       if ((da->type != FR_TYPE_STRING) && (da->flags.subtype == FLAG_ENCODE_DNS_LABEL)) {
+       if ((da->type != FR_TYPE_STRING) && (fr_dhcpv4_flag_dns_label(da))) {
                fr_strerror_const("The 'dns_label' flag can only be used with attributes of type 'string'");
                return false;
        }
 
        if ((da->type != FR_TYPE_IPV4_PREFIX) &&
-           ((da->flags.subtype == FLAG_ENCODE_SPLIT_PREFIX) || (da->flags.subtype == FLAG_ENCODE_BITS_PREFIX))) {
+           (fr_dhcpv4_flag_prefix(da))) {
                fr_strerror_const("The 'prefix=...' flag can only be used with attributes of type 'ipv4prefix'");
                return false;
        }
 
-       if ((da->type != FR_TYPE_BOOL) && (da->flags.subtype == FLAG_ENCODE_BOOL_EXISTS)) {
-               fr_strerror_const("The 'encode=exists' flag can only be used with attributes of type 'bool'");
+       if ((da->type != FR_TYPE_BOOL) && fr_dhcpv4_flag_exists(da)) {
+               fr_strerror_const("The 'exists' flag can only be used with attributes of type 'bool'");
                return false;
        }
 
@@ -733,9 +755,10 @@ fr_dict_protocol_t libfreeradius_dhcpv4_dict_protocol = {
        .name = "dhcpv4",
        .default_type_size = 1,
        .default_type_length = 1,
-       .subtype_table = subtype_table,
-       .subtype_table_len = NUM_ELEMENTS(subtype_table),
        .attr = {
+               .flags_table = dhcpv4_flags,
+               .flags_table_len = NUM_ELEMENTS(dhcpv4_flags),
+               .flags_len = sizeof(fr_dhcpv4_attr_flags_t),
                .valid = attr_valid
        },
 
index e334d4891a0f47cdcffb5cb8e8589500a8786176..b202fac037b8c5a53c9339bcd50701f06ca2323d 100644 (file)
@@ -76,7 +76,7 @@ static ssize_t decode_value_trampoline(TALLOC_CTX *ctx, fr_pair_list_t *out,
        /*
         *      @todo - we might need to limit this to only one DNS label.
         */
-       if ((parent->type == FR_TYPE_STRING) && da_is_dns_label(parent)) {
+       if ((parent->type == FR_TYPE_STRING) && fr_dhcpv4_flag_dns_label(parent)) {
                return fr_pair_dns_labels_from_network(ctx, out, parent, data, data, data_len, NULL, false);
        }
 
@@ -182,7 +182,7 @@ static ssize_t decode_value(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t
                 *      4 octets of address
                 *      4 octets of mask
                 */
-               if (da_is_split_prefix(da)) {
+               if (fr_dhcpv4_flag_prefix_split(da)) {
                        uint32_t ipaddr, mask;
 
                        if (data_len < 8) goto raw;
@@ -218,7 +218,7 @@ static ssize_t decode_value(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t
                        break;
                }
 
-               if (da_is_bits_prefix(vp->da)) {
+               if (fr_dhcpv4_flag_prefix_bits(vp->da)) {
                        size_t needs;
 
                        if ((data_len == 0) || (*p > 32)) goto raw;
@@ -485,7 +485,7 @@ static ssize_t decode_option(TALLOC_CTX *ctx, fr_pair_list_t *out,
 
                slen = fr_pair_raw_from_network(ctx, out, da, data + 2, len);
 
-       } else if ((da->type == FR_TYPE_STRING) && da_is_dns_label(da)) {
+       } else if ((da->type == FR_TYPE_STRING) && fr_dhcpv4_flag_dns_label(da)) {
                slen = fr_pair_dns_labels_from_network(ctx, out, da, data + 2, data + 2, len, NULL, true);
 
        } else if (da->flags.array) {
index c07028fbc6ca120972b6f3ec4b852f3375e822ee..59e4080fe806988ec13a8ec726c8c096d84f01c0 100644 (file)
@@ -63,22 +63,6 @@ typedef enum {
 
 #define FR_DHCP_PACKET_CODE_VALID(_code) (((_code) > 0) && ((_code) < FR_DHCP_CODE_MAX))
 
-/** subtype values for DHCPv4 and DHCPv6
- *
- */
-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
-       FLAG_ENCODE_BITS_PREFIX,                        //!< encode IPv4 prefixes as prefix bits, followed by IP.
-       FLAG_ENCODE_BOOL_EXISTS,                        //!< bool as existence checks
-};
-
-#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))
-#define da_is_bits_prefix(_da) (!(_da)->flags.extra && ((_da)->flags.subtype == FLAG_ENCODE_BITS_PREFIX))
-#define da_is_bool_exists(_da) (!(_da)->flags.extra && ((_da)->flags.subtype == FLAG_ENCODE_BOOL_EXISTS))
-
 typedef struct {
        uint8_t         opcode;
        uint8_t         htype;
@@ -152,6 +136,31 @@ typedef struct {
        TALLOC_CTX      *tmp_ctx;               //!< for temporary things cleaned up during decoding
 } fr_dhcpv4_ctx_t;
 
+typedef enum {
+       DHCPV4_FLAG_PREFIX_INVALID = -1,
+       DHCPV4_FLAG_PREFIX_NONE = 0,
+       DHCPV4_FLAG_PREFIX_BITS = 1,
+       DHCPV4_FLAG_PREFIX_SPLIT = 2,
+} fr_dhcpv4_attr_flags_prefix_t;
+
+typedef struct {
+       bool                            exists;
+       bool                            dns_label;
+       fr_dhcpv4_attr_flags_prefix_t   prefix;
+} fr_dhcpv4_attr_flags_t;
+
+static inline fr_dhcpv4_attr_flags_t const *fr_dhcpv4_attr_flags(fr_dict_attr_t const *da)
+{
+       return fr_dict_attr_ext(da, FR_DICT_ATTR_EXT_PROTOCOL_SPECIFIC);
+}
+
+#define fr_dhcpv4_flag_dns_label(_da)          (fr_dhcpv4_attr_flags(_da)->dns_label)
+#define fr_dhcpv4_flag_exists(_da)             (fr_dhcpv4_attr_flags(_da)->exists)
+
+#define fr_dhcpv4_flag_prefix(_da)             fr_dhcpv4_attr_flags(_da)->prefix
+#define fr_dhcpv4_flag_prefix_bits(_da)                (fr_dhcpv4_attr_flags(_da)->prefix == DHCPV4_FLAG_PREFIX_BITS)
+#define fr_dhcpv4_flag_prefix_split(_da)       (fr_dhcpv4_attr_flags(_da)->prefix == DHCPV4_FLAG_PREFIX_SPLIT)
+
 /*
  *     base.c
  */
index 224d4b646bf24ee01a8232f7417d99da2e0c187e..804aa699298e3636eb78960daa48967705ff1408 100644 (file)
@@ -102,14 +102,14 @@ static ssize_t encode_value(fr_dbuff_t *dbuff,
                 *      Rapid-Commit does this.  Options 19/20 require encoding as one byte of 0/1.
                 */
        case FR_TYPE_BOOL:
-               if (da_is_bool_exists(vp->da)) {
+               if (fr_dhcpv4_flag_exists(vp->da)) {
                        break;
                }
                FR_DBUFF_IN_RETURN(&work_dbuff, (uint8_t) (vp->vp_bool == true));
                break;
 
        case FR_TYPE_IPV4_PREFIX:
-               if (da_is_split_prefix(vp->da)) {
+               if (fr_dhcpv4_flag_prefix_split(vp->da)) {
                        uint32_t mask;
 
                        mask = ~((~(uint32_t) 0) >> vp->vp_ip.prefix);
@@ -121,7 +121,7 @@ static ssize_t encode_value(fr_dbuff_t *dbuff,
                        break;
                }
 
-               if (da_is_bits_prefix(vp->da)) {
+               if (fr_dhcpv4_flag_prefix_bits(vp->da)) {
                        size_t num_bytes = (vp->vp_ip.prefix + 0x07) >> 3;
 
                        FR_DBUFF_IN_RETURN(&work_dbuff, (uint8_t) vp->vp_ip.prefix);
@@ -144,7 +144,7 @@ static ssize_t encode_value(fr_dbuff_t *dbuff,
                 *
                 *      https://tools.ietf.org/html/rfc8415#section-10
                 */
-               if (da_is_dns_label(da)) {
+               if (fr_dhcpv4_flag_dns_label(da)) {
                        fr_dbuff_marker_t       last_byte, src;
 
                        fr_dbuff_marker(&last_byte, &work_dbuff);