From: Alan T. DeKok Date: Thu, 17 Mar 2022 13:54:25 +0000 (-0400) Subject: add support for split prefixes for Policy-Filter X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e3d7922baf0ec50bba3a28aef3289c107c66eeb0;p=thirdparty%2Ffreeradius-server.git add support for split prefixes for Policy-Filter made abstract for now, just in case other options are defined with the same stupid format. --- diff --git a/share/dictionary/dhcpv4/dictionary.rfc2131 b/share/dictionary/dhcpv4/dictionary.rfc2131 index b0bab52ecc4..d2ea938cd12 100644 --- a/share/dictionary/dhcpv4/dictionary.rfc2131 +++ b/share/dictionary/dhcpv4/dictionary.rfc2131 @@ -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 diff --git a/src/protocols/dhcpv4/base.c b/src/protocols/dhcpv4/base.c index d06b81b37c3..e9c9437e437 100644 --- a/src/protocols/dhcpv4/base.c +++ b/src/protocols/dhcpv4/base.c @@ -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; } diff --git a/src/protocols/dhcpv4/decode.c b/src/protocols/dhcpv4/decode.c index 89673ec35cc..c350907870d 100644 --- a/src/protocols/dhcpv4/decode.c +++ b/src/protocols/dhcpv4/decode.c @@ -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; diff --git a/src/protocols/dhcpv4/dhcpv4.h b/src/protocols/dhcpv4/dhcpv4.h index 5743165027f..bf8445fa42c 100644 --- a/src/protocols/dhcpv4/dhcpv4.h +++ b/src/protocols/dhcpv4/dhcpv4.h @@ -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; diff --git a/src/protocols/dhcpv4/encode.c b/src/protocols/dhcpv4/encode.c index 981931cb41d..45bb7bb541f 100644 --- a/src/protocols/dhcpv4/encode.c +++ b/src/protocols/dhcpv4/encode.c @@ -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 index 00000000000..36b58be38f9 --- /dev/null +++ b/src/tests/unit/protocols/dhcpv4/prefix.txt @@ -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