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
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];
}
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,
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;
}
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;
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;
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;
* "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
FALL_THROUGH;
default:
+ from_network:
slen = fr_value_box_to_network(&work_dbuff, &vp->data);
if (slen < 0) return slen;
break;
--- /dev/null
+#
+# 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